From d4c25c22057d29c074af99553f4c143af77ebf4d Mon Sep 17 00:00:00 2001 From: devzero-bot Date: Thu, 15 Oct 2026 21:48:38 +0000 Subject: [PATCH] Release v0.1.25 --- .github/actions/gpu-mig-kind/action.yml | 6 + .github/actions/installer-check/action.yml | 6 + .github/actions/k8s-compat-build/action.yml | 6 + .github/actions/k8s-compat-test/action.yml | 6 + .github/actions/lint/action.yml | 6 + .github/actions/node-storage-kind/action.yml | 77 + .../node-storage-kind/kind-config.yaml | 9 + .github/actions/oom-flow-kind/action.yml | 6 + .github/actions/policy-kind/action.yml | 6 + .../actions/runtime-metrics-kind/action.yml | 63 +- .github/actions/tests/action.yml | 15 + .github/workflows/chart-zxporter.yml | 3 + .github/workflows/image-netmon.yml | 18 +- .github/workflows/image-nodemon.yml | 18 +- .github/workflows/image-zxporter.yml | 18 +- .github/workflows/node-storage-kind-test.yml | 27 + Dockerfile | 71 +- Dockerfile.netmon | 61 +- Dockerfile.nodemon | 50 +- Makefile | 157 +- README.md | 56 + RELEASE.md | 56 +- api/v1/collectionpolicy_types.go | 199 +- api/v1/zz_generated.deepcopy.go | 460 + buf.gen.yaml | 21 +- buf.work.yaml | 3 - buf.yaml | 21 + cmd/zxporter-netmon/main.go | 215 +- cmd/zxporter-netmon/main_test.go | 142 + .../bases/devzero.io_collectionpolicies.yaml | 354 +- config/manager/env_configmap.yaml | 2 + config/manager/manager.yaml | 4 + config/rbac/kustomization.yaml | 1 + config/rbac/metrics_reader_role_binding.yaml | 19 + config/rbac/role.yaml | 80 + config/zxporter-netmon.yaml | 6 +- dist/backend-install-gcp-lowpriv.yaml | 190 +- dist/backend-install-gcp.yaml | 203 +- dist/backend-install-lowpriv.yaml | 192 +- dist/backend-install.yaml | 205 +- dist/install-gcp-lowpriv.yaml | 190 +- dist/install-gcp.yaml | 203 +- dist/install-lowpriv.yaml | 192 +- dist/install.yaml | 205 +- dist/installer_updater-gcp-lowpriv.yaml | 188 +- dist/installer_updater-gcp.yaml | 201 +- dist/installer_updater-lowpriv.yaml | 190 +- dist/installer_updater.yaml | 203 +- dist/nodemon-gcp-lowpriv.yaml | 93 +- dist/nodemon-gcp.yaml | 106 +- dist/nodemon-lowpriv.yaml | 95 +- dist/nodemon.yaml | 108 +- dist/zxporter.yaml | 97 + docs/nodemon.md | 3 + docs/rbac.md | 28 + gen/api/v1/apiv1connect/cluster.connect.go | 87 + gen/api/v1/apiv1connect/k8s.connect.go | 268 + .../apiv1connect/metrics_collector.connect.go | 58 + .../apiv1connect/operator_health.connect.go | 29 +- gen/api/v1/cluster.pb.go | 2482 ++- gen/api/v1/cluster_grpc.pb.go | 117 + gen/api/v1/common.pb.go | 14498 +++++++++---- gen/api/v1/instance.pb.go | 16 +- gen/api/v1/k8s.pb.go | 17779 ++++++++++------ gen/api/v1/k8s_grpc.pb.go | 348 + gen/api/v1/metrics_collector.pb.go | 2666 ++- gen/api/v1/metrics_collector_grpc.pb.go | 78 + gen/api/v1/operator.pb.go | 648 +- gen/api/v1/operator_health.pb.go | 514 +- gen/api/v1/operator_health_grpc.pb.go | 39 +- gen/google/type/money.pb.go | 195 - gen/io/prometheus/client/metrics.pb.go | 1407 -- go.mod | 221 +- go.sum | 442 +- hack/install-new-zxporter.sh | 5 + helm-chart/devzero-common/Chart.yaml | 13 + helm-chart/devzero-common/README.md | 67 + .../devzero-common/templates/_globals.tpl | 42 + .../devzero-common/templates/_helpers.tpl | 60 + .../devzero-common/templates/_image.tpl | 115 + .../devzero-common/templates/_security.tpl | 44 + .../templates/_serviceaccount.tpl | 42 + .../devzero-common/templates/_workload.tpl | 30 + helm-chart/internal/charttest/charttest.go | 661 + helm-chart/internal/charttest/images.go | 161 + helm-chart/internal/charttest/images_test.go | 21 + helm-chart/zxporter-netmon/.helmignore | 14 + helm-chart/zxporter-netmon/Chart.lock | 6 + helm-chart/zxporter-netmon/Chart.yaml | 11 +- helm-chart/zxporter-netmon/README.md | 84 + helm-chart/zxporter-netmon/README.md.gotmpl | 35 + .../chart_common_metadata_test.go | 11 + .../zxporter-netmon/chart_globals_test.go | 68 + .../zxporter-netmon/chart_platform_test.go | 79 + .../zxporter-netmon/templates/_helpers.tpl | 120 + .../templates/clusterrole.yaml | 4 + .../templates/clusterrolebinding.yaml | 4 + .../zxporter-netmon/templates/daemonset.yaml | 43 +- .../templates/serviceaccount.yaml | 7 +- helm-chart/zxporter-netmon/values.schema.json | 133 + helm-chart/zxporter-netmon/values.yaml | 97 +- helm-chart/zxporter-nodemon/.helmignore | 14 + helm-chart/zxporter-nodemon/Chart.lock | 6 + helm-chart/zxporter-nodemon/Chart.yaml | 11 +- helm-chart/zxporter-nodemon/README.md | 93 + helm-chart/zxporter-nodemon/README.md.gotmpl | 37 + .../chart_common_metadata_test.go | 23 + .../zxporter-nodemon/chart_globals_test.go | 47 + .../zxporter-nodemon/chart_platform_test.go | 94 + .../zxporter-nodemon/chart_rbac_test.go | 75 + .../zxporter-nodemon/chart_resources_test.go | 219 + .../zxporter-nodemon/templates/_helpers.tpl | 123 + .../zxporter-nodemon/templates/daemonset.yaml | 69 +- .../templates/dcgm-exporter-configmap.yaml | 20 +- .../templates/nodemon-configmap.yaml | 8 + .../templates/priorityclass.yaml | 4 + .../zxporter-nodemon/templates/rbac.yaml | 24 +- .../zxporter-nodemon/values.schema.json | 219 + helm-chart/zxporter-nodemon/values.yaml | 53 +- helm-chart/zxporter/.helmignore | 14 + helm-chart/zxporter/Chart.lock | 9 +- helm-chart/zxporter/Chart.yaml | 13 +- helm-chart/zxporter/README.md | 91 + helm-chart/zxporter/README.md.gotmpl | 42 + .../zxporter/chart_common_metadata_test.go | 23 + helm-chart/zxporter/chart_globals_test.go | 75 + helm-chart/zxporter/chart_platform_test.go | 125 + .../zxporter/chart_token_identity_test.go | 124 + .../zxporter/chart_topology_spread_test.go | 103 + .../crds/devzero.io_collectionpolicies.yaml | 354 +- helm-chart/zxporter/templates/NOTES.txt | 4 +- helm-chart/zxporter/templates/_helpers.tpl | 139 +- helm-chart/zxporter/templates/configmap.yaml | 15 +- helm-chart/zxporter/templates/deployment.yaml | 32 +- .../zxporter/templates/gpu-runtime-rbac.yaml | 32 + .../templates/poddisruptionbudget.yaml | 4 + .../zxporter/templates/priorityclass.yaml | 4 + .../templates/secret-credentials.yaml | 7 + helm-chart/zxporter/templates/secret.yaml | 7 + .../zxporter/templates/service-mpa.yaml | 4 + helm-chart/zxporter/templates/service.yaml | 7 + .../zxporter/templates/serviceaccount.yaml | 7 + .../zxporter/templates/zxporter-rbac.yaml | 180 + helm-chart/zxporter/values.schema.json | 197 + helm-chart/zxporter/values.yaml | 70 +- internal/collector/argo_cd_collector.go | 455 + internal/collector/argo_cd_collector_test.go | 194 + internal/collector/change_detector.go | 22 +- internal/collector/clusterrole_collector.go | 4 +- .../collector/clusterrolebinding_collector.go | 4 +- internal/collector/collector_type_aliases.go | 47 + .../collector/composite_request_count_test.go | 2 +- .../collector/container_resource_collector.go | 69 +- .../container_resource_collector_test.go | 53 + internal/collector/crd_collector.go | 2 +- internal/collector/cronjob_collector.go | 4 +- internal/collector/csidriver_collector.go | 4 +- .../collector/csistoragecapacity_collector.go | 4 +- internal/collector/daemonset_collector.go | 6 +- internal/collector/deployment_collector.go | 4 +- .../collector/deployment_collector_test.go | 68 + internal/collector/disruption_block.go | 69 +- internal/collector/disruption_block_test.go | 92 +- internal/collector/endpoint_collector.go | 4 +- internal/collector/event_collector.go | 6 +- .../collector/flagger_resource_collector.go | 272 + .../flagger_resource_collector_test.go | 281 + internal/collector/flux_resource_collector.go | 282 + .../collector/flux_resource_collector_test.go | 262 + .../horizontalpodautoscaler_collector.go | 4 +- internal/collector/ingress_collector.go | 4 +- internal/collector/ingressclass_collector.go | 4 +- internal/collector/interface.go | 91 + internal/collector/job_collector.go | 4 +- .../collector/keda_scaled_job_collector.go | 10 +- .../collector/keda_scaled_object_collector.go | 11 +- .../keda_scaler_metrics_collector.go | 568 + .../keda_scaler_metrics_collector_test.go | 228 + internal/collector/kubelet_summary_client.go | 94 +- .../collector/kubelet_summary_client_test.go | 51 +- internal/collector/limitrange_collector.go | 4 +- internal/collector/manager.go | 206 +- internal/collector/manager_lock_test.go | 147 + internal/collector/manager_start_stop_test.go | 110 + internal/collector/namespace_collector.go | 4 +- internal/collector/networkpolicy_collector.go | 4 +- internal/collector/node_collector.go | 235 +- .../collector/node_collector_perf_test.go | 38 +- .../collector/node_collector_storage_test.go | 544 + internal/collector/node_collector_test.go | 165 +- internal/collector/nodemon_client.go | 11 + .../collector/persistentvolume_collector.go | 6 +- .../poddisruptionbudget_collector.go | 4 +- internal/collector/pvc_collector.go | 6 +- internal/collector/replicaset_collector.go | 4 +- .../replicationcontroller_collector.go | 4 +- internal/collector/resourcequota_collector.go | 4 +- .../collector/resourcequota_collector_test.go | 81 + internal/collector/role_collector.go | 4 +- internal/collector/rolebinding_collector.go | 4 +- .../collector/scheduler_metrics_collector.go | 892 + .../scheduler_metrics_collector_test.go | 942 + internal/collector/service_collector.go | 4 +- .../collector/serviceaccount_collector.go | 4 +- internal/collector/statefulset_collector.go | 6 +- internal/collector/storageclass_collector.go | 4 +- internal/collector/types.go | 152 + .../collector/volumeattachment_collector.go | 4 +- internal/controller/cluster_token.go | 97 + .../controller/cluster_token_persist_test.go | 94 + internal/controller/cluster_token_test.go | 137 + .../controller/collectionpolicy_controller.go | 906 +- .../collector_retry_and_alias_test.go | 143 + internal/controller/collector_specs_test.go | 97 + internal/controller/custom.go | 141 +- .../controller/pending_collectors_test.go | 91 + .../controller/requeue_failed_start_test.go | 38 + .../scheduler_metrics_scrape_interval_test.go | 67 + internal/health/component_names.go | 8 + internal/health/component_names_test.go | 5 + internal/logger/logger.go | 37 +- internal/logger/logger_test.go | 90 + .../networkmonitor/cilium_client_linux.go | 17 +- internal/networkmonitor/conntrack_client.go | 41 +- .../networkmonitor/conntrack_client_test.go | 129 + internal/networkmonitor/dns/ip2dns.go | 90 +- internal/networkmonitor/ebpf/tracer.go | 18 +- internal/networkmonitor/ebpf/tracer_linux.go | 240 +- .../networkmonitor/ebpf/tracer_linux_test.go | 138 +- .../networkmonitor/ebpf/tracer_nolinux.go | 2 +- internal/networkmonitor/ebpf/types.go | 35 +- internal/networkmonitor/egress_path.go | 599 + internal/networkmonitor/egress_path_test.go | 659 + internal/networkmonitor/monitor.go | 170 +- .../networkmonitor/monitor_redmetrics_test.go | 283 + .../networkmonitor/redmetrics/aggregator.go | 716 + .../redmetrics/aggregator_test.go | 645 + .../networkmonitor/redmetrics/attribute.go | 94 + .../redmetrics/attribute_test.go | 132 + internal/networkmonitor/redmetrics/clock.go | 16 + internal/networkmonitor/redmetrics/decode.go | 468 + .../networkmonitor/redmetrics/decode_test.go | 771 + internal/networkmonitor/redmetrics/doc.go | 19 + internal/networkmonitor/redmetrics/event.go | 96 + .../networkmonitor/redmetrics/histogram.go | 76 + .../redmetrics/histogram_test.go | 218 + internal/networkmonitor/redmetrics/http1.go | 219 + .../networkmonitor/redmetrics/http1_test.go | 244 + internal/networkmonitor/redmetrics/http2.go | 561 + .../networkmonitor/redmetrics/http2_test.go | 877 + .../redmetrics/socketprobe/c/socketprobe.c | 529 + .../redmetrics/socketprobe/generate.go | 35 + .../redmetrics/socketprobe/objects_test.go | 60 + .../redmetrics/socketprobe/probe.go | 167 + .../redmetrics/socketprobe/probe_linux.go | 408 + .../redmetrics/socketprobe/probe_nolinux.go | 34 + .../redmetrics/socketprobe/probe_test.go | 427 + .../socketprobe/socketprobe_arm64_bpfel.go | 164 + .../socketprobe/socketprobe_arm64_bpfel.o | Bin 0 -> 52088 bytes .../socketprobe/socketprobe_x86_bpfel.go | 164 + .../socketprobe/socketprobe_x86_bpfel.o | Bin 0 -> 51792 bytes internal/nodemon/jvm_collector.go | 10 +- internal/nodemon/jvm_discovery.go | 117 +- .../nodemon/jvm_hsperfdata_access_test.go | 187 + internal/nodemon/node_storage_test.go | 133 + internal/nodemon/runtime_collector.go | 9 +- internal/nodemon/runtime_detectors.go | 54 +- internal/nodemon/runtime_detectors_test.go | 49 + internal/nodemon/runtime_discovery.go | 46 +- internal/nodemon/runtime_discovery_test.go | 55 +- internal/nodemon/runtime_types.go | 8 +- internal/nodemon/scraper.go | 4 +- internal/nodemon/stats_types.go | 30 +- internal/nodemon/unified_exporter.go | 57 + internal/nodemon/unified_types.go | 18 + internal/proto/mpa.proto | 119 - .../transport/cluster_token_exchange_test.go | 51 + .../transport/connect_compression_test.go | 17 + internal/transport/dakr_client.go | 131 +- internal/transport/dakr_client_retry_test.go | 124 + internal/transport/interface.go | 43 +- internal/transport/sender.go | 34 +- internal/util/env.go | 23 +- proto/dakr_proto_descriptor.bin | Bin 408979 -> 0 bytes test/e2e/node_storage_test.go | 210 + test/fixtures/jvm-apps.yaml | 90 + test/testserver/main.go | 52 + verification/verify_e2e.sh | 5 + verification/verify_gpu_runtime_helm.sh | 5 + zxporter-descriptor.bin | Bin 175823 -> 0 bytes 290 files changed, 55667 insertions(+), 16248 deletions(-) create mode 100644 .github/actions/node-storage-kind/action.yml create mode 100644 .github/actions/node-storage-kind/kind-config.yaml create mode 100644 .github/workflows/node-storage-kind-test.yml delete mode 100644 buf.work.yaml create mode 100644 buf.yaml create mode 100644 cmd/zxporter-netmon/main_test.go create mode 100644 config/rbac/metrics_reader_role_binding.yaml delete mode 100644 gen/google/type/money.pb.go delete mode 100644 gen/io/prometheus/client/metrics.pb.go create mode 100644 helm-chart/devzero-common/Chart.yaml create mode 100644 helm-chart/devzero-common/README.md create mode 100644 helm-chart/devzero-common/templates/_globals.tpl create mode 100644 helm-chart/devzero-common/templates/_helpers.tpl create mode 100644 helm-chart/devzero-common/templates/_image.tpl create mode 100644 helm-chart/devzero-common/templates/_security.tpl create mode 100644 helm-chart/devzero-common/templates/_serviceaccount.tpl create mode 100644 helm-chart/devzero-common/templates/_workload.tpl create mode 100644 helm-chart/internal/charttest/charttest.go create mode 100644 helm-chart/internal/charttest/images.go create mode 100644 helm-chart/internal/charttest/images_test.go create mode 100644 helm-chart/zxporter-netmon/.helmignore create mode 100644 helm-chart/zxporter-netmon/Chart.lock create mode 100644 helm-chart/zxporter-netmon/README.md create mode 100644 helm-chart/zxporter-netmon/README.md.gotmpl create mode 100644 helm-chart/zxporter-netmon/chart_common_metadata_test.go create mode 100644 helm-chart/zxporter-netmon/chart_globals_test.go create mode 100644 helm-chart/zxporter-netmon/chart_platform_test.go create mode 100644 helm-chart/zxporter-netmon/values.schema.json create mode 100644 helm-chart/zxporter-nodemon/.helmignore create mode 100644 helm-chart/zxporter-nodemon/Chart.lock create mode 100644 helm-chart/zxporter-nodemon/README.md create mode 100644 helm-chart/zxporter-nodemon/README.md.gotmpl create mode 100644 helm-chart/zxporter-nodemon/chart_common_metadata_test.go create mode 100644 helm-chart/zxporter-nodemon/chart_globals_test.go create mode 100644 helm-chart/zxporter-nodemon/chart_platform_test.go create mode 100644 helm-chart/zxporter-nodemon/chart_rbac_test.go create mode 100644 helm-chart/zxporter-nodemon/chart_resources_test.go create mode 100644 helm-chart/zxporter-nodemon/values.schema.json create mode 100644 helm-chart/zxporter/.helmignore create mode 100644 helm-chart/zxporter/README.md create mode 100644 helm-chart/zxporter/README.md.gotmpl create mode 100644 helm-chart/zxporter/chart_common_metadata_test.go create mode 100644 helm-chart/zxporter/chart_globals_test.go create mode 100644 helm-chart/zxporter/chart_platform_test.go create mode 100644 helm-chart/zxporter/chart_token_identity_test.go create mode 100644 helm-chart/zxporter/chart_topology_spread_test.go create mode 100644 helm-chart/zxporter/values.schema.json create mode 100644 internal/collector/argo_cd_collector.go create mode 100644 internal/collector/argo_cd_collector_test.go create mode 100644 internal/collector/collector_type_aliases.go create mode 100644 internal/collector/deployment_collector_test.go create mode 100644 internal/collector/flagger_resource_collector.go create mode 100644 internal/collector/flagger_resource_collector_test.go create mode 100644 internal/collector/flux_resource_collector.go create mode 100644 internal/collector/flux_resource_collector_test.go create mode 100644 internal/collector/keda_scaler_metrics_collector.go create mode 100644 internal/collector/keda_scaler_metrics_collector_test.go create mode 100644 internal/collector/manager_lock_test.go create mode 100644 internal/collector/manager_start_stop_test.go create mode 100644 internal/collector/node_collector_storage_test.go create mode 100644 internal/collector/resourcequota_collector_test.go create mode 100644 internal/collector/scheduler_metrics_collector.go create mode 100644 internal/collector/scheduler_metrics_collector_test.go create mode 100644 internal/controller/cluster_token.go create mode 100644 internal/controller/cluster_token_persist_test.go create mode 100644 internal/controller/cluster_token_test.go create mode 100644 internal/controller/collector_retry_and_alias_test.go create mode 100644 internal/controller/collector_specs_test.go create mode 100644 internal/controller/pending_collectors_test.go create mode 100644 internal/controller/requeue_failed_start_test.go create mode 100644 internal/controller/scheduler_metrics_scrape_interval_test.go create mode 100644 internal/logger/logger_test.go create mode 100644 internal/networkmonitor/egress_path.go create mode 100644 internal/networkmonitor/egress_path_test.go create mode 100644 internal/networkmonitor/monitor_redmetrics_test.go create mode 100644 internal/networkmonitor/redmetrics/aggregator.go create mode 100644 internal/networkmonitor/redmetrics/aggregator_test.go create mode 100644 internal/networkmonitor/redmetrics/attribute.go create mode 100644 internal/networkmonitor/redmetrics/attribute_test.go create mode 100644 internal/networkmonitor/redmetrics/clock.go create mode 100644 internal/networkmonitor/redmetrics/decode.go create mode 100644 internal/networkmonitor/redmetrics/decode_test.go create mode 100644 internal/networkmonitor/redmetrics/doc.go create mode 100644 internal/networkmonitor/redmetrics/event.go create mode 100644 internal/networkmonitor/redmetrics/histogram.go create mode 100644 internal/networkmonitor/redmetrics/histogram_test.go create mode 100644 internal/networkmonitor/redmetrics/http1.go create mode 100644 internal/networkmonitor/redmetrics/http1_test.go create mode 100644 internal/networkmonitor/redmetrics/http2.go create mode 100644 internal/networkmonitor/redmetrics/http2_test.go create mode 100644 internal/networkmonitor/redmetrics/socketprobe/c/socketprobe.c create mode 100644 internal/networkmonitor/redmetrics/socketprobe/generate.go create mode 100644 internal/networkmonitor/redmetrics/socketprobe/objects_test.go create mode 100644 internal/networkmonitor/redmetrics/socketprobe/probe.go create mode 100644 internal/networkmonitor/redmetrics/socketprobe/probe_linux.go create mode 100644 internal/networkmonitor/redmetrics/socketprobe/probe_nolinux.go create mode 100644 internal/networkmonitor/redmetrics/socketprobe/probe_test.go create mode 100644 internal/networkmonitor/redmetrics/socketprobe/socketprobe_arm64_bpfel.go create mode 100644 internal/networkmonitor/redmetrics/socketprobe/socketprobe_arm64_bpfel.o create mode 100644 internal/networkmonitor/redmetrics/socketprobe/socketprobe_x86_bpfel.go create mode 100644 internal/networkmonitor/redmetrics/socketprobe/socketprobe_x86_bpfel.o create mode 100644 internal/nodemon/jvm_hsperfdata_access_test.go create mode 100644 internal/nodemon/node_storage_test.go delete mode 100644 internal/proto/mpa.proto create mode 100644 internal/transport/cluster_token_exchange_test.go create mode 100644 internal/transport/dakr_client_retry_test.go delete mode 100644 proto/dakr_proto_descriptor.bin create mode 100644 test/e2e/node_storage_test.go delete mode 100644 zxporter-descriptor.bin diff --git a/.github/actions/gpu-mig-kind/action.yml b/.github/actions/gpu-mig-kind/action.yml index fdb0e6c6..da4d172d 100644 --- a/.github/actions/gpu-mig-kind/action.yml +++ b/.github/actions/gpu-mig-kind/action.yml @@ -43,6 +43,12 @@ runs: steps: - name: Set up Go uses: actions/setup-go@v5 + # setup-go runs `go version` / `go env` from the workspace root. In the + # services monorepo that is the root module, whose newer go line would make + # GOTOOLCHAIN=auto download that toolchain (unused, and it collides with the + # cache restore). local keeps the Go this module's go.mod asks for. + env: + GOTOOLCHAIN: local with: go-version-file: ${{ inputs.working-directory }}/go.mod cache: true diff --git a/.github/actions/installer-check/action.yml b/.github/actions/installer-check/action.yml index e7786edd..a817bbb7 100644 --- a/.github/actions/installer-check/action.yml +++ b/.github/actions/installer-check/action.yml @@ -20,6 +20,12 @@ runs: steps: - name: Set up Go uses: actions/setup-go@v5 + # setup-go runs `go version` / `go env` from the workspace root. In the + # services monorepo that is the root module, whose newer go line would make + # GOTOOLCHAIN=auto download that toolchain (unused, and it collides with the + # cache restore). local keeps the Go this module's go.mod asks for. + env: + GOTOOLCHAIN: local with: go-version-file: ${{ inputs.working-directory }}/go.mod cache: true diff --git a/.github/actions/k8s-compat-build/action.yml b/.github/actions/k8s-compat-build/action.yml index e4215bec..d06e7750 100644 --- a/.github/actions/k8s-compat-build/action.yml +++ b/.github/actions/k8s-compat-build/action.yml @@ -103,6 +103,12 @@ runs: - name: Set up Go uses: actions/setup-go@v5 + # setup-go runs `go version` / `go env` from the workspace root. In the + # services monorepo that is the root module, whose newer go line would make + # GOTOOLCHAIN=auto download that toolchain (unused, and it collides with the + # cache restore). local keeps the Go this module's go.mod asks for. + env: + GOTOOLCHAIN: local with: go-version-file: ${{ inputs.working-directory }}/go.mod cache: true diff --git a/.github/actions/k8s-compat-test/action.yml b/.github/actions/k8s-compat-test/action.yml index 07e3335b..57af4e17 100644 --- a/.github/actions/k8s-compat-test/action.yml +++ b/.github/actions/k8s-compat-test/action.yml @@ -52,6 +52,12 @@ runs: - name: Set up Go uses: actions/setup-go@v5 + # setup-go runs `go version` / `go env` from the workspace root. In the + # services monorepo that is the root module, whose newer go line would make + # GOTOOLCHAIN=auto download that toolchain (unused, and it collides with the + # cache restore). local keeps the Go this module's go.mod asks for. + env: + GOTOOLCHAIN: local with: go-version-file: ${{ inputs.working-directory }}/go.mod cache: true diff --git a/.github/actions/lint/action.yml b/.github/actions/lint/action.yml index f3c09e6f..86db1f23 100644 --- a/.github/actions/lint/action.yml +++ b/.github/actions/lint/action.yml @@ -19,6 +19,12 @@ runs: steps: - name: Set up Go uses: actions/setup-go@v5 + # setup-go runs `go version` / `go env` from the workspace root. In the + # services monorepo that is the root module, whose newer go line would make + # GOTOOLCHAIN=auto download that toolchain (unused, and it collides with the + # cache restore). local keeps the Go this module's go.mod asks for. + env: + GOTOOLCHAIN: local with: go-version-file: ${{ inputs.working-directory }}/go.mod cache: true diff --git a/.github/actions/node-storage-kind/action.yml b/.github/actions/node-storage-kind/action.yml new file mode 100644 index 00000000..7b47aa04 --- /dev/null +++ b/.github/actions/node-storage-kind/action.yml @@ -0,0 +1,77 @@ +name: "zxporter: node-storage-kind" +description: | + End-to-end validation of node storage occupancy collection against a real + kubelet on a real kind cluster: the production KubeletSummaryClient reads + node.fs and node.runtime.imageFs through the API-server nodes/proxy + subresource, and the spec asserts the invariants the feature rests on. + + Why a live cluster earns its keep here. Every other test of this feature + reads a captured fixture, which cannot catch the kubelet dropping a + section, renaming a field, or the nodes/proxy RBAC verb going missing. + More importantly, imagefs-is-a-subset-of-nodefs -- the invariant that + stops consumers double-counting disk -- has otherwise only ever been + checked against numbers we wrote ourselves, and the shared-device + classifier is a heuristic precisely because the kubelet does not name the + device, so the only honest test of it is a node whose real layout we know. + + The cheapest kind spec in this tree: no image to build, no operator to + install, no fixture workload, and no port-forward. The client reads + through the API server, the same path kubectl already proves reachable + from the runner, so a plain cluster with no add-ons is enough. + + Assertions live in a Go/Ginkgo spec (test/e2e/node_storage_test.go) rather + than a shell heredoc, matching oom-flow-kind: the interesting logic is + Kubernetes-API-shaped, not HTTP-response-shaped. + + Consumed by two callers: + - operators/zxporter/.github/workflows/node-storage-kind-test.yml + (published to devzero-inc/zxporter; uses this via + ./.github/actions/node-storage-kind) + - .github/workflows/zxporter-node-storage-kind.yml at services root + (uses this via ./operators/zxporter/.github/actions/node-storage-kind + with working-directory: operators/zxporter) + + Caller checks out the repo; this action assumes cwd contains the + zxporter source (or the working-directory input points at it). + +inputs: + working-directory: + description: "Path to the zxporter tree relative to the repo root." + required: false + default: "." + +runs: + using: composite + steps: + - name: Set up Go + uses: actions/setup-go@v5 + # setup-go runs `go version` / `go env` from the workspace root. In the + # services monorepo that is the root module, whose newer go line would make + # GOTOOLCHAIN=auto download that toolchain (unused, and it collides with the + # cache restore). local keeps the Go this module's go.mod asks for. + env: + GOTOOLCHAIN: local + with: + go-version-file: ${{ inputs.working-directory }}/go.mod + cache: true + cache-dependency-path: ${{ inputs.working-directory }}/go.sum + + # Two nodes on purpose: the spec walks every node in the cluster, so a + # multi-node cluster exercises the per-node loop rather than a + # single-node special case. kind's control-plane and worker both run + # containerd with its state on the same filesystem as the kubelet's, + # which is what makes the shared-device assertion meaningful. + - name: Create kind cluster + uses: helm/kind-action@v1 + with: + version: v0.27.0 + node_image: kindest/node:v1.32.3 + cluster_name: kind-node-storage + config: ${{ inputs.working-directory }}/.github/actions/node-storage-kind/kind-config.yaml + wait: 120s + + - name: Run node-storage e2e spec + shell: bash + working-directory: ${{ inputs.working-directory }} + run: | + go test ./test/e2e/... -v -ginkgo.v -ginkgo.label-filter=node-storage -timeout 10m diff --git a/.github/actions/node-storage-kind/kind-config.yaml b/.github/actions/node-storage-kind/kind-config.yaml new file mode 100644 index 00000000..0a9c34e7 --- /dev/null +++ b/.github/actions/node-storage-kind/kind-config.yaml @@ -0,0 +1,9 @@ +# Two nodes on purpose. The node-storage spec walks every node in the cluster, +# so a multi-node cluster exercises the per-node loop rather than a single-node +# special case -- and it means the shared-device assertion has to hold on more +# than one kubelet before the suite goes green. +kind: Cluster +apiVersion: kind.x-k8s.io/v1alpha4 +nodes: + - role: control-plane + - role: worker diff --git a/.github/actions/oom-flow-kind/action.yml b/.github/actions/oom-flow-kind/action.yml index 1d1e06c1..6957774b 100644 --- a/.github/actions/oom-flow-kind/action.yml +++ b/.github/actions/oom-flow-kind/action.yml @@ -33,6 +33,12 @@ runs: steps: - name: Set up Go uses: actions/setup-go@v5 + # setup-go runs `go version` / `go env` from the workspace root. In the + # services monorepo that is the root module, whose newer go line would make + # GOTOOLCHAIN=auto download that toolchain (unused, and it collides with the + # cache restore). local keeps the Go this module's go.mod asks for. + env: + GOTOOLCHAIN: local with: go-version-file: ${{ inputs.working-directory }}/go.mod cache: true diff --git a/.github/actions/policy-kind/action.yml b/.github/actions/policy-kind/action.yml index d08d4489..53d9cee7 100644 --- a/.github/actions/policy-kind/action.yml +++ b/.github/actions/policy-kind/action.yml @@ -44,6 +44,12 @@ runs: steps: - name: Set up Go uses: actions/setup-go@v5 + # setup-go runs `go version` / `go env` from the workspace root. In the + # services monorepo that is the root module, whose newer go line would make + # GOTOOLCHAIN=auto download that toolchain (unused, and it collides with the + # cache restore). local keeps the Go this module's go.mod asks for. + env: + GOTOOLCHAIN: local with: go-version-file: ${{ inputs.working-directory }}/go.mod cache: true diff --git a/.github/actions/runtime-metrics-kind/action.yml b/.github/actions/runtime-metrics-kind/action.yml index 18f02355..136f2922 100644 --- a/.github/actions/runtime-metrics-kind/action.yml +++ b/.github/actions/runtime-metrics-kind/action.yml @@ -29,6 +29,12 @@ runs: steps: - name: Set up Go uses: actions/setup-go@v5 + # setup-go runs `go version` / `go env` from the workspace root. In the + # services monorepo that is the root module, whose newer go line would make + # GOTOOLCHAIN=auto download that toolchain (unused, and it collides with the + # cache restore). local keeps the Go this module's go.mod asks for. + env: + GOTOOLCHAIN: local with: go-version-file: ${{ inputs.working-directory }}/go.mod cache: true @@ -58,6 +64,7 @@ runs: # The deprecated key must render the exact same manifests as the # canonical one (privileges, env, RBAC), and disabling via the # deprecated key alone must still work. + helm dependency build ./helm-chart/zxporter-nodemon helm template zxporter-nodemon ./helm-chart/zxporter-nodemon \ --set global.k8sProvider=other --set dcgmExporter.enabled=false \ --set jvmMetrics.enabled=true > /tmp/render-deprecated.yaml @@ -149,6 +156,8 @@ runs: kubectl -n jvm-spike rollout status deploy/java21-precedence-env --timeout=300s kubectl -n jvm-spike rollout status deploy/java21-precedence-cmdline --timeout=300s kubectl -n jvm-spike rollout status deploy/java21-per-flag-sources --timeout=300s + kubectl -n jvm-spike rollout status deploy/zipkin-writable-rootfs --timeout=300s + kubectl -n jvm-spike rollout status deploy/zipkin-readonly-rootfs --timeout=300s kubectl -n jvm-spike rollout status deploy/not-java --timeout=300s - name: Deploy Node.js test workloads @@ -213,10 +222,17 @@ runs: # until everything expected has appeared, then run the strict # assertions (which give precise failure messages if it never does). need_versions = ['node-version-env', 'node-version-binaryscan', 'python-app', - 'ruby-app', 'go-app', 'dotnet-app', 'deno-app', 'bun-app', 'rust-app'] + 'ruby-app', 'go-app', 'dotnet-app', 'deno-app', 'bun-app', 'rust-app', + # hsperfdata-less JVM: version comes from the image's + # JAVA_VERSION env, so it resolves like any other. + 'zipkin-readonly-rootfs'] + need_jvm = ['java8-xmx-only', 'zipkin-writable-rootfs'] def all_present(): c = fetch('/container/runtime-metrics') - if not any((m.get('pod') or '').startswith('java8-xmx-only') for m in c.get('jvm', [])): + jvm = c.get('jvm', []) + if not all( + any((m.get('pod') or '').startswith(p) for m in jvm) + for p in need_jvm): return False rt = c.get('runtimes', []) return all( @@ -381,6 +397,49 @@ runs: if any((p or '').startswith('not-node') for p in all_runtime_pods): raise SystemExit(f'runtime-metrics unexpectedly includes not-node in runtimes bucket; got {all_runtime_pods}') + # ============ hsperfdata-less JVM fallback ============ + # zipkin-{writable,readonly}-rootfs are the same image and uid, differing + # only in readOnlyRootFilesystem. With a read-only rootfs and no writable + # /tmp, HotSpot cannot create /tmp/hsperfdata_/, so the + # hsperfdata pipeline has nothing to read — the process must still be + # reported, as runtime "java" in the generic bucket. + jvm_entries = runtime.get('jvm', []) + + # Writable rootfs: full JVM pipeline, real heap metrics. + m_rw = [m for m in jvm_entries if (m.get('pod') or '').startswith('zipkin-writable-rootfs')] + if not m_rw: + raise SystemExit(f'zipkin-writable-rootfs missing from jvm bucket; got {jvm_pods}') + if (m_rw[0].get('heap_max_size_bytes') or 0) <= 0: + raise SystemExit(f'zipkin-writable-rootfs expected heap.max_bytes > 0; got {m_rw[0]!r}') + # The image's entrypoint sets -Xms160m -Xmx160m; proves the flags are + # read from the live cmdline, not from the pod spec (which has neither). + if m_rw[0].get('flags_extracted', {}).get('xmx_bytes') != 167772160: + raise SystemExit( + f'zipkin-writable-rootfs expected xmx_bytes=167772160 from the image entrypoint; ' + f'got {m_rw[0].get("flags_extracted")!r}') + if any((p or '').startswith('zipkin-writable-rootfs') for p in all_runtime_pods): + raise SystemExit('zipkin-writable-rootfs must not be double-reported in the runtimes bucket') + + # Read-only rootfs: no heap metrics are possible, so it must NOT be in + # the jvm bucket — and must NOT vanish entirely either. + if any((p or '').startswith('zipkin-readonly-rootfs') for p in jvm_pods): + raise SystemExit('zipkin-readonly-rootfs has no hsperfdata and must not appear in the jvm bucket') + m_ro = [m for m in runtime.get('runtimes', []) + if (m.get('pod') or '').startswith('zipkin-readonly-rootfs')] + if not m_ro: + raise SystemExit( + 'zipkin-readonly-rootfs missing from BOTH buckets — a JVM without hsperfdata ' + f'must fall back to the runtimes bucket; got runtimes={all_runtime_pods} jvm={jvm_pods}') + if m_ro[0].get('runtime') != 'java': + raise SystemExit(f'zipkin-readonly-rootfs expected runtime="java"; got {m_ro[0]!r}') + # Version still resolves passively (JAVA_VERSION env in the image). + if not (m_ro[0].get('version') or '').startswith('21.'): + raise SystemExit(f'zipkin-readonly-rootfs expected a 21.x version from env; got {m_ro[0]!r}') + if m_ro[0].get('version_source') != 'env': + raise SystemExit(f'zipkin-readonly-rootfs expected version_source=env; got {m_ro[0]!r}') + if m_ro[0].get('namespace') != 'jvm-spike' or not m_ro[0].get('container_id'): + raise SystemExit(f'zipkin-readonly-rootfs has bad pod metadata: {m_ro[0]!r}') + # ============ Generic runtimes bucket ============ # dotnet-app deliberately exercises the apphost probe path (comm is the # app name, not "dotnet"); go-app exercises the buildinfo probe. diff --git a/.github/actions/tests/action.yml b/.github/actions/tests/action.yml index 5c9db47e..039876fe 100644 --- a/.github/actions/tests/action.yml +++ b/.github/actions/tests/action.yml @@ -24,6 +24,12 @@ runs: steps: - name: Set up Go uses: actions/setup-go@v5 + # setup-go runs `go version` / `go env` from the workspace root. In the + # services monorepo that is the root module, whose newer go line would make + # GOTOOLCHAIN=auto download that toolchain (unused, and it collides with the + # cache restore). local keeps the Go this module's go.mod asks for. + env: + GOTOOLCHAIN: local with: go-version-file: ${{ inputs.working-directory }}/go.mod cache: true @@ -34,6 +40,15 @@ runs: working-directory: ${{ inputs.working-directory }} run: go mod download + # helm-chart/*/chart_resources_test.go shells out to `helm template`, and + # skips itself when helm is absent. Install it explicitly so the chart + # guards actually run instead of reporting a silent green -- the runner + # image is not guaranteed to carry helm. Pinned to the same version the + # tunr chart tests use (.github/workflows/tunr-test.yml). + - uses: azure/setup-helm@v5 + with: + version: "3.12.3" + - name: Run make test shell: bash working-directory: ${{ inputs.working-directory }} diff --git a/.github/workflows/chart-zxporter.yml b/.github/workflows/chart-zxporter.yml index a5f07135..de177b4c 100644 --- a/.github/workflows/chart-zxporter.yml +++ b/.github/workflows/chart-zxporter.yml @@ -73,6 +73,9 @@ jobs: - name: Package and push chart run: | rm -f helm-chart/zxporter/charts/zxporter-nodemon-*.tgz + # Vendor nodemon's own devzero-common first, so the nodemon copy + # bundled into this chart is complete on its own. + helm dependency build helm-chart/zxporter-nodemon helm dependency update helm-chart/${{ env.CHART_NAME }} helm lint helm-chart/${{ env.CHART_NAME }} mkdir -p helm-chart/packages diff --git a/.github/workflows/image-netmon.yml b/.github/workflows/image-netmon.yml index e11c4496..31f52dd1 100644 --- a/.github/workflows/image-netmon.yml +++ b/.github/workflows/image-netmon.yml @@ -44,13 +44,11 @@ jobs: if [[ ! "$VERSION" =~ ^v([0-9]+)\.([0-9]+)\.([0-9]+)$ ]]; then echo "::error::version '$VERSION' must match vX.Y.Z"; exit 1 fi + # The image's version interface (KUB-151 S7). The Dockerfile splits + # VERSION into the major/minor/patch the binary reports. { - echo "MAJOR=${BASH_REMATCH[1]}" - echo "MINOR=${BASH_REMATCH[2]}" - echo "PATCH=${BASH_REMATCH[3]}" - echo "GITVERSION=${VERSION}" - echo "COMMIT_HASH=$(git rev-parse --short HEAD)" - echo "GIT_TREE_STATE=clean" + echo "VERSION=${VERSION}" + echo "COMMIT=$(git rev-parse --short HEAD)" echo "BUILD_DATE=$(date -u +'%Y-%m-%dT%H:%M:%SZ')" } >> "$GITHUB_ENV" @@ -82,12 +80,8 @@ jobs: -t public.ecr.aws/devzeroinc/zxporter-netmon:${{ inputs.version }} \ --build-arg TARGETOS \ --build-arg TARGETARCH \ - --build-arg MAJOR="$MAJOR" \ - --build-arg MINOR="$MINOR" \ - --build-arg PATCH="$PATCH" \ - --build-arg GITVERSION="$GITVERSION" \ - --build-arg COMMIT_HASH="$COMMIT_HASH" \ - --build-arg GIT_TREE_STATE="$GIT_TREE_STATE" \ + --build-arg VERSION="$VERSION" \ + --build-arg COMMIT="$COMMIT" \ --build-arg BUILD_DATE="$BUILD_DATE" \ -f Dockerfile.netmon \ . diff --git a/.github/workflows/image-nodemon.yml b/.github/workflows/image-nodemon.yml index 9532c52b..b63a8636 100644 --- a/.github/workflows/image-nodemon.yml +++ b/.github/workflows/image-nodemon.yml @@ -44,13 +44,11 @@ jobs: if [[ ! "$VERSION" =~ ^v([0-9]+)\.([0-9]+)\.([0-9]+)$ ]]; then echo "::error::version '$VERSION' must match vX.Y.Z"; exit 1 fi + # The image's version interface (KUB-151 S7). The Dockerfile splits + # VERSION into the major/minor/patch the binary reports. { - echo "MAJOR=${BASH_REMATCH[1]}" - echo "MINOR=${BASH_REMATCH[2]}" - echo "PATCH=${BASH_REMATCH[3]}" - echo "GITVERSION=${VERSION}" - echo "COMMIT_HASH=$(git rev-parse --short HEAD)" - echo "GIT_TREE_STATE=clean" + echo "VERSION=${VERSION}" + echo "COMMIT=$(git rev-parse --short HEAD)" echo "BUILD_DATE=$(date -u +'%Y-%m-%dT%H:%M:%SZ')" } >> "$GITHUB_ENV" @@ -82,12 +80,8 @@ jobs: -t public.ecr.aws/devzeroinc/zxporter-nodemon:${{ inputs.version }} \ --build-arg TARGETOS \ --build-arg TARGETARCH \ - --build-arg MAJOR="$MAJOR" \ - --build-arg MINOR="$MINOR" \ - --build-arg PATCH="$PATCH" \ - --build-arg GITVERSION="$GITVERSION" \ - --build-arg COMMIT_HASH="$COMMIT_HASH" \ - --build-arg GIT_TREE_STATE="$GIT_TREE_STATE" \ + --build-arg VERSION="$VERSION" \ + --build-arg COMMIT="$COMMIT" \ --build-arg BUILD_DATE="$BUILD_DATE" \ -f Dockerfile.nodemon \ . diff --git a/.github/workflows/image-zxporter.yml b/.github/workflows/image-zxporter.yml index a5b1c99d..31480742 100644 --- a/.github/workflows/image-zxporter.yml +++ b/.github/workflows/image-zxporter.yml @@ -44,13 +44,11 @@ jobs: if [[ ! "$VERSION" =~ ^v([0-9]+)\.([0-9]+)\.([0-9]+)$ ]]; then echo "::error::version '$VERSION' must match vX.Y.Z"; exit 1 fi + # The image's version interface (KUB-151 S7). The Dockerfile splits + # VERSION into the major/minor/patch the binary reports. { - echo "MAJOR=${BASH_REMATCH[1]}" - echo "MINOR=${BASH_REMATCH[2]}" - echo "PATCH=${BASH_REMATCH[3]}" - echo "GITVERSION=${VERSION}" - echo "COMMIT_HASH=$(git rev-parse --short HEAD)" - echo "GIT_TREE_STATE=clean" + echo "VERSION=${VERSION}" + echo "COMMIT=$(git rev-parse --short HEAD)" echo "BUILD_DATE=$(date -u +'%Y-%m-%dT%H:%M:%SZ')" } >> "$GITHUB_ENV" @@ -82,11 +80,7 @@ jobs: -t public.ecr.aws/devzeroinc/zxporter:${{ inputs.version }} \ --build-arg TARGETOS \ --build-arg TARGETARCH \ - --build-arg MAJOR="$MAJOR" \ - --build-arg MINOR="$MINOR" \ - --build-arg PATCH="$PATCH" \ - --build-arg GITVERSION="$GITVERSION" \ - --build-arg COMMIT_HASH="$COMMIT_HASH" \ - --build-arg GIT_TREE_STATE="$GIT_TREE_STATE" \ + --build-arg VERSION="$VERSION" \ + --build-arg COMMIT="$COMMIT" \ --build-arg BUILD_DATE="$BUILD_DATE" \ . diff --git a/.github/workflows/node-storage-kind-test.yml b/.github/workflows/node-storage-kind-test.yml new file mode 100644 index 00000000..39207c98 --- /dev/null +++ b/.github/workflows/node-storage-kind-test.yml @@ -0,0 +1,27 @@ +name: node-storage-kind + +# End-to-end validation of node storage occupancy against a real kubelet on a +# real kind cluster. Actual test logic lives in the composite action at +# .github/actions/node-storage-kind so the services monorepo can reuse it via +# ./operators/zxporter/.github/actions/node-storage-kind. + +permissions: + contents: read + +on: + pull_request: + paths-ignore: + - '**.md' + - 'docs/**' + - 'LICENSE' + workflow_dispatch: + +jobs: + node-storage-kind: + runs-on: namespace-profile-dz-generic + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Run node-storage-kind + uses: ./.github/actions/node-storage-kind diff --git a/Dockerfile b/Dockerfile index 295c757c..09113698 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,5 +1,8 @@ # Build the manager binary -FROM golang:1.26 AS builder +# The builder runs on the build host's platform and cross-compiles for the +# target (CGO is off), so multi-arch release builds do not run the Go +# toolchain under QEMU emulation. +FROM --platform=$BUILDPLATFORM golang:1.26 AS builder ARG TARGETOS ARG TARGETARCH ENV GOOS=${TARGETOS} @@ -9,50 +12,48 @@ WORKDIR /workspace # Copy the Go Modules manifests COPY go.mod go.sum ./ -# COPY go.sum go.sum -# # cache deps before building and copying source so that we don't need to re-download as much -# # and so that source changes don't invalidate our downloaded layer -RUN go mod download +# cache deps before building and copying source so that we don't need to re-download as much +# and so that source changes don't invalidate our downloaded layer +RUN --mount=type=cache,id=gomod,target=/go/pkg,sharing=shared \ + go mod download COPY . . -ARG MAJOR=0 -ARG MINOR=0 -ARG PATCH=1 -ARG GITVERSION=unknown -ARG COMMIT_HASH=unknown -ARG GIT_TREE_STATE=unknown +# Version interface (KUB-151 S7): VERSION is vX.Y.Z[-prerelease], COMMIT the +# short sha, BUILD_DATE RFC 3339. The build step splits VERSION into the +# major/minor/patch the version package reports. The prerelease suffix is +# dropped from patch, as the release workflows did when they passed +# MAJOR/MINOR/PATCH themselves, so a release reports the same version as before. +# GIT_TREE_STATE is not part of the release interface: releases build from a +# clean tag checkout, which is the default; `make docker-build` passes the +# real state for local builds. +ARG VERSION=v0.0.1 +ARG COMMIT=unknown ARG BUILD_DATE=unknown - -RUN echo "MAJOR: ${MAJOR}" -RUN echo "MINOR: ${MINOR}" -RUN echo "PATCH: ${PATCH}" -RUN echo "GITVERSION: ${GITVERSION}" -RUN echo "COMMIT_HASH: ${COMMIT_HASH}" -RUN echo "GIT_TREE_STATE: ${GIT_TREE_STATE}" -RUN echo "BUILD_DATE: ${BUILD_DATE}" -RUN echo "TARGETOS: ${TARGETOS:-linux}" -RUN echo "TARGETARCH: ${TARGETARCH}" +ARG GIT_TREE_STATE=clean # Build -# the GOARCH has not a default value to allow the binary be built according to the host where the command -# was called. For example, if we call make docker-build in a local env which has the Apple Silicon M1 SO -# the docker BUILDPLATFORM arg will be linux/arm64 when for Apple x86 it will be linux/amd64. Therefore, -# by leaving it empty we can ensure that the container and binary shipped on it will have the same platform. -RUN CGO_ENABLED=0 GOOS=${TARGETOS:-linux} GOARCH=${TARGETARCH} GO_VERSION=$(go version | awk '{print $3}') go build \ +# GOARCH comes from TARGETARCH, which BuildKit sets from --platform (or the +# host's platform when none is given), so the binary always matches the image. +RUN --mount=type=cache,id=gomod,target=/go/pkg,sharing=shared \ + --mount=type=cache,id=gobuild,target=/root/.cache/go-build,sharing=shared \ + set -eu; \ + V="${VERSION#v}"; \ + MAJOR="${V%%.*}"; REST="${V#*.}"; \ + MINOR="${REST%%.*}"; PATCH="${REST#*.}"; PATCH="${PATCH%%-*}"; \ + CGO_ENABLED=0 GOOS=${TARGETOS:-linux} GOARCH=${TARGETARCH} go build \ -ldflags "-X github.com/devzero-inc/zxporter/internal/version.major=${MAJOR} \ -X github.com/devzero-inc/zxporter/internal/version.minor=${MINOR} \ -X github.com/devzero-inc/zxporter/internal/version.patch=${PATCH} \ - -X github.com/devzero-inc/zxporter/internal/version.gitCommit=${COMMIT_HASH} \ + -X github.com/devzero-inc/zxporter/internal/version.gitCommit=${COMMIT} \ -X github.com/devzero-inc/zxporter/internal/version.gitTreeState=${GIT_TREE_STATE} \ - -X github.com/devzero-inc/zxporter/internal/version.buildDate=${BUILD_DATE} \ - -X github.com/devzero-inc/zxporter/internal/version.goVersion=${GO_VERSION}" \ - -a -o manager cmd/main.go + -X github.com/devzero-inc/zxporter/internal/version.buildDate=${BUILD_DATE}" \ + -o manager cmd/main.go # Use distroless as minimal base image to package the manager binary # Refer to https://github.com/GoogleContainerTools/distroless for more details -FROM alpine:3.21.3@sha256:a8560b36e8b8210634f77d9f7f9efd7ffa463e380b75e2e74aff4511df3ef88c AS production +FROM alpine:3.24.2@sha256:294b683cb724975bec92580e1e685676bd4b50bda910ddb8c51d4cabeaec77e6 AS production RUN apk add --no-cache bash kubectl FROM production @@ -63,3 +64,11 @@ USER 65532:65532 COPY ./entrypoint.sh /entrypoint.sh ENTRYPOINT ["/entrypoint.sh", "/manager"] + +# OCI metadata last: per-release values must not invalidate the layers above. +ARG VERSION=v0.0.1 +ARG COMMIT=unknown +LABEL org.opencontainers.image.title="zxporter" \ + org.opencontainers.image.source="https://github.com/devzero-inc/services" \ + org.opencontainers.image.version="${VERSION}" \ + org.opencontainers.image.revision="${COMMIT}" diff --git a/Dockerfile.netmon b/Dockerfile.netmon index d3f88b02..8952694c 100644 --- a/Dockerfile.netmon +++ b/Dockerfile.netmon @@ -1,5 +1,8 @@ # Build the manager binary -FROM golang:1.26 as builder +# The builder runs on the build host's platform and cross-compiles for the +# target (CGO is off), so multi-arch release builds do not run the Go +# toolchain under QEMU emulation. +FROM --platform=$BUILDPLATFORM golang:1.26 AS builder ARG TARGETOS ARG TARGETARCH @@ -9,40 +12,60 @@ COPY go.mod go.mod COPY go.sum go.sum # cache deps before building and copying source so that we don't need to re-download as much # and so that source changes don't invalidate our downloaded layer -RUN go mod download +RUN --mount=type=cache,id=gomod,target=/go/pkg,sharing=shared \ + go mod download # Copy the go source COPY cmd/ cmd/ COPY internal/ internal/ COPY gen/ gen/ -# Version build args -ARG MAJOR=0 -ARG MINOR=0 -ARG PATCH=1 -ARG COMMIT_HASH=unknown -ARG GIT_TREE_STATE=unknown +# Version interface (KUB-151 S7): VERSION is vX.Y.Z[-prerelease], COMMIT the +# short sha, BUILD_DATE RFC 3339. The build step splits VERSION into the +# major/minor/patch the version package reports. The prerelease suffix is +# dropped from patch, as the release workflows did when they passed +# MAJOR/MINOR/PATCH themselves, so a release reports the same version as before. +# GIT_TREE_STATE is not part of the release interface: releases build from a +# clean tag checkout, which is the default; `make docker-build` passes the +# real state for local builds. +ARG VERSION=v0.0.1 +ARG COMMIT=unknown ARG BUILD_DATE=unknown +ARG GIT_TREE_STATE=clean # Build -# GOARCH doesn't have a default value to allow the binary to be built according to the host where the command -# was called. For example, if we call `make docker-build-netmon` in a local env which has the Apple Silicon M1 SO -# the docker BUILDPLATFORM arg will be linux/arm64 when for Apple x86 it will be linux/amd64. Therefore, -# by leaving it empty we can ensure that the container and binary shipped on it will have the same platform. -RUN CGO_ENABLED=0 GOOS=${TARGETOS:-linux} GOARCH=${TARGETARCH} GO_VERSION=$(go version | awk '{print $3}') go build \ +# GOARCH comes from TARGETARCH, which BuildKit sets from --platform (or the +# host's platform when none is given), so the binary always matches the image. +RUN --mount=type=cache,id=gomod,target=/go/pkg,sharing=shared \ + --mount=type=cache,id=gobuild,target=/root/.cache/go-build,sharing=shared \ + set -eu; \ + V="${VERSION#v}"; \ + MAJOR="${V%%.*}"; REST="${V#*.}"; \ + MINOR="${REST%%.*}"; PATCH="${REST#*.}"; PATCH="${PATCH%%-*}"; \ + CGO_ENABLED=0 GOOS=${TARGETOS:-linux} GOARCH=${TARGETARCH} go build \ -ldflags "-X github.com/devzero-inc/zxporter/internal/version.major=${MAJOR} \ -X github.com/devzero-inc/zxporter/internal/version.minor=${MINOR} \ -X github.com/devzero-inc/zxporter/internal/version.patch=${PATCH} \ - -X github.com/devzero-inc/zxporter/internal/version.gitCommit=${COMMIT_HASH} \ + -X github.com/devzero-inc/zxporter/internal/version.gitCommit=${COMMIT} \ -X github.com/devzero-inc/zxporter/internal/version.gitTreeState=${GIT_TREE_STATE} \ - -X github.com/devzero-inc/zxporter/internal/version.buildDate=${BUILD_DATE} \ - -X github.com/devzero-inc/zxporter/internal/version.goVersion=${GO_VERSION}" \ - -a -o zxporter-netmon cmd/zxporter-netmon/main.go + -X github.com/devzero-inc/zxporter/internal/version.buildDate=${BUILD_DATE}" \ + -o zxporter-netmon cmd/zxporter-netmon/main.go # Use distroless as minimal base image. -# Using 'latest' (root) because eBPF loading and Conntrack access require elevated privileges. -FROM gcr.io/distroless/static:latest +# The root variant (not :nonroot) because eBPF loading and Conntrack access +# require elevated privileges. This is the image `static:latest` resolved to +# when it was pinned; the explicit tag lets Dependabot move tag and digest +# together. +FROM gcr.io/distroless/static-debian13@sha256:58133991db06659feaabe0f4e97a35cebf15ef4ea08f8a4c6d2ee5f75e4aa6a0 WORKDIR / COPY --from=builder /workspace/zxporter-netmon . ENTRYPOINT ["/zxporter-netmon"] + +# OCI metadata last: per-release values must not invalidate the layers above. +ARG VERSION=v0.0.1 +ARG COMMIT=unknown +LABEL org.opencontainers.image.title="zxporter-netmon" \ + org.opencontainers.image.source="https://github.com/devzero-inc/services" \ + org.opencontainers.image.version="${VERSION}" \ + org.opencontainers.image.revision="${COMMIT}" diff --git a/Dockerfile.nodemon b/Dockerfile.nodemon index 2c94511f..e8c30b0f 100644 --- a/Dockerfile.nodemon +++ b/Dockerfile.nodemon @@ -1,5 +1,8 @@ # Build the zxporter-nodemon binary -FROM golang:1.26 as builder +# The builder runs on the build host's platform and cross-compiles for the +# target (CGO is off), so multi-arch release builds do not run the Go +# toolchain under QEMU emulation. +FROM --platform=$BUILDPLATFORM golang:1.26 AS builder ARG TARGETOS ARG TARGETARCH @@ -9,36 +12,55 @@ COPY go.mod go.mod COPY go.sum go.sum # cache deps before building and copying source so that we don't need to re-download as much # and so that source changes don't invalidate our downloaded layer -RUN go mod download +RUN --mount=type=cache,id=gomod,target=/go/pkg,sharing=shared \ + go mod download # Copy the go source COPY cmd/ cmd/ COPY internal/ internal/ COPY gen/ gen/ -# Version build args -ARG MAJOR=0 -ARG MINOR=0 -ARG PATCH=1 -ARG COMMIT_HASH=unknown -ARG GIT_TREE_STATE=unknown +# Version interface (KUB-151 S7): VERSION is vX.Y.Z[-prerelease], COMMIT the +# short sha, BUILD_DATE RFC 3339. The build step splits VERSION into the +# major/minor/patch the version package reports. The prerelease suffix is +# dropped from patch, as the release workflows did when they passed +# MAJOR/MINOR/PATCH themselves, so a release reports the same version as before. +# GIT_TREE_STATE is not part of the release interface: releases build from a +# clean tag checkout, which is the default; `make docker-build` passes the +# real state for local builds. +ARG VERSION=v0.0.1 +ARG COMMIT=unknown ARG BUILD_DATE=unknown +ARG GIT_TREE_STATE=clean # Build -RUN CGO_ENABLED=0 GOOS=${TARGETOS:-linux} GOARCH=${TARGETARCH} GO_VERSION=$(go version | awk '{print $3}') go build \ +RUN --mount=type=cache,id=gomod,target=/go/pkg,sharing=shared \ + --mount=type=cache,id=gobuild,target=/root/.cache/go-build,sharing=shared \ + set -eu; \ + V="${VERSION#v}"; \ + MAJOR="${V%%.*}"; REST="${V#*.}"; \ + MINOR="${REST%%.*}"; PATCH="${REST#*.}"; PATCH="${PATCH%%-*}"; \ + CGO_ENABLED=0 GOOS=${TARGETOS:-linux} GOARCH=${TARGETARCH} go build \ -ldflags "-X github.com/devzero-inc/zxporter/internal/version.major=${MAJOR} \ -X github.com/devzero-inc/zxporter/internal/version.minor=${MINOR} \ -X github.com/devzero-inc/zxporter/internal/version.patch=${PATCH} \ - -X github.com/devzero-inc/zxporter/internal/version.gitCommit=${COMMIT_HASH} \ + -X github.com/devzero-inc/zxporter/internal/version.gitCommit=${COMMIT} \ -X github.com/devzero-inc/zxporter/internal/version.gitTreeState=${GIT_TREE_STATE} \ - -X github.com/devzero-inc/zxporter/internal/version.buildDate=${BUILD_DATE} \ - -X github.com/devzero-inc/zxporter/internal/version.goVersion=${GO_VERSION}" \ - -a -o zxporter-nodemon cmd/zxporter-nodemon/main.go + -X github.com/devzero-inc/zxporter/internal/version.buildDate=${BUILD_DATE}" \ + -o zxporter-nodemon cmd/zxporter-nodemon/main.go # Use distroless nonroot as minimal base image. -FROM gcr.io/distroless/static:nonroot +FROM gcr.io/distroless/static:nonroot@sha256:e2e927ec666bae08560abb3c55d0659eceabb657f56b6782ab500a9fc7f555e3 WORKDIR / COPY --from=builder /workspace/zxporter-nodemon . USER 65532:65532 ENTRYPOINT ["/zxporter-nodemon"] + +# OCI metadata last: per-release values must not invalidate the layers above. +ARG VERSION=v0.0.1 +ARG COMMIT=unknown +LABEL org.opencontainers.image.title="zxporter-nodemon" \ + org.opencontainers.image.source="https://github.com/devzero-inc/services" \ + org.opencontainers.image.version="${VERSION}" \ + org.opencontainers.image.revision="${COMMIT}" diff --git a/Makefile b/Makefile index a3bd6c0d..c70c571b 100644 --- a/Makefile +++ b/Makefile @@ -194,16 +194,24 @@ COMMIT_HASH ?= $(shell git rev-parse --short HEAD 2>/dev/null) GIT_TREE_STATE ?= $(if $(shell git status --porcelain),dirty,clean) BUILD_DATE ?= $(shell date -u +'%Y-%m-%dT%H:%M:%SZ') -GO_VERSION = $(shell go version | awk '{print $$3}') - -LDFLAGS += -X github.com/devzero-inc/zxporter/internal/version.Get=*github.com/devzero-inc/zxporter/internal/version.Info -LDFLAGS += -X github.com/devzero-inc/zxporter/internal/version.Get.Major=$(MAJOR) -LDFLAGS += -X github.com/devzero-inc/zxporter/internal/version.Get.Minor=$(MINOR) -LDFLAGS += -X github.com/devzero-inc/zxporter/internal/version.Get.Patch=$(PATCH) -LDFLAGS += -X github.com/devzero-inc/zxporter/internal/version.Get.GitCommit=$(COMMIT_HASH) -LDFLAGS += -X github.com/devzero-inc/zxporter/internal/version.Get.GitTreeState=$(GIT_TREE_STATE) -LDFLAGS += -X github.com/devzero-inc/zxporter/internal/version.Get.BuildDate=$(BUILD_DATE) -LDFLAGS += -X github.com/devzero-inc/zxporter/internal/version.Get.GoVersion=$(GO_VERSION) +# The image version interface (KUB-151 S7) is VERSION / COMMIT / BUILD_DATE. +# VERSION is not the bundle VERSION above, so it gets its own name here. +IMAGE_VERSION := v$(MAJOR).$(MINOR).$(PATCH) +# Shared by every image target, so local manager, netmon and nodemon builds +# all stamp the checkout's version, commit and tree state. +IMAGE_VERSION_ARGS := --build-arg VERSION=$(IMAGE_VERSION) \ + --build-arg COMMIT=$(COMMIT_HASH) \ + --build-arg BUILD_DATE=$(BUILD_DATE) \ + --build-arg GIT_TREE_STATE=$(GIT_TREE_STATE) + +# -X only sets package-level string variables: the lowercase vars in +# internal/version (Go reports its own version via runtime.Version()). +LDFLAGS += -X github.com/devzero-inc/zxporter/internal/version.major=$(MAJOR) +LDFLAGS += -X github.com/devzero-inc/zxporter/internal/version.minor=$(MINOR) +LDFLAGS += -X github.com/devzero-inc/zxporter/internal/version.patch=$(PATCH) +LDFLAGS += -X github.com/devzero-inc/zxporter/internal/version.gitCommit=$(COMMIT_HASH) +LDFLAGS += -X github.com/devzero-inc/zxporter/internal/version.gitTreeState=$(GIT_TREE_STATE) +LDFLAGS += -X github.com/devzero-inc/zxporter/internal/version.buildDate=$(BUILD_DATE) ##@ Build @@ -220,15 +228,9 @@ run: manifests generate fmt vet ## Run a controller from your host. # More info: https://docs.docker.com/develop/develop-images/build_enhancements/ .PHONY: docker-build docker-build: helm ## Build docker image with the manager. - @echo "[INFO] For debug -> $(GO_VERSION), major $(MAJOR), minor $(MINOR), patch $(PATCH)" + @echo "[INFO] version $(IMAGE_VERSION), commit $(COMMIT_HASH), tree $(GIT_TREE_STATE)" $(CONTAINER_TOOL) build --load \ - --build-arg MAJOR=$(MAJOR) \ - --build-arg MINOR=$(MINOR) \ - --build-arg PATCH=$(PATCH) \ - --build-arg COMMIT_HASH=$(COMMIT_HASH) \ - --build-arg GIT_TREE_STATE=$(GIT_TREE_STATE) \ - --build-arg BUILD_DATE=$(BUILD_DATE) \ - --build-arg GO_VERSION="$(GO_VERSION)" \ + $(IMAGE_VERSION_ARGS) \ -t ${IMG} . .PHONY: docker-push @@ -256,7 +258,7 @@ IMG_NETMON ?= ttl.sh/zxporter-netmon:latest .PHONY: docker-build-netmon docker-build-netmon: ## Build docker image for zxporter-netmon - $(CONTAINER_TOOL) buildx build $(BUILD_ARGS) -t ${IMG_NETMON} -f Dockerfile.netmon . + $(CONTAINER_TOOL) buildx build $(IMAGE_VERSION_ARGS) $(BUILD_ARGS) -t ${IMG_NETMON} -f Dockerfile.netmon . .PHONY: docker-push-netmon docker-push-netmon: ## Push docker image for zxporter-netmon @@ -267,7 +269,7 @@ IMG_NODEMON ?= ttl.sh/zxporter-nodemon:latest .PHONY: docker-build-nodemon docker-build-nodemon: ## Build docker image for zxporter-nodemon - $(CONTAINER_TOOL) buildx build $(BUILD_ARGS) -t ${IMG_NODEMON} -f Dockerfile.nodemon . + $(CONTAINER_TOOL) buildx build $(IMAGE_VERSION_ARGS) $(BUILD_ARGS) -t ${IMG_NODEMON} -f Dockerfile.nodemon . .PHONY: docker-push-nodemon docker-push-nodemon: ## Push docker image for zxporter-nodemon @@ -282,13 +284,12 @@ docker-push-nodemon: ## Push docker image for zxporter-nodemon PLATFORMS ?= linux/arm64,linux/amd64,linux/s390x,linux/ppc64le .PHONY: docker-buildx docker-buildx: ## Build and push docker image for the manager for cross-platform support - # copy existing Dockerfile and insert --platform=${BUILDPLATFORM} into Dockerfile.cross, and preserve the original Dockerfile - sed -e '1 s/\(^FROM\)/FROM --platform=\$$\{BUILDPLATFORM\}/; t' -e ' 1,// s//FROM --platform=\$$\{BUILDPLATFORM\}/' Dockerfile > Dockerfile.cross + # The Dockerfile's builder already runs on $$BUILDPLATFORM and cross-compiles, + # so it is built as is (no Dockerfile.cross rewrite). - $(CONTAINER_TOOL) buildx create --name zxporter-builder $(CONTAINER_TOOL) buildx use zxporter-builder - - $(CONTAINER_TOOL) buildx build --push --platform=$(PLATFORMS) --tag ${IMG} -f Dockerfile.cross . + - $(CONTAINER_TOOL) buildx build --push --platform=$(PLATFORMS) $(IMAGE_VERSION_ARGS) --tag ${IMG} -f Dockerfile . - $(CONTAINER_TOOL) buildx rm zxporter-builder - rm Dockerfile.cross .PHONY: final-installer # public.ecr.aws/devzeroinc only mirrors versioned release tags, never :latest. @@ -344,7 +345,7 @@ installer-without-configmap: $(DIST_INSTALLER_UPDATER_BUNDLE) > $(DIST_INSTALLER_UPDATER_BUNDLE).tmp && mv $(DIST_INSTALLER_UPDATER_BUNDLE).tmp $(DIST_INSTALLER_UPDATER_BUNDLE) .PHONY: build-installer -build-installer: manifests generate kustomize yq helm ## Generate a consolidated YAML with deployment. +build-installer: manifests generate kustomize yq helm helm-deps ## Generate a consolidated YAML with deployment. @mkdir -p $(DIST_DIR) @echo "[INFO] Generating installer bundle..." @@ -472,8 +473,18 @@ build-chart: build-installer ## Generate a consolidated helm chart from the inst HELM_CHART_DIR := helm-chart/zxporter HELM_CHART_PACKAGE_DIR := helm-chart/packages +# Every chart vendors devzero-common (and zxporter vendors zxporter-nodemon) into +# its gitignored charts/ directory, and helm refuses to render a chart whose +# dependencies are missing. nodemon builds first so the copy zxporter packages +# carries its own devzero-common. +.PHONY: helm-deps +helm-deps: helm ## Vendor each chart's dependencies into its charts/ directory + @for chart in zxporter-nodemon zxporter-netmon zxporter; do \ + $(HELM) dependency build helm-chart/$$chart > /dev/null || exit 1; \ + done + .PHONY: helm-chart-build -helm-chart-build: helm ## Build and package the Helm chart +helm-chart-build: helm helm-deps ## Build and package the Helm chart @echo "[INFO] Building Helm chart..." @mkdir -p $(HELM_CHART_PACKAGE_DIR) @@ -486,12 +497,12 @@ helm-chart-build: helm ## Build and package the Helm chart @echo "[INFO] Helm chart packaged successfully" .PHONY: helm-chart-lint -helm-chart-lint: helm ## Lint the Helm chart +helm-chart-lint: helm helm-deps ## Lint the Helm chart @echo "[INFO] Linting Helm chart..." @$(HELM) lint $(HELM_CHART_DIR) .PHONY: helm-chart-template -helm-chart-template: helm ## Generate Kubernetes manifests from the Helm chart +helm-chart-template: helm helm-deps ## Generate Kubernetes manifests from the Helm chart @echo "[INFO] Generating manifests from Helm chart..." @$(HELM) template zxporter $(HELM_CHART_DIR) \ --namespace devzero-system \ @@ -596,7 +607,11 @@ HELMIFY ?= helmify GO_MOD_TOOLCHAIN ?= go$(shell awk '/^go [0-9]/{print $$2; exit}' go.mod) KUSTOMIZE_VERSION ?= v5.4.3 CONTROLLER_TOOLS_VERSION ?= v0.16.5 -ENVTEST_VERSION ?= release-0.19 +# setup-envtest from the release branch of the controller-runtime in go.mod, +# the same derivation operators/tunr/Makefile uses, so the two operators no +# longer pin the tool separately (KUB-154). The tool only fetches the envtest +# assets; the Kubernetes version under test is ENVTEST_K8S_VERSION above. +ENVTEST_VERSION ?= $(shell go list -m -f "{{ .Version }}" sigs.k8s.io/controller-runtime | awk -F'[v.]' '{printf "release-%d.%d", $$2, $$3}') GOLANGCI_LINT_VERSION ?= v1.62.2 HELM_VERSION ?= v3.14.2 YQ_VERSION ?= v4.40.5 @@ -612,8 +627,11 @@ $(CONTROLLER_GEN): $(LOCALBIN) $(call go-install-tool,$(CONTROLLER_GEN),sigs.k8s.io/controller-tools/cmd/controller-gen,$(CONTROLLER_TOOLS_VERSION)) .PHONY: envtest -envtest: $(ENVTEST) ## Download setup-envtest locally if necessary. -$(ENVTEST): $(LOCALBIN) +# Recipe on the phony target, not on the bin/setup-envtest file: once that +# symlink exists a file target never reruns, so a controller-runtime bump would +# keep the old tool. go-install-tool skips the install when the versioned +# binary is already there and just repoints the symlink. +envtest: $(LOCALBIN) ## Download setup-envtest locally if necessary. $(call go-install-tool,$(ENVTEST),sigs.k8s.io/controller-runtime/tools/setup-envtest,$(ENVTEST_VERSION)) .PHONY: golangci-lint @@ -736,48 +754,65 @@ catalog-build: opm ## Build a catalog image. catalog-push: ## Push a catalog image. $(MAKE) docker-push IMG=$(CATALOG_IMG) -# DAKR_* files are related to generating a gRPC client to send resource metadata to Dakr. +# DAKR_DIR is where build-installer syncs the dakr installer bundles (see above). DAKR_DIR ?= ../services/dakr -DAKR_BUF_GEN_FILE ?= buf.gen.yaml -DAKR_BUF_GEN ?= $(DAKR_DIR)/$(DAKR_BUF_GEN_FILE) -DAKR_METRICS_COLLECTOR_PROTO_FILE ?= metrics_collector.proto -DAKR_METRICS_COLLECTOR_PROTO ?= $(DAKR_DIR)/proto/api/v1/$(DAKR_METRICS_COLLECTOR_PROTO_FILE) -DAKR_MPA_PROTO_FILE ?= mpa.proto -DAKR_MPA_PROTO ?= internal/proto/$(DAKR_MPA_PROTO_FILE) -DAKR_CLUSTER_PROTO_FILE ?= cluster.proto -DAKR_CLUSTER_PROTO ?= $(DAKR_DIR)/proto/api/v1/$(DAKR_CLUSTER_PROTO_FILE) -DAKR_OPERATOR_HEALTH_PROTO_FILE ?= operator_health.proto -DAKR_OPERATOR_HEALTH_PROTO ?= $(DAKR_DIR)/proto/api/v1/$(DAKR_OPERATOR_HEALTH_PROTO_FILE) + +# gen/api/v1 holds two kinds of bindings: +# - mpa.proto (MpaService, which tunr consumes) is zxporter's own. Its only +# source is proto/api/v1/mpa.proto; `generate-mpa-proto` runs buf on it. +# - Every other file is dakr's. dakr/proto is their source and dakr/gen holds +# the bindings generated from it. zxporter keeps a copy of the ones it needs +# because the public devzero-inc/zxporter mirror has no dakr tree to import. +# The copy is byte-for-byte, except that the connect files import this +# module's gen/api/v1 instead of dakr's. tools/zxporterprotocopy, in the +# monorepo's root module, fails CI when the copy differs from dakr/gen. +# DAKR_GEN_PROTOS is the manifest of copied protos; that test reads it. +# copy-dakr-gen only works inside the services monorepo. +DAKR_GEN_DIR ?= ../../dakr/gen/api/v1 +DAKR_GEN_PROTOS := cluster common instance k8s metrics_collector operator operator_health # BUF_VERSION and BUF_BINARY_NAME are to generate a Dakr protobuf/gRPC client. -BUF_VERSION := 1.31.0 +# buf.yaml / buf.gen.yaml are v2 configs, which need buf >= 1.32; kept equal to +# dakr's pin (dakr/Justfile, dakr/Makefile). +BUF_VERSION := 1.56.0 +BUF_MIN_VERSION := 1.32.0 BUF_BINARY_NAME := buf -# Install buf +# Install buf. An existing buf older than BUF_MIN_VERSION (e.g. the 1.31.0 this +# target used to pin) cannot read the v2 configs, so it is replaced; a newer one +# on PATH is left alone. .PHONY: install-buf -install-buf: ## Install buf if not already installed - @if ! command -v $(BUF_BINARY_NAME) >/dev/null 2>&1; then \ - echo "Installing $(BUF_BINARY_NAME)..."; \ +install-buf: ## Install buf if missing or too old for the v2 configs + @have="$$($(BUF_BINARY_NAME) --version 2>/dev/null)"; \ + if [ -z "$$have" ] || ! awk -v h="$$have" -v m="$(BUF_MIN_VERSION)" 'BEGIN { split(h, a, "."); split(m, b, "."); for (i = 1; i <= 3; i++) if (a[i] + 0 != b[i] + 0) exit !(a[i] + 0 > b[i] + 0); exit 0 }'; then \ + echo "Installing $(BUF_BINARY_NAME) $(BUF_VERSION) (found: $${have:-none})..."; \ curl -sSL "https://github.com/bufbuild/buf/releases/download/v${BUF_VERSION}/${BUF_BINARY_NAME}-$(shell uname -s)-$(shell uname -m)" -o "/usr/local/bin/${BUF_BINARY_NAME}"; \ chmod +x "/usr/local/bin/${BUF_BINARY_NAME}"; \ else \ - echo "$(BUF_BINARY_NAME) is already installed."; \ + echo "$(BUF_BINARY_NAME) $$have is already installed."; \ fi -# (for local dev) Get metadata to generate a Dakr client. .PHONY: generate-proto -generate-proto: install-buf ## Fetch latest Dakr protobuf - @PROTO_DIR="$(PWD)/proto"; \ - echo "Cleaning $$PROTO_DIR..."; \ - rm -rf "$$PROTO_DIR"/*; \ - rm -rf "$(PWD)/gen"; \ - mkdir -p "$$PROTO_DIR"; \ - mkdir -p "$$PROTO_DIR/api/v1"; \ - cp "$(DAKR_MPA_PROTO)" "$$PROTO_DIR/api/v1"; \ - find "$$PROTO_DIR" -type f -name "*.yaml" -exec perl -pi -e 's|github.com/devzero-inc/services/dakr/gen|github.com/devzero-inc/zxporter/gen|g' {} +; \ - buf build "$(DAKR_DIR)" --path "$(DAKR_METRICS_COLLECTOR_PROTO)" --path "$(DAKR_CLUSTER_PROTO)" --path "$(DAKR_OPERATOR_HEALTH_PROTO)" -o "$$PROTO_DIR"/dakr_proto_descriptor.bin; \ - buf generate --include-imports "$$PROTO_DIR"/dakr_proto_descriptor.bin; - buf generate --verbose --include-imports --timeout=5m . +generate-proto: copy-dakr-gen generate-mpa-proto ## Refresh gen/api/v1: copy dakr's bindings, generate mpa.proto + +.PHONY: generate-mpa-proto +generate-mpa-proto: install-buf ## Generate zxporter's own mpa.proto into gen/api/v1 + rm -f gen/api/v1/mpa.pb.go gen/api/v1/mpa_grpc.pb.go gen/api/v1/apiv1connect/mpa.connect.go + buf generate --verbose --timeout=5m . + +.PHONY: copy-dakr-gen +copy-dakr-gen: ## Copy dakr's generated bindings for the dakr protos zxporter speaks (monorepo only) + @test -d "$(DAKR_GEN_DIR)" || { echo "$(DAKR_GEN_DIR) not found: dakr's bindings are copied from the services monorepo's dakr/gen (set DAKR_GEN_DIR)"; exit 1; } + @set -e; for p in $(DAKR_GEN_PROTOS); do \ + rm -f "gen/api/v1/$$p.pb.go" "gen/api/v1/$${p}_grpc.pb.go" "gen/api/v1/apiv1connect/$$p.connect.go"; \ + cp "$(DAKR_GEN_DIR)/$$p.pb.go" gen/api/v1/; \ + if [ -f "$(DAKR_GEN_DIR)/$${p}_grpc.pb.go" ]; then cp "$(DAKR_GEN_DIR)/$${p}_grpc.pb.go" gen/api/v1/; fi; \ + if [ -f "$(DAKR_GEN_DIR)/apiv1connect/$$p.connect.go" ]; then \ + sed 's|github.com/devzero-inc/services/dakr/gen/api/v1"|github.com/devzero-inc/zxporter/gen/api/v1"|' \ + "$(DAKR_GEN_DIR)/apiv1connect/$$p.connect.go" > "gen/api/v1/apiv1connect/$$p.connect.go"; \ + fi; \ + done; \ + echo "Copied dakr's bindings for: $(DAKR_GEN_PROTOS)" ##@ Verification diff --git a/README.md b/README.md index f341871a..a8aa7dab 100644 --- a/README.md +++ b/README.md @@ -139,7 +139,12 @@ ZXporter can also be deployed using Helm, which provides a more flexible and cus #### Quick Install +The charts vendor their dependencies (the shared `devzero-common` library, and +`zxporter-nodemon` for `zxporter`) into a `charts/` directory that is not +committed, so build it once in a fresh checkout: + ```sh +make helm-deps helm install zxporter ./helm-chart/zxporter --namespace devzero-system --create-namespace # With Makefile @@ -186,6 +191,57 @@ helm upgrade zxporter ./helm-chart/zxporter helm uninstall zxporter ``` +## Site labels and annotations (admission policy) + +Clusters that run an admission policy engine often require site-specific +metadata on every object before they will admit it. A Gatekeeper +`K8sRequiredLabels` constraint, for example, denies the chart's objects outright +and the install fails. All three charts (`zxporter`, `zxporter-nodemon`, +`zxporter-netmon`) accept four values for this, each defaulting to `{}`: + +| Value | Applies to | +|---|---| +| `commonLabels` | every object **and** every pod template | +| `commonAnnotations` | every object **and** every pod template | +| `podLabels` | pod templates only, on top of `commonLabels` | +| `podAnnotations` | pod templates only, on top of `commonAnnotations` | + +```bash +helm upgrade --install zxporter ./helm-chart/zxporter \ + --set global.commonLabels.example\\.com/cost-center=platform \ + --set global.commonLabels.example\\.com/tier=infra +``` + +Use `global.commonLabels` to cover the whole release. Helm values are +chart-scoped, so a chart-local `commonLabels` set on `zxporter` applies only to +that chart and does **not** reach the bundled `zxporter-nodemon` subchart; +`global.*` does, and a chart-local value still wins on a key collision. + +These reach the pod template as well as the owning object on purpose: +Gatekeeper's `expand-workloads` evaluates constraints against the Pod a workload +*would* create, so metadata on the DaemonSet alone is still denied. + +Keys a chart owns always win and are dropped from these values, so a collision +can neither rewrite chart state nor emit a duplicate YAML key: +`app.kubernetes.io/*`, `helm.sh/*`, `app`, `control-plane`, config checksums, +`kubectl.kubernetes.io/default-container`, and the `ignore-check.kube-linter.io/*` +and `devzero.io/gpu-runtime-*` annotations. Selector labels are never modified, +so setting these on an existing release is a safe upgrade — with all four unset +each chart renders byte-identically to before they existed. + +Independently of these values, every object (not pod templates) also carries +`app.kubernetes.io/part-of: dz-platform` and `devzero.io/component: `, the +labels all DevZero charts share. Setting either key in `commonLabels` or +`global.commonLabels` overrides it. + +**Not covered:** the curl/`dist` installer path. `dist/install.yaml` is built +from `config/` with kustomize and has no values file, so a cluster with such a +policy has to use the Helm charts. + +The contract is enforced by `helm-chart/internal/charttest`, which sweeps every +rendered object rather than a fixed sample — a template that starts emitting a +new label fails CI until that key is added to the chart's drop list. + ## Configuration The operator is configured through the `CollectionPolicy` Custom Resource. Key configuration options include: diff --git a/RELEASE.md b/RELEASE.md index 3a8f0d59..31ac4a37 100644 --- a/RELEASE.md +++ b/RELEASE.md @@ -1,30 +1,30 @@ # Releasing zxporter -Releases are **one command**; CI does the rest. - -```bash -just release zxporter vX.Y.Z # cut release vX.Y.Z -just release zxporter vX.Y.Z dry # preview only -just tag-list # recent release/chart tags -``` - -Prereq: `gh` CLI authenticated with access to `devzero-inc/zxporter`. - -`just release` validates `vX.Y.Z` and dispatches the **Release** workflow -(`.github/workflows/release.yml`), which: - -1. Bumps chart/image versions, regenerates `Chart.lock` + `dist/`, and tags - `vX.Y.Z` on the bumped commit. -2. Builds & pushes the `zxporter`, `zxporter-nodemon`, `zxporter-netmon` images - and publishes the three OCI Helm charts at `vX.Y.Z`. -3. Creates the GitHub Release and triggers the `devzero-inc/services` metadata - update. - -Watch: - -## Merge the two PRs - -The release opens two PRs — **approve and merge both** to finish: - -1. **zxporter** – `release/bump-vX.Y.Z` (version bump). -2. **../services** – metadata update opened by the metadata retriever workflow. +zxporter is developed and released from DevZero's internal monorepo, where +this directory lives at `operators/zxporter/`. The public +[devzero-inc/zxporter](https://github.com/devzero-inc/zxporter) repository is +a published mirror: each release pushes this directory's tree to it as branch +`release/vX.Y.Z`, tags that commit `vX.Y.Z`, and opens a PR to bring its `main` +up to date. + +## Consuming a release + +Each release `vX.Y.Z` publishes: + +- images `zxporter`, `zxporter-nodemon` and `zxporter-netmon`, tag `vX.Y.Z`, on + `docker.io/devzeroinc/` and `public.ecr.aws/devzeroinc/` +- Helm charts `zxporter` (bundles `zxporter-nodemon`), `zxporter-nodemon` and + `zxporter-netmon`, version `X.Y.Z`, at `oci://registry-1.docker.io/devzeroinc` +- the `vX.Y.Z` tag on the public mirror, whose `helm-chart/` and `dist/` carry + that version (pin it as an ArgoCD `targetRevision`) + +## Cutting a release + +From the monorepo root: `just release zxporter [vX.Y.Z]`. The full procedure, +what the release workflow does, how the version reaches DevZero's backend, and +recovery and rollback are in the root `RELEASE.md` of the monorepo (section +"zxporter"). + +`.github/workflows/release.yml` and the `release` recipe in `justfile` in this +directory are the pre-monorepo release flow. They are still present in the +mirror but are not how zxporter is released; do not run them. diff --git a/api/v1/collectionpolicy_types.go b/api/v1/collectionpolicy_types.go index 95cc2138..e9b515af 100644 --- a/api/v1/collectionpolicy_types.go +++ b/api/v1/collectionpolicy_types.go @@ -106,6 +106,37 @@ type Exclusions struct { // CloudNativePG Exclusions ExcludedCNPGClusters []ExcludedCNPGCluster `json:"excludedCNPGClusters,omitempty"` + + // Argo CD Exclusions + ExcludedArgoCDApplications []ExcludedArgoCDApplication `json:"excludedArgoCDApplications,omitempty"` + ExcludedArgoCDApplicationSets []ExcludedArgoCDApplicationSet `json:"excludedArgoCDApplicationSets,omitempty"` + + // FluxCD Exclusions -- full-explorer kinds + ExcludedFluxGitRepositories []ExcludedFluxGitRepository `json:"excludedFluxGitRepositories,omitempty"` + ExcludedFluxOCIRepositories []ExcludedFluxOCIRepository `json:"excludedFluxOCIRepositories,omitempty"` + ExcludedFluxHelmRepositories []ExcludedFluxHelmRepository `json:"excludedFluxHelmRepositories,omitempty"` + ExcludedFluxHelmCharts []ExcludedFluxHelmChart `json:"excludedFluxHelmCharts,omitempty"` + ExcludedFluxBuckets []ExcludedFluxBucket `json:"excludedFluxBuckets,omitempty"` + ExcludedFluxKustomizations []ExcludedFluxKustomization `json:"excludedFluxKustomizations,omitempty"` + ExcludedFluxHelmReleases []ExcludedFluxHelmRelease `json:"excludedFluxHelmReleases,omitempty"` + + // FluxCD Exclusions -- collector-only kinds + ExcludedFluxExternalArtifacts []ExcludedFluxExternalArtifact `json:"excludedFluxExternalArtifacts,omitempty"` + ExcludedFluxAlerts []ExcludedFluxAlert `json:"excludedFluxAlerts,omitempty"` + ExcludedFluxProviders []ExcludedFluxProvider `json:"excludedFluxProviders,omitempty"` + ExcludedFluxReceivers []ExcludedFluxReceiver `json:"excludedFluxReceivers,omitempty"` + ExcludedFluxInstances []ExcludedFluxInstance `json:"excludedFluxInstances,omitempty"` + ExcludedFluxReports []ExcludedFluxReport `json:"excludedFluxReports,omitempty"` + ExcludedFluxResourceSetInputProviders []ExcludedFluxResourceSetInputProvider `json:"excludedFluxResourceSetInputProviders,omitempty"` + ExcludedFluxResourceSets []ExcludedFluxResourceSet `json:"excludedFluxResourceSets,omitempty"` + ExcludedFluxImageRepositories []ExcludedFluxImageRepository `json:"excludedFluxImageRepositories,omitempty"` + ExcludedFluxImagePolicies []ExcludedFluxImagePolicy `json:"excludedFluxImagePolicies,omitempty"` + ExcludedFluxImageUpdateAutomations []ExcludedFluxImageUpdateAutomation `json:"excludedFluxImageUpdateAutomations,omitempty"` + + // Flagger Exclusions + ExcludedFlaggerCanaries []ExcludedFlaggerCanary `json:"excludedFlaggerCanaries,omitempty"` + ExcludedFlaggerMetricTemplates []ExcludedFlaggerMetricTemplate `json:"excludedFlaggerMetricTemplates,omitempty"` + ExcludedFlaggerAlertProviders []ExcludedFlaggerAlertProvider `json:"excludedFlaggerAlertProviders,omitempty"` } // Common exclusion patterns @@ -434,6 +465,150 @@ type ExcludedCNPGCluster struct { Name string `json:"name"` } +// ExcludedArgoCDApplication identifies an Argo CD Application to exclude from collection +type ExcludedArgoCDApplication struct { + // Namespace is the Argo CD Application's namespace + Namespace string `json:"namespace"` + + // Name is the Argo CD Application's name + Name string `json:"name"` +} + +// ExcludedArgoCDApplicationSet identifies an Argo CD ApplicationSet to exclude from collection +type ExcludedArgoCDApplicationSet struct { + // Namespace is the Argo CD ApplicationSet's namespace + Namespace string `json:"namespace"` + + // Name is the Argo CD ApplicationSet's name + Name string `json:"name"` +} + +// ExcludedFluxGitRepository identifies a FluxCD GitRepository to exclude from collection +type ExcludedFluxGitRepository struct { + Namespace string `json:"namespace"` + Name string `json:"name"` +} + +// ExcludedFluxOCIRepository identifies a FluxCD OCIRepository to exclude from collection +type ExcludedFluxOCIRepository struct { + Namespace string `json:"namespace"` + Name string `json:"name"` +} + +// ExcludedFluxHelmRepository identifies a FluxCD HelmRepository to exclude from collection +type ExcludedFluxHelmRepository struct { + Namespace string `json:"namespace"` + Name string `json:"name"` +} + +// ExcludedFluxHelmChart identifies a FluxCD HelmChart to exclude from collection +type ExcludedFluxHelmChart struct { + Namespace string `json:"namespace"` + Name string `json:"name"` +} + +// ExcludedFluxBucket identifies a FluxCD Bucket to exclude from collection +type ExcludedFluxBucket struct { + Namespace string `json:"namespace"` + Name string `json:"name"` +} + +// ExcludedFluxKustomization identifies a FluxCD Kustomization to exclude from collection +type ExcludedFluxKustomization struct { + Namespace string `json:"namespace"` + Name string `json:"name"` +} + +// ExcludedFluxHelmRelease identifies a FluxCD HelmRelease to exclude from collection +type ExcludedFluxHelmRelease struct { + Namespace string `json:"namespace"` + Name string `json:"name"` +} + +// ExcludedFluxExternalArtifact identifies a FluxCD ExternalArtifact to exclude from collection +type ExcludedFluxExternalArtifact struct { + Namespace string `json:"namespace"` + Name string `json:"name"` +} + +// ExcludedFluxAlert identifies a FluxCD Alert to exclude from collection +type ExcludedFluxAlert struct { + Namespace string `json:"namespace"` + Name string `json:"name"` +} + +// ExcludedFluxProvider identifies a FluxCD Provider to exclude from collection +type ExcludedFluxProvider struct { + Namespace string `json:"namespace"` + Name string `json:"name"` +} + +// ExcludedFluxReceiver identifies a FluxCD Receiver to exclude from collection +type ExcludedFluxReceiver struct { + Namespace string `json:"namespace"` + Name string `json:"name"` +} + +// ExcludedFluxInstance identifies a Flux Operator FluxInstance to exclude from collection +type ExcludedFluxInstance struct { + Namespace string `json:"namespace"` + Name string `json:"name"` +} + +// ExcludedFluxReport identifies a Flux Operator FluxReport to exclude from collection +type ExcludedFluxReport struct { + Namespace string `json:"namespace"` + Name string `json:"name"` +} + +// ExcludedFluxResourceSetInputProvider identifies a Flux Operator ResourceSetInputProvider to exclude from collection +type ExcludedFluxResourceSetInputProvider struct { + Namespace string `json:"namespace"` + Name string `json:"name"` +} + +// ExcludedFluxResourceSet identifies a Flux Operator ResourceSet to exclude from collection +type ExcludedFluxResourceSet struct { + Namespace string `json:"namespace"` + Name string `json:"name"` +} + +// ExcludedFluxImageRepository identifies a FluxCD ImageRepository to exclude from collection +type ExcludedFluxImageRepository struct { + Namespace string `json:"namespace"` + Name string `json:"name"` +} + +// ExcludedFluxImagePolicy identifies a FluxCD ImagePolicy to exclude from collection +type ExcludedFluxImagePolicy struct { + Namespace string `json:"namespace"` + Name string `json:"name"` +} + +// ExcludedFluxImageUpdateAutomation identifies a FluxCD ImageUpdateAutomation to exclude from collection +type ExcludedFluxImageUpdateAutomation struct { + Namespace string `json:"namespace"` + Name string `json:"name"` +} + +// ExcludedFlaggerCanary identifies a Flagger Canary to exclude from collection +type ExcludedFlaggerCanary struct { + Namespace string `json:"namespace"` + Name string `json:"name"` +} + +// ExcludedFlaggerMetricTemplate identifies a Flagger MetricTemplate to exclude from collection +type ExcludedFlaggerMetricTemplate struct { + Namespace string `json:"namespace"` + Name string `json:"name"` +} + +// ExcludedFlaggerAlertProvider identifies a Flagger AlertProvider to exclude from collection +type ExcludedFlaggerAlertProvider struct { + Namespace string `json:"namespace"` + Name string `json:"name"` +} + // Policies defines collection behavior type Policies struct { // KubeContextName is the name of the current context being used to apply the installation yaml @@ -504,15 +679,33 @@ type Policies struct { // +optional KubeletFallbackTimeout string `json:"kubeletFallbackTimeout,omitempty"` + // SchedulerMetricsScrapeInterval bounds how often the SchedulerMetrics + // collector scrapes dz-scheduler's /metrics endpoint (defaults to 60s -- + // see collector.DefaultSchedulerMetricsScrapeInterval's doc comment for + // why). A Go duration string (e.g. "60s", "30s"). + // +optional + SchedulerMetricsScrapeInterval string `json:"schedulerMetricsScrapeInterval,omitempty"` + + // KEDAMetricsEndpoint is the full URL of the KEDA operator /metrics endpoint + // that the KedaScalerMetrics collector scrapes. Defaults to + // collector.DefaultKedaMetricsEndpoint (KEDA in the devzero-system namespace). + // Override only when KEDA runs in a different namespace or on a different port. + // +optional + KEDAMetricsEndpoint string `json:"kedaMetricsEndpoint,omitempty"` + // ClusterSnapshotInterval is how often to take cluster snapshots (defaults to 3h) ClusterSnapshotInterval string `json:"clusterSnapshotInterval,omitempty"` // WatchedCRDs is a list of custom resource definitions to explicitly watch WatchedCRDs []string `json:"watchedCRDs,omitempty"` - // DisabledCollectors is a list of collector types to completely disable - // Valid values include: "pod", "deployment", "statefulset", "daemonset", "service", - // "configmap", "secret", "node", "event", etc. + // DisabledCollectors is a list of collector types to completely disable, by their + // canonical names, e.g. "pod", "deployment", "stateful_set", "daemon_set", "service", + // "node", "event", "persistent_volume", "persistent_volume_claim", + // "custom_resource_definition", "keda_scaled_job", "keda_scaled_object". + // The legacy spellings "statefulset", "daemonset", "persistentvolume", + // "persistentvolumeclaim", "crd", "scaledjob" and "scaledobject" are still accepted + // and mean the same collectors. DisabledCollectors []string `json:"disabledCollectors,omitempty"` // MigPartedConfigMapName is the name of the NVIDIA mig-parted ConfigMap to diff --git a/api/v1/zz_generated.deepcopy.go b/api/v1/zz_generated.deepcopy.go index 110630df..9d54fdc0 100644 --- a/api/v1/zz_generated.deepcopy.go +++ b/api/v1/zz_generated.deepcopy.go @@ -131,6 +131,36 @@ func (in *CollectionPolicyStatus) DeepCopy() *CollectionPolicyStatus { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ExcludedArgoCDApplication) DeepCopyInto(out *ExcludedArgoCDApplication) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ExcludedArgoCDApplication. +func (in *ExcludedArgoCDApplication) DeepCopy() *ExcludedArgoCDApplication { + if in == nil { + return nil + } + out := new(ExcludedArgoCDApplication) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ExcludedArgoCDApplicationSet) DeepCopyInto(out *ExcludedArgoCDApplicationSet) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ExcludedArgoCDApplicationSet. +func (in *ExcludedArgoCDApplicationSet) DeepCopy() *ExcludedArgoCDApplicationSet { + if in == nil { + return nil + } + out := new(ExcludedArgoCDApplicationSet) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ExcludedCNPGCluster) DeepCopyInto(out *ExcludedCNPGCluster) { *out = *in @@ -266,6 +296,321 @@ func (in *ExcludedEvent) DeepCopy() *ExcludedEvent { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ExcludedFlaggerAlertProvider) DeepCopyInto(out *ExcludedFlaggerAlertProvider) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ExcludedFlaggerAlertProvider. +func (in *ExcludedFlaggerAlertProvider) DeepCopy() *ExcludedFlaggerAlertProvider { + if in == nil { + return nil + } + out := new(ExcludedFlaggerAlertProvider) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ExcludedFlaggerCanary) DeepCopyInto(out *ExcludedFlaggerCanary) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ExcludedFlaggerCanary. +func (in *ExcludedFlaggerCanary) DeepCopy() *ExcludedFlaggerCanary { + if in == nil { + return nil + } + out := new(ExcludedFlaggerCanary) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ExcludedFlaggerMetricTemplate) DeepCopyInto(out *ExcludedFlaggerMetricTemplate) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ExcludedFlaggerMetricTemplate. +func (in *ExcludedFlaggerMetricTemplate) DeepCopy() *ExcludedFlaggerMetricTemplate { + if in == nil { + return nil + } + out := new(ExcludedFlaggerMetricTemplate) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ExcludedFluxAlert) DeepCopyInto(out *ExcludedFluxAlert) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ExcludedFluxAlert. +func (in *ExcludedFluxAlert) DeepCopy() *ExcludedFluxAlert { + if in == nil { + return nil + } + out := new(ExcludedFluxAlert) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ExcludedFluxBucket) DeepCopyInto(out *ExcludedFluxBucket) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ExcludedFluxBucket. +func (in *ExcludedFluxBucket) DeepCopy() *ExcludedFluxBucket { + if in == nil { + return nil + } + out := new(ExcludedFluxBucket) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ExcludedFluxExternalArtifact) DeepCopyInto(out *ExcludedFluxExternalArtifact) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ExcludedFluxExternalArtifact. +func (in *ExcludedFluxExternalArtifact) DeepCopy() *ExcludedFluxExternalArtifact { + if in == nil { + return nil + } + out := new(ExcludedFluxExternalArtifact) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ExcludedFluxGitRepository) DeepCopyInto(out *ExcludedFluxGitRepository) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ExcludedFluxGitRepository. +func (in *ExcludedFluxGitRepository) DeepCopy() *ExcludedFluxGitRepository { + if in == nil { + return nil + } + out := new(ExcludedFluxGitRepository) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ExcludedFluxHelmChart) DeepCopyInto(out *ExcludedFluxHelmChart) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ExcludedFluxHelmChart. +func (in *ExcludedFluxHelmChart) DeepCopy() *ExcludedFluxHelmChart { + if in == nil { + return nil + } + out := new(ExcludedFluxHelmChart) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ExcludedFluxHelmRelease) DeepCopyInto(out *ExcludedFluxHelmRelease) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ExcludedFluxHelmRelease. +func (in *ExcludedFluxHelmRelease) DeepCopy() *ExcludedFluxHelmRelease { + if in == nil { + return nil + } + out := new(ExcludedFluxHelmRelease) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ExcludedFluxHelmRepository) DeepCopyInto(out *ExcludedFluxHelmRepository) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ExcludedFluxHelmRepository. +func (in *ExcludedFluxHelmRepository) DeepCopy() *ExcludedFluxHelmRepository { + if in == nil { + return nil + } + out := new(ExcludedFluxHelmRepository) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ExcludedFluxImagePolicy) DeepCopyInto(out *ExcludedFluxImagePolicy) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ExcludedFluxImagePolicy. +func (in *ExcludedFluxImagePolicy) DeepCopy() *ExcludedFluxImagePolicy { + if in == nil { + return nil + } + out := new(ExcludedFluxImagePolicy) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ExcludedFluxImageRepository) DeepCopyInto(out *ExcludedFluxImageRepository) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ExcludedFluxImageRepository. +func (in *ExcludedFluxImageRepository) DeepCopy() *ExcludedFluxImageRepository { + if in == nil { + return nil + } + out := new(ExcludedFluxImageRepository) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ExcludedFluxImageUpdateAutomation) DeepCopyInto(out *ExcludedFluxImageUpdateAutomation) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ExcludedFluxImageUpdateAutomation. +func (in *ExcludedFluxImageUpdateAutomation) DeepCopy() *ExcludedFluxImageUpdateAutomation { + if in == nil { + return nil + } + out := new(ExcludedFluxImageUpdateAutomation) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ExcludedFluxInstance) DeepCopyInto(out *ExcludedFluxInstance) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ExcludedFluxInstance. +func (in *ExcludedFluxInstance) DeepCopy() *ExcludedFluxInstance { + if in == nil { + return nil + } + out := new(ExcludedFluxInstance) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ExcludedFluxKustomization) DeepCopyInto(out *ExcludedFluxKustomization) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ExcludedFluxKustomization. +func (in *ExcludedFluxKustomization) DeepCopy() *ExcludedFluxKustomization { + if in == nil { + return nil + } + out := new(ExcludedFluxKustomization) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ExcludedFluxOCIRepository) DeepCopyInto(out *ExcludedFluxOCIRepository) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ExcludedFluxOCIRepository. +func (in *ExcludedFluxOCIRepository) DeepCopy() *ExcludedFluxOCIRepository { + if in == nil { + return nil + } + out := new(ExcludedFluxOCIRepository) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ExcludedFluxProvider) DeepCopyInto(out *ExcludedFluxProvider) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ExcludedFluxProvider. +func (in *ExcludedFluxProvider) DeepCopy() *ExcludedFluxProvider { + if in == nil { + return nil + } + out := new(ExcludedFluxProvider) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ExcludedFluxReceiver) DeepCopyInto(out *ExcludedFluxReceiver) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ExcludedFluxReceiver. +func (in *ExcludedFluxReceiver) DeepCopy() *ExcludedFluxReceiver { + if in == nil { + return nil + } + out := new(ExcludedFluxReceiver) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ExcludedFluxReport) DeepCopyInto(out *ExcludedFluxReport) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ExcludedFluxReport. +func (in *ExcludedFluxReport) DeepCopy() *ExcludedFluxReport { + if in == nil { + return nil + } + out := new(ExcludedFluxReport) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ExcludedFluxResourceSet) DeepCopyInto(out *ExcludedFluxResourceSet) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ExcludedFluxResourceSet. +func (in *ExcludedFluxResourceSet) DeepCopy() *ExcludedFluxResourceSet { + if in == nil { + return nil + } + out := new(ExcludedFluxResourceSet) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ExcludedFluxResourceSetInputProvider) DeepCopyInto(out *ExcludedFluxResourceSetInputProvider) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ExcludedFluxResourceSetInputProvider. +func (in *ExcludedFluxResourceSetInputProvider) DeepCopy() *ExcludedFluxResourceSetInputProvider { + if in == nil { + return nil + } + out := new(ExcludedFluxResourceSetInputProvider) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ExcludedHPA) DeepCopyInto(out *ExcludedHPA) { *out = *in @@ -841,6 +1186,121 @@ func (in *Exclusions) DeepCopyInto(out *Exclusions) { *out = make([]ExcludedCNPGCluster, len(*in)) copy(*out, *in) } + if in.ExcludedArgoCDApplications != nil { + in, out := &in.ExcludedArgoCDApplications, &out.ExcludedArgoCDApplications + *out = make([]ExcludedArgoCDApplication, len(*in)) + copy(*out, *in) + } + if in.ExcludedArgoCDApplicationSets != nil { + in, out := &in.ExcludedArgoCDApplicationSets, &out.ExcludedArgoCDApplicationSets + *out = make([]ExcludedArgoCDApplicationSet, len(*in)) + copy(*out, *in) + } + if in.ExcludedFluxGitRepositories != nil { + in, out := &in.ExcludedFluxGitRepositories, &out.ExcludedFluxGitRepositories + *out = make([]ExcludedFluxGitRepository, len(*in)) + copy(*out, *in) + } + if in.ExcludedFluxOCIRepositories != nil { + in, out := &in.ExcludedFluxOCIRepositories, &out.ExcludedFluxOCIRepositories + *out = make([]ExcludedFluxOCIRepository, len(*in)) + copy(*out, *in) + } + if in.ExcludedFluxHelmRepositories != nil { + in, out := &in.ExcludedFluxHelmRepositories, &out.ExcludedFluxHelmRepositories + *out = make([]ExcludedFluxHelmRepository, len(*in)) + copy(*out, *in) + } + if in.ExcludedFluxHelmCharts != nil { + in, out := &in.ExcludedFluxHelmCharts, &out.ExcludedFluxHelmCharts + *out = make([]ExcludedFluxHelmChart, len(*in)) + copy(*out, *in) + } + if in.ExcludedFluxBuckets != nil { + in, out := &in.ExcludedFluxBuckets, &out.ExcludedFluxBuckets + *out = make([]ExcludedFluxBucket, len(*in)) + copy(*out, *in) + } + if in.ExcludedFluxKustomizations != nil { + in, out := &in.ExcludedFluxKustomizations, &out.ExcludedFluxKustomizations + *out = make([]ExcludedFluxKustomization, len(*in)) + copy(*out, *in) + } + if in.ExcludedFluxHelmReleases != nil { + in, out := &in.ExcludedFluxHelmReleases, &out.ExcludedFluxHelmReleases + *out = make([]ExcludedFluxHelmRelease, len(*in)) + copy(*out, *in) + } + if in.ExcludedFluxExternalArtifacts != nil { + in, out := &in.ExcludedFluxExternalArtifacts, &out.ExcludedFluxExternalArtifacts + *out = make([]ExcludedFluxExternalArtifact, len(*in)) + copy(*out, *in) + } + if in.ExcludedFluxAlerts != nil { + in, out := &in.ExcludedFluxAlerts, &out.ExcludedFluxAlerts + *out = make([]ExcludedFluxAlert, len(*in)) + copy(*out, *in) + } + if in.ExcludedFluxProviders != nil { + in, out := &in.ExcludedFluxProviders, &out.ExcludedFluxProviders + *out = make([]ExcludedFluxProvider, len(*in)) + copy(*out, *in) + } + if in.ExcludedFluxReceivers != nil { + in, out := &in.ExcludedFluxReceivers, &out.ExcludedFluxReceivers + *out = make([]ExcludedFluxReceiver, len(*in)) + copy(*out, *in) + } + if in.ExcludedFluxInstances != nil { + in, out := &in.ExcludedFluxInstances, &out.ExcludedFluxInstances + *out = make([]ExcludedFluxInstance, len(*in)) + copy(*out, *in) + } + if in.ExcludedFluxReports != nil { + in, out := &in.ExcludedFluxReports, &out.ExcludedFluxReports + *out = make([]ExcludedFluxReport, len(*in)) + copy(*out, *in) + } + if in.ExcludedFluxResourceSetInputProviders != nil { + in, out := &in.ExcludedFluxResourceSetInputProviders, &out.ExcludedFluxResourceSetInputProviders + *out = make([]ExcludedFluxResourceSetInputProvider, len(*in)) + copy(*out, *in) + } + if in.ExcludedFluxResourceSets != nil { + in, out := &in.ExcludedFluxResourceSets, &out.ExcludedFluxResourceSets + *out = make([]ExcludedFluxResourceSet, len(*in)) + copy(*out, *in) + } + if in.ExcludedFluxImageRepositories != nil { + in, out := &in.ExcludedFluxImageRepositories, &out.ExcludedFluxImageRepositories + *out = make([]ExcludedFluxImageRepository, len(*in)) + copy(*out, *in) + } + if in.ExcludedFluxImagePolicies != nil { + in, out := &in.ExcludedFluxImagePolicies, &out.ExcludedFluxImagePolicies + *out = make([]ExcludedFluxImagePolicy, len(*in)) + copy(*out, *in) + } + if in.ExcludedFluxImageUpdateAutomations != nil { + in, out := &in.ExcludedFluxImageUpdateAutomations, &out.ExcludedFluxImageUpdateAutomations + *out = make([]ExcludedFluxImageUpdateAutomation, len(*in)) + copy(*out, *in) + } + if in.ExcludedFlaggerCanaries != nil { + in, out := &in.ExcludedFlaggerCanaries, &out.ExcludedFlaggerCanaries + *out = make([]ExcludedFlaggerCanary, len(*in)) + copy(*out, *in) + } + if in.ExcludedFlaggerMetricTemplates != nil { + in, out := &in.ExcludedFlaggerMetricTemplates, &out.ExcludedFlaggerMetricTemplates + *out = make([]ExcludedFlaggerMetricTemplate, len(*in)) + copy(*out, *in) + } + if in.ExcludedFlaggerAlertProviders != nil { + in, out := &in.ExcludedFlaggerAlertProviders, &out.ExcludedFlaggerAlertProviders + *out = make([]ExcludedFlaggerAlertProvider, len(*in)) + copy(*out, *in) + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Exclusions. diff --git a/buf.gen.yaml b/buf.gen.yaml index fba32a40..257727d7 100644 --- a/buf.gen.yaml +++ b/buf.gen.yaml @@ -1,21 +1,24 @@ -version: v1 +version: v2 managed: enabled: true - go_package_prefix: - default: github.com/devzero-inc/zxporter/gen - except: - - buf.build/prometheus/client-model - - buf.build/googleapis/googleapis + disable: + - file_option: go_package + module: buf.build/prometheus/client-model + - file_option: go_package + module: buf.build/googleapis/googleapis + override: + - file_option: go_package_prefix + value: github.com/devzero-inc/zxporter/gen plugins: # Generate go files - - plugin: buf.build/protocolbuffers/go:v1.31.0 + - remote: buf.build/protocolbuffers/go:v1.31.0 out: gen opt: paths=source_relative # Generate go-grpc files - - plugin: buf.build/grpc/go:v1.3.0 + - remote: buf.build/grpc/go:v1.3.0 out: gen opt: paths=source_relative # Generate connect-go files - - plugin: buf.build/connectrpc/go:v1.12.0 + - remote: buf.build/connectrpc/go:v1.12.0 out: gen opt: paths=source_relative diff --git a/buf.work.yaml b/buf.work.yaml deleted file mode 100644 index 1878b341..00000000 --- a/buf.work.yaml +++ /dev/null @@ -1,3 +0,0 @@ -version: v1 -directories: - - proto diff --git a/buf.yaml b/buf.yaml new file mode 100644 index 00000000..62d43b67 --- /dev/null +++ b/buf.yaml @@ -0,0 +1,21 @@ +version: v2 +# Was buf.work.yaml (v1) with no module buf.yaml, i.e. v1's default rules. +# Those are pinned explicitly so the v2 defaults do not widen them: v1 DEFAULT +# is v2 STANDARD minus FIELD_NOT_REQUIRED and PACKAGE_NO_IMPORT_CYCLE, v1 FILE +# is v2 FILE minus EXTENSION_NO_DELETE and FIELD_SAME_DEFAULT, and v1 did not +# honour `// buf:lint:ignore` comments. +modules: + - path: proto +lint: + use: + - STANDARD + except: + - FIELD_NOT_REQUIRED + - PACKAGE_NO_IMPORT_CYCLE + disallow_comment_ignores: true +breaking: + use: + - FILE + except: + - EXTENSION_NO_DELETE + - FIELD_SAME_DEFAULT diff --git a/cmd/zxporter-netmon/main.go b/cmd/zxporter-netmon/main.go index 8563434a..29b47f43 100644 --- a/cmd/zxporter-netmon/main.go +++ b/cmd/zxporter-netmon/main.go @@ -3,22 +3,39 @@ package main import ( "context" "encoding/json" + "errors" "flag" "fmt" "net/http" + "net/http/pprof" "os" "os/signal" + "runtime" + "runtime/debug" "syscall" "time" + // Sets GOMEMLIMIT to 0.9 x the container's cgroup memory limit at startup so + // the GC collects aggressively before the kernel OOM-kills the pod. No-ops + // safely when no cgroup memory limit is set. Mirrors the controller-manager + // (operators/zxporter/cmd/main.go) and nodemon. + // + // Without it this binary ran at GOGC=100 with no knowledge of its cgroup + // ceiling: it idles at ~98MiB and the GC will not collect until the heap has + // doubled, so the first decodable load on the node OOM-killed it in 26s. + _ "github.com/KimMachineGun/automemlimit" + "github.com/cilium/ebpf/rlimit" gen "github.com/devzero-inc/zxporter/gen/api/v1" "github.com/devzero-inc/zxporter/internal/health" "github.com/devzero-inc/zxporter/internal/networkmonitor" "github.com/devzero-inc/zxporter/internal/networkmonitor/dns" "github.com/devzero-inc/zxporter/internal/networkmonitor/ebpf" + "github.com/devzero-inc/zxporter/internal/networkmonitor/redmetrics" + "github.com/devzero-inc/zxporter/internal/networkmonitor/redmetrics/socketprobe" "github.com/devzero-inc/zxporter/internal/transport" "github.com/devzero-inc/zxporter/internal/version" + "github.com/go-logr/logr" "github.com/go-logr/zapr" "go.uber.org/zap" @@ -32,7 +49,7 @@ import ( var ( metricsAddr = flag.String( "metrics-bind-address", - ":8081", + ":9101", "The address the metric endpoint binds to.", ) readInterval = flag.Duration( @@ -60,6 +77,33 @@ var ( false, "Run in standalone mode without K8s connection", ) + containerRedMetrics = flag.Bool( + "container-red-metrics", + false, + "Record per-container inbound RED metrics using socket probes. Off by default: "+ + "it attaches kprobes and holds per-connection state, so it must be switched on deliberately.", + ) + pprofAddr = flag.String( + "pprof-bind-address", + "", + "Address for the pprof server, e.g. 127.0.0.1:6060. Empty disables it entirely. "+ + "Bind to loopback: this pod runs with hostNetwork, so any other address publishes "+ + "profiles on the node's network, and a heap profile of this binary contains "+ + "buffered request bytes from the traffic it is decoding.", + ) + pprofMutexFraction = flag.Int( + "pprof-mutex-fraction", + 0, + "runtime.SetMutexProfileFraction: report 1/N contended mutex events. 0 disables "+ + "mutex profiling. Requires -pprof-bind-address, and costs something on every "+ + "contended lock, so switch it on for an investigation and off again after.", + ) + pprofBlockRate = flag.Int( + "pprof-block-rate", + 0, + "runtime.SetBlockProfileRate: sample one blocking event per N nanoseconds blocked. "+ + "0 disables block profiling. Same caveats as -pprof-mutex-fraction.", + ) ) const ( @@ -214,6 +258,11 @@ func main() { } } + // Read back the GOMEMLIMIT set by the automemlimit side-effect import above, + // so a pod that is still being OOM-killed can be told apart from one whose + // limit was never applied. + logger.Info("effective GOMEMLIMIT", "bytes", debug.SetMemoryLimit(-1)) + versionInfo := version.Get() monitorCfg := networkmonitor.Config{ ReadInterval: *readInterval, @@ -254,6 +303,11 @@ func main() { // Start Monitor in background go monitor.Start(ctx) + stopRedMetrics := startContainerRedMetrics( + ctx, *containerRedMetrics, logger, monitor, podCache, healthManager, + ) + defer stopRedMetrics() + // Start heartbeat sender if dakr is configured if dakrClient != nil { go func() { @@ -359,6 +413,8 @@ func main() { } }() + pprofServer := startPprofServer(logger) + // Signal handling c := make(chan os.Signal, 1) signal.Notify(c, os.Interrupt, syscall.SIGTERM) @@ -371,6 +427,80 @@ func main() { if err := server.Shutdown(shutdownCtx); err != nil { logger.Error(err, "HTTP server shutdown failed") } + shutdownPprofServer(shutdownCtx, pprofServer, logger) +} + +// shutdownPprofServer is a nil-tolerant Shutdown. It lives outside main because main is +// already over this repo's cyclomatic complexity threshold, and a diagnostic that is off by +// default has no business pushing it further. +func shutdownPprofServer(ctx context.Context, srv *http.Server, logger logr.Logger) { + if srv == nil { + return + } + if err := srv.Shutdown(ctx); err != nil { + logger.Error(err, "pprof server shutdown failed") + } +} + +// startPprofServer brings up net/http/pprof on its own listener, returning nil when +// -pprof-bind-address is empty, which is the default. +// +// It is deliberately NOT mounted on the metrics mux. That listener is published on the +// node -- this DaemonSet runs with hostNetwork -- and the profiles here are not the same +// kind of data as the counters there: the aggregator holds the leading bytes of requests +// it has not finished decoding, so a heap profile of this process contains fragments of +// whatever traffic the node is serving. It gets its own address so that exposing metrics +// can never expose profiles as a side effect, and the flag's default of "" means an +// operator has to ask for it. +// +// Access is via kubectl port-forward, which dials into the pod's network namespace and so +// reaches a loopback bind. +func startPprofServer(logger logr.Logger) *http.Server { + if *pprofAddr == "" { + return nil + } + + // Both are off unless asked for: each adds work to every contended lock and every + // blocking operation, which would distort the very measurement they are here to + // provide. Set on the runtime rather than per-request because the runtime has to be + // collecting before the profile is fetched. + if *pprofMutexFraction > 0 { + runtime.SetMutexProfileFraction(*pprofMutexFraction) + } + if *pprofBlockRate > 0 { + runtime.SetBlockProfileRate(*pprofBlockRate) + } + + // Registered explicitly rather than by importing net/http/pprof for its side effect: + // that init() writes to http.DefaultServeMux, which this binary does not serve, so a + // blank import would look like it enabled profiling and quietly do nothing. + mux := http.NewServeMux() + mux.HandleFunc("/debug/pprof/", pprof.Index) + mux.HandleFunc("/debug/pprof/cmdline", pprof.Cmdline) + mux.HandleFunc("/debug/pprof/profile", pprof.Profile) + mux.HandleFunc("/debug/pprof/symbol", pprof.Symbol) + mux.HandleFunc("/debug/pprof/trace", pprof.Trace) + + // No write timeout, and that is the point: a CPU profile blocks for its full + // -seconds duration and an execution trace for longer, so any timeout short enough + // to be worth having would truncate exactly the profiles this exists to serve. + srv := &http.Server{ + Addr: *pprofAddr, + Handler: mux, + ReadHeaderTimeout: 5 * time.Second, + } + + go func() { + logger.Info("Starting pprof server", "addr", *pprofAddr, + "mutex_fraction", *pprofMutexFraction, "block_rate", *pprofBlockRate) + if err := srv.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) { + // Not fatal. Profiling is a diagnostic; losing it must not take the node's + // network monitoring down with it. + logger.Error(err, "pprof server failed") + } + }() + + return srv } func getKubeConfig(kubeconfigPath string) (*rest.Config, error) { @@ -385,3 +515,86 @@ func getKubeConfig(kubeconfigPath string) (*rest.Config, error) { // Fallback to default local rules (e.g. ~/.kube/config) return clientcmd.BuildConfigFromFlags("", clientcmd.RecommendedHomeFile) } + +// socketProbe is the part of the socket probe this file uses. Naming it here +// rather than depending on the concrete type keeps the wiring testable without a +// kernel. +type socketProbe interface { + Attach() error + Run(ctx context.Context, sink func(redmetrics.SocketEvent)) error + Close() error +} + +// redMetricsSink is the part of the monitor this file uses. +type redMetricsSink interface { + EnableContainerRedMetrics(agg *redmetrics.Aggregator) +} + +// startContainerRedMetrics switches on container RED metrics, returning a stop +// function. When the feature is off -- which is the default -- it does nothing +// and returns a no-op, so the disabled path costs one branch and touches no +// kernel state at all. +func startContainerRedMetrics( + ctx context.Context, + enabled bool, + logger logr.Logger, + monitor redMetricsSink, + pods redmetrics.PodLookup, + healthManager *health.HealthManager, +) func() { + if !enabled { + return func() {} + } + return enableContainerRedMetrics(ctx, logger, monitor, pods, healthManager, socketprobe.New(logger)) +} + +// enableContainerRedMetrics does the work, against an injected probe. +func enableContainerRedMetrics( + ctx context.Context, + logger logr.Logger, + monitor redMetricsSink, + pods redmetrics.PodLookup, + healthManager *health.HealthManager, + probe socketProbe, +) func() { + healthManager.Register(health.ComponentContainerRedMetrics) + + if err := probe.Attach(); err != nil { + if errors.Is(err, socketprobe.ErrUnsupported) { + // The kernel cannot carry the probe. That is a node that will never + // report, not a node that is failing, so the component is removed from + // the report rather than left in it as unhealthy -- otherwise a + // privileged DaemonSet sits in a permanent alarm state over something no + // operator action can fix. + logger.Info("Container RED metrics unsupported on this kernel, continuing without them") + healthManager.Deregister(health.ComponentContainerRedMetrics) + return func() {} + } + logger.Error(err, "Failed to attach container RED metrics socket probe") + healthManager.UpdateStatus( + health.ComponentContainerRedMetrics, health.HealthStatusUnhealthy, err.Error(), nil, + ) + return func() {} + } + + aggregator := redmetrics.NewAggregator(redmetrics.DefaultConfig(), redmetrics.SystemClock{}, pods, logger) + monitor.EnableContainerRedMetrics(aggregator) + healthManager.UpdateStatus( + health.ComponentContainerRedMetrics, health.HealthStatusHealthy, "attached", nil, + ) + + go func() { + if err := probe.Run(ctx, aggregator.Observe); err != nil && !errors.Is(err, context.Canceled) { + logger.Error(err, "Container RED metrics socket probe stopped") + healthManager.UpdateStatus( + health.ComponentContainerRedMetrics, health.HealthStatusUnhealthy, err.Error(), nil, + ) + } + }() + + return func() { + if err := probe.Close(); err != nil { + logger.Error(err, "Failed to close container RED metrics socket probe") + } + } +} diff --git a/cmd/zxporter-netmon/main_test.go b/cmd/zxporter-netmon/main_test.go new file mode 100644 index 00000000..f72bad5d --- /dev/null +++ b/cmd/zxporter-netmon/main_test.go @@ -0,0 +1,142 @@ +package main + +import ( + "io" + "net" + "net/http" + "testing" + "time" + + "github.com/go-logr/logr" +) + +// The pprof server is wired by hand rather than by a blank import, so the thing worth +// testing is that the wiring is actually there: a blank import would compile, look +// enabled, and serve nothing, because net/http/pprof registers on DefaultServeMux and +// this binary never serves that mux. +// +// These run against the real server on a real listener. netmon itself cannot be started +// on a developer machine -- conntrack is Linux-only and main exits before reaching the +// HTTP servers -- so the handler wiring has no other seam to be checked at. + +// startTestPprofServer brings the server up on a free port and returns its base URL, +// restoring the flag globals afterwards so the tests do not leak into each other. +func startTestPprofServer(t *testing.T, mutexFraction, blockRate int) string { + t.Helper() + + listener, err := net.Listen("tcp", "127.0.0.1:0") + if err != nil { + t.Fatalf("reserving a port: %v", err) + } + addr := listener.Addr().String() + if err := listener.Close(); err != nil { + t.Fatalf("releasing the reserved port: %v", err) + } + + prevAddr, prevMutex, prevBlock := *pprofAddr, *pprofMutexFraction, *pprofBlockRate + *pprofAddr, *pprofMutexFraction, *pprofBlockRate = addr, mutexFraction, blockRate + t.Cleanup(func() { + *pprofAddr, *pprofMutexFraction, *pprofBlockRate = prevAddr, prevMutex, prevBlock + }) + + srv := startPprofServer(logr.Discard()) + if srv == nil { + t.Fatal("startPprofServer returned nil for a non-empty address") + } + t.Cleanup(func() { + _ = srv.Close() + }) + + // ListenAndServe runs in its own goroutine, so the port is not necessarily bound + // when this returns. Dialled rather than slept on. + base := "http://" + addr + deadline := time.Now().Add(5 * time.Second) + for { + conn, err := net.DialTimeout("tcp", addr, 100*time.Millisecond) + if err == nil { + _ = conn.Close() + return base + } + if time.Now().After(deadline) { + t.Fatalf("pprof server never accepted a connection on %s: %v", addr, err) + } + } +} + +func TestPprofServerIsOffByDefault(t *testing.T) { + prev := *pprofAddr + *pprofAddr = "" + t.Cleanup(func() { *pprofAddr = prev }) + + if srv := startPprofServer(logr.Discard()); srv != nil { + t.Fatal("pprof server started with an empty bind address; it must be opt-in") + } +} + +func TestPprofServerServesProfiles(t *testing.T) { + base := startTestPprofServer(t, 0, 0) + client := &http.Client{Timeout: 30 * time.Second} + + // One per registered route. The index is what makes heap, goroutine and allocs + // reachable -- they have no route of their own -- so a missing index would remove + // most of what an operator came for while /debug/pprof/profile still answered. + for _, path := range []string{ + "/debug/pprof/", + "/debug/pprof/cmdline", + "/debug/pprof/heap", + "/debug/pprof/goroutine", + "/debug/pprof/profile?seconds=1", + } { + t.Run(path, func(t *testing.T) { + resp, err := client.Get(base + path) + if err != nil { + t.Fatalf("GET %s: %v", path, err) + } + defer func() { _ = resp.Body.Close() }() + + body, err := io.ReadAll(resp.Body) + if err != nil { + t.Fatalf("reading %s: %v", path, err) + } + if resp.StatusCode != http.StatusOK { + t.Fatalf("GET %s = %d, want 200: %s", path, resp.StatusCode, body) + } + if len(body) == 0 { + t.Fatalf("GET %s returned an empty body", path) + } + }) + } +} + +// A CPU profile is served by holding the request open for its full duration, which is +// why the server has no write timeout. This pins that: a profile longer than any timeout +// somebody might later think is a good idea must still complete. +func TestPprofProfileOutlivesAnyWriteTimeout(t *testing.T) { + if testing.Short() { + t.Skip("takes seconds by construction") + } + + base := startTestPprofServer(t, 0, 0) + client := &http.Client{Timeout: 60 * time.Second} + + start := time.Now() + resp, err := client.Get(base + "/debug/pprof/profile?seconds=6") + if err != nil { + t.Fatalf("fetching a 6s profile: %v", err) + } + defer func() { _ = resp.Body.Close() }() + + body, err := io.ReadAll(resp.Body) + if err != nil { + t.Fatalf("reading the profile: %v", err) + } + if resp.StatusCode != http.StatusOK { + t.Fatalf("profile = %d, want 200: %s", resp.StatusCode, body) + } + if elapsed := time.Since(start); elapsed < 6*time.Second { + t.Fatalf("profile returned after %s, want at least the 6s requested", elapsed) + } + if len(body) == 0 { + t.Fatal("profile body is empty") + } +} diff --git a/config/crd/bases/devzero.io_collectionpolicies.yaml b/config/crd/bases/devzero.io_collectionpolicies.yaml index acf8e66b..75b29012 100644 --- a/config/crd/bases/devzero.io_collectionpolicies.yaml +++ b/config/crd/bases/devzero.io_collectionpolicies.yaml @@ -52,6 +52,39 @@ spec: exclusions: description: Exclusions specifies resources to exclude from collection properties: + excludedArgoCDApplicationSets: + items: + description: ExcludedArgoCDApplicationSet identifies an Argo + CD ApplicationSet to exclude from collection + properties: + name: + description: Name is the Argo CD ApplicationSet's name + type: string + namespace: + description: Namespace is the Argo CD ApplicationSet's namespace + type: string + required: + - name + - namespace + type: object + type: array + excludedArgoCDApplications: + description: Argo CD Exclusions + items: + description: ExcludedArgoCDApplication identifies an Argo CD + Application to exclude from collection + properties: + name: + description: Name is the Argo CD Application's name + type: string + namespace: + description: Namespace is the Argo CD Application's namespace + type: string + required: + - name + - namespace + type: object + type: array excludedCNPGClusters: description: CloudNativePG Exclusions items: @@ -227,6 +260,303 @@ spec: - namespace type: object type: array + excludedFlaggerAlertProviders: + items: + description: ExcludedFlaggerAlertProvider identifies a Flagger + AlertProvider to exclude from collection + properties: + name: + type: string + namespace: + type: string + required: + - name + - namespace + type: object + type: array + excludedFlaggerCanaries: + description: Flagger Exclusions + items: + description: ExcludedFlaggerCanary identifies a Flagger Canary + to exclude from collection + properties: + name: + type: string + namespace: + type: string + required: + - name + - namespace + type: object + type: array + excludedFlaggerMetricTemplates: + items: + description: ExcludedFlaggerMetricTemplate identifies a Flagger + MetricTemplate to exclude from collection + properties: + name: + type: string + namespace: + type: string + required: + - name + - namespace + type: object + type: array + excludedFluxAlerts: + items: + description: ExcludedFluxAlert identifies a FluxCD Alert to + exclude from collection + properties: + name: + type: string + namespace: + type: string + required: + - name + - namespace + type: object + type: array + excludedFluxBuckets: + items: + description: ExcludedFluxBucket identifies a FluxCD Bucket to + exclude from collection + properties: + name: + type: string + namespace: + type: string + required: + - name + - namespace + type: object + type: array + excludedFluxExternalArtifacts: + description: FluxCD Exclusions -- collector-only kinds + items: + description: ExcludedFluxExternalArtifact identifies a FluxCD + ExternalArtifact to exclude from collection + properties: + name: + type: string + namespace: + type: string + required: + - name + - namespace + type: object + type: array + excludedFluxGitRepositories: + description: FluxCD Exclusions -- full-explorer kinds + items: + description: ExcludedFluxGitRepository identifies a FluxCD GitRepository + to exclude from collection + properties: + name: + type: string + namespace: + type: string + required: + - name + - namespace + type: object + type: array + excludedFluxHelmCharts: + items: + description: ExcludedFluxHelmChart identifies a FluxCD HelmChart + to exclude from collection + properties: + name: + type: string + namespace: + type: string + required: + - name + - namespace + type: object + type: array + excludedFluxHelmReleases: + items: + description: ExcludedFluxHelmRelease identifies a FluxCD HelmRelease + to exclude from collection + properties: + name: + type: string + namespace: + type: string + required: + - name + - namespace + type: object + type: array + excludedFluxHelmRepositories: + items: + description: ExcludedFluxHelmRepository identifies a FluxCD + HelmRepository to exclude from collection + properties: + name: + type: string + namespace: + type: string + required: + - name + - namespace + type: object + type: array + excludedFluxImagePolicies: + items: + description: ExcludedFluxImagePolicy identifies a FluxCD ImagePolicy + to exclude from collection + properties: + name: + type: string + namespace: + type: string + required: + - name + - namespace + type: object + type: array + excludedFluxImageRepositories: + items: + description: ExcludedFluxImageRepository identifies a FluxCD + ImageRepository to exclude from collection + properties: + name: + type: string + namespace: + type: string + required: + - name + - namespace + type: object + type: array + excludedFluxImageUpdateAutomations: + items: + description: ExcludedFluxImageUpdateAutomation identifies a + FluxCD ImageUpdateAutomation to exclude from collection + properties: + name: + type: string + namespace: + type: string + required: + - name + - namespace + type: object + type: array + excludedFluxInstances: + items: + description: ExcludedFluxInstance identifies a Flux Operator + FluxInstance to exclude from collection + properties: + name: + type: string + namespace: + type: string + required: + - name + - namespace + type: object + type: array + excludedFluxKustomizations: + items: + description: ExcludedFluxKustomization identifies a FluxCD Kustomization + to exclude from collection + properties: + name: + type: string + namespace: + type: string + required: + - name + - namespace + type: object + type: array + excludedFluxOCIRepositories: + items: + description: ExcludedFluxOCIRepository identifies a FluxCD OCIRepository + to exclude from collection + properties: + name: + type: string + namespace: + type: string + required: + - name + - namespace + type: object + type: array + excludedFluxProviders: + items: + description: ExcludedFluxProvider identifies a FluxCD Provider + to exclude from collection + properties: + name: + type: string + namespace: + type: string + required: + - name + - namespace + type: object + type: array + excludedFluxReceivers: + items: + description: ExcludedFluxReceiver identifies a FluxCD Receiver + to exclude from collection + properties: + name: + type: string + namespace: + type: string + required: + - name + - namespace + type: object + type: array + excludedFluxReports: + items: + description: ExcludedFluxReport identifies a Flux Operator FluxReport + to exclude from collection + properties: + name: + type: string + namespace: + type: string + required: + - name + - namespace + type: object + type: array + excludedFluxResourceSetInputProviders: + items: + description: ExcludedFluxResourceSetInputProvider identifies + a Flux Operator ResourceSetInputProvider to exclude from collection + properties: + name: + type: string + namespace: + type: string + required: + - name + - namespace + type: object + type: array + excludedFluxResourceSets: + items: + description: ExcludedFluxResourceSet identifies a Flux Operator + ResourceSet to exclude from collection + properties: + name: + type: string + namespace: + type: string + required: + - name + - namespace + type: object + type: array excludedHPAs: items: description: ExcludedHPA identifies a horizontal pod autoscaler @@ -664,9 +994,13 @@ spec: type: boolean disabledCollectors: description: |- - DisabledCollectors is a list of collector types to completely disable - Valid values include: "pod", "deployment", "statefulset", "daemonset", "service", - "configmap", "secret", "node", "event", etc. + DisabledCollectors is a list of collector types to completely disable, by their + canonical names, e.g. "pod", "deployment", "stateful_set", "daemon_set", "service", + "node", "event", "persistent_volume", "persistent_volume_claim", + "custom_resource_definition", "keda_scaled_job", "keda_scaled_object". + The legacy spellings "statefulset", "daemonset", "persistentvolume", + "persistentvolumeclaim", "crd", "scaledjob" and "scaledobject" are still accepted + and mean the same collectors. items: type: string type: array @@ -674,6 +1008,13 @@ spec: description: Frequency is how often to collect resource usage metrics type: string + kedaMetricsEndpoint: + description: |- + KEDAMetricsEndpoint is the full URL of the KEDA operator /metrics endpoint + that the KedaScalerMetrics collector scrapes. Defaults to + collector.DefaultKedaMetricsEndpoint (KEDA in the devzero-system namespace). + Override only when KEDA runs in a different namespace or on a different port. + type: string kubeContextName: description: KubeContextName is the name of the current context being used to apply the installation yaml @@ -738,6 +1079,13 @@ spec: PATToken is the Personal Access Token used for automatic cluster token exchange If ClusterToken is not provided but PATToken is, the system will exchange it for a cluster token type: string + schedulerMetricsScrapeInterval: + description: |- + SchedulerMetricsScrapeInterval bounds how often the SchedulerMetrics + collector scrapes dz-scheduler's /metrics endpoint (defaults to 60s -- + see collector.DefaultSchedulerMetricsScrapeInterval's doc comment for + why). A Go duration string (e.g. "60s", "30s"). + type: string watchedCRDs: description: WatchedCRDs is a list of custom resource definitions to explicitly watch diff --git a/config/manager/env_configmap.yaml b/config/manager/env_configmap.yaml index e5945db7..2c4dc15c 100644 --- a/config/manager/env_configmap.yaml +++ b/config/manager/env_configmap.yaml @@ -20,6 +20,8 @@ data: NODE_METRICS_CONCURRENCY: "20" NODEMON_REQUEST_TIMEOUT: "15s" KUBELET_FALLBACK_TIMEOUT: "15s" + SCHEDULER_METRICS_SCRAPE_INTERVAL: "" + KEDA_METRICS_ENDPOINT: "" WATCHED_CRDS: "" DISABLED_COLLECTORS: "" # NVIDIA mig-parted ConfigMap location; empty = GPU operator defaults diff --git a/config/manager/manager.yaml b/config/manager/manager.yaml index 7030ce6b..2d5a9326 100644 --- a/config/manager/manager.yaml +++ b/config/manager/manager.yaml @@ -86,6 +86,10 @@ spec: name: token key: CLUSTER_TOKEN optional: true + # CLUSTER_TOKEN above is a pod-start copy of the mirror Secret + # zxporter writes, so zxporter reads that Secret live (KUB-158). + - name: CLUSTER_TOKEN_SOURCE + value: mirror securityContext: allowPrivilegeEscalation: false capabilities: diff --git a/config/rbac/kustomization.yaml b/config/rbac/kustomization.yaml index 1e0e4804..c1c46dfd 100644 --- a/config/rbac/kustomization.yaml +++ b/config/rbac/kustomization.yaml @@ -22,6 +22,7 @@ resources: - metrics_auth_role.yaml - metrics_auth_role_binding.yaml - metrics_reader_role.yaml +- metrics_reader_role_binding.yaml # For each CRD, "Editor" and "Viewer" roles are scaffolded by # default, aiding admins in cluster management. Those roles are # not used by the Project itself. You can comment the following lines diff --git a/config/rbac/metrics_reader_role_binding.yaml b/config/rbac/metrics_reader_role_binding.yaml new file mode 100644 index 00000000..45a11560 --- /dev/null +++ b/config/rbac/metrics_reader_role_binding.yaml @@ -0,0 +1,19 @@ +# Binds metrics-reader (nonResourceURLs: ["/metrics"], verb: get) to the +# controller's own ServiceAccount so the SchedulerMetrics collector can scrape +# dz-scheduler's /metrics endpoint. dz-scheduler runs kube-scheduler with +# delegated authorization to the API server (SubjectAccessReview), and +# "/metrics" is not in kube-scheduler's default AlwaysAllowPaths -- unlike +# /healthz and /livez, an authenticated caller still needs an explicit RBAC +# grant for this nonResourceURL, or every scrape 403s. +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: metrics-reader-rolebinding +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: metrics-reader +subjects: +- kind: ServiceAccount + name: controller-manager + namespace: system diff --git a/config/rbac/role.yaml b/config/rbac/role.yaml index fb6c9348..cffdcd4b 100644 --- a/config/rbac/role.yaml +++ b/config/rbac/role.yaml @@ -110,6 +110,8 @@ rules: - apiGroups: - argoproj.io resources: + - applications + - applicationsets - rollouts verbs: - get @@ -157,6 +159,14 @@ rules: - get - list - watch +- apiGroups: + - coordination.k8s.io + resourceNames: + - dz-scheduler + resources: + - leases + verbs: + - get - apiGroups: - dakr.devzero.io resources: @@ -208,6 +218,45 @@ rules: - get - list - watch +- apiGroups: + - flagger.app + resources: + - alertproviders + - canaries + - metrictemplates + verbs: + - get + - list + - watch +- apiGroups: + - fluxcd.controlplane.io + resources: + - fluxinstances + - fluxreports + - resourcesetinputproviders + - resourcesets + verbs: + - get + - list + - watch +- apiGroups: + - helm.toolkit.fluxcd.io + resources: + - helmreleases + verbs: + - get + - list + - watch +- apiGroups: + - image.toolkit.fluxcd.io + resources: + - imagepolicies + - imagerepositories + - imageupdateautomations + verbs: + - get + - list + - watch - apiGroups: - karpenter.azure.com resources: @@ -272,6 +321,14 @@ rules: - get - list - watch +- apiGroups: + - kustomize.toolkit.fluxcd.io + resources: + - kustomizations + verbs: + - get + - list + - watch - apiGroups: - kyverno.io resources: @@ -300,6 +357,16 @@ rules: - get - list - watch +- apiGroups: + - notification.toolkit.fluxcd.io + resources: + - alerts + - providers + - receivers + verbs: + - get + - list + - watch - apiGroups: - policy resources: @@ -337,6 +404,19 @@ rules: - get - list - watch +- apiGroups: + - source.toolkit.fluxcd.io + resources: + - buckets + - externalartifacts + - gitrepositories + - helmcharts + - helmrepositories + - ocirepositories + verbs: + - get + - list + - watch - apiGroups: - sparkoperator.k8s.io resources: diff --git a/config/zxporter-netmon.yaml b/config/zxporter-netmon.yaml index c31cb295..fd6aa242 100644 --- a/config/zxporter-netmon.yaml +++ b/config/zxporter-netmon.yaml @@ -43,7 +43,11 @@ spec: imagePullPolicy: IfNotPresent ports: - name: http - containerPort: 8081 + # Must match the -metrics-bind-address default in cmd/zxporter-netmon. + # Not 8081: dakr-operator is hostNetwork and binds :8081 for its health + # probe, and hostNetwork makes this containerPort a real node-port + # reservation, so the two pods cannot share a node. + containerPort: 9101 protocol: TCP envFrom: - configMapRef: diff --git a/dist/backend-install-gcp-lowpriv.yaml b/dist/backend-install-gcp-lowpriv.yaml index 23b28180..96b94b65 100644 --- a/dist/backend-install-gcp-lowpriv.yaml +++ b/dist/backend-install-gcp-lowpriv.yaml @@ -287,6 +287,8 @@ rules: - apiGroups: - argoproj.io resources: + - applications + - applicationsets - rollouts verbs: - get @@ -334,6 +336,14 @@ rules: - get - list - watch + - apiGroups: + - coordination.k8s.io + resourceNames: + - dz-scheduler + resources: + - leases + verbs: + - get - apiGroups: - dakr.devzero.io resources: @@ -385,6 +395,45 @@ rules: - get - list - watch + - apiGroups: + - flagger.app + resources: + - alertproviders + - canaries + - metrictemplates + verbs: + - get + - list + - watch + - apiGroups: + - fluxcd.controlplane.io + resources: + - fluxinstances + - fluxreports + - resourcesetinputproviders + - resourcesets + verbs: + - get + - list + - watch + - apiGroups: + - helm.toolkit.fluxcd.io + resources: + - helmreleases + verbs: + - get + - list + - watch + - apiGroups: + - image.toolkit.fluxcd.io + resources: + - imagepolicies + - imagerepositories + - imageupdateautomations + verbs: + - get + - list + - watch - apiGroups: - karpenter.azure.com resources: @@ -449,6 +498,14 @@ rules: - get - list - watch + - apiGroups: + - kustomize.toolkit.fluxcd.io + resources: + - kustomizations + verbs: + - get + - list + - watch - apiGroups: - kyverno.io resources: @@ -477,6 +534,16 @@ rules: - get - list - watch + - apiGroups: + - notification.toolkit.fluxcd.io + resources: + - alerts + - providers + - receivers + verbs: + - get + - list + - watch - apiGroups: - policy resources: @@ -514,6 +581,19 @@ rules: - get - list - watch + - apiGroups: + - source.toolkit.fluxcd.io + resources: + - buckets + - externalartifacts + - gitrepositories + - helmcharts + - helmrepositories + - ocirepositories + verbs: + - get + - list + - watch - apiGroups: - sparkoperator.k8s.io resources: @@ -654,6 +734,19 @@ subjects: name: devzero-zxporter-controller-manager namespace: {{.zxporter_namespace}} --- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: devzero-zxporter-metrics-reader-rolebinding +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: devzero-zxporter-metrics-reader +subjects: + - kind: ServiceAccount + name: devzero-zxporter-controller-manager + namespace: {{.zxporter_namespace}} +--- apiVersion: v1 data: BUFFER_SIZE: "" @@ -693,6 +786,7 @@ data: EXCLUDED_STORAGECLASSES: "" EXCLUDED_VPAS: "" K8S_PROVIDER: '{{ .k8s_provider }}' + KEDA_METRICS_ENDPOINT: "" KUBE_CONTEXT_NAME: '{{ .kube_context_name }}' KUBELET_FALLBACK_TIMEOUT: 15s MASK_SECRET_DATA: "" @@ -701,6 +795,7 @@ data: NODE_METRICS_CONCURRENCY: "20" NODE_METRICS_INTERVAL: "" NODEMON_REQUEST_TIMEOUT: 15s + SCHEDULER_METRICS_SCRAPE_INTERVAL: "" TARGET_NAMESPACES: "" TOKEN_CONFIGMAP_NAME: devzero-zxporter-env-config TOKEN_CREDENTIALS_SECRET_NAME: devzero-zxporter-credentials @@ -811,6 +906,8 @@ spec: key: CLUSTER_TOKEN name: devzero-zxporter-token optional: true + - name: CLUSTER_TOKEN_SOURCE + value: mirror image: docker.io/devzeroinc/zxporter:latest livenessProbe: httpGet: @@ -870,16 +967,21 @@ metadata: name: zxporter-nodemon namespace: {{.zxporter_namespace}} labels: - helm.sh/chart: zxporter-nodemon-0.1.6 + helm.sh/chart: zxporter-nodemon-0.1.25 app.kubernetes.io/name: zxporter-nodemon app.kubernetes.io/instance: zxporter-nodemon - app.kubernetes.io/version: "0.1.6" + app.kubernetes.io/version: "0.1.25" app.kubernetes.io/managed-by: Helm + app.kubernetes.io/part-of: dz-platform + devzero.io/component: zxporter-nodemon --- # Source: zxporter-nodemon/templates/dcgm-exporter-configmap.yaml apiVersion: v1 kind: ConfigMap metadata: + labels: + app.kubernetes.io/part-of: dz-platform + devzero.io/component: zxporter-nodemon name: zxporter-nodemon-dcgm-metrics namespace: {{.zxporter_namespace}} data: @@ -895,8 +997,12 @@ data: DCGM_FI_DEV_GPU_UTIL, gauge, GPU utilization (in %). # DCGM_FI_PROF_SM_ACTIVE, gauge, The ratio of cycles an SM has at least 1 warp assigned # DCGM_FI_PROF_SM_OCCUPANCY, gauge, The fraction of resident warps on a multiprocessor - # DCGM_FI_PROF_GR_ENGINE_ACTIVE, gauge, Ratio of time the graphics engine is active (in %). - # DCGM_FI_PROF_DRAM_ACTIVE, gauge, The ratio of cycles the device memory interface is active sending or receiving data. + # Enabled: required for per-MIG-instance profiling data (GPU_I_PROFILE/GPU_I_ID + # labels only attach to DCGM_FI_PROF_* fields, never to plain DCGM_FI_DEV_* + # ones) and directly consumed by nodemon's GPUMigInstance parsing + # (GraphicsEngineActive/DRAMActive/TensorActive below). + DCGM_FI_PROF_GR_ENGINE_ACTIVE, gauge, Ratio of time the graphics engine is active (in %). + DCGM_FI_PROF_DRAM_ACTIVE, gauge, The ratio of cycles the device memory interface is active sending or receiving data. # Memory usage,, DCGM_FI_DEV_FB_FREE, gauge, Framebuffer memory free (in MiB). @@ -915,7 +1021,9 @@ data: # DCGM_FI_PROF_PIPE_FP16_ACTIVE, gauge, Ratio of cycles the fp16 pipe is active. # DCGM_FI_PROF_PIPE_FP32_ACTIVE, gauge, Ratio of cycles the fp32 pipe is active. # DCGM_FI_PROF_PIPE_FP64_ACTIVE, gauge, Ratio of cycles the fp64 pipe is active. - # DCGM_FI_PROF_PIPE_TENSOR_ACTIVE, gauge, The ratio of cycles the tensor (HMMA) pipe is active (off the peak sustained elapsed cycles) + # Enabled: see the GR_ENGINE_ACTIVE/DRAM_ACTIVE note above -- this is + # GPUMigInstance.TensorActive. + DCGM_FI_PROF_PIPE_TENSOR_ACTIVE, gauge, The ratio of cycles the tensor (HMMA) pipe is active (off the peak sustained elapsed cycles) # Health,, # DCGM_FI_DEV_CLOCKS_EVENT_REASONS is not supported by DCGM 3.3.7 @@ -936,6 +1044,9 @@ data: apiVersion: v1 kind: ConfigMap metadata: + labels: + app.kubernetes.io/part-of: dz-platform + devzero.io/component: zxporter-nodemon name: zxporter-nodemon-zxporter-nodemon namespace: {{.zxporter_namespace}} data: @@ -946,11 +1057,13 @@ kind: ClusterRole metadata: name: zxporter-nodemon labels: - helm.sh/chart: zxporter-nodemon-0.1.6 + helm.sh/chart: zxporter-nodemon-0.1.25 app.kubernetes.io/name: zxporter-nodemon app.kubernetes.io/instance: zxporter-nodemon - app.kubernetes.io/version: "0.1.6" + app.kubernetes.io/version: "0.1.25" app.kubernetes.io/managed-by: Helm + app.kubernetes.io/part-of: dz-platform + devzero.io/component: zxporter-nodemon rules: - apiGroups: - "" @@ -959,6 +1072,7 @@ rules: verbs: - get - list + - watch - apiGroups: - apps resources: @@ -996,11 +1110,13 @@ kind: ClusterRoleBinding metadata: name: zxporter-nodemon labels: - helm.sh/chart: zxporter-nodemon-0.1.6 + helm.sh/chart: zxporter-nodemon-0.1.25 app.kubernetes.io/name: zxporter-nodemon app.kubernetes.io/instance: zxporter-nodemon - app.kubernetes.io/version: "0.1.6" + app.kubernetes.io/version: "0.1.25" app.kubernetes.io/managed-by: Helm + app.kubernetes.io/part-of: dz-platform + devzero.io/component: zxporter-nodemon roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole @@ -1017,10 +1133,12 @@ metadata: name: zxporter-nodemon namespace: {{.zxporter_namespace}} labels: - helm.sh/chart: zxporter-nodemon-0.1.6 + app.kubernetes.io/part-of: dz-platform + devzero.io/component: zxporter-nodemon + helm.sh/chart: zxporter-nodemon-0.1.25 app.kubernetes.io/name: zxporter-nodemon app.kubernetes.io/instance: zxporter-nodemon - app.kubernetes.io/version: "0.1.6" + app.kubernetes.io/version: "0.1.25" app.kubernetes.io/managed-by: Helm annotations: ignore-check.kube-linter.io/privileged-container: "This daemon set needs to run DCGM Exporter as privileged to access the GPU metrics." @@ -1127,6 +1245,14 @@ spec: memory: 256Mi - name: dcgm-exporter securityContext: + # SYS_ADMIN alone is not enough: enumerating MIG Compute Instance + # profiles via NVML needs broader host access than that one + # capability grants (confirmed live -- SYS_ADMIN-only reproduces + # "CacheManager Init Failed. Error: -17" the instant any MIG + # instance exists on the node; switching to privileged fixes it + # with zero other changes). This is a generic NVML/driver + # requirement, not provider-specific, so unlike the hostPath/ + # LD_LIBRARY_PATH blocks below it must not be gated on gcp only. capabilities: add: - SYS_ADMIN @@ -1154,6 +1280,16 @@ spec: valueFrom: fieldRef: fieldPath: spec.nodeName + # Without this, nvidia-container-runtime's "auto" mode only exposes + # whole/non-MIG GPUs to a non-privileged container with no explicit + # device-plugin resource request (which this sidecar deliberately + # doesn't make, since it must see every GPU on the node, not just + # one). MIG-partitioned GPUs stay invisible without it -- confirmed + # by direct comparison against GPU Operator's own dcgm-exporter, + # which gets the same visibility via privileged:true instead; this + # is the minimal fix that doesn't need the broader grant. + - name: "NVIDIA_VISIBLE_DEVICES" + value: "all" - name: "LD_LIBRARY_PATH" value: "/usr/local/nvidia/lib64" - name: "DCGM_EXPORTER_KUBERNETES_GPU_ID_TYPE" @@ -1166,6 +1302,10 @@ spec: mountPath: /usr/local/nvidia - name: zxporter-nodemon-dcgm-metrics mountPath: "/etc/dcgm-exporter" + resources: + requests: + cpu: 50m + memory: 128Mi --- # Source: zxporter-nodemon/templates/daemonset.yaml apiVersion: apps/v1 @@ -1174,10 +1314,12 @@ metadata: name: zxporter-nodemon-gpu namespace: {{.zxporter_namespace}} labels: - helm.sh/chart: zxporter-nodemon-0.1.6 + app.kubernetes.io/part-of: dz-platform + devzero.io/component: zxporter-nodemon + helm.sh/chart: zxporter-nodemon-0.1.25 app.kubernetes.io/name: zxporter-nodemon app.kubernetes.io/instance: zxporter-nodemon-gpu - app.kubernetes.io/version: "0.1.6" + app.kubernetes.io/version: "0.1.25" app.kubernetes.io/managed-by: Helm annotations: ignore-check.kube-linter.io/privileged-container: "This daemon set needs to run DCGM Exporter as privileged to access the GPU metrics." @@ -1287,6 +1429,14 @@ spec: memory: 256Mi - name: dcgm-exporter securityContext: + # SYS_ADMIN alone is not enough: enumerating MIG Compute Instance + # profiles via NVML needs broader host access than that one + # capability grants (confirmed live -- SYS_ADMIN-only reproduces + # "CacheManager Init Failed. Error: -17" the instant any MIG + # instance exists on the node; switching to privileged fixes it + # with zero other changes). This is a generic NVML/driver + # requirement, not provider-specific, so unlike the hostPath/ + # LD_LIBRARY_PATH blocks below it must not be gated on gcp only. capabilities: add: - SYS_ADMIN @@ -1314,6 +1464,16 @@ spec: valueFrom: fieldRef: fieldPath: spec.nodeName + # Without this, nvidia-container-runtime's "auto" mode only exposes + # whole/non-MIG GPUs to a non-privileged container with no explicit + # device-plugin resource request (which this sidecar deliberately + # doesn't make, since it must see every GPU on the node, not just + # one). MIG-partitioned GPUs stay invisible without it -- confirmed + # by direct comparison against GPU Operator's own dcgm-exporter, + # which gets the same visibility via privileged:true instead; this + # is the minimal fix that doesn't need the broader grant. + - name: "NVIDIA_VISIBLE_DEVICES" + value: "all" - name: "LD_LIBRARY_PATH" value: "/usr/local/nvidia/lib64" - name: "DCGM_EXPORTER_KUBERNETES_GPU_ID_TYPE" @@ -1326,3 +1486,7 @@ spec: mountPath: /usr/local/nvidia - name: zxporter-nodemon-dcgm-metrics mountPath: "/etc/dcgm-exporter" + resources: + requests: + cpu: 50m + memory: 128Mi diff --git a/dist/backend-install-gcp.yaml b/dist/backend-install-gcp.yaml index 3edd86a0..a4c24e48 100644 --- a/dist/backend-install-gcp.yaml +++ b/dist/backend-install-gcp.yaml @@ -287,6 +287,8 @@ rules: - apiGroups: - argoproj.io resources: + - applications + - applicationsets - rollouts verbs: - get @@ -334,6 +336,14 @@ rules: - get - list - watch + - apiGroups: + - coordination.k8s.io + resourceNames: + - dz-scheduler + resources: + - leases + verbs: + - get - apiGroups: - dakr.devzero.io resources: @@ -385,6 +395,45 @@ rules: - get - list - watch + - apiGroups: + - flagger.app + resources: + - alertproviders + - canaries + - metrictemplates + verbs: + - get + - list + - watch + - apiGroups: + - fluxcd.controlplane.io + resources: + - fluxinstances + - fluxreports + - resourcesetinputproviders + - resourcesets + verbs: + - get + - list + - watch + - apiGroups: + - helm.toolkit.fluxcd.io + resources: + - helmreleases + verbs: + - get + - list + - watch + - apiGroups: + - image.toolkit.fluxcd.io + resources: + - imagepolicies + - imagerepositories + - imageupdateautomations + verbs: + - get + - list + - watch - apiGroups: - karpenter.azure.com resources: @@ -449,6 +498,14 @@ rules: - get - list - watch + - apiGroups: + - kustomize.toolkit.fluxcd.io + resources: + - kustomizations + verbs: + - get + - list + - watch - apiGroups: - kyverno.io resources: @@ -477,6 +534,16 @@ rules: - get - list - watch + - apiGroups: + - notification.toolkit.fluxcd.io + resources: + - alerts + - providers + - receivers + verbs: + - get + - list + - watch - apiGroups: - policy resources: @@ -514,6 +581,19 @@ rules: - get - list - watch + - apiGroups: + - source.toolkit.fluxcd.io + resources: + - buckets + - externalartifacts + - gitrepositories + - helmcharts + - helmrepositories + - ocirepositories + verbs: + - get + - list + - watch - apiGroups: - sparkoperator.k8s.io resources: @@ -654,6 +734,19 @@ subjects: name: devzero-zxporter-controller-manager namespace: {{.zxporter_namespace}} --- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: devzero-zxporter-metrics-reader-rolebinding +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: devzero-zxporter-metrics-reader +subjects: + - kind: ServiceAccount + name: devzero-zxporter-controller-manager + namespace: {{.zxporter_namespace}} +--- apiVersion: v1 data: BUFFER_SIZE: "" @@ -693,6 +786,7 @@ data: EXCLUDED_STORAGECLASSES: "" EXCLUDED_VPAS: "" K8S_PROVIDER: '{{ .k8s_provider }}' + KEDA_METRICS_ENDPOINT: "" KUBE_CONTEXT_NAME: '{{ .kube_context_name }}' KUBELET_FALLBACK_TIMEOUT: 15s MASK_SECRET_DATA: "" @@ -701,6 +795,7 @@ data: NODE_METRICS_CONCURRENCY: "20" NODE_METRICS_INTERVAL: "" NODEMON_REQUEST_TIMEOUT: 15s + SCHEDULER_METRICS_SCRAPE_INTERVAL: "" TARGET_NAMESPACES: "" TOKEN_CONFIGMAP_NAME: devzero-zxporter-env-config TOKEN_CREDENTIALS_SECRET_NAME: devzero-zxporter-credentials @@ -811,6 +906,8 @@ spec: key: CLUSTER_TOKEN name: devzero-zxporter-token optional: true + - name: CLUSTER_TOKEN_SOURCE + value: mirror image: docker.io/devzeroinc/zxporter:latest livenessProbe: httpGet: @@ -870,16 +967,21 @@ metadata: name: zxporter-nodemon namespace: {{.zxporter_namespace}} labels: - helm.sh/chart: zxporter-nodemon-0.1.6 + helm.sh/chart: zxporter-nodemon-0.1.25 app.kubernetes.io/name: zxporter-nodemon app.kubernetes.io/instance: zxporter-nodemon - app.kubernetes.io/version: "0.1.6" + app.kubernetes.io/version: "0.1.25" app.kubernetes.io/managed-by: Helm + app.kubernetes.io/part-of: dz-platform + devzero.io/component: zxporter-nodemon --- # Source: zxporter-nodemon/templates/dcgm-exporter-configmap.yaml apiVersion: v1 kind: ConfigMap metadata: + labels: + app.kubernetes.io/part-of: dz-platform + devzero.io/component: zxporter-nodemon name: zxporter-nodemon-dcgm-metrics namespace: {{.zxporter_namespace}} data: @@ -895,8 +997,12 @@ data: DCGM_FI_DEV_GPU_UTIL, gauge, GPU utilization (in %). # DCGM_FI_PROF_SM_ACTIVE, gauge, The ratio of cycles an SM has at least 1 warp assigned # DCGM_FI_PROF_SM_OCCUPANCY, gauge, The fraction of resident warps on a multiprocessor - # DCGM_FI_PROF_GR_ENGINE_ACTIVE, gauge, Ratio of time the graphics engine is active (in %). - # DCGM_FI_PROF_DRAM_ACTIVE, gauge, The ratio of cycles the device memory interface is active sending or receiving data. + # Enabled: required for per-MIG-instance profiling data (GPU_I_PROFILE/GPU_I_ID + # labels only attach to DCGM_FI_PROF_* fields, never to plain DCGM_FI_DEV_* + # ones) and directly consumed by nodemon's GPUMigInstance parsing + # (GraphicsEngineActive/DRAMActive/TensorActive below). + DCGM_FI_PROF_GR_ENGINE_ACTIVE, gauge, Ratio of time the graphics engine is active (in %). + DCGM_FI_PROF_DRAM_ACTIVE, gauge, The ratio of cycles the device memory interface is active sending or receiving data. # Memory usage,, DCGM_FI_DEV_FB_FREE, gauge, Framebuffer memory free (in MiB). @@ -915,7 +1021,9 @@ data: # DCGM_FI_PROF_PIPE_FP16_ACTIVE, gauge, Ratio of cycles the fp16 pipe is active. # DCGM_FI_PROF_PIPE_FP32_ACTIVE, gauge, Ratio of cycles the fp32 pipe is active. # DCGM_FI_PROF_PIPE_FP64_ACTIVE, gauge, Ratio of cycles the fp64 pipe is active. - # DCGM_FI_PROF_PIPE_TENSOR_ACTIVE, gauge, The ratio of cycles the tensor (HMMA) pipe is active (off the peak sustained elapsed cycles) + # Enabled: see the GR_ENGINE_ACTIVE/DRAM_ACTIVE note above -- this is + # GPUMigInstance.TensorActive. + DCGM_FI_PROF_PIPE_TENSOR_ACTIVE, gauge, The ratio of cycles the tensor (HMMA) pipe is active (off the peak sustained elapsed cycles) # Health,, # DCGM_FI_DEV_CLOCKS_EVENT_REASONS is not supported by DCGM 3.3.7 @@ -936,6 +1044,9 @@ data: apiVersion: v1 kind: ConfigMap metadata: + labels: + app.kubernetes.io/part-of: dz-platform + devzero.io/component: zxporter-nodemon name: zxporter-nodemon-zxporter-nodemon namespace: {{.zxporter_namespace}} data: @@ -946,11 +1057,13 @@ kind: ClusterRole metadata: name: zxporter-nodemon labels: - helm.sh/chart: zxporter-nodemon-0.1.6 + helm.sh/chart: zxporter-nodemon-0.1.25 app.kubernetes.io/name: zxporter-nodemon app.kubernetes.io/instance: zxporter-nodemon - app.kubernetes.io/version: "0.1.6" + app.kubernetes.io/version: "0.1.25" app.kubernetes.io/managed-by: Helm + app.kubernetes.io/part-of: dz-platform + devzero.io/component: zxporter-nodemon rules: - apiGroups: - "" @@ -997,11 +1110,13 @@ kind: ClusterRoleBinding metadata: name: zxporter-nodemon labels: - helm.sh/chart: zxporter-nodemon-0.1.6 + helm.sh/chart: zxporter-nodemon-0.1.25 app.kubernetes.io/name: zxporter-nodemon app.kubernetes.io/instance: zxporter-nodemon - app.kubernetes.io/version: "0.1.6" + app.kubernetes.io/version: "0.1.25" app.kubernetes.io/managed-by: Helm + app.kubernetes.io/part-of: dz-platform + devzero.io/component: zxporter-nodemon roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole @@ -1018,10 +1133,12 @@ metadata: name: zxporter-nodemon namespace: {{.zxporter_namespace}} labels: - helm.sh/chart: zxporter-nodemon-0.1.6 + app.kubernetes.io/part-of: dz-platform + devzero.io/component: zxporter-nodemon + helm.sh/chart: zxporter-nodemon-0.1.25 app.kubernetes.io/name: zxporter-nodemon app.kubernetes.io/instance: zxporter-nodemon - app.kubernetes.io/version: "0.1.6" + app.kubernetes.io/version: "0.1.25" app.kubernetes.io/managed-by: Helm annotations: ignore-check.kube-linter.io/privileged-container: "This daemon set needs to run DCGM Exporter as privileged to access the GPU metrics." @@ -1080,6 +1197,13 @@ spec: capabilities: add: - SYS_PTRACE + # Bottlerocket (SELinux enforcing) gives each container a unique MCS + # category pair, which denies reading other containers' files via + # /proc//root (JVM hsperfdata, runtime binaries). Granting the + # full category range keeps the container_t type; it is a no-op where + # SELinux is not enforcing. + seLinuxOptions: + level: "s0:c0.c1023" image: "docker.io/devzeroinc/zxporter-nodemon:latest" imagePullPolicy: IfNotPresent ports: @@ -1133,6 +1257,14 @@ spec: memory: 256Mi - name: dcgm-exporter securityContext: + # SYS_ADMIN alone is not enough: enumerating MIG Compute Instance + # profiles via NVML needs broader host access than that one + # capability grants (confirmed live -- SYS_ADMIN-only reproduces + # "CacheManager Init Failed. Error: -17" the instant any MIG + # instance exists on the node; switching to privileged fixes it + # with zero other changes). This is a generic NVML/driver + # requirement, not provider-specific, so unlike the hostPath/ + # LD_LIBRARY_PATH blocks below it must not be gated on gcp only. capabilities: add: - SYS_ADMIN @@ -1160,6 +1292,16 @@ spec: valueFrom: fieldRef: fieldPath: spec.nodeName + # Without this, nvidia-container-runtime's "auto" mode only exposes + # whole/non-MIG GPUs to a non-privileged container with no explicit + # device-plugin resource request (which this sidecar deliberately + # doesn't make, since it must see every GPU on the node, not just + # one). MIG-partitioned GPUs stay invisible without it -- confirmed + # by direct comparison against GPU Operator's own dcgm-exporter, + # which gets the same visibility via privileged:true instead; this + # is the minimal fix that doesn't need the broader grant. + - name: "NVIDIA_VISIBLE_DEVICES" + value: "all" - name: "LD_LIBRARY_PATH" value: "/usr/local/nvidia/lib64" - name: "DCGM_EXPORTER_KUBERNETES_GPU_ID_TYPE" @@ -1172,6 +1314,10 @@ spec: mountPath: /usr/local/nvidia - name: zxporter-nodemon-dcgm-metrics mountPath: "/etc/dcgm-exporter" + resources: + requests: + cpu: 50m + memory: 128Mi --- # Source: zxporter-nodemon/templates/daemonset.yaml apiVersion: apps/v1 @@ -1180,10 +1326,12 @@ metadata: name: zxporter-nodemon-gpu namespace: {{.zxporter_namespace}} labels: - helm.sh/chart: zxporter-nodemon-0.1.6 + app.kubernetes.io/part-of: dz-platform + devzero.io/component: zxporter-nodemon + helm.sh/chart: zxporter-nodemon-0.1.25 app.kubernetes.io/name: zxporter-nodemon app.kubernetes.io/instance: zxporter-nodemon-gpu - app.kubernetes.io/version: "0.1.6" + app.kubernetes.io/version: "0.1.25" app.kubernetes.io/managed-by: Helm annotations: ignore-check.kube-linter.io/privileged-container: "This daemon set needs to run DCGM Exporter as privileged to access the GPU metrics." @@ -1245,6 +1393,13 @@ spec: capabilities: add: - SYS_PTRACE + # Bottlerocket (SELinux enforcing) gives each container a unique MCS + # category pair, which denies reading other containers' files via + # /proc//root (JVM hsperfdata, runtime binaries). Granting the + # full category range keeps the container_t type; it is a no-op where + # SELinux is not enforcing. + seLinuxOptions: + level: "s0:c0.c1023" image: "docker.io/devzeroinc/zxporter-nodemon:latest" imagePullPolicy: IfNotPresent ports: @@ -1298,6 +1453,14 @@ spec: memory: 256Mi - name: dcgm-exporter securityContext: + # SYS_ADMIN alone is not enough: enumerating MIG Compute Instance + # profiles via NVML needs broader host access than that one + # capability grants (confirmed live -- SYS_ADMIN-only reproduces + # "CacheManager Init Failed. Error: -17" the instant any MIG + # instance exists on the node; switching to privileged fixes it + # with zero other changes). This is a generic NVML/driver + # requirement, not provider-specific, so unlike the hostPath/ + # LD_LIBRARY_PATH blocks below it must not be gated on gcp only. capabilities: add: - SYS_ADMIN @@ -1325,6 +1488,16 @@ spec: valueFrom: fieldRef: fieldPath: spec.nodeName + # Without this, nvidia-container-runtime's "auto" mode only exposes + # whole/non-MIG GPUs to a non-privileged container with no explicit + # device-plugin resource request (which this sidecar deliberately + # doesn't make, since it must see every GPU on the node, not just + # one). MIG-partitioned GPUs stay invisible without it -- confirmed + # by direct comparison against GPU Operator's own dcgm-exporter, + # which gets the same visibility via privileged:true instead; this + # is the minimal fix that doesn't need the broader grant. + - name: "NVIDIA_VISIBLE_DEVICES" + value: "all" - name: "LD_LIBRARY_PATH" value: "/usr/local/nvidia/lib64" - name: "DCGM_EXPORTER_KUBERNETES_GPU_ID_TYPE" @@ -1337,3 +1510,7 @@ spec: mountPath: /usr/local/nvidia - name: zxporter-nodemon-dcgm-metrics mountPath: "/etc/dcgm-exporter" + resources: + requests: + cpu: 50m + memory: 128Mi diff --git a/dist/backend-install-lowpriv.yaml b/dist/backend-install-lowpriv.yaml index 065b6b40..9338e31f 100644 --- a/dist/backend-install-lowpriv.yaml +++ b/dist/backend-install-lowpriv.yaml @@ -287,6 +287,8 @@ rules: - apiGroups: - argoproj.io resources: + - applications + - applicationsets - rollouts verbs: - get @@ -334,6 +336,14 @@ rules: - get - list - watch + - apiGroups: + - coordination.k8s.io + resourceNames: + - dz-scheduler + resources: + - leases + verbs: + - get - apiGroups: - dakr.devzero.io resources: @@ -385,6 +395,45 @@ rules: - get - list - watch + - apiGroups: + - flagger.app + resources: + - alertproviders + - canaries + - metrictemplates + verbs: + - get + - list + - watch + - apiGroups: + - fluxcd.controlplane.io + resources: + - fluxinstances + - fluxreports + - resourcesetinputproviders + - resourcesets + verbs: + - get + - list + - watch + - apiGroups: + - helm.toolkit.fluxcd.io + resources: + - helmreleases + verbs: + - get + - list + - watch + - apiGroups: + - image.toolkit.fluxcd.io + resources: + - imagepolicies + - imagerepositories + - imageupdateautomations + verbs: + - get + - list + - watch - apiGroups: - karpenter.azure.com resources: @@ -449,6 +498,14 @@ rules: - get - list - watch + - apiGroups: + - kustomize.toolkit.fluxcd.io + resources: + - kustomizations + verbs: + - get + - list + - watch - apiGroups: - kyverno.io resources: @@ -477,6 +534,16 @@ rules: - get - list - watch + - apiGroups: + - notification.toolkit.fluxcd.io + resources: + - alerts + - providers + - receivers + verbs: + - get + - list + - watch - apiGroups: - policy resources: @@ -514,6 +581,19 @@ rules: - get - list - watch + - apiGroups: + - source.toolkit.fluxcd.io + resources: + - buckets + - externalartifacts + - gitrepositories + - helmcharts + - helmrepositories + - ocirepositories + verbs: + - get + - list + - watch - apiGroups: - sparkoperator.k8s.io resources: @@ -654,6 +734,19 @@ subjects: name: devzero-zxporter-controller-manager namespace: {{.zxporter_namespace}} --- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: devzero-zxporter-metrics-reader-rolebinding +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: devzero-zxporter-metrics-reader +subjects: + - kind: ServiceAccount + name: devzero-zxporter-controller-manager + namespace: {{.zxporter_namespace}} +--- apiVersion: v1 data: BUFFER_SIZE: "" @@ -693,6 +786,7 @@ data: EXCLUDED_STORAGECLASSES: "" EXCLUDED_VPAS: "" K8S_PROVIDER: '{{ .k8s_provider }}' + KEDA_METRICS_ENDPOINT: "" KUBE_CONTEXT_NAME: '{{ .kube_context_name }}' KUBELET_FALLBACK_TIMEOUT: 15s MASK_SECRET_DATA: "" @@ -701,6 +795,7 @@ data: NODE_METRICS_CONCURRENCY: "20" NODE_METRICS_INTERVAL: "" NODEMON_REQUEST_TIMEOUT: 15s + SCHEDULER_METRICS_SCRAPE_INTERVAL: "" TARGET_NAMESPACES: "" TOKEN_CONFIGMAP_NAME: devzero-zxporter-env-config TOKEN_CREDENTIALS_SECRET_NAME: devzero-zxporter-credentials @@ -811,6 +906,8 @@ spec: key: CLUSTER_TOKEN name: devzero-zxporter-token optional: true + - name: CLUSTER_TOKEN_SOURCE + value: mirror image: docker.io/devzeroinc/zxporter:latest livenessProbe: httpGet: @@ -870,16 +967,21 @@ metadata: name: zxporter-nodemon namespace: {{.zxporter_namespace}} labels: - helm.sh/chart: zxporter-nodemon-0.1.6 + helm.sh/chart: zxporter-nodemon-0.1.25 app.kubernetes.io/name: zxporter-nodemon app.kubernetes.io/instance: zxporter-nodemon - app.kubernetes.io/version: "0.1.6" + app.kubernetes.io/version: "0.1.25" app.kubernetes.io/managed-by: Helm + app.kubernetes.io/part-of: dz-platform + devzero.io/component: zxporter-nodemon --- # Source: zxporter-nodemon/templates/dcgm-exporter-configmap.yaml apiVersion: v1 kind: ConfigMap metadata: + labels: + app.kubernetes.io/part-of: dz-platform + devzero.io/component: zxporter-nodemon name: zxporter-nodemon-dcgm-metrics namespace: {{.zxporter_namespace}} data: @@ -895,8 +997,12 @@ data: DCGM_FI_DEV_GPU_UTIL, gauge, GPU utilization (in %). # DCGM_FI_PROF_SM_ACTIVE, gauge, The ratio of cycles an SM has at least 1 warp assigned # DCGM_FI_PROF_SM_OCCUPANCY, gauge, The fraction of resident warps on a multiprocessor - # DCGM_FI_PROF_GR_ENGINE_ACTIVE, gauge, Ratio of time the graphics engine is active (in %). - # DCGM_FI_PROF_DRAM_ACTIVE, gauge, The ratio of cycles the device memory interface is active sending or receiving data. + # Enabled: required for per-MIG-instance profiling data (GPU_I_PROFILE/GPU_I_ID + # labels only attach to DCGM_FI_PROF_* fields, never to plain DCGM_FI_DEV_* + # ones) and directly consumed by nodemon's GPUMigInstance parsing + # (GraphicsEngineActive/DRAMActive/TensorActive below). + DCGM_FI_PROF_GR_ENGINE_ACTIVE, gauge, Ratio of time the graphics engine is active (in %). + DCGM_FI_PROF_DRAM_ACTIVE, gauge, The ratio of cycles the device memory interface is active sending or receiving data. # Memory usage,, DCGM_FI_DEV_FB_FREE, gauge, Framebuffer memory free (in MiB). @@ -915,7 +1021,9 @@ data: # DCGM_FI_PROF_PIPE_FP16_ACTIVE, gauge, Ratio of cycles the fp16 pipe is active. # DCGM_FI_PROF_PIPE_FP32_ACTIVE, gauge, Ratio of cycles the fp32 pipe is active. # DCGM_FI_PROF_PIPE_FP64_ACTIVE, gauge, Ratio of cycles the fp64 pipe is active. - # DCGM_FI_PROF_PIPE_TENSOR_ACTIVE, gauge, The ratio of cycles the tensor (HMMA) pipe is active (off the peak sustained elapsed cycles) + # Enabled: see the GR_ENGINE_ACTIVE/DRAM_ACTIVE note above -- this is + # GPUMigInstance.TensorActive. + DCGM_FI_PROF_PIPE_TENSOR_ACTIVE, gauge, The ratio of cycles the tensor (HMMA) pipe is active (off the peak sustained elapsed cycles) # Health,, # DCGM_FI_DEV_CLOCKS_EVENT_REASONS is not supported by DCGM 3.3.7 @@ -936,6 +1044,9 @@ data: apiVersion: v1 kind: ConfigMap metadata: + labels: + app.kubernetes.io/part-of: dz-platform + devzero.io/component: zxporter-nodemon name: zxporter-nodemon-zxporter-nodemon namespace: {{.zxporter_namespace}} data: @@ -946,11 +1057,13 @@ kind: ClusterRole metadata: name: zxporter-nodemon labels: - helm.sh/chart: zxporter-nodemon-0.1.6 + helm.sh/chart: zxporter-nodemon-0.1.25 app.kubernetes.io/name: zxporter-nodemon app.kubernetes.io/instance: zxporter-nodemon - app.kubernetes.io/version: "0.1.6" + app.kubernetes.io/version: "0.1.25" app.kubernetes.io/managed-by: Helm + app.kubernetes.io/part-of: dz-platform + devzero.io/component: zxporter-nodemon rules: - apiGroups: - "" @@ -959,6 +1072,7 @@ rules: verbs: - get - list + - watch - apiGroups: - apps resources: @@ -996,11 +1110,13 @@ kind: ClusterRoleBinding metadata: name: zxporter-nodemon labels: - helm.sh/chart: zxporter-nodemon-0.1.6 + helm.sh/chart: zxporter-nodemon-0.1.25 app.kubernetes.io/name: zxporter-nodemon app.kubernetes.io/instance: zxporter-nodemon - app.kubernetes.io/version: "0.1.6" + app.kubernetes.io/version: "0.1.25" app.kubernetes.io/managed-by: Helm + app.kubernetes.io/part-of: dz-platform + devzero.io/component: zxporter-nodemon roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole @@ -1017,10 +1133,12 @@ metadata: name: zxporter-nodemon namespace: {{.zxporter_namespace}} labels: - helm.sh/chart: zxporter-nodemon-0.1.6 + app.kubernetes.io/part-of: dz-platform + devzero.io/component: zxporter-nodemon + helm.sh/chart: zxporter-nodemon-0.1.25 app.kubernetes.io/name: zxporter-nodemon app.kubernetes.io/instance: zxporter-nodemon - app.kubernetes.io/version: "0.1.6" + app.kubernetes.io/version: "0.1.25" app.kubernetes.io/managed-by: Helm annotations: ignore-check.kube-linter.io/privileged-container: "This daemon set needs to run DCGM Exporter as privileged to access the GPU metrics." @@ -1124,6 +1242,14 @@ spec: memory: 256Mi - name: dcgm-exporter securityContext: + # SYS_ADMIN alone is not enough: enumerating MIG Compute Instance + # profiles via NVML needs broader host access than that one + # capability grants (confirmed live -- SYS_ADMIN-only reproduces + # "CacheManager Init Failed. Error: -17" the instant any MIG + # instance exists on the node; switching to privileged fixes it + # with zero other changes). This is a generic NVML/driver + # requirement, not provider-specific, so unlike the hostPath/ + # LD_LIBRARY_PATH blocks below it must not be gated on gcp only. capabilities: add: - SYS_ADMIN @@ -1131,6 +1257,7 @@ spec: - NET_RAW runAsNonRoot: false runAsUser: 0 + privileged: true image: "public.ecr.aws/devzeroinc/dcgm-exporter:4.5.2-4.8.1-ubuntu22.04" imagePullPolicy: IfNotPresent command: ["/bin/bash", "-c"] @@ -1150,12 +1277,26 @@ spec: valueFrom: fieldRef: fieldPath: spec.nodeName + # Without this, nvidia-container-runtime's "auto" mode only exposes + # whole/non-MIG GPUs to a non-privileged container with no explicit + # device-plugin resource request (which this sidecar deliberately + # doesn't make, since it must see every GPU on the node, not just + # one). MIG-partitioned GPUs stay invisible without it -- confirmed + # by direct comparison against GPU Operator's own dcgm-exporter, + # which gets the same visibility via privileged:true instead; this + # is the minimal fix that doesn't need the broader grant. + - name: "NVIDIA_VISIBLE_DEVICES" + value: "all" volumeMounts: - name: "pod-gpu-resources" readOnly: true mountPath: "/var/lib/kubelet/pod-resources" - name: zxporter-nodemon-dcgm-metrics mountPath: "/etc/dcgm-exporter" + resources: + requests: + cpu: 50m + memory: 128Mi --- # Source: zxporter-nodemon/templates/daemonset.yaml apiVersion: apps/v1 @@ -1164,10 +1305,12 @@ metadata: name: zxporter-nodemon-gpu namespace: {{.zxporter_namespace}} labels: - helm.sh/chart: zxporter-nodemon-0.1.6 + app.kubernetes.io/part-of: dz-platform + devzero.io/component: zxporter-nodemon + helm.sh/chart: zxporter-nodemon-0.1.25 app.kubernetes.io/name: zxporter-nodemon app.kubernetes.io/instance: zxporter-nodemon-gpu - app.kubernetes.io/version: "0.1.6" + app.kubernetes.io/version: "0.1.25" app.kubernetes.io/managed-by: Helm annotations: ignore-check.kube-linter.io/privileged-container: "This daemon set needs to run DCGM Exporter as privileged to access the GPU metrics." @@ -1274,6 +1417,14 @@ spec: memory: 256Mi - name: dcgm-exporter securityContext: + # SYS_ADMIN alone is not enough: enumerating MIG Compute Instance + # profiles via NVML needs broader host access than that one + # capability grants (confirmed live -- SYS_ADMIN-only reproduces + # "CacheManager Init Failed. Error: -17" the instant any MIG + # instance exists on the node; switching to privileged fixes it + # with zero other changes). This is a generic NVML/driver + # requirement, not provider-specific, so unlike the hostPath/ + # LD_LIBRARY_PATH blocks below it must not be gated on gcp only. capabilities: add: - SYS_ADMIN @@ -1281,6 +1432,7 @@ spec: - NET_RAW runAsNonRoot: false runAsUser: 0 + privileged: true image: "public.ecr.aws/devzeroinc/dcgm-exporter:4.5.2-4.8.1-ubuntu22.04" imagePullPolicy: IfNotPresent command: ["/bin/bash", "-c"] @@ -1300,9 +1452,23 @@ spec: valueFrom: fieldRef: fieldPath: spec.nodeName + # Without this, nvidia-container-runtime's "auto" mode only exposes + # whole/non-MIG GPUs to a non-privileged container with no explicit + # device-plugin resource request (which this sidecar deliberately + # doesn't make, since it must see every GPU on the node, not just + # one). MIG-partitioned GPUs stay invisible without it -- confirmed + # by direct comparison against GPU Operator's own dcgm-exporter, + # which gets the same visibility via privileged:true instead; this + # is the minimal fix that doesn't need the broader grant. + - name: "NVIDIA_VISIBLE_DEVICES" + value: "all" volumeMounts: - name: "pod-gpu-resources" readOnly: true mountPath: "/var/lib/kubelet/pod-resources" - name: zxporter-nodemon-dcgm-metrics mountPath: "/etc/dcgm-exporter" + resources: + requests: + cpu: 50m + memory: 128Mi diff --git a/dist/backend-install.yaml b/dist/backend-install.yaml index 16bff229..c622de4d 100644 --- a/dist/backend-install.yaml +++ b/dist/backend-install.yaml @@ -287,6 +287,8 @@ rules: - apiGroups: - argoproj.io resources: + - applications + - applicationsets - rollouts verbs: - get @@ -334,6 +336,14 @@ rules: - get - list - watch + - apiGroups: + - coordination.k8s.io + resourceNames: + - dz-scheduler + resources: + - leases + verbs: + - get - apiGroups: - dakr.devzero.io resources: @@ -385,6 +395,45 @@ rules: - get - list - watch + - apiGroups: + - flagger.app + resources: + - alertproviders + - canaries + - metrictemplates + verbs: + - get + - list + - watch + - apiGroups: + - fluxcd.controlplane.io + resources: + - fluxinstances + - fluxreports + - resourcesetinputproviders + - resourcesets + verbs: + - get + - list + - watch + - apiGroups: + - helm.toolkit.fluxcd.io + resources: + - helmreleases + verbs: + - get + - list + - watch + - apiGroups: + - image.toolkit.fluxcd.io + resources: + - imagepolicies + - imagerepositories + - imageupdateautomations + verbs: + - get + - list + - watch - apiGroups: - karpenter.azure.com resources: @@ -449,6 +498,14 @@ rules: - get - list - watch + - apiGroups: + - kustomize.toolkit.fluxcd.io + resources: + - kustomizations + verbs: + - get + - list + - watch - apiGroups: - kyverno.io resources: @@ -477,6 +534,16 @@ rules: - get - list - watch + - apiGroups: + - notification.toolkit.fluxcd.io + resources: + - alerts + - providers + - receivers + verbs: + - get + - list + - watch - apiGroups: - policy resources: @@ -514,6 +581,19 @@ rules: - get - list - watch + - apiGroups: + - source.toolkit.fluxcd.io + resources: + - buckets + - externalartifacts + - gitrepositories + - helmcharts + - helmrepositories + - ocirepositories + verbs: + - get + - list + - watch - apiGroups: - sparkoperator.k8s.io resources: @@ -654,6 +734,19 @@ subjects: name: devzero-zxporter-controller-manager namespace: {{.zxporter_namespace}} --- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: devzero-zxporter-metrics-reader-rolebinding +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: devzero-zxporter-metrics-reader +subjects: + - kind: ServiceAccount + name: devzero-zxporter-controller-manager + namespace: {{.zxporter_namespace}} +--- apiVersion: v1 data: BUFFER_SIZE: "" @@ -693,6 +786,7 @@ data: EXCLUDED_STORAGECLASSES: "" EXCLUDED_VPAS: "" K8S_PROVIDER: '{{ .k8s_provider }}' + KEDA_METRICS_ENDPOINT: "" KUBE_CONTEXT_NAME: '{{ .kube_context_name }}' KUBELET_FALLBACK_TIMEOUT: 15s MASK_SECRET_DATA: "" @@ -701,6 +795,7 @@ data: NODE_METRICS_CONCURRENCY: "20" NODE_METRICS_INTERVAL: "" NODEMON_REQUEST_TIMEOUT: 15s + SCHEDULER_METRICS_SCRAPE_INTERVAL: "" TARGET_NAMESPACES: "" TOKEN_CONFIGMAP_NAME: devzero-zxporter-env-config TOKEN_CREDENTIALS_SECRET_NAME: devzero-zxporter-credentials @@ -811,6 +906,8 @@ spec: key: CLUSTER_TOKEN name: devzero-zxporter-token optional: true + - name: CLUSTER_TOKEN_SOURCE + value: mirror image: docker.io/devzeroinc/zxporter:latest livenessProbe: httpGet: @@ -870,16 +967,21 @@ metadata: name: zxporter-nodemon namespace: {{.zxporter_namespace}} labels: - helm.sh/chart: zxporter-nodemon-0.1.6 + helm.sh/chart: zxporter-nodemon-0.1.25 app.kubernetes.io/name: zxporter-nodemon app.kubernetes.io/instance: zxporter-nodemon - app.kubernetes.io/version: "0.1.6" + app.kubernetes.io/version: "0.1.25" app.kubernetes.io/managed-by: Helm + app.kubernetes.io/part-of: dz-platform + devzero.io/component: zxporter-nodemon --- # Source: zxporter-nodemon/templates/dcgm-exporter-configmap.yaml apiVersion: v1 kind: ConfigMap metadata: + labels: + app.kubernetes.io/part-of: dz-platform + devzero.io/component: zxporter-nodemon name: zxporter-nodemon-dcgm-metrics namespace: {{.zxporter_namespace}} data: @@ -895,8 +997,12 @@ data: DCGM_FI_DEV_GPU_UTIL, gauge, GPU utilization (in %). # DCGM_FI_PROF_SM_ACTIVE, gauge, The ratio of cycles an SM has at least 1 warp assigned # DCGM_FI_PROF_SM_OCCUPANCY, gauge, The fraction of resident warps on a multiprocessor - # DCGM_FI_PROF_GR_ENGINE_ACTIVE, gauge, Ratio of time the graphics engine is active (in %). - # DCGM_FI_PROF_DRAM_ACTIVE, gauge, The ratio of cycles the device memory interface is active sending or receiving data. + # Enabled: required for per-MIG-instance profiling data (GPU_I_PROFILE/GPU_I_ID + # labels only attach to DCGM_FI_PROF_* fields, never to plain DCGM_FI_DEV_* + # ones) and directly consumed by nodemon's GPUMigInstance parsing + # (GraphicsEngineActive/DRAMActive/TensorActive below). + DCGM_FI_PROF_GR_ENGINE_ACTIVE, gauge, Ratio of time the graphics engine is active (in %). + DCGM_FI_PROF_DRAM_ACTIVE, gauge, The ratio of cycles the device memory interface is active sending or receiving data. # Memory usage,, DCGM_FI_DEV_FB_FREE, gauge, Framebuffer memory free (in MiB). @@ -915,7 +1021,9 @@ data: # DCGM_FI_PROF_PIPE_FP16_ACTIVE, gauge, Ratio of cycles the fp16 pipe is active. # DCGM_FI_PROF_PIPE_FP32_ACTIVE, gauge, Ratio of cycles the fp32 pipe is active. # DCGM_FI_PROF_PIPE_FP64_ACTIVE, gauge, Ratio of cycles the fp64 pipe is active. - # DCGM_FI_PROF_PIPE_TENSOR_ACTIVE, gauge, The ratio of cycles the tensor (HMMA) pipe is active (off the peak sustained elapsed cycles) + # Enabled: see the GR_ENGINE_ACTIVE/DRAM_ACTIVE note above -- this is + # GPUMigInstance.TensorActive. + DCGM_FI_PROF_PIPE_TENSOR_ACTIVE, gauge, The ratio of cycles the tensor (HMMA) pipe is active (off the peak sustained elapsed cycles) # Health,, # DCGM_FI_DEV_CLOCKS_EVENT_REASONS is not supported by DCGM 3.3.7 @@ -936,6 +1044,9 @@ data: apiVersion: v1 kind: ConfigMap metadata: + labels: + app.kubernetes.io/part-of: dz-platform + devzero.io/component: zxporter-nodemon name: zxporter-nodemon-zxporter-nodemon namespace: {{.zxporter_namespace}} data: @@ -946,11 +1057,13 @@ kind: ClusterRole metadata: name: zxporter-nodemon labels: - helm.sh/chart: zxporter-nodemon-0.1.6 + helm.sh/chart: zxporter-nodemon-0.1.25 app.kubernetes.io/name: zxporter-nodemon app.kubernetes.io/instance: zxporter-nodemon - app.kubernetes.io/version: "0.1.6" + app.kubernetes.io/version: "0.1.25" app.kubernetes.io/managed-by: Helm + app.kubernetes.io/part-of: dz-platform + devzero.io/component: zxporter-nodemon rules: - apiGroups: - "" @@ -997,11 +1110,13 @@ kind: ClusterRoleBinding metadata: name: zxporter-nodemon labels: - helm.sh/chart: zxporter-nodemon-0.1.6 + helm.sh/chart: zxporter-nodemon-0.1.25 app.kubernetes.io/name: zxporter-nodemon app.kubernetes.io/instance: zxporter-nodemon - app.kubernetes.io/version: "0.1.6" + app.kubernetes.io/version: "0.1.25" app.kubernetes.io/managed-by: Helm + app.kubernetes.io/part-of: dz-platform + devzero.io/component: zxporter-nodemon roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole @@ -1018,10 +1133,12 @@ metadata: name: zxporter-nodemon namespace: {{.zxporter_namespace}} labels: - helm.sh/chart: zxporter-nodemon-0.1.6 + app.kubernetes.io/part-of: dz-platform + devzero.io/component: zxporter-nodemon + helm.sh/chart: zxporter-nodemon-0.1.25 app.kubernetes.io/name: zxporter-nodemon app.kubernetes.io/instance: zxporter-nodemon - app.kubernetes.io/version: "0.1.6" + app.kubernetes.io/version: "0.1.25" app.kubernetes.io/managed-by: Helm annotations: ignore-check.kube-linter.io/privileged-container: "This daemon set needs to run DCGM Exporter as privileged to access the GPU metrics." @@ -1077,6 +1194,13 @@ spec: capabilities: add: - SYS_PTRACE + # Bottlerocket (SELinux enforcing) gives each container a unique MCS + # category pair, which denies reading other containers' files via + # /proc//root (JVM hsperfdata, runtime binaries). Granting the + # full category range keeps the container_t type; it is a no-op where + # SELinux is not enforcing. + seLinuxOptions: + level: "s0:c0.c1023" image: "docker.io/devzeroinc/zxporter-nodemon:latest" imagePullPolicy: IfNotPresent ports: @@ -1130,6 +1254,14 @@ spec: memory: 256Mi - name: dcgm-exporter securityContext: + # SYS_ADMIN alone is not enough: enumerating MIG Compute Instance + # profiles via NVML needs broader host access than that one + # capability grants (confirmed live -- SYS_ADMIN-only reproduces + # "CacheManager Init Failed. Error: -17" the instant any MIG + # instance exists on the node; switching to privileged fixes it + # with zero other changes). This is a generic NVML/driver + # requirement, not provider-specific, so unlike the hostPath/ + # LD_LIBRARY_PATH blocks below it must not be gated on gcp only. capabilities: add: - SYS_ADMIN @@ -1137,6 +1269,7 @@ spec: - NET_RAW runAsNonRoot: false runAsUser: 0 + privileged: true image: "public.ecr.aws/devzeroinc/dcgm-exporter:4.5.2-4.8.1-ubuntu22.04" imagePullPolicy: IfNotPresent command: ["/bin/bash", "-c"] @@ -1156,12 +1289,26 @@ spec: valueFrom: fieldRef: fieldPath: spec.nodeName + # Without this, nvidia-container-runtime's "auto" mode only exposes + # whole/non-MIG GPUs to a non-privileged container with no explicit + # device-plugin resource request (which this sidecar deliberately + # doesn't make, since it must see every GPU on the node, not just + # one). MIG-partitioned GPUs stay invisible without it -- confirmed + # by direct comparison against GPU Operator's own dcgm-exporter, + # which gets the same visibility via privileged:true instead; this + # is the minimal fix that doesn't need the broader grant. + - name: "NVIDIA_VISIBLE_DEVICES" + value: "all" volumeMounts: - name: "pod-gpu-resources" readOnly: true mountPath: "/var/lib/kubelet/pod-resources" - name: zxporter-nodemon-dcgm-metrics mountPath: "/etc/dcgm-exporter" + resources: + requests: + cpu: 50m + memory: 128Mi --- # Source: zxporter-nodemon/templates/daemonset.yaml apiVersion: apps/v1 @@ -1170,10 +1317,12 @@ metadata: name: zxporter-nodemon-gpu namespace: {{.zxporter_namespace}} labels: - helm.sh/chart: zxporter-nodemon-0.1.6 + app.kubernetes.io/part-of: dz-platform + devzero.io/component: zxporter-nodemon + helm.sh/chart: zxporter-nodemon-0.1.25 app.kubernetes.io/name: zxporter-nodemon app.kubernetes.io/instance: zxporter-nodemon-gpu - app.kubernetes.io/version: "0.1.6" + app.kubernetes.io/version: "0.1.25" app.kubernetes.io/managed-by: Helm annotations: ignore-check.kube-linter.io/privileged-container: "This daemon set needs to run DCGM Exporter as privileged to access the GPU metrics." @@ -1232,6 +1381,13 @@ spec: capabilities: add: - SYS_PTRACE + # Bottlerocket (SELinux enforcing) gives each container a unique MCS + # category pair, which denies reading other containers' files via + # /proc//root (JVM hsperfdata, runtime binaries). Granting the + # full category range keeps the container_t type; it is a no-op where + # SELinux is not enforcing. + seLinuxOptions: + level: "s0:c0.c1023" image: "docker.io/devzeroinc/zxporter-nodemon:latest" imagePullPolicy: IfNotPresent ports: @@ -1285,6 +1441,14 @@ spec: memory: 256Mi - name: dcgm-exporter securityContext: + # SYS_ADMIN alone is not enough: enumerating MIG Compute Instance + # profiles via NVML needs broader host access than that one + # capability grants (confirmed live -- SYS_ADMIN-only reproduces + # "CacheManager Init Failed. Error: -17" the instant any MIG + # instance exists on the node; switching to privileged fixes it + # with zero other changes). This is a generic NVML/driver + # requirement, not provider-specific, so unlike the hostPath/ + # LD_LIBRARY_PATH blocks below it must not be gated on gcp only. capabilities: add: - SYS_ADMIN @@ -1292,6 +1456,7 @@ spec: - NET_RAW runAsNonRoot: false runAsUser: 0 + privileged: true image: "public.ecr.aws/devzeroinc/dcgm-exporter:4.5.2-4.8.1-ubuntu22.04" imagePullPolicy: IfNotPresent command: ["/bin/bash", "-c"] @@ -1311,9 +1476,23 @@ spec: valueFrom: fieldRef: fieldPath: spec.nodeName + # Without this, nvidia-container-runtime's "auto" mode only exposes + # whole/non-MIG GPUs to a non-privileged container with no explicit + # device-plugin resource request (which this sidecar deliberately + # doesn't make, since it must see every GPU on the node, not just + # one). MIG-partitioned GPUs stay invisible without it -- confirmed + # by direct comparison against GPU Operator's own dcgm-exporter, + # which gets the same visibility via privileged:true instead; this + # is the minimal fix that doesn't need the broader grant. + - name: "NVIDIA_VISIBLE_DEVICES" + value: "all" volumeMounts: - name: "pod-gpu-resources" readOnly: true mountPath: "/var/lib/kubelet/pod-resources" - name: zxporter-nodemon-dcgm-metrics mountPath: "/etc/dcgm-exporter" + resources: + requests: + cpu: 50m + memory: 128Mi diff --git a/dist/install-gcp-lowpriv.yaml b/dist/install-gcp-lowpriv.yaml index b6eb53a6..6cd62529 100644 --- a/dist/install-gcp-lowpriv.yaml +++ b/dist/install-gcp-lowpriv.yaml @@ -287,6 +287,8 @@ rules: - apiGroups: - argoproj.io resources: + - applications + - applicationsets - rollouts verbs: - get @@ -334,6 +336,14 @@ rules: - get - list - watch +- apiGroups: + - coordination.k8s.io + resourceNames: + - dz-scheduler + resources: + - leases + verbs: + - get - apiGroups: - dakr.devzero.io resources: @@ -385,6 +395,45 @@ rules: - get - list - watch +- apiGroups: + - flagger.app + resources: + - alertproviders + - canaries + - metrictemplates + verbs: + - get + - list + - watch +- apiGroups: + - fluxcd.controlplane.io + resources: + - fluxinstances + - fluxreports + - resourcesetinputproviders + - resourcesets + verbs: + - get + - list + - watch +- apiGroups: + - helm.toolkit.fluxcd.io + resources: + - helmreleases + verbs: + - get + - list + - watch +- apiGroups: + - image.toolkit.fluxcd.io + resources: + - imagepolicies + - imagerepositories + - imageupdateautomations + verbs: + - get + - list + - watch - apiGroups: - karpenter.azure.com resources: @@ -449,6 +498,14 @@ rules: - get - list - watch +- apiGroups: + - kustomize.toolkit.fluxcd.io + resources: + - kustomizations + verbs: + - get + - list + - watch - apiGroups: - kyverno.io resources: @@ -477,6 +534,16 @@ rules: - get - list - watch +- apiGroups: + - notification.toolkit.fluxcd.io + resources: + - alerts + - providers + - receivers + verbs: + - get + - list + - watch - apiGroups: - policy resources: @@ -514,6 +581,19 @@ rules: - get - list - watch +- apiGroups: + - source.toolkit.fluxcd.io + resources: + - buckets + - externalartifacts + - gitrepositories + - helmcharts + - helmrepositories + - ocirepositories + verbs: + - get + - list + - watch - apiGroups: - sparkoperator.k8s.io resources: @@ -654,6 +734,19 @@ subjects: name: devzero-zxporter-controller-manager namespace: devzero-system --- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: devzero-zxporter-metrics-reader-rolebinding +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: devzero-zxporter-metrics-reader +subjects: +- kind: ServiceAccount + name: devzero-zxporter-controller-manager + namespace: devzero-system +--- apiVersion: v1 data: BUFFER_SIZE: "" @@ -693,6 +786,7 @@ data: EXCLUDED_STORAGECLASSES: "" EXCLUDED_VPAS: "" K8S_PROVIDER: '{{ .k8s_provider }}' + KEDA_METRICS_ENDPOINT: "" KUBE_CONTEXT_NAME: '{{ .kube_context_name }}' KUBELET_FALLBACK_TIMEOUT: 15s MASK_SECRET_DATA: "" @@ -701,6 +795,7 @@ data: NODE_METRICS_CONCURRENCY: "20" NODE_METRICS_INTERVAL: "" NODEMON_REQUEST_TIMEOUT: 15s + SCHEDULER_METRICS_SCRAPE_INTERVAL: "" TARGET_NAMESPACES: "" TOKEN_CONFIGMAP_NAME: devzero-zxporter-env-config TOKEN_CREDENTIALS_SECRET_NAME: devzero-zxporter-credentials @@ -811,6 +906,8 @@ spec: key: CLUSTER_TOKEN name: devzero-zxporter-token optional: true + - name: CLUSTER_TOKEN_SOURCE + value: mirror image: ttl.sh/zxporter:latest livenessProbe: httpGet: @@ -870,16 +967,21 @@ metadata: name: zxporter-nodemon namespace: devzero-system labels: - helm.sh/chart: zxporter-nodemon-0.1.6 + helm.sh/chart: zxporter-nodemon-0.1.25 app.kubernetes.io/name: zxporter-nodemon app.kubernetes.io/instance: zxporter-nodemon - app.kubernetes.io/version: "0.1.6" + app.kubernetes.io/version: "0.1.25" app.kubernetes.io/managed-by: Helm + app.kubernetes.io/part-of: dz-platform + devzero.io/component: zxporter-nodemon --- # Source: zxporter-nodemon/templates/dcgm-exporter-configmap.yaml apiVersion: v1 kind: ConfigMap metadata: + labels: + app.kubernetes.io/part-of: dz-platform + devzero.io/component: zxporter-nodemon name: zxporter-nodemon-dcgm-metrics namespace: devzero-system data: @@ -895,8 +997,12 @@ data: DCGM_FI_DEV_GPU_UTIL, gauge, GPU utilization (in %). # DCGM_FI_PROF_SM_ACTIVE, gauge, The ratio of cycles an SM has at least 1 warp assigned # DCGM_FI_PROF_SM_OCCUPANCY, gauge, The fraction of resident warps on a multiprocessor - # DCGM_FI_PROF_GR_ENGINE_ACTIVE, gauge, Ratio of time the graphics engine is active (in %). - # DCGM_FI_PROF_DRAM_ACTIVE, gauge, The ratio of cycles the device memory interface is active sending or receiving data. + # Enabled: required for per-MIG-instance profiling data (GPU_I_PROFILE/GPU_I_ID + # labels only attach to DCGM_FI_PROF_* fields, never to plain DCGM_FI_DEV_* + # ones) and directly consumed by nodemon's GPUMigInstance parsing + # (GraphicsEngineActive/DRAMActive/TensorActive below). + DCGM_FI_PROF_GR_ENGINE_ACTIVE, gauge, Ratio of time the graphics engine is active (in %). + DCGM_FI_PROF_DRAM_ACTIVE, gauge, The ratio of cycles the device memory interface is active sending or receiving data. # Memory usage,, DCGM_FI_DEV_FB_FREE, gauge, Framebuffer memory free (in MiB). @@ -915,7 +1021,9 @@ data: # DCGM_FI_PROF_PIPE_FP16_ACTIVE, gauge, Ratio of cycles the fp16 pipe is active. # DCGM_FI_PROF_PIPE_FP32_ACTIVE, gauge, Ratio of cycles the fp32 pipe is active. # DCGM_FI_PROF_PIPE_FP64_ACTIVE, gauge, Ratio of cycles the fp64 pipe is active. - # DCGM_FI_PROF_PIPE_TENSOR_ACTIVE, gauge, The ratio of cycles the tensor (HMMA) pipe is active (off the peak sustained elapsed cycles) + # Enabled: see the GR_ENGINE_ACTIVE/DRAM_ACTIVE note above -- this is + # GPUMigInstance.TensorActive. + DCGM_FI_PROF_PIPE_TENSOR_ACTIVE, gauge, The ratio of cycles the tensor (HMMA) pipe is active (off the peak sustained elapsed cycles) # Health,, # DCGM_FI_DEV_CLOCKS_EVENT_REASONS is not supported by DCGM 3.3.7 @@ -936,6 +1044,9 @@ data: apiVersion: v1 kind: ConfigMap metadata: + labels: + app.kubernetes.io/part-of: dz-platform + devzero.io/component: zxporter-nodemon name: zxporter-nodemon-zxporter-nodemon namespace: devzero-system data: @@ -946,11 +1057,13 @@ kind: ClusterRole metadata: name: zxporter-nodemon labels: - helm.sh/chart: zxporter-nodemon-0.1.6 + helm.sh/chart: zxporter-nodemon-0.1.25 app.kubernetes.io/name: zxporter-nodemon app.kubernetes.io/instance: zxporter-nodemon - app.kubernetes.io/version: "0.1.6" + app.kubernetes.io/version: "0.1.25" app.kubernetes.io/managed-by: Helm + app.kubernetes.io/part-of: dz-platform + devzero.io/component: zxporter-nodemon rules: - apiGroups: - "" @@ -959,6 +1072,7 @@ rules: verbs: - get - list + - watch - apiGroups: - apps resources: @@ -996,11 +1110,13 @@ kind: ClusterRoleBinding metadata: name: zxporter-nodemon labels: - helm.sh/chart: zxporter-nodemon-0.1.6 + helm.sh/chart: zxporter-nodemon-0.1.25 app.kubernetes.io/name: zxporter-nodemon app.kubernetes.io/instance: zxporter-nodemon - app.kubernetes.io/version: "0.1.6" + app.kubernetes.io/version: "0.1.25" app.kubernetes.io/managed-by: Helm + app.kubernetes.io/part-of: dz-platform + devzero.io/component: zxporter-nodemon roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole @@ -1017,10 +1133,12 @@ metadata: name: zxporter-nodemon namespace: devzero-system labels: - helm.sh/chart: zxporter-nodemon-0.1.6 + app.kubernetes.io/part-of: dz-platform + devzero.io/component: zxporter-nodemon + helm.sh/chart: zxporter-nodemon-0.1.25 app.kubernetes.io/name: zxporter-nodemon app.kubernetes.io/instance: zxporter-nodemon - app.kubernetes.io/version: "0.1.6" + app.kubernetes.io/version: "0.1.25" app.kubernetes.io/managed-by: Helm annotations: ignore-check.kube-linter.io/privileged-container: "This daemon set needs to run DCGM Exporter as privileged to access the GPU metrics." @@ -1127,6 +1245,14 @@ spec: memory: 256Mi - name: dcgm-exporter securityContext: + # SYS_ADMIN alone is not enough: enumerating MIG Compute Instance + # profiles via NVML needs broader host access than that one + # capability grants (confirmed live -- SYS_ADMIN-only reproduces + # "CacheManager Init Failed. Error: -17" the instant any MIG + # instance exists on the node; switching to privileged fixes it + # with zero other changes). This is a generic NVML/driver + # requirement, not provider-specific, so unlike the hostPath/ + # LD_LIBRARY_PATH blocks below it must not be gated on gcp only. capabilities: add: - SYS_ADMIN @@ -1154,6 +1280,16 @@ spec: valueFrom: fieldRef: fieldPath: spec.nodeName + # Without this, nvidia-container-runtime's "auto" mode only exposes + # whole/non-MIG GPUs to a non-privileged container with no explicit + # device-plugin resource request (which this sidecar deliberately + # doesn't make, since it must see every GPU on the node, not just + # one). MIG-partitioned GPUs stay invisible without it -- confirmed + # by direct comparison against GPU Operator's own dcgm-exporter, + # which gets the same visibility via privileged:true instead; this + # is the minimal fix that doesn't need the broader grant. + - name: "NVIDIA_VISIBLE_DEVICES" + value: "all" - name: "LD_LIBRARY_PATH" value: "/usr/local/nvidia/lib64" - name: "DCGM_EXPORTER_KUBERNETES_GPU_ID_TYPE" @@ -1166,6 +1302,10 @@ spec: mountPath: /usr/local/nvidia - name: zxporter-nodemon-dcgm-metrics mountPath: "/etc/dcgm-exporter" + resources: + requests: + cpu: 50m + memory: 128Mi --- # Source: zxporter-nodemon/templates/daemonset.yaml apiVersion: apps/v1 @@ -1174,10 +1314,12 @@ metadata: name: zxporter-nodemon-gpu namespace: devzero-system labels: - helm.sh/chart: zxporter-nodemon-0.1.6 + app.kubernetes.io/part-of: dz-platform + devzero.io/component: zxporter-nodemon + helm.sh/chart: zxporter-nodemon-0.1.25 app.kubernetes.io/name: zxporter-nodemon app.kubernetes.io/instance: zxporter-nodemon-gpu - app.kubernetes.io/version: "0.1.6" + app.kubernetes.io/version: "0.1.25" app.kubernetes.io/managed-by: Helm annotations: ignore-check.kube-linter.io/privileged-container: "This daemon set needs to run DCGM Exporter as privileged to access the GPU metrics." @@ -1287,6 +1429,14 @@ spec: memory: 256Mi - name: dcgm-exporter securityContext: + # SYS_ADMIN alone is not enough: enumerating MIG Compute Instance + # profiles via NVML needs broader host access than that one + # capability grants (confirmed live -- SYS_ADMIN-only reproduces + # "CacheManager Init Failed. Error: -17" the instant any MIG + # instance exists on the node; switching to privileged fixes it + # with zero other changes). This is a generic NVML/driver + # requirement, not provider-specific, so unlike the hostPath/ + # LD_LIBRARY_PATH blocks below it must not be gated on gcp only. capabilities: add: - SYS_ADMIN @@ -1314,6 +1464,16 @@ spec: valueFrom: fieldRef: fieldPath: spec.nodeName + # Without this, nvidia-container-runtime's "auto" mode only exposes + # whole/non-MIG GPUs to a non-privileged container with no explicit + # device-plugin resource request (which this sidecar deliberately + # doesn't make, since it must see every GPU on the node, not just + # one). MIG-partitioned GPUs stay invisible without it -- confirmed + # by direct comparison against GPU Operator's own dcgm-exporter, + # which gets the same visibility via privileged:true instead; this + # is the minimal fix that doesn't need the broader grant. + - name: "NVIDIA_VISIBLE_DEVICES" + value: "all" - name: "LD_LIBRARY_PATH" value: "/usr/local/nvidia/lib64" - name: "DCGM_EXPORTER_KUBERNETES_GPU_ID_TYPE" @@ -1326,3 +1486,7 @@ spec: mountPath: /usr/local/nvidia - name: zxporter-nodemon-dcgm-metrics mountPath: "/etc/dcgm-exporter" + resources: + requests: + cpu: 50m + memory: 128Mi diff --git a/dist/install-gcp.yaml b/dist/install-gcp.yaml index 55c1de8a..370f8095 100644 --- a/dist/install-gcp.yaml +++ b/dist/install-gcp.yaml @@ -287,6 +287,8 @@ rules: - apiGroups: - argoproj.io resources: + - applications + - applicationsets - rollouts verbs: - get @@ -334,6 +336,14 @@ rules: - get - list - watch +- apiGroups: + - coordination.k8s.io + resourceNames: + - dz-scheduler + resources: + - leases + verbs: + - get - apiGroups: - dakr.devzero.io resources: @@ -385,6 +395,45 @@ rules: - get - list - watch +- apiGroups: + - flagger.app + resources: + - alertproviders + - canaries + - metrictemplates + verbs: + - get + - list + - watch +- apiGroups: + - fluxcd.controlplane.io + resources: + - fluxinstances + - fluxreports + - resourcesetinputproviders + - resourcesets + verbs: + - get + - list + - watch +- apiGroups: + - helm.toolkit.fluxcd.io + resources: + - helmreleases + verbs: + - get + - list + - watch +- apiGroups: + - image.toolkit.fluxcd.io + resources: + - imagepolicies + - imagerepositories + - imageupdateautomations + verbs: + - get + - list + - watch - apiGroups: - karpenter.azure.com resources: @@ -449,6 +498,14 @@ rules: - get - list - watch +- apiGroups: + - kustomize.toolkit.fluxcd.io + resources: + - kustomizations + verbs: + - get + - list + - watch - apiGroups: - kyverno.io resources: @@ -477,6 +534,16 @@ rules: - get - list - watch +- apiGroups: + - notification.toolkit.fluxcd.io + resources: + - alerts + - providers + - receivers + verbs: + - get + - list + - watch - apiGroups: - policy resources: @@ -514,6 +581,19 @@ rules: - get - list - watch +- apiGroups: + - source.toolkit.fluxcd.io + resources: + - buckets + - externalartifacts + - gitrepositories + - helmcharts + - helmrepositories + - ocirepositories + verbs: + - get + - list + - watch - apiGroups: - sparkoperator.k8s.io resources: @@ -654,6 +734,19 @@ subjects: name: devzero-zxporter-controller-manager namespace: devzero-system --- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: devzero-zxporter-metrics-reader-rolebinding +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: devzero-zxporter-metrics-reader +subjects: +- kind: ServiceAccount + name: devzero-zxporter-controller-manager + namespace: devzero-system +--- apiVersion: v1 data: BUFFER_SIZE: "" @@ -693,6 +786,7 @@ data: EXCLUDED_STORAGECLASSES: "" EXCLUDED_VPAS: "" K8S_PROVIDER: '{{ .k8s_provider }}' + KEDA_METRICS_ENDPOINT: "" KUBE_CONTEXT_NAME: '{{ .kube_context_name }}' KUBELET_FALLBACK_TIMEOUT: 15s MASK_SECRET_DATA: "" @@ -701,6 +795,7 @@ data: NODE_METRICS_CONCURRENCY: "20" NODE_METRICS_INTERVAL: "" NODEMON_REQUEST_TIMEOUT: 15s + SCHEDULER_METRICS_SCRAPE_INTERVAL: "" TARGET_NAMESPACES: "" TOKEN_CONFIGMAP_NAME: devzero-zxporter-env-config TOKEN_CREDENTIALS_SECRET_NAME: devzero-zxporter-credentials @@ -811,6 +906,8 @@ spec: key: CLUSTER_TOKEN name: devzero-zxporter-token optional: true + - name: CLUSTER_TOKEN_SOURCE + value: mirror image: ttl.sh/zxporter:latest livenessProbe: httpGet: @@ -870,16 +967,21 @@ metadata: name: zxporter-nodemon namespace: devzero-system labels: - helm.sh/chart: zxporter-nodemon-0.1.6 + helm.sh/chart: zxporter-nodemon-0.1.25 app.kubernetes.io/name: zxporter-nodemon app.kubernetes.io/instance: zxporter-nodemon - app.kubernetes.io/version: "0.1.6" + app.kubernetes.io/version: "0.1.25" app.kubernetes.io/managed-by: Helm + app.kubernetes.io/part-of: dz-platform + devzero.io/component: zxporter-nodemon --- # Source: zxporter-nodemon/templates/dcgm-exporter-configmap.yaml apiVersion: v1 kind: ConfigMap metadata: + labels: + app.kubernetes.io/part-of: dz-platform + devzero.io/component: zxporter-nodemon name: zxporter-nodemon-dcgm-metrics namespace: devzero-system data: @@ -895,8 +997,12 @@ data: DCGM_FI_DEV_GPU_UTIL, gauge, GPU utilization (in %). # DCGM_FI_PROF_SM_ACTIVE, gauge, The ratio of cycles an SM has at least 1 warp assigned # DCGM_FI_PROF_SM_OCCUPANCY, gauge, The fraction of resident warps on a multiprocessor - # DCGM_FI_PROF_GR_ENGINE_ACTIVE, gauge, Ratio of time the graphics engine is active (in %). - # DCGM_FI_PROF_DRAM_ACTIVE, gauge, The ratio of cycles the device memory interface is active sending or receiving data. + # Enabled: required for per-MIG-instance profiling data (GPU_I_PROFILE/GPU_I_ID + # labels only attach to DCGM_FI_PROF_* fields, never to plain DCGM_FI_DEV_* + # ones) and directly consumed by nodemon's GPUMigInstance parsing + # (GraphicsEngineActive/DRAMActive/TensorActive below). + DCGM_FI_PROF_GR_ENGINE_ACTIVE, gauge, Ratio of time the graphics engine is active (in %). + DCGM_FI_PROF_DRAM_ACTIVE, gauge, The ratio of cycles the device memory interface is active sending or receiving data. # Memory usage,, DCGM_FI_DEV_FB_FREE, gauge, Framebuffer memory free (in MiB). @@ -915,7 +1021,9 @@ data: # DCGM_FI_PROF_PIPE_FP16_ACTIVE, gauge, Ratio of cycles the fp16 pipe is active. # DCGM_FI_PROF_PIPE_FP32_ACTIVE, gauge, Ratio of cycles the fp32 pipe is active. # DCGM_FI_PROF_PIPE_FP64_ACTIVE, gauge, Ratio of cycles the fp64 pipe is active. - # DCGM_FI_PROF_PIPE_TENSOR_ACTIVE, gauge, The ratio of cycles the tensor (HMMA) pipe is active (off the peak sustained elapsed cycles) + # Enabled: see the GR_ENGINE_ACTIVE/DRAM_ACTIVE note above -- this is + # GPUMigInstance.TensorActive. + DCGM_FI_PROF_PIPE_TENSOR_ACTIVE, gauge, The ratio of cycles the tensor (HMMA) pipe is active (off the peak sustained elapsed cycles) # Health,, # DCGM_FI_DEV_CLOCKS_EVENT_REASONS is not supported by DCGM 3.3.7 @@ -936,6 +1044,9 @@ data: apiVersion: v1 kind: ConfigMap metadata: + labels: + app.kubernetes.io/part-of: dz-platform + devzero.io/component: zxporter-nodemon name: zxporter-nodemon-zxporter-nodemon namespace: devzero-system data: @@ -946,11 +1057,13 @@ kind: ClusterRole metadata: name: zxporter-nodemon labels: - helm.sh/chart: zxporter-nodemon-0.1.6 + helm.sh/chart: zxporter-nodemon-0.1.25 app.kubernetes.io/name: zxporter-nodemon app.kubernetes.io/instance: zxporter-nodemon - app.kubernetes.io/version: "0.1.6" + app.kubernetes.io/version: "0.1.25" app.kubernetes.io/managed-by: Helm + app.kubernetes.io/part-of: dz-platform + devzero.io/component: zxporter-nodemon rules: - apiGroups: - "" @@ -997,11 +1110,13 @@ kind: ClusterRoleBinding metadata: name: zxporter-nodemon labels: - helm.sh/chart: zxporter-nodemon-0.1.6 + helm.sh/chart: zxporter-nodemon-0.1.25 app.kubernetes.io/name: zxporter-nodemon app.kubernetes.io/instance: zxporter-nodemon - app.kubernetes.io/version: "0.1.6" + app.kubernetes.io/version: "0.1.25" app.kubernetes.io/managed-by: Helm + app.kubernetes.io/part-of: dz-platform + devzero.io/component: zxporter-nodemon roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole @@ -1018,10 +1133,12 @@ metadata: name: zxporter-nodemon namespace: devzero-system labels: - helm.sh/chart: zxporter-nodemon-0.1.6 + app.kubernetes.io/part-of: dz-platform + devzero.io/component: zxporter-nodemon + helm.sh/chart: zxporter-nodemon-0.1.25 app.kubernetes.io/name: zxporter-nodemon app.kubernetes.io/instance: zxporter-nodemon - app.kubernetes.io/version: "0.1.6" + app.kubernetes.io/version: "0.1.25" app.kubernetes.io/managed-by: Helm annotations: ignore-check.kube-linter.io/privileged-container: "This daemon set needs to run DCGM Exporter as privileged to access the GPU metrics." @@ -1080,6 +1197,13 @@ spec: capabilities: add: - SYS_PTRACE + # Bottlerocket (SELinux enforcing) gives each container a unique MCS + # category pair, which denies reading other containers' files via + # /proc//root (JVM hsperfdata, runtime binaries). Granting the + # full category range keeps the container_t type; it is a no-op where + # SELinux is not enforcing. + seLinuxOptions: + level: "s0:c0.c1023" image: "ttl.sh/zxporter-nodemon:latest" imagePullPolicy: IfNotPresent ports: @@ -1133,6 +1257,14 @@ spec: memory: 256Mi - name: dcgm-exporter securityContext: + # SYS_ADMIN alone is not enough: enumerating MIG Compute Instance + # profiles via NVML needs broader host access than that one + # capability grants (confirmed live -- SYS_ADMIN-only reproduces + # "CacheManager Init Failed. Error: -17" the instant any MIG + # instance exists on the node; switching to privileged fixes it + # with zero other changes). This is a generic NVML/driver + # requirement, not provider-specific, so unlike the hostPath/ + # LD_LIBRARY_PATH blocks below it must not be gated on gcp only. capabilities: add: - SYS_ADMIN @@ -1160,6 +1292,16 @@ spec: valueFrom: fieldRef: fieldPath: spec.nodeName + # Without this, nvidia-container-runtime's "auto" mode only exposes + # whole/non-MIG GPUs to a non-privileged container with no explicit + # device-plugin resource request (which this sidecar deliberately + # doesn't make, since it must see every GPU on the node, not just + # one). MIG-partitioned GPUs stay invisible without it -- confirmed + # by direct comparison against GPU Operator's own dcgm-exporter, + # which gets the same visibility via privileged:true instead; this + # is the minimal fix that doesn't need the broader grant. + - name: "NVIDIA_VISIBLE_DEVICES" + value: "all" - name: "LD_LIBRARY_PATH" value: "/usr/local/nvidia/lib64" - name: "DCGM_EXPORTER_KUBERNETES_GPU_ID_TYPE" @@ -1172,6 +1314,10 @@ spec: mountPath: /usr/local/nvidia - name: zxporter-nodemon-dcgm-metrics mountPath: "/etc/dcgm-exporter" + resources: + requests: + cpu: 50m + memory: 128Mi --- # Source: zxporter-nodemon/templates/daemonset.yaml apiVersion: apps/v1 @@ -1180,10 +1326,12 @@ metadata: name: zxporter-nodemon-gpu namespace: devzero-system labels: - helm.sh/chart: zxporter-nodemon-0.1.6 + app.kubernetes.io/part-of: dz-platform + devzero.io/component: zxporter-nodemon + helm.sh/chart: zxporter-nodemon-0.1.25 app.kubernetes.io/name: zxporter-nodemon app.kubernetes.io/instance: zxporter-nodemon-gpu - app.kubernetes.io/version: "0.1.6" + app.kubernetes.io/version: "0.1.25" app.kubernetes.io/managed-by: Helm annotations: ignore-check.kube-linter.io/privileged-container: "This daemon set needs to run DCGM Exporter as privileged to access the GPU metrics." @@ -1245,6 +1393,13 @@ spec: capabilities: add: - SYS_PTRACE + # Bottlerocket (SELinux enforcing) gives each container a unique MCS + # category pair, which denies reading other containers' files via + # /proc//root (JVM hsperfdata, runtime binaries). Granting the + # full category range keeps the container_t type; it is a no-op where + # SELinux is not enforcing. + seLinuxOptions: + level: "s0:c0.c1023" image: "ttl.sh/zxporter-nodemon:latest" imagePullPolicy: IfNotPresent ports: @@ -1298,6 +1453,14 @@ spec: memory: 256Mi - name: dcgm-exporter securityContext: + # SYS_ADMIN alone is not enough: enumerating MIG Compute Instance + # profiles via NVML needs broader host access than that one + # capability grants (confirmed live -- SYS_ADMIN-only reproduces + # "CacheManager Init Failed. Error: -17" the instant any MIG + # instance exists on the node; switching to privileged fixes it + # with zero other changes). This is a generic NVML/driver + # requirement, not provider-specific, so unlike the hostPath/ + # LD_LIBRARY_PATH blocks below it must not be gated on gcp only. capabilities: add: - SYS_ADMIN @@ -1325,6 +1488,16 @@ spec: valueFrom: fieldRef: fieldPath: spec.nodeName + # Without this, nvidia-container-runtime's "auto" mode only exposes + # whole/non-MIG GPUs to a non-privileged container with no explicit + # device-plugin resource request (which this sidecar deliberately + # doesn't make, since it must see every GPU on the node, not just + # one). MIG-partitioned GPUs stay invisible without it -- confirmed + # by direct comparison against GPU Operator's own dcgm-exporter, + # which gets the same visibility via privileged:true instead; this + # is the minimal fix that doesn't need the broader grant. + - name: "NVIDIA_VISIBLE_DEVICES" + value: "all" - name: "LD_LIBRARY_PATH" value: "/usr/local/nvidia/lib64" - name: "DCGM_EXPORTER_KUBERNETES_GPU_ID_TYPE" @@ -1337,3 +1510,7 @@ spec: mountPath: /usr/local/nvidia - name: zxporter-nodemon-dcgm-metrics mountPath: "/etc/dcgm-exporter" + resources: + requests: + cpu: 50m + memory: 128Mi diff --git a/dist/install-lowpriv.yaml b/dist/install-lowpriv.yaml index 9fdd1cee..9d738791 100644 --- a/dist/install-lowpriv.yaml +++ b/dist/install-lowpriv.yaml @@ -287,6 +287,8 @@ rules: - apiGroups: - argoproj.io resources: + - applications + - applicationsets - rollouts verbs: - get @@ -334,6 +336,14 @@ rules: - get - list - watch +- apiGroups: + - coordination.k8s.io + resourceNames: + - dz-scheduler + resources: + - leases + verbs: + - get - apiGroups: - dakr.devzero.io resources: @@ -385,6 +395,45 @@ rules: - get - list - watch +- apiGroups: + - flagger.app + resources: + - alertproviders + - canaries + - metrictemplates + verbs: + - get + - list + - watch +- apiGroups: + - fluxcd.controlplane.io + resources: + - fluxinstances + - fluxreports + - resourcesetinputproviders + - resourcesets + verbs: + - get + - list + - watch +- apiGroups: + - helm.toolkit.fluxcd.io + resources: + - helmreleases + verbs: + - get + - list + - watch +- apiGroups: + - image.toolkit.fluxcd.io + resources: + - imagepolicies + - imagerepositories + - imageupdateautomations + verbs: + - get + - list + - watch - apiGroups: - karpenter.azure.com resources: @@ -449,6 +498,14 @@ rules: - get - list - watch +- apiGroups: + - kustomize.toolkit.fluxcd.io + resources: + - kustomizations + verbs: + - get + - list + - watch - apiGroups: - kyverno.io resources: @@ -477,6 +534,16 @@ rules: - get - list - watch +- apiGroups: + - notification.toolkit.fluxcd.io + resources: + - alerts + - providers + - receivers + verbs: + - get + - list + - watch - apiGroups: - policy resources: @@ -514,6 +581,19 @@ rules: - get - list - watch +- apiGroups: + - source.toolkit.fluxcd.io + resources: + - buckets + - externalartifacts + - gitrepositories + - helmcharts + - helmrepositories + - ocirepositories + verbs: + - get + - list + - watch - apiGroups: - sparkoperator.k8s.io resources: @@ -654,6 +734,19 @@ subjects: name: devzero-zxporter-controller-manager namespace: devzero-system --- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: devzero-zxporter-metrics-reader-rolebinding +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: devzero-zxporter-metrics-reader +subjects: +- kind: ServiceAccount + name: devzero-zxporter-controller-manager + namespace: devzero-system +--- apiVersion: v1 data: BUFFER_SIZE: "" @@ -693,6 +786,7 @@ data: EXCLUDED_STORAGECLASSES: "" EXCLUDED_VPAS: "" K8S_PROVIDER: '{{ .k8s_provider }}' + KEDA_METRICS_ENDPOINT: "" KUBE_CONTEXT_NAME: '{{ .kube_context_name }}' KUBELET_FALLBACK_TIMEOUT: 15s MASK_SECRET_DATA: "" @@ -701,6 +795,7 @@ data: NODE_METRICS_CONCURRENCY: "20" NODE_METRICS_INTERVAL: "" NODEMON_REQUEST_TIMEOUT: 15s + SCHEDULER_METRICS_SCRAPE_INTERVAL: "" TARGET_NAMESPACES: "" TOKEN_CONFIGMAP_NAME: devzero-zxporter-env-config TOKEN_CREDENTIALS_SECRET_NAME: devzero-zxporter-credentials @@ -811,6 +906,8 @@ spec: key: CLUSTER_TOKEN name: devzero-zxporter-token optional: true + - name: CLUSTER_TOKEN_SOURCE + value: mirror image: ttl.sh/zxporter:latest livenessProbe: httpGet: @@ -870,16 +967,21 @@ metadata: name: zxporter-nodemon namespace: devzero-system labels: - helm.sh/chart: zxporter-nodemon-0.1.6 + helm.sh/chart: zxporter-nodemon-0.1.25 app.kubernetes.io/name: zxporter-nodemon app.kubernetes.io/instance: zxporter-nodemon - app.kubernetes.io/version: "0.1.6" + app.kubernetes.io/version: "0.1.25" app.kubernetes.io/managed-by: Helm + app.kubernetes.io/part-of: dz-platform + devzero.io/component: zxporter-nodemon --- # Source: zxporter-nodemon/templates/dcgm-exporter-configmap.yaml apiVersion: v1 kind: ConfigMap metadata: + labels: + app.kubernetes.io/part-of: dz-platform + devzero.io/component: zxporter-nodemon name: zxporter-nodemon-dcgm-metrics namespace: devzero-system data: @@ -895,8 +997,12 @@ data: DCGM_FI_DEV_GPU_UTIL, gauge, GPU utilization (in %). # DCGM_FI_PROF_SM_ACTIVE, gauge, The ratio of cycles an SM has at least 1 warp assigned # DCGM_FI_PROF_SM_OCCUPANCY, gauge, The fraction of resident warps on a multiprocessor - # DCGM_FI_PROF_GR_ENGINE_ACTIVE, gauge, Ratio of time the graphics engine is active (in %). - # DCGM_FI_PROF_DRAM_ACTIVE, gauge, The ratio of cycles the device memory interface is active sending or receiving data. + # Enabled: required for per-MIG-instance profiling data (GPU_I_PROFILE/GPU_I_ID + # labels only attach to DCGM_FI_PROF_* fields, never to plain DCGM_FI_DEV_* + # ones) and directly consumed by nodemon's GPUMigInstance parsing + # (GraphicsEngineActive/DRAMActive/TensorActive below). + DCGM_FI_PROF_GR_ENGINE_ACTIVE, gauge, Ratio of time the graphics engine is active (in %). + DCGM_FI_PROF_DRAM_ACTIVE, gauge, The ratio of cycles the device memory interface is active sending or receiving data. # Memory usage,, DCGM_FI_DEV_FB_FREE, gauge, Framebuffer memory free (in MiB). @@ -915,7 +1021,9 @@ data: # DCGM_FI_PROF_PIPE_FP16_ACTIVE, gauge, Ratio of cycles the fp16 pipe is active. # DCGM_FI_PROF_PIPE_FP32_ACTIVE, gauge, Ratio of cycles the fp32 pipe is active. # DCGM_FI_PROF_PIPE_FP64_ACTIVE, gauge, Ratio of cycles the fp64 pipe is active. - # DCGM_FI_PROF_PIPE_TENSOR_ACTIVE, gauge, The ratio of cycles the tensor (HMMA) pipe is active (off the peak sustained elapsed cycles) + # Enabled: see the GR_ENGINE_ACTIVE/DRAM_ACTIVE note above -- this is + # GPUMigInstance.TensorActive. + DCGM_FI_PROF_PIPE_TENSOR_ACTIVE, gauge, The ratio of cycles the tensor (HMMA) pipe is active (off the peak sustained elapsed cycles) # Health,, # DCGM_FI_DEV_CLOCKS_EVENT_REASONS is not supported by DCGM 3.3.7 @@ -936,6 +1044,9 @@ data: apiVersion: v1 kind: ConfigMap metadata: + labels: + app.kubernetes.io/part-of: dz-platform + devzero.io/component: zxporter-nodemon name: zxporter-nodemon-zxporter-nodemon namespace: devzero-system data: @@ -946,11 +1057,13 @@ kind: ClusterRole metadata: name: zxporter-nodemon labels: - helm.sh/chart: zxporter-nodemon-0.1.6 + helm.sh/chart: zxporter-nodemon-0.1.25 app.kubernetes.io/name: zxporter-nodemon app.kubernetes.io/instance: zxporter-nodemon - app.kubernetes.io/version: "0.1.6" + app.kubernetes.io/version: "0.1.25" app.kubernetes.io/managed-by: Helm + app.kubernetes.io/part-of: dz-platform + devzero.io/component: zxporter-nodemon rules: - apiGroups: - "" @@ -959,6 +1072,7 @@ rules: verbs: - get - list + - watch - apiGroups: - apps resources: @@ -996,11 +1110,13 @@ kind: ClusterRoleBinding metadata: name: zxporter-nodemon labels: - helm.sh/chart: zxporter-nodemon-0.1.6 + helm.sh/chart: zxporter-nodemon-0.1.25 app.kubernetes.io/name: zxporter-nodemon app.kubernetes.io/instance: zxporter-nodemon - app.kubernetes.io/version: "0.1.6" + app.kubernetes.io/version: "0.1.25" app.kubernetes.io/managed-by: Helm + app.kubernetes.io/part-of: dz-platform + devzero.io/component: zxporter-nodemon roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole @@ -1017,10 +1133,12 @@ metadata: name: zxporter-nodemon namespace: devzero-system labels: - helm.sh/chart: zxporter-nodemon-0.1.6 + app.kubernetes.io/part-of: dz-platform + devzero.io/component: zxporter-nodemon + helm.sh/chart: zxporter-nodemon-0.1.25 app.kubernetes.io/name: zxporter-nodemon app.kubernetes.io/instance: zxporter-nodemon - app.kubernetes.io/version: "0.1.6" + app.kubernetes.io/version: "0.1.25" app.kubernetes.io/managed-by: Helm annotations: ignore-check.kube-linter.io/privileged-container: "This daemon set needs to run DCGM Exporter as privileged to access the GPU metrics." @@ -1124,6 +1242,14 @@ spec: memory: 256Mi - name: dcgm-exporter securityContext: + # SYS_ADMIN alone is not enough: enumerating MIG Compute Instance + # profiles via NVML needs broader host access than that one + # capability grants (confirmed live -- SYS_ADMIN-only reproduces + # "CacheManager Init Failed. Error: -17" the instant any MIG + # instance exists on the node; switching to privileged fixes it + # with zero other changes). This is a generic NVML/driver + # requirement, not provider-specific, so unlike the hostPath/ + # LD_LIBRARY_PATH blocks below it must not be gated on gcp only. capabilities: add: - SYS_ADMIN @@ -1131,6 +1257,7 @@ spec: - NET_RAW runAsNonRoot: false runAsUser: 0 + privileged: true image: "public.ecr.aws/devzeroinc/dcgm-exporter:4.5.2-4.8.1-ubuntu22.04" imagePullPolicy: IfNotPresent command: [ "/bin/bash", "-c" ] @@ -1150,12 +1277,26 @@ spec: valueFrom: fieldRef: fieldPath: spec.nodeName + # Without this, nvidia-container-runtime's "auto" mode only exposes + # whole/non-MIG GPUs to a non-privileged container with no explicit + # device-plugin resource request (which this sidecar deliberately + # doesn't make, since it must see every GPU on the node, not just + # one). MIG-partitioned GPUs stay invisible without it -- confirmed + # by direct comparison against GPU Operator's own dcgm-exporter, + # which gets the same visibility via privileged:true instead; this + # is the minimal fix that doesn't need the broader grant. + - name: "NVIDIA_VISIBLE_DEVICES" + value: "all" volumeMounts: - name: "pod-gpu-resources" readOnly: true mountPath: "/var/lib/kubelet/pod-resources" - name: zxporter-nodemon-dcgm-metrics mountPath: "/etc/dcgm-exporter" + resources: + requests: + cpu: 50m + memory: 128Mi --- # Source: zxporter-nodemon/templates/daemonset.yaml apiVersion: apps/v1 @@ -1164,10 +1305,12 @@ metadata: name: zxporter-nodemon-gpu namespace: devzero-system labels: - helm.sh/chart: zxporter-nodemon-0.1.6 + app.kubernetes.io/part-of: dz-platform + devzero.io/component: zxporter-nodemon + helm.sh/chart: zxporter-nodemon-0.1.25 app.kubernetes.io/name: zxporter-nodemon app.kubernetes.io/instance: zxporter-nodemon-gpu - app.kubernetes.io/version: "0.1.6" + app.kubernetes.io/version: "0.1.25" app.kubernetes.io/managed-by: Helm annotations: ignore-check.kube-linter.io/privileged-container: "This daemon set needs to run DCGM Exporter as privileged to access the GPU metrics." @@ -1274,6 +1417,14 @@ spec: memory: 256Mi - name: dcgm-exporter securityContext: + # SYS_ADMIN alone is not enough: enumerating MIG Compute Instance + # profiles via NVML needs broader host access than that one + # capability grants (confirmed live -- SYS_ADMIN-only reproduces + # "CacheManager Init Failed. Error: -17" the instant any MIG + # instance exists on the node; switching to privileged fixes it + # with zero other changes). This is a generic NVML/driver + # requirement, not provider-specific, so unlike the hostPath/ + # LD_LIBRARY_PATH blocks below it must not be gated on gcp only. capabilities: add: - SYS_ADMIN @@ -1281,6 +1432,7 @@ spec: - NET_RAW runAsNonRoot: false runAsUser: 0 + privileged: true image: "public.ecr.aws/devzeroinc/dcgm-exporter:4.5.2-4.8.1-ubuntu22.04" imagePullPolicy: IfNotPresent command: [ "/bin/bash", "-c" ] @@ -1300,9 +1452,23 @@ spec: valueFrom: fieldRef: fieldPath: spec.nodeName + # Without this, nvidia-container-runtime's "auto" mode only exposes + # whole/non-MIG GPUs to a non-privileged container with no explicit + # device-plugin resource request (which this sidecar deliberately + # doesn't make, since it must see every GPU on the node, not just + # one). MIG-partitioned GPUs stay invisible without it -- confirmed + # by direct comparison against GPU Operator's own dcgm-exporter, + # which gets the same visibility via privileged:true instead; this + # is the minimal fix that doesn't need the broader grant. + - name: "NVIDIA_VISIBLE_DEVICES" + value: "all" volumeMounts: - name: "pod-gpu-resources" readOnly: true mountPath: "/var/lib/kubelet/pod-resources" - name: zxporter-nodemon-dcgm-metrics mountPath: "/etc/dcgm-exporter" + resources: + requests: + cpu: 50m + memory: 128Mi diff --git a/dist/install.yaml b/dist/install.yaml index eeb03113..a19a6a83 100644 --- a/dist/install.yaml +++ b/dist/install.yaml @@ -287,6 +287,8 @@ rules: - apiGroups: - argoproj.io resources: + - applications + - applicationsets - rollouts verbs: - get @@ -334,6 +336,14 @@ rules: - get - list - watch +- apiGroups: + - coordination.k8s.io + resourceNames: + - dz-scheduler + resources: + - leases + verbs: + - get - apiGroups: - dakr.devzero.io resources: @@ -385,6 +395,45 @@ rules: - get - list - watch +- apiGroups: + - flagger.app + resources: + - alertproviders + - canaries + - metrictemplates + verbs: + - get + - list + - watch +- apiGroups: + - fluxcd.controlplane.io + resources: + - fluxinstances + - fluxreports + - resourcesetinputproviders + - resourcesets + verbs: + - get + - list + - watch +- apiGroups: + - helm.toolkit.fluxcd.io + resources: + - helmreleases + verbs: + - get + - list + - watch +- apiGroups: + - image.toolkit.fluxcd.io + resources: + - imagepolicies + - imagerepositories + - imageupdateautomations + verbs: + - get + - list + - watch - apiGroups: - karpenter.azure.com resources: @@ -449,6 +498,14 @@ rules: - get - list - watch +- apiGroups: + - kustomize.toolkit.fluxcd.io + resources: + - kustomizations + verbs: + - get + - list + - watch - apiGroups: - kyverno.io resources: @@ -477,6 +534,16 @@ rules: - get - list - watch +- apiGroups: + - notification.toolkit.fluxcd.io + resources: + - alerts + - providers + - receivers + verbs: + - get + - list + - watch - apiGroups: - policy resources: @@ -514,6 +581,19 @@ rules: - get - list - watch +- apiGroups: + - source.toolkit.fluxcd.io + resources: + - buckets + - externalartifacts + - gitrepositories + - helmcharts + - helmrepositories + - ocirepositories + verbs: + - get + - list + - watch - apiGroups: - sparkoperator.k8s.io resources: @@ -654,6 +734,19 @@ subjects: name: devzero-zxporter-controller-manager namespace: devzero-system --- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: devzero-zxporter-metrics-reader-rolebinding +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: devzero-zxporter-metrics-reader +subjects: +- kind: ServiceAccount + name: devzero-zxporter-controller-manager + namespace: devzero-system +--- apiVersion: v1 data: BUFFER_SIZE: "" @@ -693,6 +786,7 @@ data: EXCLUDED_STORAGECLASSES: "" EXCLUDED_VPAS: "" K8S_PROVIDER: '{{ .k8s_provider }}' + KEDA_METRICS_ENDPOINT: "" KUBE_CONTEXT_NAME: '{{ .kube_context_name }}' KUBELET_FALLBACK_TIMEOUT: 15s MASK_SECRET_DATA: "" @@ -701,6 +795,7 @@ data: NODE_METRICS_CONCURRENCY: "20" NODE_METRICS_INTERVAL: "" NODEMON_REQUEST_TIMEOUT: 15s + SCHEDULER_METRICS_SCRAPE_INTERVAL: "" TARGET_NAMESPACES: "" TOKEN_CONFIGMAP_NAME: devzero-zxporter-env-config TOKEN_CREDENTIALS_SECRET_NAME: devzero-zxporter-credentials @@ -811,6 +906,8 @@ spec: key: CLUSTER_TOKEN name: devzero-zxporter-token optional: true + - name: CLUSTER_TOKEN_SOURCE + value: mirror image: ttl.sh/zxporter:latest livenessProbe: httpGet: @@ -870,16 +967,21 @@ metadata: name: zxporter-nodemon namespace: devzero-system labels: - helm.sh/chart: zxporter-nodemon-0.1.6 + helm.sh/chart: zxporter-nodemon-0.1.25 app.kubernetes.io/name: zxporter-nodemon app.kubernetes.io/instance: zxporter-nodemon - app.kubernetes.io/version: "0.1.6" + app.kubernetes.io/version: "0.1.25" app.kubernetes.io/managed-by: Helm + app.kubernetes.io/part-of: dz-platform + devzero.io/component: zxporter-nodemon --- # Source: zxporter-nodemon/templates/dcgm-exporter-configmap.yaml apiVersion: v1 kind: ConfigMap metadata: + labels: + app.kubernetes.io/part-of: dz-platform + devzero.io/component: zxporter-nodemon name: zxporter-nodemon-dcgm-metrics namespace: devzero-system data: @@ -895,8 +997,12 @@ data: DCGM_FI_DEV_GPU_UTIL, gauge, GPU utilization (in %). # DCGM_FI_PROF_SM_ACTIVE, gauge, The ratio of cycles an SM has at least 1 warp assigned # DCGM_FI_PROF_SM_OCCUPANCY, gauge, The fraction of resident warps on a multiprocessor - # DCGM_FI_PROF_GR_ENGINE_ACTIVE, gauge, Ratio of time the graphics engine is active (in %). - # DCGM_FI_PROF_DRAM_ACTIVE, gauge, The ratio of cycles the device memory interface is active sending or receiving data. + # Enabled: required for per-MIG-instance profiling data (GPU_I_PROFILE/GPU_I_ID + # labels only attach to DCGM_FI_PROF_* fields, never to plain DCGM_FI_DEV_* + # ones) and directly consumed by nodemon's GPUMigInstance parsing + # (GraphicsEngineActive/DRAMActive/TensorActive below). + DCGM_FI_PROF_GR_ENGINE_ACTIVE, gauge, Ratio of time the graphics engine is active (in %). + DCGM_FI_PROF_DRAM_ACTIVE, gauge, The ratio of cycles the device memory interface is active sending or receiving data. # Memory usage,, DCGM_FI_DEV_FB_FREE, gauge, Framebuffer memory free (in MiB). @@ -915,7 +1021,9 @@ data: # DCGM_FI_PROF_PIPE_FP16_ACTIVE, gauge, Ratio of cycles the fp16 pipe is active. # DCGM_FI_PROF_PIPE_FP32_ACTIVE, gauge, Ratio of cycles the fp32 pipe is active. # DCGM_FI_PROF_PIPE_FP64_ACTIVE, gauge, Ratio of cycles the fp64 pipe is active. - # DCGM_FI_PROF_PIPE_TENSOR_ACTIVE, gauge, The ratio of cycles the tensor (HMMA) pipe is active (off the peak sustained elapsed cycles) + # Enabled: see the GR_ENGINE_ACTIVE/DRAM_ACTIVE note above -- this is + # GPUMigInstance.TensorActive. + DCGM_FI_PROF_PIPE_TENSOR_ACTIVE, gauge, The ratio of cycles the tensor (HMMA) pipe is active (off the peak sustained elapsed cycles) # Health,, # DCGM_FI_DEV_CLOCKS_EVENT_REASONS is not supported by DCGM 3.3.7 @@ -936,6 +1044,9 @@ data: apiVersion: v1 kind: ConfigMap metadata: + labels: + app.kubernetes.io/part-of: dz-platform + devzero.io/component: zxporter-nodemon name: zxporter-nodemon-zxporter-nodemon namespace: devzero-system data: @@ -946,11 +1057,13 @@ kind: ClusterRole metadata: name: zxporter-nodemon labels: - helm.sh/chart: zxporter-nodemon-0.1.6 + helm.sh/chart: zxporter-nodemon-0.1.25 app.kubernetes.io/name: zxporter-nodemon app.kubernetes.io/instance: zxporter-nodemon - app.kubernetes.io/version: "0.1.6" + app.kubernetes.io/version: "0.1.25" app.kubernetes.io/managed-by: Helm + app.kubernetes.io/part-of: dz-platform + devzero.io/component: zxporter-nodemon rules: - apiGroups: - "" @@ -997,11 +1110,13 @@ kind: ClusterRoleBinding metadata: name: zxporter-nodemon labels: - helm.sh/chart: zxporter-nodemon-0.1.6 + helm.sh/chart: zxporter-nodemon-0.1.25 app.kubernetes.io/name: zxporter-nodemon app.kubernetes.io/instance: zxporter-nodemon - app.kubernetes.io/version: "0.1.6" + app.kubernetes.io/version: "0.1.25" app.kubernetes.io/managed-by: Helm + app.kubernetes.io/part-of: dz-platform + devzero.io/component: zxporter-nodemon roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole @@ -1018,10 +1133,12 @@ metadata: name: zxporter-nodemon namespace: devzero-system labels: - helm.sh/chart: zxporter-nodemon-0.1.6 + app.kubernetes.io/part-of: dz-platform + devzero.io/component: zxporter-nodemon + helm.sh/chart: zxporter-nodemon-0.1.25 app.kubernetes.io/name: zxporter-nodemon app.kubernetes.io/instance: zxporter-nodemon - app.kubernetes.io/version: "0.1.6" + app.kubernetes.io/version: "0.1.25" app.kubernetes.io/managed-by: Helm annotations: ignore-check.kube-linter.io/privileged-container: "This daemon set needs to run DCGM Exporter as privileged to access the GPU metrics." @@ -1077,6 +1194,13 @@ spec: capabilities: add: - SYS_PTRACE + # Bottlerocket (SELinux enforcing) gives each container a unique MCS + # category pair, which denies reading other containers' files via + # /proc//root (JVM hsperfdata, runtime binaries). Granting the + # full category range keeps the container_t type; it is a no-op where + # SELinux is not enforcing. + seLinuxOptions: + level: "s0:c0.c1023" image: "ttl.sh/zxporter-nodemon:latest" imagePullPolicy: IfNotPresent ports: @@ -1130,6 +1254,14 @@ spec: memory: 256Mi - name: dcgm-exporter securityContext: + # SYS_ADMIN alone is not enough: enumerating MIG Compute Instance + # profiles via NVML needs broader host access than that one + # capability grants (confirmed live -- SYS_ADMIN-only reproduces + # "CacheManager Init Failed. Error: -17" the instant any MIG + # instance exists on the node; switching to privileged fixes it + # with zero other changes). This is a generic NVML/driver + # requirement, not provider-specific, so unlike the hostPath/ + # LD_LIBRARY_PATH blocks below it must not be gated on gcp only. capabilities: add: - SYS_ADMIN @@ -1137,6 +1269,7 @@ spec: - NET_RAW runAsNonRoot: false runAsUser: 0 + privileged: true image: "public.ecr.aws/devzeroinc/dcgm-exporter:4.5.2-4.8.1-ubuntu22.04" imagePullPolicy: IfNotPresent command: [ "/bin/bash", "-c" ] @@ -1156,12 +1289,26 @@ spec: valueFrom: fieldRef: fieldPath: spec.nodeName + # Without this, nvidia-container-runtime's "auto" mode only exposes + # whole/non-MIG GPUs to a non-privileged container with no explicit + # device-plugin resource request (which this sidecar deliberately + # doesn't make, since it must see every GPU on the node, not just + # one). MIG-partitioned GPUs stay invisible without it -- confirmed + # by direct comparison against GPU Operator's own dcgm-exporter, + # which gets the same visibility via privileged:true instead; this + # is the minimal fix that doesn't need the broader grant. + - name: "NVIDIA_VISIBLE_DEVICES" + value: "all" volumeMounts: - name: "pod-gpu-resources" readOnly: true mountPath: "/var/lib/kubelet/pod-resources" - name: zxporter-nodemon-dcgm-metrics mountPath: "/etc/dcgm-exporter" + resources: + requests: + cpu: 50m + memory: 128Mi --- # Source: zxporter-nodemon/templates/daemonset.yaml apiVersion: apps/v1 @@ -1170,10 +1317,12 @@ metadata: name: zxporter-nodemon-gpu namespace: devzero-system labels: - helm.sh/chart: zxporter-nodemon-0.1.6 + app.kubernetes.io/part-of: dz-platform + devzero.io/component: zxporter-nodemon + helm.sh/chart: zxporter-nodemon-0.1.25 app.kubernetes.io/name: zxporter-nodemon app.kubernetes.io/instance: zxporter-nodemon-gpu - app.kubernetes.io/version: "0.1.6" + app.kubernetes.io/version: "0.1.25" app.kubernetes.io/managed-by: Helm annotations: ignore-check.kube-linter.io/privileged-container: "This daemon set needs to run DCGM Exporter as privileged to access the GPU metrics." @@ -1232,6 +1381,13 @@ spec: capabilities: add: - SYS_PTRACE + # Bottlerocket (SELinux enforcing) gives each container a unique MCS + # category pair, which denies reading other containers' files via + # /proc//root (JVM hsperfdata, runtime binaries). Granting the + # full category range keeps the container_t type; it is a no-op where + # SELinux is not enforcing. + seLinuxOptions: + level: "s0:c0.c1023" image: "ttl.sh/zxporter-nodemon:latest" imagePullPolicy: IfNotPresent ports: @@ -1285,6 +1441,14 @@ spec: memory: 256Mi - name: dcgm-exporter securityContext: + # SYS_ADMIN alone is not enough: enumerating MIG Compute Instance + # profiles via NVML needs broader host access than that one + # capability grants (confirmed live -- SYS_ADMIN-only reproduces + # "CacheManager Init Failed. Error: -17" the instant any MIG + # instance exists on the node; switching to privileged fixes it + # with zero other changes). This is a generic NVML/driver + # requirement, not provider-specific, so unlike the hostPath/ + # LD_LIBRARY_PATH blocks below it must not be gated on gcp only. capabilities: add: - SYS_ADMIN @@ -1292,6 +1456,7 @@ spec: - NET_RAW runAsNonRoot: false runAsUser: 0 + privileged: true image: "public.ecr.aws/devzeroinc/dcgm-exporter:4.5.2-4.8.1-ubuntu22.04" imagePullPolicy: IfNotPresent command: [ "/bin/bash", "-c" ] @@ -1311,9 +1476,23 @@ spec: valueFrom: fieldRef: fieldPath: spec.nodeName + # Without this, nvidia-container-runtime's "auto" mode only exposes + # whole/non-MIG GPUs to a non-privileged container with no explicit + # device-plugin resource request (which this sidecar deliberately + # doesn't make, since it must see every GPU on the node, not just + # one). MIG-partitioned GPUs stay invisible without it -- confirmed + # by direct comparison against GPU Operator's own dcgm-exporter, + # which gets the same visibility via privileged:true instead; this + # is the minimal fix that doesn't need the broader grant. + - name: "NVIDIA_VISIBLE_DEVICES" + value: "all" volumeMounts: - name: "pod-gpu-resources" readOnly: true mountPath: "/var/lib/kubelet/pod-resources" - name: zxporter-nodemon-dcgm-metrics mountPath: "/etc/dcgm-exporter" + resources: + requests: + cpu: 50m + memory: 128Mi diff --git a/dist/installer_updater-gcp-lowpriv.yaml b/dist/installer_updater-gcp-lowpriv.yaml index f1d096f8..8b180bf2 100644 --- a/dist/installer_updater-gcp-lowpriv.yaml +++ b/dist/installer_updater-gcp-lowpriv.yaml @@ -287,6 +287,8 @@ rules: - apiGroups: - argoproj.io resources: + - applications + - applicationsets - rollouts verbs: - get @@ -334,6 +336,14 @@ rules: - get - list - watch + - apiGroups: + - coordination.k8s.io + resourceNames: + - dz-scheduler + resources: + - leases + verbs: + - get - apiGroups: - dakr.devzero.io resources: @@ -385,6 +395,45 @@ rules: - get - list - watch + - apiGroups: + - flagger.app + resources: + - alertproviders + - canaries + - metrictemplates + verbs: + - get + - list + - watch + - apiGroups: + - fluxcd.controlplane.io + resources: + - fluxinstances + - fluxreports + - resourcesetinputproviders + - resourcesets + verbs: + - get + - list + - watch + - apiGroups: + - helm.toolkit.fluxcd.io + resources: + - helmreleases + verbs: + - get + - list + - watch + - apiGroups: + - image.toolkit.fluxcd.io + resources: + - imagepolicies + - imagerepositories + - imageupdateautomations + verbs: + - get + - list + - watch - apiGroups: - karpenter.azure.com resources: @@ -449,6 +498,14 @@ rules: - get - list - watch + - apiGroups: + - kustomize.toolkit.fluxcd.io + resources: + - kustomizations + verbs: + - get + - list + - watch - apiGroups: - kyverno.io resources: @@ -477,6 +534,16 @@ rules: - get - list - watch + - apiGroups: + - notification.toolkit.fluxcd.io + resources: + - alerts + - providers + - receivers + verbs: + - get + - list + - watch - apiGroups: - policy resources: @@ -514,6 +581,19 @@ rules: - get - list - watch + - apiGroups: + - source.toolkit.fluxcd.io + resources: + - buckets + - externalartifacts + - gitrepositories + - helmcharts + - helmrepositories + - ocirepositories + verbs: + - get + - list + - watch - apiGroups: - sparkoperator.k8s.io resources: @@ -654,6 +734,19 @@ subjects: name: devzero-zxporter-controller-manager namespace: '{{.zxporter_namespace}}' --- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: devzero-zxporter-metrics-reader-rolebinding +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: devzero-zxporter-metrics-reader +subjects: + - kind: ServiceAccount + name: devzero-zxporter-controller-manager + namespace: '{{.zxporter_namespace}}' +--- apiVersion: v1 kind: Service metadata: @@ -743,6 +836,8 @@ spec: key: CLUSTER_TOKEN name: devzero-zxporter-token optional: true + - name: CLUSTER_TOKEN_SOURCE + value: mirror image: docker.io/devzeroinc/zxporter:latest livenessProbe: httpGet: @@ -802,16 +897,21 @@ metadata: name: zxporter-nodemon namespace: '{{.zxporter_namespace}}' labels: - helm.sh/chart: zxporter-nodemon-0.1.6 + helm.sh/chart: zxporter-nodemon-0.1.25 app.kubernetes.io/name: zxporter-nodemon app.kubernetes.io/instance: zxporter-nodemon - app.kubernetes.io/version: "0.1.6" + app.kubernetes.io/version: "0.1.25" app.kubernetes.io/managed-by: Helm + app.kubernetes.io/part-of: dz-platform + devzero.io/component: zxporter-nodemon --- # Source: zxporter-nodemon/templates/dcgm-exporter-configmap.yaml apiVersion: v1 kind: ConfigMap metadata: + labels: + app.kubernetes.io/part-of: dz-platform + devzero.io/component: zxporter-nodemon name: zxporter-nodemon-dcgm-metrics namespace: '{{.zxporter_namespace}}' data: @@ -827,8 +927,12 @@ data: DCGM_FI_DEV_GPU_UTIL, gauge, GPU utilization (in %). # DCGM_FI_PROF_SM_ACTIVE, gauge, The ratio of cycles an SM has at least 1 warp assigned # DCGM_FI_PROF_SM_OCCUPANCY, gauge, The fraction of resident warps on a multiprocessor - # DCGM_FI_PROF_GR_ENGINE_ACTIVE, gauge, Ratio of time the graphics engine is active (in %). - # DCGM_FI_PROF_DRAM_ACTIVE, gauge, The ratio of cycles the device memory interface is active sending or receiving data. + # Enabled: required for per-MIG-instance profiling data (GPU_I_PROFILE/GPU_I_ID + # labels only attach to DCGM_FI_PROF_* fields, never to plain DCGM_FI_DEV_* + # ones) and directly consumed by nodemon's GPUMigInstance parsing + # (GraphicsEngineActive/DRAMActive/TensorActive below). + DCGM_FI_PROF_GR_ENGINE_ACTIVE, gauge, Ratio of time the graphics engine is active (in %). + DCGM_FI_PROF_DRAM_ACTIVE, gauge, The ratio of cycles the device memory interface is active sending or receiving data. # Memory usage,, DCGM_FI_DEV_FB_FREE, gauge, Framebuffer memory free (in MiB). @@ -847,7 +951,9 @@ data: # DCGM_FI_PROF_PIPE_FP16_ACTIVE, gauge, Ratio of cycles the fp16 pipe is active. # DCGM_FI_PROF_PIPE_FP32_ACTIVE, gauge, Ratio of cycles the fp32 pipe is active. # DCGM_FI_PROF_PIPE_FP64_ACTIVE, gauge, Ratio of cycles the fp64 pipe is active. - # DCGM_FI_PROF_PIPE_TENSOR_ACTIVE, gauge, The ratio of cycles the tensor (HMMA) pipe is active (off the peak sustained elapsed cycles) + # Enabled: see the GR_ENGINE_ACTIVE/DRAM_ACTIVE note above -- this is + # GPUMigInstance.TensorActive. + DCGM_FI_PROF_PIPE_TENSOR_ACTIVE, gauge, The ratio of cycles the tensor (HMMA) pipe is active (off the peak sustained elapsed cycles) # Health,, # DCGM_FI_DEV_CLOCKS_EVENT_REASONS is not supported by DCGM 3.3.7 @@ -868,6 +974,9 @@ data: apiVersion: v1 kind: ConfigMap metadata: + labels: + app.kubernetes.io/part-of: dz-platform + devzero.io/component: zxporter-nodemon name: zxporter-nodemon-zxporter-nodemon namespace: '{{.zxporter_namespace}}' data: @@ -878,11 +987,13 @@ kind: ClusterRole metadata: name: zxporter-nodemon labels: - helm.sh/chart: zxporter-nodemon-0.1.6 + helm.sh/chart: zxporter-nodemon-0.1.25 app.kubernetes.io/name: zxporter-nodemon app.kubernetes.io/instance: zxporter-nodemon - app.kubernetes.io/version: "0.1.6" + app.kubernetes.io/version: "0.1.25" app.kubernetes.io/managed-by: Helm + app.kubernetes.io/part-of: dz-platform + devzero.io/component: zxporter-nodemon rules: - apiGroups: - "" @@ -891,6 +1002,7 @@ rules: verbs: - get - list + - watch - apiGroups: - apps resources: @@ -928,11 +1040,13 @@ kind: ClusterRoleBinding metadata: name: zxporter-nodemon labels: - helm.sh/chart: zxporter-nodemon-0.1.6 + helm.sh/chart: zxporter-nodemon-0.1.25 app.kubernetes.io/name: zxporter-nodemon app.kubernetes.io/instance: zxporter-nodemon - app.kubernetes.io/version: "0.1.6" + app.kubernetes.io/version: "0.1.25" app.kubernetes.io/managed-by: Helm + app.kubernetes.io/part-of: dz-platform + devzero.io/component: zxporter-nodemon roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole @@ -949,10 +1063,12 @@ metadata: name: zxporter-nodemon namespace: '{{.zxporter_namespace}}' labels: - helm.sh/chart: zxporter-nodemon-0.1.6 + app.kubernetes.io/part-of: dz-platform + devzero.io/component: zxporter-nodemon + helm.sh/chart: zxporter-nodemon-0.1.25 app.kubernetes.io/name: zxporter-nodemon app.kubernetes.io/instance: zxporter-nodemon - app.kubernetes.io/version: "0.1.6" + app.kubernetes.io/version: "0.1.25" app.kubernetes.io/managed-by: Helm annotations: ignore-check.kube-linter.io/privileged-container: "This daemon set needs to run DCGM Exporter as privileged to access the GPU metrics." @@ -1059,6 +1175,14 @@ spec: memory: 256Mi - name: dcgm-exporter securityContext: + # SYS_ADMIN alone is not enough: enumerating MIG Compute Instance + # profiles via NVML needs broader host access than that one + # capability grants (confirmed live -- SYS_ADMIN-only reproduces + # "CacheManager Init Failed. Error: -17" the instant any MIG + # instance exists on the node; switching to privileged fixes it + # with zero other changes). This is a generic NVML/driver + # requirement, not provider-specific, so unlike the hostPath/ + # LD_LIBRARY_PATH blocks below it must not be gated on gcp only. capabilities: add: - SYS_ADMIN @@ -1086,6 +1210,16 @@ spec: valueFrom: fieldRef: fieldPath: spec.nodeName + # Without this, nvidia-container-runtime's "auto" mode only exposes + # whole/non-MIG GPUs to a non-privileged container with no explicit + # device-plugin resource request (which this sidecar deliberately + # doesn't make, since it must see every GPU on the node, not just + # one). MIG-partitioned GPUs stay invisible without it -- confirmed + # by direct comparison against GPU Operator's own dcgm-exporter, + # which gets the same visibility via privileged:true instead; this + # is the minimal fix that doesn't need the broader grant. + - name: "NVIDIA_VISIBLE_DEVICES" + value: "all" - name: "LD_LIBRARY_PATH" value: "/usr/local/nvidia/lib64" - name: "DCGM_EXPORTER_KUBERNETES_GPU_ID_TYPE" @@ -1098,6 +1232,10 @@ spec: mountPath: /usr/local/nvidia - name: zxporter-nodemon-dcgm-metrics mountPath: "/etc/dcgm-exporter" + resources: + requests: + cpu: 50m + memory: 128Mi --- # Source: zxporter-nodemon/templates/daemonset.yaml apiVersion: apps/v1 @@ -1106,10 +1244,12 @@ metadata: name: zxporter-nodemon-gpu namespace: '{{.zxporter_namespace}}' labels: - helm.sh/chart: zxporter-nodemon-0.1.6 + app.kubernetes.io/part-of: dz-platform + devzero.io/component: zxporter-nodemon + helm.sh/chart: zxporter-nodemon-0.1.25 app.kubernetes.io/name: zxporter-nodemon app.kubernetes.io/instance: zxporter-nodemon-gpu - app.kubernetes.io/version: "0.1.6" + app.kubernetes.io/version: "0.1.25" app.kubernetes.io/managed-by: Helm annotations: ignore-check.kube-linter.io/privileged-container: "This daemon set needs to run DCGM Exporter as privileged to access the GPU metrics." @@ -1219,6 +1359,14 @@ spec: memory: 256Mi - name: dcgm-exporter securityContext: + # SYS_ADMIN alone is not enough: enumerating MIG Compute Instance + # profiles via NVML needs broader host access than that one + # capability grants (confirmed live -- SYS_ADMIN-only reproduces + # "CacheManager Init Failed. Error: -17" the instant any MIG + # instance exists on the node; switching to privileged fixes it + # with zero other changes). This is a generic NVML/driver + # requirement, not provider-specific, so unlike the hostPath/ + # LD_LIBRARY_PATH blocks below it must not be gated on gcp only. capabilities: add: - SYS_ADMIN @@ -1246,6 +1394,16 @@ spec: valueFrom: fieldRef: fieldPath: spec.nodeName + # Without this, nvidia-container-runtime's "auto" mode only exposes + # whole/non-MIG GPUs to a non-privileged container with no explicit + # device-plugin resource request (which this sidecar deliberately + # doesn't make, since it must see every GPU on the node, not just + # one). MIG-partitioned GPUs stay invisible without it -- confirmed + # by direct comparison against GPU Operator's own dcgm-exporter, + # which gets the same visibility via privileged:true instead; this + # is the minimal fix that doesn't need the broader grant. + - name: "NVIDIA_VISIBLE_DEVICES" + value: "all" - name: "LD_LIBRARY_PATH" value: "/usr/local/nvidia/lib64" - name: "DCGM_EXPORTER_KUBERNETES_GPU_ID_TYPE" @@ -1258,3 +1416,7 @@ spec: mountPath: /usr/local/nvidia - name: zxporter-nodemon-dcgm-metrics mountPath: "/etc/dcgm-exporter" + resources: + requests: + cpu: 50m + memory: 128Mi diff --git a/dist/installer_updater-gcp.yaml b/dist/installer_updater-gcp.yaml index d1bbd8da..9b1637a6 100644 --- a/dist/installer_updater-gcp.yaml +++ b/dist/installer_updater-gcp.yaml @@ -287,6 +287,8 @@ rules: - apiGroups: - argoproj.io resources: + - applications + - applicationsets - rollouts verbs: - get @@ -334,6 +336,14 @@ rules: - get - list - watch + - apiGroups: + - coordination.k8s.io + resourceNames: + - dz-scheduler + resources: + - leases + verbs: + - get - apiGroups: - dakr.devzero.io resources: @@ -385,6 +395,45 @@ rules: - get - list - watch + - apiGroups: + - flagger.app + resources: + - alertproviders + - canaries + - metrictemplates + verbs: + - get + - list + - watch + - apiGroups: + - fluxcd.controlplane.io + resources: + - fluxinstances + - fluxreports + - resourcesetinputproviders + - resourcesets + verbs: + - get + - list + - watch + - apiGroups: + - helm.toolkit.fluxcd.io + resources: + - helmreleases + verbs: + - get + - list + - watch + - apiGroups: + - image.toolkit.fluxcd.io + resources: + - imagepolicies + - imagerepositories + - imageupdateautomations + verbs: + - get + - list + - watch - apiGroups: - karpenter.azure.com resources: @@ -449,6 +498,14 @@ rules: - get - list - watch + - apiGroups: + - kustomize.toolkit.fluxcd.io + resources: + - kustomizations + verbs: + - get + - list + - watch - apiGroups: - kyverno.io resources: @@ -477,6 +534,16 @@ rules: - get - list - watch + - apiGroups: + - notification.toolkit.fluxcd.io + resources: + - alerts + - providers + - receivers + verbs: + - get + - list + - watch - apiGroups: - policy resources: @@ -514,6 +581,19 @@ rules: - get - list - watch + - apiGroups: + - source.toolkit.fluxcd.io + resources: + - buckets + - externalartifacts + - gitrepositories + - helmcharts + - helmrepositories + - ocirepositories + verbs: + - get + - list + - watch - apiGroups: - sparkoperator.k8s.io resources: @@ -654,6 +734,19 @@ subjects: name: devzero-zxporter-controller-manager namespace: '{{.zxporter_namespace}}' --- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: devzero-zxporter-metrics-reader-rolebinding +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: devzero-zxporter-metrics-reader +subjects: + - kind: ServiceAccount + name: devzero-zxporter-controller-manager + namespace: '{{.zxporter_namespace}}' +--- apiVersion: v1 kind: Service metadata: @@ -743,6 +836,8 @@ spec: key: CLUSTER_TOKEN name: devzero-zxporter-token optional: true + - name: CLUSTER_TOKEN_SOURCE + value: mirror image: docker.io/devzeroinc/zxporter:latest livenessProbe: httpGet: @@ -802,16 +897,21 @@ metadata: name: zxporter-nodemon namespace: '{{.zxporter_namespace}}' labels: - helm.sh/chart: zxporter-nodemon-0.1.6 + helm.sh/chart: zxporter-nodemon-0.1.25 app.kubernetes.io/name: zxporter-nodemon app.kubernetes.io/instance: zxporter-nodemon - app.kubernetes.io/version: "0.1.6" + app.kubernetes.io/version: "0.1.25" app.kubernetes.io/managed-by: Helm + app.kubernetes.io/part-of: dz-platform + devzero.io/component: zxporter-nodemon --- # Source: zxporter-nodemon/templates/dcgm-exporter-configmap.yaml apiVersion: v1 kind: ConfigMap metadata: + labels: + app.kubernetes.io/part-of: dz-platform + devzero.io/component: zxporter-nodemon name: zxporter-nodemon-dcgm-metrics namespace: '{{.zxporter_namespace}}' data: @@ -827,8 +927,12 @@ data: DCGM_FI_DEV_GPU_UTIL, gauge, GPU utilization (in %). # DCGM_FI_PROF_SM_ACTIVE, gauge, The ratio of cycles an SM has at least 1 warp assigned # DCGM_FI_PROF_SM_OCCUPANCY, gauge, The fraction of resident warps on a multiprocessor - # DCGM_FI_PROF_GR_ENGINE_ACTIVE, gauge, Ratio of time the graphics engine is active (in %). - # DCGM_FI_PROF_DRAM_ACTIVE, gauge, The ratio of cycles the device memory interface is active sending or receiving data. + # Enabled: required for per-MIG-instance profiling data (GPU_I_PROFILE/GPU_I_ID + # labels only attach to DCGM_FI_PROF_* fields, never to plain DCGM_FI_DEV_* + # ones) and directly consumed by nodemon's GPUMigInstance parsing + # (GraphicsEngineActive/DRAMActive/TensorActive below). + DCGM_FI_PROF_GR_ENGINE_ACTIVE, gauge, Ratio of time the graphics engine is active (in %). + DCGM_FI_PROF_DRAM_ACTIVE, gauge, The ratio of cycles the device memory interface is active sending or receiving data. # Memory usage,, DCGM_FI_DEV_FB_FREE, gauge, Framebuffer memory free (in MiB). @@ -847,7 +951,9 @@ data: # DCGM_FI_PROF_PIPE_FP16_ACTIVE, gauge, Ratio of cycles the fp16 pipe is active. # DCGM_FI_PROF_PIPE_FP32_ACTIVE, gauge, Ratio of cycles the fp32 pipe is active. # DCGM_FI_PROF_PIPE_FP64_ACTIVE, gauge, Ratio of cycles the fp64 pipe is active. - # DCGM_FI_PROF_PIPE_TENSOR_ACTIVE, gauge, The ratio of cycles the tensor (HMMA) pipe is active (off the peak sustained elapsed cycles) + # Enabled: see the GR_ENGINE_ACTIVE/DRAM_ACTIVE note above -- this is + # GPUMigInstance.TensorActive. + DCGM_FI_PROF_PIPE_TENSOR_ACTIVE, gauge, The ratio of cycles the tensor (HMMA) pipe is active (off the peak sustained elapsed cycles) # Health,, # DCGM_FI_DEV_CLOCKS_EVENT_REASONS is not supported by DCGM 3.3.7 @@ -868,6 +974,9 @@ data: apiVersion: v1 kind: ConfigMap metadata: + labels: + app.kubernetes.io/part-of: dz-platform + devzero.io/component: zxporter-nodemon name: zxporter-nodemon-zxporter-nodemon namespace: '{{.zxporter_namespace}}' data: @@ -878,11 +987,13 @@ kind: ClusterRole metadata: name: zxporter-nodemon labels: - helm.sh/chart: zxporter-nodemon-0.1.6 + helm.sh/chart: zxporter-nodemon-0.1.25 app.kubernetes.io/name: zxporter-nodemon app.kubernetes.io/instance: zxporter-nodemon - app.kubernetes.io/version: "0.1.6" + app.kubernetes.io/version: "0.1.25" app.kubernetes.io/managed-by: Helm + app.kubernetes.io/part-of: dz-platform + devzero.io/component: zxporter-nodemon rules: - apiGroups: - "" @@ -929,11 +1040,13 @@ kind: ClusterRoleBinding metadata: name: zxporter-nodemon labels: - helm.sh/chart: zxporter-nodemon-0.1.6 + helm.sh/chart: zxporter-nodemon-0.1.25 app.kubernetes.io/name: zxporter-nodemon app.kubernetes.io/instance: zxporter-nodemon - app.kubernetes.io/version: "0.1.6" + app.kubernetes.io/version: "0.1.25" app.kubernetes.io/managed-by: Helm + app.kubernetes.io/part-of: dz-platform + devzero.io/component: zxporter-nodemon roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole @@ -950,10 +1063,12 @@ metadata: name: zxporter-nodemon namespace: '{{.zxporter_namespace}}' labels: - helm.sh/chart: zxporter-nodemon-0.1.6 + app.kubernetes.io/part-of: dz-platform + devzero.io/component: zxporter-nodemon + helm.sh/chart: zxporter-nodemon-0.1.25 app.kubernetes.io/name: zxporter-nodemon app.kubernetes.io/instance: zxporter-nodemon - app.kubernetes.io/version: "0.1.6" + app.kubernetes.io/version: "0.1.25" app.kubernetes.io/managed-by: Helm annotations: ignore-check.kube-linter.io/privileged-container: "This daemon set needs to run DCGM Exporter as privileged to access the GPU metrics." @@ -1012,6 +1127,13 @@ spec: capabilities: add: - SYS_PTRACE + # Bottlerocket (SELinux enforcing) gives each container a unique MCS + # category pair, which denies reading other containers' files via + # /proc//root (JVM hsperfdata, runtime binaries). Granting the + # full category range keeps the container_t type; it is a no-op where + # SELinux is not enforcing. + seLinuxOptions: + level: "s0:c0.c1023" image: "docker.io/devzeroinc/zxporter-nodemon:latest" imagePullPolicy: IfNotPresent ports: @@ -1065,6 +1187,14 @@ spec: memory: 256Mi - name: dcgm-exporter securityContext: + # SYS_ADMIN alone is not enough: enumerating MIG Compute Instance + # profiles via NVML needs broader host access than that one + # capability grants (confirmed live -- SYS_ADMIN-only reproduces + # "CacheManager Init Failed. Error: -17" the instant any MIG + # instance exists on the node; switching to privileged fixes it + # with zero other changes). This is a generic NVML/driver + # requirement, not provider-specific, so unlike the hostPath/ + # LD_LIBRARY_PATH blocks below it must not be gated on gcp only. capabilities: add: - SYS_ADMIN @@ -1092,6 +1222,16 @@ spec: valueFrom: fieldRef: fieldPath: spec.nodeName + # Without this, nvidia-container-runtime's "auto" mode only exposes + # whole/non-MIG GPUs to a non-privileged container with no explicit + # device-plugin resource request (which this sidecar deliberately + # doesn't make, since it must see every GPU on the node, not just + # one). MIG-partitioned GPUs stay invisible without it -- confirmed + # by direct comparison against GPU Operator's own dcgm-exporter, + # which gets the same visibility via privileged:true instead; this + # is the minimal fix that doesn't need the broader grant. + - name: "NVIDIA_VISIBLE_DEVICES" + value: "all" - name: "LD_LIBRARY_PATH" value: "/usr/local/nvidia/lib64" - name: "DCGM_EXPORTER_KUBERNETES_GPU_ID_TYPE" @@ -1104,6 +1244,10 @@ spec: mountPath: /usr/local/nvidia - name: zxporter-nodemon-dcgm-metrics mountPath: "/etc/dcgm-exporter" + resources: + requests: + cpu: 50m + memory: 128Mi --- # Source: zxporter-nodemon/templates/daemonset.yaml apiVersion: apps/v1 @@ -1112,10 +1256,12 @@ metadata: name: zxporter-nodemon-gpu namespace: '{{.zxporter_namespace}}' labels: - helm.sh/chart: zxporter-nodemon-0.1.6 + app.kubernetes.io/part-of: dz-platform + devzero.io/component: zxporter-nodemon + helm.sh/chart: zxporter-nodemon-0.1.25 app.kubernetes.io/name: zxporter-nodemon app.kubernetes.io/instance: zxporter-nodemon-gpu - app.kubernetes.io/version: "0.1.6" + app.kubernetes.io/version: "0.1.25" app.kubernetes.io/managed-by: Helm annotations: ignore-check.kube-linter.io/privileged-container: "This daemon set needs to run DCGM Exporter as privileged to access the GPU metrics." @@ -1177,6 +1323,13 @@ spec: capabilities: add: - SYS_PTRACE + # Bottlerocket (SELinux enforcing) gives each container a unique MCS + # category pair, which denies reading other containers' files via + # /proc//root (JVM hsperfdata, runtime binaries). Granting the + # full category range keeps the container_t type; it is a no-op where + # SELinux is not enforcing. + seLinuxOptions: + level: "s0:c0.c1023" image: "docker.io/devzeroinc/zxporter-nodemon:latest" imagePullPolicy: IfNotPresent ports: @@ -1230,6 +1383,14 @@ spec: memory: 256Mi - name: dcgm-exporter securityContext: + # SYS_ADMIN alone is not enough: enumerating MIG Compute Instance + # profiles via NVML needs broader host access than that one + # capability grants (confirmed live -- SYS_ADMIN-only reproduces + # "CacheManager Init Failed. Error: -17" the instant any MIG + # instance exists on the node; switching to privileged fixes it + # with zero other changes). This is a generic NVML/driver + # requirement, not provider-specific, so unlike the hostPath/ + # LD_LIBRARY_PATH blocks below it must not be gated on gcp only. capabilities: add: - SYS_ADMIN @@ -1257,6 +1418,16 @@ spec: valueFrom: fieldRef: fieldPath: spec.nodeName + # Without this, nvidia-container-runtime's "auto" mode only exposes + # whole/non-MIG GPUs to a non-privileged container with no explicit + # device-plugin resource request (which this sidecar deliberately + # doesn't make, since it must see every GPU on the node, not just + # one). MIG-partitioned GPUs stay invisible without it -- confirmed + # by direct comparison against GPU Operator's own dcgm-exporter, + # which gets the same visibility via privileged:true instead; this + # is the minimal fix that doesn't need the broader grant. + - name: "NVIDIA_VISIBLE_DEVICES" + value: "all" - name: "LD_LIBRARY_PATH" value: "/usr/local/nvidia/lib64" - name: "DCGM_EXPORTER_KUBERNETES_GPU_ID_TYPE" @@ -1269,3 +1440,7 @@ spec: mountPath: /usr/local/nvidia - name: zxporter-nodemon-dcgm-metrics mountPath: "/etc/dcgm-exporter" + resources: + requests: + cpu: 50m + memory: 128Mi diff --git a/dist/installer_updater-lowpriv.yaml b/dist/installer_updater-lowpriv.yaml index 1e3bb7db..fba46972 100644 --- a/dist/installer_updater-lowpriv.yaml +++ b/dist/installer_updater-lowpriv.yaml @@ -287,6 +287,8 @@ rules: - apiGroups: - argoproj.io resources: + - applications + - applicationsets - rollouts verbs: - get @@ -334,6 +336,14 @@ rules: - get - list - watch + - apiGroups: + - coordination.k8s.io + resourceNames: + - dz-scheduler + resources: + - leases + verbs: + - get - apiGroups: - dakr.devzero.io resources: @@ -385,6 +395,45 @@ rules: - get - list - watch + - apiGroups: + - flagger.app + resources: + - alertproviders + - canaries + - metrictemplates + verbs: + - get + - list + - watch + - apiGroups: + - fluxcd.controlplane.io + resources: + - fluxinstances + - fluxreports + - resourcesetinputproviders + - resourcesets + verbs: + - get + - list + - watch + - apiGroups: + - helm.toolkit.fluxcd.io + resources: + - helmreleases + verbs: + - get + - list + - watch + - apiGroups: + - image.toolkit.fluxcd.io + resources: + - imagepolicies + - imagerepositories + - imageupdateautomations + verbs: + - get + - list + - watch - apiGroups: - karpenter.azure.com resources: @@ -449,6 +498,14 @@ rules: - get - list - watch + - apiGroups: + - kustomize.toolkit.fluxcd.io + resources: + - kustomizations + verbs: + - get + - list + - watch - apiGroups: - kyverno.io resources: @@ -477,6 +534,16 @@ rules: - get - list - watch + - apiGroups: + - notification.toolkit.fluxcd.io + resources: + - alerts + - providers + - receivers + verbs: + - get + - list + - watch - apiGroups: - policy resources: @@ -514,6 +581,19 @@ rules: - get - list - watch + - apiGroups: + - source.toolkit.fluxcd.io + resources: + - buckets + - externalartifacts + - gitrepositories + - helmcharts + - helmrepositories + - ocirepositories + verbs: + - get + - list + - watch - apiGroups: - sparkoperator.k8s.io resources: @@ -654,6 +734,19 @@ subjects: name: devzero-zxporter-controller-manager namespace: '{{.zxporter_namespace}}' --- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: devzero-zxporter-metrics-reader-rolebinding +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: devzero-zxporter-metrics-reader +subjects: + - kind: ServiceAccount + name: devzero-zxporter-controller-manager + namespace: '{{.zxporter_namespace}}' +--- apiVersion: v1 kind: Service metadata: @@ -743,6 +836,8 @@ spec: key: CLUSTER_TOKEN name: devzero-zxporter-token optional: true + - name: CLUSTER_TOKEN_SOURCE + value: mirror image: docker.io/devzeroinc/zxporter:latest livenessProbe: httpGet: @@ -802,16 +897,21 @@ metadata: name: zxporter-nodemon namespace: '{{.zxporter_namespace}}' labels: - helm.sh/chart: zxporter-nodemon-0.1.6 + helm.sh/chart: zxporter-nodemon-0.1.25 app.kubernetes.io/name: zxporter-nodemon app.kubernetes.io/instance: zxporter-nodemon - app.kubernetes.io/version: "0.1.6" + app.kubernetes.io/version: "0.1.25" app.kubernetes.io/managed-by: Helm + app.kubernetes.io/part-of: dz-platform + devzero.io/component: zxporter-nodemon --- # Source: zxporter-nodemon/templates/dcgm-exporter-configmap.yaml apiVersion: v1 kind: ConfigMap metadata: + labels: + app.kubernetes.io/part-of: dz-platform + devzero.io/component: zxporter-nodemon name: zxporter-nodemon-dcgm-metrics namespace: '{{.zxporter_namespace}}' data: @@ -827,8 +927,12 @@ data: DCGM_FI_DEV_GPU_UTIL, gauge, GPU utilization (in %). # DCGM_FI_PROF_SM_ACTIVE, gauge, The ratio of cycles an SM has at least 1 warp assigned # DCGM_FI_PROF_SM_OCCUPANCY, gauge, The fraction of resident warps on a multiprocessor - # DCGM_FI_PROF_GR_ENGINE_ACTIVE, gauge, Ratio of time the graphics engine is active (in %). - # DCGM_FI_PROF_DRAM_ACTIVE, gauge, The ratio of cycles the device memory interface is active sending or receiving data. + # Enabled: required for per-MIG-instance profiling data (GPU_I_PROFILE/GPU_I_ID + # labels only attach to DCGM_FI_PROF_* fields, never to plain DCGM_FI_DEV_* + # ones) and directly consumed by nodemon's GPUMigInstance parsing + # (GraphicsEngineActive/DRAMActive/TensorActive below). + DCGM_FI_PROF_GR_ENGINE_ACTIVE, gauge, Ratio of time the graphics engine is active (in %). + DCGM_FI_PROF_DRAM_ACTIVE, gauge, The ratio of cycles the device memory interface is active sending or receiving data. # Memory usage,, DCGM_FI_DEV_FB_FREE, gauge, Framebuffer memory free (in MiB). @@ -847,7 +951,9 @@ data: # DCGM_FI_PROF_PIPE_FP16_ACTIVE, gauge, Ratio of cycles the fp16 pipe is active. # DCGM_FI_PROF_PIPE_FP32_ACTIVE, gauge, Ratio of cycles the fp32 pipe is active. # DCGM_FI_PROF_PIPE_FP64_ACTIVE, gauge, Ratio of cycles the fp64 pipe is active. - # DCGM_FI_PROF_PIPE_TENSOR_ACTIVE, gauge, The ratio of cycles the tensor (HMMA) pipe is active (off the peak sustained elapsed cycles) + # Enabled: see the GR_ENGINE_ACTIVE/DRAM_ACTIVE note above -- this is + # GPUMigInstance.TensorActive. + DCGM_FI_PROF_PIPE_TENSOR_ACTIVE, gauge, The ratio of cycles the tensor (HMMA) pipe is active (off the peak sustained elapsed cycles) # Health,, # DCGM_FI_DEV_CLOCKS_EVENT_REASONS is not supported by DCGM 3.3.7 @@ -868,6 +974,9 @@ data: apiVersion: v1 kind: ConfigMap metadata: + labels: + app.kubernetes.io/part-of: dz-platform + devzero.io/component: zxporter-nodemon name: zxporter-nodemon-zxporter-nodemon namespace: '{{.zxporter_namespace}}' data: @@ -878,11 +987,13 @@ kind: ClusterRole metadata: name: zxporter-nodemon labels: - helm.sh/chart: zxporter-nodemon-0.1.6 + helm.sh/chart: zxporter-nodemon-0.1.25 app.kubernetes.io/name: zxporter-nodemon app.kubernetes.io/instance: zxporter-nodemon - app.kubernetes.io/version: "0.1.6" + app.kubernetes.io/version: "0.1.25" app.kubernetes.io/managed-by: Helm + app.kubernetes.io/part-of: dz-platform + devzero.io/component: zxporter-nodemon rules: - apiGroups: - "" @@ -891,6 +1002,7 @@ rules: verbs: - get - list + - watch - apiGroups: - apps resources: @@ -928,11 +1040,13 @@ kind: ClusterRoleBinding metadata: name: zxporter-nodemon labels: - helm.sh/chart: zxporter-nodemon-0.1.6 + helm.sh/chart: zxporter-nodemon-0.1.25 app.kubernetes.io/name: zxporter-nodemon app.kubernetes.io/instance: zxporter-nodemon - app.kubernetes.io/version: "0.1.6" + app.kubernetes.io/version: "0.1.25" app.kubernetes.io/managed-by: Helm + app.kubernetes.io/part-of: dz-platform + devzero.io/component: zxporter-nodemon roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole @@ -949,10 +1063,12 @@ metadata: name: zxporter-nodemon namespace: '{{.zxporter_namespace}}' labels: - helm.sh/chart: zxporter-nodemon-0.1.6 + app.kubernetes.io/part-of: dz-platform + devzero.io/component: zxporter-nodemon + helm.sh/chart: zxporter-nodemon-0.1.25 app.kubernetes.io/name: zxporter-nodemon app.kubernetes.io/instance: zxporter-nodemon - app.kubernetes.io/version: "0.1.6" + app.kubernetes.io/version: "0.1.25" app.kubernetes.io/managed-by: Helm annotations: ignore-check.kube-linter.io/privileged-container: "This daemon set needs to run DCGM Exporter as privileged to access the GPU metrics." @@ -1056,6 +1172,14 @@ spec: memory: 256Mi - name: dcgm-exporter securityContext: + # SYS_ADMIN alone is not enough: enumerating MIG Compute Instance + # profiles via NVML needs broader host access than that one + # capability grants (confirmed live -- SYS_ADMIN-only reproduces + # "CacheManager Init Failed. Error: -17" the instant any MIG + # instance exists on the node; switching to privileged fixes it + # with zero other changes). This is a generic NVML/driver + # requirement, not provider-specific, so unlike the hostPath/ + # LD_LIBRARY_PATH blocks below it must not be gated on gcp only. capabilities: add: - SYS_ADMIN @@ -1063,6 +1187,7 @@ spec: - NET_RAW runAsNonRoot: false runAsUser: 0 + privileged: true image: "public.ecr.aws/devzeroinc/dcgm-exporter:4.5.2-4.8.1-ubuntu22.04" imagePullPolicy: IfNotPresent command: ["/bin/bash", "-c"] @@ -1082,12 +1207,26 @@ spec: valueFrom: fieldRef: fieldPath: spec.nodeName + # Without this, nvidia-container-runtime's "auto" mode only exposes + # whole/non-MIG GPUs to a non-privileged container with no explicit + # device-plugin resource request (which this sidecar deliberately + # doesn't make, since it must see every GPU on the node, not just + # one). MIG-partitioned GPUs stay invisible without it -- confirmed + # by direct comparison against GPU Operator's own dcgm-exporter, + # which gets the same visibility via privileged:true instead; this + # is the minimal fix that doesn't need the broader grant. + - name: "NVIDIA_VISIBLE_DEVICES" + value: "all" volumeMounts: - name: "pod-gpu-resources" readOnly: true mountPath: "/var/lib/kubelet/pod-resources" - name: zxporter-nodemon-dcgm-metrics mountPath: "/etc/dcgm-exporter" + resources: + requests: + cpu: 50m + memory: 128Mi --- # Source: zxporter-nodemon/templates/daemonset.yaml apiVersion: apps/v1 @@ -1096,10 +1235,12 @@ metadata: name: zxporter-nodemon-gpu namespace: '{{.zxporter_namespace}}' labels: - helm.sh/chart: zxporter-nodemon-0.1.6 + app.kubernetes.io/part-of: dz-platform + devzero.io/component: zxporter-nodemon + helm.sh/chart: zxporter-nodemon-0.1.25 app.kubernetes.io/name: zxporter-nodemon app.kubernetes.io/instance: zxporter-nodemon-gpu - app.kubernetes.io/version: "0.1.6" + app.kubernetes.io/version: "0.1.25" app.kubernetes.io/managed-by: Helm annotations: ignore-check.kube-linter.io/privileged-container: "This daemon set needs to run DCGM Exporter as privileged to access the GPU metrics." @@ -1206,6 +1347,14 @@ spec: memory: 256Mi - name: dcgm-exporter securityContext: + # SYS_ADMIN alone is not enough: enumerating MIG Compute Instance + # profiles via NVML needs broader host access than that one + # capability grants (confirmed live -- SYS_ADMIN-only reproduces + # "CacheManager Init Failed. Error: -17" the instant any MIG + # instance exists on the node; switching to privileged fixes it + # with zero other changes). This is a generic NVML/driver + # requirement, not provider-specific, so unlike the hostPath/ + # LD_LIBRARY_PATH blocks below it must not be gated on gcp only. capabilities: add: - SYS_ADMIN @@ -1213,6 +1362,7 @@ spec: - NET_RAW runAsNonRoot: false runAsUser: 0 + privileged: true image: "public.ecr.aws/devzeroinc/dcgm-exporter:4.5.2-4.8.1-ubuntu22.04" imagePullPolicy: IfNotPresent command: ["/bin/bash", "-c"] @@ -1232,9 +1382,23 @@ spec: valueFrom: fieldRef: fieldPath: spec.nodeName + # Without this, nvidia-container-runtime's "auto" mode only exposes + # whole/non-MIG GPUs to a non-privileged container with no explicit + # device-plugin resource request (which this sidecar deliberately + # doesn't make, since it must see every GPU on the node, not just + # one). MIG-partitioned GPUs stay invisible without it -- confirmed + # by direct comparison against GPU Operator's own dcgm-exporter, + # which gets the same visibility via privileged:true instead; this + # is the minimal fix that doesn't need the broader grant. + - name: "NVIDIA_VISIBLE_DEVICES" + value: "all" volumeMounts: - name: "pod-gpu-resources" readOnly: true mountPath: "/var/lib/kubelet/pod-resources" - name: zxporter-nodemon-dcgm-metrics mountPath: "/etc/dcgm-exporter" + resources: + requests: + cpu: 50m + memory: 128Mi diff --git a/dist/installer_updater.yaml b/dist/installer_updater.yaml index 46ae017a..625e1394 100644 --- a/dist/installer_updater.yaml +++ b/dist/installer_updater.yaml @@ -287,6 +287,8 @@ rules: - apiGroups: - argoproj.io resources: + - applications + - applicationsets - rollouts verbs: - get @@ -334,6 +336,14 @@ rules: - get - list - watch + - apiGroups: + - coordination.k8s.io + resourceNames: + - dz-scheduler + resources: + - leases + verbs: + - get - apiGroups: - dakr.devzero.io resources: @@ -385,6 +395,45 @@ rules: - get - list - watch + - apiGroups: + - flagger.app + resources: + - alertproviders + - canaries + - metrictemplates + verbs: + - get + - list + - watch + - apiGroups: + - fluxcd.controlplane.io + resources: + - fluxinstances + - fluxreports + - resourcesetinputproviders + - resourcesets + verbs: + - get + - list + - watch + - apiGroups: + - helm.toolkit.fluxcd.io + resources: + - helmreleases + verbs: + - get + - list + - watch + - apiGroups: + - image.toolkit.fluxcd.io + resources: + - imagepolicies + - imagerepositories + - imageupdateautomations + verbs: + - get + - list + - watch - apiGroups: - karpenter.azure.com resources: @@ -449,6 +498,14 @@ rules: - get - list - watch + - apiGroups: + - kustomize.toolkit.fluxcd.io + resources: + - kustomizations + verbs: + - get + - list + - watch - apiGroups: - kyverno.io resources: @@ -477,6 +534,16 @@ rules: - get - list - watch + - apiGroups: + - notification.toolkit.fluxcd.io + resources: + - alerts + - providers + - receivers + verbs: + - get + - list + - watch - apiGroups: - policy resources: @@ -514,6 +581,19 @@ rules: - get - list - watch + - apiGroups: + - source.toolkit.fluxcd.io + resources: + - buckets + - externalartifacts + - gitrepositories + - helmcharts + - helmrepositories + - ocirepositories + verbs: + - get + - list + - watch - apiGroups: - sparkoperator.k8s.io resources: @@ -654,6 +734,19 @@ subjects: name: devzero-zxporter-controller-manager namespace: '{{.zxporter_namespace}}' --- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: devzero-zxporter-metrics-reader-rolebinding +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: devzero-zxporter-metrics-reader +subjects: + - kind: ServiceAccount + name: devzero-zxporter-controller-manager + namespace: '{{.zxporter_namespace}}' +--- apiVersion: v1 kind: Service metadata: @@ -743,6 +836,8 @@ spec: key: CLUSTER_TOKEN name: devzero-zxporter-token optional: true + - name: CLUSTER_TOKEN_SOURCE + value: mirror image: docker.io/devzeroinc/zxporter:latest livenessProbe: httpGet: @@ -802,16 +897,21 @@ metadata: name: zxporter-nodemon namespace: '{{.zxporter_namespace}}' labels: - helm.sh/chart: zxporter-nodemon-0.1.6 + helm.sh/chart: zxporter-nodemon-0.1.25 app.kubernetes.io/name: zxporter-nodemon app.kubernetes.io/instance: zxporter-nodemon - app.kubernetes.io/version: "0.1.6" + app.kubernetes.io/version: "0.1.25" app.kubernetes.io/managed-by: Helm + app.kubernetes.io/part-of: dz-platform + devzero.io/component: zxporter-nodemon --- # Source: zxporter-nodemon/templates/dcgm-exporter-configmap.yaml apiVersion: v1 kind: ConfigMap metadata: + labels: + app.kubernetes.io/part-of: dz-platform + devzero.io/component: zxporter-nodemon name: zxporter-nodemon-dcgm-metrics namespace: '{{.zxporter_namespace}}' data: @@ -827,8 +927,12 @@ data: DCGM_FI_DEV_GPU_UTIL, gauge, GPU utilization (in %). # DCGM_FI_PROF_SM_ACTIVE, gauge, The ratio of cycles an SM has at least 1 warp assigned # DCGM_FI_PROF_SM_OCCUPANCY, gauge, The fraction of resident warps on a multiprocessor - # DCGM_FI_PROF_GR_ENGINE_ACTIVE, gauge, Ratio of time the graphics engine is active (in %). - # DCGM_FI_PROF_DRAM_ACTIVE, gauge, The ratio of cycles the device memory interface is active sending or receiving data. + # Enabled: required for per-MIG-instance profiling data (GPU_I_PROFILE/GPU_I_ID + # labels only attach to DCGM_FI_PROF_* fields, never to plain DCGM_FI_DEV_* + # ones) and directly consumed by nodemon's GPUMigInstance parsing + # (GraphicsEngineActive/DRAMActive/TensorActive below). + DCGM_FI_PROF_GR_ENGINE_ACTIVE, gauge, Ratio of time the graphics engine is active (in %). + DCGM_FI_PROF_DRAM_ACTIVE, gauge, The ratio of cycles the device memory interface is active sending or receiving data. # Memory usage,, DCGM_FI_DEV_FB_FREE, gauge, Framebuffer memory free (in MiB). @@ -847,7 +951,9 @@ data: # DCGM_FI_PROF_PIPE_FP16_ACTIVE, gauge, Ratio of cycles the fp16 pipe is active. # DCGM_FI_PROF_PIPE_FP32_ACTIVE, gauge, Ratio of cycles the fp32 pipe is active. # DCGM_FI_PROF_PIPE_FP64_ACTIVE, gauge, Ratio of cycles the fp64 pipe is active. - # DCGM_FI_PROF_PIPE_TENSOR_ACTIVE, gauge, The ratio of cycles the tensor (HMMA) pipe is active (off the peak sustained elapsed cycles) + # Enabled: see the GR_ENGINE_ACTIVE/DRAM_ACTIVE note above -- this is + # GPUMigInstance.TensorActive. + DCGM_FI_PROF_PIPE_TENSOR_ACTIVE, gauge, The ratio of cycles the tensor (HMMA) pipe is active (off the peak sustained elapsed cycles) # Health,, # DCGM_FI_DEV_CLOCKS_EVENT_REASONS is not supported by DCGM 3.3.7 @@ -868,6 +974,9 @@ data: apiVersion: v1 kind: ConfigMap metadata: + labels: + app.kubernetes.io/part-of: dz-platform + devzero.io/component: zxporter-nodemon name: zxporter-nodemon-zxporter-nodemon namespace: '{{.zxporter_namespace}}' data: @@ -878,11 +987,13 @@ kind: ClusterRole metadata: name: zxporter-nodemon labels: - helm.sh/chart: zxporter-nodemon-0.1.6 + helm.sh/chart: zxporter-nodemon-0.1.25 app.kubernetes.io/name: zxporter-nodemon app.kubernetes.io/instance: zxporter-nodemon - app.kubernetes.io/version: "0.1.6" + app.kubernetes.io/version: "0.1.25" app.kubernetes.io/managed-by: Helm + app.kubernetes.io/part-of: dz-platform + devzero.io/component: zxporter-nodemon rules: - apiGroups: - "" @@ -929,11 +1040,13 @@ kind: ClusterRoleBinding metadata: name: zxporter-nodemon labels: - helm.sh/chart: zxporter-nodemon-0.1.6 + helm.sh/chart: zxporter-nodemon-0.1.25 app.kubernetes.io/name: zxporter-nodemon app.kubernetes.io/instance: zxporter-nodemon - app.kubernetes.io/version: "0.1.6" + app.kubernetes.io/version: "0.1.25" app.kubernetes.io/managed-by: Helm + app.kubernetes.io/part-of: dz-platform + devzero.io/component: zxporter-nodemon roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole @@ -950,10 +1063,12 @@ metadata: name: zxporter-nodemon namespace: '{{.zxporter_namespace}}' labels: - helm.sh/chart: zxporter-nodemon-0.1.6 + app.kubernetes.io/part-of: dz-platform + devzero.io/component: zxporter-nodemon + helm.sh/chart: zxporter-nodemon-0.1.25 app.kubernetes.io/name: zxporter-nodemon app.kubernetes.io/instance: zxporter-nodemon - app.kubernetes.io/version: "0.1.6" + app.kubernetes.io/version: "0.1.25" app.kubernetes.io/managed-by: Helm annotations: ignore-check.kube-linter.io/privileged-container: "This daemon set needs to run DCGM Exporter as privileged to access the GPU metrics." @@ -1009,6 +1124,13 @@ spec: capabilities: add: - SYS_PTRACE + # Bottlerocket (SELinux enforcing) gives each container a unique MCS + # category pair, which denies reading other containers' files via + # /proc//root (JVM hsperfdata, runtime binaries). Granting the + # full category range keeps the container_t type; it is a no-op where + # SELinux is not enforcing. + seLinuxOptions: + level: "s0:c0.c1023" image: "docker.io/devzeroinc/zxporter-nodemon:latest" imagePullPolicy: IfNotPresent ports: @@ -1062,6 +1184,14 @@ spec: memory: 256Mi - name: dcgm-exporter securityContext: + # SYS_ADMIN alone is not enough: enumerating MIG Compute Instance + # profiles via NVML needs broader host access than that one + # capability grants (confirmed live -- SYS_ADMIN-only reproduces + # "CacheManager Init Failed. Error: -17" the instant any MIG + # instance exists on the node; switching to privileged fixes it + # with zero other changes). This is a generic NVML/driver + # requirement, not provider-specific, so unlike the hostPath/ + # LD_LIBRARY_PATH blocks below it must not be gated on gcp only. capabilities: add: - SYS_ADMIN @@ -1069,6 +1199,7 @@ spec: - NET_RAW runAsNonRoot: false runAsUser: 0 + privileged: true image: "public.ecr.aws/devzeroinc/dcgm-exporter:4.5.2-4.8.1-ubuntu22.04" imagePullPolicy: IfNotPresent command: ["/bin/bash", "-c"] @@ -1088,12 +1219,26 @@ spec: valueFrom: fieldRef: fieldPath: spec.nodeName + # Without this, nvidia-container-runtime's "auto" mode only exposes + # whole/non-MIG GPUs to a non-privileged container with no explicit + # device-plugin resource request (which this sidecar deliberately + # doesn't make, since it must see every GPU on the node, not just + # one). MIG-partitioned GPUs stay invisible without it -- confirmed + # by direct comparison against GPU Operator's own dcgm-exporter, + # which gets the same visibility via privileged:true instead; this + # is the minimal fix that doesn't need the broader grant. + - name: "NVIDIA_VISIBLE_DEVICES" + value: "all" volumeMounts: - name: "pod-gpu-resources" readOnly: true mountPath: "/var/lib/kubelet/pod-resources" - name: zxporter-nodemon-dcgm-metrics mountPath: "/etc/dcgm-exporter" + resources: + requests: + cpu: 50m + memory: 128Mi --- # Source: zxporter-nodemon/templates/daemonset.yaml apiVersion: apps/v1 @@ -1102,10 +1247,12 @@ metadata: name: zxporter-nodemon-gpu namespace: '{{.zxporter_namespace}}' labels: - helm.sh/chart: zxporter-nodemon-0.1.6 + app.kubernetes.io/part-of: dz-platform + devzero.io/component: zxporter-nodemon + helm.sh/chart: zxporter-nodemon-0.1.25 app.kubernetes.io/name: zxporter-nodemon app.kubernetes.io/instance: zxporter-nodemon-gpu - app.kubernetes.io/version: "0.1.6" + app.kubernetes.io/version: "0.1.25" app.kubernetes.io/managed-by: Helm annotations: ignore-check.kube-linter.io/privileged-container: "This daemon set needs to run DCGM Exporter as privileged to access the GPU metrics." @@ -1164,6 +1311,13 @@ spec: capabilities: add: - SYS_PTRACE + # Bottlerocket (SELinux enforcing) gives each container a unique MCS + # category pair, which denies reading other containers' files via + # /proc//root (JVM hsperfdata, runtime binaries). Granting the + # full category range keeps the container_t type; it is a no-op where + # SELinux is not enforcing. + seLinuxOptions: + level: "s0:c0.c1023" image: "docker.io/devzeroinc/zxporter-nodemon:latest" imagePullPolicy: IfNotPresent ports: @@ -1217,6 +1371,14 @@ spec: memory: 256Mi - name: dcgm-exporter securityContext: + # SYS_ADMIN alone is not enough: enumerating MIG Compute Instance + # profiles via NVML needs broader host access than that one + # capability grants (confirmed live -- SYS_ADMIN-only reproduces + # "CacheManager Init Failed. Error: -17" the instant any MIG + # instance exists on the node; switching to privileged fixes it + # with zero other changes). This is a generic NVML/driver + # requirement, not provider-specific, so unlike the hostPath/ + # LD_LIBRARY_PATH blocks below it must not be gated on gcp only. capabilities: add: - SYS_ADMIN @@ -1224,6 +1386,7 @@ spec: - NET_RAW runAsNonRoot: false runAsUser: 0 + privileged: true image: "public.ecr.aws/devzeroinc/dcgm-exporter:4.5.2-4.8.1-ubuntu22.04" imagePullPolicy: IfNotPresent command: ["/bin/bash", "-c"] @@ -1243,9 +1406,23 @@ spec: valueFrom: fieldRef: fieldPath: spec.nodeName + # Without this, nvidia-container-runtime's "auto" mode only exposes + # whole/non-MIG GPUs to a non-privileged container with no explicit + # device-plugin resource request (which this sidecar deliberately + # doesn't make, since it must see every GPU on the node, not just + # one). MIG-partitioned GPUs stay invisible without it -- confirmed + # by direct comparison against GPU Operator's own dcgm-exporter, + # which gets the same visibility via privileged:true instead; this + # is the minimal fix that doesn't need the broader grant. + - name: "NVIDIA_VISIBLE_DEVICES" + value: "all" volumeMounts: - name: "pod-gpu-resources" readOnly: true mountPath: "/var/lib/kubelet/pod-resources" - name: zxporter-nodemon-dcgm-metrics mountPath: "/etc/dcgm-exporter" + resources: + requests: + cpu: 50m + memory: 128Mi diff --git a/dist/nodemon-gcp-lowpriv.yaml b/dist/nodemon-gcp-lowpriv.yaml index b7b1cfb2..d9481810 100644 --- a/dist/nodemon-gcp-lowpriv.yaml +++ b/dist/nodemon-gcp-lowpriv.yaml @@ -6,16 +6,21 @@ metadata: name: zxporter-nodemon namespace: devzero-system labels: - helm.sh/chart: zxporter-nodemon-0.1.6 + helm.sh/chart: zxporter-nodemon-0.1.25 app.kubernetes.io/name: zxporter-nodemon app.kubernetes.io/instance: zxporter-nodemon - app.kubernetes.io/version: "0.1.6" + app.kubernetes.io/version: "0.1.25" app.kubernetes.io/managed-by: Helm + app.kubernetes.io/part-of: dz-platform + devzero.io/component: zxporter-nodemon --- # Source: zxporter-nodemon/templates/dcgm-exporter-configmap.yaml apiVersion: v1 kind: ConfigMap metadata: + labels: + app.kubernetes.io/part-of: dz-platform + devzero.io/component: zxporter-nodemon name: zxporter-nodemon-dcgm-metrics namespace: devzero-system data: @@ -31,8 +36,12 @@ data: DCGM_FI_DEV_GPU_UTIL, gauge, GPU utilization (in %). # DCGM_FI_PROF_SM_ACTIVE, gauge, The ratio of cycles an SM has at least 1 warp assigned # DCGM_FI_PROF_SM_OCCUPANCY, gauge, The fraction of resident warps on a multiprocessor - # DCGM_FI_PROF_GR_ENGINE_ACTIVE, gauge, Ratio of time the graphics engine is active (in %). - # DCGM_FI_PROF_DRAM_ACTIVE, gauge, The ratio of cycles the device memory interface is active sending or receiving data. + # Enabled: required for per-MIG-instance profiling data (GPU_I_PROFILE/GPU_I_ID + # labels only attach to DCGM_FI_PROF_* fields, never to plain DCGM_FI_DEV_* + # ones) and directly consumed by nodemon's GPUMigInstance parsing + # (GraphicsEngineActive/DRAMActive/TensorActive below). + DCGM_FI_PROF_GR_ENGINE_ACTIVE, gauge, Ratio of time the graphics engine is active (in %). + DCGM_FI_PROF_DRAM_ACTIVE, gauge, The ratio of cycles the device memory interface is active sending or receiving data. # Memory usage,, DCGM_FI_DEV_FB_FREE, gauge, Framebuffer memory free (in MiB). @@ -51,7 +60,9 @@ data: # DCGM_FI_PROF_PIPE_FP16_ACTIVE, gauge, Ratio of cycles the fp16 pipe is active. # DCGM_FI_PROF_PIPE_FP32_ACTIVE, gauge, Ratio of cycles the fp32 pipe is active. # DCGM_FI_PROF_PIPE_FP64_ACTIVE, gauge, Ratio of cycles the fp64 pipe is active. - # DCGM_FI_PROF_PIPE_TENSOR_ACTIVE, gauge, The ratio of cycles the tensor (HMMA) pipe is active (off the peak sustained elapsed cycles) + # Enabled: see the GR_ENGINE_ACTIVE/DRAM_ACTIVE note above -- this is + # GPUMigInstance.TensorActive. + DCGM_FI_PROF_PIPE_TENSOR_ACTIVE, gauge, The ratio of cycles the tensor (HMMA) pipe is active (off the peak sustained elapsed cycles) # Health,, # DCGM_FI_DEV_CLOCKS_EVENT_REASONS is not supported by DCGM 3.3.7 @@ -72,6 +83,9 @@ data: apiVersion: v1 kind: ConfigMap metadata: + labels: + app.kubernetes.io/part-of: dz-platform + devzero.io/component: zxporter-nodemon name: zxporter-nodemon-zxporter-nodemon namespace: devzero-system data: @@ -82,11 +96,13 @@ kind: ClusterRole metadata: name: zxporter-nodemon labels: - helm.sh/chart: zxporter-nodemon-0.1.6 + helm.sh/chart: zxporter-nodemon-0.1.25 app.kubernetes.io/name: zxporter-nodemon app.kubernetes.io/instance: zxporter-nodemon - app.kubernetes.io/version: "0.1.6" + app.kubernetes.io/version: "0.1.25" app.kubernetes.io/managed-by: Helm + app.kubernetes.io/part-of: dz-platform + devzero.io/component: zxporter-nodemon rules: - apiGroups: - "" @@ -95,6 +111,7 @@ rules: verbs: - get - list + - watch - apiGroups: - apps resources: @@ -132,11 +149,13 @@ kind: ClusterRoleBinding metadata: name: zxporter-nodemon labels: - helm.sh/chart: zxporter-nodemon-0.1.6 + helm.sh/chart: zxporter-nodemon-0.1.25 app.kubernetes.io/name: zxporter-nodemon app.kubernetes.io/instance: zxporter-nodemon - app.kubernetes.io/version: "0.1.6" + app.kubernetes.io/version: "0.1.25" app.kubernetes.io/managed-by: Helm + app.kubernetes.io/part-of: dz-platform + devzero.io/component: zxporter-nodemon roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole @@ -153,10 +172,12 @@ metadata: name: zxporter-nodemon namespace: devzero-system labels: - helm.sh/chart: zxporter-nodemon-0.1.6 + app.kubernetes.io/part-of: dz-platform + devzero.io/component: zxporter-nodemon + helm.sh/chart: zxporter-nodemon-0.1.25 app.kubernetes.io/name: zxporter-nodemon app.kubernetes.io/instance: zxporter-nodemon - app.kubernetes.io/version: "0.1.6" + app.kubernetes.io/version: "0.1.25" app.kubernetes.io/managed-by: Helm annotations: ignore-check.kube-linter.io/privileged-container: "This daemon set needs to run DCGM Exporter as privileged to access the GPU metrics." @@ -263,6 +284,14 @@ spec: memory: 256Mi - name: dcgm-exporter securityContext: + # SYS_ADMIN alone is not enough: enumerating MIG Compute Instance + # profiles via NVML needs broader host access than that one + # capability grants (confirmed live -- SYS_ADMIN-only reproduces + # "CacheManager Init Failed. Error: -17" the instant any MIG + # instance exists on the node; switching to privileged fixes it + # with zero other changes). This is a generic NVML/driver + # requirement, not provider-specific, so unlike the hostPath/ + # LD_LIBRARY_PATH blocks below it must not be gated on gcp only. capabilities: add: - SYS_ADMIN @@ -290,6 +319,16 @@ spec: valueFrom: fieldRef: fieldPath: spec.nodeName + # Without this, nvidia-container-runtime's "auto" mode only exposes + # whole/non-MIG GPUs to a non-privileged container with no explicit + # device-plugin resource request (which this sidecar deliberately + # doesn't make, since it must see every GPU on the node, not just + # one). MIG-partitioned GPUs stay invisible without it -- confirmed + # by direct comparison against GPU Operator's own dcgm-exporter, + # which gets the same visibility via privileged:true instead; this + # is the minimal fix that doesn't need the broader grant. + - name: "NVIDIA_VISIBLE_DEVICES" + value: "all" - name: "LD_LIBRARY_PATH" value: "/usr/local/nvidia/lib64" - name: "DCGM_EXPORTER_KUBERNETES_GPU_ID_TYPE" @@ -302,6 +341,10 @@ spec: mountPath: /usr/local/nvidia - name: zxporter-nodemon-dcgm-metrics mountPath: "/etc/dcgm-exporter" + resources: + requests: + cpu: 50m + memory: 128Mi --- # Source: zxporter-nodemon/templates/daemonset.yaml apiVersion: apps/v1 @@ -310,10 +353,12 @@ metadata: name: zxporter-nodemon-gpu namespace: devzero-system labels: - helm.sh/chart: zxporter-nodemon-0.1.6 + app.kubernetes.io/part-of: dz-platform + devzero.io/component: zxporter-nodemon + helm.sh/chart: zxporter-nodemon-0.1.25 app.kubernetes.io/name: zxporter-nodemon app.kubernetes.io/instance: zxporter-nodemon-gpu - app.kubernetes.io/version: "0.1.6" + app.kubernetes.io/version: "0.1.25" app.kubernetes.io/managed-by: Helm annotations: ignore-check.kube-linter.io/privileged-container: "This daemon set needs to run DCGM Exporter as privileged to access the GPU metrics." @@ -423,6 +468,14 @@ spec: memory: 256Mi - name: dcgm-exporter securityContext: + # SYS_ADMIN alone is not enough: enumerating MIG Compute Instance + # profiles via NVML needs broader host access than that one + # capability grants (confirmed live -- SYS_ADMIN-only reproduces + # "CacheManager Init Failed. Error: -17" the instant any MIG + # instance exists on the node; switching to privileged fixes it + # with zero other changes). This is a generic NVML/driver + # requirement, not provider-specific, so unlike the hostPath/ + # LD_LIBRARY_PATH blocks below it must not be gated on gcp only. capabilities: add: - SYS_ADMIN @@ -450,6 +503,16 @@ spec: valueFrom: fieldRef: fieldPath: spec.nodeName + # Without this, nvidia-container-runtime's "auto" mode only exposes + # whole/non-MIG GPUs to a non-privileged container with no explicit + # device-plugin resource request (which this sidecar deliberately + # doesn't make, since it must see every GPU on the node, not just + # one). MIG-partitioned GPUs stay invisible without it -- confirmed + # by direct comparison against GPU Operator's own dcgm-exporter, + # which gets the same visibility via privileged:true instead; this + # is the minimal fix that doesn't need the broader grant. + - name: "NVIDIA_VISIBLE_DEVICES" + value: "all" - name: "LD_LIBRARY_PATH" value: "/usr/local/nvidia/lib64" - name: "DCGM_EXPORTER_KUBERNETES_GPU_ID_TYPE" @@ -462,3 +525,7 @@ spec: mountPath: /usr/local/nvidia - name: zxporter-nodemon-dcgm-metrics mountPath: "/etc/dcgm-exporter" + resources: + requests: + cpu: 50m + memory: 128Mi diff --git a/dist/nodemon-gcp.yaml b/dist/nodemon-gcp.yaml index a53c89cf..95255160 100644 --- a/dist/nodemon-gcp.yaml +++ b/dist/nodemon-gcp.yaml @@ -6,16 +6,21 @@ metadata: name: zxporter-nodemon namespace: devzero-system labels: - helm.sh/chart: zxporter-nodemon-0.1.6 + helm.sh/chart: zxporter-nodemon-0.1.25 app.kubernetes.io/name: zxporter-nodemon app.kubernetes.io/instance: zxporter-nodemon - app.kubernetes.io/version: "0.1.6" + app.kubernetes.io/version: "0.1.25" app.kubernetes.io/managed-by: Helm + app.kubernetes.io/part-of: dz-platform + devzero.io/component: zxporter-nodemon --- # Source: zxporter-nodemon/templates/dcgm-exporter-configmap.yaml apiVersion: v1 kind: ConfigMap metadata: + labels: + app.kubernetes.io/part-of: dz-platform + devzero.io/component: zxporter-nodemon name: zxporter-nodemon-dcgm-metrics namespace: devzero-system data: @@ -31,8 +36,12 @@ data: DCGM_FI_DEV_GPU_UTIL, gauge, GPU utilization (in %). # DCGM_FI_PROF_SM_ACTIVE, gauge, The ratio of cycles an SM has at least 1 warp assigned # DCGM_FI_PROF_SM_OCCUPANCY, gauge, The fraction of resident warps on a multiprocessor - # DCGM_FI_PROF_GR_ENGINE_ACTIVE, gauge, Ratio of time the graphics engine is active (in %). - # DCGM_FI_PROF_DRAM_ACTIVE, gauge, The ratio of cycles the device memory interface is active sending or receiving data. + # Enabled: required for per-MIG-instance profiling data (GPU_I_PROFILE/GPU_I_ID + # labels only attach to DCGM_FI_PROF_* fields, never to plain DCGM_FI_DEV_* + # ones) and directly consumed by nodemon's GPUMigInstance parsing + # (GraphicsEngineActive/DRAMActive/TensorActive below). + DCGM_FI_PROF_GR_ENGINE_ACTIVE, gauge, Ratio of time the graphics engine is active (in %). + DCGM_FI_PROF_DRAM_ACTIVE, gauge, The ratio of cycles the device memory interface is active sending or receiving data. # Memory usage,, DCGM_FI_DEV_FB_FREE, gauge, Framebuffer memory free (in MiB). @@ -51,7 +60,9 @@ data: # DCGM_FI_PROF_PIPE_FP16_ACTIVE, gauge, Ratio of cycles the fp16 pipe is active. # DCGM_FI_PROF_PIPE_FP32_ACTIVE, gauge, Ratio of cycles the fp32 pipe is active. # DCGM_FI_PROF_PIPE_FP64_ACTIVE, gauge, Ratio of cycles the fp64 pipe is active. - # DCGM_FI_PROF_PIPE_TENSOR_ACTIVE, gauge, The ratio of cycles the tensor (HMMA) pipe is active (off the peak sustained elapsed cycles) + # Enabled: see the GR_ENGINE_ACTIVE/DRAM_ACTIVE note above -- this is + # GPUMigInstance.TensorActive. + DCGM_FI_PROF_PIPE_TENSOR_ACTIVE, gauge, The ratio of cycles the tensor (HMMA) pipe is active (off the peak sustained elapsed cycles) # Health,, # DCGM_FI_DEV_CLOCKS_EVENT_REASONS is not supported by DCGM 3.3.7 @@ -72,6 +83,9 @@ data: apiVersion: v1 kind: ConfigMap metadata: + labels: + app.kubernetes.io/part-of: dz-platform + devzero.io/component: zxporter-nodemon name: zxporter-nodemon-zxporter-nodemon namespace: devzero-system data: @@ -82,11 +96,13 @@ kind: ClusterRole metadata: name: zxporter-nodemon labels: - helm.sh/chart: zxporter-nodemon-0.1.6 + helm.sh/chart: zxporter-nodemon-0.1.25 app.kubernetes.io/name: zxporter-nodemon app.kubernetes.io/instance: zxporter-nodemon - app.kubernetes.io/version: "0.1.6" + app.kubernetes.io/version: "0.1.25" app.kubernetes.io/managed-by: Helm + app.kubernetes.io/part-of: dz-platform + devzero.io/component: zxporter-nodemon rules: - apiGroups: - "" @@ -133,11 +149,13 @@ kind: ClusterRoleBinding metadata: name: zxporter-nodemon labels: - helm.sh/chart: zxporter-nodemon-0.1.6 + helm.sh/chart: zxporter-nodemon-0.1.25 app.kubernetes.io/name: zxporter-nodemon app.kubernetes.io/instance: zxporter-nodemon - app.kubernetes.io/version: "0.1.6" + app.kubernetes.io/version: "0.1.25" app.kubernetes.io/managed-by: Helm + app.kubernetes.io/part-of: dz-platform + devzero.io/component: zxporter-nodemon roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole @@ -154,10 +172,12 @@ metadata: name: zxporter-nodemon namespace: devzero-system labels: - helm.sh/chart: zxporter-nodemon-0.1.6 + app.kubernetes.io/part-of: dz-platform + devzero.io/component: zxporter-nodemon + helm.sh/chart: zxporter-nodemon-0.1.25 app.kubernetes.io/name: zxporter-nodemon app.kubernetes.io/instance: zxporter-nodemon - app.kubernetes.io/version: "0.1.6" + app.kubernetes.io/version: "0.1.25" app.kubernetes.io/managed-by: Helm annotations: ignore-check.kube-linter.io/privileged-container: "This daemon set needs to run DCGM Exporter as privileged to access the GPU metrics." @@ -216,6 +236,13 @@ spec: capabilities: add: - SYS_PTRACE + # Bottlerocket (SELinux enforcing) gives each container a unique MCS + # category pair, which denies reading other containers' files via + # /proc//root (JVM hsperfdata, runtime binaries). Granting the + # full category range keeps the container_t type; it is a no-op where + # SELinux is not enforcing. + seLinuxOptions: + level: "s0:c0.c1023" image: "ttl.sh/zxporter-nodemon:latest" imagePullPolicy: IfNotPresent ports: @@ -269,6 +296,14 @@ spec: memory: 256Mi - name: dcgm-exporter securityContext: + # SYS_ADMIN alone is not enough: enumerating MIG Compute Instance + # profiles via NVML needs broader host access than that one + # capability grants (confirmed live -- SYS_ADMIN-only reproduces + # "CacheManager Init Failed. Error: -17" the instant any MIG + # instance exists on the node; switching to privileged fixes it + # with zero other changes). This is a generic NVML/driver + # requirement, not provider-specific, so unlike the hostPath/ + # LD_LIBRARY_PATH blocks below it must not be gated on gcp only. capabilities: add: - SYS_ADMIN @@ -296,6 +331,16 @@ spec: valueFrom: fieldRef: fieldPath: spec.nodeName + # Without this, nvidia-container-runtime's "auto" mode only exposes + # whole/non-MIG GPUs to a non-privileged container with no explicit + # device-plugin resource request (which this sidecar deliberately + # doesn't make, since it must see every GPU on the node, not just + # one). MIG-partitioned GPUs stay invisible without it -- confirmed + # by direct comparison against GPU Operator's own dcgm-exporter, + # which gets the same visibility via privileged:true instead; this + # is the minimal fix that doesn't need the broader grant. + - name: "NVIDIA_VISIBLE_DEVICES" + value: "all" - name: "LD_LIBRARY_PATH" value: "/usr/local/nvidia/lib64" - name: "DCGM_EXPORTER_KUBERNETES_GPU_ID_TYPE" @@ -308,6 +353,10 @@ spec: mountPath: /usr/local/nvidia - name: zxporter-nodemon-dcgm-metrics mountPath: "/etc/dcgm-exporter" + resources: + requests: + cpu: 50m + memory: 128Mi --- # Source: zxporter-nodemon/templates/daemonset.yaml apiVersion: apps/v1 @@ -316,10 +365,12 @@ metadata: name: zxporter-nodemon-gpu namespace: devzero-system labels: - helm.sh/chart: zxporter-nodemon-0.1.6 + app.kubernetes.io/part-of: dz-platform + devzero.io/component: zxporter-nodemon + helm.sh/chart: zxporter-nodemon-0.1.25 app.kubernetes.io/name: zxporter-nodemon app.kubernetes.io/instance: zxporter-nodemon-gpu - app.kubernetes.io/version: "0.1.6" + app.kubernetes.io/version: "0.1.25" app.kubernetes.io/managed-by: Helm annotations: ignore-check.kube-linter.io/privileged-container: "This daemon set needs to run DCGM Exporter as privileged to access the GPU metrics." @@ -381,6 +432,13 @@ spec: capabilities: add: - SYS_PTRACE + # Bottlerocket (SELinux enforcing) gives each container a unique MCS + # category pair, which denies reading other containers' files via + # /proc//root (JVM hsperfdata, runtime binaries). Granting the + # full category range keeps the container_t type; it is a no-op where + # SELinux is not enforcing. + seLinuxOptions: + level: "s0:c0.c1023" image: "ttl.sh/zxporter-nodemon:latest" imagePullPolicy: IfNotPresent ports: @@ -434,6 +492,14 @@ spec: memory: 256Mi - name: dcgm-exporter securityContext: + # SYS_ADMIN alone is not enough: enumerating MIG Compute Instance + # profiles via NVML needs broader host access than that one + # capability grants (confirmed live -- SYS_ADMIN-only reproduces + # "CacheManager Init Failed. Error: -17" the instant any MIG + # instance exists on the node; switching to privileged fixes it + # with zero other changes). This is a generic NVML/driver + # requirement, not provider-specific, so unlike the hostPath/ + # LD_LIBRARY_PATH blocks below it must not be gated on gcp only. capabilities: add: - SYS_ADMIN @@ -461,6 +527,16 @@ spec: valueFrom: fieldRef: fieldPath: spec.nodeName + # Without this, nvidia-container-runtime's "auto" mode only exposes + # whole/non-MIG GPUs to a non-privileged container with no explicit + # device-plugin resource request (which this sidecar deliberately + # doesn't make, since it must see every GPU on the node, not just + # one). MIG-partitioned GPUs stay invisible without it -- confirmed + # by direct comparison against GPU Operator's own dcgm-exporter, + # which gets the same visibility via privileged:true instead; this + # is the minimal fix that doesn't need the broader grant. + - name: "NVIDIA_VISIBLE_DEVICES" + value: "all" - name: "LD_LIBRARY_PATH" value: "/usr/local/nvidia/lib64" - name: "DCGM_EXPORTER_KUBERNETES_GPU_ID_TYPE" @@ -473,3 +549,7 @@ spec: mountPath: /usr/local/nvidia - name: zxporter-nodemon-dcgm-metrics mountPath: "/etc/dcgm-exporter" + resources: + requests: + cpu: 50m + memory: 128Mi diff --git a/dist/nodemon-lowpriv.yaml b/dist/nodemon-lowpriv.yaml index 4be52d8a..8bc3f65e 100644 --- a/dist/nodemon-lowpriv.yaml +++ b/dist/nodemon-lowpriv.yaml @@ -6,16 +6,21 @@ metadata: name: zxporter-nodemon namespace: devzero-system labels: - helm.sh/chart: zxporter-nodemon-0.1.6 + helm.sh/chart: zxporter-nodemon-0.1.25 app.kubernetes.io/name: zxporter-nodemon app.kubernetes.io/instance: zxporter-nodemon - app.kubernetes.io/version: "0.1.6" + app.kubernetes.io/version: "0.1.25" app.kubernetes.io/managed-by: Helm + app.kubernetes.io/part-of: dz-platform + devzero.io/component: zxporter-nodemon --- # Source: zxporter-nodemon/templates/dcgm-exporter-configmap.yaml apiVersion: v1 kind: ConfigMap metadata: + labels: + app.kubernetes.io/part-of: dz-platform + devzero.io/component: zxporter-nodemon name: zxporter-nodemon-dcgm-metrics namespace: devzero-system data: @@ -31,8 +36,12 @@ data: DCGM_FI_DEV_GPU_UTIL, gauge, GPU utilization (in %). # DCGM_FI_PROF_SM_ACTIVE, gauge, The ratio of cycles an SM has at least 1 warp assigned # DCGM_FI_PROF_SM_OCCUPANCY, gauge, The fraction of resident warps on a multiprocessor - # DCGM_FI_PROF_GR_ENGINE_ACTIVE, gauge, Ratio of time the graphics engine is active (in %). - # DCGM_FI_PROF_DRAM_ACTIVE, gauge, The ratio of cycles the device memory interface is active sending or receiving data. + # Enabled: required for per-MIG-instance profiling data (GPU_I_PROFILE/GPU_I_ID + # labels only attach to DCGM_FI_PROF_* fields, never to plain DCGM_FI_DEV_* + # ones) and directly consumed by nodemon's GPUMigInstance parsing + # (GraphicsEngineActive/DRAMActive/TensorActive below). + DCGM_FI_PROF_GR_ENGINE_ACTIVE, gauge, Ratio of time the graphics engine is active (in %). + DCGM_FI_PROF_DRAM_ACTIVE, gauge, The ratio of cycles the device memory interface is active sending or receiving data. # Memory usage,, DCGM_FI_DEV_FB_FREE, gauge, Framebuffer memory free (in MiB). @@ -51,7 +60,9 @@ data: # DCGM_FI_PROF_PIPE_FP16_ACTIVE, gauge, Ratio of cycles the fp16 pipe is active. # DCGM_FI_PROF_PIPE_FP32_ACTIVE, gauge, Ratio of cycles the fp32 pipe is active. # DCGM_FI_PROF_PIPE_FP64_ACTIVE, gauge, Ratio of cycles the fp64 pipe is active. - # DCGM_FI_PROF_PIPE_TENSOR_ACTIVE, gauge, The ratio of cycles the tensor (HMMA) pipe is active (off the peak sustained elapsed cycles) + # Enabled: see the GR_ENGINE_ACTIVE/DRAM_ACTIVE note above -- this is + # GPUMigInstance.TensorActive. + DCGM_FI_PROF_PIPE_TENSOR_ACTIVE, gauge, The ratio of cycles the tensor (HMMA) pipe is active (off the peak sustained elapsed cycles) # Health,, # DCGM_FI_DEV_CLOCKS_EVENT_REASONS is not supported by DCGM 3.3.7 @@ -72,6 +83,9 @@ data: apiVersion: v1 kind: ConfigMap metadata: + labels: + app.kubernetes.io/part-of: dz-platform + devzero.io/component: zxporter-nodemon name: zxporter-nodemon-zxporter-nodemon namespace: devzero-system data: @@ -82,11 +96,13 @@ kind: ClusterRole metadata: name: zxporter-nodemon labels: - helm.sh/chart: zxporter-nodemon-0.1.6 + helm.sh/chart: zxporter-nodemon-0.1.25 app.kubernetes.io/name: zxporter-nodemon app.kubernetes.io/instance: zxporter-nodemon - app.kubernetes.io/version: "0.1.6" + app.kubernetes.io/version: "0.1.25" app.kubernetes.io/managed-by: Helm + app.kubernetes.io/part-of: dz-platform + devzero.io/component: zxporter-nodemon rules: - apiGroups: - "" @@ -95,6 +111,7 @@ rules: verbs: - get - list + - watch - apiGroups: - apps resources: @@ -132,11 +149,13 @@ kind: ClusterRoleBinding metadata: name: zxporter-nodemon labels: - helm.sh/chart: zxporter-nodemon-0.1.6 + helm.sh/chart: zxporter-nodemon-0.1.25 app.kubernetes.io/name: zxporter-nodemon app.kubernetes.io/instance: zxporter-nodemon - app.kubernetes.io/version: "0.1.6" + app.kubernetes.io/version: "0.1.25" app.kubernetes.io/managed-by: Helm + app.kubernetes.io/part-of: dz-platform + devzero.io/component: zxporter-nodemon roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole @@ -153,10 +172,12 @@ metadata: name: zxporter-nodemon namespace: devzero-system labels: - helm.sh/chart: zxporter-nodemon-0.1.6 + app.kubernetes.io/part-of: dz-platform + devzero.io/component: zxporter-nodemon + helm.sh/chart: zxporter-nodemon-0.1.25 app.kubernetes.io/name: zxporter-nodemon app.kubernetes.io/instance: zxporter-nodemon - app.kubernetes.io/version: "0.1.6" + app.kubernetes.io/version: "0.1.25" app.kubernetes.io/managed-by: Helm annotations: ignore-check.kube-linter.io/privileged-container: "This daemon set needs to run DCGM Exporter as privileged to access the GPU metrics." @@ -260,6 +281,14 @@ spec: memory: 256Mi - name: dcgm-exporter securityContext: + # SYS_ADMIN alone is not enough: enumerating MIG Compute Instance + # profiles via NVML needs broader host access than that one + # capability grants (confirmed live -- SYS_ADMIN-only reproduces + # "CacheManager Init Failed. Error: -17" the instant any MIG + # instance exists on the node; switching to privileged fixes it + # with zero other changes). This is a generic NVML/driver + # requirement, not provider-specific, so unlike the hostPath/ + # LD_LIBRARY_PATH blocks below it must not be gated on gcp only. capabilities: add: - SYS_ADMIN @@ -267,6 +296,7 @@ spec: - NET_RAW runAsNonRoot: false runAsUser: 0 + privileged: true image: "public.ecr.aws/devzeroinc/dcgm-exporter:4.5.2-4.8.1-ubuntu22.04" imagePullPolicy: IfNotPresent command: [ "/bin/bash", "-c" ] @@ -286,12 +316,26 @@ spec: valueFrom: fieldRef: fieldPath: spec.nodeName + # Without this, nvidia-container-runtime's "auto" mode only exposes + # whole/non-MIG GPUs to a non-privileged container with no explicit + # device-plugin resource request (which this sidecar deliberately + # doesn't make, since it must see every GPU on the node, not just + # one). MIG-partitioned GPUs stay invisible without it -- confirmed + # by direct comparison against GPU Operator's own dcgm-exporter, + # which gets the same visibility via privileged:true instead; this + # is the minimal fix that doesn't need the broader grant. + - name: "NVIDIA_VISIBLE_DEVICES" + value: "all" volumeMounts: - name: "pod-gpu-resources" readOnly: true mountPath: "/var/lib/kubelet/pod-resources" - name: zxporter-nodemon-dcgm-metrics mountPath: "/etc/dcgm-exporter" + resources: + requests: + cpu: 50m + memory: 128Mi --- # Source: zxporter-nodemon/templates/daemonset.yaml apiVersion: apps/v1 @@ -300,10 +344,12 @@ metadata: name: zxporter-nodemon-gpu namespace: devzero-system labels: - helm.sh/chart: zxporter-nodemon-0.1.6 + app.kubernetes.io/part-of: dz-platform + devzero.io/component: zxporter-nodemon + helm.sh/chart: zxporter-nodemon-0.1.25 app.kubernetes.io/name: zxporter-nodemon app.kubernetes.io/instance: zxporter-nodemon-gpu - app.kubernetes.io/version: "0.1.6" + app.kubernetes.io/version: "0.1.25" app.kubernetes.io/managed-by: Helm annotations: ignore-check.kube-linter.io/privileged-container: "This daemon set needs to run DCGM Exporter as privileged to access the GPU metrics." @@ -410,6 +456,14 @@ spec: memory: 256Mi - name: dcgm-exporter securityContext: + # SYS_ADMIN alone is not enough: enumerating MIG Compute Instance + # profiles via NVML needs broader host access than that one + # capability grants (confirmed live -- SYS_ADMIN-only reproduces + # "CacheManager Init Failed. Error: -17" the instant any MIG + # instance exists on the node; switching to privileged fixes it + # with zero other changes). This is a generic NVML/driver + # requirement, not provider-specific, so unlike the hostPath/ + # LD_LIBRARY_PATH blocks below it must not be gated on gcp only. capabilities: add: - SYS_ADMIN @@ -417,6 +471,7 @@ spec: - NET_RAW runAsNonRoot: false runAsUser: 0 + privileged: true image: "public.ecr.aws/devzeroinc/dcgm-exporter:4.5.2-4.8.1-ubuntu22.04" imagePullPolicy: IfNotPresent command: [ "/bin/bash", "-c" ] @@ -436,9 +491,23 @@ spec: valueFrom: fieldRef: fieldPath: spec.nodeName + # Without this, nvidia-container-runtime's "auto" mode only exposes + # whole/non-MIG GPUs to a non-privileged container with no explicit + # device-plugin resource request (which this sidecar deliberately + # doesn't make, since it must see every GPU on the node, not just + # one). MIG-partitioned GPUs stay invisible without it -- confirmed + # by direct comparison against GPU Operator's own dcgm-exporter, + # which gets the same visibility via privileged:true instead; this + # is the minimal fix that doesn't need the broader grant. + - name: "NVIDIA_VISIBLE_DEVICES" + value: "all" volumeMounts: - name: "pod-gpu-resources" readOnly: true mountPath: "/var/lib/kubelet/pod-resources" - name: zxporter-nodemon-dcgm-metrics mountPath: "/etc/dcgm-exporter" + resources: + requests: + cpu: 50m + memory: 128Mi diff --git a/dist/nodemon.yaml b/dist/nodemon.yaml index 3b554299..f8c0d052 100644 --- a/dist/nodemon.yaml +++ b/dist/nodemon.yaml @@ -6,16 +6,21 @@ metadata: name: zxporter-nodemon namespace: devzero-system labels: - helm.sh/chart: zxporter-nodemon-0.1.6 + helm.sh/chart: zxporter-nodemon-0.1.25 app.kubernetes.io/name: zxporter-nodemon app.kubernetes.io/instance: zxporter-nodemon - app.kubernetes.io/version: "0.1.6" + app.kubernetes.io/version: "0.1.25" app.kubernetes.io/managed-by: Helm + app.kubernetes.io/part-of: dz-platform + devzero.io/component: zxporter-nodemon --- # Source: zxporter-nodemon/templates/dcgm-exporter-configmap.yaml apiVersion: v1 kind: ConfigMap metadata: + labels: + app.kubernetes.io/part-of: dz-platform + devzero.io/component: zxporter-nodemon name: zxporter-nodemon-dcgm-metrics namespace: devzero-system data: @@ -31,8 +36,12 @@ data: DCGM_FI_DEV_GPU_UTIL, gauge, GPU utilization (in %). # DCGM_FI_PROF_SM_ACTIVE, gauge, The ratio of cycles an SM has at least 1 warp assigned # DCGM_FI_PROF_SM_OCCUPANCY, gauge, The fraction of resident warps on a multiprocessor - # DCGM_FI_PROF_GR_ENGINE_ACTIVE, gauge, Ratio of time the graphics engine is active (in %). - # DCGM_FI_PROF_DRAM_ACTIVE, gauge, The ratio of cycles the device memory interface is active sending or receiving data. + # Enabled: required for per-MIG-instance profiling data (GPU_I_PROFILE/GPU_I_ID + # labels only attach to DCGM_FI_PROF_* fields, never to plain DCGM_FI_DEV_* + # ones) and directly consumed by nodemon's GPUMigInstance parsing + # (GraphicsEngineActive/DRAMActive/TensorActive below). + DCGM_FI_PROF_GR_ENGINE_ACTIVE, gauge, Ratio of time the graphics engine is active (in %). + DCGM_FI_PROF_DRAM_ACTIVE, gauge, The ratio of cycles the device memory interface is active sending or receiving data. # Memory usage,, DCGM_FI_DEV_FB_FREE, gauge, Framebuffer memory free (in MiB). @@ -51,7 +60,9 @@ data: # DCGM_FI_PROF_PIPE_FP16_ACTIVE, gauge, Ratio of cycles the fp16 pipe is active. # DCGM_FI_PROF_PIPE_FP32_ACTIVE, gauge, Ratio of cycles the fp32 pipe is active. # DCGM_FI_PROF_PIPE_FP64_ACTIVE, gauge, Ratio of cycles the fp64 pipe is active. - # DCGM_FI_PROF_PIPE_TENSOR_ACTIVE, gauge, The ratio of cycles the tensor (HMMA) pipe is active (off the peak sustained elapsed cycles) + # Enabled: see the GR_ENGINE_ACTIVE/DRAM_ACTIVE note above -- this is + # GPUMigInstance.TensorActive. + DCGM_FI_PROF_PIPE_TENSOR_ACTIVE, gauge, The ratio of cycles the tensor (HMMA) pipe is active (off the peak sustained elapsed cycles) # Health,, # DCGM_FI_DEV_CLOCKS_EVENT_REASONS is not supported by DCGM 3.3.7 @@ -72,6 +83,9 @@ data: apiVersion: v1 kind: ConfigMap metadata: + labels: + app.kubernetes.io/part-of: dz-platform + devzero.io/component: zxporter-nodemon name: zxporter-nodemon-zxporter-nodemon namespace: devzero-system data: @@ -82,11 +96,13 @@ kind: ClusterRole metadata: name: zxporter-nodemon labels: - helm.sh/chart: zxporter-nodemon-0.1.6 + helm.sh/chart: zxporter-nodemon-0.1.25 app.kubernetes.io/name: zxporter-nodemon app.kubernetes.io/instance: zxporter-nodemon - app.kubernetes.io/version: "0.1.6" + app.kubernetes.io/version: "0.1.25" app.kubernetes.io/managed-by: Helm + app.kubernetes.io/part-of: dz-platform + devzero.io/component: zxporter-nodemon rules: - apiGroups: - "" @@ -133,11 +149,13 @@ kind: ClusterRoleBinding metadata: name: zxporter-nodemon labels: - helm.sh/chart: zxporter-nodemon-0.1.6 + helm.sh/chart: zxporter-nodemon-0.1.25 app.kubernetes.io/name: zxporter-nodemon app.kubernetes.io/instance: zxporter-nodemon - app.kubernetes.io/version: "0.1.6" + app.kubernetes.io/version: "0.1.25" app.kubernetes.io/managed-by: Helm + app.kubernetes.io/part-of: dz-platform + devzero.io/component: zxporter-nodemon roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole @@ -154,10 +172,12 @@ metadata: name: zxporter-nodemon namespace: devzero-system labels: - helm.sh/chart: zxporter-nodemon-0.1.6 + app.kubernetes.io/part-of: dz-platform + devzero.io/component: zxporter-nodemon + helm.sh/chart: zxporter-nodemon-0.1.25 app.kubernetes.io/name: zxporter-nodemon app.kubernetes.io/instance: zxporter-nodemon - app.kubernetes.io/version: "0.1.6" + app.kubernetes.io/version: "0.1.25" app.kubernetes.io/managed-by: Helm annotations: ignore-check.kube-linter.io/privileged-container: "This daemon set needs to run DCGM Exporter as privileged to access the GPU metrics." @@ -213,6 +233,13 @@ spec: capabilities: add: - SYS_PTRACE + # Bottlerocket (SELinux enforcing) gives each container a unique MCS + # category pair, which denies reading other containers' files via + # /proc//root (JVM hsperfdata, runtime binaries). Granting the + # full category range keeps the container_t type; it is a no-op where + # SELinux is not enforcing. + seLinuxOptions: + level: "s0:c0.c1023" image: "ttl.sh/zxporter-nodemon:latest" imagePullPolicy: IfNotPresent ports: @@ -266,6 +293,14 @@ spec: memory: 256Mi - name: dcgm-exporter securityContext: + # SYS_ADMIN alone is not enough: enumerating MIG Compute Instance + # profiles via NVML needs broader host access than that one + # capability grants (confirmed live -- SYS_ADMIN-only reproduces + # "CacheManager Init Failed. Error: -17" the instant any MIG + # instance exists on the node; switching to privileged fixes it + # with zero other changes). This is a generic NVML/driver + # requirement, not provider-specific, so unlike the hostPath/ + # LD_LIBRARY_PATH blocks below it must not be gated on gcp only. capabilities: add: - SYS_ADMIN @@ -273,6 +308,7 @@ spec: - NET_RAW runAsNonRoot: false runAsUser: 0 + privileged: true image: "public.ecr.aws/devzeroinc/dcgm-exporter:4.5.2-4.8.1-ubuntu22.04" imagePullPolicy: IfNotPresent command: [ "/bin/bash", "-c" ] @@ -292,12 +328,26 @@ spec: valueFrom: fieldRef: fieldPath: spec.nodeName + # Without this, nvidia-container-runtime's "auto" mode only exposes + # whole/non-MIG GPUs to a non-privileged container with no explicit + # device-plugin resource request (which this sidecar deliberately + # doesn't make, since it must see every GPU on the node, not just + # one). MIG-partitioned GPUs stay invisible without it -- confirmed + # by direct comparison against GPU Operator's own dcgm-exporter, + # which gets the same visibility via privileged:true instead; this + # is the minimal fix that doesn't need the broader grant. + - name: "NVIDIA_VISIBLE_DEVICES" + value: "all" volumeMounts: - name: "pod-gpu-resources" readOnly: true mountPath: "/var/lib/kubelet/pod-resources" - name: zxporter-nodemon-dcgm-metrics mountPath: "/etc/dcgm-exporter" + resources: + requests: + cpu: 50m + memory: 128Mi --- # Source: zxporter-nodemon/templates/daemonset.yaml apiVersion: apps/v1 @@ -306,10 +356,12 @@ metadata: name: zxporter-nodemon-gpu namespace: devzero-system labels: - helm.sh/chart: zxporter-nodemon-0.1.6 + app.kubernetes.io/part-of: dz-platform + devzero.io/component: zxporter-nodemon + helm.sh/chart: zxporter-nodemon-0.1.25 app.kubernetes.io/name: zxporter-nodemon app.kubernetes.io/instance: zxporter-nodemon-gpu - app.kubernetes.io/version: "0.1.6" + app.kubernetes.io/version: "0.1.25" app.kubernetes.io/managed-by: Helm annotations: ignore-check.kube-linter.io/privileged-container: "This daemon set needs to run DCGM Exporter as privileged to access the GPU metrics." @@ -368,6 +420,13 @@ spec: capabilities: add: - SYS_PTRACE + # Bottlerocket (SELinux enforcing) gives each container a unique MCS + # category pair, which denies reading other containers' files via + # /proc//root (JVM hsperfdata, runtime binaries). Granting the + # full category range keeps the container_t type; it is a no-op where + # SELinux is not enforcing. + seLinuxOptions: + level: "s0:c0.c1023" image: "ttl.sh/zxporter-nodemon:latest" imagePullPolicy: IfNotPresent ports: @@ -421,6 +480,14 @@ spec: memory: 256Mi - name: dcgm-exporter securityContext: + # SYS_ADMIN alone is not enough: enumerating MIG Compute Instance + # profiles via NVML needs broader host access than that one + # capability grants (confirmed live -- SYS_ADMIN-only reproduces + # "CacheManager Init Failed. Error: -17" the instant any MIG + # instance exists on the node; switching to privileged fixes it + # with zero other changes). This is a generic NVML/driver + # requirement, not provider-specific, so unlike the hostPath/ + # LD_LIBRARY_PATH blocks below it must not be gated on gcp only. capabilities: add: - SYS_ADMIN @@ -428,6 +495,7 @@ spec: - NET_RAW runAsNonRoot: false runAsUser: 0 + privileged: true image: "public.ecr.aws/devzeroinc/dcgm-exporter:4.5.2-4.8.1-ubuntu22.04" imagePullPolicy: IfNotPresent command: [ "/bin/bash", "-c" ] @@ -447,9 +515,23 @@ spec: valueFrom: fieldRef: fieldPath: spec.nodeName + # Without this, nvidia-container-runtime's "auto" mode only exposes + # whole/non-MIG GPUs to a non-privileged container with no explicit + # device-plugin resource request (which this sidecar deliberately + # doesn't make, since it must see every GPU on the node, not just + # one). MIG-partitioned GPUs stay invisible without it -- confirmed + # by direct comparison against GPU Operator's own dcgm-exporter, + # which gets the same visibility via privileged:true instead; this + # is the minimal fix that doesn't need the broader grant. + - name: "NVIDIA_VISIBLE_DEVICES" + value: "all" volumeMounts: - name: "pod-gpu-resources" readOnly: true mountPath: "/var/lib/kubelet/pod-resources" - name: zxporter-nodemon-dcgm-metrics mountPath: "/etc/dcgm-exporter" + resources: + requests: + cpu: 50m + memory: 128Mi diff --git a/dist/zxporter.yaml b/dist/zxporter.yaml index 3d5fde53..cb9dda6b 100644 --- a/dist/zxporter.yaml +++ b/dist/zxporter.yaml @@ -278,6 +278,8 @@ rules: - apiGroups: - argoproj.io resources: + - applications + - applicationsets - rollouts verbs: - get @@ -325,6 +327,14 @@ rules: - get - list - watch +- apiGroups: + - coordination.k8s.io + resourceNames: + - dz-scheduler + resources: + - leases + verbs: + - get - apiGroups: - dakr.devzero.io resources: @@ -376,6 +386,45 @@ rules: - get - list - watch +- apiGroups: + - flagger.app + resources: + - alertproviders + - canaries + - metrictemplates + verbs: + - get + - list + - watch +- apiGroups: + - fluxcd.controlplane.io + resources: + - fluxinstances + - fluxreports + - resourcesetinputproviders + - resourcesets + verbs: + - get + - list + - watch +- apiGroups: + - helm.toolkit.fluxcd.io + resources: + - helmreleases + verbs: + - get + - list + - watch +- apiGroups: + - image.toolkit.fluxcd.io + resources: + - imagepolicies + - imagerepositories + - imageupdateautomations + verbs: + - get + - list + - watch - apiGroups: - karpenter.azure.com resources: @@ -440,6 +489,14 @@ rules: - get - list - watch +- apiGroups: + - kustomize.toolkit.fluxcd.io + resources: + - kustomizations + verbs: + - get + - list + - watch - apiGroups: - kyverno.io resources: @@ -468,6 +525,16 @@ rules: - get - list - watch +- apiGroups: + - notification.toolkit.fluxcd.io + resources: + - alerts + - providers + - receivers + verbs: + - get + - list + - watch - apiGroups: - policy resources: @@ -505,6 +572,19 @@ rules: - get - list - watch +- apiGroups: + - source.toolkit.fluxcd.io + resources: + - buckets + - externalartifacts + - gitrepositories + - helmcharts + - helmrepositories + - ocirepositories + verbs: + - get + - list + - watch - apiGroups: - sparkoperator.k8s.io resources: @@ -645,6 +725,19 @@ subjects: name: devzero-zxporter-controller-manager namespace: devzero-system --- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: devzero-zxporter-metrics-reader-rolebinding +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: devzero-zxporter-metrics-reader +subjects: +- kind: ServiceAccount + name: devzero-zxporter-controller-manager + namespace: devzero-system +--- apiVersion: v1 data: BUFFER_SIZE: "" @@ -684,6 +777,7 @@ data: EXCLUDED_STORAGECLASSES: "" EXCLUDED_VPAS: "" K8S_PROVIDER: '{{ .k8s_provider }}' + KEDA_METRICS_ENDPOINT: "" KUBE_CONTEXT_NAME: '{{ .kube_context_name }}' KUBELET_FALLBACK_TIMEOUT: 15s MASK_SECRET_DATA: "" @@ -692,6 +786,7 @@ data: NODE_METRICS_CONCURRENCY: "20" NODE_METRICS_INTERVAL: "" NODEMON_REQUEST_TIMEOUT: 15s + SCHEDULER_METRICS_SCRAPE_INTERVAL: "" TARGET_NAMESPACES: "" TOKEN_CONFIGMAP_NAME: devzero-zxporter-env-config TOKEN_CREDENTIALS_SECRET_NAME: devzero-zxporter-credentials @@ -802,6 +897,8 @@ spec: key: CLUSTER_TOKEN name: devzero-zxporter-token optional: true + - name: CLUSTER_TOKEN_SOURCE + value: mirror image: ttl.sh/zxporter:latest livenessProbe: httpGet: diff --git a/docs/nodemon.md b/docs/nodemon.md index b29c7b18..da6a2e4b 100644 --- a/docs/nodemon.md +++ b/docs/nodemon.md @@ -15,6 +15,9 @@ The Nodemon scrapes metrics from NVIDIA DCGM (Data Center GPU Manager) exporters ## Quick Start +From a source checkout, run `make helm-deps` first: the chart vendors the +shared `devzero-common` library into its uncommitted `charts/` directory. + ### Deploy with Embedded DCGM Exporter (Recommended) ```bash diff --git a/docs/rbac.md b/docs/rbac.md index 2658d7e8..95d3a3de 100644 --- a/docs/rbac.md +++ b/docs/rbac.md @@ -72,8 +72,17 @@ The ZXporter Operator is a comprehensive Kubernetes monitoring and data collecti | `keda.sh` | `triggerauthentications` | ✓ | ✓ | ✓ | | | | | KEDA installed | | `keda.sh` | `clustertriggerauthentications` | ✓ | ✓ | ✓ | | | | | KEDA installed | | `argoproj.io` | `rollouts` | ✓ | ✓ | ✓ | | | | | Argo Rollouts installed | +| `argoproj.io` | `applications` | ✓ | ✓ | ✓ | | | | | Argo CD installed | +| `argoproj.io` | `applicationsets` | ✓ | ✓ | ✓ | | | | | Argo CD installed | | `datadoghq.com` | `extendeddaemonsetreplicasets` | ✓ | ✓ | ✓ | | | | | Datadog operator installed | | `kubeflow.org` | `notebooks` | ✓ | ✓ | ✓ | | | | | Kubeflow installed | +| `source.toolkit.fluxcd.io` | `gitrepositories`, `ocirepositories`, `helmrepositories`, `helmcharts`, `buckets`, `externalartifacts` | ✓ | ✓ | ✓ | | | | | Flux installed | +| `kustomize.toolkit.fluxcd.io` | `kustomizations` | ✓ | ✓ | ✓ | | | | | Flux installed | +| `helm.toolkit.fluxcd.io` | `helmreleases` | ✓ | ✓ | ✓ | | | | | Flux installed | +| `notification.toolkit.fluxcd.io` | `alerts`, `providers`, `receivers` | ✓ | ✓ | ✓ | | | | | Flux installed | +| `fluxcd.controlplane.io` | `fluxinstances`, `fluxreports`, `resourcesetinputproviders`, `resourcesets` | ✓ | ✓ | ✓ | | | | | Flux Operator installed | +| `image.toolkit.fluxcd.io` | `imagerepositories`, `imagepolicies`, `imageupdateautomations` | ✓ | ✓ | ✓ | | | | | Flux image automation controllers installed (optional add-on) | +| `flagger.app` | `canaries`, `metrictemplates`, `alertproviders` | ✓ | ✓ | ✓ | | | | | Flagger installed | ### Leader Election Role | API Group | Resource | get | list | watch | create | update | patch | delete | @@ -238,6 +247,23 @@ metadata: - **Code**: `internal/collector/argo_rollouts_collector.go` - **Condition**: Only when Argo Rollouts CRDs detected +#### Argo CD Integration +**Purpose**: GitOps inventory -- which Applications exist, what they sync from, +and what they manage + +Argo CD and Argo Rollouts are separate projects that share the `argoproj.io` +group, so these rules are additive to the `rollouts` rule above. + +- **`applications`** (`get`, `list`, `watch`) + - Tracks sync and health status, source coordinates, sync policy, and the + resources each Application manages + - **Code**: `internal/collector/argo_cd_collector.go` + - **Condition**: Only when Argo CD CRDs detected +- **`applicationsets`** (`get`, `list`, `watch`) + - Tracks generators, templates, and generated-Application counts + - **Code**: `internal/collector/argo_cd_collector.go` + - **Condition**: Only when Argo CD CRDs detected + #### Datadog Integration **Purpose**: Datadog operator monitoring @@ -316,6 +342,8 @@ spec: - "karpenter" - "keda_scaled_job" - "argo_rollouts" + - "argo_cd_application" + - "argo_cd_application_set" - "datadog" - "kubeflow_notebook" ``` diff --git a/gen/api/v1/apiv1connect/cluster.connect.go b/gen/api/v1/apiv1connect/cluster.connect.go index 0e5dd21e..6d34e534 100644 --- a/gen/api/v1/apiv1connect/cluster.connect.go +++ b/gen/api/v1/apiv1connect/cluster.connect.go @@ -63,6 +63,15 @@ const ( // ClusterServiceGetClusterInfoByNameProcedure is the fully-qualified name of the ClusterService's // GetClusterInfoByName RPC. ClusterServiceGetClusterInfoByNameProcedure = "/api.v1.ClusterService/GetClusterInfoByName" + // ClusterServiceGetClustersCurrentStateProcedure is the fully-qualified name of the + // ClusterService's GetClustersCurrentState RPC. + ClusterServiceGetClustersCurrentStateProcedure = "/api.v1.ClusterService/GetClustersCurrentState" + // ClusterServiceGetTeamTimeSeriesProcedure is the fully-qualified name of the ClusterService's + // GetTeamTimeSeries RPC. + ClusterServiceGetTeamTimeSeriesProcedure = "/api.v1.ClusterService/GetTeamTimeSeries" + // ClusterServiceGetClustersPeriodDeltasProcedure is the fully-qualified name of the + // ClusterService's GetClustersPeriodDeltas RPC. + ClusterServiceGetClustersPeriodDeltasProcedure = "/api.v1.ClusterService/GetClustersPeriodDeltas" ) // ClusterServiceClient is a client for the api.v1.ClusterService service. @@ -87,6 +96,12 @@ type ClusterServiceClient interface { GetClusterIDByName(context.Context, *connect.Request[v1.GetClusterIDByNameRequest]) (*connect.Response[v1.GetClusterIDByNameResponse], error) // GetClusterInfoByName retrieves full info of the first active cluster matching the given team and name GetClusterInfoByName(context.Context, *connect.Request[v1.GetClusterInfoByNameRequest]) (*connect.Response[v1.GetClusterInfoByNameResponse], error) + // GetClustersCurrentState returns identity plus the latest observed resource snapshot per + // cluster. Nothing in the response depends on a time range. + GetClustersCurrentState(context.Context, *connect.Request[v1.GetClustersCurrentStateRequest]) (*connect.Response[v1.GetClustersCurrentStateResponse], error) + // GetTeamTimeSeries returns fleet-level points over [start_time, end_time). + GetTeamTimeSeries(context.Context, *connect.Request[v1.GetTeamTimeSeriesRequest]) (*connect.Response[v1.GetTeamTimeSeriesResponse], error) + GetClustersPeriodDeltas(context.Context, *connect.Request[v1.GetClustersPeriodDeltasRequest]) (*connect.Response[v1.GetClustersPeriodDeltasResponse], error) } // NewClusterServiceClient constructs a client for the api.v1.ClusterService service. By default, it @@ -149,6 +164,21 @@ func NewClusterServiceClient(httpClient connect.HTTPClient, baseURL string, opts baseURL+ClusterServiceGetClusterInfoByNameProcedure, opts..., ), + getClustersCurrentState: connect.NewClient[v1.GetClustersCurrentStateRequest, v1.GetClustersCurrentStateResponse]( + httpClient, + baseURL+ClusterServiceGetClustersCurrentStateProcedure, + opts..., + ), + getTeamTimeSeries: connect.NewClient[v1.GetTeamTimeSeriesRequest, v1.GetTeamTimeSeriesResponse]( + httpClient, + baseURL+ClusterServiceGetTeamTimeSeriesProcedure, + opts..., + ), + getClustersPeriodDeltas: connect.NewClient[v1.GetClustersPeriodDeltasRequest, v1.GetClustersPeriodDeltasResponse]( + httpClient, + baseURL+ClusterServiceGetClustersPeriodDeltasProcedure, + opts..., + ), } } @@ -164,6 +194,9 @@ type clusterServiceClient struct { getNodeTypeCounts *connect.Client[v1.GetNodeTypeCountsRequest, v1.GetNodeTypeCountsResponse] getClusterIDByName *connect.Client[v1.GetClusterIDByNameRequest, v1.GetClusterIDByNameResponse] getClusterInfoByName *connect.Client[v1.GetClusterInfoByNameRequest, v1.GetClusterInfoByNameResponse] + getClustersCurrentState *connect.Client[v1.GetClustersCurrentStateRequest, v1.GetClustersCurrentStateResponse] + getTeamTimeSeries *connect.Client[v1.GetTeamTimeSeriesRequest, v1.GetTeamTimeSeriesResponse] + getClustersPeriodDeltas *connect.Client[v1.GetClustersPeriodDeltasRequest, v1.GetClustersPeriodDeltasResponse] } // GetClustersBasicInfo calls api.v1.ClusterService.GetClustersBasicInfo. @@ -216,6 +249,21 @@ func (c *clusterServiceClient) GetClusterInfoByName(ctx context.Context, req *co return c.getClusterInfoByName.CallUnary(ctx, req) } +// GetClustersCurrentState calls api.v1.ClusterService.GetClustersCurrentState. +func (c *clusterServiceClient) GetClustersCurrentState(ctx context.Context, req *connect.Request[v1.GetClustersCurrentStateRequest]) (*connect.Response[v1.GetClustersCurrentStateResponse], error) { + return c.getClustersCurrentState.CallUnary(ctx, req) +} + +// GetTeamTimeSeries calls api.v1.ClusterService.GetTeamTimeSeries. +func (c *clusterServiceClient) GetTeamTimeSeries(ctx context.Context, req *connect.Request[v1.GetTeamTimeSeriesRequest]) (*connect.Response[v1.GetTeamTimeSeriesResponse], error) { + return c.getTeamTimeSeries.CallUnary(ctx, req) +} + +// GetClustersPeriodDeltas calls api.v1.ClusterService.GetClustersPeriodDeltas. +func (c *clusterServiceClient) GetClustersPeriodDeltas(ctx context.Context, req *connect.Request[v1.GetClustersPeriodDeltasRequest]) (*connect.Response[v1.GetClustersPeriodDeltasResponse], error) { + return c.getClustersPeriodDeltas.CallUnary(ctx, req) +} + // ClusterServiceHandler is an implementation of the api.v1.ClusterService service. type ClusterServiceHandler interface { // GetClustersBasicInfo retrieves basic information for all clusters in a team @@ -238,6 +286,12 @@ type ClusterServiceHandler interface { GetClusterIDByName(context.Context, *connect.Request[v1.GetClusterIDByNameRequest]) (*connect.Response[v1.GetClusterIDByNameResponse], error) // GetClusterInfoByName retrieves full info of the first active cluster matching the given team and name GetClusterInfoByName(context.Context, *connect.Request[v1.GetClusterInfoByNameRequest]) (*connect.Response[v1.GetClusterInfoByNameResponse], error) + // GetClustersCurrentState returns identity plus the latest observed resource snapshot per + // cluster. Nothing in the response depends on a time range. + GetClustersCurrentState(context.Context, *connect.Request[v1.GetClustersCurrentStateRequest]) (*connect.Response[v1.GetClustersCurrentStateResponse], error) + // GetTeamTimeSeries returns fleet-level points over [start_time, end_time). + GetTeamTimeSeries(context.Context, *connect.Request[v1.GetTeamTimeSeriesRequest]) (*connect.Response[v1.GetTeamTimeSeriesResponse], error) + GetClustersPeriodDeltas(context.Context, *connect.Request[v1.GetClustersPeriodDeltasRequest]) (*connect.Response[v1.GetClustersPeriodDeltasResponse], error) } // NewClusterServiceHandler builds an HTTP handler from the service implementation. It returns the @@ -296,6 +350,21 @@ func NewClusterServiceHandler(svc ClusterServiceHandler, opts ...connect.Handler svc.GetClusterInfoByName, opts..., ) + clusterServiceGetClustersCurrentStateHandler := connect.NewUnaryHandler( + ClusterServiceGetClustersCurrentStateProcedure, + svc.GetClustersCurrentState, + opts..., + ) + clusterServiceGetTeamTimeSeriesHandler := connect.NewUnaryHandler( + ClusterServiceGetTeamTimeSeriesProcedure, + svc.GetTeamTimeSeries, + opts..., + ) + clusterServiceGetClustersPeriodDeltasHandler := connect.NewUnaryHandler( + ClusterServiceGetClustersPeriodDeltasProcedure, + svc.GetClustersPeriodDeltas, + opts..., + ) return "/api.v1.ClusterService/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { switch r.URL.Path { case ClusterServiceGetClustersBasicInfoProcedure: @@ -318,6 +387,12 @@ func NewClusterServiceHandler(svc ClusterServiceHandler, opts ...connect.Handler clusterServiceGetClusterIDByNameHandler.ServeHTTP(w, r) case ClusterServiceGetClusterInfoByNameProcedure: clusterServiceGetClusterInfoByNameHandler.ServeHTTP(w, r) + case ClusterServiceGetClustersCurrentStateProcedure: + clusterServiceGetClustersCurrentStateHandler.ServeHTTP(w, r) + case ClusterServiceGetTeamTimeSeriesProcedure: + clusterServiceGetTeamTimeSeriesHandler.ServeHTTP(w, r) + case ClusterServiceGetClustersPeriodDeltasProcedure: + clusterServiceGetClustersPeriodDeltasHandler.ServeHTTP(w, r) default: http.NotFound(w, r) } @@ -366,3 +441,15 @@ func (UnimplementedClusterServiceHandler) GetClusterIDByName(context.Context, *c func (UnimplementedClusterServiceHandler) GetClusterInfoByName(context.Context, *connect.Request[v1.GetClusterInfoByNameRequest]) (*connect.Response[v1.GetClusterInfoByNameResponse], error) { return nil, connect.NewError(connect.CodeUnimplemented, errors.New("api.v1.ClusterService.GetClusterInfoByName is not implemented")) } + +func (UnimplementedClusterServiceHandler) GetClustersCurrentState(context.Context, *connect.Request[v1.GetClustersCurrentStateRequest]) (*connect.Response[v1.GetClustersCurrentStateResponse], error) { + return nil, connect.NewError(connect.CodeUnimplemented, errors.New("api.v1.ClusterService.GetClustersCurrentState is not implemented")) +} + +func (UnimplementedClusterServiceHandler) GetTeamTimeSeries(context.Context, *connect.Request[v1.GetTeamTimeSeriesRequest]) (*connect.Response[v1.GetTeamTimeSeriesResponse], error) { + return nil, connect.NewError(connect.CodeUnimplemented, errors.New("api.v1.ClusterService.GetTeamTimeSeries is not implemented")) +} + +func (UnimplementedClusterServiceHandler) GetClustersPeriodDeltas(context.Context, *connect.Request[v1.GetClustersPeriodDeltasRequest]) (*connect.Response[v1.GetClustersPeriodDeltasResponse], error) { + return nil, connect.NewError(connect.CodeUnimplemented, errors.New("api.v1.ClusterService.GetClustersPeriodDeltas is not implemented")) +} diff --git a/gen/api/v1/apiv1connect/k8s.connect.go b/gen/api/v1/apiv1connect/k8s.connect.go index e3a3d541..f0ef5e90 100644 --- a/gen/api/v1/apiv1connect/k8s.connect.go +++ b/gen/api/v1/apiv1connect/k8s.connect.go @@ -79,10 +79,28 @@ const ( // K8SServiceGetNodeGroupsUtilizationProcedure is the fully-qualified name of the K8SService's // GetNodeGroupsUtilization RPC. K8SServiceGetNodeGroupsUtilizationProcedure = "/api.v1.K8SService/GetNodeGroupsUtilization" + // K8SServiceGetClusterNodeInventoryProcedure is the fully-qualified name of the K8SService's + // GetClusterNodeInventory RPC. + K8SServiceGetClusterNodeInventoryProcedure = "/api.v1.K8SService/GetClusterNodeInventory" + // K8SServiceGetClusterNodeFilterIndexProcedure is the fully-qualified name of the K8SService's + // GetClusterNodeFilterIndex RPC. + K8SServiceGetClusterNodeFilterIndexProcedure = "/api.v1.K8SService/GetClusterNodeFilterIndex" // K8SServiceGetNodeGroupProcedure is the fully-qualified name of the K8SService's GetNodeGroup RPC. K8SServiceGetNodeGroupProcedure = "/api.v1.K8SService/GetNodeGroup" // K8SServiceGetNodeProcedure is the fully-qualified name of the K8SService's GetNode RPC. K8SServiceGetNodeProcedure = "/api.v1.K8SService/GetNode" + // K8SServiceRequestNodeClaimDeletionProcedure is the fully-qualified name of the K8SService's + // RequestNodeClaimDeletion RPC. + K8SServiceRequestNodeClaimDeletionProcedure = "/api.v1.K8SService/RequestNodeClaimDeletion" + // K8SServiceGetNodeClaimDeletionStatusProcedure is the fully-qualified name of the K8SService's + // GetNodeClaimDeletionStatus RPC. + K8SServiceGetNodeClaimDeletionStatusProcedure = "/api.v1.K8SService/GetNodeClaimDeletionStatus" + // K8SServiceGetNodeClaimRefsProcedure is the fully-qualified name of the K8SService's + // GetNodeClaimRefs RPC. + K8SServiceGetNodeClaimRefsProcedure = "/api.v1.K8SService/GetNodeClaimRefs" + // K8SServiceRequestNodeClaimDeletionsProcedure is the fully-qualified name of the K8SService's + // RequestNodeClaimDeletions RPC. + K8SServiceRequestNodeClaimDeletionsProcedure = "/api.v1.K8SService/RequestNodeClaimDeletions" // K8SServiceGetWorkloadsProcedure is the fully-qualified name of the K8SService's GetWorkloads RPC. K8SServiceGetWorkloadsProcedure = "/api.v1.K8SService/GetWorkloads" // K8SServiceListWorkloadsPaginatedProcedure is the fully-qualified name of the K8SService's @@ -91,6 +109,9 @@ const ( // K8SServiceGetWorkloadsSummaryProcedure is the fully-qualified name of the K8SService's // GetWorkloadsSummary RPC. K8SServiceGetWorkloadsSummaryProcedure = "/api.v1.K8SService/GetWorkloadsSummary" + // K8SServiceGetWorkloadRequestTrendsProcedure is the fully-qualified name of the K8SService's + // GetWorkloadRequestTrends RPC. + K8SServiceGetWorkloadRequestTrendsProcedure = "/api.v1.K8SService/GetWorkloadRequestTrends" // K8SServiceGetWorkloadProcedure is the fully-qualified name of the K8SService's GetWorkload RPC. K8SServiceGetWorkloadProcedure = "/api.v1.K8SService/GetWorkload" // K8SServiceGetWorkloadContainerPercentilesProcedure is the fully-qualified name of the @@ -105,6 +126,9 @@ const ( // K8SServiceGetNodeGPUMigInstancesProcedure is the fully-qualified name of the K8SService's // GetNodeGPUMigInstances RPC. K8SServiceGetNodeGPUMigInstancesProcedure = "/api.v1.K8SService/GetNodeGPUMigInstances" + // K8SServiceGetNodeGPUMigInstanceTimeseriesProcedure is the fully-qualified name of the + // K8SService's GetNodeGPUMigInstanceTimeseries RPC. + K8SServiceGetNodeGPUMigInstanceTimeseriesProcedure = "/api.v1.K8SService/GetNodeGPUMigInstanceTimeseries" // K8SServiceGetContainerIndividualGPUMetricsProcedure is the fully-qualified name of the // K8SService's GetContainerIndividualGPUMetrics RPC. K8SServiceGetContainerIndividualGPUMetricsProcedure = "/api.v1.K8SService/GetContainerIndividualGPUMetrics" @@ -231,10 +255,33 @@ type K8SServiceClient interface { GetAllNodeGroups(context.Context, *connect.Request[v1.GetAllNodeGroupsRequest]) (*connect.Response[v1.GetAllNodeGroupsResponse], error) // GetNodeGroupsUtilization retrieves utilization data for node groups in a specific cluster. GetNodeGroupsUtilization(context.Context, *connect.Request[v1.GetNodeGroupsUtilizationRequest]) (*connect.Response[v1.GetNodeGroupsUtilizationResponse], error) + // GetClusterNodeInventory returns the current node inventory of a cluster: + // one row per node pool, or one keyset page of nodes, plus a whole-cluster + // rollup on every response. Postgres only; nothing in it reads a time range. + GetClusterNodeInventory(context.Context, *connect.Request[v1.GetClusterNodeInventoryRequest]) (*connect.Response[v1.GetClusterNodeInventoryResponse], error) + // GetClusterNodeFilterIndex retrieves the filterable node attributes for a + // cluster: distinct node label sets with their per-node attribution, and the + // scalar spec fields of the Karpenter NodePools backing its node groups. + GetClusterNodeFilterIndex(context.Context, *connect.Request[v1.GetClusterNodeFilterIndexRequest]) (*connect.Response[v1.GetClusterNodeFilterIndexResponse], error) // GetNodeGroup retrieves node group for a specific cluster. GetNodeGroup(context.Context, *connect.Request[v1.GetNodeGroupRequest]) (*connect.Response[v1.GetNodeGroupResponse], error) // GetNode retrieves detailed information for a specific node. GetNode(context.Context, *connect.Request[v1.GetNodeRequest]) (*connect.Response[v1.GetNodeResponse], error) + // RequestNodeClaimDeletion queues a delete of the Karpenter NodeClaim that + // owns a live node. The operator performs the delete; poll + // GetNodeClaimDeletionStatus for progress. + RequestNodeClaimDeletion(context.Context, *connect.Request[v1.RequestNodeClaimDeletionRequest]) (*connect.Response[v1.RequestNodeClaimDeletionResponse], error) + // GetNodeClaimDeletionStatus returns the newest deletion request for a node. + GetNodeClaimDeletionStatus(context.Context, *connect.Request[v1.GetNodeClaimDeletionStatusRequest]) (*connect.Response[v1.GetNodeClaimDeletionStatusResponse], error) + // GetNodeClaimRefs is the batch form of the node_claim field on + // GetNodeResponse: one call answers "may this NodeClaim be deleted right now" + // for a page of nodes, so a table does not fetch a node at a time. + GetNodeClaimRefs(context.Context, *connect.Request[v1.GetNodeClaimRefsRequest]) (*connect.Response[v1.GetNodeClaimRefsResponse], error) + // RequestNodeClaimDeletions queues deletes for several nodes in one call so + // they reach Karpenter inside one provisioning batch window, which is what + // lets it consolidate rather than replace each node it loses. Refusals are + // reported per item; the queued items stay queued. + RequestNodeClaimDeletions(context.Context, *connect.Request[v1.RequestNodeClaimDeletionsRequest]) (*connect.Response[v1.RequestNodeClaimDeletionsResponse], error) // GetWorkloads retrieves all workloads for a specific cluster. GetWorkloads(context.Context, *connect.Request[v1.GetWorkloadsRequest]) (*connect.Response[v1.GetWorkloadsResponse], error) // ListWorkloadsPaginated retrieves a single keyset-paginated page of top-level @@ -244,6 +291,13 @@ type K8SServiceClient interface { // memory/gpu top-N + other) over the full filtered workload set in one pass. // Companion to ListWorkloadsPaginated for summary cards, top-N lists, treemaps. GetWorkloadsSummary(context.Context, *connect.Request[v1.GetWorkloadsSummaryRequest]) (*connect.Response[v1.GetWorkloadsSummaryResponse], error) + // GetWorkloadRequestTrends returns CPU and memory requested over time for the + // same filtered top-level workload set as GetWorkloadsSummary, split into + // managed workloads (tracked by an enabled legacy policy target) and + // unmanaged ones. For managed workloads it also returns the original request: + // the baseline requests captured before the first DevZero change, times the + // pods running in each bucket. + GetWorkloadRequestTrends(context.Context, *connect.Request[v1.GetWorkloadRequestTrendsRequest]) (*connect.Response[v1.GetWorkloadRequestTrendsResponse], error) // GetWorkload retrieves detailed information for a specific workload. GetWorkload(context.Context, *connect.Request[v1.GetWorkloadRequest]) (*connect.Response[v1.GetWorkloadResponse], error) // GetWorkloadContainerPercentiles retrieves per-container percentile metrics for a workload. @@ -258,6 +312,10 @@ type K8SServiceClient interface { // GetNodeGPUMigInstances retrieves the most recent MIG-instance telemetry for a node. // Returns an empty instances list for nodes with no MIG-partitioned GPUs. GetNodeGPUMigInstances(context.Context, *connect.Request[v1.GetNodeGPUMigInstancesRequest]) (*connect.Response[v1.GetNodeGPUMigInstancesResponse], error) + // GetNodeGPUMigInstanceTimeseries retrieves per-MIG-instance utilization as + // a time series for a node over a time range. Returns an empty instances + // list for nodes with no MIG-partitioned GPUs. + GetNodeGPUMigInstanceTimeseries(context.Context, *connect.Request[v1.GetNodeGPUMigInstanceTimeseriesRequest]) (*connect.Response[v1.GetNodeGPUMigInstanceTimeseriesResponse], error) // GetContainerIndividualGPUMetrics retrieves the most recent per-GPU breakdown for every // container in a workload that has GPU data. GetContainerIndividualGPUMetrics(context.Context, *connect.Request[v1.GetContainerIndividualGPUMetricsRequest]) (*connect.Response[v1.GetContainerIndividualGPUMetricsResponse], error) @@ -393,6 +451,16 @@ func NewK8SServiceClient(httpClient connect.HTTPClient, baseURL string, opts ... baseURL+K8SServiceGetNodeGroupsUtilizationProcedure, opts..., ), + getClusterNodeInventory: connect.NewClient[v1.GetClusterNodeInventoryRequest, v1.GetClusterNodeInventoryResponse]( + httpClient, + baseURL+K8SServiceGetClusterNodeInventoryProcedure, + opts..., + ), + getClusterNodeFilterIndex: connect.NewClient[v1.GetClusterNodeFilterIndexRequest, v1.GetClusterNodeFilterIndexResponse]( + httpClient, + baseURL+K8SServiceGetClusterNodeFilterIndexProcedure, + opts..., + ), getNodeGroup: connect.NewClient[v1.GetNodeGroupRequest, v1.GetNodeGroupResponse]( httpClient, baseURL+K8SServiceGetNodeGroupProcedure, @@ -403,6 +471,26 @@ func NewK8SServiceClient(httpClient connect.HTTPClient, baseURL string, opts ... baseURL+K8SServiceGetNodeProcedure, opts..., ), + requestNodeClaimDeletion: connect.NewClient[v1.RequestNodeClaimDeletionRequest, v1.RequestNodeClaimDeletionResponse]( + httpClient, + baseURL+K8SServiceRequestNodeClaimDeletionProcedure, + opts..., + ), + getNodeClaimDeletionStatus: connect.NewClient[v1.GetNodeClaimDeletionStatusRequest, v1.GetNodeClaimDeletionStatusResponse]( + httpClient, + baseURL+K8SServiceGetNodeClaimDeletionStatusProcedure, + opts..., + ), + getNodeClaimRefs: connect.NewClient[v1.GetNodeClaimRefsRequest, v1.GetNodeClaimRefsResponse]( + httpClient, + baseURL+K8SServiceGetNodeClaimRefsProcedure, + opts..., + ), + requestNodeClaimDeletions: connect.NewClient[v1.RequestNodeClaimDeletionsRequest, v1.RequestNodeClaimDeletionsResponse]( + httpClient, + baseURL+K8SServiceRequestNodeClaimDeletionsProcedure, + opts..., + ), getWorkloads: connect.NewClient[v1.GetWorkloadsRequest, v1.GetWorkloadsResponse]( httpClient, baseURL+K8SServiceGetWorkloadsProcedure, @@ -418,6 +506,11 @@ func NewK8SServiceClient(httpClient connect.HTTPClient, baseURL string, opts ... baseURL+K8SServiceGetWorkloadsSummaryProcedure, opts..., ), + getWorkloadRequestTrends: connect.NewClient[v1.GetWorkloadRequestTrendsRequest, v1.GetWorkloadRequestTrendsResponse]( + httpClient, + baseURL+K8SServiceGetWorkloadRequestTrendsProcedure, + opts..., + ), getWorkload: connect.NewClient[v1.GetWorkloadRequest, v1.GetWorkloadResponse]( httpClient, baseURL+K8SServiceGetWorkloadProcedure, @@ -443,6 +536,11 @@ func NewK8SServiceClient(httpClient connect.HTTPClient, baseURL string, opts ... baseURL+K8SServiceGetNodeGPUMigInstancesProcedure, opts..., ), + getNodeGPUMigInstanceTimeseries: connect.NewClient[v1.GetNodeGPUMigInstanceTimeseriesRequest, v1.GetNodeGPUMigInstanceTimeseriesResponse]( + httpClient, + baseURL+K8SServiceGetNodeGPUMigInstanceTimeseriesProcedure, + opts..., + ), getContainerIndividualGPUMetrics: connect.NewClient[v1.GetContainerIndividualGPUMetricsRequest, v1.GetContainerIndividualGPUMetricsResponse]( httpClient, baseURL+K8SServiceGetContainerIndividualGPUMetricsProcedure, @@ -583,16 +681,24 @@ type k8SServiceClient struct { getNodeGroups *connect.Client[v1.GetNodeGroupsRequest, v1.GetNodeGroupsResponse] getAllNodeGroups *connect.Client[v1.GetAllNodeGroupsRequest, v1.GetAllNodeGroupsResponse] getNodeGroupsUtilization *connect.Client[v1.GetNodeGroupsUtilizationRequest, v1.GetNodeGroupsUtilizationResponse] + getClusterNodeInventory *connect.Client[v1.GetClusterNodeInventoryRequest, v1.GetClusterNodeInventoryResponse] + getClusterNodeFilterIndex *connect.Client[v1.GetClusterNodeFilterIndexRequest, v1.GetClusterNodeFilterIndexResponse] getNodeGroup *connect.Client[v1.GetNodeGroupRequest, v1.GetNodeGroupResponse] getNode *connect.Client[v1.GetNodeRequest, v1.GetNodeResponse] + requestNodeClaimDeletion *connect.Client[v1.RequestNodeClaimDeletionRequest, v1.RequestNodeClaimDeletionResponse] + getNodeClaimDeletionStatus *connect.Client[v1.GetNodeClaimDeletionStatusRequest, v1.GetNodeClaimDeletionStatusResponse] + getNodeClaimRefs *connect.Client[v1.GetNodeClaimRefsRequest, v1.GetNodeClaimRefsResponse] + requestNodeClaimDeletions *connect.Client[v1.RequestNodeClaimDeletionsRequest, v1.RequestNodeClaimDeletionsResponse] getWorkloads *connect.Client[v1.GetWorkloadsRequest, v1.GetWorkloadsResponse] listWorkloadsPaginated *connect.Client[v1.ListWorkloadsPaginatedRequest, v1.ListWorkloadsPaginatedResponse] getWorkloadsSummary *connect.Client[v1.GetWorkloadsSummaryRequest, v1.GetWorkloadsSummaryResponse] + getWorkloadRequestTrends *connect.Client[v1.GetWorkloadRequestTrendsRequest, v1.GetWorkloadRequestTrendsResponse] getWorkload *connect.Client[v1.GetWorkloadRequest, v1.GetWorkloadResponse] getWorkloadContainerPercentiles *connect.Client[v1.GetWorkloadContainerPercentilesRequest, v1.GetWorkloadContainerPercentilesResponse] getWorkloadContainerTimeSeries *connect.Client[v1.GetWorkloadContainerTimeSeriesRequest, v1.GetWorkloadContainerTimeSeriesResponse] getLatestContainerRequestLimits *connect.Client[v1.GetLatestContainerRequestLimitsRequest, v1.GetLatestContainerRequestLimitsResponse] getNodeGPUMigInstances *connect.Client[v1.GetNodeGPUMigInstancesRequest, v1.GetNodeGPUMigInstancesResponse] + getNodeGPUMigInstanceTimeseries *connect.Client[v1.GetNodeGPUMigInstanceTimeseriesRequest, v1.GetNodeGPUMigInstanceTimeseriesResponse] getContainerIndividualGPUMetrics *connect.Client[v1.GetContainerIndividualGPUMetricsRequest, v1.GetContainerIndividualGPUMetricsResponse] getWorkloadLanguages *connect.Client[v1.GetWorkloadLanguagesRequest, v1.GetWorkloadLanguagesResponse] getForecastWorkloads *connect.Client[v1.GetForecastWorkloadsRequest, v1.GetForecastWorkloadsResponse] @@ -696,6 +802,16 @@ func (c *k8SServiceClient) GetNodeGroupsUtilization(ctx context.Context, req *co return c.getNodeGroupsUtilization.CallUnary(ctx, req) } +// GetClusterNodeInventory calls api.v1.K8SService.GetClusterNodeInventory. +func (c *k8SServiceClient) GetClusterNodeInventory(ctx context.Context, req *connect.Request[v1.GetClusterNodeInventoryRequest]) (*connect.Response[v1.GetClusterNodeInventoryResponse], error) { + return c.getClusterNodeInventory.CallUnary(ctx, req) +} + +// GetClusterNodeFilterIndex calls api.v1.K8SService.GetClusterNodeFilterIndex. +func (c *k8SServiceClient) GetClusterNodeFilterIndex(ctx context.Context, req *connect.Request[v1.GetClusterNodeFilterIndexRequest]) (*connect.Response[v1.GetClusterNodeFilterIndexResponse], error) { + return c.getClusterNodeFilterIndex.CallUnary(ctx, req) +} + // GetNodeGroup calls api.v1.K8SService.GetNodeGroup. func (c *k8SServiceClient) GetNodeGroup(ctx context.Context, req *connect.Request[v1.GetNodeGroupRequest]) (*connect.Response[v1.GetNodeGroupResponse], error) { return c.getNodeGroup.CallUnary(ctx, req) @@ -706,6 +822,26 @@ func (c *k8SServiceClient) GetNode(ctx context.Context, req *connect.Request[v1. return c.getNode.CallUnary(ctx, req) } +// RequestNodeClaimDeletion calls api.v1.K8SService.RequestNodeClaimDeletion. +func (c *k8SServiceClient) RequestNodeClaimDeletion(ctx context.Context, req *connect.Request[v1.RequestNodeClaimDeletionRequest]) (*connect.Response[v1.RequestNodeClaimDeletionResponse], error) { + return c.requestNodeClaimDeletion.CallUnary(ctx, req) +} + +// GetNodeClaimDeletionStatus calls api.v1.K8SService.GetNodeClaimDeletionStatus. +func (c *k8SServiceClient) GetNodeClaimDeletionStatus(ctx context.Context, req *connect.Request[v1.GetNodeClaimDeletionStatusRequest]) (*connect.Response[v1.GetNodeClaimDeletionStatusResponse], error) { + return c.getNodeClaimDeletionStatus.CallUnary(ctx, req) +} + +// GetNodeClaimRefs calls api.v1.K8SService.GetNodeClaimRefs. +func (c *k8SServiceClient) GetNodeClaimRefs(ctx context.Context, req *connect.Request[v1.GetNodeClaimRefsRequest]) (*connect.Response[v1.GetNodeClaimRefsResponse], error) { + return c.getNodeClaimRefs.CallUnary(ctx, req) +} + +// RequestNodeClaimDeletions calls api.v1.K8SService.RequestNodeClaimDeletions. +func (c *k8SServiceClient) RequestNodeClaimDeletions(ctx context.Context, req *connect.Request[v1.RequestNodeClaimDeletionsRequest]) (*connect.Response[v1.RequestNodeClaimDeletionsResponse], error) { + return c.requestNodeClaimDeletions.CallUnary(ctx, req) +} + // GetWorkloads calls api.v1.K8SService.GetWorkloads. func (c *k8SServiceClient) GetWorkloads(ctx context.Context, req *connect.Request[v1.GetWorkloadsRequest]) (*connect.Response[v1.GetWorkloadsResponse], error) { return c.getWorkloads.CallUnary(ctx, req) @@ -721,6 +857,11 @@ func (c *k8SServiceClient) GetWorkloadsSummary(ctx context.Context, req *connect return c.getWorkloadsSummary.CallUnary(ctx, req) } +// GetWorkloadRequestTrends calls api.v1.K8SService.GetWorkloadRequestTrends. +func (c *k8SServiceClient) GetWorkloadRequestTrends(ctx context.Context, req *connect.Request[v1.GetWorkloadRequestTrendsRequest]) (*connect.Response[v1.GetWorkloadRequestTrendsResponse], error) { + return c.getWorkloadRequestTrends.CallUnary(ctx, req) +} + // GetWorkload calls api.v1.K8SService.GetWorkload. func (c *k8SServiceClient) GetWorkload(ctx context.Context, req *connect.Request[v1.GetWorkloadRequest]) (*connect.Response[v1.GetWorkloadResponse], error) { return c.getWorkload.CallUnary(ctx, req) @@ -746,6 +887,11 @@ func (c *k8SServiceClient) GetNodeGPUMigInstances(ctx context.Context, req *conn return c.getNodeGPUMigInstances.CallUnary(ctx, req) } +// GetNodeGPUMigInstanceTimeseries calls api.v1.K8SService.GetNodeGPUMigInstanceTimeseries. +func (c *k8SServiceClient) GetNodeGPUMigInstanceTimeseries(ctx context.Context, req *connect.Request[v1.GetNodeGPUMigInstanceTimeseriesRequest]) (*connect.Response[v1.GetNodeGPUMigInstanceTimeseriesResponse], error) { + return c.getNodeGPUMigInstanceTimeseries.CallUnary(ctx, req) +} + // GetContainerIndividualGPUMetrics calls api.v1.K8SService.GetContainerIndividualGPUMetrics. func (c *k8SServiceClient) GetContainerIndividualGPUMetrics(ctx context.Context, req *connect.Request[v1.GetContainerIndividualGPUMetricsRequest]) (*connect.Response[v1.GetContainerIndividualGPUMetricsResponse], error) { return c.getContainerIndividualGPUMetrics.CallUnary(ctx, req) @@ -904,10 +1050,33 @@ type K8SServiceHandler interface { GetAllNodeGroups(context.Context, *connect.Request[v1.GetAllNodeGroupsRequest]) (*connect.Response[v1.GetAllNodeGroupsResponse], error) // GetNodeGroupsUtilization retrieves utilization data for node groups in a specific cluster. GetNodeGroupsUtilization(context.Context, *connect.Request[v1.GetNodeGroupsUtilizationRequest]) (*connect.Response[v1.GetNodeGroupsUtilizationResponse], error) + // GetClusterNodeInventory returns the current node inventory of a cluster: + // one row per node pool, or one keyset page of nodes, plus a whole-cluster + // rollup on every response. Postgres only; nothing in it reads a time range. + GetClusterNodeInventory(context.Context, *connect.Request[v1.GetClusterNodeInventoryRequest]) (*connect.Response[v1.GetClusterNodeInventoryResponse], error) + // GetClusterNodeFilterIndex retrieves the filterable node attributes for a + // cluster: distinct node label sets with their per-node attribution, and the + // scalar spec fields of the Karpenter NodePools backing its node groups. + GetClusterNodeFilterIndex(context.Context, *connect.Request[v1.GetClusterNodeFilterIndexRequest]) (*connect.Response[v1.GetClusterNodeFilterIndexResponse], error) // GetNodeGroup retrieves node group for a specific cluster. GetNodeGroup(context.Context, *connect.Request[v1.GetNodeGroupRequest]) (*connect.Response[v1.GetNodeGroupResponse], error) // GetNode retrieves detailed information for a specific node. GetNode(context.Context, *connect.Request[v1.GetNodeRequest]) (*connect.Response[v1.GetNodeResponse], error) + // RequestNodeClaimDeletion queues a delete of the Karpenter NodeClaim that + // owns a live node. The operator performs the delete; poll + // GetNodeClaimDeletionStatus for progress. + RequestNodeClaimDeletion(context.Context, *connect.Request[v1.RequestNodeClaimDeletionRequest]) (*connect.Response[v1.RequestNodeClaimDeletionResponse], error) + // GetNodeClaimDeletionStatus returns the newest deletion request for a node. + GetNodeClaimDeletionStatus(context.Context, *connect.Request[v1.GetNodeClaimDeletionStatusRequest]) (*connect.Response[v1.GetNodeClaimDeletionStatusResponse], error) + // GetNodeClaimRefs is the batch form of the node_claim field on + // GetNodeResponse: one call answers "may this NodeClaim be deleted right now" + // for a page of nodes, so a table does not fetch a node at a time. + GetNodeClaimRefs(context.Context, *connect.Request[v1.GetNodeClaimRefsRequest]) (*connect.Response[v1.GetNodeClaimRefsResponse], error) + // RequestNodeClaimDeletions queues deletes for several nodes in one call so + // they reach Karpenter inside one provisioning batch window, which is what + // lets it consolidate rather than replace each node it loses. Refusals are + // reported per item; the queued items stay queued. + RequestNodeClaimDeletions(context.Context, *connect.Request[v1.RequestNodeClaimDeletionsRequest]) (*connect.Response[v1.RequestNodeClaimDeletionsResponse], error) // GetWorkloads retrieves all workloads for a specific cluster. GetWorkloads(context.Context, *connect.Request[v1.GetWorkloadsRequest]) (*connect.Response[v1.GetWorkloadsResponse], error) // ListWorkloadsPaginated retrieves a single keyset-paginated page of top-level @@ -917,6 +1086,13 @@ type K8SServiceHandler interface { // memory/gpu top-N + other) over the full filtered workload set in one pass. // Companion to ListWorkloadsPaginated for summary cards, top-N lists, treemaps. GetWorkloadsSummary(context.Context, *connect.Request[v1.GetWorkloadsSummaryRequest]) (*connect.Response[v1.GetWorkloadsSummaryResponse], error) + // GetWorkloadRequestTrends returns CPU and memory requested over time for the + // same filtered top-level workload set as GetWorkloadsSummary, split into + // managed workloads (tracked by an enabled legacy policy target) and + // unmanaged ones. For managed workloads it also returns the original request: + // the baseline requests captured before the first DevZero change, times the + // pods running in each bucket. + GetWorkloadRequestTrends(context.Context, *connect.Request[v1.GetWorkloadRequestTrendsRequest]) (*connect.Response[v1.GetWorkloadRequestTrendsResponse], error) // GetWorkload retrieves detailed information for a specific workload. GetWorkload(context.Context, *connect.Request[v1.GetWorkloadRequest]) (*connect.Response[v1.GetWorkloadResponse], error) // GetWorkloadContainerPercentiles retrieves per-container percentile metrics for a workload. @@ -931,6 +1107,10 @@ type K8SServiceHandler interface { // GetNodeGPUMigInstances retrieves the most recent MIG-instance telemetry for a node. // Returns an empty instances list for nodes with no MIG-partitioned GPUs. GetNodeGPUMigInstances(context.Context, *connect.Request[v1.GetNodeGPUMigInstancesRequest]) (*connect.Response[v1.GetNodeGPUMigInstancesResponse], error) + // GetNodeGPUMigInstanceTimeseries retrieves per-MIG-instance utilization as + // a time series for a node over a time range. Returns an empty instances + // list for nodes with no MIG-partitioned GPUs. + GetNodeGPUMigInstanceTimeseries(context.Context, *connect.Request[v1.GetNodeGPUMigInstanceTimeseriesRequest]) (*connect.Response[v1.GetNodeGPUMigInstanceTimeseriesResponse], error) // GetContainerIndividualGPUMetrics retrieves the most recent per-GPU breakdown for every // container in a workload that has GPU data. GetContainerIndividualGPUMetrics(context.Context, *connect.Request[v1.GetContainerIndividualGPUMetricsRequest]) (*connect.Response[v1.GetContainerIndividualGPUMetricsResponse], error) @@ -1062,6 +1242,16 @@ func NewK8SServiceHandler(svc K8SServiceHandler, opts ...connect.HandlerOption) svc.GetNodeGroupsUtilization, opts..., ) + k8SServiceGetClusterNodeInventoryHandler := connect.NewUnaryHandler( + K8SServiceGetClusterNodeInventoryProcedure, + svc.GetClusterNodeInventory, + opts..., + ) + k8SServiceGetClusterNodeFilterIndexHandler := connect.NewUnaryHandler( + K8SServiceGetClusterNodeFilterIndexProcedure, + svc.GetClusterNodeFilterIndex, + opts..., + ) k8SServiceGetNodeGroupHandler := connect.NewUnaryHandler( K8SServiceGetNodeGroupProcedure, svc.GetNodeGroup, @@ -1072,6 +1262,26 @@ func NewK8SServiceHandler(svc K8SServiceHandler, opts ...connect.HandlerOption) svc.GetNode, opts..., ) + k8SServiceRequestNodeClaimDeletionHandler := connect.NewUnaryHandler( + K8SServiceRequestNodeClaimDeletionProcedure, + svc.RequestNodeClaimDeletion, + opts..., + ) + k8SServiceGetNodeClaimDeletionStatusHandler := connect.NewUnaryHandler( + K8SServiceGetNodeClaimDeletionStatusProcedure, + svc.GetNodeClaimDeletionStatus, + opts..., + ) + k8SServiceGetNodeClaimRefsHandler := connect.NewUnaryHandler( + K8SServiceGetNodeClaimRefsProcedure, + svc.GetNodeClaimRefs, + opts..., + ) + k8SServiceRequestNodeClaimDeletionsHandler := connect.NewUnaryHandler( + K8SServiceRequestNodeClaimDeletionsProcedure, + svc.RequestNodeClaimDeletions, + opts..., + ) k8SServiceGetWorkloadsHandler := connect.NewUnaryHandler( K8SServiceGetWorkloadsProcedure, svc.GetWorkloads, @@ -1087,6 +1297,11 @@ func NewK8SServiceHandler(svc K8SServiceHandler, opts ...connect.HandlerOption) svc.GetWorkloadsSummary, opts..., ) + k8SServiceGetWorkloadRequestTrendsHandler := connect.NewUnaryHandler( + K8SServiceGetWorkloadRequestTrendsProcedure, + svc.GetWorkloadRequestTrends, + opts..., + ) k8SServiceGetWorkloadHandler := connect.NewUnaryHandler( K8SServiceGetWorkloadProcedure, svc.GetWorkload, @@ -1112,6 +1327,11 @@ func NewK8SServiceHandler(svc K8SServiceHandler, opts ...connect.HandlerOption) svc.GetNodeGPUMigInstances, opts..., ) + k8SServiceGetNodeGPUMigInstanceTimeseriesHandler := connect.NewUnaryHandler( + K8SServiceGetNodeGPUMigInstanceTimeseriesProcedure, + svc.GetNodeGPUMigInstanceTimeseries, + opts..., + ) k8SServiceGetContainerIndividualGPUMetricsHandler := connect.NewUnaryHandler( K8SServiceGetContainerIndividualGPUMetricsProcedure, svc.GetContainerIndividualGPUMetrics, @@ -1264,16 +1484,30 @@ func NewK8SServiceHandler(svc K8SServiceHandler, opts ...connect.HandlerOption) k8SServiceGetAllNodeGroupsHandler.ServeHTTP(w, r) case K8SServiceGetNodeGroupsUtilizationProcedure: k8SServiceGetNodeGroupsUtilizationHandler.ServeHTTP(w, r) + case K8SServiceGetClusterNodeInventoryProcedure: + k8SServiceGetClusterNodeInventoryHandler.ServeHTTP(w, r) + case K8SServiceGetClusterNodeFilterIndexProcedure: + k8SServiceGetClusterNodeFilterIndexHandler.ServeHTTP(w, r) case K8SServiceGetNodeGroupProcedure: k8SServiceGetNodeGroupHandler.ServeHTTP(w, r) case K8SServiceGetNodeProcedure: k8SServiceGetNodeHandler.ServeHTTP(w, r) + case K8SServiceRequestNodeClaimDeletionProcedure: + k8SServiceRequestNodeClaimDeletionHandler.ServeHTTP(w, r) + case K8SServiceGetNodeClaimDeletionStatusProcedure: + k8SServiceGetNodeClaimDeletionStatusHandler.ServeHTTP(w, r) + case K8SServiceGetNodeClaimRefsProcedure: + k8SServiceGetNodeClaimRefsHandler.ServeHTTP(w, r) + case K8SServiceRequestNodeClaimDeletionsProcedure: + k8SServiceRequestNodeClaimDeletionsHandler.ServeHTTP(w, r) case K8SServiceGetWorkloadsProcedure: k8SServiceGetWorkloadsHandler.ServeHTTP(w, r) case K8SServiceListWorkloadsPaginatedProcedure: k8SServiceListWorkloadsPaginatedHandler.ServeHTTP(w, r) case K8SServiceGetWorkloadsSummaryProcedure: k8SServiceGetWorkloadsSummaryHandler.ServeHTTP(w, r) + case K8SServiceGetWorkloadRequestTrendsProcedure: + k8SServiceGetWorkloadRequestTrendsHandler.ServeHTTP(w, r) case K8SServiceGetWorkloadProcedure: k8SServiceGetWorkloadHandler.ServeHTTP(w, r) case K8SServiceGetWorkloadContainerPercentilesProcedure: @@ -1284,6 +1518,8 @@ func NewK8SServiceHandler(svc K8SServiceHandler, opts ...connect.HandlerOption) k8SServiceGetLatestContainerRequestLimitsHandler.ServeHTTP(w, r) case K8SServiceGetNodeGPUMigInstancesProcedure: k8SServiceGetNodeGPUMigInstancesHandler.ServeHTTP(w, r) + case K8SServiceGetNodeGPUMigInstanceTimeseriesProcedure: + k8SServiceGetNodeGPUMigInstanceTimeseriesHandler.ServeHTTP(w, r) case K8SServiceGetContainerIndividualGPUMetricsProcedure: k8SServiceGetContainerIndividualGPUMetricsHandler.ServeHTTP(w, r) case K8SServiceGetWorkloadLanguagesProcedure: @@ -1401,6 +1637,14 @@ func (UnimplementedK8SServiceHandler) GetNodeGroupsUtilization(context.Context, return nil, connect.NewError(connect.CodeUnimplemented, errors.New("api.v1.K8SService.GetNodeGroupsUtilization is not implemented")) } +func (UnimplementedK8SServiceHandler) GetClusterNodeInventory(context.Context, *connect.Request[v1.GetClusterNodeInventoryRequest]) (*connect.Response[v1.GetClusterNodeInventoryResponse], error) { + return nil, connect.NewError(connect.CodeUnimplemented, errors.New("api.v1.K8SService.GetClusterNodeInventory is not implemented")) +} + +func (UnimplementedK8SServiceHandler) GetClusterNodeFilterIndex(context.Context, *connect.Request[v1.GetClusterNodeFilterIndexRequest]) (*connect.Response[v1.GetClusterNodeFilterIndexResponse], error) { + return nil, connect.NewError(connect.CodeUnimplemented, errors.New("api.v1.K8SService.GetClusterNodeFilterIndex is not implemented")) +} + func (UnimplementedK8SServiceHandler) GetNodeGroup(context.Context, *connect.Request[v1.GetNodeGroupRequest]) (*connect.Response[v1.GetNodeGroupResponse], error) { return nil, connect.NewError(connect.CodeUnimplemented, errors.New("api.v1.K8SService.GetNodeGroup is not implemented")) } @@ -1409,6 +1653,22 @@ func (UnimplementedK8SServiceHandler) GetNode(context.Context, *connect.Request[ return nil, connect.NewError(connect.CodeUnimplemented, errors.New("api.v1.K8SService.GetNode is not implemented")) } +func (UnimplementedK8SServiceHandler) RequestNodeClaimDeletion(context.Context, *connect.Request[v1.RequestNodeClaimDeletionRequest]) (*connect.Response[v1.RequestNodeClaimDeletionResponse], error) { + return nil, connect.NewError(connect.CodeUnimplemented, errors.New("api.v1.K8SService.RequestNodeClaimDeletion is not implemented")) +} + +func (UnimplementedK8SServiceHandler) GetNodeClaimDeletionStatus(context.Context, *connect.Request[v1.GetNodeClaimDeletionStatusRequest]) (*connect.Response[v1.GetNodeClaimDeletionStatusResponse], error) { + return nil, connect.NewError(connect.CodeUnimplemented, errors.New("api.v1.K8SService.GetNodeClaimDeletionStatus is not implemented")) +} + +func (UnimplementedK8SServiceHandler) GetNodeClaimRefs(context.Context, *connect.Request[v1.GetNodeClaimRefsRequest]) (*connect.Response[v1.GetNodeClaimRefsResponse], error) { + return nil, connect.NewError(connect.CodeUnimplemented, errors.New("api.v1.K8SService.GetNodeClaimRefs is not implemented")) +} + +func (UnimplementedK8SServiceHandler) RequestNodeClaimDeletions(context.Context, *connect.Request[v1.RequestNodeClaimDeletionsRequest]) (*connect.Response[v1.RequestNodeClaimDeletionsResponse], error) { + return nil, connect.NewError(connect.CodeUnimplemented, errors.New("api.v1.K8SService.RequestNodeClaimDeletions is not implemented")) +} + func (UnimplementedK8SServiceHandler) GetWorkloads(context.Context, *connect.Request[v1.GetWorkloadsRequest]) (*connect.Response[v1.GetWorkloadsResponse], error) { return nil, connect.NewError(connect.CodeUnimplemented, errors.New("api.v1.K8SService.GetWorkloads is not implemented")) } @@ -1421,6 +1681,10 @@ func (UnimplementedK8SServiceHandler) GetWorkloadsSummary(context.Context, *conn return nil, connect.NewError(connect.CodeUnimplemented, errors.New("api.v1.K8SService.GetWorkloadsSummary is not implemented")) } +func (UnimplementedK8SServiceHandler) GetWorkloadRequestTrends(context.Context, *connect.Request[v1.GetWorkloadRequestTrendsRequest]) (*connect.Response[v1.GetWorkloadRequestTrendsResponse], error) { + return nil, connect.NewError(connect.CodeUnimplemented, errors.New("api.v1.K8SService.GetWorkloadRequestTrends is not implemented")) +} + func (UnimplementedK8SServiceHandler) GetWorkload(context.Context, *connect.Request[v1.GetWorkloadRequest]) (*connect.Response[v1.GetWorkloadResponse], error) { return nil, connect.NewError(connect.CodeUnimplemented, errors.New("api.v1.K8SService.GetWorkload is not implemented")) } @@ -1441,6 +1705,10 @@ func (UnimplementedK8SServiceHandler) GetNodeGPUMigInstances(context.Context, *c return nil, connect.NewError(connect.CodeUnimplemented, errors.New("api.v1.K8SService.GetNodeGPUMigInstances is not implemented")) } +func (UnimplementedK8SServiceHandler) GetNodeGPUMigInstanceTimeseries(context.Context, *connect.Request[v1.GetNodeGPUMigInstanceTimeseriesRequest]) (*connect.Response[v1.GetNodeGPUMigInstanceTimeseriesResponse], error) { + return nil, connect.NewError(connect.CodeUnimplemented, errors.New("api.v1.K8SService.GetNodeGPUMigInstanceTimeseries is not implemented")) +} + func (UnimplementedK8SServiceHandler) GetContainerIndividualGPUMetrics(context.Context, *connect.Request[v1.GetContainerIndividualGPUMetricsRequest]) (*connect.Response[v1.GetContainerIndividualGPUMetricsResponse], error) { return nil, connect.NewError(connect.CodeUnimplemented, errors.New("api.v1.K8SService.GetContainerIndividualGPUMetrics is not implemented")) } diff --git a/gen/api/v1/apiv1connect/metrics_collector.connect.go b/gen/api/v1/apiv1connect/metrics_collector.connect.go index de01e14c..c9d94faa 100644 --- a/gen/api/v1/apiv1connect/metrics_collector.connect.go +++ b/gen/api/v1/apiv1connect/metrics_collector.connect.go @@ -54,6 +54,12 @@ const ( // MetricsCollectorServiceSendNetworkTrafficMetricsProcedure is the fully-qualified name of the // MetricsCollectorService's SendNetworkTrafficMetrics RPC. MetricsCollectorServiceSendNetworkTrafficMetricsProcedure = "/api.v1.MetricsCollectorService/SendNetworkTrafficMetrics" + // MetricsCollectorServiceSendScalerMetricsProcedure is the fully-qualified name of the + // MetricsCollectorService's SendScalerMetrics RPC. + MetricsCollectorServiceSendScalerMetricsProcedure = "/api.v1.MetricsCollectorService/SendScalerMetrics" + // MetricsCollectorServiceSendContainerRedMetricsProcedure is the fully-qualified name of the + // MetricsCollectorService's SendContainerRedMetrics RPC. + MetricsCollectorServiceSendContainerRedMetricsProcedure = "/api.v1.MetricsCollectorService/SendContainerRedMetrics" // MetricsCollectorServiceNodeMetadataProcedure is the fully-qualified name of the // MetricsCollectorService's NodeMetadata RPC. MetricsCollectorServiceNodeMetadataProcedure = "/api.v1.MetricsCollectorService/NodeMetadata" @@ -77,6 +83,10 @@ type MetricsCollectorServiceClient interface { SendTelemetryLogs(context.Context, *connect.Request[v1.SendTelemetryLogsRequest]) (*connect.Response[v1.SendTelemetryLogsResponse], error) // SendNetworkTrafficMetrics pushes network traffic metrics from a node. SendNetworkTrafficMetrics(context.Context, *connect.Request[v1.SendNetworkTrafficMetricsRequest]) (*connect.Response[v1.SendNetworkTrafficMetricsResponse], error) + // SendScalerMetrics pushes a batch of KEDA scaler metric samples from a cluster. + SendScalerMetrics(context.Context, *connect.Request[v1.SendScalerMetricsRequest]) (*connect.Response[v1.SendScalerMetricsResponse], error) + // SendContainerRedMetrics pushes per-container inbound RED metrics from a node. + SendContainerRedMetrics(context.Context, *connect.Request[v1.SendContainerRedMetricsRequest]) (*connect.Response[v1.SendContainerRedMetricsResponse], error) NodeMetadata(context.Context, *connect.Request[v1.NodeMetadataRequest]) (*connect.Response[v1.NodeMetadataResponse], error) } @@ -125,6 +135,16 @@ func NewMetricsCollectorServiceClient(httpClient connect.HTTPClient, baseURL str baseURL+MetricsCollectorServiceSendNetworkTrafficMetricsProcedure, opts..., ), + sendScalerMetrics: connect.NewClient[v1.SendScalerMetricsRequest, v1.SendScalerMetricsResponse]( + httpClient, + baseURL+MetricsCollectorServiceSendScalerMetricsProcedure, + opts..., + ), + sendContainerRedMetrics: connect.NewClient[v1.SendContainerRedMetricsRequest, v1.SendContainerRedMetricsResponse]( + httpClient, + baseURL+MetricsCollectorServiceSendContainerRedMetricsProcedure, + opts..., + ), nodeMetadata: connect.NewClient[v1.NodeMetadataRequest, v1.NodeMetadataResponse]( httpClient, baseURL+MetricsCollectorServiceNodeMetadataProcedure, @@ -142,6 +162,8 @@ type metricsCollectorServiceClient struct { sendClusterSnapshotBatched *connect.Client[v1.ClusterSnapshotBatch, v1.SendClusterSnapshotBatchedResponse] sendTelemetryLogs *connect.Client[v1.SendTelemetryLogsRequest, v1.SendTelemetryLogsResponse] sendNetworkTrafficMetrics *connect.Client[v1.SendNetworkTrafficMetricsRequest, v1.SendNetworkTrafficMetricsResponse] + sendScalerMetrics *connect.Client[v1.SendScalerMetricsRequest, v1.SendScalerMetricsResponse] + sendContainerRedMetrics *connect.Client[v1.SendContainerRedMetricsRequest, v1.SendContainerRedMetricsResponse] nodeMetadata *connect.Client[v1.NodeMetadataRequest, v1.NodeMetadataResponse] } @@ -180,6 +202,16 @@ func (c *metricsCollectorServiceClient) SendNetworkTrafficMetrics(ctx context.Co return c.sendNetworkTrafficMetrics.CallUnary(ctx, req) } +// SendScalerMetrics calls api.v1.MetricsCollectorService.SendScalerMetrics. +func (c *metricsCollectorServiceClient) SendScalerMetrics(ctx context.Context, req *connect.Request[v1.SendScalerMetricsRequest]) (*connect.Response[v1.SendScalerMetricsResponse], error) { + return c.sendScalerMetrics.CallUnary(ctx, req) +} + +// SendContainerRedMetrics calls api.v1.MetricsCollectorService.SendContainerRedMetrics. +func (c *metricsCollectorServiceClient) SendContainerRedMetrics(ctx context.Context, req *connect.Request[v1.SendContainerRedMetricsRequest]) (*connect.Response[v1.SendContainerRedMetricsResponse], error) { + return c.sendContainerRedMetrics.CallUnary(ctx, req) +} + // NodeMetadata calls api.v1.MetricsCollectorService.NodeMetadata. func (c *metricsCollectorServiceClient) NodeMetadata(ctx context.Context, req *connect.Request[v1.NodeMetadataRequest]) (*connect.Response[v1.NodeMetadataResponse], error) { return c.nodeMetadata.CallUnary(ctx, req) @@ -204,6 +236,10 @@ type MetricsCollectorServiceHandler interface { SendTelemetryLogs(context.Context, *connect.Request[v1.SendTelemetryLogsRequest]) (*connect.Response[v1.SendTelemetryLogsResponse], error) // SendNetworkTrafficMetrics pushes network traffic metrics from a node. SendNetworkTrafficMetrics(context.Context, *connect.Request[v1.SendNetworkTrafficMetricsRequest]) (*connect.Response[v1.SendNetworkTrafficMetricsResponse], error) + // SendScalerMetrics pushes a batch of KEDA scaler metric samples from a cluster. + SendScalerMetrics(context.Context, *connect.Request[v1.SendScalerMetricsRequest]) (*connect.Response[v1.SendScalerMetricsResponse], error) + // SendContainerRedMetrics pushes per-container inbound RED metrics from a node. + SendContainerRedMetrics(context.Context, *connect.Request[v1.SendContainerRedMetricsRequest]) (*connect.Response[v1.SendContainerRedMetricsResponse], error) NodeMetadata(context.Context, *connect.Request[v1.NodeMetadataRequest]) (*connect.Response[v1.NodeMetadataResponse], error) } @@ -248,6 +284,16 @@ func NewMetricsCollectorServiceHandler(svc MetricsCollectorServiceHandler, opts svc.SendNetworkTrafficMetrics, opts..., ) + metricsCollectorServiceSendScalerMetricsHandler := connect.NewUnaryHandler( + MetricsCollectorServiceSendScalerMetricsProcedure, + svc.SendScalerMetrics, + opts..., + ) + metricsCollectorServiceSendContainerRedMetricsHandler := connect.NewUnaryHandler( + MetricsCollectorServiceSendContainerRedMetricsProcedure, + svc.SendContainerRedMetrics, + opts..., + ) metricsCollectorServiceNodeMetadataHandler := connect.NewUnaryHandler( MetricsCollectorServiceNodeMetadataProcedure, svc.NodeMetadata, @@ -269,6 +315,10 @@ func NewMetricsCollectorServiceHandler(svc MetricsCollectorServiceHandler, opts metricsCollectorServiceSendTelemetryLogsHandler.ServeHTTP(w, r) case MetricsCollectorServiceSendNetworkTrafficMetricsProcedure: metricsCollectorServiceSendNetworkTrafficMetricsHandler.ServeHTTP(w, r) + case MetricsCollectorServiceSendScalerMetricsProcedure: + metricsCollectorServiceSendScalerMetricsHandler.ServeHTTP(w, r) + case MetricsCollectorServiceSendContainerRedMetricsProcedure: + metricsCollectorServiceSendContainerRedMetricsHandler.ServeHTTP(w, r) case MetricsCollectorServiceNodeMetadataProcedure: metricsCollectorServiceNodeMetadataHandler.ServeHTTP(w, r) default: @@ -308,6 +358,14 @@ func (UnimplementedMetricsCollectorServiceHandler) SendNetworkTrafficMetrics(con return nil, connect.NewError(connect.CodeUnimplemented, errors.New("api.v1.MetricsCollectorService.SendNetworkTrafficMetrics is not implemented")) } +func (UnimplementedMetricsCollectorServiceHandler) SendScalerMetrics(context.Context, *connect.Request[v1.SendScalerMetricsRequest]) (*connect.Response[v1.SendScalerMetricsResponse], error) { + return nil, connect.NewError(connect.CodeUnimplemented, errors.New("api.v1.MetricsCollectorService.SendScalerMetrics is not implemented")) +} + +func (UnimplementedMetricsCollectorServiceHandler) SendContainerRedMetrics(context.Context, *connect.Request[v1.SendContainerRedMetricsRequest]) (*connect.Response[v1.SendContainerRedMetricsResponse], error) { + return nil, connect.NewError(connect.CodeUnimplemented, errors.New("api.v1.MetricsCollectorService.SendContainerRedMetrics is not implemented")) +} + func (UnimplementedMetricsCollectorServiceHandler) NodeMetadata(context.Context, *connect.Request[v1.NodeMetadataRequest]) (*connect.Response[v1.NodeMetadataResponse], error) { return nil, connect.NewError(connect.CodeUnimplemented, errors.New("api.v1.MetricsCollectorService.NodeMetadata is not implemented")) } diff --git a/gen/api/v1/apiv1connect/operator_health.connect.go b/gen/api/v1/apiv1connect/operator_health.connect.go index 7a314bfc..f5f71ed0 100644 --- a/gen/api/v1/apiv1connect/operator_health.connect.go +++ b/gen/api/v1/apiv1connect/operator_health.connect.go @@ -36,15 +36,11 @@ const ( // OperatorHealthServiceReportHealthProcedure is the fully-qualified name of the // OperatorHealthService's ReportHealth RPC. OperatorHealthServiceReportHealthProcedure = "/api.v1.OperatorHealthService/ReportHealth" - // OperatorHealthServiceReportConnectorHealthProcedure is the fully-qualified name of the - // OperatorHealthService's ReportConnectorHealth RPC. - OperatorHealthServiceReportConnectorHealthProcedure = "/api.v1.OperatorHealthService/ReportConnectorHealth" ) // OperatorHealthServiceClient is a client for the api.v1.OperatorHealthService service. type OperatorHealthServiceClient interface { ReportHealth(context.Context, *connect.Request[v1.ReportHealthRequest]) (*connect.Response[v1.ReportHealthResponse], error) - ReportConnectorHealth(context.Context, *connect.Request[v1.ReportConnectorHealthRequest]) (*connect.Response[v1.ReportConnectorHealthResponse], error) } // NewOperatorHealthServiceClient constructs a client for the api.v1.OperatorHealthService service. @@ -62,18 +58,12 @@ func NewOperatorHealthServiceClient(httpClient connect.HTTPClient, baseURL strin baseURL+OperatorHealthServiceReportHealthProcedure, opts..., ), - reportConnectorHealth: connect.NewClient[v1.ReportConnectorHealthRequest, v1.ReportConnectorHealthResponse]( - httpClient, - baseURL+OperatorHealthServiceReportConnectorHealthProcedure, - opts..., - ), } } // operatorHealthServiceClient implements OperatorHealthServiceClient. type operatorHealthServiceClient struct { - reportHealth *connect.Client[v1.ReportHealthRequest, v1.ReportHealthResponse] - reportConnectorHealth *connect.Client[v1.ReportConnectorHealthRequest, v1.ReportConnectorHealthResponse] + reportHealth *connect.Client[v1.ReportHealthRequest, v1.ReportHealthResponse] } // ReportHealth calls api.v1.OperatorHealthService.ReportHealth. @@ -81,15 +71,9 @@ func (c *operatorHealthServiceClient) ReportHealth(ctx context.Context, req *con return c.reportHealth.CallUnary(ctx, req) } -// ReportConnectorHealth calls api.v1.OperatorHealthService.ReportConnectorHealth. -func (c *operatorHealthServiceClient) ReportConnectorHealth(ctx context.Context, req *connect.Request[v1.ReportConnectorHealthRequest]) (*connect.Response[v1.ReportConnectorHealthResponse], error) { - return c.reportConnectorHealth.CallUnary(ctx, req) -} - // OperatorHealthServiceHandler is an implementation of the api.v1.OperatorHealthService service. type OperatorHealthServiceHandler interface { ReportHealth(context.Context, *connect.Request[v1.ReportHealthRequest]) (*connect.Response[v1.ReportHealthResponse], error) - ReportConnectorHealth(context.Context, *connect.Request[v1.ReportConnectorHealthRequest]) (*connect.Response[v1.ReportConnectorHealthResponse], error) } // NewOperatorHealthServiceHandler builds an HTTP handler from the service implementation. It @@ -103,17 +87,10 @@ func NewOperatorHealthServiceHandler(svc OperatorHealthServiceHandler, opts ...c svc.ReportHealth, opts..., ) - operatorHealthServiceReportConnectorHealthHandler := connect.NewUnaryHandler( - OperatorHealthServiceReportConnectorHealthProcedure, - svc.ReportConnectorHealth, - opts..., - ) return "/api.v1.OperatorHealthService/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { switch r.URL.Path { case OperatorHealthServiceReportHealthProcedure: operatorHealthServiceReportHealthHandler.ServeHTTP(w, r) - case OperatorHealthServiceReportConnectorHealthProcedure: - operatorHealthServiceReportConnectorHealthHandler.ServeHTTP(w, r) default: http.NotFound(w, r) } @@ -126,7 +103,3 @@ type UnimplementedOperatorHealthServiceHandler struct{} func (UnimplementedOperatorHealthServiceHandler) ReportHealth(context.Context, *connect.Request[v1.ReportHealthRequest]) (*connect.Response[v1.ReportHealthResponse], error) { return nil, connect.NewError(connect.CodeUnimplemented, errors.New("api.v1.OperatorHealthService.ReportHealth is not implemented")) } - -func (UnimplementedOperatorHealthServiceHandler) ReportConnectorHealth(context.Context, *connect.Request[v1.ReportConnectorHealthRequest]) (*connect.Response[v1.ReportConnectorHealthResponse], error) { - return nil, connect.NewError(connect.CodeUnimplemented, errors.New("api.v1.OperatorHealthService.ReportConnectorHealth is not implemented")) -} diff --git a/gen/api/v1/cluster.pb.go b/gen/api/v1/cluster.pb.go index d7119402..c344250d 100644 --- a/gen/api/v1/cluster.pb.go +++ b/gen/api/v1/cluster.pb.go @@ -651,8 +651,10 @@ type CreateClusterTokenRequest struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - ClusterName string `protobuf:"bytes,1,opt,name=cluster_name,json=clusterName,proto3" json:"cluster_name,omitempty"` // Name for the new cluster. - K8SProvider string `protobuf:"bytes,2,opt,name=k8s_provider,json=k8sProvider,proto3" json:"k8s_provider,omitempty"` // Kubernetes provider (aws, azure, gcp, oci, other). + ClusterName string `protobuf:"bytes,1,opt,name=cluster_name,json=clusterName,proto3" json:"cluster_name,omitempty"` // Name for the new cluster. + K8SProvider string `protobuf:"bytes,2,opt,name=k8s_provider,json=k8sProvider,proto3" json:"k8s_provider,omitempty"` // Kubernetes provider (aws, azure, gcp, oci, other). + KubeSystemUid string `protobuf:"bytes,3,opt,name=kube_system_uid,json=kubeSystemUid,proto3" json:"kube_system_uid,omitempty"` // UID of the cluster kube-system namespace. A live cluster in the caller team with this UID is re-attached instead of a new one created. + ClusterId string `protobuf:"bytes,4,opt,name=cluster_id,json=clusterId,proto3" json:"cluster_id,omitempty"` // Existing cluster to re-attach to. NotFound unless it is a live cluster in the caller team; never falls back to creating one. } func (x *CreateClusterTokenRequest) Reset() { @@ -701,14 +703,29 @@ func (x *CreateClusterTokenRequest) GetK8SProvider() string { return "" } +func (x *CreateClusterTokenRequest) GetKubeSystemUid() string { + if x != nil { + return x.KubeSystemUid + } + return "" +} + +func (x *CreateClusterTokenRequest) GetClusterId() string { + if x != nil { + return x.ClusterId + } + return "" +} + // CreateClusterTokenResponse returns the authentication token and cluster ID type CreateClusterTokenResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Token string `protobuf:"bytes,1,opt,name=token,proto3" json:"token,omitempty"` // Authentication token for the cluster. - ClusterId string `protobuf:"bytes,2,opt,name=cluster_id,json=clusterId,proto3" json:"cluster_id,omitempty"` // Unique identifier for the created cluster. + Token string `protobuf:"bytes,1,opt,name=token,proto3" json:"token,omitempty"` // Authentication token for the cluster. + ClusterId string `protobuf:"bytes,2,opt,name=cluster_id,json=clusterId,proto3" json:"cluster_id,omitempty"` // Unique identifier for the created cluster. + Reattached bool `protobuf:"varint,3,opt,name=reattached,proto3" json:"reattached,omitempty"` // True when an existing cluster token was rotated instead of a cluster being created. } func (x *CreateClusterTokenResponse) Reset() { @@ -757,6 +774,13 @@ func (x *CreateClusterTokenResponse) GetClusterId() string { return "" } +func (x *CreateClusterTokenResponse) GetReattached() bool { + if x != nil { + return x.Reattached + } + return false +} + // Request for network dependency graph (workload-level aggregation) type GetNetworkDependenciesRequest struct { state protoimpl.MessageState @@ -2053,577 +2077,1769 @@ func (x *GetClusterInfoByNameResponse) GetCluster() *Cluster { return nil } -var File_api_v1_cluster_proto protoreflect.FileDescriptor +type GetClustersCurrentStateRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields -var file_api_v1_cluster_proto_rawDesc = []byte{ - 0x0a, 0x14, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, - 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x06, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x1a, 0x13, - 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x1a, 0x10, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x6b, 0x38, 0x73, 0x2e, - 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1f, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, - 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x5b, 0x0a, 0x1b, 0x47, 0x65, 0x74, 0x43, 0x6c, 0x75, - 0x73, 0x74, 0x65, 0x72, 0x73, 0x42, 0x61, 0x73, 0x69, 0x63, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x17, 0x0a, 0x07, 0x74, 0x65, 0x61, 0x6d, 0x5f, 0x69, 0x64, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x74, 0x65, 0x61, 0x6d, 0x49, 0x64, 0x12, 0x23, - 0x0a, 0x0d, 0x77, 0x69, 0x74, 0x68, 0x5f, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x77, 0x69, 0x74, 0x68, 0x50, 0x72, 0x6f, 0x76, 0x69, - 0x64, 0x65, 0x72, 0x22, 0x4b, 0x0a, 0x1c, 0x47, 0x65, 0x74, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, - 0x72, 0x73, 0x42, 0x61, 0x73, 0x69, 0x63, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x12, 0x2b, 0x0a, 0x08, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x73, 0x18, - 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x43, - 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x52, 0x08, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x73, - 0x22, 0x54, 0x0a, 0x1a, 0x47, 0x65, 0x74, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x42, 0x61, - 0x73, 0x69, 0x63, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x17, - 0x0a, 0x07, 0x74, 0x65, 0x61, 0x6d, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x06, 0x74, 0x65, 0x61, 0x6d, 0x49, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x6c, 0x75, 0x73, 0x74, - 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x6c, 0x75, - 0x73, 0x74, 0x65, 0x72, 0x49, 0x64, 0x22, 0x48, 0x0a, 0x1b, 0x47, 0x65, 0x74, 0x43, 0x6c, 0x75, - 0x73, 0x74, 0x65, 0x72, 0x42, 0x61, 0x73, 0x69, 0x63, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x29, 0x0a, 0x07, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, - 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x52, 0x07, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, - 0x22, 0xe3, 0x02, 0x0a, 0x1d, 0x47, 0x65, 0x74, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x73, - 0x57, 0x69, 0x74, 0x68, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x12, 0x17, 0x0a, 0x07, 0x74, 0x65, 0x61, 0x6d, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x06, 0x74, 0x65, 0x61, 0x6d, 0x49, 0x64, 0x12, 0x3e, 0x0a, 0x0a, 0x73, - 0x74, 0x61, 0x72, 0x74, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, - 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x48, 0x00, 0x52, 0x09, 0x73, - 0x74, 0x61, 0x72, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x88, 0x01, 0x01, 0x12, 0x3a, 0x0a, 0x08, 0x65, - 0x6e, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, - 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, - 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x48, 0x01, 0x52, 0x07, 0x65, 0x6e, 0x64, - 0x54, 0x69, 0x6d, 0x65, 0x88, 0x01, 0x01, 0x12, 0x24, 0x0a, 0x0b, 0x62, 0x75, 0x63, 0x6b, 0x65, - 0x74, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x05, 0x48, 0x02, 0x52, 0x0a, - 0x62, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x53, 0x69, 0x7a, 0x65, 0x88, 0x01, 0x01, 0x12, 0x28, 0x0a, - 0x10, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x6e, 0x61, 0x6d, 0x65, - 0x73, 0x18, 0x0e, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0e, 0x6e, 0x6f, 0x64, 0x65, 0x47, 0x72, 0x6f, - 0x75, 0x70, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x12, 0x31, 0x0a, 0x14, 0x69, 0x6e, 0x63, 0x6c, 0x75, - 0x64, 0x65, 0x5f, 0x64, 0x69, 0x73, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x65, 0x64, 0x18, - 0x0f, 0x20, 0x01, 0x28, 0x08, 0x52, 0x13, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x44, 0x69, - 0x73, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x65, 0x64, 0x42, 0x0d, 0x0a, 0x0b, 0x5f, 0x73, - 0x74, 0x61, 0x72, 0x74, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x42, 0x0b, 0x0a, 0x09, 0x5f, 0x65, 0x6e, - 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x42, 0x0e, 0x0a, 0x0c, 0x5f, 0x62, 0x75, 0x63, 0x6b, 0x65, - 0x74, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x22, 0xfd, 0x02, 0x0a, 0x1e, 0x47, 0x65, 0x74, 0x43, 0x6c, - 0x75, 0x73, 0x74, 0x65, 0x72, 0x73, 0x57, 0x69, 0x74, 0x68, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, - 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2b, 0x0a, 0x08, 0x63, 0x6c, 0x75, - 0x73, 0x74, 0x65, 0x72, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x61, 0x70, - 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x52, 0x08, 0x63, 0x6c, - 0x75, 0x73, 0x74, 0x65, 0x72, 0x73, 0x12, 0x42, 0x0a, 0x10, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, - 0x63, 0x65, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x17, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, - 0x63, 0x65, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x52, 0x0f, 0x72, 0x65, 0x73, 0x6f, 0x75, - 0x72, 0x63, 0x65, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x12, 0x2d, 0x0a, 0x09, 0x63, 0x6f, - 0x73, 0x74, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, - 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x73, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, - 0x08, 0x63, 0x6f, 0x73, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x2d, 0x0a, 0x09, 0x6e, 0x6f, 0x64, - 0x65, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x61, - 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x08, - 0x6e, 0x6f, 0x64, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x3f, 0x0a, 0x10, 0x63, 0x6f, 0x73, 0x74, - 0x5f, 0x64, 0x61, 0x74, 0x61, 0x5f, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x18, 0x05, 0x20, 0x03, - 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x73, 0x74, - 0x44, 0x61, 0x74, 0x61, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x52, 0x0e, 0x63, 0x6f, 0x73, 0x74, 0x44, - 0x61, 0x74, 0x61, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x12, 0x4b, 0x0a, 0x14, 0x72, 0x65, 0x73, - 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x5f, 0x70, 0x6f, 0x69, 0x6e, 0x74, - 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, - 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x44, 0x61, 0x74, 0x61, 0x50, 0x6f, 0x69, - 0x6e, 0x74, 0x52, 0x12, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x44, 0x61, 0x74, 0x61, - 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x22, 0xde, 0x01, 0x0a, 0x1e, 0x47, 0x65, 0x74, 0x43, 0x6c, - 0x75, 0x73, 0x74, 0x65, 0x72, 0x73, 0x44, 0x65, 0x6c, 0x74, 0x61, 0x4d, 0x65, 0x74, 0x72, 0x69, - 0x63, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x17, 0x0a, 0x07, 0x74, 0x65, 0x61, - 0x6d, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x74, 0x65, 0x61, 0x6d, - 0x49, 0x64, 0x12, 0x39, 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x74, 0x69, 0x6d, 0x65, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, - 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, - 0x6d, 0x70, 0x52, 0x09, 0x73, 0x74, 0x61, 0x72, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x35, 0x0a, - 0x08, 0x65, 0x6e, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, - 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x07, 0x65, 0x6e, 0x64, - 0x54, 0x69, 0x6d, 0x65, 0x12, 0x31, 0x0a, 0x14, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x5f, - 0x64, 0x69, 0x73, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x65, 0x64, 0x18, 0x04, 0x20, 0x01, - 0x28, 0x08, 0x52, 0x13, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x44, 0x69, 0x73, 0x63, 0x6f, - 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x65, 0x64, 0x22, 0x98, 0x01, 0x0a, 0x13, 0x43, 0x6c, 0x75, 0x73, - 0x74, 0x65, 0x72, 0x44, 0x65, 0x6c, 0x74, 0x61, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x12, - 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, - 0x2d, 0x0a, 0x09, 0x63, 0x6f, 0x73, 0x74, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x73, 0x74, - 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x08, 0x63, 0x6f, 0x73, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x42, - 0x0a, 0x10, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, - 0x63, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, - 0x31, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, - 0x73, 0x52, 0x0f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x4d, 0x65, 0x74, 0x72, 0x69, - 0x63, 0x73, 0x22, 0x5a, 0x0a, 0x1f, 0x47, 0x65, 0x74, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, - 0x73, 0x44, 0x65, 0x6c, 0x74, 0x61, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x37, 0x0a, 0x08, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, - 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, - 0x2e, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x44, 0x65, 0x6c, 0x74, 0x61, 0x4d, 0x65, 0x74, - 0x72, 0x69, 0x63, 0x73, 0x52, 0x08, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x73, 0x22, 0x61, - 0x0a, 0x19, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x54, - 0x6f, 0x6b, 0x65, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x21, 0x0a, 0x0c, 0x63, - 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x0b, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x21, - 0x0a, 0x0c, 0x6b, 0x38, 0x73, 0x5f, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x6b, 0x38, 0x73, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, - 0x72, 0x22, 0x51, 0x0a, 0x1a, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x43, 0x6c, 0x75, 0x73, 0x74, - 0x65, 0x72, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, - 0x14, 0x0a, 0x05, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, - 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, - 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x6c, 0x75, 0x73, 0x74, - 0x65, 0x72, 0x49, 0x64, 0x22, 0xae, 0x02, 0x0a, 0x1d, 0x47, 0x65, 0x74, 0x4e, 0x65, 0x74, 0x77, - 0x6f, 0x72, 0x6b, 0x44, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x6e, 0x63, 0x69, 0x65, 0x73, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, - 0x72, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x6c, 0x75, 0x73, - 0x74, 0x65, 0x72, 0x49, 0x64, 0x12, 0x39, 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x74, - 0x69, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, - 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, - 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x73, 0x74, 0x61, 0x72, 0x74, 0x54, 0x69, 0x6d, 0x65, - 0x12, 0x35, 0x0a, 0x08, 0x65, 0x6e, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x07, - 0x65, 0x6e, 0x64, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x25, 0x0a, 0x0e, 0x73, 0x72, 0x63, 0x5f, 0x6e, - 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x09, 0x52, - 0x0d, 0x73, 0x72, 0x63, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x12, 0x26, - 0x0a, 0x0f, 0x73, 0x72, 0x63, 0x5f, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x67, 0x72, 0x6f, 0x75, 0x70, - 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0d, 0x73, 0x72, 0x63, 0x4e, 0x6f, 0x64, 0x65, - 0x47, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x18, - 0x06, 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x12, 0x17, 0x0a, 0x07, - 0x72, 0x61, 0x6e, 0x6b, 0x5f, 0x62, 0x79, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x72, - 0x61, 0x6e, 0x6b, 0x42, 0x79, 0x22, 0x76, 0x0a, 0x0c, 0x57, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, - 0x64, 0x4e, 0x6f, 0x64, 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x77, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, - 0x64, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x77, 0x6f, - 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x6e, 0x61, - 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6e, - 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x77, 0x6f, 0x72, 0x6b, - 0x6c, 0x6f, 0x61, 0x64, 0x5f, 0x6b, 0x69, 0x6e, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x0c, 0x77, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x4b, 0x69, 0x6e, 0x64, 0x22, 0x36, 0x0a, - 0x0c, 0x45, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x4e, 0x6f, 0x64, 0x65, 0x12, 0x16, 0x0a, - 0x06, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x64, - 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x70, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x02, 0x69, 0x70, 0x22, 0x4e, 0x0a, 0x0b, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, - 0x4e, 0x6f, 0x64, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x5f, - 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x73, 0x65, 0x72, 0x76, - 0x69, 0x63, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, - 0x70, 0x61, 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6e, 0x61, 0x6d, 0x65, - 0x73, 0x70, 0x61, 0x63, 0x65, 0x22, 0x60, 0x0a, 0x11, 0x43, 0x6c, 0x6f, 0x75, 0x64, 0x52, 0x65, - 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x4e, 0x6f, 0x64, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x64, 0x6f, - 0x6d, 0x61, 0x69, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x64, 0x6f, 0x6d, 0x61, - 0x69, 0x6e, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x70, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, - 0x69, 0x70, 0x12, 0x23, 0x0a, 0x0d, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x6b, - 0x69, 0x6e, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x72, 0x65, 0x73, 0x6f, 0x75, - 0x72, 0x63, 0x65, 0x4b, 0x69, 0x6e, 0x64, 0x22, 0xc2, 0x03, 0x0a, 0x0e, 0x44, 0x65, 0x70, 0x65, - 0x6e, 0x64, 0x65, 0x6e, 0x63, 0x79, 0x45, 0x64, 0x67, 0x65, 0x12, 0x37, 0x0a, 0x0c, 0x73, 0x72, - 0x63, 0x5f, 0x77, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x14, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, - 0x61, 0x64, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x0b, 0x73, 0x72, 0x63, 0x57, 0x6f, 0x72, 0x6b, 0x6c, - 0x6f, 0x61, 0x64, 0x12, 0x37, 0x0a, 0x0c, 0x64, 0x73, 0x74, 0x5f, 0x77, 0x6f, 0x72, 0x6b, 0x6c, - 0x6f, 0x61, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x61, 0x70, 0x69, 0x2e, - 0x76, 0x31, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x4e, 0x6f, 0x64, 0x65, 0x52, - 0x0b, 0x64, 0x73, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x12, 0x37, 0x0a, 0x0c, - 0x64, 0x73, 0x74, 0x5f, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x18, 0x03, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x78, 0x74, 0x65, - 0x72, 0x6e, 0x61, 0x6c, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x0b, 0x64, 0x73, 0x74, 0x45, 0x78, 0x74, - 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x12, 0x34, 0x0a, 0x0b, 0x64, 0x73, 0x74, 0x5f, 0x73, 0x65, 0x72, - 0x76, 0x69, 0x63, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x61, 0x70, 0x69, - 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x4e, 0x6f, 0x64, 0x65, 0x52, - 0x0a, 0x64, 0x73, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x47, 0x0a, 0x12, 0x64, - 0x73, 0x74, 0x5f, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x5f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, - 0x65, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, - 0x2e, 0x43, 0x6c, 0x6f, 0x75, 0x64, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x4e, 0x6f, - 0x64, 0x65, 0x52, 0x10, 0x64, 0x73, 0x74, 0x43, 0x6c, 0x6f, 0x75, 0x64, 0x52, 0x65, 0x73, 0x6f, - 0x75, 0x72, 0x63, 0x65, 0x12, 0x19, 0x0a, 0x08, 0x64, 0x73, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, - 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x64, 0x73, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, - 0x21, 0x0a, 0x0c, 0x74, 0x72, 0x61, 0x66, 0x66, 0x69, 0x63, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, - 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x74, 0x72, 0x61, 0x66, 0x66, 0x69, 0x63, 0x54, 0x79, - 0x70, 0x65, 0x12, 0x19, 0x0a, 0x08, 0x74, 0x78, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x18, 0x05, - 0x20, 0x01, 0x28, 0x03, 0x52, 0x07, 0x74, 0x78, 0x42, 0x79, 0x74, 0x65, 0x73, 0x12, 0x19, 0x0a, - 0x08, 0x72, 0x78, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x03, 0x52, - 0x07, 0x72, 0x78, 0x42, 0x79, 0x74, 0x65, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x63, 0x6f, 0x73, 0x74, - 0x18, 0x07, 0x20, 0x01, 0x28, 0x01, 0x52, 0x04, 0x63, 0x6f, 0x73, 0x74, 0x22, 0x4e, 0x0a, 0x1e, - 0x47, 0x65, 0x74, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x44, 0x65, 0x70, 0x65, 0x6e, 0x64, - 0x65, 0x6e, 0x63, 0x69, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2c, - 0x0a, 0x05, 0x65, 0x64, 0x67, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x16, 0x2e, - 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x6e, 0x63, - 0x79, 0x45, 0x64, 0x67, 0x65, 0x52, 0x05, 0x65, 0x64, 0x67, 0x65, 0x73, 0x22, 0xce, 0x02, 0x0a, - 0x22, 0x47, 0x65, 0x74, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x4d, 0x65, 0x74, 0x72, 0x69, - 0x63, 0x73, 0x54, 0x69, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x69, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x69, - 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, - 0x49, 0x64, 0x12, 0x39, 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x74, 0x69, 0x6d, 0x65, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, - 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, - 0x6d, 0x70, 0x52, 0x09, 0x73, 0x74, 0x61, 0x72, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x35, 0x0a, - 0x08, 0x65, 0x6e, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, - 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x07, 0x65, 0x6e, 0x64, - 0x54, 0x69, 0x6d, 0x65, 0x12, 0x25, 0x0a, 0x0e, 0x73, 0x72, 0x63, 0x5f, 0x6e, 0x61, 0x6d, 0x65, - 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0d, 0x73, 0x72, - 0x63, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x12, 0x23, 0x0a, 0x0d, 0x73, - 0x72, 0x63, 0x5f, 0x77, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x73, 0x18, 0x05, 0x20, 0x03, - 0x28, 0x09, 0x52, 0x0c, 0x73, 0x72, 0x63, 0x57, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x73, - 0x12, 0x23, 0x0a, 0x0d, 0x74, 0x72, 0x61, 0x66, 0x66, 0x69, 0x63, 0x5f, 0x74, 0x79, 0x70, 0x65, - 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0c, 0x74, 0x72, 0x61, 0x66, 0x66, 0x69, 0x63, - 0x54, 0x79, 0x70, 0x65, 0x73, 0x12, 0x26, 0x0a, 0x0f, 0x6d, 0x61, 0x78, 0x5f, 0x64, 0x61, 0x74, - 0x61, 0x5f, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x18, 0x07, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0d, - 0x6d, 0x61, 0x78, 0x44, 0x61, 0x74, 0x61, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x22, 0x8e, 0x01, - 0x0a, 0x23, 0x47, 0x65, 0x74, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x4d, 0x65, 0x74, 0x72, - 0x69, 0x63, 0x73, 0x54, 0x69, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x69, 0x65, 0x73, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x39, 0x0a, 0x07, 0x62, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x73, - 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, - 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x54, 0x69, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x69, 0x65, - 0x73, 0x42, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x52, 0x07, 0x62, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x73, - 0x12, 0x2c, 0x0a, 0x05, 0x7a, 0x6f, 0x6e, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, - 0x16, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x53, 0x65, 0x72, - 0x69, 0x65, 0x73, 0x5a, 0x6f, 0x6e, 0x65, 0x52, 0x05, 0x7a, 0x6f, 0x6e, 0x65, 0x73, 0x22, 0xc4, - 0x01, 0x0a, 0x0e, 0x54, 0x69, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x69, 0x65, 0x73, 0x5a, 0x6f, 0x6e, - 0x65, 0x12, 0x39, 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, - 0x70, 0x52, 0x09, 0x73, 0x74, 0x61, 0x72, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x35, 0x0a, 0x08, - 0x65, 0x6e, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, - 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, - 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x07, 0x65, 0x6e, 0x64, 0x54, - 0x69, 0x6d, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x62, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x5f, 0x73, 0x69, - 0x7a, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x62, 0x75, 0x63, 0x6b, 0x65, 0x74, - 0x53, 0x69, 0x7a, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x5f, 0x63, 0x6f, - 0x75, 0x6e, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0a, 0x70, 0x6f, 0x69, 0x6e, 0x74, - 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x22, 0xbd, 0x04, 0x0a, 0x17, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, - 0x6b, 0x54, 0x69, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x69, 0x65, 0x73, 0x42, 0x75, 0x63, 0x6b, 0x65, - 0x74, 0x12, 0x38, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, - 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x21, 0x0a, 0x0c, 0x74, - 0x72, 0x61, 0x66, 0x66, 0x69, 0x63, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x0b, 0x74, 0x72, 0x61, 0x66, 0x66, 0x69, 0x63, 0x54, 0x79, 0x70, 0x65, 0x12, 0x19, - 0x0a, 0x08, 0x74, 0x78, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, - 0x52, 0x07, 0x74, 0x78, 0x42, 0x79, 0x74, 0x65, 0x73, 0x12, 0x19, 0x0a, 0x08, 0x72, 0x78, 0x5f, - 0x62, 0x79, 0x74, 0x65, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x07, 0x72, 0x78, 0x42, - 0x79, 0x74, 0x65, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x63, 0x6f, 0x73, 0x74, 0x18, 0x05, 0x20, 0x01, - 0x28, 0x01, 0x52, 0x04, 0x63, 0x6f, 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x74, 0x78, 0x5f, 0x70, - 0x61, 0x63, 0x6b, 0x65, 0x74, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x74, 0x78, - 0x50, 0x61, 0x63, 0x6b, 0x65, 0x74, 0x73, 0x12, 0x1d, 0x0a, 0x0a, 0x72, 0x78, 0x5f, 0x70, 0x61, - 0x63, 0x6b, 0x65, 0x74, 0x73, 0x18, 0x07, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x72, 0x78, 0x50, - 0x61, 0x63, 0x6b, 0x65, 0x74, 0x73, 0x12, 0x21, 0x0a, 0x0c, 0x73, 0x61, 0x6d, 0x70, 0x6c, 0x65, - 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x08, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0b, 0x73, 0x61, - 0x6d, 0x70, 0x6c, 0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x51, 0x0a, 0x14, 0x74, 0x78, 0x5f, - 0x62, 0x79, 0x74, 0x65, 0x73, 0x5f, 0x70, 0x65, 0x72, 0x63, 0x65, 0x6e, 0x74, 0x69, 0x6c, 0x65, - 0x73, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, - 0x2e, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x50, 0x65, 0x72, 0x63, 0x65, 0x6e, 0x74, 0x69, - 0x6c, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x52, 0x12, 0x74, 0x78, 0x42, 0x79, 0x74, 0x65, - 0x73, 0x50, 0x65, 0x72, 0x63, 0x65, 0x6e, 0x74, 0x69, 0x6c, 0x65, 0x73, 0x12, 0x51, 0x0a, 0x14, - 0x72, 0x78, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x5f, 0x70, 0x65, 0x72, 0x63, 0x65, 0x6e, 0x74, - 0x69, 0x6c, 0x65, 0x73, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x61, 0x70, 0x69, - 0x2e, 0x76, 0x31, 0x2e, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x50, 0x65, 0x72, 0x63, 0x65, - 0x6e, 0x74, 0x69, 0x6c, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x52, 0x12, 0x72, 0x78, 0x42, - 0x79, 0x74, 0x65, 0x73, 0x50, 0x65, 0x72, 0x63, 0x65, 0x6e, 0x74, 0x69, 0x6c, 0x65, 0x73, 0x12, - 0x4a, 0x0a, 0x10, 0x63, 0x6f, 0x73, 0x74, 0x5f, 0x70, 0x65, 0x72, 0x63, 0x65, 0x6e, 0x74, 0x69, - 0x6c, 0x65, 0x73, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x61, 0x70, 0x69, 0x2e, - 0x76, 0x31, 0x2e, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x50, 0x65, 0x72, 0x63, 0x65, 0x6e, - 0x74, 0x69, 0x6c, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x52, 0x0f, 0x63, 0x6f, 0x73, 0x74, - 0x50, 0x65, 0x72, 0x63, 0x65, 0x6e, 0x74, 0x69, 0x6c, 0x65, 0x73, 0x12, 0x28, 0x0a, 0x10, 0x7a, - 0x6f, 0x6e, 0x65, 0x5f, 0x62, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x18, - 0x0c, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x7a, 0x6f, 0x6e, 0x65, 0x42, 0x75, 0x63, 0x6b, 0x65, - 0x74, 0x53, 0x69, 0x7a, 0x65, 0x22, 0x73, 0x0a, 0x17, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, - 0x50, 0x65, 0x72, 0x63, 0x65, 0x6e, 0x74, 0x69, 0x6c, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x73, - 0x12, 0x10, 0x0a, 0x03, 0x70, 0x35, 0x30, 0x18, 0x01, 0x20, 0x01, 0x28, 0x01, 0x52, 0x03, 0x70, - 0x35, 0x30, 0x12, 0x10, 0x0a, 0x03, 0x70, 0x37, 0x35, 0x18, 0x02, 0x20, 0x01, 0x28, 0x01, 0x52, - 0x03, 0x70, 0x37, 0x35, 0x12, 0x10, 0x0a, 0x03, 0x70, 0x39, 0x30, 0x18, 0x03, 0x20, 0x01, 0x28, - 0x01, 0x52, 0x03, 0x70, 0x39, 0x30, 0x12, 0x10, 0x0a, 0x03, 0x70, 0x39, 0x35, 0x18, 0x04, 0x20, - 0x01, 0x28, 0x01, 0x52, 0x03, 0x70, 0x39, 0x35, 0x12, 0x10, 0x0a, 0x03, 0x70, 0x39, 0x39, 0x18, - 0x05, 0x20, 0x01, 0x28, 0x01, 0x52, 0x03, 0x70, 0x39, 0x39, 0x22, 0xec, 0x01, 0x0a, 0x18, 0x47, - 0x65, 0x74, 0x4e, 0x6f, 0x64, 0x65, 0x54, 0x79, 0x70, 0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x73, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x17, 0x0a, 0x07, 0x74, 0x65, 0x61, 0x6d, 0x5f, - 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x74, 0x65, 0x61, 0x6d, 0x49, 0x64, - 0x12, 0x1f, 0x0a, 0x0b, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x73, 0x18, - 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0a, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, 0x64, - 0x73, 0x12, 0x3e, 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, - 0x0b, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, - 0x70, 0x48, 0x00, 0x52, 0x09, 0x73, 0x74, 0x61, 0x72, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x88, 0x01, - 0x01, 0x12, 0x3a, 0x0a, 0x08, 0x65, 0x6e, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x0c, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x48, - 0x01, 0x52, 0x07, 0x65, 0x6e, 0x64, 0x54, 0x69, 0x6d, 0x65, 0x88, 0x01, 0x01, 0x42, 0x0d, 0x0a, - 0x0b, 0x5f, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x42, 0x0b, 0x0a, 0x09, - 0x5f, 0x65, 0x6e, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x22, 0x4a, 0x0a, 0x19, 0x47, 0x65, 0x74, - 0x4e, 0x6f, 0x64, 0x65, 0x54, 0x79, 0x70, 0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2d, 0x0a, 0x09, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x69, - 0x6e, 0x66, 0x6f, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x61, 0x70, 0x69, 0x2e, - 0x76, 0x31, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x08, 0x6e, 0x6f, 0x64, - 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x22, 0x80, 0x02, 0x0a, 0x19, 0x47, 0x65, 0x74, 0x43, 0x6c, 0x75, - 0x73, 0x74, 0x65, 0x72, 0x49, 0x44, 0x42, 0x79, 0x4e, 0x61, 0x6d, 0x65, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x12, 0x17, 0x0a, 0x07, 0x74, 0x65, 0x61, 0x6d, 0x5f, 0x69, 0x64, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x74, 0x65, 0x61, 0x6d, 0x49, 0x64, 0x12, 0x12, 0x0a, 0x04, - 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, - 0x12, 0x1b, 0x0a, 0x06, 0x72, 0x65, 0x67, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, - 0x48, 0x00, 0x52, 0x06, 0x72, 0x65, 0x67, 0x69, 0x6f, 0x6e, 0x88, 0x01, 0x01, 0x12, 0x2a, 0x0a, - 0x0e, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x5f, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x18, - 0x04, 0x20, 0x01, 0x28, 0x09, 0x48, 0x01, 0x52, 0x0d, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x50, 0x72, - 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x88, 0x01, 0x01, 0x12, 0x42, 0x0a, 0x08, 0x6c, 0x69, 0x76, - 0x65, 0x6e, 0x65, 0x73, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x21, 0x2e, 0x61, 0x70, - 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x4c, 0x69, 0x76, 0x65, - 0x6e, 0x65, 0x73, 0x73, 0x50, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x48, 0x02, - 0x52, 0x08, 0x6c, 0x69, 0x76, 0x65, 0x6e, 0x65, 0x73, 0x73, 0x88, 0x01, 0x01, 0x42, 0x09, 0x0a, - 0x07, 0x5f, 0x72, 0x65, 0x67, 0x69, 0x6f, 0x6e, 0x42, 0x11, 0x0a, 0x0f, 0x5f, 0x63, 0x6c, 0x6f, - 0x75, 0x64, 0x5f, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x42, 0x0b, 0x0a, 0x09, 0x5f, - 0x6c, 0x69, 0x76, 0x65, 0x6e, 0x65, 0x73, 0x73, 0x22, 0x2c, 0x0a, 0x1a, 0x47, 0x65, 0x74, 0x43, - 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, 0x44, 0x42, 0x79, 0x4e, 0x61, 0x6d, 0x65, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x22, 0x4a, 0x0a, 0x1b, 0x47, 0x65, 0x74, 0x43, 0x6c, 0x75, - 0x73, 0x74, 0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x42, 0x79, 0x4e, 0x61, 0x6d, 0x65, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x17, 0x0a, 0x07, 0x74, 0x65, 0x61, 0x6d, 0x5f, 0x69, 0x64, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x74, 0x65, 0x61, 0x6d, 0x49, 0x64, 0x12, 0x12, - 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, - 0x6d, 0x65, 0x22, 0x49, 0x0a, 0x1c, 0x47, 0x65, 0x74, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, - 0x49, 0x6e, 0x66, 0x6f, 0x42, 0x79, 0x4e, 0x61, 0x6d, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x12, 0x29, 0x0a, 0x07, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6c, 0x75, - 0x73, 0x74, 0x65, 0x72, 0x52, 0x07, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x2a, 0xcb, 0x01, - 0x0a, 0x19, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x4c, 0x69, 0x76, 0x65, 0x6e, 0x65, 0x73, - 0x73, 0x50, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x12, 0x2b, 0x0a, 0x27, 0x43, - 0x4c, 0x55, 0x53, 0x54, 0x45, 0x52, 0x5f, 0x4c, 0x49, 0x56, 0x45, 0x4e, 0x45, 0x53, 0x53, 0x5f, - 0x50, 0x52, 0x45, 0x46, 0x45, 0x52, 0x45, 0x4e, 0x43, 0x45, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, - 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x26, 0x0a, 0x22, 0x43, 0x4c, 0x55, 0x53, - 0x54, 0x45, 0x52, 0x5f, 0x4c, 0x49, 0x56, 0x45, 0x4e, 0x45, 0x53, 0x53, 0x5f, 0x50, 0x52, 0x45, - 0x46, 0x45, 0x52, 0x45, 0x4e, 0x43, 0x45, 0x5f, 0x49, 0x47, 0x4e, 0x4f, 0x52, 0x45, 0x10, 0x01, - 0x12, 0x2b, 0x0a, 0x27, 0x43, 0x4c, 0x55, 0x53, 0x54, 0x45, 0x52, 0x5f, 0x4c, 0x49, 0x56, 0x45, - 0x4e, 0x45, 0x53, 0x53, 0x5f, 0x50, 0x52, 0x45, 0x46, 0x45, 0x52, 0x45, 0x4e, 0x43, 0x45, 0x5f, - 0x50, 0x52, 0x45, 0x46, 0x45, 0x52, 0x5f, 0x4c, 0x49, 0x56, 0x45, 0x10, 0x02, 0x12, 0x2c, 0x0a, - 0x28, 0x43, 0x4c, 0x55, 0x53, 0x54, 0x45, 0x52, 0x5f, 0x4c, 0x49, 0x56, 0x45, 0x4e, 0x45, 0x53, - 0x53, 0x5f, 0x50, 0x52, 0x45, 0x46, 0x45, 0x52, 0x45, 0x4e, 0x43, 0x45, 0x5f, 0x52, 0x45, 0x51, - 0x55, 0x49, 0x52, 0x45, 0x5f, 0x4c, 0x49, 0x56, 0x45, 0x10, 0x03, 0x32, 0x80, 0x08, 0x0a, 0x0e, - 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x61, - 0x0a, 0x14, 0x47, 0x65, 0x74, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x73, 0x42, 0x61, 0x73, - 0x69, 0x63, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x23, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, - 0x47, 0x65, 0x74, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x73, 0x42, 0x61, 0x73, 0x69, 0x63, - 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x24, 0x2e, 0x61, 0x70, - 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x73, - 0x42, 0x61, 0x73, 0x69, 0x63, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x12, 0x5e, 0x0a, 0x13, 0x47, 0x65, 0x74, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x42, - 0x61, 0x73, 0x69, 0x63, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x22, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, - 0x31, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x42, 0x61, 0x73, 0x69, - 0x63, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x61, - 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, - 0x42, 0x61, 0x73, 0x69, 0x63, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x12, 0x67, 0x0a, 0x16, 0x47, 0x65, 0x74, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x73, - 0x57, 0x69, 0x74, 0x68, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x12, 0x25, 0x2e, 0x61, 0x70, - 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x73, - 0x57, 0x69, 0x74, 0x68, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x26, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x43, - 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x73, 0x57, 0x69, 0x74, 0x68, 0x4d, 0x65, 0x74, 0x72, 0x69, - 0x63, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x5b, 0x0a, 0x12, 0x43, 0x72, - 0x65, 0x61, 0x74, 0x65, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x54, 0x6f, 0x6b, 0x65, 0x6e, - 0x12, 0x21, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, - 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x72, 0x65, - 0x61, 0x74, 0x65, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x6a, 0x0a, 0x17, 0x47, 0x65, 0x74, 0x43, 0x6c, - 0x75, 0x73, 0x74, 0x65, 0x72, 0x73, 0x44, 0x65, 0x6c, 0x74, 0x61, 0x4d, 0x65, 0x74, 0x72, 0x69, - 0x63, 0x73, 0x12, 0x26, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x43, - 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x73, 0x44, 0x65, 0x6c, 0x74, 0x61, 0x4d, 0x65, 0x74, 0x72, - 0x69, 0x63, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x27, 0x2e, 0x61, 0x70, 0x69, - 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x73, 0x44, - 0x65, 0x6c, 0x74, 0x61, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x12, 0x67, 0x0a, 0x16, 0x47, 0x65, 0x74, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, - 0x6b, 0x44, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x6e, 0x63, 0x69, 0x65, 0x73, 0x12, 0x25, 0x2e, - 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, - 0x6b, 0x44, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x6e, 0x63, 0x69, 0x65, 0x73, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x26, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, - 0x74, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x44, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x6e, - 0x63, 0x69, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x76, 0x0a, 0x1b, - 0x47, 0x65, 0x74, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, - 0x73, 0x54, 0x69, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x69, 0x65, 0x73, 0x12, 0x2a, 0x2e, 0x61, 0x70, - 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x4d, - 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x54, 0x69, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x69, 0x65, 0x73, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2b, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, - 0x2e, 0x47, 0x65, 0x74, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x4d, 0x65, 0x74, 0x72, 0x69, - 0x63, 0x73, 0x54, 0x69, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x69, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x58, 0x0a, 0x11, 0x47, 0x65, 0x74, 0x4e, 0x6f, 0x64, 0x65, 0x54, - 0x79, 0x70, 0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x12, 0x20, 0x2e, 0x61, 0x70, 0x69, 0x2e, - 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x4e, 0x6f, 0x64, 0x65, 0x54, 0x79, 0x70, 0x65, 0x43, 0x6f, - 0x75, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x61, 0x70, - 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x4e, 0x6f, 0x64, 0x65, 0x54, 0x79, 0x70, 0x65, - 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x5b, - 0x0a, 0x12, 0x47, 0x65, 0x74, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, 0x44, 0x42, 0x79, - 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x21, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, - 0x74, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, 0x44, 0x42, 0x79, 0x4e, 0x61, 0x6d, 0x65, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, - 0x2e, 0x47, 0x65, 0x74, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, 0x44, 0x42, 0x79, 0x4e, - 0x61, 0x6d, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x61, 0x0a, 0x14, 0x47, - 0x65, 0x74, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x42, 0x79, 0x4e, - 0x61, 0x6d, 0x65, 0x12, 0x23, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, - 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x42, 0x79, 0x4e, 0x61, 0x6d, - 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x24, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, - 0x31, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f, - 0x42, 0x79, 0x4e, 0x61, 0x6d, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x85, - 0x01, 0x0a, 0x0a, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x42, 0x0c, 0x43, - 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x30, 0x67, - 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x64, 0x65, 0x76, 0x7a, 0x65, 0x72, - 0x6f, 0x2d, 0x69, 0x6e, 0x63, 0x2f, 0x7a, 0x78, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x72, 0x2f, 0x67, - 0x65, 0x6e, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x3b, 0x61, 0x70, 0x69, 0x76, 0x31, 0xa2, - 0x02, 0x03, 0x41, 0x58, 0x58, 0xaa, 0x02, 0x06, 0x41, 0x70, 0x69, 0x2e, 0x56, 0x31, 0xca, 0x02, - 0x06, 0x41, 0x70, 0x69, 0x5c, 0x56, 0x31, 0xe2, 0x02, 0x12, 0x41, 0x70, 0x69, 0x5c, 0x56, 0x31, - 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x07, 0x41, - 0x70, 0x69, 0x3a, 0x3a, 0x56, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + TeamId string `protobuf:"bytes,1,opt,name=team_id,json=teamId,proto3" json:"team_id,omitempty"` + IncludeDisconnected bool `protobuf:"varint,2,opt,name=include_disconnected,json=includeDisconnected,proto3" json:"include_disconnected,omitempty"` } -var ( - file_api_v1_cluster_proto_rawDescOnce sync.Once - file_api_v1_cluster_proto_rawDescData = file_api_v1_cluster_proto_rawDesc -) +func (x *GetClustersCurrentStateRequest) Reset() { + *x = GetClustersCurrentStateRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_api_v1_cluster_proto_msgTypes[29] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} -func file_api_v1_cluster_proto_rawDescGZIP() []byte { - file_api_v1_cluster_proto_rawDescOnce.Do(func() { - file_api_v1_cluster_proto_rawDescData = protoimpl.X.CompressGZIP(file_api_v1_cluster_proto_rawDescData) - }) - return file_api_v1_cluster_proto_rawDescData +func (x *GetClustersCurrentStateRequest) String() string { + return protoimpl.X.MessageStringOf(x) } -var file_api_v1_cluster_proto_enumTypes = make([]protoimpl.EnumInfo, 1) -var file_api_v1_cluster_proto_msgTypes = make([]protoimpl.MessageInfo, 29) -var file_api_v1_cluster_proto_goTypes = []interface{}{ - (ClusterLivenessPreference)(0), // 0: api.v1.ClusterLivenessPreference - (*GetClustersBasicInfoRequest)(nil), // 1: api.v1.GetClustersBasicInfoRequest - (*GetClustersBasicInfoResponse)(nil), // 2: api.v1.GetClustersBasicInfoResponse - (*GetClusterBasicInfoRequest)(nil), // 3: api.v1.GetClusterBasicInfoRequest - (*GetClusterBasicInfoResponse)(nil), // 4: api.v1.GetClusterBasicInfoResponse - (*GetClustersWithMetricsRequest)(nil), // 5: api.v1.GetClustersWithMetricsRequest - (*GetClustersWithMetricsResponse)(nil), // 6: api.v1.GetClustersWithMetricsResponse - (*GetClustersDeltaMetricsRequest)(nil), // 7: api.v1.GetClustersDeltaMetricsRequest - (*ClusterDeltaMetrics)(nil), // 8: api.v1.ClusterDeltaMetrics - (*GetClustersDeltaMetricsResponse)(nil), // 9: api.v1.GetClustersDeltaMetricsResponse - (*CreateClusterTokenRequest)(nil), // 10: api.v1.CreateClusterTokenRequest - (*CreateClusterTokenResponse)(nil), // 11: api.v1.CreateClusterTokenResponse - (*GetNetworkDependenciesRequest)(nil), // 12: api.v1.GetNetworkDependenciesRequest - (*WorkloadNode)(nil), // 13: api.v1.WorkloadNode - (*ExternalNode)(nil), // 14: api.v1.ExternalNode - (*ServiceNode)(nil), // 15: api.v1.ServiceNode - (*CloudResourceNode)(nil), // 16: api.v1.CloudResourceNode - (*DependencyEdge)(nil), // 17: api.v1.DependencyEdge - (*GetNetworkDependenciesResponse)(nil), // 18: api.v1.GetNetworkDependenciesResponse - (*GetNetworkMetricsTimeSeriesRequest)(nil), // 19: api.v1.GetNetworkMetricsTimeSeriesRequest - (*GetNetworkMetricsTimeSeriesResponse)(nil), // 20: api.v1.GetNetworkMetricsTimeSeriesResponse - (*TimeSeriesZone)(nil), // 21: api.v1.TimeSeriesZone - (*NetworkTimeSeriesBucket)(nil), // 22: api.v1.NetworkTimeSeriesBucket - (*NetworkPercentileValues)(nil), // 23: api.v1.NetworkPercentileValues - (*GetNodeTypeCountsRequest)(nil), // 24: api.v1.GetNodeTypeCountsRequest - (*GetNodeTypeCountsResponse)(nil), // 25: api.v1.GetNodeTypeCountsResponse - (*GetClusterIDByNameRequest)(nil), // 26: api.v1.GetClusterIDByNameRequest - (*GetClusterIDByNameResponse)(nil), // 27: api.v1.GetClusterIDByNameResponse - (*GetClusterInfoByNameRequest)(nil), // 28: api.v1.GetClusterInfoByNameRequest - (*GetClusterInfoByNameResponse)(nil), // 29: api.v1.GetClusterInfoByNameResponse - (*Cluster)(nil), // 30: api.v1.Cluster - (*timestamppb.Timestamp)(nil), // 31: google.protobuf.Timestamp - (*ResourceMetrics)(nil), // 32: api.v1.ResourceMetrics - (*CostInfo)(nil), // 33: api.v1.CostInfo - (*NodeInfo)(nil), // 34: api.v1.NodeInfo - (*CostDataPoint)(nil), // 35: api.v1.CostDataPoint - (*ResourceDataPoint)(nil), // 36: api.v1.ResourceDataPoint +func (*GetClustersCurrentStateRequest) ProtoMessage() {} + +func (x *GetClustersCurrentStateRequest) ProtoReflect() protoreflect.Message { + mi := &file_api_v1_cluster_proto_msgTypes[29] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) } -var file_api_v1_cluster_proto_depIdxs = []int32{ - 30, // 0: api.v1.GetClustersBasicInfoResponse.clusters:type_name -> api.v1.Cluster - 30, // 1: api.v1.GetClusterBasicInfoResponse.cluster:type_name -> api.v1.Cluster - 31, // 2: api.v1.GetClustersWithMetricsRequest.start_time:type_name -> google.protobuf.Timestamp - 31, // 3: api.v1.GetClustersWithMetricsRequest.end_time:type_name -> google.protobuf.Timestamp - 30, // 4: api.v1.GetClustersWithMetricsResponse.clusters:type_name -> api.v1.Cluster - 32, // 5: api.v1.GetClustersWithMetricsResponse.resource_metrics:type_name -> api.v1.ResourceMetrics - 33, // 6: api.v1.GetClustersWithMetricsResponse.cost_info:type_name -> api.v1.CostInfo - 34, // 7: api.v1.GetClustersWithMetricsResponse.node_info:type_name -> api.v1.NodeInfo - 35, // 8: api.v1.GetClustersWithMetricsResponse.cost_data_points:type_name -> api.v1.CostDataPoint - 36, // 9: api.v1.GetClustersWithMetricsResponse.resource_data_points:type_name -> api.v1.ResourceDataPoint - 31, // 10: api.v1.GetClustersDeltaMetricsRequest.start_time:type_name -> google.protobuf.Timestamp - 31, // 11: api.v1.GetClustersDeltaMetricsRequest.end_time:type_name -> google.protobuf.Timestamp - 33, // 12: api.v1.ClusterDeltaMetrics.cost_info:type_name -> api.v1.CostInfo - 32, // 13: api.v1.ClusterDeltaMetrics.resource_metrics:type_name -> api.v1.ResourceMetrics - 8, // 14: api.v1.GetClustersDeltaMetricsResponse.clusters:type_name -> api.v1.ClusterDeltaMetrics - 31, // 15: api.v1.GetNetworkDependenciesRequest.start_time:type_name -> google.protobuf.Timestamp - 31, // 16: api.v1.GetNetworkDependenciesRequest.end_time:type_name -> google.protobuf.Timestamp - 13, // 17: api.v1.DependencyEdge.src_workload:type_name -> api.v1.WorkloadNode - 13, // 18: api.v1.DependencyEdge.dst_workload:type_name -> api.v1.WorkloadNode - 14, // 19: api.v1.DependencyEdge.dst_external:type_name -> api.v1.ExternalNode - 15, // 20: api.v1.DependencyEdge.dst_service:type_name -> api.v1.ServiceNode - 16, // 21: api.v1.DependencyEdge.dst_cloud_resource:type_name -> api.v1.CloudResourceNode - 17, // 22: api.v1.GetNetworkDependenciesResponse.edges:type_name -> api.v1.DependencyEdge - 31, // 23: api.v1.GetNetworkMetricsTimeSeriesRequest.start_time:type_name -> google.protobuf.Timestamp - 31, // 24: api.v1.GetNetworkMetricsTimeSeriesRequest.end_time:type_name -> google.protobuf.Timestamp - 22, // 25: api.v1.GetNetworkMetricsTimeSeriesResponse.buckets:type_name -> api.v1.NetworkTimeSeriesBucket - 21, // 26: api.v1.GetNetworkMetricsTimeSeriesResponse.zones:type_name -> api.v1.TimeSeriesZone - 31, // 27: api.v1.TimeSeriesZone.start_time:type_name -> google.protobuf.Timestamp - 31, // 28: api.v1.TimeSeriesZone.end_time:type_name -> google.protobuf.Timestamp - 31, // 29: api.v1.NetworkTimeSeriesBucket.timestamp:type_name -> google.protobuf.Timestamp - 23, // 30: api.v1.NetworkTimeSeriesBucket.tx_bytes_percentiles:type_name -> api.v1.NetworkPercentileValues - 23, // 31: api.v1.NetworkTimeSeriesBucket.rx_bytes_percentiles:type_name -> api.v1.NetworkPercentileValues - 23, // 32: api.v1.NetworkTimeSeriesBucket.cost_percentiles:type_name -> api.v1.NetworkPercentileValues - 31, // 33: api.v1.GetNodeTypeCountsRequest.start_time:type_name -> google.protobuf.Timestamp - 31, // 34: api.v1.GetNodeTypeCountsRequest.end_time:type_name -> google.protobuf.Timestamp - 34, // 35: api.v1.GetNodeTypeCountsResponse.node_info:type_name -> api.v1.NodeInfo - 0, // 36: api.v1.GetClusterIDByNameRequest.liveness:type_name -> api.v1.ClusterLivenessPreference - 30, // 37: api.v1.GetClusterInfoByNameResponse.cluster:type_name -> api.v1.Cluster - 1, // 38: api.v1.ClusterService.GetClustersBasicInfo:input_type -> api.v1.GetClustersBasicInfoRequest - 3, // 39: api.v1.ClusterService.GetClusterBasicInfo:input_type -> api.v1.GetClusterBasicInfoRequest - 5, // 40: api.v1.ClusterService.GetClustersWithMetrics:input_type -> api.v1.GetClustersWithMetricsRequest - 10, // 41: api.v1.ClusterService.CreateClusterToken:input_type -> api.v1.CreateClusterTokenRequest - 7, // 42: api.v1.ClusterService.GetClustersDeltaMetrics:input_type -> api.v1.GetClustersDeltaMetricsRequest - 12, // 43: api.v1.ClusterService.GetNetworkDependencies:input_type -> api.v1.GetNetworkDependenciesRequest - 19, // 44: api.v1.ClusterService.GetNetworkMetricsTimeSeries:input_type -> api.v1.GetNetworkMetricsTimeSeriesRequest - 24, // 45: api.v1.ClusterService.GetNodeTypeCounts:input_type -> api.v1.GetNodeTypeCountsRequest - 26, // 46: api.v1.ClusterService.GetClusterIDByName:input_type -> api.v1.GetClusterIDByNameRequest - 28, // 47: api.v1.ClusterService.GetClusterInfoByName:input_type -> api.v1.GetClusterInfoByNameRequest - 2, // 48: api.v1.ClusterService.GetClustersBasicInfo:output_type -> api.v1.GetClustersBasicInfoResponse - 4, // 49: api.v1.ClusterService.GetClusterBasicInfo:output_type -> api.v1.GetClusterBasicInfoResponse - 6, // 50: api.v1.ClusterService.GetClustersWithMetrics:output_type -> api.v1.GetClustersWithMetricsResponse - 11, // 51: api.v1.ClusterService.CreateClusterToken:output_type -> api.v1.CreateClusterTokenResponse - 9, // 52: api.v1.ClusterService.GetClustersDeltaMetrics:output_type -> api.v1.GetClustersDeltaMetricsResponse - 18, // 53: api.v1.ClusterService.GetNetworkDependencies:output_type -> api.v1.GetNetworkDependenciesResponse - 20, // 54: api.v1.ClusterService.GetNetworkMetricsTimeSeries:output_type -> api.v1.GetNetworkMetricsTimeSeriesResponse - 25, // 55: api.v1.ClusterService.GetNodeTypeCounts:output_type -> api.v1.GetNodeTypeCountsResponse - 27, // 56: api.v1.ClusterService.GetClusterIDByName:output_type -> api.v1.GetClusterIDByNameResponse - 29, // 57: api.v1.ClusterService.GetClusterInfoByName:output_type -> api.v1.GetClusterInfoByNameResponse - 48, // [48:58] is the sub-list for method output_type - 38, // [38:48] is the sub-list for method input_type - 38, // [38:38] is the sub-list for extension type_name - 38, // [38:38] is the sub-list for extension extendee - 0, // [0:38] is the sub-list for field type_name + +// Deprecated: Use GetClustersCurrentStateRequest.ProtoReflect.Descriptor instead. +func (*GetClustersCurrentStateRequest) Descriptor() ([]byte, []int) { + return file_api_v1_cluster_proto_rawDescGZIP(), []int{29} } -func init() { file_api_v1_cluster_proto_init() } -func file_api_v1_cluster_proto_init() { - if File_api_v1_cluster_proto != nil { - return +func (x *GetClustersCurrentStateRequest) GetTeamId() string { + if x != nil { + return x.TeamId } - file_api_v1_common_proto_init() - file_api_v1_k8s_proto_init() - if !protoimpl.UnsafeEnabled { - file_api_v1_cluster_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetClustersBasicInfoRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_api_v1_cluster_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetClustersBasicInfoResponse); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_api_v1_cluster_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetClusterBasicInfoRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache + return "" +} + +func (x *GetClustersCurrentStateRequest) GetIncludeDisconnected() bool { + if x != nil { + return x.IncludeDisconnected + } + return false +} + +type GetClustersCurrentStateResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Clusters []*ClusterCurrentState `protobuf:"bytes,1,rep,name=clusters,proto3" json:"clusters,omitempty"` +} + +func (x *GetClustersCurrentStateResponse) Reset() { + *x = GetClustersCurrentStateResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_api_v1_cluster_proto_msgTypes[30] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetClustersCurrentStateResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetClustersCurrentStateResponse) ProtoMessage() {} + +func (x *GetClustersCurrentStateResponse) ProtoReflect() protoreflect.Message { + mi := &file_api_v1_cluster_proto_msgTypes[30] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetClustersCurrentStateResponse.ProtoReflect.Descriptor instead. +func (*GetClustersCurrentStateResponse) Descriptor() ([]byte, []int) { + return file_api_v1_cluster_proto_rawDescGZIP(), []int{30} +} + +func (x *GetClustersCurrentStateResponse) GetClusters() []*ClusterCurrentState { + if x != nil { + return x.Clusters + } + return nil +} + +type ClusterCurrentState struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` + DisplayName string `protobuf:"bytes,3,opt,name=display_name,json=displayName,proto3" json:"display_name,omitempty"` + CloudProvider string `protobuf:"bytes,4,opt,name=cloud_provider,json=cloudProvider,proto3" json:"cloud_provider,omitempty"` // aws | azure | gcp | oci + Region string `protobuf:"bytes,5,opt,name=region,proto3" json:"region,omitempty"` + CreatedAt int64 `protobuf:"varint,6,opt,name=created_at,json=createdAt,proto3" json:"created_at,omitempty"` // Unix seconds; cluster row creation, not first heartbeat. + KubernetesVersion string `protobuf:"bytes,7,opt,name=kubernetes_version,json=kubernetesVersion,proto3" json:"kubernetes_version,omitempty"` + Tags []string `protobuf:"bytes,8,rep,name=tags,proto3" json:"tags,omitempty"` + IsDisconnected bool `protobuf:"varint,9,opt,name=is_disconnected,json=isDisconnected,proto3" json:"is_disconnected,omitempty"` + AutoscalerType string `protobuf:"bytes,10,opt,name=autoscaler_type,json=autoscalerType,proto3" json:"autoscaler_type,omitempty"` // The cluster's autoscaler from its stored detection: karpenter | cluster_autoscaler | cast_ai | transitioning (two or more at once) | unmanaged (none observed). Empty until the first detection. + AutoscalerDetectedAt *timestamppb.Timestamp `protobuf:"bytes,11,opt,name=autoscaler_detected_at,json=autoscalerDetectedAt,proto3" json:"autoscaler_detected_at,omitempty"` // When that detection ran: hourly while the cluster is connected; a disconnected cluster keeps its last. Unset until the first detection. + // Unset when the cluster is disconnected or its inventory holds no live node, whatever the + // usage minutes say: absent means no data, never zero. + Snapshot *ClusterResourceSnapshot `protobuf:"bytes,20,opt,name=snapshot,proto3,oneof" json:"snapshot,omitempty"` +} + +func (x *ClusterCurrentState) Reset() { + *x = ClusterCurrentState{} + if protoimpl.UnsafeEnabled { + mi := &file_api_v1_cluster_proto_msgTypes[31] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ClusterCurrentState) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ClusterCurrentState) ProtoMessage() {} + +func (x *ClusterCurrentState) ProtoReflect() protoreflect.Message { + mi := &file_api_v1_cluster_proto_msgTypes[31] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ClusterCurrentState.ProtoReflect.Descriptor instead. +func (*ClusterCurrentState) Descriptor() ([]byte, []int) { + return file_api_v1_cluster_proto_rawDescGZIP(), []int{31} +} + +func (x *ClusterCurrentState) GetId() string { + if x != nil { + return x.Id + } + return "" +} + +func (x *ClusterCurrentState) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *ClusterCurrentState) GetDisplayName() string { + if x != nil { + return x.DisplayName + } + return "" +} + +func (x *ClusterCurrentState) GetCloudProvider() string { + if x != nil { + return x.CloudProvider + } + return "" +} + +func (x *ClusterCurrentState) GetRegion() string { + if x != nil { + return x.Region + } + return "" +} + +func (x *ClusterCurrentState) GetCreatedAt() int64 { + if x != nil { + return x.CreatedAt + } + return 0 +} + +func (x *ClusterCurrentState) GetKubernetesVersion() string { + if x != nil { + return x.KubernetesVersion + } + return "" +} + +func (x *ClusterCurrentState) GetTags() []string { + if x != nil { + return x.Tags + } + return nil +} + +func (x *ClusterCurrentState) GetIsDisconnected() bool { + if x != nil { + return x.IsDisconnected + } + return false +} + +func (x *ClusterCurrentState) GetAutoscalerType() string { + if x != nil { + return x.AutoscalerType + } + return "" +} + +func (x *ClusterCurrentState) GetAutoscalerDetectedAt() *timestamppb.Timestamp { + if x != nil { + return x.AutoscalerDetectedAt + } + return nil +} + +func (x *ClusterCurrentState) GetSnapshot() *ClusterResourceSnapshot { + if x != nil { + return x.Snapshot + } + return nil +} + +// ClusterResourceSnapshot: what exists now, plus the newest usable usage minute. +type ClusterResourceSnapshot struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Live nodes now, from the node inventory (Postgres k8s_nodes, deleted_at IS NULL): the + // same rollup GetClusterNodeInventory totals, never a ClickHouse minute. + // The two halves of this message are bounded differently. node_info and the capacities + // below have no time bound of their own: they are the inventory as the collector last left + // it, bounded only by the cluster still counting as connected, which allows up to 60 + // minutes without a heartbeat; a disconnected cluster, listed under include_disconnected, + // carries no snapshot at all. usage is bounded at 15 minutes. A cluster that went quiet 20 + // minutes ago therefore shows its last known nodes and capacity with no usage: node_info is + // not evidence that the cluster is reporting now. + NodeInfo *NodeInfo `protobuf:"bytes,2,opt,name=node_info,json=nodeInfo,proto3" json:"node_info,omitempty"` + CpuCapacityCores float64 `protobuf:"fixed64,3,opt,name=cpu_capacity_cores,json=cpuCapacityCores,proto3" json:"cpu_capacity_cores,omitempty"` + MemoryCapacityBytes float64 `protobuf:"fixed64,6,opt,name=memory_capacity_bytes,json=memoryCapacityBytes,proto3" json:"memory_capacity_bytes,omitempty"` + // GPUs on live nodes, from the node inventory: the node's nvidia.com/gpu capacity, as the + // scheduler counts them (a time-sliced GPU once per replica), or, for a node that advertises + // none, Karpenter's instance GPU count. 0 when none. + GpuCapacity float64 `protobuf:"fixed64,9,opt,name=gpu_capacity,json=gpuCapacity,proto3" json:"gpu_capacity,omitempty"` + // The newest ClickHouse minute at least two minutes old inside the freshness window. Unset + // when there is none: the page shows capacity and "no recent usage", never zeros. + Usage *ClusterUsageSnapshot `protobuf:"bytes,10,opt,name=usage,proto3,oneof" json:"usage,omitempty"` +} + +func (x *ClusterResourceSnapshot) Reset() { + *x = ClusterResourceSnapshot{} + if protoimpl.UnsafeEnabled { + mi := &file_api_v1_cluster_proto_msgTypes[32] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ClusterResourceSnapshot) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ClusterResourceSnapshot) ProtoMessage() {} + +func (x *ClusterResourceSnapshot) ProtoReflect() protoreflect.Message { + mi := &file_api_v1_cluster_proto_msgTypes[32] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ClusterResourceSnapshot.ProtoReflect.Descriptor instead. +func (*ClusterResourceSnapshot) Descriptor() ([]byte, []int) { + return file_api_v1_cluster_proto_rawDescGZIP(), []int{32} +} + +func (x *ClusterResourceSnapshot) GetNodeInfo() *NodeInfo { + if x != nil { + return x.NodeInfo + } + return nil +} + +func (x *ClusterResourceSnapshot) GetCpuCapacityCores() float64 { + if x != nil { + return x.CpuCapacityCores + } + return 0 +} + +func (x *ClusterResourceSnapshot) GetMemoryCapacityBytes() float64 { + if x != nil { + return x.MemoryCapacityBytes + } + return 0 +} + +func (x *ClusterResourceSnapshot) GetGpuCapacity() float64 { + if x != nil { + return x.GpuCapacity + } + return 0 +} + +func (x *ClusterResourceSnapshot) GetUsage() *ClusterUsageSnapshot { + if x != nil { + return x.Usage + } + return nil +} + +type ClusterUsageSnapshot struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + ObservedAt *timestamppb.Timestamp `protobuf:"bytes,1,opt,name=observed_at,json=observedAt,proto3" json:"observed_at,omitempty"` // The minute the figures belong to. + // Nodes with a row in that minute. The minute and the inventory are minutes apart, so this is + // below node_info's total while the collector is still sending and above it after a scale-down; + // the page states either mismatch rather than showing the minute's fleet as the fleet. + NodesReporting int32 `protobuf:"varint,2,opt,name=nodes_reporting,json=nodesReporting,proto3" json:"nodes_reporting,omitempty"` + CpuRequestedCores float64 `protobuf:"fixed64,3,opt,name=cpu_requested_cores,json=cpuRequestedCores,proto3" json:"cpu_requested_cores,omitempty"` + CpuUsedCores float64 `protobuf:"fixed64,4,opt,name=cpu_used_cores,json=cpuUsedCores,proto3" json:"cpu_used_cores,omitempty"` + MemoryRequestedBytes float64 `protobuf:"fixed64,5,opt,name=memory_requested_bytes,json=memoryRequestedBytes,proto3" json:"memory_requested_bytes,omitempty"` + MemoryUsedBytes float64 `protobuf:"fixed64,6,opt,name=memory_used_bytes,json=memoryUsedBytes,proto3" json:"memory_used_bytes,omitempty"` + // GPUs (nvidia.com/gpu) requested by live pods in that minute, in gpu_capacity's units. + GpuRequested float64 `protobuf:"fixed64,7,opt,name=gpu_requested,json=gpuRequested,proto3" json:"gpu_requested,omitempty"` + // Physical GPUs' worth busy in that minute, from DCGM utilisation, which MIG-partitioned GPUs + // do not report. Read against gpu_devices less gpu_devices_mig. + GpuUsed float64 `protobuf:"fixed64,8,opt,name=gpu_used,json=gpuUsed,proto3" json:"gpu_used,omitempty"` + // Physical GPUs that reported in that minute, each counted once whatever its MIG slices or + // time-sliced replicas. 0 when none did: gpu_used is then no reading, not idle. + GpuDevices float64 `protobuf:"fixed64,9,opt,name=gpu_devices,json=gpuDevices,proto3" json:"gpu_devices,omitempty"` + // Physical GPUs in gpu_devices that are MIG-partitioned. DCGM reports no utilisation for them, so + // gpu_used leaves them out. + GpuDevicesMig float64 `protobuf:"fixed64,10,opt,name=gpu_devices_mig,json=gpuDevicesMig,proto3" json:"gpu_devices_mig,omitempty"` +} + +func (x *ClusterUsageSnapshot) Reset() { + *x = ClusterUsageSnapshot{} + if protoimpl.UnsafeEnabled { + mi := &file_api_v1_cluster_proto_msgTypes[33] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ClusterUsageSnapshot) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ClusterUsageSnapshot) ProtoMessage() {} + +func (x *ClusterUsageSnapshot) ProtoReflect() protoreflect.Message { + mi := &file_api_v1_cluster_proto_msgTypes[33] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ClusterUsageSnapshot.ProtoReflect.Descriptor instead. +func (*ClusterUsageSnapshot) Descriptor() ([]byte, []int) { + return file_api_v1_cluster_proto_rawDescGZIP(), []int{33} +} + +func (x *ClusterUsageSnapshot) GetObservedAt() *timestamppb.Timestamp { + if x != nil { + return x.ObservedAt + } + return nil +} + +func (x *ClusterUsageSnapshot) GetNodesReporting() int32 { + if x != nil { + return x.NodesReporting + } + return 0 +} + +func (x *ClusterUsageSnapshot) GetCpuRequestedCores() float64 { + if x != nil { + return x.CpuRequestedCores + } + return 0 +} + +func (x *ClusterUsageSnapshot) GetCpuUsedCores() float64 { + if x != nil { + return x.CpuUsedCores + } + return 0 +} + +func (x *ClusterUsageSnapshot) GetMemoryRequestedBytes() float64 { + if x != nil { + return x.MemoryRequestedBytes + } + return 0 +} + +func (x *ClusterUsageSnapshot) GetMemoryUsedBytes() float64 { + if x != nil { + return x.MemoryUsedBytes + } + return 0 +} + +func (x *ClusterUsageSnapshot) GetGpuRequested() float64 { + if x != nil { + return x.GpuRequested + } + return 0 +} + +func (x *ClusterUsageSnapshot) GetGpuUsed() float64 { + if x != nil { + return x.GpuUsed + } + return 0 +} + +func (x *ClusterUsageSnapshot) GetGpuDevices() float64 { + if x != nil { + return x.GpuDevices + } + return 0 +} + +func (x *ClusterUsageSnapshot) GetGpuDevicesMig() float64 { + if x != nil { + return x.GpuDevicesMig + } + return 0 +} + +type GetTeamTimeSeriesRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + TeamId string `protobuf:"bytes,1,opt,name=team_id,json=teamId,proto3" json:"team_id,omitempty"` + StartTime *timestamppb.Timestamp `protobuf:"bytes,2,opt,name=start_time,json=startTime,proto3" json:"start_time,omitempty"` + EndTime *timestamppb.Timestamp `protobuf:"bytes,3,opt,name=end_time,json=endTime,proto3" json:"end_time,omitempty"` + IncludeDisconnected bool `protobuf:"varint,4,opt,name=include_disconnected,json=includeDisconnected,proto3" json:"include_disconnected,omitempty"` +} + +func (x *GetTeamTimeSeriesRequest) Reset() { + *x = GetTeamTimeSeriesRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_api_v1_cluster_proto_msgTypes[34] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetTeamTimeSeriesRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetTeamTimeSeriesRequest) ProtoMessage() {} + +func (x *GetTeamTimeSeriesRequest) ProtoReflect() protoreflect.Message { + mi := &file_api_v1_cluster_proto_msgTypes[34] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetTeamTimeSeriesRequest.ProtoReflect.Descriptor instead. +func (*GetTeamTimeSeriesRequest) Descriptor() ([]byte, []int) { + return file_api_v1_cluster_proto_rawDescGZIP(), []int{34} +} + +func (x *GetTeamTimeSeriesRequest) GetTeamId() string { + if x != nil { + return x.TeamId + } + return "" +} + +func (x *GetTeamTimeSeriesRequest) GetStartTime() *timestamppb.Timestamp { + if x != nil { + return x.StartTime + } + return nil +} + +func (x *GetTeamTimeSeriesRequest) GetEndTime() *timestamppb.Timestamp { + if x != nil { + return x.EndTime + } + return nil +} + +func (x *GetTeamTimeSeriesRequest) GetIncludeDisconnected() bool { + if x != nil { + return x.IncludeDisconnected + } + return false +} + +type GetTeamTimeSeriesResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Points []*TeamTimeSeriesPoint `protobuf:"bytes,1,rep,name=points,proto3" json:"points,omitempty"` // Ascending by timestamp. +} + +func (x *GetTeamTimeSeriesResponse) Reset() { + *x = GetTeamTimeSeriesResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_api_v1_cluster_proto_msgTypes[35] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetTeamTimeSeriesResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetTeamTimeSeriesResponse) ProtoMessage() {} + +func (x *GetTeamTimeSeriesResponse) ProtoReflect() protoreflect.Message { + mi := &file_api_v1_cluster_proto_msgTypes[35] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetTeamTimeSeriesResponse.ProtoReflect.Descriptor instead. +func (*GetTeamTimeSeriesResponse) Descriptor() ([]byte, []int) { + return file_api_v1_cluster_proto_rawDescGZIP(), []int{35} +} + +func (x *GetTeamTimeSeriesResponse) GetPoints() []*TeamTimeSeriesPoint { + if x != nil { + return x.Points + } + return nil +} + +type TeamTimeSeriesPoint struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Timestamp int64 `protobuf:"varint,1,opt,name=timestamp,proto3" json:"timestamp,omitempty"` // Unix seconds, bucket start. + BucketSeconds int32 `protobuf:"varint,2,opt,name=bucket_seconds,json=bucketSeconds,proto3" json:"bucket_seconds,omitempty"` // Width of this bucket; the edges of a long range use minute buckets. + CpuCapacityCores float64 `protobuf:"fixed64,3,opt,name=cpu_capacity_cores,json=cpuCapacityCores,proto3" json:"cpu_capacity_cores,omitempty"` + CpuRequestedCores float64 `protobuf:"fixed64,4,opt,name=cpu_requested_cores,json=cpuRequestedCores,proto3" json:"cpu_requested_cores,omitempty"` + CpuUsedCores float64 `protobuf:"fixed64,5,opt,name=cpu_used_cores,json=cpuUsedCores,proto3" json:"cpu_used_cores,omitempty"` + MoneyCost float64 `protobuf:"fixed64,6,opt,name=money_cost,json=moneyCost,proto3" json:"money_cost,omitempty"` // Cost incurred inside the bucket. +} + +func (x *TeamTimeSeriesPoint) Reset() { + *x = TeamTimeSeriesPoint{} + if protoimpl.UnsafeEnabled { + mi := &file_api_v1_cluster_proto_msgTypes[36] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *TeamTimeSeriesPoint) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*TeamTimeSeriesPoint) ProtoMessage() {} + +func (x *TeamTimeSeriesPoint) ProtoReflect() protoreflect.Message { + mi := &file_api_v1_cluster_proto_msgTypes[36] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use TeamTimeSeriesPoint.ProtoReflect.Descriptor instead. +func (*TeamTimeSeriesPoint) Descriptor() ([]byte, []int) { + return file_api_v1_cluster_proto_rawDescGZIP(), []int{36} +} + +func (x *TeamTimeSeriesPoint) GetTimestamp() int64 { + if x != nil { + return x.Timestamp + } + return 0 +} + +func (x *TeamTimeSeriesPoint) GetBucketSeconds() int32 { + if x != nil { + return x.BucketSeconds + } + return 0 +} + +func (x *TeamTimeSeriesPoint) GetCpuCapacityCores() float64 { + if x != nil { + return x.CpuCapacityCores + } + return 0 +} + +func (x *TeamTimeSeriesPoint) GetCpuRequestedCores() float64 { + if x != nil { + return x.CpuRequestedCores + } + return 0 +} + +func (x *TeamTimeSeriesPoint) GetCpuUsedCores() float64 { + if x != nil { + return x.CpuUsedCores + } + return 0 +} + +func (x *TeamTimeSeriesPoint) GetMoneyCost() float64 { + if x != nil { + return x.MoneyCost + } + return 0 +} + +type GetClustersPeriodDeltasRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + TeamId string `protobuf:"bytes,1,opt,name=team_id,json=teamId,proto3" json:"team_id,omitempty"` + StartTime *timestamppb.Timestamp `protobuf:"bytes,2,opt,name=start_time,json=startTime,proto3" json:"start_time,omitempty"` // The current period. The previous period has the same length and ends at start_time. + EndTime *timestamppb.Timestamp `protobuf:"bytes,3,opt,name=end_time,json=endTime,proto3" json:"end_time,omitempty"` + IncludeDisconnected bool `protobuf:"varint,4,opt,name=include_disconnected,json=includeDisconnected,proto3" json:"include_disconnected,omitempty"` +} + +func (x *GetClustersPeriodDeltasRequest) Reset() { + *x = GetClustersPeriodDeltasRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_api_v1_cluster_proto_msgTypes[37] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetClustersPeriodDeltasRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetClustersPeriodDeltasRequest) ProtoMessage() {} + +func (x *GetClustersPeriodDeltasRequest) ProtoReflect() protoreflect.Message { + mi := &file_api_v1_cluster_proto_msgTypes[37] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetClustersPeriodDeltasRequest.ProtoReflect.Descriptor instead. +func (*GetClustersPeriodDeltasRequest) Descriptor() ([]byte, []int) { + return file_api_v1_cluster_proto_rawDescGZIP(), []int{37} +} + +func (x *GetClustersPeriodDeltasRequest) GetTeamId() string { + if x != nil { + return x.TeamId + } + return "" +} + +func (x *GetClustersPeriodDeltasRequest) GetStartTime() *timestamppb.Timestamp { + if x != nil { + return x.StartTime + } + return nil +} + +func (x *GetClustersPeriodDeltasRequest) GetEndTime() *timestamppb.Timestamp { + if x != nil { + return x.EndTime + } + return nil +} + +func (x *GetClustersPeriodDeltasRequest) GetIncludeDisconnected() bool { + if x != nil { + return x.IncludeDisconnected + } + return false +} + +// Both sides of every pair are averages over the minutes of their period that carry data, so a +// cluster that reported for part of a period reads its rate while it reported. +type ClusterPeriodDelta struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + ClusterId string `protobuf:"bytes,1,opt,name=cluster_id,json=clusterId,proto3" json:"cluster_id,omitempty"` + MoneyCostPerHourCurrent float64 `protobuf:"fixed64,2,opt,name=money_cost_per_hour_current,json=moneyCostPerHourCurrent,proto3" json:"money_cost_per_hour_current,omitempty"` + MoneyCostPerHourPrevious float64 `protobuf:"fixed64,3,opt,name=money_cost_per_hour_previous,json=moneyCostPerHourPrevious,proto3" json:"money_cost_per_hour_previous,omitempty"` + CpuRequestedCoresCurrent float64 `protobuf:"fixed64,4,opt,name=cpu_requested_cores_current,json=cpuRequestedCoresCurrent,proto3" json:"cpu_requested_cores_current,omitempty"` + CpuRequestedCoresPrevious float64 `protobuf:"fixed64,5,opt,name=cpu_requested_cores_previous,json=cpuRequestedCoresPrevious,proto3" json:"cpu_requested_cores_previous,omitempty"` + MemoryRequestedBytesCurrent float64 `protobuf:"fixed64,6,opt,name=memory_requested_bytes_current,json=memoryRequestedBytesCurrent,proto3" json:"memory_requested_bytes_current,omitempty"` + MemoryRequestedBytesPrevious float64 `protobuf:"fixed64,7,opt,name=memory_requested_bytes_previous,json=memoryRequestedBytesPrevious,proto3" json:"memory_requested_bytes_previous,omitempty"` + DataMinutesCurrent int64 `protobuf:"varint,8,opt,name=data_minutes_current,json=dataMinutesCurrent,proto3" json:"data_minutes_current,omitempty"` // Minutes of the current period that carry data for this cluster. 0 when none did: the current side of every pair is then no reading, not 0. + DataMinutesPrevious int64 `protobuf:"varint,9,opt,name=data_minutes_previous,json=dataMinutesPrevious,proto3" json:"data_minutes_previous,omitempty"` // The same for the previous period. +} + +func (x *ClusterPeriodDelta) Reset() { + *x = ClusterPeriodDelta{} + if protoimpl.UnsafeEnabled { + mi := &file_api_v1_cluster_proto_msgTypes[38] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ClusterPeriodDelta) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ClusterPeriodDelta) ProtoMessage() {} + +func (x *ClusterPeriodDelta) ProtoReflect() protoreflect.Message { + mi := &file_api_v1_cluster_proto_msgTypes[38] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ClusterPeriodDelta.ProtoReflect.Descriptor instead. +func (*ClusterPeriodDelta) Descriptor() ([]byte, []int) { + return file_api_v1_cluster_proto_rawDescGZIP(), []int{38} +} + +func (x *ClusterPeriodDelta) GetClusterId() string { + if x != nil { + return x.ClusterId + } + return "" +} + +func (x *ClusterPeriodDelta) GetMoneyCostPerHourCurrent() float64 { + if x != nil { + return x.MoneyCostPerHourCurrent + } + return 0 +} + +func (x *ClusterPeriodDelta) GetMoneyCostPerHourPrevious() float64 { + if x != nil { + return x.MoneyCostPerHourPrevious + } + return 0 +} + +func (x *ClusterPeriodDelta) GetCpuRequestedCoresCurrent() float64 { + if x != nil { + return x.CpuRequestedCoresCurrent + } + return 0 +} + +func (x *ClusterPeriodDelta) GetCpuRequestedCoresPrevious() float64 { + if x != nil { + return x.CpuRequestedCoresPrevious + } + return 0 +} + +func (x *ClusterPeriodDelta) GetMemoryRequestedBytesCurrent() float64 { + if x != nil { + return x.MemoryRequestedBytesCurrent + } + return 0 +} + +func (x *ClusterPeriodDelta) GetMemoryRequestedBytesPrevious() float64 { + if x != nil { + return x.MemoryRequestedBytesPrevious + } + return 0 +} + +func (x *ClusterPeriodDelta) GetDataMinutesCurrent() int64 { + if x != nil { + return x.DataMinutesCurrent + } + return 0 +} + +func (x *ClusterPeriodDelta) GetDataMinutesPrevious() int64 { + if x != nil { + return x.DataMinutesPrevious + } + return 0 +} + +type GetClustersPeriodDeltasResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Clusters []*ClusterPeriodDelta `protobuf:"bytes,1,rep,name=clusters,proto3" json:"clusters,omitempty"` + TeamMoneyCostCurrent float64 `protobuf:"fixed64,2,opt,name=team_money_cost_current,json=teamMoneyCostCurrent,proto3" json:"team_money_cost_current,omitempty"` // Sum over the current period, for the hero's delta. + TeamMoneyCostPrevious float64 `protobuf:"fixed64,3,opt,name=team_money_cost_previous,json=teamMoneyCostPrevious,proto3" json:"team_money_cost_previous,omitempty"` + // The current period as measured: start_time and end_time on the minute, the end no later than + // the last settled minute. The previous period is as long and ends at current_start_time. Set on + // every answer; a start at or after the end means no part of the range could be measured. + CurrentStartTime *timestamppb.Timestamp `protobuf:"bytes,4,opt,name=current_start_time,json=currentStartTime,proto3" json:"current_start_time,omitempty"` + CurrentEndTime *timestamppb.Timestamp `protobuf:"bytes,5,opt,name=current_end_time,json=currentEndTime,proto3" json:"current_end_time,omitempty"` +} + +func (x *GetClustersPeriodDeltasResponse) Reset() { + *x = GetClustersPeriodDeltasResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_api_v1_cluster_proto_msgTypes[39] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetClustersPeriodDeltasResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetClustersPeriodDeltasResponse) ProtoMessage() {} + +func (x *GetClustersPeriodDeltasResponse) ProtoReflect() protoreflect.Message { + mi := &file_api_v1_cluster_proto_msgTypes[39] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetClustersPeriodDeltasResponse.ProtoReflect.Descriptor instead. +func (*GetClustersPeriodDeltasResponse) Descriptor() ([]byte, []int) { + return file_api_v1_cluster_proto_rawDescGZIP(), []int{39} +} + +func (x *GetClustersPeriodDeltasResponse) GetClusters() []*ClusterPeriodDelta { + if x != nil { + return x.Clusters + } + return nil +} + +func (x *GetClustersPeriodDeltasResponse) GetTeamMoneyCostCurrent() float64 { + if x != nil { + return x.TeamMoneyCostCurrent + } + return 0 +} + +func (x *GetClustersPeriodDeltasResponse) GetTeamMoneyCostPrevious() float64 { + if x != nil { + return x.TeamMoneyCostPrevious + } + return 0 +} + +func (x *GetClustersPeriodDeltasResponse) GetCurrentStartTime() *timestamppb.Timestamp { + if x != nil { + return x.CurrentStartTime + } + return nil +} + +func (x *GetClustersPeriodDeltasResponse) GetCurrentEndTime() *timestamppb.Timestamp { + if x != nil { + return x.CurrentEndTime + } + return nil +} + +var File_api_v1_cluster_proto protoreflect.FileDescriptor + +var file_api_v1_cluster_proto_rawDesc = []byte{ + 0x0a, 0x14, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, + 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x06, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x1a, 0x13, + 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x1a, 0x10, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x6b, 0x38, 0x73, 0x2e, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1f, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, + 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x5b, 0x0a, 0x1b, 0x47, 0x65, 0x74, 0x43, 0x6c, 0x75, + 0x73, 0x74, 0x65, 0x72, 0x73, 0x42, 0x61, 0x73, 0x69, 0x63, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x17, 0x0a, 0x07, 0x74, 0x65, 0x61, 0x6d, 0x5f, 0x69, 0x64, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x74, 0x65, 0x61, 0x6d, 0x49, 0x64, 0x12, 0x23, + 0x0a, 0x0d, 0x77, 0x69, 0x74, 0x68, 0x5f, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x77, 0x69, 0x74, 0x68, 0x50, 0x72, 0x6f, 0x76, 0x69, + 0x64, 0x65, 0x72, 0x22, 0x4b, 0x0a, 0x1c, 0x47, 0x65, 0x74, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, + 0x72, 0x73, 0x42, 0x61, 0x73, 0x69, 0x63, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x12, 0x2b, 0x0a, 0x08, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x73, 0x18, + 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x43, + 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x52, 0x08, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x73, + 0x22, 0x54, 0x0a, 0x1a, 0x47, 0x65, 0x74, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x42, 0x61, + 0x73, 0x69, 0x63, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x17, + 0x0a, 0x07, 0x74, 0x65, 0x61, 0x6d, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x06, 0x74, 0x65, 0x61, 0x6d, 0x49, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x6c, 0x75, 0x73, 0x74, + 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x6c, 0x75, + 0x73, 0x74, 0x65, 0x72, 0x49, 0x64, 0x22, 0x48, 0x0a, 0x1b, 0x47, 0x65, 0x74, 0x43, 0x6c, 0x75, + 0x73, 0x74, 0x65, 0x72, 0x42, 0x61, 0x73, 0x69, 0x63, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x29, 0x0a, 0x07, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, + 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x52, 0x07, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, + 0x22, 0xe3, 0x02, 0x0a, 0x1d, 0x47, 0x65, 0x74, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x73, + 0x57, 0x69, 0x74, 0x68, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x12, 0x17, 0x0a, 0x07, 0x74, 0x65, 0x61, 0x6d, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x06, 0x74, 0x65, 0x61, 0x6d, 0x49, 0x64, 0x12, 0x3e, 0x0a, 0x0a, 0x73, + 0x74, 0x61, 0x72, 0x74, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, + 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x48, 0x00, 0x52, 0x09, 0x73, + 0x74, 0x61, 0x72, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x88, 0x01, 0x01, 0x12, 0x3a, 0x0a, 0x08, 0x65, + 0x6e, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, + 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, + 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x48, 0x01, 0x52, 0x07, 0x65, 0x6e, 0x64, + 0x54, 0x69, 0x6d, 0x65, 0x88, 0x01, 0x01, 0x12, 0x24, 0x0a, 0x0b, 0x62, 0x75, 0x63, 0x6b, 0x65, + 0x74, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x05, 0x48, 0x02, 0x52, 0x0a, + 0x62, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x53, 0x69, 0x7a, 0x65, 0x88, 0x01, 0x01, 0x12, 0x28, 0x0a, + 0x10, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x6e, 0x61, 0x6d, 0x65, + 0x73, 0x18, 0x0e, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0e, 0x6e, 0x6f, 0x64, 0x65, 0x47, 0x72, 0x6f, + 0x75, 0x70, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x12, 0x31, 0x0a, 0x14, 0x69, 0x6e, 0x63, 0x6c, 0x75, + 0x64, 0x65, 0x5f, 0x64, 0x69, 0x73, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x65, 0x64, 0x18, + 0x0f, 0x20, 0x01, 0x28, 0x08, 0x52, 0x13, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x44, 0x69, + 0x73, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x65, 0x64, 0x42, 0x0d, 0x0a, 0x0b, 0x5f, 0x73, + 0x74, 0x61, 0x72, 0x74, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x42, 0x0b, 0x0a, 0x09, 0x5f, 0x65, 0x6e, + 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x42, 0x0e, 0x0a, 0x0c, 0x5f, 0x62, 0x75, 0x63, 0x6b, 0x65, + 0x74, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x22, 0xfd, 0x02, 0x0a, 0x1e, 0x47, 0x65, 0x74, 0x43, 0x6c, + 0x75, 0x73, 0x74, 0x65, 0x72, 0x73, 0x57, 0x69, 0x74, 0x68, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, + 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2b, 0x0a, 0x08, 0x63, 0x6c, 0x75, + 0x73, 0x74, 0x65, 0x72, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x61, 0x70, + 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x52, 0x08, 0x63, 0x6c, + 0x75, 0x73, 0x74, 0x65, 0x72, 0x73, 0x12, 0x42, 0x0a, 0x10, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, + 0x63, 0x65, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x17, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, + 0x63, 0x65, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x52, 0x0f, 0x72, 0x65, 0x73, 0x6f, 0x75, + 0x72, 0x63, 0x65, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x12, 0x2d, 0x0a, 0x09, 0x63, 0x6f, + 0x73, 0x74, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, + 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x73, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, + 0x08, 0x63, 0x6f, 0x73, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x2d, 0x0a, 0x09, 0x6e, 0x6f, 0x64, + 0x65, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x61, + 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x08, + 0x6e, 0x6f, 0x64, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x3f, 0x0a, 0x10, 0x63, 0x6f, 0x73, 0x74, + 0x5f, 0x64, 0x61, 0x74, 0x61, 0x5f, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x18, 0x05, 0x20, 0x03, + 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x73, 0x74, + 0x44, 0x61, 0x74, 0x61, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x52, 0x0e, 0x63, 0x6f, 0x73, 0x74, 0x44, + 0x61, 0x74, 0x61, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x12, 0x4b, 0x0a, 0x14, 0x72, 0x65, 0x73, + 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x5f, 0x70, 0x6f, 0x69, 0x6e, 0x74, + 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, + 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x44, 0x61, 0x74, 0x61, 0x50, 0x6f, 0x69, + 0x6e, 0x74, 0x52, 0x12, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x44, 0x61, 0x74, 0x61, + 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x22, 0xde, 0x01, 0x0a, 0x1e, 0x47, 0x65, 0x74, 0x43, 0x6c, + 0x75, 0x73, 0x74, 0x65, 0x72, 0x73, 0x44, 0x65, 0x6c, 0x74, 0x61, 0x4d, 0x65, 0x74, 0x72, 0x69, + 0x63, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x17, 0x0a, 0x07, 0x74, 0x65, 0x61, + 0x6d, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x74, 0x65, 0x61, 0x6d, + 0x49, 0x64, 0x12, 0x39, 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x74, 0x69, 0x6d, 0x65, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, + 0x6d, 0x70, 0x52, 0x09, 0x73, 0x74, 0x61, 0x72, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x35, 0x0a, + 0x08, 0x65, 0x6e, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, + 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x07, 0x65, 0x6e, 0x64, + 0x54, 0x69, 0x6d, 0x65, 0x12, 0x31, 0x0a, 0x14, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x5f, + 0x64, 0x69, 0x73, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x65, 0x64, 0x18, 0x04, 0x20, 0x01, + 0x28, 0x08, 0x52, 0x13, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x44, 0x69, 0x73, 0x63, 0x6f, + 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x65, 0x64, 0x22, 0x98, 0x01, 0x0a, 0x13, 0x43, 0x6c, 0x75, 0x73, + 0x74, 0x65, 0x72, 0x44, 0x65, 0x6c, 0x74, 0x61, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x12, + 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, + 0x2d, 0x0a, 0x09, 0x63, 0x6f, 0x73, 0x74, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x73, 0x74, + 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x08, 0x63, 0x6f, 0x73, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x42, + 0x0a, 0x10, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, + 0x63, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, + 0x31, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, + 0x73, 0x52, 0x0f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x4d, 0x65, 0x74, 0x72, 0x69, + 0x63, 0x73, 0x22, 0x5a, 0x0a, 0x1f, 0x47, 0x65, 0x74, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, + 0x73, 0x44, 0x65, 0x6c, 0x74, 0x61, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x37, 0x0a, 0x08, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, + 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, + 0x2e, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x44, 0x65, 0x6c, 0x74, 0x61, 0x4d, 0x65, 0x74, + 0x72, 0x69, 0x63, 0x73, 0x52, 0x08, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x73, 0x22, 0xa8, + 0x01, 0x0a, 0x19, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, + 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x21, 0x0a, 0x0c, + 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x0b, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x12, + 0x21, 0x0a, 0x0c, 0x6b, 0x38, 0x73, 0x5f, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x6b, 0x38, 0x73, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, + 0x65, 0x72, 0x12, 0x26, 0x0a, 0x0f, 0x6b, 0x75, 0x62, 0x65, 0x5f, 0x73, 0x79, 0x73, 0x74, 0x65, + 0x6d, 0x5f, 0x75, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x6b, 0x75, 0x62, + 0x65, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x55, 0x69, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x6c, + 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, + 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, 0x64, 0x22, 0x71, 0x0a, 0x1a, 0x43, 0x72, 0x65, + 0x61, 0x74, 0x65, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x6f, 0x6b, 0x65, 0x6e, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x1d, 0x0a, + 0x0a, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x09, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, 0x64, 0x12, 0x1e, 0x0a, 0x0a, + 0x72, 0x65, 0x61, 0x74, 0x74, 0x61, 0x63, 0x68, 0x65, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, + 0x52, 0x0a, 0x72, 0x65, 0x61, 0x74, 0x74, 0x61, 0x63, 0x68, 0x65, 0x64, 0x22, 0xae, 0x02, 0x0a, + 0x1d, 0x47, 0x65, 0x74, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x44, 0x65, 0x70, 0x65, 0x6e, + 0x64, 0x65, 0x6e, 0x63, 0x69, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, + 0x0a, 0x0a, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x09, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, 0x64, 0x12, 0x39, 0x0a, + 0x0a, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x73, + 0x74, 0x61, 0x72, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x35, 0x0a, 0x08, 0x65, 0x6e, 0x64, 0x5f, + 0x74, 0x69, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, + 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, + 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x07, 0x65, 0x6e, 0x64, 0x54, 0x69, 0x6d, 0x65, 0x12, + 0x25, 0x0a, 0x0e, 0x73, 0x72, 0x63, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, + 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0d, 0x73, 0x72, 0x63, 0x4e, 0x61, 0x6d, 0x65, + 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x12, 0x26, 0x0a, 0x0f, 0x73, 0x72, 0x63, 0x5f, 0x6e, 0x6f, + 0x64, 0x65, 0x5f, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x09, 0x52, + 0x0d, 0x73, 0x72, 0x63, 0x4e, 0x6f, 0x64, 0x65, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x12, 0x14, + 0x0a, 0x05, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x6c, + 0x69, 0x6d, 0x69, 0x74, 0x12, 0x17, 0x0a, 0x07, 0x72, 0x61, 0x6e, 0x6b, 0x5f, 0x62, 0x79, 0x18, + 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x72, 0x61, 0x6e, 0x6b, 0x42, 0x79, 0x22, 0x76, 0x0a, + 0x0c, 0x57, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x4e, 0x6f, 0x64, 0x65, 0x12, 0x23, 0x0a, + 0x0d, 0x77, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x77, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x4e, 0x61, + 0x6d, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, + 0x12, 0x23, 0x0a, 0x0d, 0x77, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x5f, 0x6b, 0x69, 0x6e, + 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x77, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, + 0x64, 0x4b, 0x69, 0x6e, 0x64, 0x22, 0x36, 0x0a, 0x0c, 0x45, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, + 0x6c, 0x4e, 0x6f, 0x64, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x12, 0x0e, 0x0a, + 0x02, 0x69, 0x70, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x70, 0x22, 0x4e, 0x0a, + 0x0b, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x4e, 0x6f, 0x64, 0x65, 0x12, 0x21, 0x0a, 0x0c, + 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x0b, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, + 0x1c, 0x0a, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x22, 0x60, 0x0a, + 0x11, 0x43, 0x6c, 0x6f, 0x75, 0x64, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x4e, 0x6f, + 0x64, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x06, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x70, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x70, 0x12, 0x23, 0x0a, 0x0d, 0x72, 0x65, + 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x6b, 0x69, 0x6e, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x0c, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x4b, 0x69, 0x6e, 0x64, 0x22, + 0xc2, 0x03, 0x0a, 0x0e, 0x44, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x6e, 0x63, 0x79, 0x45, 0x64, + 0x67, 0x65, 0x12, 0x37, 0x0a, 0x0c, 0x73, 0x72, 0x63, 0x5f, 0x77, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, + 0x61, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, + 0x31, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x0b, + 0x73, 0x72, 0x63, 0x57, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x12, 0x37, 0x0a, 0x0c, 0x64, + 0x73, 0x74, 0x5f, 0x77, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x14, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x6c, + 0x6f, 0x61, 0x64, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x0b, 0x64, 0x73, 0x74, 0x57, 0x6f, 0x72, 0x6b, + 0x6c, 0x6f, 0x61, 0x64, 0x12, 0x37, 0x0a, 0x0c, 0x64, 0x73, 0x74, 0x5f, 0x65, 0x78, 0x74, 0x65, + 0x72, 0x6e, 0x61, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x61, 0x70, 0x69, + 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x4e, 0x6f, 0x64, 0x65, + 0x52, 0x0b, 0x64, 0x73, 0x74, 0x45, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x12, 0x34, 0x0a, + 0x0b, 0x64, 0x73, 0x74, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x18, 0x08, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x72, 0x76, + 0x69, 0x63, 0x65, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x0a, 0x64, 0x73, 0x74, 0x53, 0x65, 0x72, 0x76, + 0x69, 0x63, 0x65, 0x12, 0x47, 0x0a, 0x12, 0x64, 0x73, 0x74, 0x5f, 0x63, 0x6c, 0x6f, 0x75, 0x64, + 0x5f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x19, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6c, 0x6f, 0x75, 0x64, 0x52, 0x65, + 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x10, 0x64, 0x73, 0x74, 0x43, + 0x6c, 0x6f, 0x75, 0x64, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x19, 0x0a, 0x08, + 0x64, 0x73, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, + 0x64, 0x73, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x74, 0x72, 0x61, 0x66, 0x66, + 0x69, 0x63, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x74, + 0x72, 0x61, 0x66, 0x66, 0x69, 0x63, 0x54, 0x79, 0x70, 0x65, 0x12, 0x19, 0x0a, 0x08, 0x74, 0x78, + 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x07, 0x74, 0x78, + 0x42, 0x79, 0x74, 0x65, 0x73, 0x12, 0x19, 0x0a, 0x08, 0x72, 0x78, 0x5f, 0x62, 0x79, 0x74, 0x65, + 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x03, 0x52, 0x07, 0x72, 0x78, 0x42, 0x79, 0x74, 0x65, 0x73, + 0x12, 0x12, 0x0a, 0x04, 0x63, 0x6f, 0x73, 0x74, 0x18, 0x07, 0x20, 0x01, 0x28, 0x01, 0x52, 0x04, + 0x63, 0x6f, 0x73, 0x74, 0x22, 0x4e, 0x0a, 0x1e, 0x47, 0x65, 0x74, 0x4e, 0x65, 0x74, 0x77, 0x6f, + 0x72, 0x6b, 0x44, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x6e, 0x63, 0x69, 0x65, 0x73, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2c, 0x0a, 0x05, 0x65, 0x64, 0x67, 0x65, 0x73, 0x18, + 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x44, + 0x65, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x6e, 0x63, 0x79, 0x45, 0x64, 0x67, 0x65, 0x52, 0x05, 0x65, + 0x64, 0x67, 0x65, 0x73, 0x22, 0xce, 0x02, 0x0a, 0x22, 0x47, 0x65, 0x74, 0x4e, 0x65, 0x74, 0x77, + 0x6f, 0x72, 0x6b, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x54, 0x69, 0x6d, 0x65, 0x53, 0x65, + 0x72, 0x69, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x63, + 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x09, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, 0x64, 0x12, 0x39, 0x0a, 0x0a, 0x73, 0x74, + 0x61, 0x72, 0x74, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, + 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, + 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x73, 0x74, 0x61, 0x72, + 0x74, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x35, 0x0a, 0x08, 0x65, 0x6e, 0x64, 0x5f, 0x74, 0x69, 0x6d, + 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, + 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, + 0x61, 0x6d, 0x70, 0x52, 0x07, 0x65, 0x6e, 0x64, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x25, 0x0a, 0x0e, + 0x73, 0x72, 0x63, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x18, 0x04, + 0x20, 0x03, 0x28, 0x09, 0x52, 0x0d, 0x73, 0x72, 0x63, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, + 0x63, 0x65, 0x73, 0x12, 0x23, 0x0a, 0x0d, 0x73, 0x72, 0x63, 0x5f, 0x77, 0x6f, 0x72, 0x6b, 0x6c, + 0x6f, 0x61, 0x64, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0c, 0x73, 0x72, 0x63, 0x57, + 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x73, 0x12, 0x23, 0x0a, 0x0d, 0x74, 0x72, 0x61, 0x66, + 0x66, 0x69, 0x63, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x09, 0x52, + 0x0c, 0x74, 0x72, 0x61, 0x66, 0x66, 0x69, 0x63, 0x54, 0x79, 0x70, 0x65, 0x73, 0x12, 0x26, 0x0a, + 0x0f, 0x6d, 0x61, 0x78, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x5f, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, + 0x18, 0x07, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0d, 0x6d, 0x61, 0x78, 0x44, 0x61, 0x74, 0x61, 0x50, + 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x22, 0x8e, 0x01, 0x0a, 0x23, 0x47, 0x65, 0x74, 0x4e, 0x65, 0x74, + 0x77, 0x6f, 0x72, 0x6b, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x54, 0x69, 0x6d, 0x65, 0x53, + 0x65, 0x72, 0x69, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x39, 0x0a, + 0x07, 0x62, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1f, + 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x54, + 0x69, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x69, 0x65, 0x73, 0x42, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x52, + 0x07, 0x62, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x73, 0x12, 0x2c, 0x0a, 0x05, 0x7a, 0x6f, 0x6e, 0x65, + 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, + 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x69, 0x65, 0x73, 0x5a, 0x6f, 0x6e, 0x65, 0x52, + 0x05, 0x7a, 0x6f, 0x6e, 0x65, 0x73, 0x22, 0xc4, 0x01, 0x0a, 0x0e, 0x54, 0x69, 0x6d, 0x65, 0x53, + 0x65, 0x72, 0x69, 0x65, 0x73, 0x5a, 0x6f, 0x6e, 0x65, 0x12, 0x39, 0x0a, 0x0a, 0x73, 0x74, 0x61, + 0x72, 0x74, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, + 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, + 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x73, 0x74, 0x61, 0x72, 0x74, + 0x54, 0x69, 0x6d, 0x65, 0x12, 0x35, 0x0a, 0x08, 0x65, 0x6e, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, + 0x6d, 0x70, 0x52, 0x07, 0x65, 0x6e, 0x64, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x62, + 0x75, 0x63, 0x6b, 0x65, 0x74, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x0a, 0x62, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x53, 0x69, 0x7a, 0x65, 0x12, 0x1f, 0x0a, 0x0b, + 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, + 0x05, 0x52, 0x0a, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x22, 0xbd, 0x04, + 0x0a, 0x17, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x54, 0x69, 0x6d, 0x65, 0x53, 0x65, 0x72, + 0x69, 0x65, 0x73, 0x42, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x12, 0x38, 0x0a, 0x09, 0x74, 0x69, 0x6d, + 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, + 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, + 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, + 0x61, 0x6d, 0x70, 0x12, 0x21, 0x0a, 0x0c, 0x74, 0x72, 0x61, 0x66, 0x66, 0x69, 0x63, 0x5f, 0x74, + 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x74, 0x72, 0x61, 0x66, 0x66, + 0x69, 0x63, 0x54, 0x79, 0x70, 0x65, 0x12, 0x19, 0x0a, 0x08, 0x74, 0x78, 0x5f, 0x62, 0x79, 0x74, + 0x65, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x07, 0x74, 0x78, 0x42, 0x79, 0x74, 0x65, + 0x73, 0x12, 0x19, 0x0a, 0x08, 0x72, 0x78, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x18, 0x04, 0x20, + 0x01, 0x28, 0x03, 0x52, 0x07, 0x72, 0x78, 0x42, 0x79, 0x74, 0x65, 0x73, 0x12, 0x12, 0x0a, 0x04, + 0x63, 0x6f, 0x73, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x01, 0x52, 0x04, 0x63, 0x6f, 0x73, 0x74, + 0x12, 0x1d, 0x0a, 0x0a, 0x74, 0x78, 0x5f, 0x70, 0x61, 0x63, 0x6b, 0x65, 0x74, 0x73, 0x18, 0x06, + 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x74, 0x78, 0x50, 0x61, 0x63, 0x6b, 0x65, 0x74, 0x73, 0x12, + 0x1d, 0x0a, 0x0a, 0x72, 0x78, 0x5f, 0x70, 0x61, 0x63, 0x6b, 0x65, 0x74, 0x73, 0x18, 0x07, 0x20, + 0x01, 0x28, 0x03, 0x52, 0x09, 0x72, 0x78, 0x50, 0x61, 0x63, 0x6b, 0x65, 0x74, 0x73, 0x12, 0x21, + 0x0a, 0x0c, 0x73, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x08, + 0x20, 0x01, 0x28, 0x05, 0x52, 0x0b, 0x73, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x43, 0x6f, 0x75, 0x6e, + 0x74, 0x12, 0x51, 0x0a, 0x14, 0x74, 0x78, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x5f, 0x70, 0x65, + 0x72, 0x63, 0x65, 0x6e, 0x74, 0x69, 0x6c, 0x65, 0x73, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x1f, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, + 0x50, 0x65, 0x72, 0x63, 0x65, 0x6e, 0x74, 0x69, 0x6c, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x73, + 0x52, 0x12, 0x74, 0x78, 0x42, 0x79, 0x74, 0x65, 0x73, 0x50, 0x65, 0x72, 0x63, 0x65, 0x6e, 0x74, + 0x69, 0x6c, 0x65, 0x73, 0x12, 0x51, 0x0a, 0x14, 0x72, 0x78, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, + 0x5f, 0x70, 0x65, 0x72, 0x63, 0x65, 0x6e, 0x74, 0x69, 0x6c, 0x65, 0x73, 0x18, 0x0a, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4e, 0x65, 0x74, 0x77, + 0x6f, 0x72, 0x6b, 0x50, 0x65, 0x72, 0x63, 0x65, 0x6e, 0x74, 0x69, 0x6c, 0x65, 0x56, 0x61, 0x6c, + 0x75, 0x65, 0x73, 0x52, 0x12, 0x72, 0x78, 0x42, 0x79, 0x74, 0x65, 0x73, 0x50, 0x65, 0x72, 0x63, + 0x65, 0x6e, 0x74, 0x69, 0x6c, 0x65, 0x73, 0x12, 0x4a, 0x0a, 0x10, 0x63, 0x6f, 0x73, 0x74, 0x5f, + 0x70, 0x65, 0x72, 0x63, 0x65, 0x6e, 0x74, 0x69, 0x6c, 0x65, 0x73, 0x18, 0x0b, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x1f, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4e, 0x65, 0x74, 0x77, 0x6f, + 0x72, 0x6b, 0x50, 0x65, 0x72, 0x63, 0x65, 0x6e, 0x74, 0x69, 0x6c, 0x65, 0x56, 0x61, 0x6c, 0x75, + 0x65, 0x73, 0x52, 0x0f, 0x63, 0x6f, 0x73, 0x74, 0x50, 0x65, 0x72, 0x63, 0x65, 0x6e, 0x74, 0x69, + 0x6c, 0x65, 0x73, 0x12, 0x28, 0x0a, 0x10, 0x7a, 0x6f, 0x6e, 0x65, 0x5f, 0x62, 0x75, 0x63, 0x6b, + 0x65, 0x74, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x7a, + 0x6f, 0x6e, 0x65, 0x42, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x53, 0x69, 0x7a, 0x65, 0x22, 0x73, 0x0a, + 0x17, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x50, 0x65, 0x72, 0x63, 0x65, 0x6e, 0x74, 0x69, + 0x6c, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x12, 0x10, 0x0a, 0x03, 0x70, 0x35, 0x30, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x01, 0x52, 0x03, 0x70, 0x35, 0x30, 0x12, 0x10, 0x0a, 0x03, 0x70, 0x37, + 0x35, 0x18, 0x02, 0x20, 0x01, 0x28, 0x01, 0x52, 0x03, 0x70, 0x37, 0x35, 0x12, 0x10, 0x0a, 0x03, + 0x70, 0x39, 0x30, 0x18, 0x03, 0x20, 0x01, 0x28, 0x01, 0x52, 0x03, 0x70, 0x39, 0x30, 0x12, 0x10, + 0x0a, 0x03, 0x70, 0x39, 0x35, 0x18, 0x04, 0x20, 0x01, 0x28, 0x01, 0x52, 0x03, 0x70, 0x39, 0x35, + 0x12, 0x10, 0x0a, 0x03, 0x70, 0x39, 0x39, 0x18, 0x05, 0x20, 0x01, 0x28, 0x01, 0x52, 0x03, 0x70, + 0x39, 0x39, 0x22, 0xec, 0x01, 0x0a, 0x18, 0x47, 0x65, 0x74, 0x4e, 0x6f, 0x64, 0x65, 0x54, 0x79, + 0x70, 0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, + 0x17, 0x0a, 0x07, 0x74, 0x65, 0x61, 0x6d, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x06, 0x74, 0x65, 0x61, 0x6d, 0x49, 0x64, 0x12, 0x1f, 0x0a, 0x0b, 0x63, 0x6c, 0x75, 0x73, + 0x74, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0a, 0x63, + 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, 0x64, 0x73, 0x12, 0x3e, 0x0a, 0x0a, 0x73, 0x74, 0x61, + 0x72, 0x74, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, + 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, + 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x48, 0x00, 0x52, 0x09, 0x73, 0x74, 0x61, + 0x72, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x88, 0x01, 0x01, 0x12, 0x3a, 0x0a, 0x08, 0x65, 0x6e, 0x64, + 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, + 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, + 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x48, 0x01, 0x52, 0x07, 0x65, 0x6e, 0x64, 0x54, 0x69, + 0x6d, 0x65, 0x88, 0x01, 0x01, 0x42, 0x0d, 0x0a, 0x0b, 0x5f, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, + 0x74, 0x69, 0x6d, 0x65, 0x42, 0x0b, 0x0a, 0x09, 0x5f, 0x65, 0x6e, 0x64, 0x5f, 0x74, 0x69, 0x6d, + 0x65, 0x22, 0x4a, 0x0a, 0x19, 0x47, 0x65, 0x74, 0x4e, 0x6f, 0x64, 0x65, 0x54, 0x79, 0x70, 0x65, + 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2d, + 0x0a, 0x09, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x10, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x49, + 0x6e, 0x66, 0x6f, 0x52, 0x08, 0x6e, 0x6f, 0x64, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x22, 0x80, 0x02, + 0x0a, 0x19, 0x47, 0x65, 0x74, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, 0x44, 0x42, 0x79, + 0x4e, 0x61, 0x6d, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x17, 0x0a, 0x07, 0x74, + 0x65, 0x61, 0x6d, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x74, 0x65, + 0x61, 0x6d, 0x49, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1b, 0x0a, 0x06, 0x72, 0x65, 0x67, 0x69, + 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x06, 0x72, 0x65, 0x67, 0x69, + 0x6f, 0x6e, 0x88, 0x01, 0x01, 0x12, 0x2a, 0x0a, 0x0e, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x5f, 0x70, + 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x48, 0x01, 0x52, + 0x0d, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x88, 0x01, + 0x01, 0x12, 0x42, 0x0a, 0x08, 0x6c, 0x69, 0x76, 0x65, 0x6e, 0x65, 0x73, 0x73, 0x18, 0x05, 0x20, + 0x01, 0x28, 0x0e, 0x32, 0x21, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6c, 0x75, + 0x73, 0x74, 0x65, 0x72, 0x4c, 0x69, 0x76, 0x65, 0x6e, 0x65, 0x73, 0x73, 0x50, 0x72, 0x65, 0x66, + 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x48, 0x02, 0x52, 0x08, 0x6c, 0x69, 0x76, 0x65, 0x6e, 0x65, + 0x73, 0x73, 0x88, 0x01, 0x01, 0x42, 0x09, 0x0a, 0x07, 0x5f, 0x72, 0x65, 0x67, 0x69, 0x6f, 0x6e, + 0x42, 0x11, 0x0a, 0x0f, 0x5f, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x5f, 0x70, 0x72, 0x6f, 0x76, 0x69, + 0x64, 0x65, 0x72, 0x42, 0x0b, 0x0a, 0x09, 0x5f, 0x6c, 0x69, 0x76, 0x65, 0x6e, 0x65, 0x73, 0x73, + 0x22, 0x2c, 0x0a, 0x1a, 0x47, 0x65, 0x74, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, 0x44, + 0x42, 0x79, 0x4e, 0x61, 0x6d, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x0e, + 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x22, 0x4a, + 0x0a, 0x1b, 0x47, 0x65, 0x74, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f, + 0x42, 0x79, 0x4e, 0x61, 0x6d, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x17, 0x0a, + 0x07, 0x74, 0x65, 0x61, 0x6d, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, + 0x74, 0x65, 0x61, 0x6d, 0x49, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0x49, 0x0a, 0x1c, 0x47, 0x65, + 0x74, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x42, 0x79, 0x4e, 0x61, + 0x6d, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x29, 0x0a, 0x07, 0x63, 0x6c, + 0x75, 0x73, 0x74, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x61, 0x70, + 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x52, 0x07, 0x63, 0x6c, + 0x75, 0x73, 0x74, 0x65, 0x72, 0x22, 0x6c, 0x0a, 0x1e, 0x47, 0x65, 0x74, 0x43, 0x6c, 0x75, 0x73, + 0x74, 0x65, 0x72, 0x73, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x17, 0x0a, 0x07, 0x74, 0x65, 0x61, 0x6d, 0x5f, + 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x74, 0x65, 0x61, 0x6d, 0x49, 0x64, + 0x12, 0x31, 0x0a, 0x14, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x5f, 0x64, 0x69, 0x73, 0x63, + 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x65, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x13, + 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x44, 0x69, 0x73, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, + 0x74, 0x65, 0x64, 0x22, 0x5a, 0x0a, 0x1f, 0x47, 0x65, 0x74, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, + 0x72, 0x73, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x37, 0x0a, 0x08, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, + 0x72, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, + 0x31, 0x2e, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, + 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x08, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x73, 0x22, + 0xf0, 0x03, 0x0a, 0x13, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x43, 0x75, 0x72, 0x72, 0x65, + 0x6e, 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x64, + 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x0b, 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x25, + 0x0a, 0x0e, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x5f, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, + 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x50, 0x72, 0x6f, + 0x76, 0x69, 0x64, 0x65, 0x72, 0x12, 0x16, 0x0a, 0x06, 0x72, 0x65, 0x67, 0x69, 0x6f, 0x6e, 0x18, + 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x72, 0x65, 0x67, 0x69, 0x6f, 0x6e, 0x12, 0x1d, 0x0a, + 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, + 0x03, 0x52, 0x09, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x12, 0x2d, 0x0a, 0x12, + 0x6b, 0x75, 0x62, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x65, 0x73, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, + 0x6f, 0x6e, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x11, 0x6b, 0x75, 0x62, 0x65, 0x72, 0x6e, + 0x65, 0x74, 0x65, 0x73, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x74, + 0x61, 0x67, 0x73, 0x18, 0x08, 0x20, 0x03, 0x28, 0x09, 0x52, 0x04, 0x74, 0x61, 0x67, 0x73, 0x12, + 0x27, 0x0a, 0x0f, 0x69, 0x73, 0x5f, 0x64, 0x69, 0x73, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, + 0x65, 0x64, 0x18, 0x09, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0e, 0x69, 0x73, 0x44, 0x69, 0x73, 0x63, + 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x65, 0x64, 0x12, 0x27, 0x0a, 0x0f, 0x61, 0x75, 0x74, 0x6f, + 0x73, 0x63, 0x61, 0x6c, 0x65, 0x72, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x0a, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x0e, 0x61, 0x75, 0x74, 0x6f, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x72, 0x54, 0x79, 0x70, + 0x65, 0x12, 0x50, 0x0a, 0x16, 0x61, 0x75, 0x74, 0x6f, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x72, 0x5f, + 0x64, 0x65, 0x74, 0x65, 0x63, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x0b, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x14, 0x61, + 0x75, 0x74, 0x6f, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x72, 0x44, 0x65, 0x74, 0x65, 0x63, 0x74, 0x65, + 0x64, 0x41, 0x74, 0x12, 0x40, 0x0a, 0x08, 0x73, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x18, + 0x14, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x43, + 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x6e, + 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x48, 0x00, 0x52, 0x08, 0x73, 0x6e, 0x61, 0x70, 0x73, 0x68, + 0x6f, 0x74, 0x88, 0x01, 0x01, 0x42, 0x0b, 0x0a, 0x09, 0x5f, 0x73, 0x6e, 0x61, 0x70, 0x73, 0x68, + 0x6f, 0x74, 0x22, 0x8b, 0x03, 0x0a, 0x17, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x52, 0x65, + 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x12, 0x2d, + 0x0a, 0x09, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x10, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x49, + 0x6e, 0x66, 0x6f, 0x52, 0x08, 0x6e, 0x6f, 0x64, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x2c, 0x0a, + 0x12, 0x63, 0x70, 0x75, 0x5f, 0x63, 0x61, 0x70, 0x61, 0x63, 0x69, 0x74, 0x79, 0x5f, 0x63, 0x6f, + 0x72, 0x65, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x01, 0x52, 0x10, 0x63, 0x70, 0x75, 0x43, 0x61, + 0x70, 0x61, 0x63, 0x69, 0x74, 0x79, 0x43, 0x6f, 0x72, 0x65, 0x73, 0x12, 0x32, 0x0a, 0x15, 0x6d, + 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x5f, 0x63, 0x61, 0x70, 0x61, 0x63, 0x69, 0x74, 0x79, 0x5f, 0x62, + 0x79, 0x74, 0x65, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x01, 0x52, 0x13, 0x6d, 0x65, 0x6d, 0x6f, + 0x72, 0x79, 0x43, 0x61, 0x70, 0x61, 0x63, 0x69, 0x74, 0x79, 0x42, 0x79, 0x74, 0x65, 0x73, 0x12, + 0x21, 0x0a, 0x0c, 0x67, 0x70, 0x75, 0x5f, 0x63, 0x61, 0x70, 0x61, 0x63, 0x69, 0x74, 0x79, 0x18, + 0x09, 0x20, 0x01, 0x28, 0x01, 0x52, 0x0b, 0x67, 0x70, 0x75, 0x43, 0x61, 0x70, 0x61, 0x63, 0x69, + 0x74, 0x79, 0x12, 0x37, 0x0a, 0x05, 0x75, 0x73, 0x61, 0x67, 0x65, 0x18, 0x0a, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x1c, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6c, 0x75, 0x73, 0x74, + 0x65, 0x72, 0x55, 0x73, 0x61, 0x67, 0x65, 0x53, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x48, + 0x00, 0x52, 0x05, 0x75, 0x73, 0x61, 0x67, 0x65, 0x88, 0x01, 0x01, 0x42, 0x08, 0x0a, 0x06, 0x5f, + 0x75, 0x73, 0x61, 0x67, 0x65, 0x4a, 0x04, 0x08, 0x01, 0x10, 0x02, 0x4a, 0x04, 0x08, 0x04, 0x10, + 0x05, 0x4a, 0x04, 0x08, 0x05, 0x10, 0x06, 0x4a, 0x04, 0x08, 0x07, 0x10, 0x08, 0x4a, 0x04, 0x08, + 0x08, 0x10, 0x09, 0x52, 0x0b, 0x6f, 0x62, 0x73, 0x65, 0x72, 0x76, 0x65, 0x64, 0x5f, 0x61, 0x74, + 0x52, 0x13, 0x63, 0x70, 0x75, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x65, 0x64, 0x5f, + 0x63, 0x6f, 0x72, 0x65, 0x73, 0x52, 0x0e, 0x63, 0x70, 0x75, 0x5f, 0x75, 0x73, 0x65, 0x64, 0x5f, + 0x63, 0x6f, 0x72, 0x65, 0x73, 0x52, 0x16, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x5f, 0x72, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x65, 0x64, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x52, 0x11, 0x6d, + 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x5f, 0x75, 0x73, 0x65, 0x64, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, + 0x22, 0xbd, 0x03, 0x0a, 0x14, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x55, 0x73, 0x61, 0x67, + 0x65, 0x53, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x12, 0x3b, 0x0a, 0x0b, 0x6f, 0x62, 0x73, + 0x65, 0x72, 0x76, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, + 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, + 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0a, 0x6f, 0x62, 0x73, 0x65, + 0x72, 0x76, 0x65, 0x64, 0x41, 0x74, 0x12, 0x27, 0x0a, 0x0f, 0x6e, 0x6f, 0x64, 0x65, 0x73, 0x5f, + 0x72, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x69, 0x6e, 0x67, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, + 0x0e, 0x6e, 0x6f, 0x64, 0x65, 0x73, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x69, 0x6e, 0x67, 0x12, + 0x2e, 0x0a, 0x13, 0x63, 0x70, 0x75, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x65, 0x64, + 0x5f, 0x63, 0x6f, 0x72, 0x65, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x01, 0x52, 0x11, 0x63, 0x70, + 0x75, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x65, 0x64, 0x43, 0x6f, 0x72, 0x65, 0x73, 0x12, + 0x24, 0x0a, 0x0e, 0x63, 0x70, 0x75, 0x5f, 0x75, 0x73, 0x65, 0x64, 0x5f, 0x63, 0x6f, 0x72, 0x65, + 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x01, 0x52, 0x0c, 0x63, 0x70, 0x75, 0x55, 0x73, 0x65, 0x64, + 0x43, 0x6f, 0x72, 0x65, 0x73, 0x12, 0x34, 0x0a, 0x16, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x5f, + 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x65, 0x64, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x18, + 0x05, 0x20, 0x01, 0x28, 0x01, 0x52, 0x14, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x65, 0x64, 0x42, 0x79, 0x74, 0x65, 0x73, 0x12, 0x2a, 0x0a, 0x11, 0x6d, + 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x5f, 0x75, 0x73, 0x65, 0x64, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, + 0x18, 0x06, 0x20, 0x01, 0x28, 0x01, 0x52, 0x0f, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x55, 0x73, + 0x65, 0x64, 0x42, 0x79, 0x74, 0x65, 0x73, 0x12, 0x23, 0x0a, 0x0d, 0x67, 0x70, 0x75, 0x5f, 0x72, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x65, 0x64, 0x18, 0x07, 0x20, 0x01, 0x28, 0x01, 0x52, 0x0c, + 0x67, 0x70, 0x75, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x65, 0x64, 0x12, 0x19, 0x0a, 0x08, + 0x67, 0x70, 0x75, 0x5f, 0x75, 0x73, 0x65, 0x64, 0x18, 0x08, 0x20, 0x01, 0x28, 0x01, 0x52, 0x07, + 0x67, 0x70, 0x75, 0x55, 0x73, 0x65, 0x64, 0x12, 0x1f, 0x0a, 0x0b, 0x67, 0x70, 0x75, 0x5f, 0x64, + 0x65, 0x76, 0x69, 0x63, 0x65, 0x73, 0x18, 0x09, 0x20, 0x01, 0x28, 0x01, 0x52, 0x0a, 0x67, 0x70, + 0x75, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x73, 0x12, 0x26, 0x0a, 0x0f, 0x67, 0x70, 0x75, 0x5f, + 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x73, 0x5f, 0x6d, 0x69, 0x67, 0x18, 0x0a, 0x20, 0x01, 0x28, + 0x01, 0x52, 0x0d, 0x67, 0x70, 0x75, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x73, 0x4d, 0x69, 0x67, + 0x22, 0xd8, 0x01, 0x0a, 0x18, 0x47, 0x65, 0x74, 0x54, 0x65, 0x61, 0x6d, 0x54, 0x69, 0x6d, 0x65, + 0x53, 0x65, 0x72, 0x69, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x17, 0x0a, + 0x07, 0x74, 0x65, 0x61, 0x6d, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, + 0x74, 0x65, 0x61, 0x6d, 0x49, 0x64, 0x12, 0x39, 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, + 0x74, 0x69, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, + 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, + 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x73, 0x74, 0x61, 0x72, 0x74, 0x54, 0x69, 0x6d, + 0x65, 0x12, 0x35, 0x0a, 0x08, 0x65, 0x6e, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, + 0x07, 0x65, 0x6e, 0x64, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x31, 0x0a, 0x14, 0x69, 0x6e, 0x63, 0x6c, + 0x75, 0x64, 0x65, 0x5f, 0x64, 0x69, 0x73, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x65, 0x64, + 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x13, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x44, + 0x69, 0x73, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x65, 0x64, 0x22, 0x50, 0x0a, 0x19, 0x47, + 0x65, 0x74, 0x54, 0x65, 0x61, 0x6d, 0x54, 0x69, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x69, 0x65, 0x73, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x33, 0x0a, 0x06, 0x70, 0x6f, 0x69, 0x6e, + 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, + 0x31, 0x2e, 0x54, 0x65, 0x61, 0x6d, 0x54, 0x69, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x69, 0x65, 0x73, + 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x52, 0x06, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x22, 0xfd, 0x01, + 0x0a, 0x13, 0x54, 0x65, 0x61, 0x6d, 0x54, 0x69, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x69, 0x65, 0x73, + 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, + 0x6d, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, + 0x61, 0x6d, 0x70, 0x12, 0x25, 0x0a, 0x0e, 0x62, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x5f, 0x73, 0x65, + 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0d, 0x62, 0x75, 0x63, + 0x6b, 0x65, 0x74, 0x53, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x12, 0x2c, 0x0a, 0x12, 0x63, 0x70, + 0x75, 0x5f, 0x63, 0x61, 0x70, 0x61, 0x63, 0x69, 0x74, 0x79, 0x5f, 0x63, 0x6f, 0x72, 0x65, 0x73, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x01, 0x52, 0x10, 0x63, 0x70, 0x75, 0x43, 0x61, 0x70, 0x61, 0x63, + 0x69, 0x74, 0x79, 0x43, 0x6f, 0x72, 0x65, 0x73, 0x12, 0x2e, 0x0a, 0x13, 0x63, 0x70, 0x75, 0x5f, + 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x65, 0x64, 0x5f, 0x63, 0x6f, 0x72, 0x65, 0x73, 0x18, + 0x04, 0x20, 0x01, 0x28, 0x01, 0x52, 0x11, 0x63, 0x70, 0x75, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x65, 0x64, 0x43, 0x6f, 0x72, 0x65, 0x73, 0x12, 0x24, 0x0a, 0x0e, 0x63, 0x70, 0x75, 0x5f, + 0x75, 0x73, 0x65, 0x64, 0x5f, 0x63, 0x6f, 0x72, 0x65, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x01, + 0x52, 0x0c, 0x63, 0x70, 0x75, 0x55, 0x73, 0x65, 0x64, 0x43, 0x6f, 0x72, 0x65, 0x73, 0x12, 0x1d, + 0x0a, 0x0a, 0x6d, 0x6f, 0x6e, 0x65, 0x79, 0x5f, 0x63, 0x6f, 0x73, 0x74, 0x18, 0x06, 0x20, 0x01, + 0x28, 0x01, 0x52, 0x09, 0x6d, 0x6f, 0x6e, 0x65, 0x79, 0x43, 0x6f, 0x73, 0x74, 0x22, 0xde, 0x01, + 0x0a, 0x1e, 0x47, 0x65, 0x74, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x73, 0x50, 0x65, 0x72, + 0x69, 0x6f, 0x64, 0x44, 0x65, 0x6c, 0x74, 0x61, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x12, 0x17, 0x0a, 0x07, 0x74, 0x65, 0x61, 0x6d, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x06, 0x74, 0x65, 0x61, 0x6d, 0x49, 0x64, 0x12, 0x39, 0x0a, 0x0a, 0x73, 0x74, 0x61, + 0x72, 0x74, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, + 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, + 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x73, 0x74, 0x61, 0x72, 0x74, + 0x54, 0x69, 0x6d, 0x65, 0x12, 0x35, 0x0a, 0x08, 0x65, 0x6e, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, + 0x6d, 0x70, 0x52, 0x07, 0x65, 0x6e, 0x64, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x31, 0x0a, 0x14, 0x69, + 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x5f, 0x64, 0x69, 0x73, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, + 0x74, 0x65, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x13, 0x69, 0x6e, 0x63, 0x6c, 0x75, + 0x64, 0x65, 0x44, 0x69, 0x73, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x65, 0x64, 0x22, 0xa3, + 0x04, 0x0a, 0x12, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x50, 0x65, 0x72, 0x69, 0x6f, 0x64, + 0x44, 0x65, 0x6c, 0x74, 0x61, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, + 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x6c, 0x75, 0x73, 0x74, + 0x65, 0x72, 0x49, 0x64, 0x12, 0x3c, 0x0a, 0x1b, 0x6d, 0x6f, 0x6e, 0x65, 0x79, 0x5f, 0x63, 0x6f, + 0x73, 0x74, 0x5f, 0x70, 0x65, 0x72, 0x5f, 0x68, 0x6f, 0x75, 0x72, 0x5f, 0x63, 0x75, 0x72, 0x72, + 0x65, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x01, 0x52, 0x17, 0x6d, 0x6f, 0x6e, 0x65, 0x79, + 0x43, 0x6f, 0x73, 0x74, 0x50, 0x65, 0x72, 0x48, 0x6f, 0x75, 0x72, 0x43, 0x75, 0x72, 0x72, 0x65, + 0x6e, 0x74, 0x12, 0x3e, 0x0a, 0x1c, 0x6d, 0x6f, 0x6e, 0x65, 0x79, 0x5f, 0x63, 0x6f, 0x73, 0x74, + 0x5f, 0x70, 0x65, 0x72, 0x5f, 0x68, 0x6f, 0x75, 0x72, 0x5f, 0x70, 0x72, 0x65, 0x76, 0x69, 0x6f, + 0x75, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x01, 0x52, 0x18, 0x6d, 0x6f, 0x6e, 0x65, 0x79, 0x43, + 0x6f, 0x73, 0x74, 0x50, 0x65, 0x72, 0x48, 0x6f, 0x75, 0x72, 0x50, 0x72, 0x65, 0x76, 0x69, 0x6f, + 0x75, 0x73, 0x12, 0x3d, 0x0a, 0x1b, 0x63, 0x70, 0x75, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x65, 0x64, 0x5f, 0x63, 0x6f, 0x72, 0x65, 0x73, 0x5f, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, + 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x01, 0x52, 0x18, 0x63, 0x70, 0x75, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x65, 0x64, 0x43, 0x6f, 0x72, 0x65, 0x73, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, + 0x74, 0x12, 0x3f, 0x0a, 0x1c, 0x63, 0x70, 0x75, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x65, 0x64, 0x5f, 0x63, 0x6f, 0x72, 0x65, 0x73, 0x5f, 0x70, 0x72, 0x65, 0x76, 0x69, 0x6f, 0x75, + 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x01, 0x52, 0x19, 0x63, 0x70, 0x75, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x65, 0x64, 0x43, 0x6f, 0x72, 0x65, 0x73, 0x50, 0x72, 0x65, 0x76, 0x69, 0x6f, + 0x75, 0x73, 0x12, 0x43, 0x0a, 0x1e, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x5f, 0x72, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x65, 0x64, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x5f, 0x63, 0x75, 0x72, + 0x72, 0x65, 0x6e, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x01, 0x52, 0x1b, 0x6d, 0x65, 0x6d, 0x6f, + 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x65, 0x64, 0x42, 0x79, 0x74, 0x65, 0x73, + 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x12, 0x45, 0x0a, 0x1f, 0x6d, 0x65, 0x6d, 0x6f, 0x72, + 0x79, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x65, 0x64, 0x5f, 0x62, 0x79, 0x74, 0x65, + 0x73, 0x5f, 0x70, 0x72, 0x65, 0x76, 0x69, 0x6f, 0x75, 0x73, 0x18, 0x07, 0x20, 0x01, 0x28, 0x01, + 0x52, 0x1c, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x65, + 0x64, 0x42, 0x79, 0x74, 0x65, 0x73, 0x50, 0x72, 0x65, 0x76, 0x69, 0x6f, 0x75, 0x73, 0x12, 0x30, + 0x0a, 0x14, 0x64, 0x61, 0x74, 0x61, 0x5f, 0x6d, 0x69, 0x6e, 0x75, 0x74, 0x65, 0x73, 0x5f, 0x63, + 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x18, 0x08, 0x20, 0x01, 0x28, 0x03, 0x52, 0x12, 0x64, 0x61, + 0x74, 0x61, 0x4d, 0x69, 0x6e, 0x75, 0x74, 0x65, 0x73, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, + 0x12, 0x32, 0x0a, 0x15, 0x64, 0x61, 0x74, 0x61, 0x5f, 0x6d, 0x69, 0x6e, 0x75, 0x74, 0x65, 0x73, + 0x5f, 0x70, 0x72, 0x65, 0x76, 0x69, 0x6f, 0x75, 0x73, 0x18, 0x09, 0x20, 0x01, 0x28, 0x03, 0x52, + 0x13, 0x64, 0x61, 0x74, 0x61, 0x4d, 0x69, 0x6e, 0x75, 0x74, 0x65, 0x73, 0x50, 0x72, 0x65, 0x76, + 0x69, 0x6f, 0x75, 0x73, 0x22, 0xd9, 0x02, 0x0a, 0x1f, 0x47, 0x65, 0x74, 0x43, 0x6c, 0x75, 0x73, + 0x74, 0x65, 0x72, 0x73, 0x50, 0x65, 0x72, 0x69, 0x6f, 0x64, 0x44, 0x65, 0x6c, 0x74, 0x61, 0x73, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x36, 0x0a, 0x08, 0x63, 0x6c, 0x75, 0x73, + 0x74, 0x65, 0x72, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x61, 0x70, 0x69, + 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x50, 0x65, 0x72, 0x69, 0x6f, + 0x64, 0x44, 0x65, 0x6c, 0x74, 0x61, 0x52, 0x08, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x73, + 0x12, 0x35, 0x0a, 0x17, 0x74, 0x65, 0x61, 0x6d, 0x5f, 0x6d, 0x6f, 0x6e, 0x65, 0x79, 0x5f, 0x63, + 0x6f, 0x73, 0x74, 0x5f, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x01, 0x52, 0x14, 0x74, 0x65, 0x61, 0x6d, 0x4d, 0x6f, 0x6e, 0x65, 0x79, 0x43, 0x6f, 0x73, 0x74, + 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x12, 0x37, 0x0a, 0x18, 0x74, 0x65, 0x61, 0x6d, 0x5f, + 0x6d, 0x6f, 0x6e, 0x65, 0x79, 0x5f, 0x63, 0x6f, 0x73, 0x74, 0x5f, 0x70, 0x72, 0x65, 0x76, 0x69, + 0x6f, 0x75, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x01, 0x52, 0x15, 0x74, 0x65, 0x61, 0x6d, 0x4d, + 0x6f, 0x6e, 0x65, 0x79, 0x43, 0x6f, 0x73, 0x74, 0x50, 0x72, 0x65, 0x76, 0x69, 0x6f, 0x75, 0x73, + 0x12, 0x48, 0x0a, 0x12, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x73, 0x74, 0x61, 0x72, + 0x74, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, + 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, + 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x10, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, + 0x74, 0x53, 0x74, 0x61, 0x72, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x44, 0x0a, 0x10, 0x63, 0x75, + 0x72, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x65, 0x6e, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x05, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, + 0x52, 0x0e, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x45, 0x6e, 0x64, 0x54, 0x69, 0x6d, 0x65, + 0x2a, 0xcb, 0x01, 0x0a, 0x19, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x4c, 0x69, 0x76, 0x65, + 0x6e, 0x65, 0x73, 0x73, 0x50, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x12, 0x2b, + 0x0a, 0x27, 0x43, 0x4c, 0x55, 0x53, 0x54, 0x45, 0x52, 0x5f, 0x4c, 0x49, 0x56, 0x45, 0x4e, 0x45, + 0x53, 0x53, 0x5f, 0x50, 0x52, 0x45, 0x46, 0x45, 0x52, 0x45, 0x4e, 0x43, 0x45, 0x5f, 0x55, 0x4e, + 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x26, 0x0a, 0x22, 0x43, + 0x4c, 0x55, 0x53, 0x54, 0x45, 0x52, 0x5f, 0x4c, 0x49, 0x56, 0x45, 0x4e, 0x45, 0x53, 0x53, 0x5f, + 0x50, 0x52, 0x45, 0x46, 0x45, 0x52, 0x45, 0x4e, 0x43, 0x45, 0x5f, 0x49, 0x47, 0x4e, 0x4f, 0x52, + 0x45, 0x10, 0x01, 0x12, 0x2b, 0x0a, 0x27, 0x43, 0x4c, 0x55, 0x53, 0x54, 0x45, 0x52, 0x5f, 0x4c, + 0x49, 0x56, 0x45, 0x4e, 0x45, 0x53, 0x53, 0x5f, 0x50, 0x52, 0x45, 0x46, 0x45, 0x52, 0x45, 0x4e, + 0x43, 0x45, 0x5f, 0x50, 0x52, 0x45, 0x46, 0x45, 0x52, 0x5f, 0x4c, 0x49, 0x56, 0x45, 0x10, 0x02, + 0x12, 0x2c, 0x0a, 0x28, 0x43, 0x4c, 0x55, 0x53, 0x54, 0x45, 0x52, 0x5f, 0x4c, 0x49, 0x56, 0x45, + 0x4e, 0x45, 0x53, 0x53, 0x5f, 0x50, 0x52, 0x45, 0x46, 0x45, 0x52, 0x45, 0x4e, 0x43, 0x45, 0x5f, + 0x52, 0x45, 0x51, 0x55, 0x49, 0x52, 0x45, 0x5f, 0x4c, 0x49, 0x56, 0x45, 0x10, 0x03, 0x32, 0xb2, + 0x0a, 0x0a, 0x0e, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, + 0x65, 0x12, 0x61, 0x0a, 0x14, 0x47, 0x65, 0x74, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x73, + 0x42, 0x61, 0x73, 0x69, 0x63, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x23, 0x2e, 0x61, 0x70, 0x69, 0x2e, + 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x73, 0x42, 0x61, + 0x73, 0x69, 0x63, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x24, + 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x6c, 0x75, 0x73, 0x74, + 0x65, 0x72, 0x73, 0x42, 0x61, 0x73, 0x69, 0x63, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x5e, 0x0a, 0x13, 0x47, 0x65, 0x74, 0x43, 0x6c, 0x75, 0x73, 0x74, + 0x65, 0x72, 0x42, 0x61, 0x73, 0x69, 0x63, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x22, 0x2e, 0x61, 0x70, + 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x42, + 0x61, 0x73, 0x69, 0x63, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x23, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x6c, 0x75, 0x73, + 0x74, 0x65, 0x72, 0x42, 0x61, 0x73, 0x69, 0x63, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x67, 0x0a, 0x16, 0x47, 0x65, 0x74, 0x43, 0x6c, 0x75, 0x73, 0x74, + 0x65, 0x72, 0x73, 0x57, 0x69, 0x74, 0x68, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x12, 0x25, + 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x6c, 0x75, 0x73, 0x74, + 0x65, 0x72, 0x73, 0x57, 0x69, 0x74, 0x68, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x26, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, + 0x65, 0x74, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x73, 0x57, 0x69, 0x74, 0x68, 0x4d, 0x65, + 0x74, 0x72, 0x69, 0x63, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x5b, 0x0a, + 0x12, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x54, 0x6f, + 0x6b, 0x65, 0x6e, 0x12, 0x21, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x72, 0x65, + 0x61, 0x74, 0x65, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, + 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x54, 0x6f, 0x6b, + 0x65, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x6a, 0x0a, 0x17, 0x47, 0x65, + 0x74, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x73, 0x44, 0x65, 0x6c, 0x74, 0x61, 0x4d, 0x65, + 0x74, 0x72, 0x69, 0x63, 0x73, 0x12, 0x26, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, + 0x65, 0x74, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x73, 0x44, 0x65, 0x6c, 0x74, 0x61, 0x4d, + 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x27, 0x2e, + 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, + 0x72, 0x73, 0x44, 0x65, 0x6c, 0x74, 0x61, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x67, 0x0a, 0x16, 0x47, 0x65, 0x74, 0x4e, 0x65, 0x74, + 0x77, 0x6f, 0x72, 0x6b, 0x44, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x6e, 0x63, 0x69, 0x65, 0x73, + 0x12, 0x25, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x4e, 0x65, 0x74, + 0x77, 0x6f, 0x72, 0x6b, 0x44, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x6e, 0x63, 0x69, 0x65, 0x73, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x26, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, + 0x2e, 0x47, 0x65, 0x74, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x44, 0x65, 0x70, 0x65, 0x6e, + 0x64, 0x65, 0x6e, 0x63, 0x69, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, + 0x76, 0x0a, 0x1b, 0x47, 0x65, 0x74, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x4d, 0x65, 0x74, + 0x72, 0x69, 0x63, 0x73, 0x54, 0x69, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x69, 0x65, 0x73, 0x12, 0x2a, + 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x4e, 0x65, 0x74, 0x77, 0x6f, + 0x72, 0x6b, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x54, 0x69, 0x6d, 0x65, 0x53, 0x65, 0x72, + 0x69, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2b, 0x2e, 0x61, 0x70, 0x69, + 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x4d, 0x65, + 0x74, 0x72, 0x69, 0x63, 0x73, 0x54, 0x69, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x69, 0x65, 0x73, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x58, 0x0a, 0x11, 0x47, 0x65, 0x74, 0x4e, 0x6f, + 0x64, 0x65, 0x54, 0x79, 0x70, 0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x12, 0x20, 0x2e, 0x61, + 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x4e, 0x6f, 0x64, 0x65, 0x54, 0x79, 0x70, + 0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, + 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x4e, 0x6f, 0x64, 0x65, 0x54, + 0x79, 0x70, 0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x12, 0x5b, 0x0a, 0x12, 0x47, 0x65, 0x74, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, + 0x44, 0x42, 0x79, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x21, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, + 0x2e, 0x47, 0x65, 0x74, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, 0x44, 0x42, 0x79, 0x4e, + 0x61, 0x6d, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x61, 0x70, 0x69, + 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, 0x44, + 0x42, 0x79, 0x4e, 0x61, 0x6d, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x61, + 0x0a, 0x14, 0x47, 0x65, 0x74, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f, + 0x42, 0x79, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x23, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, + 0x47, 0x65, 0x74, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x42, 0x79, + 0x4e, 0x61, 0x6d, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x24, 0x2e, 0x61, 0x70, + 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, + 0x6e, 0x66, 0x6f, 0x42, 0x79, 0x4e, 0x61, 0x6d, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x12, 0x6a, 0x0a, 0x17, 0x47, 0x65, 0x74, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x73, + 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x26, 0x2e, 0x61, + 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, + 0x73, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x27, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, + 0x74, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x73, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, + 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x58, 0x0a, + 0x11, 0x47, 0x65, 0x74, 0x54, 0x65, 0x61, 0x6d, 0x54, 0x69, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x69, + 0x65, 0x73, 0x12, 0x20, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x54, + 0x65, 0x61, 0x6d, 0x54, 0x69, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x69, 0x65, 0x73, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, + 0x74, 0x54, 0x65, 0x61, 0x6d, 0x54, 0x69, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x69, 0x65, 0x73, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x6a, 0x0a, 0x17, 0x47, 0x65, 0x74, 0x43, 0x6c, + 0x75, 0x73, 0x74, 0x65, 0x72, 0x73, 0x50, 0x65, 0x72, 0x69, 0x6f, 0x64, 0x44, 0x65, 0x6c, 0x74, + 0x61, 0x73, 0x12, 0x26, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x43, + 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x73, 0x50, 0x65, 0x72, 0x69, 0x6f, 0x64, 0x44, 0x65, 0x6c, + 0x74, 0x61, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x27, 0x2e, 0x61, 0x70, 0x69, + 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x73, 0x50, + 0x65, 0x72, 0x69, 0x6f, 0x64, 0x44, 0x65, 0x6c, 0x74, 0x61, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x42, 0x8a, 0x01, 0x0a, 0x0a, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x69, 0x2e, + 0x76, 0x31, 0x42, 0x0c, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x50, 0x72, 0x6f, 0x74, 0x6f, + 0x50, 0x01, 0x5a, 0x35, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x64, + 0x65, 0x76, 0x7a, 0x65, 0x72, 0x6f, 0x2d, 0x69, 0x6e, 0x63, 0x2f, 0x73, 0x65, 0x72, 0x76, 0x69, + 0x63, 0x65, 0x73, 0x2f, 0x64, 0x61, 0x6b, 0x72, 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x61, 0x70, 0x69, + 0x2f, 0x76, 0x31, 0x3b, 0x61, 0x70, 0x69, 0x76, 0x31, 0xa2, 0x02, 0x03, 0x41, 0x58, 0x58, 0xaa, + 0x02, 0x06, 0x41, 0x70, 0x69, 0x2e, 0x56, 0x31, 0xca, 0x02, 0x06, 0x41, 0x70, 0x69, 0x5c, 0x56, + 0x31, 0xe2, 0x02, 0x12, 0x41, 0x70, 0x69, 0x5c, 0x56, 0x31, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, + 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x07, 0x41, 0x70, 0x69, 0x3a, 0x3a, 0x56, 0x31, + 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_api_v1_cluster_proto_rawDescOnce sync.Once + file_api_v1_cluster_proto_rawDescData = file_api_v1_cluster_proto_rawDesc +) + +func file_api_v1_cluster_proto_rawDescGZIP() []byte { + file_api_v1_cluster_proto_rawDescOnce.Do(func() { + file_api_v1_cluster_proto_rawDescData = protoimpl.X.CompressGZIP(file_api_v1_cluster_proto_rawDescData) + }) + return file_api_v1_cluster_proto_rawDescData +} + +var file_api_v1_cluster_proto_enumTypes = make([]protoimpl.EnumInfo, 1) +var file_api_v1_cluster_proto_msgTypes = make([]protoimpl.MessageInfo, 40) +var file_api_v1_cluster_proto_goTypes = []interface{}{ + (ClusterLivenessPreference)(0), // 0: api.v1.ClusterLivenessPreference + (*GetClustersBasicInfoRequest)(nil), // 1: api.v1.GetClustersBasicInfoRequest + (*GetClustersBasicInfoResponse)(nil), // 2: api.v1.GetClustersBasicInfoResponse + (*GetClusterBasicInfoRequest)(nil), // 3: api.v1.GetClusterBasicInfoRequest + (*GetClusterBasicInfoResponse)(nil), // 4: api.v1.GetClusterBasicInfoResponse + (*GetClustersWithMetricsRequest)(nil), // 5: api.v1.GetClustersWithMetricsRequest + (*GetClustersWithMetricsResponse)(nil), // 6: api.v1.GetClustersWithMetricsResponse + (*GetClustersDeltaMetricsRequest)(nil), // 7: api.v1.GetClustersDeltaMetricsRequest + (*ClusterDeltaMetrics)(nil), // 8: api.v1.ClusterDeltaMetrics + (*GetClustersDeltaMetricsResponse)(nil), // 9: api.v1.GetClustersDeltaMetricsResponse + (*CreateClusterTokenRequest)(nil), // 10: api.v1.CreateClusterTokenRequest + (*CreateClusterTokenResponse)(nil), // 11: api.v1.CreateClusterTokenResponse + (*GetNetworkDependenciesRequest)(nil), // 12: api.v1.GetNetworkDependenciesRequest + (*WorkloadNode)(nil), // 13: api.v1.WorkloadNode + (*ExternalNode)(nil), // 14: api.v1.ExternalNode + (*ServiceNode)(nil), // 15: api.v1.ServiceNode + (*CloudResourceNode)(nil), // 16: api.v1.CloudResourceNode + (*DependencyEdge)(nil), // 17: api.v1.DependencyEdge + (*GetNetworkDependenciesResponse)(nil), // 18: api.v1.GetNetworkDependenciesResponse + (*GetNetworkMetricsTimeSeriesRequest)(nil), // 19: api.v1.GetNetworkMetricsTimeSeriesRequest + (*GetNetworkMetricsTimeSeriesResponse)(nil), // 20: api.v1.GetNetworkMetricsTimeSeriesResponse + (*TimeSeriesZone)(nil), // 21: api.v1.TimeSeriesZone + (*NetworkTimeSeriesBucket)(nil), // 22: api.v1.NetworkTimeSeriesBucket + (*NetworkPercentileValues)(nil), // 23: api.v1.NetworkPercentileValues + (*GetNodeTypeCountsRequest)(nil), // 24: api.v1.GetNodeTypeCountsRequest + (*GetNodeTypeCountsResponse)(nil), // 25: api.v1.GetNodeTypeCountsResponse + (*GetClusterIDByNameRequest)(nil), // 26: api.v1.GetClusterIDByNameRequest + (*GetClusterIDByNameResponse)(nil), // 27: api.v1.GetClusterIDByNameResponse + (*GetClusterInfoByNameRequest)(nil), // 28: api.v1.GetClusterInfoByNameRequest + (*GetClusterInfoByNameResponse)(nil), // 29: api.v1.GetClusterInfoByNameResponse + (*GetClustersCurrentStateRequest)(nil), // 30: api.v1.GetClustersCurrentStateRequest + (*GetClustersCurrentStateResponse)(nil), // 31: api.v1.GetClustersCurrentStateResponse + (*ClusterCurrentState)(nil), // 32: api.v1.ClusterCurrentState + (*ClusterResourceSnapshot)(nil), // 33: api.v1.ClusterResourceSnapshot + (*ClusterUsageSnapshot)(nil), // 34: api.v1.ClusterUsageSnapshot + (*GetTeamTimeSeriesRequest)(nil), // 35: api.v1.GetTeamTimeSeriesRequest + (*GetTeamTimeSeriesResponse)(nil), // 36: api.v1.GetTeamTimeSeriesResponse + (*TeamTimeSeriesPoint)(nil), // 37: api.v1.TeamTimeSeriesPoint + (*GetClustersPeriodDeltasRequest)(nil), // 38: api.v1.GetClustersPeriodDeltasRequest + (*ClusterPeriodDelta)(nil), // 39: api.v1.ClusterPeriodDelta + (*GetClustersPeriodDeltasResponse)(nil), // 40: api.v1.GetClustersPeriodDeltasResponse + (*Cluster)(nil), // 41: api.v1.Cluster + (*timestamppb.Timestamp)(nil), // 42: google.protobuf.Timestamp + (*ResourceMetrics)(nil), // 43: api.v1.ResourceMetrics + (*CostInfo)(nil), // 44: api.v1.CostInfo + (*NodeInfo)(nil), // 45: api.v1.NodeInfo + (*CostDataPoint)(nil), // 46: api.v1.CostDataPoint + (*ResourceDataPoint)(nil), // 47: api.v1.ResourceDataPoint +} +var file_api_v1_cluster_proto_depIdxs = []int32{ + 41, // 0: api.v1.GetClustersBasicInfoResponse.clusters:type_name -> api.v1.Cluster + 41, // 1: api.v1.GetClusterBasicInfoResponse.cluster:type_name -> api.v1.Cluster + 42, // 2: api.v1.GetClustersWithMetricsRequest.start_time:type_name -> google.protobuf.Timestamp + 42, // 3: api.v1.GetClustersWithMetricsRequest.end_time:type_name -> google.protobuf.Timestamp + 41, // 4: api.v1.GetClustersWithMetricsResponse.clusters:type_name -> api.v1.Cluster + 43, // 5: api.v1.GetClustersWithMetricsResponse.resource_metrics:type_name -> api.v1.ResourceMetrics + 44, // 6: api.v1.GetClustersWithMetricsResponse.cost_info:type_name -> api.v1.CostInfo + 45, // 7: api.v1.GetClustersWithMetricsResponse.node_info:type_name -> api.v1.NodeInfo + 46, // 8: api.v1.GetClustersWithMetricsResponse.cost_data_points:type_name -> api.v1.CostDataPoint + 47, // 9: api.v1.GetClustersWithMetricsResponse.resource_data_points:type_name -> api.v1.ResourceDataPoint + 42, // 10: api.v1.GetClustersDeltaMetricsRequest.start_time:type_name -> google.protobuf.Timestamp + 42, // 11: api.v1.GetClustersDeltaMetricsRequest.end_time:type_name -> google.protobuf.Timestamp + 44, // 12: api.v1.ClusterDeltaMetrics.cost_info:type_name -> api.v1.CostInfo + 43, // 13: api.v1.ClusterDeltaMetrics.resource_metrics:type_name -> api.v1.ResourceMetrics + 8, // 14: api.v1.GetClustersDeltaMetricsResponse.clusters:type_name -> api.v1.ClusterDeltaMetrics + 42, // 15: api.v1.GetNetworkDependenciesRequest.start_time:type_name -> google.protobuf.Timestamp + 42, // 16: api.v1.GetNetworkDependenciesRequest.end_time:type_name -> google.protobuf.Timestamp + 13, // 17: api.v1.DependencyEdge.src_workload:type_name -> api.v1.WorkloadNode + 13, // 18: api.v1.DependencyEdge.dst_workload:type_name -> api.v1.WorkloadNode + 14, // 19: api.v1.DependencyEdge.dst_external:type_name -> api.v1.ExternalNode + 15, // 20: api.v1.DependencyEdge.dst_service:type_name -> api.v1.ServiceNode + 16, // 21: api.v1.DependencyEdge.dst_cloud_resource:type_name -> api.v1.CloudResourceNode + 17, // 22: api.v1.GetNetworkDependenciesResponse.edges:type_name -> api.v1.DependencyEdge + 42, // 23: api.v1.GetNetworkMetricsTimeSeriesRequest.start_time:type_name -> google.protobuf.Timestamp + 42, // 24: api.v1.GetNetworkMetricsTimeSeriesRequest.end_time:type_name -> google.protobuf.Timestamp + 22, // 25: api.v1.GetNetworkMetricsTimeSeriesResponse.buckets:type_name -> api.v1.NetworkTimeSeriesBucket + 21, // 26: api.v1.GetNetworkMetricsTimeSeriesResponse.zones:type_name -> api.v1.TimeSeriesZone + 42, // 27: api.v1.TimeSeriesZone.start_time:type_name -> google.protobuf.Timestamp + 42, // 28: api.v1.TimeSeriesZone.end_time:type_name -> google.protobuf.Timestamp + 42, // 29: api.v1.NetworkTimeSeriesBucket.timestamp:type_name -> google.protobuf.Timestamp + 23, // 30: api.v1.NetworkTimeSeriesBucket.tx_bytes_percentiles:type_name -> api.v1.NetworkPercentileValues + 23, // 31: api.v1.NetworkTimeSeriesBucket.rx_bytes_percentiles:type_name -> api.v1.NetworkPercentileValues + 23, // 32: api.v1.NetworkTimeSeriesBucket.cost_percentiles:type_name -> api.v1.NetworkPercentileValues + 42, // 33: api.v1.GetNodeTypeCountsRequest.start_time:type_name -> google.protobuf.Timestamp + 42, // 34: api.v1.GetNodeTypeCountsRequest.end_time:type_name -> google.protobuf.Timestamp + 45, // 35: api.v1.GetNodeTypeCountsResponse.node_info:type_name -> api.v1.NodeInfo + 0, // 36: api.v1.GetClusterIDByNameRequest.liveness:type_name -> api.v1.ClusterLivenessPreference + 41, // 37: api.v1.GetClusterInfoByNameResponse.cluster:type_name -> api.v1.Cluster + 32, // 38: api.v1.GetClustersCurrentStateResponse.clusters:type_name -> api.v1.ClusterCurrentState + 42, // 39: api.v1.ClusterCurrentState.autoscaler_detected_at:type_name -> google.protobuf.Timestamp + 33, // 40: api.v1.ClusterCurrentState.snapshot:type_name -> api.v1.ClusterResourceSnapshot + 45, // 41: api.v1.ClusterResourceSnapshot.node_info:type_name -> api.v1.NodeInfo + 34, // 42: api.v1.ClusterResourceSnapshot.usage:type_name -> api.v1.ClusterUsageSnapshot + 42, // 43: api.v1.ClusterUsageSnapshot.observed_at:type_name -> google.protobuf.Timestamp + 42, // 44: api.v1.GetTeamTimeSeriesRequest.start_time:type_name -> google.protobuf.Timestamp + 42, // 45: api.v1.GetTeamTimeSeriesRequest.end_time:type_name -> google.protobuf.Timestamp + 37, // 46: api.v1.GetTeamTimeSeriesResponse.points:type_name -> api.v1.TeamTimeSeriesPoint + 42, // 47: api.v1.GetClustersPeriodDeltasRequest.start_time:type_name -> google.protobuf.Timestamp + 42, // 48: api.v1.GetClustersPeriodDeltasRequest.end_time:type_name -> google.protobuf.Timestamp + 39, // 49: api.v1.GetClustersPeriodDeltasResponse.clusters:type_name -> api.v1.ClusterPeriodDelta + 42, // 50: api.v1.GetClustersPeriodDeltasResponse.current_start_time:type_name -> google.protobuf.Timestamp + 42, // 51: api.v1.GetClustersPeriodDeltasResponse.current_end_time:type_name -> google.protobuf.Timestamp + 1, // 52: api.v1.ClusterService.GetClustersBasicInfo:input_type -> api.v1.GetClustersBasicInfoRequest + 3, // 53: api.v1.ClusterService.GetClusterBasicInfo:input_type -> api.v1.GetClusterBasicInfoRequest + 5, // 54: api.v1.ClusterService.GetClustersWithMetrics:input_type -> api.v1.GetClustersWithMetricsRequest + 10, // 55: api.v1.ClusterService.CreateClusterToken:input_type -> api.v1.CreateClusterTokenRequest + 7, // 56: api.v1.ClusterService.GetClustersDeltaMetrics:input_type -> api.v1.GetClustersDeltaMetricsRequest + 12, // 57: api.v1.ClusterService.GetNetworkDependencies:input_type -> api.v1.GetNetworkDependenciesRequest + 19, // 58: api.v1.ClusterService.GetNetworkMetricsTimeSeries:input_type -> api.v1.GetNetworkMetricsTimeSeriesRequest + 24, // 59: api.v1.ClusterService.GetNodeTypeCounts:input_type -> api.v1.GetNodeTypeCountsRequest + 26, // 60: api.v1.ClusterService.GetClusterIDByName:input_type -> api.v1.GetClusterIDByNameRequest + 28, // 61: api.v1.ClusterService.GetClusterInfoByName:input_type -> api.v1.GetClusterInfoByNameRequest + 30, // 62: api.v1.ClusterService.GetClustersCurrentState:input_type -> api.v1.GetClustersCurrentStateRequest + 35, // 63: api.v1.ClusterService.GetTeamTimeSeries:input_type -> api.v1.GetTeamTimeSeriesRequest + 38, // 64: api.v1.ClusterService.GetClustersPeriodDeltas:input_type -> api.v1.GetClustersPeriodDeltasRequest + 2, // 65: api.v1.ClusterService.GetClustersBasicInfo:output_type -> api.v1.GetClustersBasicInfoResponse + 4, // 66: api.v1.ClusterService.GetClusterBasicInfo:output_type -> api.v1.GetClusterBasicInfoResponse + 6, // 67: api.v1.ClusterService.GetClustersWithMetrics:output_type -> api.v1.GetClustersWithMetricsResponse + 11, // 68: api.v1.ClusterService.CreateClusterToken:output_type -> api.v1.CreateClusterTokenResponse + 9, // 69: api.v1.ClusterService.GetClustersDeltaMetrics:output_type -> api.v1.GetClustersDeltaMetricsResponse + 18, // 70: api.v1.ClusterService.GetNetworkDependencies:output_type -> api.v1.GetNetworkDependenciesResponse + 20, // 71: api.v1.ClusterService.GetNetworkMetricsTimeSeries:output_type -> api.v1.GetNetworkMetricsTimeSeriesResponse + 25, // 72: api.v1.ClusterService.GetNodeTypeCounts:output_type -> api.v1.GetNodeTypeCountsResponse + 27, // 73: api.v1.ClusterService.GetClusterIDByName:output_type -> api.v1.GetClusterIDByNameResponse + 29, // 74: api.v1.ClusterService.GetClusterInfoByName:output_type -> api.v1.GetClusterInfoByNameResponse + 31, // 75: api.v1.ClusterService.GetClustersCurrentState:output_type -> api.v1.GetClustersCurrentStateResponse + 36, // 76: api.v1.ClusterService.GetTeamTimeSeries:output_type -> api.v1.GetTeamTimeSeriesResponse + 40, // 77: api.v1.ClusterService.GetClustersPeriodDeltas:output_type -> api.v1.GetClustersPeriodDeltasResponse + 65, // [65:78] is the sub-list for method output_type + 52, // [52:65] is the sub-list for method input_type + 52, // [52:52] is the sub-list for extension type_name + 52, // [52:52] is the sub-list for extension extendee + 0, // [0:52] is the sub-list for field type_name +} + +func init() { file_api_v1_cluster_proto_init() } +func file_api_v1_cluster_proto_init() { + if File_api_v1_cluster_proto != nil { + return + } + file_api_v1_common_proto_init() + file_api_v1_k8s_proto_init() + if !protoimpl.UnsafeEnabled { + file_api_v1_cluster_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetClustersBasicInfoRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_api_v1_cluster_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetClustersBasicInfoResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_api_v1_cluster_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetClusterBasicInfoRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache case 2: return &v.unknownFields default: @@ -2942,17 +4158,151 @@ func file_api_v1_cluster_proto_init() { return nil } } + file_api_v1_cluster_proto_msgTypes[29].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetClustersCurrentStateRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_api_v1_cluster_proto_msgTypes[30].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetClustersCurrentStateResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_api_v1_cluster_proto_msgTypes[31].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ClusterCurrentState); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_api_v1_cluster_proto_msgTypes[32].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ClusterResourceSnapshot); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_api_v1_cluster_proto_msgTypes[33].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ClusterUsageSnapshot); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_api_v1_cluster_proto_msgTypes[34].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetTeamTimeSeriesRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_api_v1_cluster_proto_msgTypes[35].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetTeamTimeSeriesResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_api_v1_cluster_proto_msgTypes[36].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*TeamTimeSeriesPoint); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_api_v1_cluster_proto_msgTypes[37].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetClustersPeriodDeltasRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_api_v1_cluster_proto_msgTypes[38].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ClusterPeriodDelta); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_api_v1_cluster_proto_msgTypes[39].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetClustersPeriodDeltasResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } } file_api_v1_cluster_proto_msgTypes[4].OneofWrappers = []interface{}{} file_api_v1_cluster_proto_msgTypes[23].OneofWrappers = []interface{}{} file_api_v1_cluster_proto_msgTypes[25].OneofWrappers = []interface{}{} + file_api_v1_cluster_proto_msgTypes[31].OneofWrappers = []interface{}{} + file_api_v1_cluster_proto_msgTypes[32].OneofWrappers = []interface{}{} type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_api_v1_cluster_proto_rawDesc, NumEnums: 1, - NumMessages: 29, + NumMessages: 40, NumExtensions: 0, NumServices: 1, }, diff --git a/gen/api/v1/cluster_grpc.pb.go b/gen/api/v1/cluster_grpc.pb.go index 668fd8b5..8e78aeef 100644 --- a/gen/api/v1/cluster_grpc.pb.go +++ b/gen/api/v1/cluster_grpc.pb.go @@ -29,6 +29,9 @@ const ( ClusterService_GetNodeTypeCounts_FullMethodName = "/api.v1.ClusterService/GetNodeTypeCounts" ClusterService_GetClusterIDByName_FullMethodName = "/api.v1.ClusterService/GetClusterIDByName" ClusterService_GetClusterInfoByName_FullMethodName = "/api.v1.ClusterService/GetClusterInfoByName" + ClusterService_GetClustersCurrentState_FullMethodName = "/api.v1.ClusterService/GetClustersCurrentState" + ClusterService_GetTeamTimeSeries_FullMethodName = "/api.v1.ClusterService/GetTeamTimeSeries" + ClusterService_GetClustersPeriodDeltas_FullMethodName = "/api.v1.ClusterService/GetClustersPeriodDeltas" ) // ClusterServiceClient is the client API for ClusterService service. @@ -55,6 +58,12 @@ type ClusterServiceClient interface { GetClusterIDByName(ctx context.Context, in *GetClusterIDByNameRequest, opts ...grpc.CallOption) (*GetClusterIDByNameResponse, error) // GetClusterInfoByName retrieves full info of the first active cluster matching the given team and name GetClusterInfoByName(ctx context.Context, in *GetClusterInfoByNameRequest, opts ...grpc.CallOption) (*GetClusterInfoByNameResponse, error) + // GetClustersCurrentState returns identity plus the latest observed resource snapshot per + // cluster. Nothing in the response depends on a time range. + GetClustersCurrentState(ctx context.Context, in *GetClustersCurrentStateRequest, opts ...grpc.CallOption) (*GetClustersCurrentStateResponse, error) + // GetTeamTimeSeries returns fleet-level points over [start_time, end_time). + GetTeamTimeSeries(ctx context.Context, in *GetTeamTimeSeriesRequest, opts ...grpc.CallOption) (*GetTeamTimeSeriesResponse, error) + GetClustersPeriodDeltas(ctx context.Context, in *GetClustersPeriodDeltasRequest, opts ...grpc.CallOption) (*GetClustersPeriodDeltasResponse, error) } type clusterServiceClient struct { @@ -155,6 +164,33 @@ func (c *clusterServiceClient) GetClusterInfoByName(ctx context.Context, in *Get return out, nil } +func (c *clusterServiceClient) GetClustersCurrentState(ctx context.Context, in *GetClustersCurrentStateRequest, opts ...grpc.CallOption) (*GetClustersCurrentStateResponse, error) { + out := new(GetClustersCurrentStateResponse) + err := c.cc.Invoke(ctx, ClusterService_GetClustersCurrentState_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *clusterServiceClient) GetTeamTimeSeries(ctx context.Context, in *GetTeamTimeSeriesRequest, opts ...grpc.CallOption) (*GetTeamTimeSeriesResponse, error) { + out := new(GetTeamTimeSeriesResponse) + err := c.cc.Invoke(ctx, ClusterService_GetTeamTimeSeries_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *clusterServiceClient) GetClustersPeriodDeltas(ctx context.Context, in *GetClustersPeriodDeltasRequest, opts ...grpc.CallOption) (*GetClustersPeriodDeltasResponse, error) { + out := new(GetClustersPeriodDeltasResponse) + err := c.cc.Invoke(ctx, ClusterService_GetClustersPeriodDeltas_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + // ClusterServiceServer is the server API for ClusterService service. // All implementations must embed UnimplementedClusterServiceServer // for forward compatibility @@ -179,6 +215,12 @@ type ClusterServiceServer interface { GetClusterIDByName(context.Context, *GetClusterIDByNameRequest) (*GetClusterIDByNameResponse, error) // GetClusterInfoByName retrieves full info of the first active cluster matching the given team and name GetClusterInfoByName(context.Context, *GetClusterInfoByNameRequest) (*GetClusterInfoByNameResponse, error) + // GetClustersCurrentState returns identity plus the latest observed resource snapshot per + // cluster. Nothing in the response depends on a time range. + GetClustersCurrentState(context.Context, *GetClustersCurrentStateRequest) (*GetClustersCurrentStateResponse, error) + // GetTeamTimeSeries returns fleet-level points over [start_time, end_time). + GetTeamTimeSeries(context.Context, *GetTeamTimeSeriesRequest) (*GetTeamTimeSeriesResponse, error) + GetClustersPeriodDeltas(context.Context, *GetClustersPeriodDeltasRequest) (*GetClustersPeriodDeltasResponse, error) mustEmbedUnimplementedClusterServiceServer() } @@ -216,6 +258,15 @@ func (UnimplementedClusterServiceServer) GetClusterIDByName(context.Context, *Ge func (UnimplementedClusterServiceServer) GetClusterInfoByName(context.Context, *GetClusterInfoByNameRequest) (*GetClusterInfoByNameResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method GetClusterInfoByName not implemented") } +func (UnimplementedClusterServiceServer) GetClustersCurrentState(context.Context, *GetClustersCurrentStateRequest) (*GetClustersCurrentStateResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetClustersCurrentState not implemented") +} +func (UnimplementedClusterServiceServer) GetTeamTimeSeries(context.Context, *GetTeamTimeSeriesRequest) (*GetTeamTimeSeriesResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetTeamTimeSeries not implemented") +} +func (UnimplementedClusterServiceServer) GetClustersPeriodDeltas(context.Context, *GetClustersPeriodDeltasRequest) (*GetClustersPeriodDeltasResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetClustersPeriodDeltas not implemented") +} func (UnimplementedClusterServiceServer) mustEmbedUnimplementedClusterServiceServer() {} // UnsafeClusterServiceServer may be embedded to opt out of forward compatibility for this service. @@ -409,6 +460,60 @@ func _ClusterService_GetClusterInfoByName_Handler(srv interface{}, ctx context.C return interceptor(ctx, in, info, handler) } +func _ClusterService_GetClustersCurrentState_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(GetClustersCurrentStateRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ClusterServiceServer).GetClustersCurrentState(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: ClusterService_GetClustersCurrentState_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ClusterServiceServer).GetClustersCurrentState(ctx, req.(*GetClustersCurrentStateRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _ClusterService_GetTeamTimeSeries_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(GetTeamTimeSeriesRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ClusterServiceServer).GetTeamTimeSeries(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: ClusterService_GetTeamTimeSeries_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ClusterServiceServer).GetTeamTimeSeries(ctx, req.(*GetTeamTimeSeriesRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _ClusterService_GetClustersPeriodDeltas_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(GetClustersPeriodDeltasRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ClusterServiceServer).GetClustersPeriodDeltas(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: ClusterService_GetClustersPeriodDeltas_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ClusterServiceServer).GetClustersPeriodDeltas(ctx, req.(*GetClustersPeriodDeltasRequest)) + } + return interceptor(ctx, in, info, handler) +} + // ClusterService_ServiceDesc is the grpc.ServiceDesc for ClusterService service. // It's only intended for direct use with grpc.RegisterService, // and not to be introspected or modified (even as a copy) @@ -456,6 +561,18 @@ var ClusterService_ServiceDesc = grpc.ServiceDesc{ MethodName: "GetClusterInfoByName", Handler: _ClusterService_GetClusterInfoByName_Handler, }, + { + MethodName: "GetClustersCurrentState", + Handler: _ClusterService_GetClustersCurrentState_Handler, + }, + { + MethodName: "GetTeamTimeSeries", + Handler: _ClusterService_GetTeamTimeSeries_Handler, + }, + { + MethodName: "GetClustersPeriodDeltas", + Handler: _ClusterService_GetClustersPeriodDeltas_Handler, + }, }, Streams: []grpc.StreamDesc{}, Metadata: "api/v1/cluster.proto", diff --git a/gen/api/v1/common.pb.go b/gen/api/v1/common.pb.go index 724ea0a6..82b4b472 100644 --- a/gen/api/v1/common.pb.go +++ b/gen/api/v1/common.pb.go @@ -73,6 +73,93 @@ const ( K8SObjectKind_K8S_OBJECT_KIND_GATEKEEPER_CONSTRAINT_TEMPLATE K8SObjectKind = 40 // Gatekeeper constraint instances (constraints.gatekeeper.sh, dynamic kinds). K8SObjectKind_K8S_OBJECT_KIND_GATEKEEPER_CONSTRAINT K8SObjectKind = 41 + // Argo CD Application (argoproj.io/v1alpha1). The GitOps unit of deployment. + // Its status carries sync/health plus the list of resources it manages. + K8SObjectKind_K8S_OBJECT_KIND_ARGO_APPLICATION K8SObjectKind = 42 + // Argo CD ApplicationSet (argoproj.io/v1alpha1). Generates Applications from + // one or more generators; its children are Applications, not workloads. + K8SObjectKind_K8S_OBJECT_KIND_ARGO_APPLICATION_SET K8SObjectKind = 43 + // CustomResourceDefinition (apiextensions.k8s.io/v1). The declaration of an + // API extension, not an instance of one: it answers which CRDs an operator + // installed on the cluster, at which versions, and whether the API server + // established them. + K8SObjectKind_K8S_OBJECT_KIND_CUSTOM_RESOURCE_DEFINITION K8SObjectKind = 44 + // FluxCD GitRepository (source.toolkit.fluxcd.io/v1). A source that produces + // an Artifact tarball from a Git ref. + K8SObjectKind_K8S_OBJECT_KIND_FLUX_GIT_REPOSITORY K8SObjectKind = 45 + // FluxCD OCIRepository (source.toolkit.fluxcd.io/v1). A source that produces + // an Artifact tarball from an OCI-compliant registry. + K8SObjectKind_K8S_OBJECT_KIND_FLUX_OCI_REPOSITORY K8SObjectKind = 46 + // FluxCD HelmRepository (source.toolkit.fluxcd.io/v1). A source pointing at a + // Helm chart repository (or OCI registry acting as one). + K8SObjectKind_K8S_OBJECT_KIND_FLUX_HELM_REPOSITORY K8SObjectKind = 47 + // FluxCD HelmChart (source.toolkit.fluxcd.io/v1). A source that packages a + // Helm chart resolved from a HelmRepository/GitRepository/Bucket sourceRef. + K8SObjectKind_K8S_OBJECT_KIND_FLUX_HELM_CHART K8SObjectKind = 48 + // FluxCD Bucket (source.toolkit.fluxcd.io/v1). A source that produces an + // Artifact tarball from an S3-compatible bucket. + K8SObjectKind_K8S_OBJECT_KIND_FLUX_BUCKET K8SObjectKind = 49 + // FluxCD Kustomization (kustomize.toolkit.fluxcd.io/v1). Reconciles a + // kustomize overlay from a sourceRef's Artifact onto the cluster. + K8SObjectKind_K8S_OBJECT_KIND_FLUX_KUSTOMIZATION K8SObjectKind = 50 + // FluxCD HelmRelease (helm.toolkit.fluxcd.io/v2). Reconciles a Helm chart + // install/upgrade from a chartRef or chart.spec.sourceRef. + K8SObjectKind_K8S_OBJECT_KIND_FLUX_HELM_RELEASE K8SObjectKind = 51 + // FluxCD ExternalArtifact (source.toolkit.fluxcd.io/v1). An Artifact produced + // by a third-party controller outside the source-controller sources above. + K8SObjectKind_K8S_OBJECT_KIND_FLUX_EXTERNAL_ARTIFACT K8SObjectKind = 52 + // FluxCD Alert (notification.toolkit.fluxcd.io/v1beta3). Routes reconciler + // events matching a set of sources to a Provider. + K8SObjectKind_K8S_OBJECT_KIND_FLUX_ALERT K8SObjectKind = 53 + // FluxCD Provider (notification.toolkit.fluxcd.io/v1beta3). A notification + // sink (Slack, webhook, etc.) that Alerts and Receivers reference. + K8SObjectKind_K8S_OBJECT_KIND_FLUX_PROVIDER K8SObjectKind = 54 + // FluxCD Receiver (notification.toolkit.fluxcd.io/v1). A webhook endpoint + // that triggers reconciliation of referenced resources. + K8SObjectKind_K8S_OBJECT_KIND_FLUX_RECEIVER K8SObjectKind = 55 + // Flux Operator FluxInstance (fluxcd.controlplane.io/v1). Declares and + // manages the Flux controller installation itself. + K8SObjectKind_K8S_OBJECT_KIND_FLUX_INSTANCE K8SObjectKind = 56 + // Flux Operator FluxReport (fluxcd.controlplane.io/v1). A generated report + // summarizing the health of the managed Flux installation. + K8SObjectKind_K8S_OBJECT_KIND_FLUX_REPORT K8SObjectKind = 57 + // Flux Operator ResourceSetInputProvider (fluxcd.controlplane.io/v1). Supplies + // dynamic inputs (e.g. from a Git provider API) to a ResourceSet. + K8SObjectKind_K8S_OBJECT_KIND_FLUX_RESOURCE_SET_INPUT_PROVIDER K8SObjectKind = 58 + // Flux Operator ResourceSet (fluxcd.controlplane.io/v1). Templates a set of + // resources from static or provider-supplied inputs. + K8SObjectKind_K8S_OBJECT_KIND_FLUX_RESOURCE_SET K8SObjectKind = 59 + // FluxCD ImageRepository (image.toolkit.fluxcd.io/v1). Scans a container + // registry for available tags. + K8SObjectKind_K8S_OBJECT_KIND_FLUX_IMAGE_REPOSITORY K8SObjectKind = 60 + // FluxCD ImagePolicy (image.toolkit.fluxcd.io/v1). Selects the latest tag + // from an ImageRepository per a policy (semver, alphabetical, numerical). + K8SObjectKind_K8S_OBJECT_KIND_FLUX_IMAGE_POLICY K8SObjectKind = 61 + // FluxCD ImageUpdateAutomation (image.toolkit.fluxcd.io/v1). Commits + // ImagePolicy-selected tags back into a GitRepository. + K8SObjectKind_K8S_OBJECT_KIND_FLUX_IMAGE_UPDATE_AUTOMATION K8SObjectKind = 62 + // Synthetic, explorer-only kind: the "Flux Resources" aggregating page, + // covering all 11 collector-only Flux kinds (ExternalArtifact, Alert, + // Provider, Receiver, FluxInstance, FluxReport, ResourceSetInputProvider, + // ResourceSet, ImageRepository, ImagePolicy, ImageUpdateAutomation) in one + // list -- mirrors KARPENTER_RESOURCE's shape (one page, one shared table, + // a Resource Type column disambiguates rows). No on-cluster object ever + // reports this as its kind; it exists only as an explorer routing target, + // so it has no models.K8sObjectKind FromString() case. + K8SObjectKind_K8S_OBJECT_KIND_FLUX_RESOURCE K8SObjectKind = 63 + // Flagger Canary (flagger.app/v1beta1). Declares a progressive delivery + // rollout for one target workload: the analysis to run, the traffic-shifting + // steps to take and the promotion state machine. Runs no pods of its own -- + // it drives the primary and canary copies of the workload it targets. + K8SObjectKind_K8S_OBJECT_KIND_FLAGGER_CANARY K8SObjectKind = 64 + // Flagger MetricTemplate (flagger.app/v1beta1). A named, reusable query + // against a metrics provider (Prometheus, Datadog, CloudWatch, ...) that a + // Canary analysis step references by templateRef. Runs no pods. + K8SObjectKind_K8S_OBJECT_KIND_FLAGGER_METRIC_TEMPLATE K8SObjectKind = 65 + // Flagger AlertProvider (flagger.app/v1beta1). A notification target (Slack, + // Teams, Discord, Rocket, generic webhook) that a Canary alert references by + // providerRef. Runs no pods. + K8SObjectKind_K8S_OBJECT_KIND_FLAGGER_ALERT_PROVIDER K8SObjectKind = 66 ) // Enum value maps for K8SObjectKind. @@ -120,50 +207,100 @@ var ( 39: "K8S_OBJECT_KIND_KYVERNO_POLICY_REPORT", 40: "K8S_OBJECT_KIND_GATEKEEPER_CONSTRAINT_TEMPLATE", 41: "K8S_OBJECT_KIND_GATEKEEPER_CONSTRAINT", + 42: "K8S_OBJECT_KIND_ARGO_APPLICATION", + 43: "K8S_OBJECT_KIND_ARGO_APPLICATION_SET", + 44: "K8S_OBJECT_KIND_CUSTOM_RESOURCE_DEFINITION", + 45: "K8S_OBJECT_KIND_FLUX_GIT_REPOSITORY", + 46: "K8S_OBJECT_KIND_FLUX_OCI_REPOSITORY", + 47: "K8S_OBJECT_KIND_FLUX_HELM_REPOSITORY", + 48: "K8S_OBJECT_KIND_FLUX_HELM_CHART", + 49: "K8S_OBJECT_KIND_FLUX_BUCKET", + 50: "K8S_OBJECT_KIND_FLUX_KUSTOMIZATION", + 51: "K8S_OBJECT_KIND_FLUX_HELM_RELEASE", + 52: "K8S_OBJECT_KIND_FLUX_EXTERNAL_ARTIFACT", + 53: "K8S_OBJECT_KIND_FLUX_ALERT", + 54: "K8S_OBJECT_KIND_FLUX_PROVIDER", + 55: "K8S_OBJECT_KIND_FLUX_RECEIVER", + 56: "K8S_OBJECT_KIND_FLUX_INSTANCE", + 57: "K8S_OBJECT_KIND_FLUX_REPORT", + 58: "K8S_OBJECT_KIND_FLUX_RESOURCE_SET_INPUT_PROVIDER", + 59: "K8S_OBJECT_KIND_FLUX_RESOURCE_SET", + 60: "K8S_OBJECT_KIND_FLUX_IMAGE_REPOSITORY", + 61: "K8S_OBJECT_KIND_FLUX_IMAGE_POLICY", + 62: "K8S_OBJECT_KIND_FLUX_IMAGE_UPDATE_AUTOMATION", + 63: "K8S_OBJECT_KIND_FLUX_RESOURCE", + 64: "K8S_OBJECT_KIND_FLAGGER_CANARY", + 65: "K8S_OBJECT_KIND_FLAGGER_METRIC_TEMPLATE", + 66: "K8S_OBJECT_KIND_FLAGGER_ALERT_PROVIDER", } K8SObjectKind_value = map[string]int32{ - "K8S_OBJECT_KIND_UNSPECIFIED": 0, - "K8S_OBJECT_KIND_DEPLOYMENT": 1, - "K8S_OBJECT_KIND_REPLICA_SET": 2, - "K8S_OBJECT_KIND_STATEFUL_SET": 3, - "K8S_OBJECT_KIND_DAEMON_SET": 4, - "K8S_OBJECT_KIND_JOB": 5, - "K8S_OBJECT_KIND_CRON_JOB": 6, - "K8S_OBJECT_KIND_REPLICATION_CONTROLLER": 7, - "K8S_OBJECT_KIND_POD": 8, - "K8S_OBJECT_KIND_NAMESPACE": 9, - "K8S_OBJECT_KIND_NODE": 10, - "K8S_OBJECT_KIND_INGRESS": 11, - "K8S_OBJECT_KIND_CONTAINER": 12, - "K8S_OBJECT_KIND_SERVICE": 13, - "K8S_OBJECT_KIND_CONFIG_MAP": 14, - "K8S_OBJECT_KIND_PERSISTENT_VOLUME": 15, - "K8S_OBJECT_KIND_PERSISTENT_VOLUME_CLAIM": 16, - "K8S_OBJECT_KIND_STORAGE_CLASS": 17, - "K8S_OBJECT_KIND_ARGO_ROLLOUT": 18, - "K8S_OBJECT_KIND_HORIZONTAL_POD_AUTOSCALER": 19, - "K8S_OBJECT_KIND_VERTICAL_POD_AUTOSCALER": 20, - "K8S_OBJECT_KIND_LIMIT_RANGE": 21, - "K8S_OBJECT_KIND_SERVICE_ACCOUNT": 22, - "K8S_OBJECT_KIND_ROLE": 23, - "K8S_OBJECT_KIND_ROLE_BINDING": 24, - "K8S_OBJECT_KIND_KEDA_SCALED_OBJECT": 25, - "K8S_OBJECT_KIND_KARPENTER_RESOURCE": 26, - "K8S_OBJECT_KIND_POD_DISRUPTION_BUDGET": 27, - "K8S_OBJECT_KIND_RESOURCE_QUOTA": 28, - "K8S_OBJECT_KIND_KUBEFLOW_NOTEBOOK": 29, - "K8S_OBJECT_KIND_VOLCANO_JOB": 30, - "K8S_OBJECT_KIND_SECRET": 31, - "K8S_OBJECT_KIND_CLUSTER_ROLE": 32, - "K8S_OBJECT_KIND_CLUSTER_ROLE_BINDING": 33, - "K8S_OBJECT_KIND_NETWORK_POLICY": 34, - "K8S_OBJECT_KIND_CNPG_CLUSTER": 35, - "K8S_OBJECT_KIND_SPARK_APPLICATION": 36, - "K8S_OBJECT_KIND_SCHEDULED_SPARK_APPLICATION": 37, - "K8S_OBJECT_KIND_KYVERNO_POLICY": 38, - "K8S_OBJECT_KIND_KYVERNO_POLICY_REPORT": 39, - "K8S_OBJECT_KIND_GATEKEEPER_CONSTRAINT_TEMPLATE": 40, - "K8S_OBJECT_KIND_GATEKEEPER_CONSTRAINT": 41, + "K8S_OBJECT_KIND_UNSPECIFIED": 0, + "K8S_OBJECT_KIND_DEPLOYMENT": 1, + "K8S_OBJECT_KIND_REPLICA_SET": 2, + "K8S_OBJECT_KIND_STATEFUL_SET": 3, + "K8S_OBJECT_KIND_DAEMON_SET": 4, + "K8S_OBJECT_KIND_JOB": 5, + "K8S_OBJECT_KIND_CRON_JOB": 6, + "K8S_OBJECT_KIND_REPLICATION_CONTROLLER": 7, + "K8S_OBJECT_KIND_POD": 8, + "K8S_OBJECT_KIND_NAMESPACE": 9, + "K8S_OBJECT_KIND_NODE": 10, + "K8S_OBJECT_KIND_INGRESS": 11, + "K8S_OBJECT_KIND_CONTAINER": 12, + "K8S_OBJECT_KIND_SERVICE": 13, + "K8S_OBJECT_KIND_CONFIG_MAP": 14, + "K8S_OBJECT_KIND_PERSISTENT_VOLUME": 15, + "K8S_OBJECT_KIND_PERSISTENT_VOLUME_CLAIM": 16, + "K8S_OBJECT_KIND_STORAGE_CLASS": 17, + "K8S_OBJECT_KIND_ARGO_ROLLOUT": 18, + "K8S_OBJECT_KIND_HORIZONTAL_POD_AUTOSCALER": 19, + "K8S_OBJECT_KIND_VERTICAL_POD_AUTOSCALER": 20, + "K8S_OBJECT_KIND_LIMIT_RANGE": 21, + "K8S_OBJECT_KIND_SERVICE_ACCOUNT": 22, + "K8S_OBJECT_KIND_ROLE": 23, + "K8S_OBJECT_KIND_ROLE_BINDING": 24, + "K8S_OBJECT_KIND_KEDA_SCALED_OBJECT": 25, + "K8S_OBJECT_KIND_KARPENTER_RESOURCE": 26, + "K8S_OBJECT_KIND_POD_DISRUPTION_BUDGET": 27, + "K8S_OBJECT_KIND_RESOURCE_QUOTA": 28, + "K8S_OBJECT_KIND_KUBEFLOW_NOTEBOOK": 29, + "K8S_OBJECT_KIND_VOLCANO_JOB": 30, + "K8S_OBJECT_KIND_SECRET": 31, + "K8S_OBJECT_KIND_CLUSTER_ROLE": 32, + "K8S_OBJECT_KIND_CLUSTER_ROLE_BINDING": 33, + "K8S_OBJECT_KIND_NETWORK_POLICY": 34, + "K8S_OBJECT_KIND_CNPG_CLUSTER": 35, + "K8S_OBJECT_KIND_SPARK_APPLICATION": 36, + "K8S_OBJECT_KIND_SCHEDULED_SPARK_APPLICATION": 37, + "K8S_OBJECT_KIND_KYVERNO_POLICY": 38, + "K8S_OBJECT_KIND_KYVERNO_POLICY_REPORT": 39, + "K8S_OBJECT_KIND_GATEKEEPER_CONSTRAINT_TEMPLATE": 40, + "K8S_OBJECT_KIND_GATEKEEPER_CONSTRAINT": 41, + "K8S_OBJECT_KIND_ARGO_APPLICATION": 42, + "K8S_OBJECT_KIND_ARGO_APPLICATION_SET": 43, + "K8S_OBJECT_KIND_CUSTOM_RESOURCE_DEFINITION": 44, + "K8S_OBJECT_KIND_FLUX_GIT_REPOSITORY": 45, + "K8S_OBJECT_KIND_FLUX_OCI_REPOSITORY": 46, + "K8S_OBJECT_KIND_FLUX_HELM_REPOSITORY": 47, + "K8S_OBJECT_KIND_FLUX_HELM_CHART": 48, + "K8S_OBJECT_KIND_FLUX_BUCKET": 49, + "K8S_OBJECT_KIND_FLUX_KUSTOMIZATION": 50, + "K8S_OBJECT_KIND_FLUX_HELM_RELEASE": 51, + "K8S_OBJECT_KIND_FLUX_EXTERNAL_ARTIFACT": 52, + "K8S_OBJECT_KIND_FLUX_ALERT": 53, + "K8S_OBJECT_KIND_FLUX_PROVIDER": 54, + "K8S_OBJECT_KIND_FLUX_RECEIVER": 55, + "K8S_OBJECT_KIND_FLUX_INSTANCE": 56, + "K8S_OBJECT_KIND_FLUX_REPORT": 57, + "K8S_OBJECT_KIND_FLUX_RESOURCE_SET_INPUT_PROVIDER": 58, + "K8S_OBJECT_KIND_FLUX_RESOURCE_SET": 59, + "K8S_OBJECT_KIND_FLUX_IMAGE_REPOSITORY": 60, + "K8S_OBJECT_KIND_FLUX_IMAGE_POLICY": 61, + "K8S_OBJECT_KIND_FLUX_IMAGE_UPDATE_AUTOMATION": 62, + "K8S_OBJECT_KIND_FLUX_RESOURCE": 63, + "K8S_OBJECT_KIND_FLAGGER_CANARY": 64, + "K8S_OBJECT_KIND_FLAGGER_METRIC_TEMPLATE": 65, + "K8S_OBJECT_KIND_FLAGGER_ALERT_PROVIDER": 66, } ) @@ -241,13 +378,155 @@ func (OrderByEnum) EnumDescriptor() ([]byte, []int) { return file_api_v1_common_proto_rawDescGZIP(), []int{1} } -// LabelSelectorOperator is the set of operators that can be used in a selector requirement. +// NodeGroupProvisioner says who creates a node group's nodes. Derived server-side +// from node labels plus the cluster's dakr-managed NodePool set; see +// dakr/services/nodegroupsim/provisioner.go. +type NodeGroupProvisioner int32 + +const ( + NodeGroupProvisioner_NODE_GROUP_PROVISIONER_UNSPECIFIED NodeGroupProvisioner = 0 + NodeGroupProvisioner_NODE_GROUP_PROVISIONER_CLUSTER_AUTOSCALER NodeGroupProvisioner = 1 + NodeGroupProvisioner_NODE_GROUP_PROVISIONER_KARPENTER NodeGroupProvisioner = 2 + NodeGroupProvisioner_NODE_GROUP_PROVISIONER_DEVZERO_NODE_POLICY NodeGroupProvisioner = 3 +) + +// Enum value maps for NodeGroupProvisioner. +var ( + NodeGroupProvisioner_name = map[int32]string{ + 0: "NODE_GROUP_PROVISIONER_UNSPECIFIED", + 1: "NODE_GROUP_PROVISIONER_CLUSTER_AUTOSCALER", + 2: "NODE_GROUP_PROVISIONER_KARPENTER", + 3: "NODE_GROUP_PROVISIONER_DEVZERO_NODE_POLICY", + } + NodeGroupProvisioner_value = map[string]int32{ + "NODE_GROUP_PROVISIONER_UNSPECIFIED": 0, + "NODE_GROUP_PROVISIONER_CLUSTER_AUTOSCALER": 1, + "NODE_GROUP_PROVISIONER_KARPENTER": 2, + "NODE_GROUP_PROVISIONER_DEVZERO_NODE_POLICY": 3, + } +) + +func (x NodeGroupProvisioner) Enum() *NodeGroupProvisioner { + p := new(NodeGroupProvisioner) + *p = x + return p +} + +func (x NodeGroupProvisioner) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (NodeGroupProvisioner) Descriptor() protoreflect.EnumDescriptor { + return file_api_v1_common_proto_enumTypes[2].Descriptor() +} + +func (NodeGroupProvisioner) Type() protoreflect.EnumType { + return &file_api_v1_common_proto_enumTypes[2] +} + +func (x NodeGroupProvisioner) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use NodeGroupProvisioner.Descriptor instead. +func (NodeGroupProvisioner) EnumDescriptor() ([]byte, []int) { + return file_api_v1_common_proto_rawDescGZIP(), []int{2} +} + +// NodeManager names what created a single node. Unlike NodeGroupProvisioner, +// which classifies a whole group from its nodes' labels pooled together, this +// is decided per node; see dakr/k8s-gen/k8smodels.Node.ManagedBy. +type NodeManager int32 + +const ( + NodeManager_NODE_MANAGER_UNSPECIFIED NodeManager = 0 + NodeManager_NODE_MANAGER_KARPENTER NodeManager = 1 + NodeManager_NODE_MANAGER_EKS_NODE_GROUP NodeManager = 2 + NodeManager_NODE_MANAGER_EKSCTL NodeManager = 3 + NodeManager_NODE_MANAGER_GKE_NODE_POOL NodeManager = 4 + NodeManager_NODE_MANAGER_AKS_AGENT_POOL NodeManager = 5 + NodeManager_NODE_MANAGER_CAST_AI NodeManager = 6 +) + +// Enum value maps for NodeManager. +var ( + NodeManager_name = map[int32]string{ + 0: "NODE_MANAGER_UNSPECIFIED", + 1: "NODE_MANAGER_KARPENTER", + 2: "NODE_MANAGER_EKS_NODE_GROUP", + 3: "NODE_MANAGER_EKSCTL", + 4: "NODE_MANAGER_GKE_NODE_POOL", + 5: "NODE_MANAGER_AKS_AGENT_POOL", + 6: "NODE_MANAGER_CAST_AI", + } + NodeManager_value = map[string]int32{ + "NODE_MANAGER_UNSPECIFIED": 0, + "NODE_MANAGER_KARPENTER": 1, + "NODE_MANAGER_EKS_NODE_GROUP": 2, + "NODE_MANAGER_EKSCTL": 3, + "NODE_MANAGER_GKE_NODE_POOL": 4, + "NODE_MANAGER_AKS_AGENT_POOL": 5, + "NODE_MANAGER_CAST_AI": 6, + } +) + +func (x NodeManager) Enum() *NodeManager { + p := new(NodeManager) + *p = x + return p +} + +func (x NodeManager) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (NodeManager) Descriptor() protoreflect.EnumDescriptor { + return file_api_v1_common_proto_enumTypes[3].Descriptor() +} + +func (NodeManager) Type() protoreflect.EnumType { + return &file_api_v1_common_proto_enumTypes[3] +} + +func (x NodeManager) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use NodeManager.Descriptor instead. +func (NodeManager) EnumDescriptor() ([]byte, []int) { + return file_api_v1_common_proto_rawDescGZIP(), []int{3} +} + +// LabelSelectorOperator is the set of operators that can be used in a selector +// requirement. +// +// The message shape mirrors Kubernetes metav1.LabelSelector, and matching +// follows Kubernetes for the IN and NOT_IN operators. In particular, a +// workload whose labels lack the key still matches NOT_IN, because a missing +// key is not in the set. This agrees with Kubernetes: in +// k8s.io/apimachinery/pkg/labels, Requirement.Matches returns true for NotIn +// when the key is absent, so it is not a divergence. +// +// Two DevZero-specific caveats apply (PC-1452): +// +// First, the department and workload filter paths implement only IN and +// NOT_IN. EXISTS, DOES_NOT_EXIST, GT, and LT are part of this proto for shape +// compatibility but are not honored by those SQL filters. +// +// Second, a workload whose entire labels or annotations column is SQL NULL +// (an unset column, distinct from an empty map) is EXCLUDED from a NOT_IN +// match by the department query, which ANDs a column IS NOT NULL guard ahead +// of the selector condition (dakr/repositories/departments.go). This is a +// genuine divergence from Kubernetes, which matches a label-less object +// against NotIn. It is also inconsistent with the workload-metrics filter +// path, which matches a NULL column against NOT_IN +// (dakr/repositories/workload_metrics_filters.go). type LabelSelectorOperator int32 const ( LabelSelectorOperator_LABEL_SELECTOR_OPERATOR_UNSPECIFIED LabelSelectorOperator = 0 LabelSelectorOperator_LABEL_SELECTOR_OPERATOR_IN LabelSelectorOperator = 1 // In - LabelSelectorOperator_LABEL_SELECTOR_OPERATOR_NOT_IN LabelSelectorOperator = 2 // NotIn + LabelSelectorOperator_LABEL_SELECTOR_OPERATOR_NOT_IN LabelSelectorOperator = 2 // NotIn (missing key matches; see caveats above and PC-1452) LabelSelectorOperator_LABEL_SELECTOR_OPERATOR_EXISTS LabelSelectorOperator = 3 // Exists LabelSelectorOperator_LABEL_SELECTOR_OPERATOR_DOES_NOT_EXIST LabelSelectorOperator = 4 // DoesNotExist LabelSelectorOperator_LABEL_SELECTOR_OPERATOR_GT LabelSelectorOperator = 5 // GreaterThan @@ -287,11 +566,11 @@ func (x LabelSelectorOperator) String() string { } func (LabelSelectorOperator) Descriptor() protoreflect.EnumDescriptor { - return file_api_v1_common_proto_enumTypes[2].Descriptor() + return file_api_v1_common_proto_enumTypes[4].Descriptor() } func (LabelSelectorOperator) Type() protoreflect.EnumType { - return &file_api_v1_common_proto_enumTypes[2] + return &file_api_v1_common_proto_enumTypes[4] } func (x LabelSelectorOperator) Number() protoreflect.EnumNumber { @@ -300,7 +579,7 @@ func (x LabelSelectorOperator) Number() protoreflect.EnumNumber { // Deprecated: Use LabelSelectorOperator.Descriptor instead. func (LabelSelectorOperator) EnumDescriptor() ([]byte, []int) { - return file_api_v1_common_proto_rawDescGZIP(), []int{2} + return file_api_v1_common_proto_rawDescGZIP(), []int{4} } // Filter to select ACTIVE, DELETED, or BOTH types of workloads. @@ -340,11 +619,11 @@ func (x WorkloadStatusFilter) String() string { } func (WorkloadStatusFilter) Descriptor() protoreflect.EnumDescriptor { - return file_api_v1_common_proto_enumTypes[3].Descriptor() + return file_api_v1_common_proto_enumTypes[5].Descriptor() } func (WorkloadStatusFilter) Type() protoreflect.EnumType { - return &file_api_v1_common_proto_enumTypes[3] + return &file_api_v1_common_proto_enumTypes[5] } func (x WorkloadStatusFilter) Number() protoreflect.EnumNumber { @@ -353,7 +632,169 @@ func (x WorkloadStatusFilter) Number() protoreflect.EnumNumber { // Deprecated: Use WorkloadStatusFilter.Descriptor instead. func (WorkloadStatusFilter) EnumDescriptor() ([]byte, []int) { - return file_api_v1_common_proto_rawDescGZIP(), []int{3} + return file_api_v1_common_proto_rawDescGZIP(), []int{5} +} + +// WorkloadHealthStatusFilter selects workloads by the label the Health badge +// shows. RECOVERED overrides the severity label, exactly as the badge does. +type WorkloadHealthStatusFilter int32 + +const ( + WorkloadHealthStatusFilter_WORKLOAD_HEALTH_STATUS_FILTER_UNSPECIFIED WorkloadHealthStatusFilter = 0 + WorkloadHealthStatusFilter_WORKLOAD_HEALTH_STATUS_FILTER_HEALTHY WorkloadHealthStatusFilter = 1 + WorkloadHealthStatusFilter_WORKLOAD_HEALTH_STATUS_FILTER_WARNING WorkloadHealthStatusFilter = 2 + WorkloadHealthStatusFilter_WORKLOAD_HEALTH_STATUS_FILTER_CRITICAL WorkloadHealthStatusFilter = 3 + WorkloadHealthStatusFilter_WORKLOAD_HEALTH_STATUS_FILTER_RECOVERED WorkloadHealthStatusFilter = 4 +) + +// Enum value maps for WorkloadHealthStatusFilter. +var ( + WorkloadHealthStatusFilter_name = map[int32]string{ + 0: "WORKLOAD_HEALTH_STATUS_FILTER_UNSPECIFIED", + 1: "WORKLOAD_HEALTH_STATUS_FILTER_HEALTHY", + 2: "WORKLOAD_HEALTH_STATUS_FILTER_WARNING", + 3: "WORKLOAD_HEALTH_STATUS_FILTER_CRITICAL", + 4: "WORKLOAD_HEALTH_STATUS_FILTER_RECOVERED", + } + WorkloadHealthStatusFilter_value = map[string]int32{ + "WORKLOAD_HEALTH_STATUS_FILTER_UNSPECIFIED": 0, + "WORKLOAD_HEALTH_STATUS_FILTER_HEALTHY": 1, + "WORKLOAD_HEALTH_STATUS_FILTER_WARNING": 2, + "WORKLOAD_HEALTH_STATUS_FILTER_CRITICAL": 3, + "WORKLOAD_HEALTH_STATUS_FILTER_RECOVERED": 4, + } +) + +func (x WorkloadHealthStatusFilter) Enum() *WorkloadHealthStatusFilter { + p := new(WorkloadHealthStatusFilter) + *p = x + return p +} + +func (x WorkloadHealthStatusFilter) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (WorkloadHealthStatusFilter) Descriptor() protoreflect.EnumDescriptor { + return file_api_v1_common_proto_enumTypes[6].Descriptor() +} + +func (WorkloadHealthStatusFilter) Type() protoreflect.EnumType { + return &file_api_v1_common_proto_enumTypes[6] +} + +func (x WorkloadHealthStatusFilter) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use WorkloadHealthStatusFilter.Descriptor instead. +func (WorkloadHealthStatusFilter) EnumDescriptor() ([]byte, []int) { + return file_api_v1_common_proto_rawDescGZIP(), []int{6} +} + +// WorkloadHealthIssue selects workloads that had at least one event of this +// kind in the requested time window, regardless of current severity. +type WorkloadHealthIssue int32 + +const ( + WorkloadHealthIssue_WORKLOAD_HEALTH_ISSUE_UNSPECIFIED WorkloadHealthIssue = 0 + WorkloadHealthIssue_WORKLOAD_HEALTH_ISSUE_OOM WorkloadHealthIssue = 1 + WorkloadHealthIssue_WORKLOAD_HEALTH_ISSUE_CPU_THROTTLE WorkloadHealthIssue = 2 + WorkloadHealthIssue_WORKLOAD_HEALTH_ISSUE_CRASHLOOP WorkloadHealthIssue = 3 +) + +// Enum value maps for WorkloadHealthIssue. +var ( + WorkloadHealthIssue_name = map[int32]string{ + 0: "WORKLOAD_HEALTH_ISSUE_UNSPECIFIED", + 1: "WORKLOAD_HEALTH_ISSUE_OOM", + 2: "WORKLOAD_HEALTH_ISSUE_CPU_THROTTLE", + 3: "WORKLOAD_HEALTH_ISSUE_CRASHLOOP", + } + WorkloadHealthIssue_value = map[string]int32{ + "WORKLOAD_HEALTH_ISSUE_UNSPECIFIED": 0, + "WORKLOAD_HEALTH_ISSUE_OOM": 1, + "WORKLOAD_HEALTH_ISSUE_CPU_THROTTLE": 2, + "WORKLOAD_HEALTH_ISSUE_CRASHLOOP": 3, + } +) + +func (x WorkloadHealthIssue) Enum() *WorkloadHealthIssue { + p := new(WorkloadHealthIssue) + *p = x + return p +} + +func (x WorkloadHealthIssue) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (WorkloadHealthIssue) Descriptor() protoreflect.EnumDescriptor { + return file_api_v1_common_proto_enumTypes[7].Descriptor() +} + +func (WorkloadHealthIssue) Type() protoreflect.EnumType { + return &file_api_v1_common_proto_enumTypes[7] +} + +func (x WorkloadHealthIssue) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use WorkloadHealthIssue.Descriptor instead. +func (WorkloadHealthIssue) EnumDescriptor() ([]byte, []int) { + return file_api_v1_common_proto_rawDescGZIP(), []int{7} +} + +// HealthIssueMatchMode says how several WorkloadHealthIssue values combine. +// UNSPECIFIED behaves as ANY. +type HealthIssueMatchMode int32 + +const ( + HealthIssueMatchMode_HEALTH_ISSUE_MATCH_MODE_UNSPECIFIED HealthIssueMatchMode = 0 + HealthIssueMatchMode_HEALTH_ISSUE_MATCH_MODE_ANY HealthIssueMatchMode = 1 + HealthIssueMatchMode_HEALTH_ISSUE_MATCH_MODE_ALL HealthIssueMatchMode = 2 +) + +// Enum value maps for HealthIssueMatchMode. +var ( + HealthIssueMatchMode_name = map[int32]string{ + 0: "HEALTH_ISSUE_MATCH_MODE_UNSPECIFIED", + 1: "HEALTH_ISSUE_MATCH_MODE_ANY", + 2: "HEALTH_ISSUE_MATCH_MODE_ALL", + } + HealthIssueMatchMode_value = map[string]int32{ + "HEALTH_ISSUE_MATCH_MODE_UNSPECIFIED": 0, + "HEALTH_ISSUE_MATCH_MODE_ANY": 1, + "HEALTH_ISSUE_MATCH_MODE_ALL": 2, + } +) + +func (x HealthIssueMatchMode) Enum() *HealthIssueMatchMode { + p := new(HealthIssueMatchMode) + *p = x + return p +} + +func (x HealthIssueMatchMode) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (HealthIssueMatchMode) Descriptor() protoreflect.EnumDescriptor { + return file_api_v1_common_proto_enumTypes[8].Descriptor() +} + +func (HealthIssueMatchMode) Type() protoreflect.EnumType { + return &file_api_v1_common_proto_enumTypes[8] +} + +func (x HealthIssueMatchMode) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use HealthIssueMatchMode.Descriptor instead. +func (HealthIssueMatchMode) EnumDescriptor() ([]byte, []int) { + return file_api_v1_common_proto_rawDescGZIP(), []int{8} } // DisruptionBlockReason is the structured classification of a consolidation / scale-down @@ -396,7 +837,7 @@ const ( // Autoscaler has no equivalent override. DisruptionBlockReason_DISRUPTION_BLOCK_REASON_FORCE_DISRUPTED DisruptionBlockReason = 3 // Classified, but the block matched none of the above — e.g. a node-level block - // (uninitialized, nominated for a pending pod, already deleting), or any Cluster + // (uninitialized, already deleting, missing its NodePool label), or any Cluster // Autoscaler unremovable reason without its own bucket (NodeGroupMinSizeReached, // NotUnderutilized, CurrentlyBeingDeleted, ...). The verbatim `message` is the only // remaining detail, which is why it is never dropped. This is the REQUIRED fallback: @@ -411,6 +852,24 @@ const ( // so the node cannot be drained. Actionable, unlike NOT_UNNEEDED_LONG_ENOUGH. // Cluster-Autoscaler-only. DisruptionBlockReason_DISRUPTION_BLOCK_REASON_NO_PLACE_TO_MOVE_PODS DisruptionBlockReason = 6 + // The scheduler has provisionally assigned a pending pod to this node + // (pod.Status.NominatedNodeName). The autoscaler will not disrupt a node it is about to + // hand a pod, since that would just make the pod re-pend immediately — a self-resolving, + // expected-waiting state, not a misconfiguration. related_pod_name carries the pod. + // Derived structurally, not from the event message — reachable via the Karpenter path + // today. + DisruptionBlockReason_DISRUPTION_BLOCK_REASON_POD_NOMINATED DisruptionBlockReason = 7 + // Karpenter's own `disruption.budgets` allowance for the NodePool is currently spent, so + // it throttled itself. Operationally the opposite of every other value here: a config + // choice the cluster's owner made, not an obstruction to go find, which is why it is not + // folded into OTHER. Reported against the NodePool rather than a node, so it counts as a + // blocking event and never as a blocked node. Karpenter-only. + DisruptionBlockReason_DISRUPTION_BLOCK_REASON_DISRUPTION_BUDGET DisruptionBlockReason = 8 + // Karpenter's scheduling simulation found nowhere for the node's pods to go, so draining + // it would leave them pending. Same situation as NO_PLACE_TO_MOVE_PODS, which is spelled + // to match Cluster Autoscaler's own unremovable reason verbatim and is therefore kept + // CAS-only; the per-pod sub-reason stays readable in the verbatim `message`. + DisruptionBlockReason_DISRUPTION_BLOCK_REASON_PODS_WOULD_NOT_SCHEDULE DisruptionBlockReason = 9 ) // Enum value maps for DisruptionBlockReason. @@ -423,6 +882,9 @@ var ( 4: "DISRUPTION_BLOCK_REASON_OTHER", 5: "DISRUPTION_BLOCK_REASON_NOT_UNNEEDED_LONG_ENOUGH", 6: "DISRUPTION_BLOCK_REASON_NO_PLACE_TO_MOVE_PODS", + 7: "DISRUPTION_BLOCK_REASON_POD_NOMINATED", + 8: "DISRUPTION_BLOCK_REASON_DISRUPTION_BUDGET", + 9: "DISRUPTION_BLOCK_REASON_PODS_WOULD_NOT_SCHEDULE", } DisruptionBlockReason_value = map[string]int32{ "DISRUPTION_BLOCK_REASON_UNSPECIFIED": 0, @@ -432,6 +894,9 @@ var ( "DISRUPTION_BLOCK_REASON_OTHER": 4, "DISRUPTION_BLOCK_REASON_NOT_UNNEEDED_LONG_ENOUGH": 5, "DISRUPTION_BLOCK_REASON_NO_PLACE_TO_MOVE_PODS": 6, + "DISRUPTION_BLOCK_REASON_POD_NOMINATED": 7, + "DISRUPTION_BLOCK_REASON_DISRUPTION_BUDGET": 8, + "DISRUPTION_BLOCK_REASON_PODS_WOULD_NOT_SCHEDULE": 9, } ) @@ -446,11 +911,11 @@ func (x DisruptionBlockReason) String() string { } func (DisruptionBlockReason) Descriptor() protoreflect.EnumDescriptor { - return file_api_v1_common_proto_enumTypes[4].Descriptor() + return file_api_v1_common_proto_enumTypes[9].Descriptor() } func (DisruptionBlockReason) Type() protoreflect.EnumType { - return &file_api_v1_common_proto_enumTypes[4] + return &file_api_v1_common_proto_enumTypes[9] } func (x DisruptionBlockReason) Number() protoreflect.EnumNumber { @@ -459,7 +924,7 @@ func (x DisruptionBlockReason) Number() protoreflect.EnumNumber { // Deprecated: Use DisruptionBlockReason.Descriptor instead. func (DisruptionBlockReason) EnumDescriptor() ([]byte, []int) { - return file_api_v1_common_proto_rawDescGZIP(), []int{4} + return file_api_v1_common_proto_rawDescGZIP(), []int{9} } // Represents a single audit log entry @@ -808,6 +1273,13 @@ type CostInfo struct { MoneyOptimizedGpuCostPerHour *money.Money `protobuf:"bytes,119,opt,name=money_optimized_gpu_cost_per_hour,json=moneyOptimizedGpuCostPerHour,proto3" json:"money_optimized_gpu_cost_per_hour,omitempty"` MoneyCpuCostPerVcpuPerHour *money.Money `protobuf:"bytes,120,opt,name=money_cpu_cost_per_vcpu_per_hour,json=moneyCpuCostPerVcpuPerHour,proto3" json:"money_cpu_cost_per_vcpu_per_hour,omitempty"` MoneyMemoryCostPerGibPerHour *money.Money `protobuf:"bytes,121,opt,name=money_memory_cost_per_gib_per_hour,json=moneyMemoryCostPerGibPerHour,proto3" json:"money_memory_cost_per_gib_per_hour,omitempty"` + // Calendar-month figures. The month is the UTC calendar month containing the + // server's own clock, never a window derived from the request. Distinct from + // money_total_cost_per_month, which stays a rate extrapolated over a flat 730 + // hours and is unaffected by these. + MoneyMonthToDateCost *money.Money `protobuf:"bytes,123,opt,name=money_month_to_date_cost,json=moneyMonthToDateCost,proto3" json:"money_month_to_date_cost,omitempty"` // Spend already incurred, UTC month start to now. + MoneyProjectedMonthCost *money.Money `protobuf:"bytes,124,opt,name=money_projected_month_cost,json=moneyProjectedMonthCost,proto3" json:"money_projected_month_cost,omitempty"` // Month to date plus the current hourly rate over the hours left in the month. + MoneyOptimizedProjectedMonthCost *money.Money `protobuf:"bytes,125,opt,name=money_optimized_projected_month_cost,json=moneyOptimizedProjectedMonthCost,proto3" json:"money_optimized_projected_month_cost,omitempty"` // Right-sized hourly rate over the real length of the month. } func (x *CostInfo) Reset() { @@ -1179,6 +1651,27 @@ func (x *CostInfo) GetMoneyMemoryCostPerGibPerHour() *money.Money { return nil } +func (x *CostInfo) GetMoneyMonthToDateCost() *money.Money { + if x != nil { + return x.MoneyMonthToDateCost + } + return nil +} + +func (x *CostInfo) GetMoneyProjectedMonthCost() *money.Money { + if x != nil { + return x.MoneyProjectedMonthCost + } + return nil +} + +func (x *CostInfo) GetMoneyOptimizedProjectedMonthCost() *money.Money { + if x != nil { + return x.MoneyOptimizedProjectedMonthCost + } + return nil +} + // ResourceMetrics encapsulates CPU and memory capacity, usage, and utilization for nodes and containers. type ResourceMetrics struct { state protoimpl.MessageState @@ -1240,6 +1733,40 @@ type ResourceMetrics struct { NodeFsWriteBytes float64 `protobuf:"fixed64,102,opt,name=node_fs_write_bytes,json=nodeFsWriteBytes,proto3" json:"node_fs_write_bytes,omitempty"` // in bytes NodeFsReads float64 `protobuf:"fixed64,103,opt,name=node_fs_reads,json=nodeFsReads,proto3" json:"node_fs_reads,omitempty"` // number NodeFsWrites float64 `protobuf:"fixed64,104,opt,name=node_fs_writes,json=nodeFsWrites,proto3" json:"node_fs_writes,omitempty"` // number + // Node-level Storage Occupancy Metrics. + // + // Distinct from the Filesystem metrics above, which are I/O rates. These + // answer how full the disk is, not how busy it is. + // + // node_imagefs_shared_device reports that imagefs and nodefs are the same + // filesystem. When it is true, node_imagefs_used_bytes is a SUBSET of + // node_storage_used_bytes and the two must never be added together. + // + // It is a per-node fact, not a quantity, so every query reduces it with + // MAX rather than an average. For a single-node result that MAX degenerates + // to exactly the node's own flag. For a result spanning more than one node + // (for example a node-group bucket summed across every node in the group), + // the MAX is taken across every node in the bucket, so the field then means + // "at least one node in this bucket shares the device", not "every node + // does". Do not read a multi-node result's true value as a fleet-wide + // property: summing nodefs and imagefs across the whole bucket on that + // basis is wrong for any node in the bucket that does not share the + // device. + NodeStorageUsedBytes float64 `protobuf:"fixed64,135,opt,name=node_storage_used_bytes,json=nodeStorageUsedBytes,proto3" json:"node_storage_used_bytes,omitempty"` + NodeStorageCapacityBytes float64 `protobuf:"fixed64,136,opt,name=node_storage_capacity_bytes,json=nodeStorageCapacityBytes,proto3" json:"node_storage_capacity_bytes,omitempty"` + NodeStorageAvailableBytes float64 `protobuf:"fixed64,137,opt,name=node_storage_available_bytes,json=nodeStorageAvailableBytes,proto3" json:"node_storage_available_bytes,omitempty"` + NodeStorageInodesUsed float64 `protobuf:"fixed64,138,opt,name=node_storage_inodes_used,json=nodeStorageInodesUsed,proto3" json:"node_storage_inodes_used,omitempty"` + NodeStorageInodesTotal float64 `protobuf:"fixed64,139,opt,name=node_storage_inodes_total,json=nodeStorageInodesTotal,proto3" json:"node_storage_inodes_total,omitempty"` + NodeImagefsUsedBytes float64 `protobuf:"fixed64,140,opt,name=node_imagefs_used_bytes,json=nodeImagefsUsedBytes,proto3" json:"node_imagefs_used_bytes,omitempty"` + NodeImagefsCapacityBytes float64 `protobuf:"fixed64,141,opt,name=node_imagefs_capacity_bytes,json=nodeImagefsCapacityBytes,proto3" json:"node_imagefs_capacity_bytes,omitempty"` + NodeImagefsAvailableBytes float64 `protobuf:"fixed64,142,opt,name=node_imagefs_available_bytes,json=nodeImagefsAvailableBytes,proto3" json:"node_imagefs_available_bytes,omitempty"` + NodeImagefsSharedDevice bool `protobuf:"varint,143,opt,name=node_imagefs_shared_device,json=nodeImagefsSharedDevice,proto3" json:"node_imagefs_shared_device,omitempty"` + // Ephemeral storage, the scheduler's view: capacity and allocatable come + // from the Node object, requests and limits are summed across its pods. + NodeEphemeralStorageCapacity float64 `protobuf:"fixed64,144,opt,name=node_ephemeral_storage_capacity,json=nodeEphemeralStorageCapacity,proto3" json:"node_ephemeral_storage_capacity,omitempty"` + NodeEphemeralStorageAllocatable float64 `protobuf:"fixed64,145,opt,name=node_ephemeral_storage_allocatable,json=nodeEphemeralStorageAllocatable,proto3" json:"node_ephemeral_storage_allocatable,omitempty"` + NodeEphemeralStorageRequest float64 `protobuf:"fixed64,146,opt,name=node_ephemeral_storage_request,json=nodeEphemeralStorageRequest,proto3" json:"node_ephemeral_storage_request,omitempty"` + NodeEphemeralStorageLimit float64 `protobuf:"fixed64,147,opt,name=node_ephemeral_storage_limit,json=nodeEphemeralStorageLimit,proto3" json:"node_ephemeral_storage_limit,omitempty"` // Pod count PodCount int32 `protobuf:"varint,111,opt,name=pod_count,json=podCount,proto3" json:"pod_count,omitempty"` // Number of distinct pods observed in this time bucket. // JVM heap metrics (0 for non-Java containers) @@ -1665,6 +2192,97 @@ func (x *ResourceMetrics) GetNodeFsWrites() float64 { return 0 } +func (x *ResourceMetrics) GetNodeStorageUsedBytes() float64 { + if x != nil { + return x.NodeStorageUsedBytes + } + return 0 +} + +func (x *ResourceMetrics) GetNodeStorageCapacityBytes() float64 { + if x != nil { + return x.NodeStorageCapacityBytes + } + return 0 +} + +func (x *ResourceMetrics) GetNodeStorageAvailableBytes() float64 { + if x != nil { + return x.NodeStorageAvailableBytes + } + return 0 +} + +func (x *ResourceMetrics) GetNodeStorageInodesUsed() float64 { + if x != nil { + return x.NodeStorageInodesUsed + } + return 0 +} + +func (x *ResourceMetrics) GetNodeStorageInodesTotal() float64 { + if x != nil { + return x.NodeStorageInodesTotal + } + return 0 +} + +func (x *ResourceMetrics) GetNodeImagefsUsedBytes() float64 { + if x != nil { + return x.NodeImagefsUsedBytes + } + return 0 +} + +func (x *ResourceMetrics) GetNodeImagefsCapacityBytes() float64 { + if x != nil { + return x.NodeImagefsCapacityBytes + } + return 0 +} + +func (x *ResourceMetrics) GetNodeImagefsAvailableBytes() float64 { + if x != nil { + return x.NodeImagefsAvailableBytes + } + return 0 +} + +func (x *ResourceMetrics) GetNodeImagefsSharedDevice() bool { + if x != nil { + return x.NodeImagefsSharedDevice + } + return false +} + +func (x *ResourceMetrics) GetNodeEphemeralStorageCapacity() float64 { + if x != nil { + return x.NodeEphemeralStorageCapacity + } + return 0 +} + +func (x *ResourceMetrics) GetNodeEphemeralStorageAllocatable() float64 { + if x != nil { + return x.NodeEphemeralStorageAllocatable + } + return 0 +} + +func (x *ResourceMetrics) GetNodeEphemeralStorageRequest() float64 { + if x != nil { + return x.NodeEphemeralStorageRequest + } + return 0 +} + +func (x *ResourceMetrics) GetNodeEphemeralStorageLimit() float64 { + if x != nil { + return x.NodeEphemeralStorageLimit + } + return 0 +} + func (x *ResourceMetrics) GetPodCount() int32 { if x != nil { return x.PodCount @@ -3323,6 +3941,7 @@ type OptimizationStatus struct { LegacyPolicies []*LegacyPolicyRef `protobuf:"bytes,4,rep,name=legacy_policies,json=legacyPolicies,proto3" json:"legacy_policies,omitempty"` // legacy policies whose targets match this workload WorkloadRuleId string `protobuf:"bytes,5,opt,name=workload_rule_id,json=workloadRuleId,proto3" json:"workload_rule_id,omitempty"` // v2: empty if no active WorkloadRule WorkloadRulePolicyName string `protobuf:"bytes,6,opt,name=workload_rule_policy_name,json=workloadRulePolicyName,proto3" json:"workload_rule_policy_name,omitempty"` // v2: display name of the policy behind the active rule, empty if unresolved + Disabled bool `protobuf:"varint,7,opt,name=disabled,proto3" json:"disabled,omitempty"` // v2: whether the active WorkloadRule (workload_rule_id) is currently disabled. Always false when workload_rule_id is empty. } func (x *OptimizationStatus) Reset() { @@ -3399,6 +4018,13 @@ func (x *OptimizationStatus) GetWorkloadRulePolicyName() string { return "" } +func (x *OptimizationStatus) GetDisabled() bool { + if x != nil { + return x.Disabled + } + return false +} + type WorkloadItem struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -3934,8 +4560,29 @@ type Node struct { // money_on_demand_price_per_hour holds the baseline list price before any discount. // For on-demand and spot nodes both fields are equal. MoneyOnDemandPricePerHour *money.Money `protobuf:"bytes,85,opt,name=money_on_demand_price_per_hour,json=moneyOnDemandPricePerHour,proto3" json:"money_on_demand_price_per_hour,omitempty"` - PriceSource string `protobuf:"bytes,86,opt,name=price_source,json=priceSource,proto3" json:"price_source,omitempty"` // "ri_exact", "ri_flexible", "sp_api_rate", "sp_estimated", "mixed", "spot", "on_demand", "unknown" - PricingAccuracy string `protobuf:"bytes,87,opt,name=pricing_accuracy,json=pricingAccuracy,proto3" json:"pricing_accuracy,omitempty"` // "accurate", "estimated", "fallback", "unknown" + PriceSource string `protobuf:"bytes,86,opt,name=price_source,json=priceSource,proto3" json:"price_source,omitempty"` // "spot", "on_demand", "unknown" + PricingAccuracy string `protobuf:"bytes,87,opt,name=pricing_accuracy,json=pricingAccuracy,proto3" json:"pricing_accuracy,omitempty"` // "accurate", "estimated", "unknown" + // Id of the Reserved Instance or Savings Plan whose discount this node is + // billed under, already normalized (no "SP:" prefix, empty for on-demand). + // Carried on the node so every table that renders one can deep-link to the + // commitment without a second per-node attribution call. + CoveringCommitmentId string `protobuf:"bytes,88,opt,name=covering_commitment_id,json=coveringCommitmentId,proto3" json:"covering_commitment_id,omitempty"` + // Per-node request-versus-capacity trend over the requested window, in + // bucket order. Populated by GetNodeGroup for the group drawer's Nodes tab; + // each point carries only the container_*_requested and node_*_capacity + // fields of ResourceMetrics. Empty when the node reported no samples in the + // window -- absent means unknown, never zero. + ResourceDataPoints []*ResourceDataPoint `protobuf:"bytes,89,rep,name=resource_data_points,json=resourceDataPoints,proto3" json:"resource_data_points,omitempty"` + // Pods on the node right now, from the live pod inventory rather than the + // metrics window: every pod scheduled to the node that has not reached + // Succeeded or Failed, which is what the kubelet counts against max pods. + // Populated by GetNodeGroup for the group drawer's Nodes tab. Unset for a + // node that has been terminated, whose pods are gone with it -- absent + // means unknown, and zero means a live node running nothing. + PodCount *int32 `protobuf:"varint,90,opt,name=pod_count,json=podCount,proto3,oneof" json:"pod_count,omitempty"` + // How many of pod_count are DaemonSet pods: the per-node overhead every + // node in the group carries regardless of what is scheduled onto it. + DaemonsetPodCount int32 `protobuf:"varint,91,opt,name=daemonset_pod_count,json=daemonsetPodCount,proto3" json:"daemonset_pod_count,omitempty"` // Enhanced node classification ExtractedNodeType string `protobuf:"bytes,60,opt,name=extracted_node_type,json=extractedNodeType,proto3" json:"extracted_node_type,omitempty"` // "spot", "on-demand", "reserved" AvailabilityZone string `protobuf:"bytes,61,opt,name=availability_zone,json=availabilityZone,proto3" json:"availability_zone,omitempty"` // Multi-cloud AZ extraction @@ -3949,6 +4596,13 @@ type Node struct { LastSeen *timestamppb.Timestamp `protobuf:"bytes,70,opt,name=last_seen,json=lastSeen,proto3" json:"last_seen,omitempty"` CollectedAt *timestamppb.Timestamp `protobuf:"bytes,71,opt,name=collected_at,json=collectedAt,proto3" json:"collected_at,omitempty"` DeletionTimestamp *timestamppb.Timestamp `protobuf:"bytes,72,opt,name=deletion_timestamp,json=deletionTimestamp,proto3" json:"deletion_timestamp,omitempty"` + // What created this node, and the pool or group that manager files it under. + // Read from THIS node's ownerReferences and labels, never from its node + // group: node_group_name is a label value, and nodes from two different + // managers can share one. UNSPECIFIED means no recognized signal, which is a + // static or hand-built node -- not "unknown". + ManagedBy NodeManager `protobuf:"varint,92,opt,name=managed_by,json=managedBy,proto3,enum=api.v1.NodeManager" json:"managed_by,omitempty"` + ManagedByName string `protobuf:"bytes,93,opt,name=managed_by_name,json=managedByName,proto3" json:"managed_by_name,omitempty"` } func (x *Node) Reset() { @@ -4242,6 +4896,34 @@ func (x *Node) GetPricingAccuracy() string { return "" } +func (x *Node) GetCoveringCommitmentId() string { + if x != nil { + return x.CoveringCommitmentId + } + return "" +} + +func (x *Node) GetResourceDataPoints() []*ResourceDataPoint { + if x != nil { + return x.ResourceDataPoints + } + return nil +} + +func (x *Node) GetPodCount() int32 { + if x != nil && x.PodCount != nil { + return *x.PodCount + } + return 0 +} + +func (x *Node) GetDaemonsetPodCount() int32 { + if x != nil { + return x.DaemonsetPodCount + } + return 0 +} + func (x *Node) GetExtractedNodeType() string { if x != nil { return x.ExtractedNodeType @@ -4305,6 +4987,20 @@ func (x *Node) GetDeletionTimestamp() *timestamppb.Timestamp { return nil } +func (x *Node) GetManagedBy() NodeManager { + if x != nil { + return x.ManagedBy + } + return NodeManager_NODE_MANAGER_UNSPECIFIED +} + +func (x *Node) GetManagedByName() string { + if x != nil { + return x.ManagedByName + } + return "" +} + type AttachedVolume struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -4374,6 +5070,9 @@ type NodeGroup struct { ResourceInfo *ResourceInfo `protobuf:"bytes,6,opt,name=resource_info,json=resourceInfo,proto3" json:"resource_info,omitempty"` // Resource scheduling information for the node group. CostDataPoints []*CostDataPoint `protobuf:"bytes,11,rep,name=cost_data_points,json=costDataPoints,proto3" json:"cost_data_points,omitempty"` // Time-series cost data points for the node group. ResourceDataPoints []*ResourceDataPoint `protobuf:"bytes,12,rep,name=resource_data_points,json=resourceDataPoints,proto3" json:"resource_data_points,omitempty"` // Time-series resource metrics for the node group. + // Who provisions this group. UNSPECIFIED for GKE/AKS/Cast pools and the + // literal "default" fallback group. + Provisioner NodeGroupProvisioner `protobuf:"varint,13,opt,name=provisioner,proto3,enum=api.v1.NodeGroupProvisioner" json:"provisioner,omitempty"` } func (x *NodeGroup) Reset() { @@ -4464,6 +5163,13 @@ func (x *NodeGroup) GetResourceDataPoints() []*ResourceDataPoint { return nil } +func (x *NodeGroup) GetProvisioner() NodeGroupProvisioner { + if x != nil { + return x.Provisioner + } + return NodeGroupProvisioner_NODE_GROUP_PROVISIONER_UNSPECIFIED +} + // CostDataPoint represents cost information at a specific time. type CostDataPoint struct { state protoimpl.MessageState @@ -4472,6 +5178,21 @@ type CostDataPoint struct { Timestamp int64 `protobuf:"varint,1,opt,name=timestamp,proto3" json:"timestamp,omitempty"` // Unix timestamp (seconds since epoch). CostInfo *CostInfo `protobuf:"bytes,2,opt,name=cost_info,json=costInfo,proto3" json:"cost_info,omitempty"` // Cost information for the data point. + // What the bucket actually cost, split by how its nodes were purchased. + // ABSOLUTE dollars for the bucket, not a per-hour rate like the CostInfo + // fields beside them: buckets are a mix of 60s and 3600s widths, so a rate + // cannot be folded into a day without also carrying the width. + // + // spot is the spot nodes; on_demand is everything else, at whatever the node + // was billed -- the Reserved Instance or Savings Plan rate where a commitment + // covered the hour and the CUR has reported it, the list-price estimate until + // then. The two are a partition of the bucket, so they sum to the bucket's + // total cost. + // + // Only the cluster-level timeseries fills these; every other producer of a + // CostDataPoint leaves them unset, and unset means unknown, not zero. + MoneyOnDemandCost *money.Money `protobuf:"bytes,3,opt,name=money_on_demand_cost,json=moneyOnDemandCost,proto3" json:"money_on_demand_cost,omitempty"` + MoneySpotCost *money.Money `protobuf:"bytes,4,opt,name=money_spot_cost,json=moneySpotCost,proto3" json:"money_spot_cost,omitempty"` } func (x *CostDataPoint) Reset() { @@ -4520,6 +5241,20 @@ func (x *CostDataPoint) GetCostInfo() *CostInfo { return nil } +func (x *CostDataPoint) GetMoneyOnDemandCost() *money.Money { + if x != nil { + return x.MoneyOnDemandCost + } + return nil +} + +func (x *CostDataPoint) GetMoneySpotCost() *money.Money { + if x != nil { + return x.MoneySpotCost + } + return nil +} + // ResourceDataPoint represents resource utilization at a specific time. type ResourceDataPoint struct { state protoimpl.MessageState @@ -4879,7 +5614,9 @@ type LabelSelectorRequirement struct { // key is the label key that the selector applies to. Key string `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"` // operator represents a key's relationship to a set of values. - // Valid operators are In, NotIn, Exists and DoesNotExist. + // Valid operators are In, NotIn, Exists and DoesNotExist, but the DevZero + // department and workload filter paths honor only In and NotIn (see the + // LabelSelectorOperator caveats above, PC-1452). Operator LabelSelectorOperator `protobuf:"varint,2,opt,name=operator,proto3,enum=api.v1.LabelSelectorOperator" json:"operator,omitempty"` // values is an array of string values. If the operator is In or NotIn, // the values array must be non-empty. If the operator is Exists or DoesNotExist, @@ -5165,6 +5902,30 @@ type ResourceDetails struct { // *ResourceDetails_KyvernoPolicyReportDetails // *ResourceDetails_GatekeeperConstraintTemplateDetails // *ResourceDetails_GatekeeperConstraintDetails + // *ResourceDetails_ArgoApplicationDetails + // *ResourceDetails_ArgoApplicationSetDetails + // *ResourceDetails_CustomResourceDefinitionDetails + // *ResourceDetails_FluxGitRepositoryDetails + // *ResourceDetails_FluxOciRepositoryDetails + // *ResourceDetails_FluxHelmRepositoryDetails + // *ResourceDetails_FluxHelmChartDetails + // *ResourceDetails_FluxBucketDetails + // *ResourceDetails_FluxKustomizationDetails + // *ResourceDetails_FluxHelmReleaseDetails + // *ResourceDetails_FluxExternalArtifactDetails + // *ResourceDetails_FluxAlertDetails + // *ResourceDetails_FluxProviderDetails + // *ResourceDetails_FluxReceiverDetails + // *ResourceDetails_FluxInstanceDetails + // *ResourceDetails_FluxReportDetails + // *ResourceDetails_FluxResourceSetInputProviderDetails + // *ResourceDetails_FluxResourceSetDetails + // *ResourceDetails_FluxImageRepositoryDetails + // *ResourceDetails_FluxImagePolicyDetails + // *ResourceDetails_FluxImageUpdateAutomationDetails + // *ResourceDetails_FlaggerCanaryDetails + // *ResourceDetails_FlaggerMetricTemplateDetails + // *ResourceDetails_FlaggerAlertProviderDetails Details isResourceDetails_Details `protobuf_oneof:"details"` // Common fields that can be flattened at this level for easier access // These are populated based on the resource type @@ -5593,6 +6354,174 @@ func (x *ResourceDetails) GetGatekeeperConstraintDetails() *GatekeeperConstraint return nil } +func (x *ResourceDetails) GetArgoApplicationDetails() *ArgoApplicationDetails { + if x, ok := x.GetDetails().(*ResourceDetails_ArgoApplicationDetails); ok { + return x.ArgoApplicationDetails + } + return nil +} + +func (x *ResourceDetails) GetArgoApplicationSetDetails() *ArgoApplicationSetDetails { + if x, ok := x.GetDetails().(*ResourceDetails_ArgoApplicationSetDetails); ok { + return x.ArgoApplicationSetDetails + } + return nil +} + +func (x *ResourceDetails) GetCustomResourceDefinitionDetails() *CustomResourceDefinitionDetails { + if x, ok := x.GetDetails().(*ResourceDetails_CustomResourceDefinitionDetails); ok { + return x.CustomResourceDefinitionDetails + } + return nil +} + +func (x *ResourceDetails) GetFluxGitRepositoryDetails() *FluxGitRepositoryDetails { + if x, ok := x.GetDetails().(*ResourceDetails_FluxGitRepositoryDetails); ok { + return x.FluxGitRepositoryDetails + } + return nil +} + +func (x *ResourceDetails) GetFluxOciRepositoryDetails() *FluxOCIRepositoryDetails { + if x, ok := x.GetDetails().(*ResourceDetails_FluxOciRepositoryDetails); ok { + return x.FluxOciRepositoryDetails + } + return nil +} + +func (x *ResourceDetails) GetFluxHelmRepositoryDetails() *FluxHelmRepositoryDetails { + if x, ok := x.GetDetails().(*ResourceDetails_FluxHelmRepositoryDetails); ok { + return x.FluxHelmRepositoryDetails + } + return nil +} + +func (x *ResourceDetails) GetFluxHelmChartDetails() *FluxHelmChartDetails { + if x, ok := x.GetDetails().(*ResourceDetails_FluxHelmChartDetails); ok { + return x.FluxHelmChartDetails + } + return nil +} + +func (x *ResourceDetails) GetFluxBucketDetails() *FluxBucketDetails { + if x, ok := x.GetDetails().(*ResourceDetails_FluxBucketDetails); ok { + return x.FluxBucketDetails + } + return nil +} + +func (x *ResourceDetails) GetFluxKustomizationDetails() *FluxKustomizationDetails { + if x, ok := x.GetDetails().(*ResourceDetails_FluxKustomizationDetails); ok { + return x.FluxKustomizationDetails + } + return nil +} + +func (x *ResourceDetails) GetFluxHelmReleaseDetails() *FluxHelmReleaseDetails { + if x, ok := x.GetDetails().(*ResourceDetails_FluxHelmReleaseDetails); ok { + return x.FluxHelmReleaseDetails + } + return nil +} + +func (x *ResourceDetails) GetFluxExternalArtifactDetails() *FluxGenericDetails { + if x, ok := x.GetDetails().(*ResourceDetails_FluxExternalArtifactDetails); ok { + return x.FluxExternalArtifactDetails + } + return nil +} + +func (x *ResourceDetails) GetFluxAlertDetails() *FluxGenericDetails { + if x, ok := x.GetDetails().(*ResourceDetails_FluxAlertDetails); ok { + return x.FluxAlertDetails + } + return nil +} + +func (x *ResourceDetails) GetFluxProviderDetails() *FluxGenericDetails { + if x, ok := x.GetDetails().(*ResourceDetails_FluxProviderDetails); ok { + return x.FluxProviderDetails + } + return nil +} + +func (x *ResourceDetails) GetFluxReceiverDetails() *FluxGenericDetails { + if x, ok := x.GetDetails().(*ResourceDetails_FluxReceiverDetails); ok { + return x.FluxReceiverDetails + } + return nil +} + +func (x *ResourceDetails) GetFluxInstanceDetails() *FluxGenericDetails { + if x, ok := x.GetDetails().(*ResourceDetails_FluxInstanceDetails); ok { + return x.FluxInstanceDetails + } + return nil +} + +func (x *ResourceDetails) GetFluxReportDetails() *FluxGenericDetails { + if x, ok := x.GetDetails().(*ResourceDetails_FluxReportDetails); ok { + return x.FluxReportDetails + } + return nil +} + +func (x *ResourceDetails) GetFluxResourceSetInputProviderDetails() *FluxGenericDetails { + if x, ok := x.GetDetails().(*ResourceDetails_FluxResourceSetInputProviderDetails); ok { + return x.FluxResourceSetInputProviderDetails + } + return nil +} + +func (x *ResourceDetails) GetFluxResourceSetDetails() *FluxGenericDetails { + if x, ok := x.GetDetails().(*ResourceDetails_FluxResourceSetDetails); ok { + return x.FluxResourceSetDetails + } + return nil +} + +func (x *ResourceDetails) GetFluxImageRepositoryDetails() *FluxGenericDetails { + if x, ok := x.GetDetails().(*ResourceDetails_FluxImageRepositoryDetails); ok { + return x.FluxImageRepositoryDetails + } + return nil +} + +func (x *ResourceDetails) GetFluxImagePolicyDetails() *FluxGenericDetails { + if x, ok := x.GetDetails().(*ResourceDetails_FluxImagePolicyDetails); ok { + return x.FluxImagePolicyDetails + } + return nil +} + +func (x *ResourceDetails) GetFluxImageUpdateAutomationDetails() *FluxGenericDetails { + if x, ok := x.GetDetails().(*ResourceDetails_FluxImageUpdateAutomationDetails); ok { + return x.FluxImageUpdateAutomationDetails + } + return nil +} + +func (x *ResourceDetails) GetFlaggerCanaryDetails() *FlaggerCanaryDetails { + if x, ok := x.GetDetails().(*ResourceDetails_FlaggerCanaryDetails); ok { + return x.FlaggerCanaryDetails + } + return nil +} + +func (x *ResourceDetails) GetFlaggerMetricTemplateDetails() *FlaggerMetricTemplateDetails { + if x, ok := x.GetDetails().(*ResourceDetails_FlaggerMetricTemplateDetails); ok { + return x.FlaggerMetricTemplateDetails + } + return nil +} + +func (x *ResourceDetails) GetFlaggerAlertProviderDetails() *FlaggerAlertProviderDetails { + if x, ok := x.GetDetails().(*ResourceDetails_FlaggerAlertProviderDetails); ok { + return x.FlaggerAlertProviderDetails + } + return nil +} + func (x *ResourceDetails) GetPhase() string { if x != nil { return x.Phase @@ -6809,6 +7738,128 @@ type ResourceDetails_GatekeeperConstraintDetails struct { GatekeeperConstraintDetails *GatekeeperConstraintDetails `protobuf:"bytes,247,opt,name=gatekeeper_constraint_details,json=gatekeeperConstraintDetails,proto3,oneof"` } +type ResourceDetails_ArgoApplicationDetails struct { + // Argo CD Application-specific fields + ArgoApplicationDetails *ArgoApplicationDetails `protobuf:"bytes,248,opt,name=argo_application_details,json=argoApplicationDetails,proto3,oneof"` +} + +type ResourceDetails_ArgoApplicationSetDetails struct { + // Argo CD ApplicationSet-specific fields + ArgoApplicationSetDetails *ArgoApplicationSetDetails `protobuf:"bytes,249,opt,name=argo_application_set_details,json=argoApplicationSetDetails,proto3,oneof"` +} + +type ResourceDetails_CustomResourceDefinitionDetails struct { + // CustomResourceDefinition-specific fields + CustomResourceDefinitionDetails *CustomResourceDefinitionDetails `protobuf:"bytes,250,opt,name=custom_resource_definition_details,json=customResourceDefinitionDetails,proto3,oneof"` +} + +type ResourceDetails_FluxGitRepositoryDetails struct { + // FluxCD GitRepository-specific fields + FluxGitRepositoryDetails *FluxGitRepositoryDetails `protobuf:"bytes,251,opt,name=flux_git_repository_details,json=fluxGitRepositoryDetails,proto3,oneof"` +} + +type ResourceDetails_FluxOciRepositoryDetails struct { + // FluxCD OCIRepository-specific fields + FluxOciRepositoryDetails *FluxOCIRepositoryDetails `protobuf:"bytes,252,opt,name=flux_oci_repository_details,json=fluxOciRepositoryDetails,proto3,oneof"` +} + +type ResourceDetails_FluxHelmRepositoryDetails struct { + // FluxCD HelmRepository-specific fields + FluxHelmRepositoryDetails *FluxHelmRepositoryDetails `protobuf:"bytes,253,opt,name=flux_helm_repository_details,json=fluxHelmRepositoryDetails,proto3,oneof"` +} + +type ResourceDetails_FluxHelmChartDetails struct { + // FluxCD HelmChart-specific fields + FluxHelmChartDetails *FluxHelmChartDetails `protobuf:"bytes,254,opt,name=flux_helm_chart_details,json=fluxHelmChartDetails,proto3,oneof"` +} + +type ResourceDetails_FluxBucketDetails struct { + // FluxCD Bucket-specific fields + FluxBucketDetails *FluxBucketDetails `protobuf:"bytes,255,opt,name=flux_bucket_details,json=fluxBucketDetails,proto3,oneof"` +} + +type ResourceDetails_FluxKustomizationDetails struct { + // FluxCD Kustomization-specific fields + FluxKustomizationDetails *FluxKustomizationDetails `protobuf:"bytes,256,opt,name=flux_kustomization_details,json=fluxKustomizationDetails,proto3,oneof"` +} + +type ResourceDetails_FluxHelmReleaseDetails struct { + // FluxCD HelmRelease-specific fields + FluxHelmReleaseDetails *FluxHelmReleaseDetails `protobuf:"bytes,257,opt,name=flux_helm_release_details,json=fluxHelmReleaseDetails,proto3,oneof"` +} + +type ResourceDetails_FluxExternalArtifactDetails struct { + // FluxCD ExternalArtifact-specific fields (collector-only tier, generic shape) + FluxExternalArtifactDetails *FluxGenericDetails `protobuf:"bytes,258,opt,name=flux_external_artifact_details,json=fluxExternalArtifactDetails,proto3,oneof"` +} + +type ResourceDetails_FluxAlertDetails struct { + // FluxCD Alert-specific fields (collector-only tier, generic shape) + FluxAlertDetails *FluxGenericDetails `protobuf:"bytes,259,opt,name=flux_alert_details,json=fluxAlertDetails,proto3,oneof"` +} + +type ResourceDetails_FluxProviderDetails struct { + // FluxCD Provider-specific fields (collector-only tier, generic shape) + FluxProviderDetails *FluxGenericDetails `protobuf:"bytes,260,opt,name=flux_provider_details,json=fluxProviderDetails,proto3,oneof"` +} + +type ResourceDetails_FluxReceiverDetails struct { + // FluxCD Receiver-specific fields (collector-only tier, generic shape) + FluxReceiverDetails *FluxGenericDetails `protobuf:"bytes,261,opt,name=flux_receiver_details,json=fluxReceiverDetails,proto3,oneof"` +} + +type ResourceDetails_FluxInstanceDetails struct { + // Flux Operator FluxInstance-specific fields (collector-only tier, generic shape) + FluxInstanceDetails *FluxGenericDetails `protobuf:"bytes,262,opt,name=flux_instance_details,json=fluxInstanceDetails,proto3,oneof"` +} + +type ResourceDetails_FluxReportDetails struct { + // Flux Operator FluxReport-specific fields (collector-only tier, generic shape) + FluxReportDetails *FluxGenericDetails `protobuf:"bytes,263,opt,name=flux_report_details,json=fluxReportDetails,proto3,oneof"` +} + +type ResourceDetails_FluxResourceSetInputProviderDetails struct { + // Flux Operator ResourceSetInputProvider-specific fields (collector-only tier, generic shape) + FluxResourceSetInputProviderDetails *FluxGenericDetails `protobuf:"bytes,264,opt,name=flux_resource_set_input_provider_details,json=fluxResourceSetInputProviderDetails,proto3,oneof"` +} + +type ResourceDetails_FluxResourceSetDetails struct { + // Flux Operator ResourceSet-specific fields (collector-only tier, generic shape) + FluxResourceSetDetails *FluxGenericDetails `protobuf:"bytes,265,opt,name=flux_resource_set_details,json=fluxResourceSetDetails,proto3,oneof"` +} + +type ResourceDetails_FluxImageRepositoryDetails struct { + // FluxCD ImageRepository-specific fields (collector-only tier, generic shape) + FluxImageRepositoryDetails *FluxGenericDetails `protobuf:"bytes,266,opt,name=flux_image_repository_details,json=fluxImageRepositoryDetails,proto3,oneof"` +} + +type ResourceDetails_FluxImagePolicyDetails struct { + // FluxCD ImagePolicy-specific fields (collector-only tier, generic shape) + FluxImagePolicyDetails *FluxGenericDetails `protobuf:"bytes,267,opt,name=flux_image_policy_details,json=fluxImagePolicyDetails,proto3,oneof"` +} + +type ResourceDetails_FluxImageUpdateAutomationDetails struct { + // FluxCD ImageUpdateAutomation-specific fields (collector-only tier, generic shape) + FluxImageUpdateAutomationDetails *FluxGenericDetails `protobuf:"bytes,268,opt,name=flux_image_update_automation_details,json=fluxImageUpdateAutomationDetails,proto3,oneof"` +} + +type ResourceDetails_FlaggerCanaryDetails struct { + // Flagger Canary-specific fields + FlaggerCanaryDetails *FlaggerCanaryDetails `protobuf:"bytes,269,opt,name=flagger_canary_details,json=flaggerCanaryDetails,proto3,oneof"` +} + +type ResourceDetails_FlaggerMetricTemplateDetails struct { + // Flagger MetricTemplate-specific fields + FlaggerMetricTemplateDetails *FlaggerMetricTemplateDetails `protobuf:"bytes,270,opt,name=flagger_metric_template_details,json=flaggerMetricTemplateDetails,proto3,oneof"` +} + +type ResourceDetails_FlaggerAlertProviderDetails struct { + // Flagger AlertProvider-specific fields (listed tier: columns and search, no + // bespoke detail panel yet, but it carries its own message so every Flagger + // kind resolves through the same oneof and the same per-field authz path) + FlaggerAlertProviderDetails *FlaggerAlertProviderDetails `protobuf:"bytes,271,opt,name=flagger_alert_provider_details,json=flaggerAlertProviderDetails,proto3,oneof"` +} + func (*ResourceDetails_PodDetails) isResourceDetails_Details() {} func (*ResourceDetails_DeploymentDetails) isResourceDetails_Details() {} @@ -6871,6 +7922,54 @@ func (*ResourceDetails_GatekeeperConstraintTemplateDetails) isResourceDetails_De func (*ResourceDetails_GatekeeperConstraintDetails) isResourceDetails_Details() {} +func (*ResourceDetails_ArgoApplicationDetails) isResourceDetails_Details() {} + +func (*ResourceDetails_ArgoApplicationSetDetails) isResourceDetails_Details() {} + +func (*ResourceDetails_CustomResourceDefinitionDetails) isResourceDetails_Details() {} + +func (*ResourceDetails_FluxGitRepositoryDetails) isResourceDetails_Details() {} + +func (*ResourceDetails_FluxOciRepositoryDetails) isResourceDetails_Details() {} + +func (*ResourceDetails_FluxHelmRepositoryDetails) isResourceDetails_Details() {} + +func (*ResourceDetails_FluxHelmChartDetails) isResourceDetails_Details() {} + +func (*ResourceDetails_FluxBucketDetails) isResourceDetails_Details() {} + +func (*ResourceDetails_FluxKustomizationDetails) isResourceDetails_Details() {} + +func (*ResourceDetails_FluxHelmReleaseDetails) isResourceDetails_Details() {} + +func (*ResourceDetails_FluxExternalArtifactDetails) isResourceDetails_Details() {} + +func (*ResourceDetails_FluxAlertDetails) isResourceDetails_Details() {} + +func (*ResourceDetails_FluxProviderDetails) isResourceDetails_Details() {} + +func (*ResourceDetails_FluxReceiverDetails) isResourceDetails_Details() {} + +func (*ResourceDetails_FluxInstanceDetails) isResourceDetails_Details() {} + +func (*ResourceDetails_FluxReportDetails) isResourceDetails_Details() {} + +func (*ResourceDetails_FluxResourceSetInputProviderDetails) isResourceDetails_Details() {} + +func (*ResourceDetails_FluxResourceSetDetails) isResourceDetails_Details() {} + +func (*ResourceDetails_FluxImageRepositoryDetails) isResourceDetails_Details() {} + +func (*ResourceDetails_FluxImagePolicyDetails) isResourceDetails_Details() {} + +func (*ResourceDetails_FluxImageUpdateAutomationDetails) isResourceDetails_Details() {} + +func (*ResourceDetails_FlaggerCanaryDetails) isResourceDetails_Details() {} + +func (*ResourceDetails_FlaggerMetricTemplateDetails) isResourceDetails_Details() {} + +func (*ResourceDetails_FlaggerAlertProviderDetails) isResourceDetails_Details() {} + // PodDetails contains detailed Pod information (for the oneof, if we need nested data later) type PodDetails struct { state protoimpl.MessageState @@ -13143,10 +14242,15 @@ type WorkloadFilters struct { LanguageFilter []string `protobuf:"bytes,17,rep,name=language_filter,json=languageFilter,proto3" json:"language_filter,omitempty"` // Exact detected version strings (e.g. "3.11.4"). Only meaningful when // language_filter is also set; ignored otherwise. - LanguageVersionFilter []string `protobuf:"bytes,18,rep,name=language_version_filter,json=languageVersionFilter,proto3" json:"language_version_filter,omitempty"` - NodeGroupNames []string `protobuf:"bytes,21,rep,name=node_group_names,json=nodeGroupNames,proto3" json:"node_group_names,omitempty"` // target workloads running on specific nodes - NamespaceNames []string `protobuf:"bytes,22,rep,name=namespace_names,json=namespaceNames,proto3" json:"namespace_names,omitempty"` // Target workloads in any of these namespaces by exact name (OR'd together; ANDed with other filter fields; no-op when empty) - NamespacePattern *RegexPattern `protobuf:"bytes,23,opt,name=namespace_pattern,json=namespacePattern,proto3,oneof" json:"namespace_pattern,omitempty"` // Target namespaces by name pattern + LanguageVersionFilter []string `protobuf:"bytes,18,rep,name=language_version_filter,json=languageVersionFilter,proto3" json:"language_version_filter,omitempty"` + NodeGroupNames []string `protobuf:"bytes,21,rep,name=node_group_names,json=nodeGroupNames,proto3" json:"node_group_names,omitempty"` // target workloads running on specific nodes + NamespaceNames []string `protobuf:"bytes,22,rep,name=namespace_names,json=namespaceNames,proto3" json:"namespace_names,omitempty"` // Target workloads in any of these namespaces by exact name (OR'd together; ANDed with other filter fields; no-op when empty) + NamespacePattern *RegexPattern `protobuf:"bytes,23,opt,name=namespace_pattern,json=namespacePattern,proto3,oneof" json:"namespace_pattern,omitempty"` // Target namespaces by name pattern + Health *WorkloadHealthFilter `protobuf:"bytes,24,opt,name=health,proto3,oneof" json:"health,omitempty"` // Narrow by health status / health issues (see WorkloadHealthFilter) + // Match workloads whose owner (WorkloadItem.owner_kind) has one of these + // kinds, e.g. OpenTelemetryCollector. ORed with kind_filter, not ANDed: a + // workload matches on its own kind or its owner's kind. + OwnerKinds []string `protobuf:"bytes,25,rep,name=owner_kinds,json=ownerKinds,proto3" json:"owner_kinds,omitempty"` // Lifecycle status filter (overrides legacy show_deleted when set) Status WorkloadStatusFilter `protobuf:"varint,30,opt,name=status,proto3,enum=api.v1.WorkloadStatusFilter" json:"status,omitempty"` // ACTIVE, DELETED, or BOTH } @@ -13260,6 +14364,20 @@ func (x *WorkloadFilters) GetNamespacePattern() *RegexPattern { return nil } +func (x *WorkloadFilters) GetHealth() *WorkloadHealthFilter { + if x != nil { + return x.Health + } + return nil +} + +func (x *WorkloadFilters) GetOwnerKinds() []string { + if x != nil { + return x.OwnerKinds + } + return nil +} + func (x *WorkloadFilters) GetStatus() WorkloadStatusFilter { if x != nil { return x.Status @@ -13267,6 +14385,73 @@ func (x *WorkloadFilters) GetStatus() WorkloadStatusFilter { return WorkloadStatusFilter_WORKLOAD_STATUS_FILTER_UNSPECIFIED } +// WorkloadHealthFilter narrows a workload set by the same health facts the +// Health badge renders. status values are OR'd together; issues combine per +// issue_match; status and issues are AND'd when both are set. Empty means no +// health filtering. +type WorkloadHealthFilter struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Status []WorkloadHealthStatusFilter `protobuf:"varint,1,rep,packed,name=status,proto3,enum=api.v1.WorkloadHealthStatusFilter" json:"status,omitempty"` + Issues []WorkloadHealthIssue `protobuf:"varint,2,rep,packed,name=issues,proto3,enum=api.v1.WorkloadHealthIssue" json:"issues,omitempty"` + IssueMatch HealthIssueMatchMode `protobuf:"varint,3,opt,name=issue_match,json=issueMatch,proto3,enum=api.v1.HealthIssueMatchMode" json:"issue_match,omitempty"` +} + +func (x *WorkloadHealthFilter) Reset() { + *x = WorkloadHealthFilter{} + if protoimpl.UnsafeEnabled { + mi := &file_api_v1_common_proto_msgTypes[123] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *WorkloadHealthFilter) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*WorkloadHealthFilter) ProtoMessage() {} + +func (x *WorkloadHealthFilter) ProtoReflect() protoreflect.Message { + mi := &file_api_v1_common_proto_msgTypes[123] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use WorkloadHealthFilter.ProtoReflect.Descriptor instead. +func (*WorkloadHealthFilter) Descriptor() ([]byte, []int) { + return file_api_v1_common_proto_rawDescGZIP(), []int{123} +} + +func (x *WorkloadHealthFilter) GetStatus() []WorkloadHealthStatusFilter { + if x != nil { + return x.Status + } + return nil +} + +func (x *WorkloadHealthFilter) GetIssues() []WorkloadHealthIssue { + if x != nil { + return x.Issues + } + return nil +} + +func (x *WorkloadHealthFilter) GetIssueMatch() HealthIssueMatchMode { + if x != nil { + return x.IssueMatch + } + return HealthIssueMatchMode_HEALTH_ISSUE_MATCH_MODE_UNSPECIFIED +} + // PodDisruptionBudgetDetails contains detailed PodDisruptionBudget information type PodDisruptionBudgetDetails struct { state protoimpl.MessageState @@ -13287,7 +14472,7 @@ type PodDisruptionBudgetDetails struct { func (x *PodDisruptionBudgetDetails) Reset() { *x = PodDisruptionBudgetDetails{} if protoimpl.UnsafeEnabled { - mi := &file_api_v1_common_proto_msgTypes[123] + mi := &file_api_v1_common_proto_msgTypes[124] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -13300,7 +14485,7 @@ func (x *PodDisruptionBudgetDetails) String() string { func (*PodDisruptionBudgetDetails) ProtoMessage() {} func (x *PodDisruptionBudgetDetails) ProtoReflect() protoreflect.Message { - mi := &file_api_v1_common_proto_msgTypes[123] + mi := &file_api_v1_common_proto_msgTypes[124] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -13313,7 +14498,7 @@ func (x *PodDisruptionBudgetDetails) ProtoReflect() protoreflect.Message { // Deprecated: Use PodDisruptionBudgetDetails.ProtoReflect.Descriptor instead. func (*PodDisruptionBudgetDetails) Descriptor() ([]byte, []int) { - return file_api_v1_common_proto_rawDescGZIP(), []int{123} + return file_api_v1_common_proto_rawDescGZIP(), []int{124} } func (x *PodDisruptionBudgetDetails) GetMinAvailable() int32 { @@ -13395,7 +14580,7 @@ type PodDisruptionBudgetCondition struct { func (x *PodDisruptionBudgetCondition) Reset() { *x = PodDisruptionBudgetCondition{} if protoimpl.UnsafeEnabled { - mi := &file_api_v1_common_proto_msgTypes[124] + mi := &file_api_v1_common_proto_msgTypes[125] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -13408,7 +14593,7 @@ func (x *PodDisruptionBudgetCondition) String() string { func (*PodDisruptionBudgetCondition) ProtoMessage() {} func (x *PodDisruptionBudgetCondition) ProtoReflect() protoreflect.Message { - mi := &file_api_v1_common_proto_msgTypes[124] + mi := &file_api_v1_common_proto_msgTypes[125] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -13421,7 +14606,7 @@ func (x *PodDisruptionBudgetCondition) ProtoReflect() protoreflect.Message { // Deprecated: Use PodDisruptionBudgetCondition.ProtoReflect.Descriptor instead. func (*PodDisruptionBudgetCondition) Descriptor() ([]byte, []int) { - return file_api_v1_common_proto_rawDescGZIP(), []int{124} + return file_api_v1_common_proto_rawDescGZIP(), []int{125} } func (x *PodDisruptionBudgetCondition) GetType() string { @@ -13476,7 +14661,7 @@ type ResourceQuotaDetails struct { func (x *ResourceQuotaDetails) Reset() { *x = ResourceQuotaDetails{} if protoimpl.UnsafeEnabled { - mi := &file_api_v1_common_proto_msgTypes[125] + mi := &file_api_v1_common_proto_msgTypes[126] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -13489,7 +14674,7 @@ func (x *ResourceQuotaDetails) String() string { func (*ResourceQuotaDetails) ProtoMessage() {} func (x *ResourceQuotaDetails) ProtoReflect() protoreflect.Message { - mi := &file_api_v1_common_proto_msgTypes[125] + mi := &file_api_v1_common_proto_msgTypes[126] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -13502,7 +14687,7 @@ func (x *ResourceQuotaDetails) ProtoReflect() protoreflect.Message { // Deprecated: Use ResourceQuotaDetails.ProtoReflect.Descriptor instead. func (*ResourceQuotaDetails) Descriptor() ([]byte, []int) { - return file_api_v1_common_proto_rawDescGZIP(), []int{125} + return file_api_v1_common_proto_rawDescGZIP(), []int{126} } func (x *ResourceQuotaDetails) GetHardLimits() map[string]string { @@ -13563,7 +14748,7 @@ type ResourceQuotaCondition struct { func (x *ResourceQuotaCondition) Reset() { *x = ResourceQuotaCondition{} if protoimpl.UnsafeEnabled { - mi := &file_api_v1_common_proto_msgTypes[126] + mi := &file_api_v1_common_proto_msgTypes[127] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -13576,7 +14761,7 @@ func (x *ResourceQuotaCondition) String() string { func (*ResourceQuotaCondition) ProtoMessage() {} func (x *ResourceQuotaCondition) ProtoReflect() protoreflect.Message { - mi := &file_api_v1_common_proto_msgTypes[126] + mi := &file_api_v1_common_proto_msgTypes[127] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -13589,7 +14774,7 @@ func (x *ResourceQuotaCondition) ProtoReflect() protoreflect.Message { // Deprecated: Use ResourceQuotaCondition.ProtoReflect.Descriptor instead. func (*ResourceQuotaCondition) Descriptor() ([]byte, []int) { - return file_api_v1_common_proto_rawDescGZIP(), []int{126} + return file_api_v1_common_proto_rawDescGZIP(), []int{127} } func (x *ResourceQuotaCondition) GetType() string { @@ -13645,7 +14830,7 @@ type VolcanoJobDetails struct { func (x *VolcanoJobDetails) Reset() { *x = VolcanoJobDetails{} if protoimpl.UnsafeEnabled { - mi := &file_api_v1_common_proto_msgTypes[127] + mi := &file_api_v1_common_proto_msgTypes[128] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -13658,7 +14843,7 @@ func (x *VolcanoJobDetails) String() string { func (*VolcanoJobDetails) ProtoMessage() {} func (x *VolcanoJobDetails) ProtoReflect() protoreflect.Message { - mi := &file_api_v1_common_proto_msgTypes[127] + mi := &file_api_v1_common_proto_msgTypes[128] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -13671,7 +14856,7 @@ func (x *VolcanoJobDetails) ProtoReflect() protoreflect.Message { // Deprecated: Use VolcanoJobDetails.ProtoReflect.Descriptor instead. func (*VolcanoJobDetails) Descriptor() ([]byte, []int) { - return file_api_v1_common_proto_rawDescGZIP(), []int{127} + return file_api_v1_common_proto_rawDescGZIP(), []int{128} } func (x *VolcanoJobDetails) GetContainers() []*ContainerTemplate { @@ -13739,7 +14924,7 @@ type VolcanoJobCondition struct { func (x *VolcanoJobCondition) Reset() { *x = VolcanoJobCondition{} if protoimpl.UnsafeEnabled { - mi := &file_api_v1_common_proto_msgTypes[128] + mi := &file_api_v1_common_proto_msgTypes[129] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -13752,7 +14937,7 @@ func (x *VolcanoJobCondition) String() string { func (*VolcanoJobCondition) ProtoMessage() {} func (x *VolcanoJobCondition) ProtoReflect() protoreflect.Message { - mi := &file_api_v1_common_proto_msgTypes[128] + mi := &file_api_v1_common_proto_msgTypes[129] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -13765,7 +14950,7 @@ func (x *VolcanoJobCondition) ProtoReflect() protoreflect.Message { // Deprecated: Use VolcanoJobCondition.ProtoReflect.Descriptor instead. func (*VolcanoJobCondition) Descriptor() ([]byte, []int) { - return file_api_v1_common_proto_rawDescGZIP(), []int{128} + return file_api_v1_common_proto_rawDescGZIP(), []int{129} } func (x *VolcanoJobCondition) GetType() string { @@ -13817,7 +15002,7 @@ type VolcanoTask struct { func (x *VolcanoTask) Reset() { *x = VolcanoTask{} if protoimpl.UnsafeEnabled { - mi := &file_api_v1_common_proto_msgTypes[129] + mi := &file_api_v1_common_proto_msgTypes[130] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -13830,7 +15015,7 @@ func (x *VolcanoTask) String() string { func (*VolcanoTask) ProtoMessage() {} func (x *VolcanoTask) ProtoReflect() protoreflect.Message { - mi := &file_api_v1_common_proto_msgTypes[129] + mi := &file_api_v1_common_proto_msgTypes[130] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -13843,7 +15028,7 @@ func (x *VolcanoTask) ProtoReflect() protoreflect.Message { // Deprecated: Use VolcanoTask.ProtoReflect.Descriptor instead. func (*VolcanoTask) Descriptor() ([]byte, []int) { - return file_api_v1_common_proto_rawDescGZIP(), []int{129} + return file_api_v1_common_proto_rawDescGZIP(), []int{130} } func (x *VolcanoTask) GetName() string { @@ -13894,7 +15079,7 @@ type SparkApplicationDetails struct { func (x *SparkApplicationDetails) Reset() { *x = SparkApplicationDetails{} if protoimpl.UnsafeEnabled { - mi := &file_api_v1_common_proto_msgTypes[130] + mi := &file_api_v1_common_proto_msgTypes[131] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -13907,7 +15092,7 @@ func (x *SparkApplicationDetails) String() string { func (*SparkApplicationDetails) ProtoMessage() {} func (x *SparkApplicationDetails) ProtoReflect() protoreflect.Message { - mi := &file_api_v1_common_proto_msgTypes[130] + mi := &file_api_v1_common_proto_msgTypes[131] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -13920,7 +15105,7 @@ func (x *SparkApplicationDetails) ProtoReflect() protoreflect.Message { // Deprecated: Use SparkApplicationDetails.ProtoReflect.Descriptor instead. func (*SparkApplicationDetails) Descriptor() ([]byte, []int) { - return file_api_v1_common_proto_rawDescGZIP(), []int{130} + return file_api_v1_common_proto_rawDescGZIP(), []int{131} } func (x *SparkApplicationDetails) GetContainers() []*ContainerTemplate { @@ -14055,7 +15240,7 @@ type SparkDriverInfo struct { func (x *SparkDriverInfo) Reset() { *x = SparkDriverInfo{} if protoimpl.UnsafeEnabled { - mi := &file_api_v1_common_proto_msgTypes[131] + mi := &file_api_v1_common_proto_msgTypes[132] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -14068,7 +15253,7 @@ func (x *SparkDriverInfo) String() string { func (*SparkDriverInfo) ProtoMessage() {} func (x *SparkDriverInfo) ProtoReflect() protoreflect.Message { - mi := &file_api_v1_common_proto_msgTypes[131] + mi := &file_api_v1_common_proto_msgTypes[132] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -14081,7 +15266,7 @@ func (x *SparkDriverInfo) ProtoReflect() protoreflect.Message { // Deprecated: Use SparkDriverInfo.ProtoReflect.Descriptor instead. func (*SparkDriverInfo) Descriptor() ([]byte, []int) { - return file_api_v1_common_proto_rawDescGZIP(), []int{131} + return file_api_v1_common_proto_rawDescGZIP(), []int{132} } func (x *SparkDriverInfo) GetPodName() string { @@ -14163,7 +15348,7 @@ type SparkRestartPolicyInfo struct { func (x *SparkRestartPolicyInfo) Reset() { *x = SparkRestartPolicyInfo{} if protoimpl.UnsafeEnabled { - mi := &file_api_v1_common_proto_msgTypes[132] + mi := &file_api_v1_common_proto_msgTypes[133] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -14176,7 +15361,7 @@ func (x *SparkRestartPolicyInfo) String() string { func (*SparkRestartPolicyInfo) ProtoMessage() {} func (x *SparkRestartPolicyInfo) ProtoReflect() protoreflect.Message { - mi := &file_api_v1_common_proto_msgTypes[132] + mi := &file_api_v1_common_proto_msgTypes[133] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -14189,7 +15374,7 @@ func (x *SparkRestartPolicyInfo) ProtoReflect() protoreflect.Message { // Deprecated: Use SparkRestartPolicyInfo.ProtoReflect.Descriptor instead. func (*SparkRestartPolicyInfo) Descriptor() ([]byte, []int) { - return file_api_v1_common_proto_rawDescGZIP(), []int{132} + return file_api_v1_common_proto_rawDescGZIP(), []int{133} } func (x *SparkRestartPolicyInfo) GetType() string { @@ -14244,7 +15429,7 @@ type SparkDynamicAllocationInfo struct { func (x *SparkDynamicAllocationInfo) Reset() { *x = SparkDynamicAllocationInfo{} if protoimpl.UnsafeEnabled { - mi := &file_api_v1_common_proto_msgTypes[133] + mi := &file_api_v1_common_proto_msgTypes[134] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -14257,7 +15442,7 @@ func (x *SparkDynamicAllocationInfo) String() string { func (*SparkDynamicAllocationInfo) ProtoMessage() {} func (x *SparkDynamicAllocationInfo) ProtoReflect() protoreflect.Message { - mi := &file_api_v1_common_proto_msgTypes[133] + mi := &file_api_v1_common_proto_msgTypes[134] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -14270,7 +15455,7 @@ func (x *SparkDynamicAllocationInfo) ProtoReflect() protoreflect.Message { // Deprecated: Use SparkDynamicAllocationInfo.ProtoReflect.Descriptor instead. func (*SparkDynamicAllocationInfo) Descriptor() ([]byte, []int) { - return file_api_v1_common_proto_rawDescGZIP(), []int{133} + return file_api_v1_common_proto_rawDescGZIP(), []int{134} } func (x *SparkDynamicAllocationInfo) GetEnabled() bool { @@ -14341,7 +15526,7 @@ type ScheduledSparkApplicationDetails struct { func (x *ScheduledSparkApplicationDetails) Reset() { *x = ScheduledSparkApplicationDetails{} if protoimpl.UnsafeEnabled { - mi := &file_api_v1_common_proto_msgTypes[134] + mi := &file_api_v1_common_proto_msgTypes[135] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -14354,7 +15539,7 @@ func (x *ScheduledSparkApplicationDetails) String() string { func (*ScheduledSparkApplicationDetails) ProtoMessage() {} func (x *ScheduledSparkApplicationDetails) ProtoReflect() protoreflect.Message { - mi := &file_api_v1_common_proto_msgTypes[134] + mi := &file_api_v1_common_proto_msgTypes[135] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -14367,7 +15552,7 @@ func (x *ScheduledSparkApplicationDetails) ProtoReflect() protoreflect.Message { // Deprecated: Use ScheduledSparkApplicationDetails.ProtoReflect.Descriptor instead. func (*ScheduledSparkApplicationDetails) Descriptor() ([]byte, []int) { - return file_api_v1_common_proto_rawDescGZIP(), []int{134} + return file_api_v1_common_proto_rawDescGZIP(), []int{135} } func (x *ScheduledSparkApplicationDetails) GetSchedule() string { @@ -14485,7 +15670,7 @@ type KyvernoPolicyDetails struct { func (x *KyvernoPolicyDetails) Reset() { *x = KyvernoPolicyDetails{} if protoimpl.UnsafeEnabled { - mi := &file_api_v1_common_proto_msgTypes[135] + mi := &file_api_v1_common_proto_msgTypes[136] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -14498,7 +15683,7 @@ func (x *KyvernoPolicyDetails) String() string { func (*KyvernoPolicyDetails) ProtoMessage() {} func (x *KyvernoPolicyDetails) ProtoReflect() protoreflect.Message { - mi := &file_api_v1_common_proto_msgTypes[135] + mi := &file_api_v1_common_proto_msgTypes[136] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -14511,7 +15696,7 @@ func (x *KyvernoPolicyDetails) ProtoReflect() protoreflect.Message { // Deprecated: Use KyvernoPolicyDetails.ProtoReflect.Descriptor instead. func (*KyvernoPolicyDetails) Descriptor() ([]byte, []int) { - return file_api_v1_common_proto_rawDescGZIP(), []int{135} + return file_api_v1_common_proto_rawDescGZIP(), []int{136} } func (x *KyvernoPolicyDetails) GetKind() string { @@ -14575,7 +15760,7 @@ type PolicyReportResult struct { func (x *PolicyReportResult) Reset() { *x = PolicyReportResult{} if protoimpl.UnsafeEnabled { - mi := &file_api_v1_common_proto_msgTypes[136] + mi := &file_api_v1_common_proto_msgTypes[137] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -14588,7 +15773,7 @@ func (x *PolicyReportResult) String() string { func (*PolicyReportResult) ProtoMessage() {} func (x *PolicyReportResult) ProtoReflect() protoreflect.Message { - mi := &file_api_v1_common_proto_msgTypes[136] + mi := &file_api_v1_common_proto_msgTypes[137] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -14601,7 +15786,7 @@ func (x *PolicyReportResult) ProtoReflect() protoreflect.Message { // Deprecated: Use PolicyReportResult.ProtoReflect.Descriptor instead. func (*PolicyReportResult) Descriptor() ([]byte, []int) { - return file_api_v1_common_proto_rawDescGZIP(), []int{136} + return file_api_v1_common_proto_rawDescGZIP(), []int{137} } func (x *PolicyReportResult) GetPolicy() string { @@ -14683,7 +15868,7 @@ type KyvernoPolicyReportDetails struct { func (x *KyvernoPolicyReportDetails) Reset() { *x = KyvernoPolicyReportDetails{} if protoimpl.UnsafeEnabled { - mi := &file_api_v1_common_proto_msgTypes[137] + mi := &file_api_v1_common_proto_msgTypes[138] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -14696,7 +15881,7 @@ func (x *KyvernoPolicyReportDetails) String() string { func (*KyvernoPolicyReportDetails) ProtoMessage() {} func (x *KyvernoPolicyReportDetails) ProtoReflect() protoreflect.Message { - mi := &file_api_v1_common_proto_msgTypes[137] + mi := &file_api_v1_common_proto_msgTypes[138] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -14709,7 +15894,7 @@ func (x *KyvernoPolicyReportDetails) ProtoReflect() protoreflect.Message { // Deprecated: Use KyvernoPolicyReportDetails.ProtoReflect.Descriptor instead. func (*KyvernoPolicyReportDetails) Descriptor() ([]byte, []int) { - return file_api_v1_common_proto_rawDescGZIP(), []int{137} + return file_api_v1_common_proto_rawDescGZIP(), []int{138} } func (x *KyvernoPolicyReportDetails) GetKind() string { @@ -14804,7 +15989,7 @@ type GatekeeperConstraintTemplateDetails struct { func (x *GatekeeperConstraintTemplateDetails) Reset() { *x = GatekeeperConstraintTemplateDetails{} if protoimpl.UnsafeEnabled { - mi := &file_api_v1_common_proto_msgTypes[138] + mi := &file_api_v1_common_proto_msgTypes[139] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -14817,7 +16002,7 @@ func (x *GatekeeperConstraintTemplateDetails) String() string { func (*GatekeeperConstraintTemplateDetails) ProtoMessage() {} func (x *GatekeeperConstraintTemplateDetails) ProtoReflect() protoreflect.Message { - mi := &file_api_v1_common_proto_msgTypes[138] + mi := &file_api_v1_common_proto_msgTypes[139] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -14830,7 +16015,7 @@ func (x *GatekeeperConstraintTemplateDetails) ProtoReflect() protoreflect.Messag // Deprecated: Use GatekeeperConstraintTemplateDetails.ProtoReflect.Descriptor instead. func (*GatekeeperConstraintTemplateDetails) Descriptor() ([]byte, []int) { - return file_api_v1_common_proto_rawDescGZIP(), []int{138} + return file_api_v1_common_proto_rawDescGZIP(), []int{139} } func (x *GatekeeperConstraintTemplateDetails) GetCrdKind() string { @@ -14877,7 +16062,7 @@ type ConstraintViolation struct { func (x *ConstraintViolation) Reset() { *x = ConstraintViolation{} if protoimpl.UnsafeEnabled { - mi := &file_api_v1_common_proto_msgTypes[139] + mi := &file_api_v1_common_proto_msgTypes[140] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -14890,7 +16075,7 @@ func (x *ConstraintViolation) String() string { func (*ConstraintViolation) ProtoMessage() {} func (x *ConstraintViolation) ProtoReflect() protoreflect.Message { - mi := &file_api_v1_common_proto_msgTypes[139] + mi := &file_api_v1_common_proto_msgTypes[140] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -14903,7 +16088,7 @@ func (x *ConstraintViolation) ProtoReflect() protoreflect.Message { // Deprecated: Use ConstraintViolation.ProtoReflect.Descriptor instead. func (*ConstraintViolation) Descriptor() ([]byte, []int) { - return file_api_v1_common_proto_rawDescGZIP(), []int{139} + return file_api_v1_common_proto_rawDescGZIP(), []int{140} } func (x *ConstraintViolation) GetResourceKind() string { @@ -14960,7 +16145,7 @@ type GatekeeperConstraintDetails struct { func (x *GatekeeperConstraintDetails) Reset() { *x = GatekeeperConstraintDetails{} if protoimpl.UnsafeEnabled { - mi := &file_api_v1_common_proto_msgTypes[140] + mi := &file_api_v1_common_proto_msgTypes[141] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -14973,7 +16158,7 @@ func (x *GatekeeperConstraintDetails) String() string { func (*GatekeeperConstraintDetails) ProtoMessage() {} func (x *GatekeeperConstraintDetails) ProtoReflect() protoreflect.Message { - mi := &file_api_v1_common_proto_msgTypes[140] + mi := &file_api_v1_common_proto_msgTypes[141] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -14986,7 +16171,7 @@ func (x *GatekeeperConstraintDetails) ProtoReflect() protoreflect.Message { // Deprecated: Use GatekeeperConstraintDetails.ProtoReflect.Descriptor instead. func (*GatekeeperConstraintDetails) Descriptor() ([]byte, []int) { - return file_api_v1_common_proto_rawDescGZIP(), []int{140} + return file_api_v1_common_proto_rawDescGZIP(), []int{141} } func (x *GatekeeperConstraintDetails) GetKind() string { @@ -15038,67 +16223,55 @@ func (x *GatekeeperConstraintDetails) GetViolations() []*ConstraintViolation { return nil } -type Event struct { +// ArgoApplicationDetails contains detailed Argo CD Application information for +// drill-down. Every field here is a first-class column on k8s_argo_applications +// rather than a re-parse of the status blob, so the explorer can sort and filter +// on sync and health. +type ArgoApplicationDetails struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` - Kind string `protobuf:"bytes,2,opt,name=kind,proto3" json:"kind,omitempty"` - Name string `protobuf:"bytes,3,opt,name=name,proto3" json:"name,omitempty"` - Namespace string `protobuf:"bytes,4,opt,name=namespace,proto3" json:"namespace,omitempty"` - ClusterId string `protobuf:"bytes,5,opt,name=cluster_id,json=clusterId,proto3" json:"cluster_id,omitempty"` - OwnerUid string `protobuf:"bytes,7,opt,name=owner_uid,json=ownerUid,proto3" json:"owner_uid,omitempty"` - OwnerKind string `protobuf:"bytes,8,opt,name=owner_kind,json=ownerKind,proto3" json:"owner_kind,omitempty"` - OwnerName string `protobuf:"bytes,9,opt,name=owner_name,json=ownerName,proto3" json:"owner_name,omitempty"` - ApiVersion string `protobuf:"bytes,10,opt,name=api_version,json=apiVersion,proto3" json:"api_version,omitempty"` - ResourceVersion string `protobuf:"bytes,11,opt,name=resource_version,json=resourceVersion,proto3" json:"resource_version,omitempty"` - Count int32 `protobuf:"varint,12,opt,name=count,proto3" json:"count,omitempty"` - Reason string `protobuf:"bytes,13,opt,name=reason,proto3" json:"reason,omitempty"` - Message string `protobuf:"bytes,14,opt,name=message,proto3" json:"message,omitempty"` - Type string `protobuf:"bytes,15,opt,name=type,proto3" json:"type,omitempty"` - Action string `protobuf:"bytes,16,opt,name=action,proto3" json:"action,omitempty"` - Source string `protobuf:"bytes,17,opt,name=source,proto3" json:"source,omitempty"` - InvolvedObjectKind string `protobuf:"bytes,18,opt,name=involved_object_kind,json=involvedObjectKind,proto3" json:"involved_object_kind,omitempty"` - InvolvedObjectName string `protobuf:"bytes,19,opt,name=involved_object_name,json=involvedObjectName,proto3" json:"involved_object_name,omitempty"` - InvolvedObjectNamespace string `protobuf:"bytes,20,opt,name=involved_object_namespace,json=involvedObjectNamespace,proto3" json:"involved_object_namespace,omitempty"` - ReportingController string `protobuf:"bytes,21,opt,name=reporting_controller,json=reportingController,proto3" json:"reporting_controller,omitempty"` - ReportingInstance string `protobuf:"bytes,22,opt,name=reporting_instance,json=reportingInstance,proto3" json:"reporting_instance,omitempty"` - Annotations map[string]string `protobuf:"bytes,23,rep,name=annotations,proto3" json:"annotations,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` - Labels map[string]string `protobuf:"bytes,24,rep,name=labels,proto3" json:"labels,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` - InvolvedObjectUid string `protobuf:"bytes,25,opt,name=involved_object_uid,json=involvedObjectUid,proto3" json:"involved_object_uid,omitempty"` - // Set only on Karpenter `DisruptionBlocked` events; UNSPECIFIED on every other event. - DisruptionBlockReason DisruptionBlockReason `protobuf:"varint,26,opt,name=disruption_block_reason,json=disruptionBlockReason,proto3,enum=api.v1.DisruptionBlockReason" json:"disruption_block_reason,omitempty"` - // The PodDisruptionBudget that blocked eviction, as "namespace/name". Set only - // alongside DISRUPTION_BLOCK_REASON_PDB_VIOLATION; absent — not empty-string — - // otherwise, since "no PDB was involved" is not the same as "a PDB with no name". - BlockingPdbName *string `protobuf:"bytes,27,opt,name=blocking_pdb_name,json=blockingPdbName,proto3,oneof" json:"blocking_pdb_name,omitempty"` - CreatedAt *timestamppb.Timestamp `protobuf:"bytes,41,opt,name=created_at,json=createdAt,proto3" json:"created_at,omitempty"` - UpdatedAt *timestamppb.Timestamp `protobuf:"bytes,42,opt,name=updated_at,json=updatedAt,proto3" json:"updated_at,omitempty"` - LastSeen *timestamppb.Timestamp `protobuf:"bytes,43,opt,name=last_seen,json=lastSeen,proto3" json:"last_seen,omitempty"` - CreatedAtAge string `protobuf:"bytes,44,opt,name=created_at_age,json=createdAtAge,proto3" json:"created_at_age,omitempty"` - UpdatedAtAge string `protobuf:"bytes,45,opt,name=updated_at_age,json=updatedAtAge,proto3" json:"updated_at_age,omitempty"` - LastSeenAge string `protobuf:"bytes,46,opt,name=last_seen_age,json=lastSeenAge,proto3" json:"last_seen_age,omitempty"` - DeletedAt *timestamppb.Timestamp `protobuf:"bytes,47,opt,name=deleted_at,json=deletedAt,proto3" json:"deleted_at,omitempty"` -} - -func (x *Event) Reset() { - *x = Event{} + SyncStatus string `protobuf:"bytes,1,opt,name=sync_status,json=syncStatus,proto3" json:"sync_status,omitempty"` // Synced, OutOfSync or Unknown + HealthStatus string `protobuf:"bytes,2,opt,name=health_status,json=healthStatus,proto3" json:"health_status,omitempty"` // Healthy, Progressing, Degraded, Suspended, Missing or Unknown + SyncRevision string `protobuf:"bytes,3,opt,name=sync_revision,json=syncRevision,proto3" json:"sync_revision,omitempty"` // Git SHA the live state was compared against + ResourceHealthSource string `protobuf:"bytes,4,opt,name=resource_health_source,json=resourceHealthSource,proto3" json:"resource_health_source,omitempty"` // Empty means per-resource health is inline; appTree means it is not + Project string `protobuf:"bytes,5,opt,name=project,proto3" json:"project,omitempty"` // AppProject this Application belongs to + DestinationServer string `protobuf:"bytes,6,opt,name=destination_server,json=destinationServer,proto3" json:"destination_server,omitempty"` // Destination cluster API server URL + DestinationName string `protobuf:"bytes,7,opt,name=destination_name,json=destinationName,proto3" json:"destination_name,omitempty"` // Destination cluster name, when named rather than addressed + DestinationNamespace string `protobuf:"bytes,8,opt,name=destination_namespace,json=destinationNamespace,proto3" json:"destination_namespace,omitempty"` // Namespace the Application deploys into + RepoUrl string `protobuf:"bytes,9,opt,name=repo_url,json=repoUrl,proto3" json:"repo_url,omitempty"` // First source repository URL + SourcePath string `protobuf:"bytes,10,opt,name=source_path,json=sourcePath,proto3" json:"source_path,omitempty"` // First source path within the repository + TargetRevision string `protobuf:"bytes,11,opt,name=target_revision,json=targetRevision,proto3" json:"target_revision,omitempty"` // First source target revision (branch, tag or SHA) + SourceCount int32 `protobuf:"varint,12,opt,name=source_count,json=sourceCount,proto3" json:"source_count,omitempty"` // Number of sources; greater than 1 means the source fields above are truncated + AutomatedSync bool `protobuf:"varint,13,opt,name=automated_sync,json=automatedSync,proto3" json:"automated_sync,omitempty"` // spec.syncPolicy.automated is present and enabled + SelfHeal bool `protobuf:"varint,14,opt,name=self_heal,json=selfHeal,proto3" json:"self_heal,omitempty"` // Automated sync reverts manual drift + Prune bool `protobuf:"varint,15,opt,name=prune,proto3" json:"prune,omitempty"` // Automated sync deletes resources removed from git + ManagedResourceCount int32 `protobuf:"varint,16,opt,name=managed_resource_count,json=managedResourceCount,proto3" json:"managed_resource_count,omitempty"` // Resources Argo reports itself as managing + ReconciledAt *timestamppb.Timestamp `protobuf:"bytes,17,opt,name=reconciled_at,json=reconciledAt,proto3" json:"reconciled_at,omitempty"` // When Argo last reconciled the Application + LastSyncAt *timestamppb.Timestamp `protobuf:"bytes,18,opt,name=last_sync_at,json=lastSyncAt,proto3" json:"last_sync_at,omitempty"` // status.operationState.finishedAt: when the last sync operation ended, which is what Argo labels Last Sync. Unset while a sync is in flight or if the Application was never synced + LastSyncPhase string `protobuf:"bytes,19,opt,name=last_sync_phase,json=lastSyncPhase,proto3" json:"last_sync_phase,omitempty"` // Outcome of that sync operation: Succeeded, Failed, Error, Running or Terminating + SpecJson string `protobuf:"bytes,20,opt,name=spec_json,json=specJson,proto3" json:"spec_json,omitempty"` // Full spec, verbatim as ingested (not re-derived from the fields above), so a field this message has not lifted yet is still visible + StatusJson string `protobuf:"bytes,21,opt,name=status_json,json=statusJson,proto3" json:"status_json,omitempty"` // Full status, verbatim as ingested +} + +func (x *ArgoApplicationDetails) Reset() { + *x = ArgoApplicationDetails{} if protoimpl.UnsafeEnabled { - mi := &file_api_v1_common_proto_msgTypes[141] + mi := &file_api_v1_common_proto_msgTypes[142] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } -func (x *Event) String() string { +func (x *ArgoApplicationDetails) String() string { return protoimpl.X.MessageStringOf(x) } -func (*Event) ProtoMessage() {} +func (*ArgoApplicationDetails) ProtoMessage() {} -func (x *Event) ProtoReflect() protoreflect.Message { - mi := &file_api_v1_common_proto_msgTypes[141] +func (x *ArgoApplicationDetails) ProtoReflect() protoreflect.Message { + mi := &file_api_v1_common_proto_msgTypes[142] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -15109,273 +16282,333 @@ func (x *Event) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use Event.ProtoReflect.Descriptor instead. -func (*Event) Descriptor() ([]byte, []int) { - return file_api_v1_common_proto_rawDescGZIP(), []int{141} +// Deprecated: Use ArgoApplicationDetails.ProtoReflect.Descriptor instead. +func (*ArgoApplicationDetails) Descriptor() ([]byte, []int) { + return file_api_v1_common_proto_rawDescGZIP(), []int{142} } -func (x *Event) GetId() string { +func (x *ArgoApplicationDetails) GetSyncStatus() string { if x != nil { - return x.Id + return x.SyncStatus } return "" } -func (x *Event) GetKind() string { +func (x *ArgoApplicationDetails) GetHealthStatus() string { if x != nil { - return x.Kind + return x.HealthStatus } return "" } -func (x *Event) GetName() string { +func (x *ArgoApplicationDetails) GetSyncRevision() string { if x != nil { - return x.Name + return x.SyncRevision } return "" } -func (x *Event) GetNamespace() string { +func (x *ArgoApplicationDetails) GetResourceHealthSource() string { if x != nil { - return x.Namespace + return x.ResourceHealthSource } return "" } -func (x *Event) GetClusterId() string { +func (x *ArgoApplicationDetails) GetProject() string { if x != nil { - return x.ClusterId + return x.Project } return "" } -func (x *Event) GetOwnerUid() string { +func (x *ArgoApplicationDetails) GetDestinationServer() string { if x != nil { - return x.OwnerUid + return x.DestinationServer } return "" } -func (x *Event) GetOwnerKind() string { +func (x *ArgoApplicationDetails) GetDestinationName() string { if x != nil { - return x.OwnerKind + return x.DestinationName } return "" } -func (x *Event) GetOwnerName() string { +func (x *ArgoApplicationDetails) GetDestinationNamespace() string { if x != nil { - return x.OwnerName + return x.DestinationNamespace } return "" } -func (x *Event) GetApiVersion() string { +func (x *ArgoApplicationDetails) GetRepoUrl() string { if x != nil { - return x.ApiVersion + return x.RepoUrl } return "" } -func (x *Event) GetResourceVersion() string { +func (x *ArgoApplicationDetails) GetSourcePath() string { if x != nil { - return x.ResourceVersion + return x.SourcePath } return "" } -func (x *Event) GetCount() int32 { +func (x *ArgoApplicationDetails) GetTargetRevision() string { if x != nil { - return x.Count + return x.TargetRevision + } + return "" +} + +func (x *ArgoApplicationDetails) GetSourceCount() int32 { + if x != nil { + return x.SourceCount } return 0 } -func (x *Event) GetReason() string { +func (x *ArgoApplicationDetails) GetAutomatedSync() bool { if x != nil { - return x.Reason + return x.AutomatedSync } - return "" + return false } -func (x *Event) GetMessage() string { +func (x *ArgoApplicationDetails) GetSelfHeal() bool { if x != nil { - return x.Message + return x.SelfHeal } - return "" + return false } -func (x *Event) GetType() string { +func (x *ArgoApplicationDetails) GetPrune() bool { if x != nil { - return x.Type + return x.Prune } - return "" + return false } -func (x *Event) GetAction() string { +func (x *ArgoApplicationDetails) GetManagedResourceCount() int32 { if x != nil { - return x.Action + return x.ManagedResourceCount } - return "" + return 0 } -func (x *Event) GetSource() string { +func (x *ArgoApplicationDetails) GetReconciledAt() *timestamppb.Timestamp { if x != nil { - return x.Source + return x.ReconciledAt } - return "" + return nil } -func (x *Event) GetInvolvedObjectKind() string { +func (x *ArgoApplicationDetails) GetLastSyncAt() *timestamppb.Timestamp { if x != nil { - return x.InvolvedObjectKind + return x.LastSyncAt } - return "" + return nil } -func (x *Event) GetInvolvedObjectName() string { +func (x *ArgoApplicationDetails) GetLastSyncPhase() string { if x != nil { - return x.InvolvedObjectName + return x.LastSyncPhase } return "" } -func (x *Event) GetInvolvedObjectNamespace() string { +func (x *ArgoApplicationDetails) GetSpecJson() string { if x != nil { - return x.InvolvedObjectNamespace + return x.SpecJson } return "" } -func (x *Event) GetReportingController() string { +func (x *ArgoApplicationDetails) GetStatusJson() string { if x != nil { - return x.ReportingController + return x.StatusJson } return "" } -func (x *Event) GetReportingInstance() string { +// ArgoApplicationSetDetails contains detailed Argo CD ApplicationSet information +// for drill-down. +type ArgoApplicationSetDetails struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + GeneratorCount int32 `protobuf:"varint,1,opt,name=generator_count,json=generatorCount,proto3" json:"generator_count,omitempty"` // Number of entries in spec.generators + GeneratorTypes []string `protobuf:"bytes,2,rep,name=generator_types,json=generatorTypes,proto3" json:"generator_types,omitempty"` // Generator kinds, e.g. list, git, clusters + GoTemplate bool `protobuf:"varint,3,opt,name=go_template,json=goTemplate,proto3" json:"go_template,omitempty"` // spec.goTemplate, which changes the templating syntax + StrategyType string `protobuf:"bytes,4,opt,name=strategy_type,json=strategyType,proto3" json:"strategy_type,omitempty"` // spec.strategy.type, e.g. RollingSync, or empty + TemplateProject string `protobuf:"bytes,5,opt,name=template_project,json=templateProject,proto3" json:"template_project,omitempty"` // AppProject every generated Application inherits + TemplateRepoUrl string `protobuf:"bytes,6,opt,name=template_repo_url,json=templateRepoUrl,proto3" json:"template_repo_url,omitempty"` // Source repository every generated Application inherits + TemplateDestinationNamespace string `protobuf:"bytes,7,opt,name=template_destination_namespace,json=templateDestinationNamespace,proto3" json:"template_destination_namespace,omitempty"` // Destination namespace template + ResourcesCount int64 `protobuf:"varint,8,opt,name=resources_count,json=resourcesCount,proto3" json:"resources_count,omitempty"` // status.resourcesCount: the authoritative generated-Application count + ReportedResourceCount int32 `protobuf:"varint,9,opt,name=reported_resource_count,json=reportedResourceCount,proto3" json:"reported_resource_count,omitempty"` // len(status.resources), which the controller truncates on large sets + HasErrorCondition bool `protobuf:"varint,10,opt,name=has_error_condition,json=hasErrorCondition,proto3" json:"has_error_condition,omitempty"` // Any status.conditions entry reports an error + ConditionMessage string `protobuf:"bytes,11,opt,name=condition_message,json=conditionMessage,proto3" json:"condition_message,omitempty"` // Message from the erroring condition + SpecJson string `protobuf:"bytes,12,opt,name=spec_json,json=specJson,proto3" json:"spec_json,omitempty"` // Full spec, verbatim as ingested (not re-derived from the fields above), so a field this message has not lifted yet is still visible + StatusJson string `protobuf:"bytes,13,opt,name=status_json,json=statusJson,proto3" json:"status_json,omitempty"` // Full status, verbatim as ingested +} + +func (x *ArgoApplicationSetDetails) Reset() { + *x = ArgoApplicationSetDetails{} + if protoimpl.UnsafeEnabled { + mi := &file_api_v1_common_proto_msgTypes[143] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ArgoApplicationSetDetails) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ArgoApplicationSetDetails) ProtoMessage() {} + +func (x *ArgoApplicationSetDetails) ProtoReflect() protoreflect.Message { + mi := &file_api_v1_common_proto_msgTypes[143] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ArgoApplicationSetDetails.ProtoReflect.Descriptor instead. +func (*ArgoApplicationSetDetails) Descriptor() ([]byte, []int) { + return file_api_v1_common_proto_rawDescGZIP(), []int{143} +} + +func (x *ArgoApplicationSetDetails) GetGeneratorCount() int32 { if x != nil { - return x.ReportingInstance + return x.GeneratorCount } - return "" + return 0 } -func (x *Event) GetAnnotations() map[string]string { +func (x *ArgoApplicationSetDetails) GetGeneratorTypes() []string { if x != nil { - return x.Annotations + return x.GeneratorTypes } return nil } -func (x *Event) GetLabels() map[string]string { +func (x *ArgoApplicationSetDetails) GetGoTemplate() bool { if x != nil { - return x.Labels + return x.GoTemplate } - return nil + return false } -func (x *Event) GetInvolvedObjectUid() string { +func (x *ArgoApplicationSetDetails) GetStrategyType() string { if x != nil { - return x.InvolvedObjectUid + return x.StrategyType } return "" } -func (x *Event) GetDisruptionBlockReason() DisruptionBlockReason { +func (x *ArgoApplicationSetDetails) GetTemplateProject() string { if x != nil { - return x.DisruptionBlockReason + return x.TemplateProject } - return DisruptionBlockReason_DISRUPTION_BLOCK_REASON_UNSPECIFIED + return "" } -func (x *Event) GetBlockingPdbName() string { - if x != nil && x.BlockingPdbName != nil { - return *x.BlockingPdbName +func (x *ArgoApplicationSetDetails) GetTemplateRepoUrl() string { + if x != nil { + return x.TemplateRepoUrl } return "" } -func (x *Event) GetCreatedAt() *timestamppb.Timestamp { +func (x *ArgoApplicationSetDetails) GetTemplateDestinationNamespace() string { if x != nil { - return x.CreatedAt + return x.TemplateDestinationNamespace } - return nil + return "" } -func (x *Event) GetUpdatedAt() *timestamppb.Timestamp { +func (x *ArgoApplicationSetDetails) GetResourcesCount() int64 { if x != nil { - return x.UpdatedAt + return x.ResourcesCount } - return nil + return 0 } -func (x *Event) GetLastSeen() *timestamppb.Timestamp { +func (x *ArgoApplicationSetDetails) GetReportedResourceCount() int32 { if x != nil { - return x.LastSeen + return x.ReportedResourceCount } - return nil + return 0 } -func (x *Event) GetCreatedAtAge() string { +func (x *ArgoApplicationSetDetails) GetHasErrorCondition() bool { if x != nil { - return x.CreatedAtAge + return x.HasErrorCondition } - return "" + return false } -func (x *Event) GetUpdatedAtAge() string { +func (x *ArgoApplicationSetDetails) GetConditionMessage() string { if x != nil { - return x.UpdatedAtAge + return x.ConditionMessage } return "" } -func (x *Event) GetLastSeenAge() string { +func (x *ArgoApplicationSetDetails) GetSpecJson() string { if x != nil { - return x.LastSeenAge + return x.SpecJson } return "" } -func (x *Event) GetDeletedAt() *timestamppb.Timestamp { +func (x *ArgoApplicationSetDetails) GetStatusJson() string { if x != nil { - return x.DeletedAt + return x.StatusJson } - return nil + return "" } -type EventDatapointInfo struct { +// CustomResourceDefinitionVersion is one entry of spec.versions on a CRD. +// A CRD always serves at least one version and stores exactly one. +type CustomResourceDefinitionVersion struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` // Event name - Message string `protobuf:"bytes,2,opt,name=message,proto3" json:"message,omitempty"` // Event message - OwnerUid string `protobuf:"bytes,3,opt,name=owner_uid,json=ownerUid,proto3" json:"owner_uid,omitempty"` // Event owner name - OwnerKind string `protobuf:"bytes,4,opt,name=owner_kind,json=ownerKind,proto3" json:"owner_kind,omitempty"` // Event owner kind - OwnerName string `protobuf:"bytes,5,opt,name=owner_name,json=ownerName,proto3" json:"owner_name,omitempty"` // Event owner name - Type string `protobuf:"bytes,6,opt,name=type,proto3" json:"type,omitempty"` // Event type - Reason string `protobuf:"bytes,7,opt,name=reason,proto3" json:"reason,omitempty"` // Event reason + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` // Version name, e.g. v1alpha1 + Served bool `protobuf:"varint,2,opt,name=served,proto3" json:"served,omitempty"` // Whether the API server serves this version + Storage bool `protobuf:"varint,3,opt,name=storage,proto3" json:"storage,omitempty"` // Whether objects are persisted at this version (exactly one is true) + Deprecated bool `protobuf:"varint,4,opt,name=deprecated,proto3" json:"deprecated,omitempty"` // Whether the API server warns on use of this version } -func (x *EventDatapointInfo) Reset() { - *x = EventDatapointInfo{} +func (x *CustomResourceDefinitionVersion) Reset() { + *x = CustomResourceDefinitionVersion{} if protoimpl.UnsafeEnabled { - mi := &file_api_v1_common_proto_msgTypes[142] + mi := &file_api_v1_common_proto_msgTypes[144] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } -func (x *EventDatapointInfo) String() string { +func (x *CustomResourceDefinitionVersion) String() string { return protoimpl.X.MessageStringOf(x) } -func (*EventDatapointInfo) ProtoMessage() {} +func (*CustomResourceDefinitionVersion) ProtoMessage() {} -func (x *EventDatapointInfo) ProtoReflect() protoreflect.Message { - mi := &file_api_v1_common_proto_msgTypes[142] +func (x *CustomResourceDefinitionVersion) ProtoReflect() protoreflect.Message { + mi := &file_api_v1_common_proto_msgTypes[144] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -15386,90 +16619,90 @@ func (x *EventDatapointInfo) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use EventDatapointInfo.ProtoReflect.Descriptor instead. -func (*EventDatapointInfo) Descriptor() ([]byte, []int) { - return file_api_v1_common_proto_rawDescGZIP(), []int{142} +// Deprecated: Use CustomResourceDefinitionVersion.ProtoReflect.Descriptor instead. +func (*CustomResourceDefinitionVersion) Descriptor() ([]byte, []int) { + return file_api_v1_common_proto_rawDescGZIP(), []int{144} } -func (x *EventDatapointInfo) GetName() string { +func (x *CustomResourceDefinitionVersion) GetName() string { if x != nil { return x.Name } return "" } -func (x *EventDatapointInfo) GetMessage() string { - if x != nil { - return x.Message - } - return "" -} - -func (x *EventDatapointInfo) GetOwnerUid() string { - if x != nil { - return x.OwnerUid - } - return "" -} - -func (x *EventDatapointInfo) GetOwnerKind() string { - if x != nil { - return x.OwnerKind - } - return "" -} - -func (x *EventDatapointInfo) GetOwnerName() string { +func (x *CustomResourceDefinitionVersion) GetServed() bool { if x != nil { - return x.OwnerName + return x.Served } - return "" + return false } -func (x *EventDatapointInfo) GetType() string { +func (x *CustomResourceDefinitionVersion) GetStorage() bool { if x != nil { - return x.Type + return x.Storage } - return "" + return false } -func (x *EventDatapointInfo) GetReason() string { +func (x *CustomResourceDefinitionVersion) GetDeprecated() bool { if x != nil { - return x.Reason + return x.Deprecated } - return "" + return false } -type EventDatapoint struct { +// CustomResourceDefinitionDetails contains detailed CustomResourceDefinition +// information for drill-down. A CRD is the declaration of an API extension, so +// every field here is read from spec.names / spec.versions rather than from any +// running object. Name, cluster and creation timestamp ride the WorkloadItem, +// not this message. +type CustomResourceDefinitionDetails struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - UtcTime string `protobuf:"bytes,1,opt,name=utc_time,json=utcTime,proto3" json:"utc_time,omitempty"` - Count int32 `protobuf:"varint,2,opt,name=count,proto3" json:"count,omitempty"` // Number of events in this time bucket - Events []*EventDatapointInfo `protobuf:"bytes,3,rep,name=events,proto3" json:"events,omitempty"` // Details of events in this bucket (deprecated: use normal_count/warning_count) - NormalCount int32 `protobuf:"varint,4,opt,name=normal_count,json=normalCount,proto3" json:"normal_count,omitempty"` // Count of Normal events in this time bucket - WarningCount int32 `protobuf:"varint,5,opt,name=warning_count,json=warningCount,proto3" json:"warning_count,omitempty"` // Count of Warning events in this time bucket - CriticalCount int32 `protobuf:"varint,6,opt,name=critical_count,json=criticalCount,proto3" json:"critical_count,omitempty"` // Count of events with critical reasons (OOMKilled, CrashLoopBackOff, NodeNotReady, etc.) -} - -func (x *EventDatapoint) Reset() { - *x = EventDatapoint{} + Group string `protobuf:"bytes,1,opt,name=group,proto3" json:"group,omitempty"` // spec.group, e.g. cert-manager.io + Scope string `protobuf:"bytes,2,opt,name=scope,proto3" json:"scope,omitempty"` // spec.scope: Namespaced or Cluster + Kind string `protobuf:"bytes,3,opt,name=kind,proto3" json:"kind,omitempty"` // spec.names.kind, the kind this CRD declares + Plural string `protobuf:"bytes,4,opt,name=plural,proto3" json:"plural,omitempty"` // spec.names.plural, the resource path segment + Singular string `protobuf:"bytes,5,opt,name=singular,proto3" json:"singular,omitempty"` // spec.names.singular + ListKind string `protobuf:"bytes,6,opt,name=list_kind,json=listKind,proto3" json:"list_kind,omitempty"` // spec.names.listKind + ShortNames []string `protobuf:"bytes,7,rep,name=short_names,json=shortNames,proto3" json:"short_names,omitempty"` // spec.names.shortNames, the kubectl aliases + Versions []*CustomResourceDefinitionVersion `protobuf:"bytes,8,rep,name=versions,proto3" json:"versions,omitempty"` // spec.versions, in declaration order + StorageVersion string `protobuf:"bytes,9,opt,name=storage_version,json=storageVersion,proto3" json:"storage_version,omitempty"` // Name of the spec.versions entry with storage=true + StoredVersions []string `protobuf:"bytes,10,rep,name=stored_versions,json=storedVersions,proto3" json:"stored_versions,omitempty"` // status.storedVersions: versions still present in etcd, which gate version removal + Established bool `protobuf:"varint,11,opt,name=established,proto3" json:"established,omitempty"` // status.conditions Established=True, meaning the API server accepted and is serving this CRD + ConversionStrategy string `protobuf:"bytes,12,opt,name=conversion_strategy,json=conversionStrategy,proto3" json:"conversion_strategy,omitempty"` // spec.conversion.strategy: None or Webhook + // Full spec as JSON for raw inspection, set by GetWorkload only. A CRD spec is + // dominated by its per-version OpenAPI schemas and is unbounded in a way no + // other kind here is -- applications.argoproj.io alone is 190 KB of compact + // JSON -- so the paginated list path leaves it empty and serves the typed + // fields above. Empty therefore means "not requested", never "no spec". + SpecJson string `protobuf:"bytes,13,opt,name=spec_json,json=specJson,proto3" json:"spec_json,omitempty"` + // Full status as JSON for raw inspection, on both the list and detail paths. + // Unlike the spec it is small and bounded: stored versions, accepted names and + // a handful of conditions. + StatusJson string `protobuf:"bytes,14,opt,name=status_json,json=statusJson,proto3" json:"status_json,omitempty"` +} + +func (x *CustomResourceDefinitionDetails) Reset() { + *x = CustomResourceDefinitionDetails{} if protoimpl.UnsafeEnabled { - mi := &file_api_v1_common_proto_msgTypes[143] + mi := &file_api_v1_common_proto_msgTypes[145] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } -func (x *EventDatapoint) String() string { +func (x *CustomResourceDefinitionDetails) String() string { return protoimpl.X.MessageStringOf(x) } -func (*EventDatapoint) ProtoMessage() {} +func (*CustomResourceDefinitionDetails) ProtoMessage() {} -func (x *EventDatapoint) ProtoReflect() protoreflect.Message { - mi := &file_api_v1_common_proto_msgTypes[143] +func (x *CustomResourceDefinitionDetails) ProtoReflect() protoreflect.Message { + mi := &file_api_v1_common_proto_msgTypes[145] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -15480,150 +16713,151 @@ func (x *EventDatapoint) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use EventDatapoint.ProtoReflect.Descriptor instead. -func (*EventDatapoint) Descriptor() ([]byte, []int) { - return file_api_v1_common_proto_rawDescGZIP(), []int{143} +// Deprecated: Use CustomResourceDefinitionDetails.ProtoReflect.Descriptor instead. +func (*CustomResourceDefinitionDetails) Descriptor() ([]byte, []int) { + return file_api_v1_common_proto_rawDescGZIP(), []int{145} } -func (x *EventDatapoint) GetUtcTime() string { +func (x *CustomResourceDefinitionDetails) GetGroup() string { if x != nil { - return x.UtcTime + return x.Group } return "" } -func (x *EventDatapoint) GetCount() int32 { +func (x *CustomResourceDefinitionDetails) GetScope() string { if x != nil { - return x.Count + return x.Scope } - return 0 + return "" } -func (x *EventDatapoint) GetEvents() []*EventDatapointInfo { +func (x *CustomResourceDefinitionDetails) GetKind() string { if x != nil { - return x.Events + return x.Kind } - return nil + return "" } -func (x *EventDatapoint) GetNormalCount() int32 { +func (x *CustomResourceDefinitionDetails) GetPlural() string { if x != nil { - return x.NormalCount + return x.Plural } - return 0 + return "" } -func (x *EventDatapoint) GetWarningCount() int32 { +func (x *CustomResourceDefinitionDetails) GetSingular() string { if x != nil { - return x.WarningCount + return x.Singular } - return 0 + return "" } -func (x *EventDatapoint) GetCriticalCount() int32 { +func (x *CustomResourceDefinitionDetails) GetListKind() string { if x != nil { - return x.CriticalCount + return x.ListKind } - return 0 + return "" } -type DimensionCount struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Value string `protobuf:"bytes,1,opt,name=value,proto3" json:"value,omitempty"` - Count int32 `protobuf:"varint,2,opt,name=count,proto3" json:"count,omitempty"` - Percentage float64 `protobuf:"fixed64,3,opt,name=percentage,proto3" json:"percentage,omitempty"` +func (x *CustomResourceDefinitionDetails) GetShortNames() []string { + if x != nil { + return x.ShortNames + } + return nil } -func (x *DimensionCount) Reset() { - *x = DimensionCount{} - if protoimpl.UnsafeEnabled { - mi := &file_api_v1_common_proto_msgTypes[144] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) +func (x *CustomResourceDefinitionDetails) GetVersions() []*CustomResourceDefinitionVersion { + if x != nil { + return x.Versions } + return nil } -func (x *DimensionCount) String() string { - return protoimpl.X.MessageStringOf(x) +func (x *CustomResourceDefinitionDetails) GetStorageVersion() string { + if x != nil { + return x.StorageVersion + } + return "" } -func (*DimensionCount) ProtoMessage() {} - -func (x *DimensionCount) ProtoReflect() protoreflect.Message { - mi := &file_api_v1_common_proto_msgTypes[144] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms +func (x *CustomResourceDefinitionDetails) GetStoredVersions() []string { + if x != nil { + return x.StoredVersions } - return mi.MessageOf(x) + return nil } -// Deprecated: Use DimensionCount.ProtoReflect.Descriptor instead. -func (*DimensionCount) Descriptor() ([]byte, []int) { - return file_api_v1_common_proto_rawDescGZIP(), []int{144} +func (x *CustomResourceDefinitionDetails) GetEstablished() bool { + if x != nil { + return x.Established + } + return false } -func (x *DimensionCount) GetValue() string { +func (x *CustomResourceDefinitionDetails) GetConversionStrategy() string { if x != nil { - return x.Value + return x.ConversionStrategy } return "" } -func (x *DimensionCount) GetCount() int32 { +func (x *CustomResourceDefinitionDetails) GetSpecJson() string { if x != nil { - return x.Count + return x.SpecJson } - return 0 + return "" } -func (x *DimensionCount) GetPercentage() float64 { +func (x *CustomResourceDefinitionDetails) GetStatusJson() string { if x != nil { - return x.Percentage + return x.StatusJson } - return 0 + return "" } -type EventGroup struct { +// FluxGitRepositoryDetails contains detailed FluxCD GitRepository information for +// drill-down (source.toolkit.fluxcd.io/v1). +type FluxGitRepositoryDetails struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Reason string `protobuf:"bytes,1,opt,name=reason,proto3" json:"reason,omitempty"` - Namespace string `protobuf:"bytes,2,opt,name=namespace,proto3" json:"namespace,omitempty"` - WorkloadName string `protobuf:"bytes,3,opt,name=workload_name,json=workloadName,proto3" json:"workload_name,omitempty"` - WorkloadKind string `protobuf:"bytes,4,opt,name=workload_kind,json=workloadKind,proto3" json:"workload_kind,omitempty"` - Node string `protobuf:"bytes,5,opt,name=node,proto3" json:"node,omitempty"` - Count int32 `protobuf:"varint,6,opt,name=count,proto3" json:"count,omitempty"` - AffectedObjects int32 `protobuf:"varint,7,opt,name=affected_objects,json=affectedObjects,proto3" json:"affected_objects,omitempty"` - FirstSeen *timestamppb.Timestamp `protobuf:"bytes,8,opt,name=first_seen,json=firstSeen,proto3" json:"first_seen,omitempty"` - LastSeen *timestamppb.Timestamp `protobuf:"bytes,9,opt,name=last_seen,json=lastSeen,proto3" json:"last_seen,omitempty"` - Type string `protobuf:"bytes,10,opt,name=type,proto3" json:"type,omitempty"` -} - -func (x *EventGroup) Reset() { - *x = EventGroup{} + Url string `protobuf:"bytes,1,opt,name=url,proto3" json:"url,omitempty"` // spec.url + GitRef string `protobuf:"bytes,2,opt,name=git_ref,json=gitRef,proto3" json:"git_ref,omitempty"` // spec.ref, resolved to a single display string (branch/tag/semver/commit/name) + IntervalSeconds int64 `protobuf:"varint,3,opt,name=interval_seconds,json=intervalSeconds,proto3" json:"interval_seconds,omitempty"` // spec.interval, parsed to seconds + Suspended bool `protobuf:"varint,4,opt,name=suspended,proto3" json:"suspended,omitempty"` // spec.suspend + Ready bool `protobuf:"varint,5,opt,name=ready,proto3" json:"ready,omitempty"` // status Ready condition status == True + ReadyReason string `protobuf:"bytes,6,opt,name=ready_reason,json=readyReason,proto3" json:"ready_reason,omitempty"` // status Ready condition reason + ReadyMessage string `protobuf:"bytes,7,opt,name=ready_message,json=readyMessage,proto3" json:"ready_message,omitempty"` // status Ready condition message + ObservedGeneration int64 `protobuf:"varint,8,opt,name=observed_generation,json=observedGeneration,proto3" json:"observed_generation,omitempty"` // status.observedGeneration + ArtifactRevision string `protobuf:"bytes,9,opt,name=artifact_revision,json=artifactRevision,proto3" json:"artifact_revision,omitempty"` // status.artifact.revision + ArtifactDigest string `protobuf:"bytes,10,opt,name=artifact_digest,json=artifactDigest,proto3" json:"artifact_digest,omitempty"` // status.artifact.digest + ArtifactLastUpdateTime *timestamppb.Timestamp `protobuf:"bytes,11,opt,name=artifact_last_update_time,json=artifactLastUpdateTime,proto3" json:"artifact_last_update_time,omitempty"` // status.artifact.lastUpdateTime + ArtifactSize int64 `protobuf:"varint,12,opt,name=artifact_size,json=artifactSize,proto3" json:"artifact_size,omitempty"` // status.artifact.size, bytes + ArtifactPath string `protobuf:"bytes,13,opt,name=artifact_path,json=artifactPath,proto3" json:"artifact_path,omitempty"` // status.artifact.path + ArtifactUrl string `protobuf:"bytes,14,opt,name=artifact_url,json=artifactUrl,proto3" json:"artifact_url,omitempty"` // status.artifact.url + SpecJson string `protobuf:"bytes,15,opt,name=spec_json,json=specJson,proto3" json:"spec_json,omitempty"` // Full spec, verbatim as ingested + StatusJson string `protobuf:"bytes,16,opt,name=status_json,json=statusJson,proto3" json:"status_json,omitempty"` // Full status, verbatim as ingested +} + +func (x *FluxGitRepositoryDetails) Reset() { + *x = FluxGitRepositoryDetails{} if protoimpl.UnsafeEnabled { - mi := &file_api_v1_common_proto_msgTypes[145] + mi := &file_api_v1_common_proto_msgTypes[146] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } -func (x *EventGroup) String() string { +func (x *FluxGitRepositoryDetails) String() string { return protoimpl.X.MessageStringOf(x) } -func (*EventGroup) ProtoMessage() {} +func (*FluxGitRepositoryDetails) ProtoMessage() {} -func (x *EventGroup) ProtoReflect() protoreflect.Message { - mi := &file_api_v1_common_proto_msgTypes[145] +func (x *FluxGitRepositoryDetails) ProtoReflect() protoreflect.Message { + mi := &file_api_v1_common_proto_msgTypes[146] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -15634,84 +16868,3282 @@ func (x *EventGroup) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use EventGroup.ProtoReflect.Descriptor instead. -func (*EventGroup) Descriptor() ([]byte, []int) { - return file_api_v1_common_proto_rawDescGZIP(), []int{145} +// Deprecated: Use FluxGitRepositoryDetails.ProtoReflect.Descriptor instead. +func (*FluxGitRepositoryDetails) Descriptor() ([]byte, []int) { + return file_api_v1_common_proto_rawDescGZIP(), []int{146} } -func (x *EventGroup) GetReason() string { +func (x *FluxGitRepositoryDetails) GetUrl() string { if x != nil { - return x.Reason + return x.Url } return "" } -func (x *EventGroup) GetNamespace() string { +func (x *FluxGitRepositoryDetails) GetGitRef() string { if x != nil { - return x.Namespace + return x.GitRef } return "" } -func (x *EventGroup) GetWorkloadName() string { +func (x *FluxGitRepositoryDetails) GetIntervalSeconds() int64 { if x != nil { - return x.WorkloadName + return x.IntervalSeconds } - return "" + return 0 } -func (x *EventGroup) GetWorkloadKind() string { +func (x *FluxGitRepositoryDetails) GetSuspended() bool { if x != nil { - return x.WorkloadKind + return x.Suspended + } + return false +} + +func (x *FluxGitRepositoryDetails) GetReady() bool { + if x != nil { + return x.Ready + } + return false +} + +func (x *FluxGitRepositoryDetails) GetReadyReason() string { + if x != nil { + return x.ReadyReason } return "" } -func (x *EventGroup) GetNode() string { +func (x *FluxGitRepositoryDetails) GetReadyMessage() string { if x != nil { - return x.Node + return x.ReadyMessage } return "" } -func (x *EventGroup) GetCount() int32 { +func (x *FluxGitRepositoryDetails) GetObservedGeneration() int64 { if x != nil { - return x.Count + return x.ObservedGeneration } return 0 } -func (x *EventGroup) GetAffectedObjects() int32 { +func (x *FluxGitRepositoryDetails) GetArtifactRevision() string { if x != nil { - return x.AffectedObjects + return x.ArtifactRevision } - return 0 + return "" } -func (x *EventGroup) GetFirstSeen() *timestamppb.Timestamp { +func (x *FluxGitRepositoryDetails) GetArtifactDigest() string { if x != nil { - return x.FirstSeen + return x.ArtifactDigest } - return nil + return "" } -func (x *EventGroup) GetLastSeen() *timestamppb.Timestamp { +func (x *FluxGitRepositoryDetails) GetArtifactLastUpdateTime() *timestamppb.Timestamp { if x != nil { - return x.LastSeen + return x.ArtifactLastUpdateTime } return nil } -func (x *EventGroup) GetType() string { +func (x *FluxGitRepositoryDetails) GetArtifactSize() int64 { if x != nil { - return x.Type + return x.ArtifactSize } - return "" + return 0 } -var File_api_v1_common_proto protoreflect.FileDescriptor +func (x *FluxGitRepositoryDetails) GetArtifactPath() string { + if x != nil { + return x.ArtifactPath + } + return "" +} -var file_api_v1_common_proto_rawDesc = []byte{ +func (x *FluxGitRepositoryDetails) GetArtifactUrl() string { + if x != nil { + return x.ArtifactUrl + } + return "" +} + +func (x *FluxGitRepositoryDetails) GetSpecJson() string { + if x != nil { + return x.SpecJson + } + return "" +} + +func (x *FluxGitRepositoryDetails) GetStatusJson() string { + if x != nil { + return x.StatusJson + } + return "" +} + +// FluxOCIRepositoryDetails contains detailed FluxCD OCIRepository information for +// drill-down (source.toolkit.fluxcd.io/v1). +type FluxOCIRepositoryDetails struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Url string `protobuf:"bytes,1,opt,name=url,proto3" json:"url,omitempty"` // spec.url (oci://...) + OciRef string `protobuf:"bytes,2,opt,name=oci_ref,json=ociRef,proto3" json:"oci_ref,omitempty"` // spec.ref, resolved to a single display string (tag/semver/digest) + Provider string `protobuf:"bytes,3,opt,name=provider,proto3" json:"provider,omitempty"` // spec.provider: generic, aws, azure or gcp + IntervalSeconds int64 `protobuf:"varint,4,opt,name=interval_seconds,json=intervalSeconds,proto3" json:"interval_seconds,omitempty"` // spec.interval, parsed to seconds + Suspended bool `protobuf:"varint,5,opt,name=suspended,proto3" json:"suspended,omitempty"` // spec.suspend + Ready bool `protobuf:"varint,6,opt,name=ready,proto3" json:"ready,omitempty"` // status Ready condition status == True + ReadyReason string `protobuf:"bytes,7,opt,name=ready_reason,json=readyReason,proto3" json:"ready_reason,omitempty"` // status Ready condition reason + ReadyMessage string `protobuf:"bytes,8,opt,name=ready_message,json=readyMessage,proto3" json:"ready_message,omitempty"` // status Ready condition message + ObservedGeneration int64 `protobuf:"varint,9,opt,name=observed_generation,json=observedGeneration,proto3" json:"observed_generation,omitempty"` // status.observedGeneration + ArtifactRevision string `protobuf:"bytes,10,opt,name=artifact_revision,json=artifactRevision,proto3" json:"artifact_revision,omitempty"` // status.artifact.revision + ArtifactDigest string `protobuf:"bytes,11,opt,name=artifact_digest,json=artifactDigest,proto3" json:"artifact_digest,omitempty"` // status.artifact.digest + ArtifactLastUpdateTime *timestamppb.Timestamp `protobuf:"bytes,12,opt,name=artifact_last_update_time,json=artifactLastUpdateTime,proto3" json:"artifact_last_update_time,omitempty"` // status.artifact.lastUpdateTime + ArtifactSize int64 `protobuf:"varint,13,opt,name=artifact_size,json=artifactSize,proto3" json:"artifact_size,omitempty"` // status.artifact.size, bytes + ArtifactPath string `protobuf:"bytes,14,opt,name=artifact_path,json=artifactPath,proto3" json:"artifact_path,omitempty"` // status.artifact.path + ArtifactUrl string `protobuf:"bytes,15,opt,name=artifact_url,json=artifactUrl,proto3" json:"artifact_url,omitempty"` // status.artifact.url + SpecJson string `protobuf:"bytes,16,opt,name=spec_json,json=specJson,proto3" json:"spec_json,omitempty"` // Full spec, verbatim as ingested + StatusJson string `protobuf:"bytes,17,opt,name=status_json,json=statusJson,proto3" json:"status_json,omitempty"` // Full status, verbatim as ingested +} + +func (x *FluxOCIRepositoryDetails) Reset() { + *x = FluxOCIRepositoryDetails{} + if protoimpl.UnsafeEnabled { + mi := &file_api_v1_common_proto_msgTypes[147] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *FluxOCIRepositoryDetails) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*FluxOCIRepositoryDetails) ProtoMessage() {} + +func (x *FluxOCIRepositoryDetails) ProtoReflect() protoreflect.Message { + mi := &file_api_v1_common_proto_msgTypes[147] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use FluxOCIRepositoryDetails.ProtoReflect.Descriptor instead. +func (*FluxOCIRepositoryDetails) Descriptor() ([]byte, []int) { + return file_api_v1_common_proto_rawDescGZIP(), []int{147} +} + +func (x *FluxOCIRepositoryDetails) GetUrl() string { + if x != nil { + return x.Url + } + return "" +} + +func (x *FluxOCIRepositoryDetails) GetOciRef() string { + if x != nil { + return x.OciRef + } + return "" +} + +func (x *FluxOCIRepositoryDetails) GetProvider() string { + if x != nil { + return x.Provider + } + return "" +} + +func (x *FluxOCIRepositoryDetails) GetIntervalSeconds() int64 { + if x != nil { + return x.IntervalSeconds + } + return 0 +} + +func (x *FluxOCIRepositoryDetails) GetSuspended() bool { + if x != nil { + return x.Suspended + } + return false +} + +func (x *FluxOCIRepositoryDetails) GetReady() bool { + if x != nil { + return x.Ready + } + return false +} + +func (x *FluxOCIRepositoryDetails) GetReadyReason() string { + if x != nil { + return x.ReadyReason + } + return "" +} + +func (x *FluxOCIRepositoryDetails) GetReadyMessage() string { + if x != nil { + return x.ReadyMessage + } + return "" +} + +func (x *FluxOCIRepositoryDetails) GetObservedGeneration() int64 { + if x != nil { + return x.ObservedGeneration + } + return 0 +} + +func (x *FluxOCIRepositoryDetails) GetArtifactRevision() string { + if x != nil { + return x.ArtifactRevision + } + return "" +} + +func (x *FluxOCIRepositoryDetails) GetArtifactDigest() string { + if x != nil { + return x.ArtifactDigest + } + return "" +} + +func (x *FluxOCIRepositoryDetails) GetArtifactLastUpdateTime() *timestamppb.Timestamp { + if x != nil { + return x.ArtifactLastUpdateTime + } + return nil +} + +func (x *FluxOCIRepositoryDetails) GetArtifactSize() int64 { + if x != nil { + return x.ArtifactSize + } + return 0 +} + +func (x *FluxOCIRepositoryDetails) GetArtifactPath() string { + if x != nil { + return x.ArtifactPath + } + return "" +} + +func (x *FluxOCIRepositoryDetails) GetArtifactUrl() string { + if x != nil { + return x.ArtifactUrl + } + return "" +} + +func (x *FluxOCIRepositoryDetails) GetSpecJson() string { + if x != nil { + return x.SpecJson + } + return "" +} + +func (x *FluxOCIRepositoryDetails) GetStatusJson() string { + if x != nil { + return x.StatusJson + } + return "" +} + +// FluxHelmRepositoryDetails contains detailed FluxCD HelmRepository information for +// drill-down (source.toolkit.fluxcd.io/v1). +type FluxHelmRepositoryDetails struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Url string `protobuf:"bytes,1,opt,name=url,proto3" json:"url,omitempty"` // spec.url + RepoType string `protobuf:"bytes,2,opt,name=repo_type,json=repoType,proto3" json:"repo_type,omitempty"` // spec.type: default or oci + Provider string `protobuf:"bytes,3,opt,name=provider,proto3" json:"provider,omitempty"` // spec.provider: generic, aws, azure or gcp + IntervalSeconds int64 `protobuf:"varint,4,opt,name=interval_seconds,json=intervalSeconds,proto3" json:"interval_seconds,omitempty"` // spec.interval, parsed to seconds + Suspended bool `protobuf:"varint,5,opt,name=suspended,proto3" json:"suspended,omitempty"` // spec.suspend + Ready bool `protobuf:"varint,6,opt,name=ready,proto3" json:"ready,omitempty"` // status Ready condition status == True + ReadyReason string `protobuf:"bytes,7,opt,name=ready_reason,json=readyReason,proto3" json:"ready_reason,omitempty"` // status Ready condition reason + ReadyMessage string `protobuf:"bytes,8,opt,name=ready_message,json=readyMessage,proto3" json:"ready_message,omitempty"` // status Ready condition message + ObservedGeneration int64 `protobuf:"varint,9,opt,name=observed_generation,json=observedGeneration,proto3" json:"observed_generation,omitempty"` // status.observedGeneration + ArtifactRevision string `protobuf:"bytes,10,opt,name=artifact_revision,json=artifactRevision,proto3" json:"artifact_revision,omitempty"` // status.artifact.revision + ArtifactDigest string `protobuf:"bytes,11,opt,name=artifact_digest,json=artifactDigest,proto3" json:"artifact_digest,omitempty"` // status.artifact.digest + ArtifactLastUpdateTime *timestamppb.Timestamp `protobuf:"bytes,12,opt,name=artifact_last_update_time,json=artifactLastUpdateTime,proto3" json:"artifact_last_update_time,omitempty"` // status.artifact.lastUpdateTime + ArtifactSize int64 `protobuf:"varint,13,opt,name=artifact_size,json=artifactSize,proto3" json:"artifact_size,omitempty"` // status.artifact.size, bytes + ArtifactPath string `protobuf:"bytes,14,opt,name=artifact_path,json=artifactPath,proto3" json:"artifact_path,omitempty"` // status.artifact.path + ArtifactUrl string `protobuf:"bytes,15,opt,name=artifact_url,json=artifactUrl,proto3" json:"artifact_url,omitempty"` // status.artifact.url + SpecJson string `protobuf:"bytes,16,opt,name=spec_json,json=specJson,proto3" json:"spec_json,omitempty"` // Full spec, verbatim as ingested + StatusJson string `protobuf:"bytes,17,opt,name=status_json,json=statusJson,proto3" json:"status_json,omitempty"` // Full status, verbatim as ingested +} + +func (x *FluxHelmRepositoryDetails) Reset() { + *x = FluxHelmRepositoryDetails{} + if protoimpl.UnsafeEnabled { + mi := &file_api_v1_common_proto_msgTypes[148] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *FluxHelmRepositoryDetails) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*FluxHelmRepositoryDetails) ProtoMessage() {} + +func (x *FluxHelmRepositoryDetails) ProtoReflect() protoreflect.Message { + mi := &file_api_v1_common_proto_msgTypes[148] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use FluxHelmRepositoryDetails.ProtoReflect.Descriptor instead. +func (*FluxHelmRepositoryDetails) Descriptor() ([]byte, []int) { + return file_api_v1_common_proto_rawDescGZIP(), []int{148} +} + +func (x *FluxHelmRepositoryDetails) GetUrl() string { + if x != nil { + return x.Url + } + return "" +} + +func (x *FluxHelmRepositoryDetails) GetRepoType() string { + if x != nil { + return x.RepoType + } + return "" +} + +func (x *FluxHelmRepositoryDetails) GetProvider() string { + if x != nil { + return x.Provider + } + return "" +} + +func (x *FluxHelmRepositoryDetails) GetIntervalSeconds() int64 { + if x != nil { + return x.IntervalSeconds + } + return 0 +} + +func (x *FluxHelmRepositoryDetails) GetSuspended() bool { + if x != nil { + return x.Suspended + } + return false +} + +func (x *FluxHelmRepositoryDetails) GetReady() bool { + if x != nil { + return x.Ready + } + return false +} + +func (x *FluxHelmRepositoryDetails) GetReadyReason() string { + if x != nil { + return x.ReadyReason + } + return "" +} + +func (x *FluxHelmRepositoryDetails) GetReadyMessage() string { + if x != nil { + return x.ReadyMessage + } + return "" +} + +func (x *FluxHelmRepositoryDetails) GetObservedGeneration() int64 { + if x != nil { + return x.ObservedGeneration + } + return 0 +} + +func (x *FluxHelmRepositoryDetails) GetArtifactRevision() string { + if x != nil { + return x.ArtifactRevision + } + return "" +} + +func (x *FluxHelmRepositoryDetails) GetArtifactDigest() string { + if x != nil { + return x.ArtifactDigest + } + return "" +} + +func (x *FluxHelmRepositoryDetails) GetArtifactLastUpdateTime() *timestamppb.Timestamp { + if x != nil { + return x.ArtifactLastUpdateTime + } + return nil +} + +func (x *FluxHelmRepositoryDetails) GetArtifactSize() int64 { + if x != nil { + return x.ArtifactSize + } + return 0 +} + +func (x *FluxHelmRepositoryDetails) GetArtifactPath() string { + if x != nil { + return x.ArtifactPath + } + return "" +} + +func (x *FluxHelmRepositoryDetails) GetArtifactUrl() string { + if x != nil { + return x.ArtifactUrl + } + return "" +} + +func (x *FluxHelmRepositoryDetails) GetSpecJson() string { + if x != nil { + return x.SpecJson + } + return "" +} + +func (x *FluxHelmRepositoryDetails) GetStatusJson() string { + if x != nil { + return x.StatusJson + } + return "" +} + +// FluxHelmChartDetails contains detailed FluxCD HelmChart information for drill-down +// (source.toolkit.fluxcd.io/v1). HelmChart is itself a generated source, usually +// created by a HelmRelease rather than authored directly. +type FluxHelmChartDetails struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + ChartName string `protobuf:"bytes,1,opt,name=chart_name,json=chartName,proto3" json:"chart_name,omitempty"` // spec.chart + ChartVersion string `protobuf:"bytes,2,opt,name=chart_version,json=chartVersion,proto3" json:"chart_version,omitempty"` // spec.version + SourceRefKind string `protobuf:"bytes,3,opt,name=source_ref_kind,json=sourceRefKind,proto3" json:"source_ref_kind,omitempty"` // spec.sourceRef.kind: HelmRepository, GitRepository or Bucket + SourceRefName string `protobuf:"bytes,4,opt,name=source_ref_name,json=sourceRefName,proto3" json:"source_ref_name,omitempty"` // spec.sourceRef.name + SourceRefNamespace string `protobuf:"bytes,5,opt,name=source_ref_namespace,json=sourceRefNamespace,proto3" json:"source_ref_namespace,omitempty"` // spec.sourceRef.namespace + IntervalSeconds int64 `protobuf:"varint,6,opt,name=interval_seconds,json=intervalSeconds,proto3" json:"interval_seconds,omitempty"` // spec.interval, parsed to seconds + Suspended bool `protobuf:"varint,7,opt,name=suspended,proto3" json:"suspended,omitempty"` // spec.suspend + Ready bool `protobuf:"varint,8,opt,name=ready,proto3" json:"ready,omitempty"` // status Ready condition status == True + ReadyReason string `protobuf:"bytes,9,opt,name=ready_reason,json=readyReason,proto3" json:"ready_reason,omitempty"` // status Ready condition reason + ReadyMessage string `protobuf:"bytes,10,opt,name=ready_message,json=readyMessage,proto3" json:"ready_message,omitempty"` // status Ready condition message + ObservedGeneration int64 `protobuf:"varint,11,opt,name=observed_generation,json=observedGeneration,proto3" json:"observed_generation,omitempty"` // status.observedGeneration + ArtifactRevision string `protobuf:"bytes,12,opt,name=artifact_revision,json=artifactRevision,proto3" json:"artifact_revision,omitempty"` // status.artifact.revision + ArtifactDigest string `protobuf:"bytes,13,opt,name=artifact_digest,json=artifactDigest,proto3" json:"artifact_digest,omitempty"` // status.artifact.digest + ArtifactLastUpdateTime *timestamppb.Timestamp `protobuf:"bytes,14,opt,name=artifact_last_update_time,json=artifactLastUpdateTime,proto3" json:"artifact_last_update_time,omitempty"` // status.artifact.lastUpdateTime + ArtifactSize int64 `protobuf:"varint,15,opt,name=artifact_size,json=artifactSize,proto3" json:"artifact_size,omitempty"` // status.artifact.size, bytes + ArtifactPath string `protobuf:"bytes,16,opt,name=artifact_path,json=artifactPath,proto3" json:"artifact_path,omitempty"` // status.artifact.path + ArtifactUrl string `protobuf:"bytes,17,opt,name=artifact_url,json=artifactUrl,proto3" json:"artifact_url,omitempty"` // status.artifact.url + SpecJson string `protobuf:"bytes,18,opt,name=spec_json,json=specJson,proto3" json:"spec_json,omitempty"` // Full spec, verbatim as ingested + StatusJson string `protobuf:"bytes,19,opt,name=status_json,json=statusJson,proto3" json:"status_json,omitempty"` // Full status, verbatim as ingested +} + +func (x *FluxHelmChartDetails) Reset() { + *x = FluxHelmChartDetails{} + if protoimpl.UnsafeEnabled { + mi := &file_api_v1_common_proto_msgTypes[149] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *FluxHelmChartDetails) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*FluxHelmChartDetails) ProtoMessage() {} + +func (x *FluxHelmChartDetails) ProtoReflect() protoreflect.Message { + mi := &file_api_v1_common_proto_msgTypes[149] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use FluxHelmChartDetails.ProtoReflect.Descriptor instead. +func (*FluxHelmChartDetails) Descriptor() ([]byte, []int) { + return file_api_v1_common_proto_rawDescGZIP(), []int{149} +} + +func (x *FluxHelmChartDetails) GetChartName() string { + if x != nil { + return x.ChartName + } + return "" +} + +func (x *FluxHelmChartDetails) GetChartVersion() string { + if x != nil { + return x.ChartVersion + } + return "" +} + +func (x *FluxHelmChartDetails) GetSourceRefKind() string { + if x != nil { + return x.SourceRefKind + } + return "" +} + +func (x *FluxHelmChartDetails) GetSourceRefName() string { + if x != nil { + return x.SourceRefName + } + return "" +} + +func (x *FluxHelmChartDetails) GetSourceRefNamespace() string { + if x != nil { + return x.SourceRefNamespace + } + return "" +} + +func (x *FluxHelmChartDetails) GetIntervalSeconds() int64 { + if x != nil { + return x.IntervalSeconds + } + return 0 +} + +func (x *FluxHelmChartDetails) GetSuspended() bool { + if x != nil { + return x.Suspended + } + return false +} + +func (x *FluxHelmChartDetails) GetReady() bool { + if x != nil { + return x.Ready + } + return false +} + +func (x *FluxHelmChartDetails) GetReadyReason() string { + if x != nil { + return x.ReadyReason + } + return "" +} + +func (x *FluxHelmChartDetails) GetReadyMessage() string { + if x != nil { + return x.ReadyMessage + } + return "" +} + +func (x *FluxHelmChartDetails) GetObservedGeneration() int64 { + if x != nil { + return x.ObservedGeneration + } + return 0 +} + +func (x *FluxHelmChartDetails) GetArtifactRevision() string { + if x != nil { + return x.ArtifactRevision + } + return "" +} + +func (x *FluxHelmChartDetails) GetArtifactDigest() string { + if x != nil { + return x.ArtifactDigest + } + return "" +} + +func (x *FluxHelmChartDetails) GetArtifactLastUpdateTime() *timestamppb.Timestamp { + if x != nil { + return x.ArtifactLastUpdateTime + } + return nil +} + +func (x *FluxHelmChartDetails) GetArtifactSize() int64 { + if x != nil { + return x.ArtifactSize + } + return 0 +} + +func (x *FluxHelmChartDetails) GetArtifactPath() string { + if x != nil { + return x.ArtifactPath + } + return "" +} + +func (x *FluxHelmChartDetails) GetArtifactUrl() string { + if x != nil { + return x.ArtifactUrl + } + return "" +} + +func (x *FluxHelmChartDetails) GetSpecJson() string { + if x != nil { + return x.SpecJson + } + return "" +} + +func (x *FluxHelmChartDetails) GetStatusJson() string { + if x != nil { + return x.StatusJson + } + return "" +} + +// FluxBucketDetails contains detailed FluxCD Bucket information for drill-down +// (source.toolkit.fluxcd.io/v1). +type FluxBucketDetails struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + BucketName string `protobuf:"bytes,1,opt,name=bucket_name,json=bucketName,proto3" json:"bucket_name,omitempty"` // spec.bucketName + Provider string `protobuf:"bytes,2,opt,name=provider,proto3" json:"provider,omitempty"` // spec.provider: generic, aws, gcp or azure + Region string `protobuf:"bytes,3,opt,name=region,proto3" json:"region,omitempty"` // spec.region + Endpoint string `protobuf:"bytes,4,opt,name=endpoint,proto3" json:"endpoint,omitempty"` // spec.endpoint + IntervalSeconds int64 `protobuf:"varint,5,opt,name=interval_seconds,json=intervalSeconds,proto3" json:"interval_seconds,omitempty"` // spec.interval, parsed to seconds + Suspended bool `protobuf:"varint,6,opt,name=suspended,proto3" json:"suspended,omitempty"` // spec.suspend + Ready bool `protobuf:"varint,7,opt,name=ready,proto3" json:"ready,omitempty"` // status Ready condition status == True + ReadyReason string `protobuf:"bytes,8,opt,name=ready_reason,json=readyReason,proto3" json:"ready_reason,omitempty"` // status Ready condition reason + ReadyMessage string `protobuf:"bytes,9,opt,name=ready_message,json=readyMessage,proto3" json:"ready_message,omitempty"` // status Ready condition message + ObservedGeneration int64 `protobuf:"varint,10,opt,name=observed_generation,json=observedGeneration,proto3" json:"observed_generation,omitempty"` // status.observedGeneration + ArtifactRevision string `protobuf:"bytes,11,opt,name=artifact_revision,json=artifactRevision,proto3" json:"artifact_revision,omitempty"` // status.artifact.revision + ArtifactDigest string `protobuf:"bytes,12,opt,name=artifact_digest,json=artifactDigest,proto3" json:"artifact_digest,omitempty"` // status.artifact.digest + ArtifactLastUpdateTime *timestamppb.Timestamp `protobuf:"bytes,13,opt,name=artifact_last_update_time,json=artifactLastUpdateTime,proto3" json:"artifact_last_update_time,omitempty"` // status.artifact.lastUpdateTime + ArtifactSize int64 `protobuf:"varint,14,opt,name=artifact_size,json=artifactSize,proto3" json:"artifact_size,omitempty"` // status.artifact.size, bytes + ArtifactPath string `protobuf:"bytes,15,opt,name=artifact_path,json=artifactPath,proto3" json:"artifact_path,omitempty"` // status.artifact.path + ArtifactUrl string `protobuf:"bytes,16,opt,name=artifact_url,json=artifactUrl,proto3" json:"artifact_url,omitempty"` // status.artifact.url + SpecJson string `protobuf:"bytes,17,opt,name=spec_json,json=specJson,proto3" json:"spec_json,omitempty"` // Full spec, verbatim as ingested + StatusJson string `protobuf:"bytes,18,opt,name=status_json,json=statusJson,proto3" json:"status_json,omitempty"` // Full status, verbatim as ingested +} + +func (x *FluxBucketDetails) Reset() { + *x = FluxBucketDetails{} + if protoimpl.UnsafeEnabled { + mi := &file_api_v1_common_proto_msgTypes[150] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *FluxBucketDetails) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*FluxBucketDetails) ProtoMessage() {} + +func (x *FluxBucketDetails) ProtoReflect() protoreflect.Message { + mi := &file_api_v1_common_proto_msgTypes[150] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use FluxBucketDetails.ProtoReflect.Descriptor instead. +func (*FluxBucketDetails) Descriptor() ([]byte, []int) { + return file_api_v1_common_proto_rawDescGZIP(), []int{150} +} + +func (x *FluxBucketDetails) GetBucketName() string { + if x != nil { + return x.BucketName + } + return "" +} + +func (x *FluxBucketDetails) GetProvider() string { + if x != nil { + return x.Provider + } + return "" +} + +func (x *FluxBucketDetails) GetRegion() string { + if x != nil { + return x.Region + } + return "" +} + +func (x *FluxBucketDetails) GetEndpoint() string { + if x != nil { + return x.Endpoint + } + return "" +} + +func (x *FluxBucketDetails) GetIntervalSeconds() int64 { + if x != nil { + return x.IntervalSeconds + } + return 0 +} + +func (x *FluxBucketDetails) GetSuspended() bool { + if x != nil { + return x.Suspended + } + return false +} + +func (x *FluxBucketDetails) GetReady() bool { + if x != nil { + return x.Ready + } + return false +} + +func (x *FluxBucketDetails) GetReadyReason() string { + if x != nil { + return x.ReadyReason + } + return "" +} + +func (x *FluxBucketDetails) GetReadyMessage() string { + if x != nil { + return x.ReadyMessage + } + return "" +} + +func (x *FluxBucketDetails) GetObservedGeneration() int64 { + if x != nil { + return x.ObservedGeneration + } + return 0 +} + +func (x *FluxBucketDetails) GetArtifactRevision() string { + if x != nil { + return x.ArtifactRevision + } + return "" +} + +func (x *FluxBucketDetails) GetArtifactDigest() string { + if x != nil { + return x.ArtifactDigest + } + return "" +} + +func (x *FluxBucketDetails) GetArtifactLastUpdateTime() *timestamppb.Timestamp { + if x != nil { + return x.ArtifactLastUpdateTime + } + return nil +} + +func (x *FluxBucketDetails) GetArtifactSize() int64 { + if x != nil { + return x.ArtifactSize + } + return 0 +} + +func (x *FluxBucketDetails) GetArtifactPath() string { + if x != nil { + return x.ArtifactPath + } + return "" +} + +func (x *FluxBucketDetails) GetArtifactUrl() string { + if x != nil { + return x.ArtifactUrl + } + return "" +} + +func (x *FluxBucketDetails) GetSpecJson() string { + if x != nil { + return x.SpecJson + } + return "" +} + +func (x *FluxBucketDetails) GetStatusJson() string { + if x != nil { + return x.StatusJson + } + return "" +} + +// FluxKustomizationDetails contains detailed FluxCD Kustomization information for +// drill-down (kustomize.toolkit.fluxcd.io/v1). +type FluxKustomizationDetails struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + SourceRefKind string `protobuf:"bytes,1,opt,name=source_ref_kind,json=sourceRefKind,proto3" json:"source_ref_kind,omitempty"` // spec.sourceRef.kind: GitRepository, OCIRepository or Bucket + SourceRefName string `protobuf:"bytes,2,opt,name=source_ref_name,json=sourceRefName,proto3" json:"source_ref_name,omitempty"` // spec.sourceRef.name + SourceRefNamespace string `protobuf:"bytes,3,opt,name=source_ref_namespace,json=sourceRefNamespace,proto3" json:"source_ref_namespace,omitempty"` // spec.sourceRef.namespace + Path string `protobuf:"bytes,4,opt,name=path,proto3" json:"path,omitempty"` // spec.path + Prune bool `protobuf:"varint,5,opt,name=prune,proto3" json:"prune,omitempty"` // spec.prune + Force bool `protobuf:"varint,6,opt,name=force,proto3" json:"force,omitempty"` // spec.force + Wait bool `protobuf:"varint,7,opt,name=wait,proto3" json:"wait,omitempty"` // spec.wait + IntervalSeconds int64 `protobuf:"varint,8,opt,name=interval_seconds,json=intervalSeconds,proto3" json:"interval_seconds,omitempty"` // spec.interval, parsed to seconds + TimeoutSeconds int64 `protobuf:"varint,9,opt,name=timeout_seconds,json=timeoutSeconds,proto3" json:"timeout_seconds,omitempty"` // spec.timeout, parsed to seconds + Suspended bool `protobuf:"varint,10,opt,name=suspended,proto3" json:"suspended,omitempty"` // spec.suspend + Ready bool `protobuf:"varint,11,opt,name=ready,proto3" json:"ready,omitempty"` // status Ready condition status == True + ReadyReason string `protobuf:"bytes,12,opt,name=ready_reason,json=readyReason,proto3" json:"ready_reason,omitempty"` // status Ready condition reason + ReadyMessage string `protobuf:"bytes,13,opt,name=ready_message,json=readyMessage,proto3" json:"ready_message,omitempty"` // status Ready condition message + Healthy bool `protobuf:"varint,14,opt,name=healthy,proto3" json:"healthy,omitempty"` // status Healthy condition status == True (condition absent when spec.wait is false) + HealthyReason string `protobuf:"bytes,15,opt,name=healthy_reason,json=healthyReason,proto3" json:"healthy_reason,omitempty"` // status Healthy condition reason + LastAppliedRevision string `protobuf:"bytes,16,opt,name=last_applied_revision,json=lastAppliedRevision,proto3" json:"last_applied_revision,omitempty"` // status.lastAppliedRevision + LastAttemptedRevision string `protobuf:"bytes,17,opt,name=last_attempted_revision,json=lastAttemptedRevision,proto3" json:"last_attempted_revision,omitempty"` // status.lastAttemptedRevision + InventoryEntryCount int32 `protobuf:"varint,18,opt,name=inventory_entry_count,json=inventoryEntryCount,proto3" json:"inventory_entry_count,omitempty"` // len(status.inventory.entries) + ObservedGeneration int64 `protobuf:"varint,19,opt,name=observed_generation,json=observedGeneration,proto3" json:"observed_generation,omitempty"` // status.observedGeneration + SpecJson string `protobuf:"bytes,20,opt,name=spec_json,json=specJson,proto3" json:"spec_json,omitempty"` // Full spec, verbatim as ingested + StatusJson string `protobuf:"bytes,21,opt,name=status_json,json=statusJson,proto3" json:"status_json,omitempty"` // Full status, verbatim as ingested +} + +func (x *FluxKustomizationDetails) Reset() { + *x = FluxKustomizationDetails{} + if protoimpl.UnsafeEnabled { + mi := &file_api_v1_common_proto_msgTypes[151] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *FluxKustomizationDetails) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*FluxKustomizationDetails) ProtoMessage() {} + +func (x *FluxKustomizationDetails) ProtoReflect() protoreflect.Message { + mi := &file_api_v1_common_proto_msgTypes[151] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use FluxKustomizationDetails.ProtoReflect.Descriptor instead. +func (*FluxKustomizationDetails) Descriptor() ([]byte, []int) { + return file_api_v1_common_proto_rawDescGZIP(), []int{151} +} + +func (x *FluxKustomizationDetails) GetSourceRefKind() string { + if x != nil { + return x.SourceRefKind + } + return "" +} + +func (x *FluxKustomizationDetails) GetSourceRefName() string { + if x != nil { + return x.SourceRefName + } + return "" +} + +func (x *FluxKustomizationDetails) GetSourceRefNamespace() string { + if x != nil { + return x.SourceRefNamespace + } + return "" +} + +func (x *FluxKustomizationDetails) GetPath() string { + if x != nil { + return x.Path + } + return "" +} + +func (x *FluxKustomizationDetails) GetPrune() bool { + if x != nil { + return x.Prune + } + return false +} + +func (x *FluxKustomizationDetails) GetForce() bool { + if x != nil { + return x.Force + } + return false +} + +func (x *FluxKustomizationDetails) GetWait() bool { + if x != nil { + return x.Wait + } + return false +} + +func (x *FluxKustomizationDetails) GetIntervalSeconds() int64 { + if x != nil { + return x.IntervalSeconds + } + return 0 +} + +func (x *FluxKustomizationDetails) GetTimeoutSeconds() int64 { + if x != nil { + return x.TimeoutSeconds + } + return 0 +} + +func (x *FluxKustomizationDetails) GetSuspended() bool { + if x != nil { + return x.Suspended + } + return false +} + +func (x *FluxKustomizationDetails) GetReady() bool { + if x != nil { + return x.Ready + } + return false +} + +func (x *FluxKustomizationDetails) GetReadyReason() string { + if x != nil { + return x.ReadyReason + } + return "" +} + +func (x *FluxKustomizationDetails) GetReadyMessage() string { + if x != nil { + return x.ReadyMessage + } + return "" +} + +func (x *FluxKustomizationDetails) GetHealthy() bool { + if x != nil { + return x.Healthy + } + return false +} + +func (x *FluxKustomizationDetails) GetHealthyReason() string { + if x != nil { + return x.HealthyReason + } + return "" +} + +func (x *FluxKustomizationDetails) GetLastAppliedRevision() string { + if x != nil { + return x.LastAppliedRevision + } + return "" +} + +func (x *FluxKustomizationDetails) GetLastAttemptedRevision() string { + if x != nil { + return x.LastAttemptedRevision + } + return "" +} + +func (x *FluxKustomizationDetails) GetInventoryEntryCount() int32 { + if x != nil { + return x.InventoryEntryCount + } + return 0 +} + +func (x *FluxKustomizationDetails) GetObservedGeneration() int64 { + if x != nil { + return x.ObservedGeneration + } + return 0 +} + +func (x *FluxKustomizationDetails) GetSpecJson() string { + if x != nil { + return x.SpecJson + } + return "" +} + +func (x *FluxKustomizationDetails) GetStatusJson() string { + if x != nil { + return x.StatusJson + } + return "" +} + +// FluxHelmReleaseDetails contains detailed FluxCD HelmRelease information for +// drill-down (helm.toolkit.fluxcd.io/v2). +type FluxHelmReleaseDetails struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + ChartRefKind string `protobuf:"bytes,1,opt,name=chart_ref_kind,json=chartRefKind,proto3" json:"chart_ref_kind,omitempty"` // spec.chartRef.kind: HelmChart or OCIRepository, when spec.chartRef is set + ChartRefName string `protobuf:"bytes,2,opt,name=chart_ref_name,json=chartRefName,proto3" json:"chart_ref_name,omitempty"` // spec.chartRef.name + ChartRefNamespace string `protobuf:"bytes,3,opt,name=chart_ref_namespace,json=chartRefNamespace,proto3" json:"chart_ref_namespace,omitempty"` // spec.chartRef.namespace + ChartSourceRefKind string `protobuf:"bytes,4,opt,name=chart_source_ref_kind,json=chartSourceRefKind,proto3" json:"chart_source_ref_kind,omitempty"` // spec.chart.spec.sourceRef.kind, when spec.chart is set instead of spec.chartRef + ChartSourceRefName string `protobuf:"bytes,5,opt,name=chart_source_ref_name,json=chartSourceRefName,proto3" json:"chart_source_ref_name,omitempty"` // spec.chart.spec.sourceRef.name + ChartSourceRefNamespace string `protobuf:"bytes,6,opt,name=chart_source_ref_namespace,json=chartSourceRefNamespace,proto3" json:"chart_source_ref_namespace,omitempty"` // spec.chart.spec.sourceRef.namespace + ChartName string `protobuf:"bytes,7,opt,name=chart_name,json=chartName,proto3" json:"chart_name,omitempty"` // spec.chart.spec.chart + ChartVersion string `protobuf:"bytes,8,opt,name=chart_version,json=chartVersion,proto3" json:"chart_version,omitempty"` // spec.chart.spec.version + ReleaseName string `protobuf:"bytes,9,opt,name=release_name,json=releaseName,proto3" json:"release_name,omitempty"` // spec.releaseName, or the generated name when unset + TargetNamespace string `protobuf:"bytes,10,opt,name=target_namespace,json=targetNamespace,proto3" json:"target_namespace,omitempty"` // spec.targetNamespace + StorageNamespace string `protobuf:"bytes,11,opt,name=storage_namespace,json=storageNamespace,proto3" json:"storage_namespace,omitempty"` // spec.storageNamespace + IntervalSeconds int64 `protobuf:"varint,12,opt,name=interval_seconds,json=intervalSeconds,proto3" json:"interval_seconds,omitempty"` // spec.interval, parsed to seconds + Suspended bool `protobuf:"varint,13,opt,name=suspended,proto3" json:"suspended,omitempty"` // spec.suspend + Ready bool `protobuf:"varint,14,opt,name=ready,proto3" json:"ready,omitempty"` // status Ready condition status == True + ReadyReason string `protobuf:"bytes,15,opt,name=ready_reason,json=readyReason,proto3" json:"ready_reason,omitempty"` // status Ready condition reason + ReadyMessage string `protobuf:"bytes,16,opt,name=ready_message,json=readyMessage,proto3" json:"ready_message,omitempty"` // status Ready condition message + LastAttemptedRevision string `protobuf:"bytes,17,opt,name=last_attempted_revision,json=lastAttemptedRevision,proto3" json:"last_attempted_revision,omitempty"` // status.lastAttemptedRevision + LastAttemptedRevisionDigest string `protobuf:"bytes,18,opt,name=last_attempted_revision_digest,json=lastAttemptedRevisionDigest,proto3" json:"last_attempted_revision_digest,omitempty"` // status.lastAttemptedRevisionDigest + LastReleaseRevision string `protobuf:"bytes,19,opt,name=last_release_revision,json=lastReleaseRevision,proto3" json:"last_release_revision,omitempty"` // status.history[0].chartVersion, the most recent Helm release's chart version + HelmChart string `protobuf:"bytes,20,opt,name=helm_chart,json=helmChart,proto3" json:"helm_chart,omitempty"` // status.helmChart, "namespace/name" of the generated HelmChart source + HistoryCount int32 `protobuf:"varint,21,opt,name=history_count,json=historyCount,proto3" json:"history_count,omitempty"` // len(status.history) + ObservedGeneration int64 `protobuf:"varint,22,opt,name=observed_generation,json=observedGeneration,proto3" json:"observed_generation,omitempty"` // status.observedGeneration + SpecJson string `protobuf:"bytes,23,opt,name=spec_json,json=specJson,proto3" json:"spec_json,omitempty"` // Full spec, verbatim as ingested + StatusJson string `protobuf:"bytes,24,opt,name=status_json,json=statusJson,proto3" json:"status_json,omitempty"` // Full status, verbatim as ingested +} + +func (x *FluxHelmReleaseDetails) Reset() { + *x = FluxHelmReleaseDetails{} + if protoimpl.UnsafeEnabled { + mi := &file_api_v1_common_proto_msgTypes[152] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *FluxHelmReleaseDetails) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*FluxHelmReleaseDetails) ProtoMessage() {} + +func (x *FluxHelmReleaseDetails) ProtoReflect() protoreflect.Message { + mi := &file_api_v1_common_proto_msgTypes[152] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use FluxHelmReleaseDetails.ProtoReflect.Descriptor instead. +func (*FluxHelmReleaseDetails) Descriptor() ([]byte, []int) { + return file_api_v1_common_proto_rawDescGZIP(), []int{152} +} + +func (x *FluxHelmReleaseDetails) GetChartRefKind() string { + if x != nil { + return x.ChartRefKind + } + return "" +} + +func (x *FluxHelmReleaseDetails) GetChartRefName() string { + if x != nil { + return x.ChartRefName + } + return "" +} + +func (x *FluxHelmReleaseDetails) GetChartRefNamespace() string { + if x != nil { + return x.ChartRefNamespace + } + return "" +} + +func (x *FluxHelmReleaseDetails) GetChartSourceRefKind() string { + if x != nil { + return x.ChartSourceRefKind + } + return "" +} + +func (x *FluxHelmReleaseDetails) GetChartSourceRefName() string { + if x != nil { + return x.ChartSourceRefName + } + return "" +} + +func (x *FluxHelmReleaseDetails) GetChartSourceRefNamespace() string { + if x != nil { + return x.ChartSourceRefNamespace + } + return "" +} + +func (x *FluxHelmReleaseDetails) GetChartName() string { + if x != nil { + return x.ChartName + } + return "" +} + +func (x *FluxHelmReleaseDetails) GetChartVersion() string { + if x != nil { + return x.ChartVersion + } + return "" +} + +func (x *FluxHelmReleaseDetails) GetReleaseName() string { + if x != nil { + return x.ReleaseName + } + return "" +} + +func (x *FluxHelmReleaseDetails) GetTargetNamespace() string { + if x != nil { + return x.TargetNamespace + } + return "" +} + +func (x *FluxHelmReleaseDetails) GetStorageNamespace() string { + if x != nil { + return x.StorageNamespace + } + return "" +} + +func (x *FluxHelmReleaseDetails) GetIntervalSeconds() int64 { + if x != nil { + return x.IntervalSeconds + } + return 0 +} + +func (x *FluxHelmReleaseDetails) GetSuspended() bool { + if x != nil { + return x.Suspended + } + return false +} + +func (x *FluxHelmReleaseDetails) GetReady() bool { + if x != nil { + return x.Ready + } + return false +} + +func (x *FluxHelmReleaseDetails) GetReadyReason() string { + if x != nil { + return x.ReadyReason + } + return "" +} + +func (x *FluxHelmReleaseDetails) GetReadyMessage() string { + if x != nil { + return x.ReadyMessage + } + return "" +} + +func (x *FluxHelmReleaseDetails) GetLastAttemptedRevision() string { + if x != nil { + return x.LastAttemptedRevision + } + return "" +} + +func (x *FluxHelmReleaseDetails) GetLastAttemptedRevisionDigest() string { + if x != nil { + return x.LastAttemptedRevisionDigest + } + return "" +} + +func (x *FluxHelmReleaseDetails) GetLastReleaseRevision() string { + if x != nil { + return x.LastReleaseRevision + } + return "" +} + +func (x *FluxHelmReleaseDetails) GetHelmChart() string { + if x != nil { + return x.HelmChart + } + return "" +} + +func (x *FluxHelmReleaseDetails) GetHistoryCount() int32 { + if x != nil { + return x.HistoryCount + } + return 0 +} + +func (x *FluxHelmReleaseDetails) GetObservedGeneration() int64 { + if x != nil { + return x.ObservedGeneration + } + return 0 +} + +func (x *FluxHelmReleaseDetails) GetSpecJson() string { + if x != nil { + return x.SpecJson + } + return "" +} + +func (x *FluxHelmReleaseDetails) GetStatusJson() string { + if x != nil { + return x.StatusJson + } + return "" +} + +// FluxGenericDetails is shared by the FluxCD kinds that get collector + storage +// coverage but not yet a bespoke explorer detail panel: ExternalArtifact, Alert, +// Provider, Receiver, and the Flux Operator's FluxInstance, FluxReport, +// ResourceSetInputProvider and ResourceSet. Reused across several ResourceDetails +// oneof fields (one message type, many field numbers) -- see FluxGitRepositoryDetails +// etc. for the shape to split a kind into once it gets a bespoke detail panel. +type FluxGenericDetails struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Ready bool `protobuf:"varint,1,opt,name=ready,proto3" json:"ready,omitempty"` // status Ready condition status == True, when the kind has one + ReadyReason string `protobuf:"bytes,2,opt,name=ready_reason,json=readyReason,proto3" json:"ready_reason,omitempty"` // status Ready condition reason + ReadyMessage string `protobuf:"bytes,3,opt,name=ready_message,json=readyMessage,proto3" json:"ready_message,omitempty"` // status Ready condition message + Suspended bool `protobuf:"varint,4,opt,name=suspended,proto3" json:"suspended,omitempty"` // spec.suspend, when the kind has one (Alert, Provider, Receiver) + ObservedGeneration int64 `protobuf:"varint,5,opt,name=observed_generation,json=observedGeneration,proto3" json:"observed_generation,omitempty"` // status.observedGeneration + SpecJson string `protobuf:"bytes,6,opt,name=spec_json,json=specJson,proto3" json:"spec_json,omitempty"` // Full spec, verbatim as ingested + StatusJson string `protobuf:"bytes,7,opt,name=status_json,json=statusJson,proto3" json:"status_json,omitempty"` // Full status, verbatim as ingested +} + +func (x *FluxGenericDetails) Reset() { + *x = FluxGenericDetails{} + if protoimpl.UnsafeEnabled { + mi := &file_api_v1_common_proto_msgTypes[153] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *FluxGenericDetails) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*FluxGenericDetails) ProtoMessage() {} + +func (x *FluxGenericDetails) ProtoReflect() protoreflect.Message { + mi := &file_api_v1_common_proto_msgTypes[153] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use FluxGenericDetails.ProtoReflect.Descriptor instead. +func (*FluxGenericDetails) Descriptor() ([]byte, []int) { + return file_api_v1_common_proto_rawDescGZIP(), []int{153} +} + +func (x *FluxGenericDetails) GetReady() bool { + if x != nil { + return x.Ready + } + return false +} + +func (x *FluxGenericDetails) GetReadyReason() string { + if x != nil { + return x.ReadyReason + } + return "" +} + +func (x *FluxGenericDetails) GetReadyMessage() string { + if x != nil { + return x.ReadyMessage + } + return "" +} + +func (x *FluxGenericDetails) GetSuspended() bool { + if x != nil { + return x.Suspended + } + return false +} + +func (x *FluxGenericDetails) GetObservedGeneration() int64 { + if x != nil { + return x.ObservedGeneration + } + return 0 +} + +func (x *FluxGenericDetails) GetSpecJson() string { + if x != nil { + return x.SpecJson + } + return "" +} + +func (x *FluxGenericDetails) GetStatusJson() string { + if x != nil { + return x.StatusJson + } + return "" +} + +// FlaggerObjectReference is the one shape shared by every object pointer Flagger +// uses: Canary spec.targetRef and spec.autoscalerRef, a canary metric +// templateRef (to a MetricTemplate) and a canary alert providerRef (to an +// AlertProvider). Upstream spells these as four different Go types +// (LocalObjectReference, AutoscalerRefernce -- the upstream spelling is missing +// an e -- and CrossNamespaceObjectReference) that differ only in whether +// namespace is present, so they collapse to one message here rather than four +// near-identical ones. +// +// namespace is empty when the reference is namespace-local, meaning it resolves +// in the Canary object own namespace. api_version and kind are empty when the +// author omitted them and the controller applied its default. +type FlaggerObjectReference struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + ApiVersion string `protobuf:"bytes,1,opt,name=api_version,json=apiVersion,proto3" json:"api_version,omitempty"` // apiVersion, e.g. apps/v1 + Kind string `protobuf:"bytes,2,opt,name=kind,proto3" json:"kind,omitempty"` // kind, e.g. Deployment, HorizontalPodAutoscaler, MetricTemplate, AlertProvider + Name string `protobuf:"bytes,3,opt,name=name,proto3" json:"name,omitempty"` // name, the only field upstream marks required + Namespace string `protobuf:"bytes,4,opt,name=namespace,proto3" json:"namespace,omitempty"` // namespace, only on the cross-namespace reference types; empty means same namespace as the Canary +} + +func (x *FlaggerObjectReference) Reset() { + *x = FlaggerObjectReference{} + if protoimpl.UnsafeEnabled { + mi := &file_api_v1_common_proto_msgTypes[154] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *FlaggerObjectReference) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*FlaggerObjectReference) ProtoMessage() {} + +func (x *FlaggerObjectReference) ProtoReflect() protoreflect.Message { + mi := &file_api_v1_common_proto_msgTypes[154] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use FlaggerObjectReference.ProtoReflect.Descriptor instead. +func (*FlaggerObjectReference) Descriptor() ([]byte, []int) { + return file_api_v1_common_proto_rawDescGZIP(), []int{154} +} + +func (x *FlaggerObjectReference) GetApiVersion() string { + if x != nil { + return x.ApiVersion + } + return "" +} + +func (x *FlaggerObjectReference) GetKind() string { + if x != nil { + return x.Kind + } + return "" +} + +func (x *FlaggerObjectReference) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *FlaggerObjectReference) GetNamespace() string { + if x != nil { + return x.Namespace + } + return "" +} + +// FlaggerGatewayRef is one entry of Canary spec.service.gatewayRefs. This is a +// Gateway API ParentReference, NOT one of the Flagger reference types, which is +// why it is a separate message from FlaggerObjectReference: it carries group, +// section_name and port and has no apiVersion. +// +// RELATION NOTE: Gateway and HTTPRoute are not kinds dakr collects, so these are +// reference TEXT only. The web layer must render them as plain text and must not +// link them to an explorer row -- there is no row to link to. +type FlaggerGatewayRef struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Group string `protobuf:"bytes,1,opt,name=group,proto3" json:"group,omitempty"` // parentRef.group; the controller defaults an omitted value to gateway.networking.k8s.io + Kind string `protobuf:"bytes,2,opt,name=kind,proto3" json:"kind,omitempty"` // parentRef.kind; the controller defaults an omitted value to Gateway + Namespace string `protobuf:"bytes,3,opt,name=namespace,proto3" json:"namespace,omitempty"` // parentRef.namespace; empty means the Canary own namespace + Name string `protobuf:"bytes,4,opt,name=name,proto3" json:"name,omitempty"` // parentRef.name, the only required field + SectionName string `protobuf:"bytes,5,opt,name=section_name,json=sectionName,proto3" json:"section_name,omitempty"` // parentRef.sectionName, e.g. a named listener on the Gateway + Port int32 `protobuf:"varint,6,opt,name=port,proto3" json:"port,omitempty"` // parentRef.port; 0 means unset, which is the common case +} + +func (x *FlaggerGatewayRef) Reset() { + *x = FlaggerGatewayRef{} + if protoimpl.UnsafeEnabled { + mi := &file_api_v1_common_proto_msgTypes[155] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *FlaggerGatewayRef) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*FlaggerGatewayRef) ProtoMessage() {} + +func (x *FlaggerGatewayRef) ProtoReflect() protoreflect.Message { + mi := &file_api_v1_common_proto_msgTypes[155] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use FlaggerGatewayRef.ProtoReflect.Descriptor instead. +func (*FlaggerGatewayRef) Descriptor() ([]byte, []int) { + return file_api_v1_common_proto_rawDescGZIP(), []int{155} +} + +func (x *FlaggerGatewayRef) GetGroup() string { + if x != nil { + return x.Group + } + return "" +} + +func (x *FlaggerGatewayRef) GetKind() string { + if x != nil { + return x.Kind + } + return "" +} + +func (x *FlaggerGatewayRef) GetNamespace() string { + if x != nil { + return x.Namespace + } + return "" +} + +func (x *FlaggerGatewayRef) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *FlaggerGatewayRef) GetSectionName() string { + if x != nil { + return x.SectionName + } + return "" +} + +func (x *FlaggerGatewayRef) GetPort() int32 { + if x != nil { + return x.Port + } + return 0 +} + +// FlaggerCanaryMetric is one entry of the effective canary analysis metrics list +// (see FlaggerCanaryDetails.analysis_* for which of the two analysis blocks the +// list came from). +// +// RESOLUTION RULE for the two deprecated/replacement pairs on this type, applied +// once at ingest so readers never see both halves: +// - thresholdRange.min / thresholdRange.max win over the deprecated scalar +// metric threshold. When only the scalar is set it is carried in +// threshold_max, because the scalar upstream meaning is an upper bound. +// - templateRef wins over the deprecated inline query. template_ref is set +// only when the metric actually points at a MetricTemplate; a metric using +// the deprecated inline query leaves template_ref unset and the query text +// stays visible only in spec_json. +type FlaggerCanaryMetric struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` // metric name, e.g. request-success-rate + Interval string `protobuf:"bytes,2,opt,name=interval,proto3" json:"interval,omitempty"` // metric interval as authored, e.g. 1m; empty means the analysis interval applies + // Lower bound from thresholdRange.min. Optional because 0 is a legitimate + // bound and must stay distinguishable from absent -- the live podinfo Canary + // sets only a max. + ThresholdMin *float64 `protobuf:"fixed64,3,opt,name=threshold_min,json=thresholdMin,proto3,oneof" json:"threshold_min,omitempty"` + // Upper bound from thresholdRange.max, or the deprecated scalar threshold when + // no range is set. Same absent-vs-zero reasoning as threshold_min. + ThresholdMax *float64 `protobuf:"fixed64,4,opt,name=threshold_max,json=thresholdMax,proto3,oneof" json:"threshold_max,omitempty"` + TemplateRef *FlaggerObjectReference `protobuf:"bytes,5,opt,name=template_ref,json=templateRef,proto3" json:"template_ref,omitempty"` // templateRef, resolving to a MetricTemplate; unset when the metric is built in or uses the deprecated inline query +} + +func (x *FlaggerCanaryMetric) Reset() { + *x = FlaggerCanaryMetric{} + if protoimpl.UnsafeEnabled { + mi := &file_api_v1_common_proto_msgTypes[156] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *FlaggerCanaryMetric) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*FlaggerCanaryMetric) ProtoMessage() {} + +func (x *FlaggerCanaryMetric) ProtoReflect() protoreflect.Message { + mi := &file_api_v1_common_proto_msgTypes[156] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use FlaggerCanaryMetric.ProtoReflect.Descriptor instead. +func (*FlaggerCanaryMetric) Descriptor() ([]byte, []int) { + return file_api_v1_common_proto_rawDescGZIP(), []int{156} +} + +func (x *FlaggerCanaryMetric) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *FlaggerCanaryMetric) GetInterval() string { + if x != nil { + return x.Interval + } + return "" +} + +func (x *FlaggerCanaryMetric) GetThresholdMin() float64 { + if x != nil && x.ThresholdMin != nil { + return *x.ThresholdMin + } + return 0 +} + +func (x *FlaggerCanaryMetric) GetThresholdMax() float64 { + if x != nil && x.ThresholdMax != nil { + return *x.ThresholdMax + } + return 0 +} + +func (x *FlaggerCanaryMetric) GetTemplateRef() *FlaggerObjectReference { + if x != nil { + return x.TemplateRef + } + return nil +} + +// FlaggerCanaryAlert is one entry of the effective canary analysis alerts list. +type FlaggerCanaryAlert struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` // alert name + // severity as authored: info, warn or error. Empty is a real authored value + // upstream and means the provider default applies, so render it as the default + // rather than as unknown. + Severity string `protobuf:"bytes,2,opt,name=severity,proto3" json:"severity,omitempty"` + ProviderRef *FlaggerObjectReference `protobuf:"bytes,3,opt,name=provider_ref,json=providerRef,proto3" json:"provider_ref,omitempty"` // providerRef, resolving to an AlertProvider +} + +func (x *FlaggerCanaryAlert) Reset() { + *x = FlaggerCanaryAlert{} + if protoimpl.UnsafeEnabled { + mi := &file_api_v1_common_proto_msgTypes[157] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *FlaggerCanaryAlert) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*FlaggerCanaryAlert) ProtoMessage() {} + +func (x *FlaggerCanaryAlert) ProtoReflect() protoreflect.Message { + mi := &file_api_v1_common_proto_msgTypes[157] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use FlaggerCanaryAlert.ProtoReflect.Descriptor instead. +func (*FlaggerCanaryAlert) Descriptor() ([]byte, []int) { + return file_api_v1_common_proto_rawDescGZIP(), []int{157} +} + +func (x *FlaggerCanaryAlert) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *FlaggerCanaryAlert) GetSeverity() string { + if x != nil { + return x.Severity + } + return "" +} + +func (x *FlaggerCanaryAlert) GetProviderRef() *FlaggerObjectReference { + if x != nil { + return x.ProviderRef + } + return nil +} + +// FlaggerCanaryWebhook is one entry of the effective canary analysis webhooks +// list. The webhook metadata map is deliberately not lifted: it is arbitrary +// operator-supplied key/value text and stays in spec_json. +type FlaggerCanaryWebhook struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` // webhook name + Type string `protobuf:"bytes,2,opt,name=type,proto3" json:"type,omitempty"` // hook type: rollout, pre-rollout, post-rollout, confirm-rollout, confirm-promotion, event, rollback or confirm-traffic-increase + // AUTHZ: free text a cluster operator wrote. Frequently an in-cluster URL that + // names internal services and may carry a token in its query string, so it is + // droppable in demo mode. + Url string `protobuf:"bytes,3,opt,name=url,proto3" json:"url,omitempty"` + Timeout string `protobuf:"bytes,4,opt,name=timeout,proto3" json:"timeout,omitempty"` // webhook timeout as authored, e.g. 30s + MuteAlert bool `protobuf:"varint,5,opt,name=mute_alert,json=muteAlert,proto3" json:"mute_alert,omitempty"` // muteAlert: suppress alerts for this webhook failures + Retries int32 `protobuf:"varint,6,opt,name=retries,proto3" json:"retries,omitempty"` // retries; 0 means unset + DisableTls bool `protobuf:"varint,7,opt,name=disable_tls,json=disableTls,proto3" json:"disable_tls,omitempty"` // disableTLS +} + +func (x *FlaggerCanaryWebhook) Reset() { + *x = FlaggerCanaryWebhook{} + if protoimpl.UnsafeEnabled { + mi := &file_api_v1_common_proto_msgTypes[158] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *FlaggerCanaryWebhook) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*FlaggerCanaryWebhook) ProtoMessage() {} + +func (x *FlaggerCanaryWebhook) ProtoReflect() protoreflect.Message { + mi := &file_api_v1_common_proto_msgTypes[158] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use FlaggerCanaryWebhook.ProtoReflect.Descriptor instead. +func (*FlaggerCanaryWebhook) Descriptor() ([]byte, []int) { + return file_api_v1_common_proto_rawDescGZIP(), []int{158} +} + +func (x *FlaggerCanaryWebhook) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *FlaggerCanaryWebhook) GetType() string { + if x != nil { + return x.Type + } + return "" +} + +func (x *FlaggerCanaryWebhook) GetUrl() string { + if x != nil { + return x.Url + } + return "" +} + +func (x *FlaggerCanaryWebhook) GetTimeout() string { + if x != nil { + return x.Timeout + } + return "" +} + +func (x *FlaggerCanaryWebhook) GetMuteAlert() bool { + if x != nil { + return x.MuteAlert + } + return false +} + +func (x *FlaggerCanaryWebhook) GetRetries() int32 { + if x != nil { + return x.Retries + } + return 0 +} + +func (x *FlaggerCanaryWebhook) GetDisableTls() bool { + if x != nil { + return x.DisableTls + } + return false +} + +// FlaggerCanaryDetails contains detailed Flagger Canary information for +// drill-down (flagger.app/v1beta1). A Canary runs no pods: it drives the primary +// and canary copies of the workload named by target_ref. +type FlaggerCanaryDetails struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // status.phase: where the rollout sits in the state machine right now. One of + // Initializing, Initialized, Waiting, Progressing, WaitingPromotion, + // Promoting, Finalising (British spelling, as upstream), Succeeded, Failed, + // Terminating, Terminated. The CRD schema also permits empty, which means the + // controller has not reconciled the object yet. + // + // This answers "what is happening right now". It does NOT on its own answer + // "did my last rollout promote or roll back" -- for that, read promoted_reason + // and promoted_message below. + Phase string `protobuf:"bytes,1,opt,name=phase,proto3" json:"phase,omitempty"` + // The Promoted condition status: True, False or Unknown. Promoted is the only + // condition type Flagger sets, and it -- not phase -- carries the outcome of + // the most recent analysis. + PromotedStatus string `protobuf:"bytes,2,opt,name=promoted_status,json=promotedStatus,proto3" json:"promoted_status,omitempty"` + // The Promoted condition reason. This is the field that answers "did my canary + // promote or roll back": it mirrors the terminal phase value of the last + // completed rollout even after phase has moved on. + PromotedReason string `protobuf:"bytes,3,opt,name=promoted_reason,json=promotedReason,proto3" json:"promoted_reason,omitempty"` + // The Promoted condition message, e.g. a sentence saying the canary analysis + // completed and promotion finished, or which metric check failed. + // + // AUTHZ: free text the Flagger controller wrote. It can quote workload names, + // metric queries and provider errors, so it is droppable in demo mode. + PromotedMessage string `protobuf:"bytes,4,opt,name=promoted_message,json=promotedMessage,proto3" json:"promoted_message,omitempty"` + LastTransitionTime *timestamppb.Timestamp `protobuf:"bytes,5,opt,name=last_transition_time,json=lastTransitionTime,proto3" json:"last_transition_time,omitempty"` // status.conditions Promoted lastTransitionTime: when the outcome above last changed + CanaryWeight int32 `protobuf:"varint,6,opt,name=canary_weight,json=canaryWeight,proto3" json:"canary_weight,omitempty"` // status.canaryWeight: percent of traffic currently on the canary. Meaningless in iteration mode, where analysis_iterations drives the rollout instead + FailedChecks int32 `protobuf:"varint,7,opt,name=failed_checks,json=failedChecks,proto3" json:"failed_checks,omitempty"` // status.failedChecks: consecutive failed metric checks so far, compared against analysis_threshold + Iterations int32 `protobuf:"varint,8,opt,name=iterations,proto3" json:"iterations,omitempty"` // status.iterations: iterations completed so far, in iteration (blue/green) mode + LastAppliedSpec string `protobuf:"bytes,9,opt,name=last_applied_spec,json=lastAppliedSpec,proto3" json:"last_applied_spec,omitempty"` // status.lastAppliedSpec: hash of the target workload spec the canary last applied + LastPromotedSpec string `protobuf:"bytes,10,opt,name=last_promoted_spec,json=lastPromotedSpec,proto3" json:"last_promoted_spec,omitempty"` // status.lastPromotedSpec: hash of the target workload spec last promoted to primary + SessionAffinityCookie string `protobuf:"bytes,11,opt,name=session_affinity_cookie,json=sessionAffinityCookie,proto3" json:"session_affinity_cookie,omitempty"` // status.sessionAffinityCookie: the live cookie pinning a session to the canary, when session affinity is configured + // spec.provider, e.g. istio, nginx, gatewayapi, kubernetes. The value may carry + // a version suffix such as gatewayapi:v1, so treat it as free text, not a + // closed set. + // + // EMPTY IS MEANINGFUL AND IS NOT UNKNOWN: an omitted provider means the Canary + // inherits the Flagger controller global mesh-provider flag. Render empty as + // the controller default, never as N/A or unknown. + Provider string `protobuf:"bytes,12,opt,name=provider,proto3" json:"provider,omitempty"` + // spec.progressDeadlineSeconds. Optional because upstream defaults it to 600 + // when omitted, and absent must stay distinguishable from an explicit 0 (which + // would mean no progress is ever tolerated). + ProgressDeadlineSeconds *int32 `protobuf:"varint,13,opt,name=progress_deadline_seconds,json=progressDeadlineSeconds,proto3,oneof" json:"progress_deadline_seconds,omitempty"` + SkipAnalysis bool `protobuf:"varint,14,opt,name=skip_analysis,json=skipAnalysis,proto3" json:"skip_analysis,omitempty"` // spec.skipAnalysis: promote straight to primary without running the analysis + Suspend bool `protobuf:"varint,15,opt,name=suspend,proto3" json:"suspend,omitempty"` // spec.suspend: the controller ignores this Canary entirely + RevertOnDeletion bool `protobuf:"varint,16,opt,name=revert_on_deletion,json=revertOnDeletion,proto3" json:"revert_on_deletion,omitempty"` // spec.revertOnDeletion: restore the target workload when the Canary is deleted + TargetRef *FlaggerObjectReference `protobuf:"bytes,17,opt,name=target_ref,json=targetRef,proto3" json:"target_ref,omitempty"` // spec.targetRef: the workload this Canary drives + AutoscalerRef *FlaggerObjectReference `protobuf:"bytes,18,opt,name=autoscaler_ref,json=autoscalerRef,proto3" json:"autoscaler_ref,omitempty"` // spec.autoscalerRef: the HPA or ScaledObject moved onto the primary copy; unset when the workload has no autoscaler + // spec.service.port. Optional because 0 is what an author writing no port + // leaves behind and 0 is not a valid port, so absent carries real information. + ServicePort *int32 `protobuf:"varint,19,opt,name=service_port,json=servicePort,proto3,oneof" json:"service_port,omitempty"` + // spec.service.targetPort, rendered as text. The upstream type is an + // IntOrString, so this is either a number or a named container port; the web + // layer must not assume it parses as an integer. + ServiceTargetPort string `protobuf:"bytes,20,opt,name=service_target_port,json=serviceTargetPort,proto3" json:"service_target_port,omitempty"` + ServicePortName string `protobuf:"bytes,21,opt,name=service_port_name,json=servicePortName,proto3" json:"service_port_name,omitempty"` // spec.service.portName, e.g. http + ServiceHosts []string `protobuf:"bytes,22,rep,name=service_hosts,json=serviceHosts,proto3" json:"service_hosts,omitempty"` // spec.service.hosts + ServiceHeadless bool `protobuf:"varint,23,opt,name=service_headless,json=serviceHeadless,proto3" json:"service_headless,omitempty"` // spec.service.headless + ServicePortDiscovery bool `protobuf:"varint,24,opt,name=service_port_discovery,json=servicePortDiscovery,proto3" json:"service_port_discovery,omitempty"` // spec.service.portDiscovery + ServiceTimeout string `protobuf:"bytes,25,opt,name=service_timeout,json=serviceTimeout,proto3" json:"service_timeout,omitempty"` // spec.service.timeout as authored, e.g. 5s + // spec.service.gatewayRefs. Reference text only -- Gateway and HTTPRoute are + // not kinds dakr collects, so these must not be rendered as links. + GatewayRefs []*FlaggerGatewayRef `protobuf:"bytes,26,rep,name=gateway_refs,json=gatewayRefs,proto3" json:"gateway_refs,omitempty"` + // Every analysis_* field below, and the metrics, alerts and webhooks lists, + // come from ONE effective analysis block: spec.analysis, falling back to the + // deprecated spec.canaryAnalysis when spec.analysis is unset. That mirrors the + // upstream GetAnalysis() accessor. Readers see one resolved analysis and never + // both, so there is no second set of legacy fields to reconcile. + AnalysisInterval string `protobuf:"bytes,27,opt,name=analysis_interval,json=analysisInterval,proto3" json:"analysis_interval,omitempty"` // analysis.interval as authored, e.g. 1m: how often a metric check runs + AnalysisThreshold int32 `protobuf:"varint,28,opt,name=analysis_threshold,json=analysisThreshold,proto3" json:"analysis_threshold,omitempty"` // analysis.threshold: how many failed checks are tolerated before rollback + AnalysisIterations int32 `protobuf:"varint,29,opt,name=analysis_iterations,json=analysisIterations,proto3" json:"analysis_iterations,omitempty"` // analysis.iterations: total iterations in blue/green or A/B mode; 0 means weight-based progressive traffic shifting instead + AnalysisMaxWeight int32 `protobuf:"varint,30,opt,name=analysis_max_weight,json=analysisMaxWeight,proto3" json:"analysis_max_weight,omitempty"` // analysis.maxWeight: canary traffic percent at which promotion happens + AnalysisStepWeight int32 `protobuf:"varint,31,opt,name=analysis_step_weight,json=analysisStepWeight,proto3" json:"analysis_step_weight,omitempty"` // analysis.stepWeight: percent added to the canary each interval + // analysis.stepWeights: an explicit, non-uniform traffic ramp. Set instead of + // analysis_step_weight, never alongside it, so a zero analysis_step_weight + // with a non-empty list here is a configured canary, not an unconfigured one. + AnalysisStepWeights []int32 `protobuf:"varint,32,rep,packed,name=analysis_step_weights,json=analysisStepWeights,proto3" json:"analysis_step_weights,omitempty"` + AnalysisStepWeightPromotion int32 `protobuf:"varint,33,opt,name=analysis_step_weight_promotion,json=analysisStepWeightPromotion,proto3" json:"analysis_step_weight_promotion,omitempty"` // analysis.stepWeightPromotion: percent added to the primary each interval while promoting + AnalysisMirror bool `protobuf:"varint,34,opt,name=analysis_mirror,json=analysisMirror,proto3" json:"analysis_mirror,omitempty"` // analysis.mirror: mirror traffic to the canary instead of shifting it + // analysis.primaryReadyThreshold: percent of primary pods that must be ready. + // Optional because upstream models it as a pointer and 0 is a meaningful + // explicit value. + PrimaryReadyThreshold *int32 `protobuf:"varint,35,opt,name=primary_ready_threshold,json=primaryReadyThreshold,proto3,oneof" json:"primary_ready_threshold,omitempty"` + // analysis.canaryReadyThreshold, same absent-vs-zero reasoning as + // primary_ready_threshold. + CanaryReadyThreshold *int32 `protobuf:"varint,36,opt,name=canary_ready_threshold,json=canaryReadyThreshold,proto3,oneof" json:"canary_ready_threshold,omitempty"` + SessionAffinityCookieName string `protobuf:"bytes,37,opt,name=session_affinity_cookie_name,json=sessionAffinityCookieName,proto3" json:"session_affinity_cookie_name,omitempty"` // analysis.sessionAffinity.cookieName: the configured cookie name, as opposed to session_affinity_cookie, which is the live value + Metrics []*FlaggerCanaryMetric `protobuf:"bytes,38,rep,name=metrics,proto3" json:"metrics,omitempty"` // analysis.metrics, in authored order + Alerts []*FlaggerCanaryAlert `protobuf:"bytes,39,rep,name=alerts,proto3" json:"alerts,omitempty"` // analysis.alerts, in authored order + Webhooks []*FlaggerCanaryWebhook `protobuf:"bytes,40,rep,name=webhooks,proto3" json:"webhooks,omitempty"` // analysis.webhooks, in authored order + // Full spec, verbatim as ingested (not re-derived from the fields above), so a + // field not lifted into this message is still visible. + // + // Carried on BOTH the paginated list path and the GetWorkload drawer path: the + // blob is small and bounded, measured at 213 B and 1128 B on the two live + // Canary objects, so it needs no split populator the way a CRD spec does. + // + // AUTHZ: free text an operator wrote, including webhook URLs and metric + // queries. Droppable in demo mode. + SpecJson string `protobuf:"bytes,41,opt,name=spec_json,json=specJson,proto3" json:"spec_json,omitempty"` + // Full status, verbatim as ingested. On both the list and detail paths for the + // same reason as spec_json -- measured at 425 B on the live objects. + // + // AUTHZ: contains the controller condition messages. Droppable in demo mode. + StatusJson string `protobuf:"bytes,42,opt,name=status_json,json=statusJson,proto3" json:"status_json,omitempty"` +} + +func (x *FlaggerCanaryDetails) Reset() { + *x = FlaggerCanaryDetails{} + if protoimpl.UnsafeEnabled { + mi := &file_api_v1_common_proto_msgTypes[159] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *FlaggerCanaryDetails) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*FlaggerCanaryDetails) ProtoMessage() {} + +func (x *FlaggerCanaryDetails) ProtoReflect() protoreflect.Message { + mi := &file_api_v1_common_proto_msgTypes[159] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use FlaggerCanaryDetails.ProtoReflect.Descriptor instead. +func (*FlaggerCanaryDetails) Descriptor() ([]byte, []int) { + return file_api_v1_common_proto_rawDescGZIP(), []int{159} +} + +func (x *FlaggerCanaryDetails) GetPhase() string { + if x != nil { + return x.Phase + } + return "" +} + +func (x *FlaggerCanaryDetails) GetPromotedStatus() string { + if x != nil { + return x.PromotedStatus + } + return "" +} + +func (x *FlaggerCanaryDetails) GetPromotedReason() string { + if x != nil { + return x.PromotedReason + } + return "" +} + +func (x *FlaggerCanaryDetails) GetPromotedMessage() string { + if x != nil { + return x.PromotedMessage + } + return "" +} + +func (x *FlaggerCanaryDetails) GetLastTransitionTime() *timestamppb.Timestamp { + if x != nil { + return x.LastTransitionTime + } + return nil +} + +func (x *FlaggerCanaryDetails) GetCanaryWeight() int32 { + if x != nil { + return x.CanaryWeight + } + return 0 +} + +func (x *FlaggerCanaryDetails) GetFailedChecks() int32 { + if x != nil { + return x.FailedChecks + } + return 0 +} + +func (x *FlaggerCanaryDetails) GetIterations() int32 { + if x != nil { + return x.Iterations + } + return 0 +} + +func (x *FlaggerCanaryDetails) GetLastAppliedSpec() string { + if x != nil { + return x.LastAppliedSpec + } + return "" +} + +func (x *FlaggerCanaryDetails) GetLastPromotedSpec() string { + if x != nil { + return x.LastPromotedSpec + } + return "" +} + +func (x *FlaggerCanaryDetails) GetSessionAffinityCookie() string { + if x != nil { + return x.SessionAffinityCookie + } + return "" +} + +func (x *FlaggerCanaryDetails) GetProvider() string { + if x != nil { + return x.Provider + } + return "" +} + +func (x *FlaggerCanaryDetails) GetProgressDeadlineSeconds() int32 { + if x != nil && x.ProgressDeadlineSeconds != nil { + return *x.ProgressDeadlineSeconds + } + return 0 +} + +func (x *FlaggerCanaryDetails) GetSkipAnalysis() bool { + if x != nil { + return x.SkipAnalysis + } + return false +} + +func (x *FlaggerCanaryDetails) GetSuspend() bool { + if x != nil { + return x.Suspend + } + return false +} + +func (x *FlaggerCanaryDetails) GetRevertOnDeletion() bool { + if x != nil { + return x.RevertOnDeletion + } + return false +} + +func (x *FlaggerCanaryDetails) GetTargetRef() *FlaggerObjectReference { + if x != nil { + return x.TargetRef + } + return nil +} + +func (x *FlaggerCanaryDetails) GetAutoscalerRef() *FlaggerObjectReference { + if x != nil { + return x.AutoscalerRef + } + return nil +} + +func (x *FlaggerCanaryDetails) GetServicePort() int32 { + if x != nil && x.ServicePort != nil { + return *x.ServicePort + } + return 0 +} + +func (x *FlaggerCanaryDetails) GetServiceTargetPort() string { + if x != nil { + return x.ServiceTargetPort + } + return "" +} + +func (x *FlaggerCanaryDetails) GetServicePortName() string { + if x != nil { + return x.ServicePortName + } + return "" +} + +func (x *FlaggerCanaryDetails) GetServiceHosts() []string { + if x != nil { + return x.ServiceHosts + } + return nil +} + +func (x *FlaggerCanaryDetails) GetServiceHeadless() bool { + if x != nil { + return x.ServiceHeadless + } + return false +} + +func (x *FlaggerCanaryDetails) GetServicePortDiscovery() bool { + if x != nil { + return x.ServicePortDiscovery + } + return false +} + +func (x *FlaggerCanaryDetails) GetServiceTimeout() string { + if x != nil { + return x.ServiceTimeout + } + return "" +} + +func (x *FlaggerCanaryDetails) GetGatewayRefs() []*FlaggerGatewayRef { + if x != nil { + return x.GatewayRefs + } + return nil +} + +func (x *FlaggerCanaryDetails) GetAnalysisInterval() string { + if x != nil { + return x.AnalysisInterval + } + return "" +} + +func (x *FlaggerCanaryDetails) GetAnalysisThreshold() int32 { + if x != nil { + return x.AnalysisThreshold + } + return 0 +} + +func (x *FlaggerCanaryDetails) GetAnalysisIterations() int32 { + if x != nil { + return x.AnalysisIterations + } + return 0 +} + +func (x *FlaggerCanaryDetails) GetAnalysisMaxWeight() int32 { + if x != nil { + return x.AnalysisMaxWeight + } + return 0 +} + +func (x *FlaggerCanaryDetails) GetAnalysisStepWeight() int32 { + if x != nil { + return x.AnalysisStepWeight + } + return 0 +} + +func (x *FlaggerCanaryDetails) GetAnalysisStepWeights() []int32 { + if x != nil { + return x.AnalysisStepWeights + } + return nil +} + +func (x *FlaggerCanaryDetails) GetAnalysisStepWeightPromotion() int32 { + if x != nil { + return x.AnalysisStepWeightPromotion + } + return 0 +} + +func (x *FlaggerCanaryDetails) GetAnalysisMirror() bool { + if x != nil { + return x.AnalysisMirror + } + return false +} + +func (x *FlaggerCanaryDetails) GetPrimaryReadyThreshold() int32 { + if x != nil && x.PrimaryReadyThreshold != nil { + return *x.PrimaryReadyThreshold + } + return 0 +} + +func (x *FlaggerCanaryDetails) GetCanaryReadyThreshold() int32 { + if x != nil && x.CanaryReadyThreshold != nil { + return *x.CanaryReadyThreshold + } + return 0 +} + +func (x *FlaggerCanaryDetails) GetSessionAffinityCookieName() string { + if x != nil { + return x.SessionAffinityCookieName + } + return "" +} + +func (x *FlaggerCanaryDetails) GetMetrics() []*FlaggerCanaryMetric { + if x != nil { + return x.Metrics + } + return nil +} + +func (x *FlaggerCanaryDetails) GetAlerts() []*FlaggerCanaryAlert { + if x != nil { + return x.Alerts + } + return nil +} + +func (x *FlaggerCanaryDetails) GetWebhooks() []*FlaggerCanaryWebhook { + if x != nil { + return x.Webhooks + } + return nil +} + +func (x *FlaggerCanaryDetails) GetSpecJson() string { + if x != nil { + return x.SpecJson + } + return "" +} + +func (x *FlaggerCanaryDetails) GetStatusJson() string { + if x != nil { + return x.StatusJson + } + return "" +} + +// FlaggerMetricTemplateDetails contains detailed Flagger MetricTemplate +// information for drill-down (flagger.app/v1beta1). A MetricTemplate is a named, +// reusable query a Canary analysis metric references by templateRef; it runs no +// pods and has no spec.suspend. +type FlaggerMetricTemplateDetails struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + ProviderType string `protobuf:"bytes,1,opt,name=provider_type,json=providerType,proto3" json:"provider_type,omitempty"` // spec.provider.type, e.g. prometheus, datadog, cloudwatch, newrelic + // spec.provider.address: the metrics backend URL. + // + // AUTHZ: free text an operator wrote, typically an internal service address. + // Droppable in demo mode. + ProviderAddress string `protobuf:"bytes,2,opt,name=provider_address,json=providerAddress,proto3" json:"provider_address,omitempty"` + ProviderRegion string `protobuf:"bytes,3,opt,name=provider_region,json=providerRegion,proto3" json:"provider_region,omitempty"` // spec.provider.region, for the cloud providers that need one + ProviderInsecureSkipVerify bool `protobuf:"varint,4,opt,name=provider_insecure_skip_verify,json=providerInsecureSkipVerify,proto3" json:"provider_insecure_skip_verify,omitempty"` // spec.provider.insecureSkipVerify: TLS verification disabled against the backend + ProviderSecretName string `protobuf:"bytes,5,opt,name=provider_secret_name,json=providerSecretName,proto3" json:"provider_secret_name,omitempty"` // spec.provider.secretRef.name: the Secret holding the backend credentials. The name only -- no secret content is ever read + // spec.query: the query executed against the provider. + // + // AUTHZ: free text an operator wrote. Queries routinely embed namespace, + // workload and label names, so this is droppable in demo mode. + Query string `protobuf:"bytes,6,opt,name=query,proto3" json:"query,omitempty"` + ReadyStatus string `protobuf:"bytes,7,opt,name=ready_status,json=readyStatus,proto3" json:"ready_status,omitempty"` // status.conditions Ready status: True, False or Unknown. Set when the controller has validated the template + ReadyReason string `protobuf:"bytes,8,opt,name=ready_reason,json=readyReason,proto3" json:"ready_reason,omitempty"` // status.conditions Ready reason + // status.conditions Ready message. + // + // AUTHZ: free text the controller wrote, and on a validation failure it quotes + // the query back. Droppable in demo mode. + ReadyMessage string `protobuf:"bytes,9,opt,name=ready_message,json=readyMessage,proto3" json:"ready_message,omitempty"` + // Full spec, verbatim as ingested. Carried on both the paginated list path and + // the GetWorkload drawer path: measured at 555 B on the live object, small and + // bounded enough that no split populator is needed. + // + // AUTHZ: contains the query and the provider address. Droppable in demo mode. + SpecJson string `protobuf:"bytes,10,opt,name=spec_json,json=specJson,proto3" json:"spec_json,omitempty"` + // Full status, verbatim as ingested. On both paths, same reasoning as + // spec_json. + // + // AUTHZ: contains the controller condition messages. Droppable in demo mode. + StatusJson string `protobuf:"bytes,11,opt,name=status_json,json=statusJson,proto3" json:"status_json,omitempty"` +} + +func (x *FlaggerMetricTemplateDetails) Reset() { + *x = FlaggerMetricTemplateDetails{} + if protoimpl.UnsafeEnabled { + mi := &file_api_v1_common_proto_msgTypes[160] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *FlaggerMetricTemplateDetails) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*FlaggerMetricTemplateDetails) ProtoMessage() {} + +func (x *FlaggerMetricTemplateDetails) ProtoReflect() protoreflect.Message { + mi := &file_api_v1_common_proto_msgTypes[160] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use FlaggerMetricTemplateDetails.ProtoReflect.Descriptor instead. +func (*FlaggerMetricTemplateDetails) Descriptor() ([]byte, []int) { + return file_api_v1_common_proto_rawDescGZIP(), []int{160} +} + +func (x *FlaggerMetricTemplateDetails) GetProviderType() string { + if x != nil { + return x.ProviderType + } + return "" +} + +func (x *FlaggerMetricTemplateDetails) GetProviderAddress() string { + if x != nil { + return x.ProviderAddress + } + return "" +} + +func (x *FlaggerMetricTemplateDetails) GetProviderRegion() string { + if x != nil { + return x.ProviderRegion + } + return "" +} + +func (x *FlaggerMetricTemplateDetails) GetProviderInsecureSkipVerify() bool { + if x != nil { + return x.ProviderInsecureSkipVerify + } + return false +} + +func (x *FlaggerMetricTemplateDetails) GetProviderSecretName() string { + if x != nil { + return x.ProviderSecretName + } + return "" +} + +func (x *FlaggerMetricTemplateDetails) GetQuery() string { + if x != nil { + return x.Query + } + return "" +} + +func (x *FlaggerMetricTemplateDetails) GetReadyStatus() string { + if x != nil { + return x.ReadyStatus + } + return "" +} + +func (x *FlaggerMetricTemplateDetails) GetReadyReason() string { + if x != nil { + return x.ReadyReason + } + return "" +} + +func (x *FlaggerMetricTemplateDetails) GetReadyMessage() string { + if x != nil { + return x.ReadyMessage + } + return "" +} + +func (x *FlaggerMetricTemplateDetails) GetSpecJson() string { + if x != nil { + return x.SpecJson + } + return "" +} + +func (x *FlaggerMetricTemplateDetails) GetStatusJson() string { + if x != nil { + return x.StatusJson + } + return "" +} + +// FlaggerAlertProviderDetails contains Flagger AlertProvider information +// (flagger.app/v1beta1). Listed tier: this kind gets explorer columns and search +// but no bespoke detail panel yet, and it carries its own message rather than a +// generic one so that every Flagger kind resolves through the same +// ResourceDetails oneof and the same per-field authz path. An AlertProvider runs +// no pods and has no spec.suspend. +type FlaggerAlertProviderDetails struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Type string `protobuf:"bytes,1,opt,name=type,proto3" json:"type,omitempty"` // spec.type, e.g. slack, msteams, discord, rocket + // spec.channel: the destination channel or room. + // + // AUTHZ: free text an operator wrote, and it names an internal team channel. + // Droppable in demo mode. + Channel string `protobuf:"bytes,2,opt,name=channel,proto3" json:"channel,omitempty"` + // spec.username: the bot username messages are posted as. + // + // AUTHZ: free text an operator wrote. Droppable in demo mode. + Username string `protobuf:"bytes,3,opt,name=username,proto3" json:"username,omitempty"` + // spec.address: the webhook URL messages are posted to. + // + // AUTHZ: free text an operator wrote, and an incoming-webhook URL is itself a + // credential. Droppable in demo mode. + Address string `protobuf:"bytes,4,opt,name=address,proto3" json:"address,omitempty"` + // spec.proxy: the HTTP proxy used to reach the address. + // + // AUTHZ: free text an operator wrote. Droppable in demo mode. + Proxy string `protobuf:"bytes,5,opt,name=proxy,proto3" json:"proxy,omitempty"` + SecretName string `protobuf:"bytes,6,opt,name=secret_name,json=secretName,proto3" json:"secret_name,omitempty"` // spec.secretRef.name: the Secret holding the provider credentials. The name only -- no secret content is ever read + ReadyStatus string `protobuf:"bytes,7,opt,name=ready_status,json=readyStatus,proto3" json:"ready_status,omitempty"` // status.conditions Ready status: True, False or Unknown + ReadyReason string `protobuf:"bytes,8,opt,name=ready_reason,json=readyReason,proto3" json:"ready_reason,omitempty"` // status.conditions Ready reason + // status.conditions Ready message. + // + // AUTHZ: free text the controller wrote, and a failure message can quote the + // address. Droppable in demo mode. + ReadyMessage string `protobuf:"bytes,9,opt,name=ready_message,json=readyMessage,proto3" json:"ready_message,omitempty"` + // Full spec, verbatim as ingested. Carried on both the paginated list path and + // the GetWorkload drawer path: an AlertProvider spec is a handful of short + // scalars, smaller than the MetricTemplate spec measured at 555 B, so no split + // populator is needed. + // + // AUTHZ: contains the address, proxy, channel and username. Droppable in demo + // mode. + SpecJson string `protobuf:"bytes,10,opt,name=spec_json,json=specJson,proto3" json:"spec_json,omitempty"` + // Full status, verbatim as ingested. On both paths, same reasoning as + // spec_json. + // + // AUTHZ: contains the controller condition messages. Droppable in demo mode. + StatusJson string `protobuf:"bytes,11,opt,name=status_json,json=statusJson,proto3" json:"status_json,omitempty"` +} + +func (x *FlaggerAlertProviderDetails) Reset() { + *x = FlaggerAlertProviderDetails{} + if protoimpl.UnsafeEnabled { + mi := &file_api_v1_common_proto_msgTypes[161] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *FlaggerAlertProviderDetails) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*FlaggerAlertProviderDetails) ProtoMessage() {} + +func (x *FlaggerAlertProviderDetails) ProtoReflect() protoreflect.Message { + mi := &file_api_v1_common_proto_msgTypes[161] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use FlaggerAlertProviderDetails.ProtoReflect.Descriptor instead. +func (*FlaggerAlertProviderDetails) Descriptor() ([]byte, []int) { + return file_api_v1_common_proto_rawDescGZIP(), []int{161} +} + +func (x *FlaggerAlertProviderDetails) GetType() string { + if x != nil { + return x.Type + } + return "" +} + +func (x *FlaggerAlertProviderDetails) GetChannel() string { + if x != nil { + return x.Channel + } + return "" +} + +func (x *FlaggerAlertProviderDetails) GetUsername() string { + if x != nil { + return x.Username + } + return "" +} + +func (x *FlaggerAlertProviderDetails) GetAddress() string { + if x != nil { + return x.Address + } + return "" +} + +func (x *FlaggerAlertProviderDetails) GetProxy() string { + if x != nil { + return x.Proxy + } + return "" +} + +func (x *FlaggerAlertProviderDetails) GetSecretName() string { + if x != nil { + return x.SecretName + } + return "" +} + +func (x *FlaggerAlertProviderDetails) GetReadyStatus() string { + if x != nil { + return x.ReadyStatus + } + return "" +} + +func (x *FlaggerAlertProviderDetails) GetReadyReason() string { + if x != nil { + return x.ReadyReason + } + return "" +} + +func (x *FlaggerAlertProviderDetails) GetReadyMessage() string { + if x != nil { + return x.ReadyMessage + } + return "" +} + +func (x *FlaggerAlertProviderDetails) GetSpecJson() string { + if x != nil { + return x.SpecJson + } + return "" +} + +func (x *FlaggerAlertProviderDetails) GetStatusJson() string { + if x != nil { + return x.StatusJson + } + return "" +} + +type Event struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + Kind string `protobuf:"bytes,2,opt,name=kind,proto3" json:"kind,omitempty"` + Name string `protobuf:"bytes,3,opt,name=name,proto3" json:"name,omitempty"` + Namespace string `protobuf:"bytes,4,opt,name=namespace,proto3" json:"namespace,omitempty"` + ClusterId string `protobuf:"bytes,5,opt,name=cluster_id,json=clusterId,proto3" json:"cluster_id,omitempty"` + OwnerUid string `protobuf:"bytes,7,opt,name=owner_uid,json=ownerUid,proto3" json:"owner_uid,omitempty"` + OwnerKind string `protobuf:"bytes,8,opt,name=owner_kind,json=ownerKind,proto3" json:"owner_kind,omitempty"` + OwnerName string `protobuf:"bytes,9,opt,name=owner_name,json=ownerName,proto3" json:"owner_name,omitempty"` + ApiVersion string `protobuf:"bytes,10,opt,name=api_version,json=apiVersion,proto3" json:"api_version,omitempty"` + ResourceVersion string `protobuf:"bytes,11,opt,name=resource_version,json=resourceVersion,proto3" json:"resource_version,omitempty"` + Count int32 `protobuf:"varint,12,opt,name=count,proto3" json:"count,omitempty"` + Reason string `protobuf:"bytes,13,opt,name=reason,proto3" json:"reason,omitempty"` + Message string `protobuf:"bytes,14,opt,name=message,proto3" json:"message,omitempty"` + Type string `protobuf:"bytes,15,opt,name=type,proto3" json:"type,omitempty"` + Action string `protobuf:"bytes,16,opt,name=action,proto3" json:"action,omitempty"` + Source string `protobuf:"bytes,17,opt,name=source,proto3" json:"source,omitempty"` + InvolvedObjectKind string `protobuf:"bytes,18,opt,name=involved_object_kind,json=involvedObjectKind,proto3" json:"involved_object_kind,omitempty"` + InvolvedObjectName string `protobuf:"bytes,19,opt,name=involved_object_name,json=involvedObjectName,proto3" json:"involved_object_name,omitempty"` + InvolvedObjectNamespace string `protobuf:"bytes,20,opt,name=involved_object_namespace,json=involvedObjectNamespace,proto3" json:"involved_object_namespace,omitempty"` + ReportingController string `protobuf:"bytes,21,opt,name=reporting_controller,json=reportingController,proto3" json:"reporting_controller,omitempty"` + ReportingInstance string `protobuf:"bytes,22,opt,name=reporting_instance,json=reportingInstance,proto3" json:"reporting_instance,omitempty"` + Annotations map[string]string `protobuf:"bytes,23,rep,name=annotations,proto3" json:"annotations,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + Labels map[string]string `protobuf:"bytes,24,rep,name=labels,proto3" json:"labels,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + InvolvedObjectUid string `protobuf:"bytes,25,opt,name=involved_object_uid,json=involvedObjectUid,proto3" json:"involved_object_uid,omitempty"` + // Set only on Karpenter `DisruptionBlocked` events; UNSPECIFIED on every other event. + DisruptionBlockReason DisruptionBlockReason `protobuf:"varint,26,opt,name=disruption_block_reason,json=disruptionBlockReason,proto3,enum=api.v1.DisruptionBlockReason" json:"disruption_block_reason,omitempty"` + // The PodDisruptionBudget that blocked eviction, as "namespace/name". Set only + // alongside DISRUPTION_BLOCK_REASON_PDB_VIOLATION; absent — not empty-string — + // otherwise, since "no PDB was involved" is not the same as "a PDB with no name". + BlockingPdbName *string `protobuf:"bytes,27,opt,name=blocking_pdb_name,json=blockingPdbName,proto3,oneof" json:"blocking_pdb_name,omitempty"` + CreatedAt *timestamppb.Timestamp `protobuf:"bytes,41,opt,name=created_at,json=createdAt,proto3" json:"created_at,omitempty"` + UpdatedAt *timestamppb.Timestamp `protobuf:"bytes,42,opt,name=updated_at,json=updatedAt,proto3" json:"updated_at,omitempty"` + LastSeen *timestamppb.Timestamp `protobuf:"bytes,43,opt,name=last_seen,json=lastSeen,proto3" json:"last_seen,omitempty"` + CreatedAtAge string `protobuf:"bytes,44,opt,name=created_at_age,json=createdAtAge,proto3" json:"created_at_age,omitempty"` + UpdatedAtAge string `protobuf:"bytes,45,opt,name=updated_at_age,json=updatedAtAge,proto3" json:"updated_at_age,omitempty"` + LastSeenAge string `protobuf:"bytes,46,opt,name=last_seen_age,json=lastSeenAge,proto3" json:"last_seen_age,omitempty"` + DeletedAt *timestamppb.Timestamp `protobuf:"bytes,47,opt,name=deleted_at,json=deletedAt,proto3" json:"deleted_at,omitempty"` + // The pod behind a DISRUPTION_BLOCK_REASON_DO_NOT_DISRUPT_ANNOTATION or + // DISRUPTION_BLOCK_REASON_POD_NOMINATED classification, as "namespace/name". Same + // absent-means-not-applicable contract as blocking_pdb_name. + RelatedPodName *string `protobuf:"bytes,48,opt,name=related_pod_name,json=relatedPodName,proto3,oneof" json:"related_pod_name,omitempty"` +} + +func (x *Event) Reset() { + *x = Event{} + if protoimpl.UnsafeEnabled { + mi := &file_api_v1_common_proto_msgTypes[162] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Event) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Event) ProtoMessage() {} + +func (x *Event) ProtoReflect() protoreflect.Message { + mi := &file_api_v1_common_proto_msgTypes[162] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Event.ProtoReflect.Descriptor instead. +func (*Event) Descriptor() ([]byte, []int) { + return file_api_v1_common_proto_rawDescGZIP(), []int{162} +} + +func (x *Event) GetId() string { + if x != nil { + return x.Id + } + return "" +} + +func (x *Event) GetKind() string { + if x != nil { + return x.Kind + } + return "" +} + +func (x *Event) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *Event) GetNamespace() string { + if x != nil { + return x.Namespace + } + return "" +} + +func (x *Event) GetClusterId() string { + if x != nil { + return x.ClusterId + } + return "" +} + +func (x *Event) GetOwnerUid() string { + if x != nil { + return x.OwnerUid + } + return "" +} + +func (x *Event) GetOwnerKind() string { + if x != nil { + return x.OwnerKind + } + return "" +} + +func (x *Event) GetOwnerName() string { + if x != nil { + return x.OwnerName + } + return "" +} + +func (x *Event) GetApiVersion() string { + if x != nil { + return x.ApiVersion + } + return "" +} + +func (x *Event) GetResourceVersion() string { + if x != nil { + return x.ResourceVersion + } + return "" +} + +func (x *Event) GetCount() int32 { + if x != nil { + return x.Count + } + return 0 +} + +func (x *Event) GetReason() string { + if x != nil { + return x.Reason + } + return "" +} + +func (x *Event) GetMessage() string { + if x != nil { + return x.Message + } + return "" +} + +func (x *Event) GetType() string { + if x != nil { + return x.Type + } + return "" +} + +func (x *Event) GetAction() string { + if x != nil { + return x.Action + } + return "" +} + +func (x *Event) GetSource() string { + if x != nil { + return x.Source + } + return "" +} + +func (x *Event) GetInvolvedObjectKind() string { + if x != nil { + return x.InvolvedObjectKind + } + return "" +} + +func (x *Event) GetInvolvedObjectName() string { + if x != nil { + return x.InvolvedObjectName + } + return "" +} + +func (x *Event) GetInvolvedObjectNamespace() string { + if x != nil { + return x.InvolvedObjectNamespace + } + return "" +} + +func (x *Event) GetReportingController() string { + if x != nil { + return x.ReportingController + } + return "" +} + +func (x *Event) GetReportingInstance() string { + if x != nil { + return x.ReportingInstance + } + return "" +} + +func (x *Event) GetAnnotations() map[string]string { + if x != nil { + return x.Annotations + } + return nil +} + +func (x *Event) GetLabels() map[string]string { + if x != nil { + return x.Labels + } + return nil +} + +func (x *Event) GetInvolvedObjectUid() string { + if x != nil { + return x.InvolvedObjectUid + } + return "" +} + +func (x *Event) GetDisruptionBlockReason() DisruptionBlockReason { + if x != nil { + return x.DisruptionBlockReason + } + return DisruptionBlockReason_DISRUPTION_BLOCK_REASON_UNSPECIFIED +} + +func (x *Event) GetBlockingPdbName() string { + if x != nil && x.BlockingPdbName != nil { + return *x.BlockingPdbName + } + return "" +} + +func (x *Event) GetCreatedAt() *timestamppb.Timestamp { + if x != nil { + return x.CreatedAt + } + return nil +} + +func (x *Event) GetUpdatedAt() *timestamppb.Timestamp { + if x != nil { + return x.UpdatedAt + } + return nil +} + +func (x *Event) GetLastSeen() *timestamppb.Timestamp { + if x != nil { + return x.LastSeen + } + return nil +} + +func (x *Event) GetCreatedAtAge() string { + if x != nil { + return x.CreatedAtAge + } + return "" +} + +func (x *Event) GetUpdatedAtAge() string { + if x != nil { + return x.UpdatedAtAge + } + return "" +} + +func (x *Event) GetLastSeenAge() string { + if x != nil { + return x.LastSeenAge + } + return "" +} + +func (x *Event) GetDeletedAt() *timestamppb.Timestamp { + if x != nil { + return x.DeletedAt + } + return nil +} + +func (x *Event) GetRelatedPodName() string { + if x != nil && x.RelatedPodName != nil { + return *x.RelatedPodName + } + return "" +} + +type EventDatapointInfo struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` // Event name + Message string `protobuf:"bytes,2,opt,name=message,proto3" json:"message,omitempty"` // Event message + OwnerUid string `protobuf:"bytes,3,opt,name=owner_uid,json=ownerUid,proto3" json:"owner_uid,omitempty"` // Event owner name + OwnerKind string `protobuf:"bytes,4,opt,name=owner_kind,json=ownerKind,proto3" json:"owner_kind,omitempty"` // Event owner kind + OwnerName string `protobuf:"bytes,5,opt,name=owner_name,json=ownerName,proto3" json:"owner_name,omitempty"` // Event owner name + Type string `protobuf:"bytes,6,opt,name=type,proto3" json:"type,omitempty"` // Event type + Reason string `protobuf:"bytes,7,opt,name=reason,proto3" json:"reason,omitempty"` // Event reason +} + +func (x *EventDatapointInfo) Reset() { + *x = EventDatapointInfo{} + if protoimpl.UnsafeEnabled { + mi := &file_api_v1_common_proto_msgTypes[163] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *EventDatapointInfo) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*EventDatapointInfo) ProtoMessage() {} + +func (x *EventDatapointInfo) ProtoReflect() protoreflect.Message { + mi := &file_api_v1_common_proto_msgTypes[163] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use EventDatapointInfo.ProtoReflect.Descriptor instead. +func (*EventDatapointInfo) Descriptor() ([]byte, []int) { + return file_api_v1_common_proto_rawDescGZIP(), []int{163} +} + +func (x *EventDatapointInfo) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *EventDatapointInfo) GetMessage() string { + if x != nil { + return x.Message + } + return "" +} + +func (x *EventDatapointInfo) GetOwnerUid() string { + if x != nil { + return x.OwnerUid + } + return "" +} + +func (x *EventDatapointInfo) GetOwnerKind() string { + if x != nil { + return x.OwnerKind + } + return "" +} + +func (x *EventDatapointInfo) GetOwnerName() string { + if x != nil { + return x.OwnerName + } + return "" +} + +func (x *EventDatapointInfo) GetType() string { + if x != nil { + return x.Type + } + return "" +} + +func (x *EventDatapointInfo) GetReason() string { + if x != nil { + return x.Reason + } + return "" +} + +type EventDatapoint struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + UtcTime string `protobuf:"bytes,1,opt,name=utc_time,json=utcTime,proto3" json:"utc_time,omitempty"` + Count int32 `protobuf:"varint,2,opt,name=count,proto3" json:"count,omitempty"` // Number of events in this time bucket + Events []*EventDatapointInfo `protobuf:"bytes,3,rep,name=events,proto3" json:"events,omitempty"` // Details of events in this bucket (deprecated: use normal_count/warning_count) + NormalCount int32 `protobuf:"varint,4,opt,name=normal_count,json=normalCount,proto3" json:"normal_count,omitempty"` // Count of Normal events in this time bucket + WarningCount int32 `protobuf:"varint,5,opt,name=warning_count,json=warningCount,proto3" json:"warning_count,omitempty"` // Count of Warning events in this time bucket + CriticalCount int32 `protobuf:"varint,6,opt,name=critical_count,json=criticalCount,proto3" json:"critical_count,omitempty"` // Count of events with critical reasons (OOMKilled, CrashLoopBackOff, NodeNotReady, etc.) +} + +func (x *EventDatapoint) Reset() { + *x = EventDatapoint{} + if protoimpl.UnsafeEnabled { + mi := &file_api_v1_common_proto_msgTypes[164] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *EventDatapoint) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*EventDatapoint) ProtoMessage() {} + +func (x *EventDatapoint) ProtoReflect() protoreflect.Message { + mi := &file_api_v1_common_proto_msgTypes[164] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use EventDatapoint.ProtoReflect.Descriptor instead. +func (*EventDatapoint) Descriptor() ([]byte, []int) { + return file_api_v1_common_proto_rawDescGZIP(), []int{164} +} + +func (x *EventDatapoint) GetUtcTime() string { + if x != nil { + return x.UtcTime + } + return "" +} + +func (x *EventDatapoint) GetCount() int32 { + if x != nil { + return x.Count + } + return 0 +} + +func (x *EventDatapoint) GetEvents() []*EventDatapointInfo { + if x != nil { + return x.Events + } + return nil +} + +func (x *EventDatapoint) GetNormalCount() int32 { + if x != nil { + return x.NormalCount + } + return 0 +} + +func (x *EventDatapoint) GetWarningCount() int32 { + if x != nil { + return x.WarningCount + } + return 0 +} + +func (x *EventDatapoint) GetCriticalCount() int32 { + if x != nil { + return x.CriticalCount + } + return 0 +} + +type DimensionCount struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Value string `protobuf:"bytes,1,opt,name=value,proto3" json:"value,omitempty"` + Count int32 `protobuf:"varint,2,opt,name=count,proto3" json:"count,omitempty"` + Percentage float64 `protobuf:"fixed64,3,opt,name=percentage,proto3" json:"percentage,omitempty"` +} + +func (x *DimensionCount) Reset() { + *x = DimensionCount{} + if protoimpl.UnsafeEnabled { + mi := &file_api_v1_common_proto_msgTypes[165] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *DimensionCount) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*DimensionCount) ProtoMessage() {} + +func (x *DimensionCount) ProtoReflect() protoreflect.Message { + mi := &file_api_v1_common_proto_msgTypes[165] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use DimensionCount.ProtoReflect.Descriptor instead. +func (*DimensionCount) Descriptor() ([]byte, []int) { + return file_api_v1_common_proto_rawDescGZIP(), []int{165} +} + +func (x *DimensionCount) GetValue() string { + if x != nil { + return x.Value + } + return "" +} + +func (x *DimensionCount) GetCount() int32 { + if x != nil { + return x.Count + } + return 0 +} + +func (x *DimensionCount) GetPercentage() float64 { + if x != nil { + return x.Percentage + } + return 0 +} + +type EventGroup struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Reason string `protobuf:"bytes,1,opt,name=reason,proto3" json:"reason,omitempty"` + Namespace string `protobuf:"bytes,2,opt,name=namespace,proto3" json:"namespace,omitempty"` + WorkloadName string `protobuf:"bytes,3,opt,name=workload_name,json=workloadName,proto3" json:"workload_name,omitempty"` + WorkloadKind string `protobuf:"bytes,4,opt,name=workload_kind,json=workloadKind,proto3" json:"workload_kind,omitempty"` + Node string `protobuf:"bytes,5,opt,name=node,proto3" json:"node,omitempty"` + Count int32 `protobuf:"varint,6,opt,name=count,proto3" json:"count,omitempty"` + AffectedObjects int32 `protobuf:"varint,7,opt,name=affected_objects,json=affectedObjects,proto3" json:"affected_objects,omitempty"` + FirstSeen *timestamppb.Timestamp `protobuf:"bytes,8,opt,name=first_seen,json=firstSeen,proto3" json:"first_seen,omitempty"` + LastSeen *timestamppb.Timestamp `protobuf:"bytes,9,opt,name=last_seen,json=lastSeen,proto3" json:"last_seen,omitempty"` + Type string `protobuf:"bytes,10,opt,name=type,proto3" json:"type,omitempty"` +} + +func (x *EventGroup) Reset() { + *x = EventGroup{} + if protoimpl.UnsafeEnabled { + mi := &file_api_v1_common_proto_msgTypes[166] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *EventGroup) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*EventGroup) ProtoMessage() {} + +func (x *EventGroup) ProtoReflect() protoreflect.Message { + mi := &file_api_v1_common_proto_msgTypes[166] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use EventGroup.ProtoReflect.Descriptor instead. +func (*EventGroup) Descriptor() ([]byte, []int) { + return file_api_v1_common_proto_rawDescGZIP(), []int{166} +} + +func (x *EventGroup) GetReason() string { + if x != nil { + return x.Reason + } + return "" +} + +func (x *EventGroup) GetNamespace() string { + if x != nil { + return x.Namespace + } + return "" +} + +func (x *EventGroup) GetWorkloadName() string { + if x != nil { + return x.WorkloadName + } + return "" +} + +func (x *EventGroup) GetWorkloadKind() string { + if x != nil { + return x.WorkloadKind + } + return "" +} + +func (x *EventGroup) GetNode() string { + if x != nil { + return x.Node + } + return "" +} + +func (x *EventGroup) GetCount() int32 { + if x != nil { + return x.Count + } + return 0 +} + +func (x *EventGroup) GetAffectedObjects() int32 { + if x != nil { + return x.AffectedObjects + } + return 0 +} + +func (x *EventGroup) GetFirstSeen() *timestamppb.Timestamp { + if x != nil { + return x.FirstSeen + } + return nil +} + +func (x *EventGroup) GetLastSeen() *timestamppb.Timestamp { + if x != nil { + return x.LastSeen + } + return nil +} + +func (x *EventGroup) GetType() string { + if x != nil { + return x.Type + } + return "" +} + +var File_api_v1_common_proto protoreflect.FileDescriptor + +var file_api_v1_common_proto_rawDesc = []byte{ 0x0a, 0x13, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x06, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x1a, 0x1f, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x74, @@ -15777,7 +20209,7 @@ var file_api_v1_common_proto_rawDesc = []byte{ 0x73, 0x12, 0x2f, 0x0a, 0x13, 0x6f, 0x76, 0x65, 0x72, 0x72, 0x69, 0x64, 0x65, 0x5f, 0x70, 0x61, 0x67, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x07, 0x20, 0x01, 0x28, 0x08, 0x52, 0x12, 0x6f, 0x76, 0x65, 0x72, 0x72, 0x69, 0x64, 0x65, 0x50, 0x61, 0x67, 0x69, 0x6e, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x22, 0xe1, 0x1b, 0x0a, 0x08, 0x43, 0x6f, 0x73, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x12, + 0x6f, 0x6e, 0x22, 0xe2, 0x1d, 0x0a, 0x08, 0x43, 0x6f, 0x73, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x2d, 0x0a, 0x11, 0x63, 0x70, 0x75, 0x5f, 0x63, 0x6f, 0x73, 0x74, 0x5f, 0x70, 0x65, 0x72, 0x5f, 0x68, 0x6f, 0x75, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x01, 0x42, 0x02, 0x18, 0x01, 0x52, 0x0e, 0x63, 0x70, 0x75, 0x43, 0x6f, 0x73, 0x74, 0x50, 0x65, 0x72, 0x48, 0x6f, 0x75, 0x72, 0x12, 0x33, @@ -15999,2286 +20431,2510 @@ var file_api_v1_common_proto_rawDesc = []byte{ 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x2e, 0x4d, 0x6f, 0x6e, 0x65, 0x79, 0x52, 0x1c, 0x6d, 0x6f, 0x6e, 0x65, 0x79, 0x4d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x43, 0x6f, 0x73, 0x74, 0x50, 0x65, 0x72, 0x47, 0x69, 0x62, 0x50, 0x65, 0x72, 0x48, 0x6f, 0x75, 0x72, - 0x4a, 0x04, 0x08, 0x17, 0x10, 0x18, 0x22, 0xf7, 0x1d, 0x0a, 0x0f, 0x52, 0x65, 0x73, 0x6f, 0x75, - 0x72, 0x63, 0x65, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x12, 0x2a, 0x0a, 0x11, 0x6e, 0x6f, - 0x64, 0x65, 0x5f, 0x63, 0x70, 0x75, 0x5f, 0x63, 0x61, 0x70, 0x61, 0x63, 0x69, 0x74, 0x79, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x01, 0x52, 0x0f, 0x6e, 0x6f, 0x64, 0x65, 0x43, 0x70, 0x75, 0x43, 0x61, - 0x70, 0x61, 0x63, 0x69, 0x74, 0x79, 0x12, 0x30, 0x0a, 0x14, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x6d, - 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x5f, 0x63, 0x61, 0x70, 0x61, 0x63, 0x69, 0x74, 0x79, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x01, 0x52, 0x12, 0x6e, 0x6f, 0x64, 0x65, 0x4d, 0x65, 0x6d, 0x6f, 0x72, 0x79, - 0x43, 0x61, 0x70, 0x61, 0x63, 0x69, 0x74, 0x79, 0x12, 0x24, 0x0a, 0x0e, 0x6e, 0x6f, 0x64, 0x65, - 0x5f, 0x63, 0x70, 0x75, 0x5f, 0x75, 0x73, 0x61, 0x67, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x01, - 0x52, 0x0c, 0x6e, 0x6f, 0x64, 0x65, 0x43, 0x70, 0x75, 0x55, 0x73, 0x61, 0x67, 0x65, 0x12, 0x2a, - 0x0a, 0x11, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x5f, 0x75, 0x73, - 0x61, 0x67, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x01, 0x52, 0x0f, 0x6e, 0x6f, 0x64, 0x65, 0x4d, - 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x55, 0x73, 0x61, 0x67, 0x65, 0x12, 0x30, 0x0a, 0x14, 0x6e, 0x6f, - 0x64, 0x65, 0x5f, 0x63, 0x70, 0x75, 0x5f, 0x75, 0x74, 0x69, 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x01, 0x52, 0x12, 0x6e, 0x6f, 0x64, 0x65, 0x43, 0x70, - 0x75, 0x55, 0x74, 0x69, 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x36, 0x0a, 0x17, - 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x5f, 0x75, 0x74, 0x69, 0x6c, - 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, 0x01, 0x52, 0x15, 0x6e, - 0x6f, 0x64, 0x65, 0x4d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x55, 0x74, 0x69, 0x6c, 0x69, 0x7a, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x2e, 0x0a, 0x13, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, - 0x72, 0x5f, 0x63, 0x70, 0x75, 0x5f, 0x75, 0x73, 0x61, 0x67, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, - 0x01, 0x52, 0x11, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x43, 0x70, 0x75, 0x55, - 0x73, 0x61, 0x67, 0x65, 0x12, 0x34, 0x0a, 0x16, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, - 0x72, 0x5f, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x5f, 0x75, 0x73, 0x61, 0x67, 0x65, 0x18, 0x08, - 0x20, 0x01, 0x28, 0x01, 0x52, 0x14, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x4d, - 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x55, 0x73, 0x61, 0x67, 0x65, 0x12, 0x3a, 0x0a, 0x19, 0x63, 0x6f, - 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x63, 0x70, 0x75, 0x5f, 0x75, 0x74, 0x69, 0x6c, - 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x09, 0x20, 0x01, 0x28, 0x01, 0x52, 0x17, 0x63, - 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x43, 0x70, 0x75, 0x55, 0x74, 0x69, 0x6c, 0x69, - 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x40, 0x0a, 0x1c, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, - 0x6e, 0x65, 0x72, 0x5f, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x5f, 0x75, 0x74, 0x69, 0x6c, 0x69, - 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x01, 0x52, 0x1a, 0x63, 0x6f, - 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x4d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x55, 0x74, 0x69, - 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x36, 0x0a, 0x17, 0x63, 0x6f, 0x6e, 0x74, - 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x63, 0x70, 0x75, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x65, 0x64, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x01, 0x52, 0x15, 0x63, 0x6f, 0x6e, 0x74, 0x61, - 0x69, 0x6e, 0x65, 0x72, 0x43, 0x70, 0x75, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x65, 0x64, - 0x12, 0x3c, 0x0a, 0x1a, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x6d, 0x65, - 0x6d, 0x6f, 0x72, 0x79, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x65, 0x64, 0x18, 0x0c, - 0x20, 0x01, 0x28, 0x01, 0x52, 0x18, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x4d, - 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x65, 0x64, 0x12, 0x30, - 0x0a, 0x14, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x63, 0x70, 0x75, 0x5f, - 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x73, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x01, 0x52, 0x12, 0x63, 0x6f, - 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x43, 0x70, 0x75, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x73, - 0x12, 0x36, 0x0a, 0x17, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x6d, 0x65, - 0x6d, 0x6f, 0x72, 0x79, 0x5f, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x73, 0x18, 0x0e, 0x20, 0x01, 0x28, - 0x01, 0x52, 0x15, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x4d, 0x65, 0x6d, 0x6f, - 0x72, 0x79, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x73, 0x12, 0x2e, 0x0a, 0x13, 0x63, 0x6f, 0x6e, 0x74, - 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x67, 0x70, 0x75, 0x5f, 0x75, 0x73, 0x61, 0x67, 0x65, 0x18, - 0x0f, 0x20, 0x01, 0x28, 0x01, 0x52, 0x11, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, - 0x47, 0x70, 0x75, 0x55, 0x73, 0x61, 0x67, 0x65, 0x12, 0x36, 0x0a, 0x17, 0x63, 0x6f, 0x6e, 0x74, - 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x67, 0x70, 0x75, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x65, 0x64, 0x18, 0x10, 0x20, 0x01, 0x28, 0x01, 0x52, 0x15, 0x63, 0x6f, 0x6e, 0x74, 0x61, - 0x69, 0x6e, 0x65, 0x72, 0x47, 0x70, 0x75, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x65, 0x64, - 0x12, 0x30, 0x0a, 0x14, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x67, 0x70, - 0x75, 0x5f, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x73, 0x18, 0x11, 0x20, 0x01, 0x28, 0x01, 0x52, 0x12, - 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x47, 0x70, 0x75, 0x4c, 0x69, 0x6d, 0x69, - 0x74, 0x73, 0x12, 0x3a, 0x0a, 0x19, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, - 0x67, 0x70, 0x75, 0x5f, 0x75, 0x74, 0x69, 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, - 0x12, 0x20, 0x01, 0x28, 0x01, 0x52, 0x17, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, - 0x47, 0x70, 0x75, 0x55, 0x74, 0x69, 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x2a, - 0x0a, 0x11, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x67, 0x70, 0x75, 0x5f, 0x63, 0x61, 0x70, 0x61, 0x63, - 0x69, 0x74, 0x79, 0x18, 0x13, 0x20, 0x01, 0x28, 0x01, 0x52, 0x0f, 0x6e, 0x6f, 0x64, 0x65, 0x47, - 0x70, 0x75, 0x43, 0x61, 0x70, 0x61, 0x63, 0x69, 0x74, 0x79, 0x12, 0x24, 0x0a, 0x0e, 0x6e, 0x6f, - 0x64, 0x65, 0x5f, 0x67, 0x70, 0x75, 0x5f, 0x75, 0x73, 0x61, 0x67, 0x65, 0x18, 0x14, 0x20, 0x01, - 0x28, 0x01, 0x52, 0x0c, 0x6e, 0x6f, 0x64, 0x65, 0x47, 0x70, 0x75, 0x55, 0x73, 0x61, 0x67, 0x65, - 0x12, 0x30, 0x0a, 0x14, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x67, 0x70, 0x75, 0x5f, 0x75, 0x74, 0x69, - 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x15, 0x20, 0x01, 0x28, 0x01, 0x52, 0x12, - 0x6e, 0x6f, 0x64, 0x65, 0x47, 0x70, 0x75, 0x55, 0x74, 0x69, 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x12, 0x2d, 0x0a, 0x13, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x67, 0x70, 0x75, 0x5f, 0x76, - 0x72, 0x61, 0x6d, 0x5f, 0x75, 0x73, 0x61, 0x67, 0x65, 0x18, 0x16, 0x20, 0x01, 0x28, 0x01, 0x52, - 0x10, 0x6e, 0x6f, 0x64, 0x65, 0x47, 0x70, 0x75, 0x56, 0x72, 0x61, 0x6d, 0x55, 0x73, 0x61, 0x67, - 0x65, 0x12, 0x37, 0x0a, 0x18, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x67, - 0x70, 0x75, 0x5f, 0x76, 0x72, 0x61, 0x6d, 0x5f, 0x75, 0x73, 0x61, 0x67, 0x65, 0x18, 0x17, 0x20, - 0x01, 0x28, 0x01, 0x52, 0x15, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x47, 0x70, - 0x75, 0x56, 0x72, 0x61, 0x6d, 0x55, 0x73, 0x61, 0x67, 0x65, 0x12, 0x33, 0x0a, 0x16, 0x6e, 0x6f, - 0x64, 0x65, 0x5f, 0x67, 0x70, 0x75, 0x5f, 0x76, 0x72, 0x61, 0x6d, 0x5f, 0x63, 0x61, 0x70, 0x61, - 0x63, 0x69, 0x74, 0x79, 0x18, 0x18, 0x20, 0x01, 0x28, 0x01, 0x52, 0x13, 0x6e, 0x6f, 0x64, 0x65, - 0x47, 0x70, 0x75, 0x56, 0x72, 0x61, 0x6d, 0x43, 0x61, 0x70, 0x61, 0x63, 0x69, 0x74, 0x79, 0x12, - 0x39, 0x0a, 0x19, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x67, 0x70, 0x75, 0x5f, 0x76, 0x72, 0x61, 0x6d, - 0x5f, 0x75, 0x74, 0x69, 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x19, 0x20, 0x01, - 0x28, 0x01, 0x52, 0x16, 0x6e, 0x6f, 0x64, 0x65, 0x47, 0x70, 0x75, 0x56, 0x72, 0x61, 0x6d, 0x55, - 0x74, 0x69, 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x45, 0x0a, 0x1f, 0x6e, 0x6f, + 0x12, 0x4a, 0x0a, 0x18, 0x6d, 0x6f, 0x6e, 0x65, 0x79, 0x5f, 0x6d, 0x6f, 0x6e, 0x74, 0x68, 0x5f, + 0x74, 0x6f, 0x5f, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x63, 0x6f, 0x73, 0x74, 0x18, 0x7b, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x74, 0x79, 0x70, 0x65, + 0x2e, 0x4d, 0x6f, 0x6e, 0x65, 0x79, 0x52, 0x14, 0x6d, 0x6f, 0x6e, 0x65, 0x79, 0x4d, 0x6f, 0x6e, + 0x74, 0x68, 0x54, 0x6f, 0x44, 0x61, 0x74, 0x65, 0x43, 0x6f, 0x73, 0x74, 0x12, 0x4f, 0x0a, 0x1a, + 0x6d, 0x6f, 0x6e, 0x65, 0x79, 0x5f, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x65, 0x64, 0x5f, + 0x6d, 0x6f, 0x6e, 0x74, 0x68, 0x5f, 0x63, 0x6f, 0x73, 0x74, 0x18, 0x7c, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x12, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x2e, 0x4d, + 0x6f, 0x6e, 0x65, 0x79, 0x52, 0x17, 0x6d, 0x6f, 0x6e, 0x65, 0x79, 0x50, 0x72, 0x6f, 0x6a, 0x65, + 0x63, 0x74, 0x65, 0x64, 0x4d, 0x6f, 0x6e, 0x74, 0x68, 0x43, 0x6f, 0x73, 0x74, 0x12, 0x62, 0x0a, + 0x24, 0x6d, 0x6f, 0x6e, 0x65, 0x79, 0x5f, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x69, 0x7a, 0x65, 0x64, + 0x5f, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x65, 0x64, 0x5f, 0x6d, 0x6f, 0x6e, 0x74, 0x68, + 0x5f, 0x63, 0x6f, 0x73, 0x74, 0x18, 0x7d, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x67, 0x6f, + 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x2e, 0x4d, 0x6f, 0x6e, 0x65, 0x79, 0x52, + 0x20, 0x6d, 0x6f, 0x6e, 0x65, 0x79, 0x4f, 0x70, 0x74, 0x69, 0x6d, 0x69, 0x7a, 0x65, 0x64, 0x50, + 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x65, 0x64, 0x4d, 0x6f, 0x6e, 0x74, 0x68, 0x43, 0x6f, 0x73, + 0x74, 0x4a, 0x04, 0x08, 0x17, 0x10, 0x18, 0x22, 0xbd, 0x24, 0x0a, 0x0f, 0x52, 0x65, 0x73, 0x6f, + 0x75, 0x72, 0x63, 0x65, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x12, 0x2a, 0x0a, 0x11, 0x6e, + 0x6f, 0x64, 0x65, 0x5f, 0x63, 0x70, 0x75, 0x5f, 0x63, 0x61, 0x70, 0x61, 0x63, 0x69, 0x74, 0x79, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x01, 0x52, 0x0f, 0x6e, 0x6f, 0x64, 0x65, 0x43, 0x70, 0x75, 0x43, + 0x61, 0x70, 0x61, 0x63, 0x69, 0x74, 0x79, 0x12, 0x30, 0x0a, 0x14, 0x6e, 0x6f, 0x64, 0x65, 0x5f, + 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x5f, 0x63, 0x61, 0x70, 0x61, 0x63, 0x69, 0x74, 0x79, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x01, 0x52, 0x12, 0x6e, 0x6f, 0x64, 0x65, 0x4d, 0x65, 0x6d, 0x6f, 0x72, + 0x79, 0x43, 0x61, 0x70, 0x61, 0x63, 0x69, 0x74, 0x79, 0x12, 0x24, 0x0a, 0x0e, 0x6e, 0x6f, 0x64, + 0x65, 0x5f, 0x63, 0x70, 0x75, 0x5f, 0x75, 0x73, 0x61, 0x67, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x01, 0x52, 0x0c, 0x6e, 0x6f, 0x64, 0x65, 0x43, 0x70, 0x75, 0x55, 0x73, 0x61, 0x67, 0x65, 0x12, + 0x2a, 0x0a, 0x11, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x5f, 0x75, + 0x73, 0x61, 0x67, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x01, 0x52, 0x0f, 0x6e, 0x6f, 0x64, 0x65, + 0x4d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x55, 0x73, 0x61, 0x67, 0x65, 0x12, 0x30, 0x0a, 0x14, 0x6e, + 0x6f, 0x64, 0x65, 0x5f, 0x63, 0x70, 0x75, 0x5f, 0x75, 0x74, 0x69, 0x6c, 0x69, 0x7a, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x01, 0x52, 0x12, 0x6e, 0x6f, 0x64, 0x65, 0x43, + 0x70, 0x75, 0x55, 0x74, 0x69, 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x36, 0x0a, + 0x17, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x5f, 0x75, 0x74, 0x69, + 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, 0x01, 0x52, 0x15, + 0x6e, 0x6f, 0x64, 0x65, 0x4d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x55, 0x74, 0x69, 0x6c, 0x69, 0x7a, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x2e, 0x0a, 0x13, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, + 0x65, 0x72, 0x5f, 0x63, 0x70, 0x75, 0x5f, 0x75, 0x73, 0x61, 0x67, 0x65, 0x18, 0x07, 0x20, 0x01, + 0x28, 0x01, 0x52, 0x11, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x43, 0x70, 0x75, + 0x55, 0x73, 0x61, 0x67, 0x65, 0x12, 0x34, 0x0a, 0x16, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, + 0x65, 0x72, 0x5f, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x5f, 0x75, 0x73, 0x61, 0x67, 0x65, 0x18, + 0x08, 0x20, 0x01, 0x28, 0x01, 0x52, 0x14, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, + 0x4d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x55, 0x73, 0x61, 0x67, 0x65, 0x12, 0x3a, 0x0a, 0x19, 0x63, + 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x63, 0x70, 0x75, 0x5f, 0x75, 0x74, 0x69, + 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x09, 0x20, 0x01, 0x28, 0x01, 0x52, 0x17, + 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x43, 0x70, 0x75, 0x55, 0x74, 0x69, 0x6c, + 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x40, 0x0a, 0x1c, 0x63, 0x6f, 0x6e, 0x74, 0x61, + 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x5f, 0x75, 0x74, 0x69, 0x6c, + 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x01, 0x52, 0x1a, 0x63, + 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x4d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x55, 0x74, + 0x69, 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x36, 0x0a, 0x17, 0x63, 0x6f, 0x6e, + 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x63, 0x70, 0x75, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x65, 0x64, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x01, 0x52, 0x15, 0x63, 0x6f, 0x6e, 0x74, + 0x61, 0x69, 0x6e, 0x65, 0x72, 0x43, 0x70, 0x75, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x65, + 0x64, 0x12, 0x3c, 0x0a, 0x1a, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x6d, + 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x65, 0x64, 0x18, + 0x0c, 0x20, 0x01, 0x28, 0x01, 0x52, 0x18, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, + 0x4d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x65, 0x64, 0x12, + 0x30, 0x0a, 0x14, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x63, 0x70, 0x75, + 0x5f, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x73, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x01, 0x52, 0x12, 0x63, + 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x43, 0x70, 0x75, 0x4c, 0x69, 0x6d, 0x69, 0x74, + 0x73, 0x12, 0x36, 0x0a, 0x17, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x6d, + 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x5f, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x73, 0x18, 0x0e, 0x20, 0x01, + 0x28, 0x01, 0x52, 0x15, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x4d, 0x65, 0x6d, + 0x6f, 0x72, 0x79, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x73, 0x12, 0x2e, 0x0a, 0x13, 0x63, 0x6f, 0x6e, + 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x67, 0x70, 0x75, 0x5f, 0x75, 0x73, 0x61, 0x67, 0x65, + 0x18, 0x0f, 0x20, 0x01, 0x28, 0x01, 0x52, 0x11, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, + 0x72, 0x47, 0x70, 0x75, 0x55, 0x73, 0x61, 0x67, 0x65, 0x12, 0x36, 0x0a, 0x17, 0x63, 0x6f, 0x6e, + 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x67, 0x70, 0x75, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x65, 0x64, 0x18, 0x10, 0x20, 0x01, 0x28, 0x01, 0x52, 0x15, 0x63, 0x6f, 0x6e, 0x74, + 0x61, 0x69, 0x6e, 0x65, 0x72, 0x47, 0x70, 0x75, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x65, + 0x64, 0x12, 0x30, 0x0a, 0x14, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x67, + 0x70, 0x75, 0x5f, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x73, 0x18, 0x11, 0x20, 0x01, 0x28, 0x01, 0x52, + 0x12, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x47, 0x70, 0x75, 0x4c, 0x69, 0x6d, + 0x69, 0x74, 0x73, 0x12, 0x3a, 0x0a, 0x19, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, + 0x5f, 0x67, 0x70, 0x75, 0x5f, 0x75, 0x74, 0x69, 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x18, 0x12, 0x20, 0x01, 0x28, 0x01, 0x52, 0x17, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, + 0x72, 0x47, 0x70, 0x75, 0x55, 0x74, 0x69, 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, + 0x2a, 0x0a, 0x11, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x67, 0x70, 0x75, 0x5f, 0x63, 0x61, 0x70, 0x61, + 0x63, 0x69, 0x74, 0x79, 0x18, 0x13, 0x20, 0x01, 0x28, 0x01, 0x52, 0x0f, 0x6e, 0x6f, 0x64, 0x65, + 0x47, 0x70, 0x75, 0x43, 0x61, 0x70, 0x61, 0x63, 0x69, 0x74, 0x79, 0x12, 0x24, 0x0a, 0x0e, 0x6e, + 0x6f, 0x64, 0x65, 0x5f, 0x67, 0x70, 0x75, 0x5f, 0x75, 0x73, 0x61, 0x67, 0x65, 0x18, 0x14, 0x20, + 0x01, 0x28, 0x01, 0x52, 0x0c, 0x6e, 0x6f, 0x64, 0x65, 0x47, 0x70, 0x75, 0x55, 0x73, 0x61, 0x67, + 0x65, 0x12, 0x30, 0x0a, 0x14, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x67, 0x70, 0x75, 0x5f, 0x75, 0x74, + 0x69, 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x15, 0x20, 0x01, 0x28, 0x01, 0x52, + 0x12, 0x6e, 0x6f, 0x64, 0x65, 0x47, 0x70, 0x75, 0x55, 0x74, 0x69, 0x6c, 0x69, 0x7a, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x12, 0x2d, 0x0a, 0x13, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x67, 0x70, 0x75, 0x5f, + 0x76, 0x72, 0x61, 0x6d, 0x5f, 0x75, 0x73, 0x61, 0x67, 0x65, 0x18, 0x16, 0x20, 0x01, 0x28, 0x01, + 0x52, 0x10, 0x6e, 0x6f, 0x64, 0x65, 0x47, 0x70, 0x75, 0x56, 0x72, 0x61, 0x6d, 0x55, 0x73, 0x61, + 0x67, 0x65, 0x12, 0x37, 0x0a, 0x18, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, + 0x67, 0x70, 0x75, 0x5f, 0x76, 0x72, 0x61, 0x6d, 0x5f, 0x75, 0x73, 0x61, 0x67, 0x65, 0x18, 0x17, + 0x20, 0x01, 0x28, 0x01, 0x52, 0x15, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x47, + 0x70, 0x75, 0x56, 0x72, 0x61, 0x6d, 0x55, 0x73, 0x61, 0x67, 0x65, 0x12, 0x33, 0x0a, 0x16, 0x6e, + 0x6f, 0x64, 0x65, 0x5f, 0x67, 0x70, 0x75, 0x5f, 0x76, 0x72, 0x61, 0x6d, 0x5f, 0x63, 0x61, 0x70, + 0x61, 0x63, 0x69, 0x74, 0x79, 0x18, 0x18, 0x20, 0x01, 0x28, 0x01, 0x52, 0x13, 0x6e, 0x6f, 0x64, + 0x65, 0x47, 0x70, 0x75, 0x56, 0x72, 0x61, 0x6d, 0x43, 0x61, 0x70, 0x61, 0x63, 0x69, 0x74, 0x79, + 0x12, 0x39, 0x0a, 0x19, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x67, 0x70, 0x75, 0x5f, 0x76, 0x72, 0x61, + 0x6d, 0x5f, 0x75, 0x74, 0x69, 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x19, 0x20, + 0x01, 0x28, 0x01, 0x52, 0x16, 0x6e, 0x6f, 0x64, 0x65, 0x47, 0x70, 0x75, 0x56, 0x72, 0x61, 0x6d, + 0x55, 0x74, 0x69, 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x45, 0x0a, 0x1f, 0x6e, + 0x6f, 0x72, 0x6d, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x5f, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x6d, + 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x5f, 0x63, 0x61, 0x70, 0x61, 0x63, 0x69, 0x74, 0x79, 0x18, 0x1a, + 0x20, 0x01, 0x28, 0x01, 0x52, 0x1c, 0x6e, 0x6f, 0x72, 0x6d, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x64, + 0x4e, 0x6f, 0x64, 0x65, 0x4d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x43, 0x61, 0x70, 0x61, 0x63, 0x69, + 0x74, 0x79, 0x12, 0x3f, 0x0a, 0x1c, 0x6e, 0x6f, 0x72, 0x6d, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x64, + 0x5f, 0x67, 0x70, 0x75, 0x5f, 0x76, 0x72, 0x61, 0x6d, 0x5f, 0x63, 0x61, 0x70, 0x61, 0x63, 0x69, + 0x74, 0x79, 0x18, 0x1b, 0x20, 0x01, 0x28, 0x01, 0x52, 0x19, 0x6e, 0x6f, 0x72, 0x6d, 0x61, 0x6c, + 0x69, 0x7a, 0x65, 0x64, 0x47, 0x70, 0x75, 0x56, 0x72, 0x61, 0x6d, 0x43, 0x61, 0x70, 0x61, 0x63, + 0x69, 0x74, 0x79, 0x12, 0x41, 0x0a, 0x1b, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x63, 0x70, 0x75, 0x5f, + 0x63, 0x61, 0x70, 0x61, 0x63, 0x69, 0x74, 0x79, 0x5f, 0x69, 0x6e, 0x5f, 0x70, 0x65, 0x72, 0x69, + 0x6f, 0x64, 0x18, 0x1f, 0x20, 0x01, 0x28, 0x01, 0x48, 0x00, 0x52, 0x17, 0x6e, 0x6f, 0x64, 0x65, + 0x43, 0x70, 0x75, 0x43, 0x61, 0x70, 0x61, 0x63, 0x69, 0x74, 0x79, 0x49, 0x6e, 0x50, 0x65, 0x72, + 0x69, 0x6f, 0x64, 0x88, 0x01, 0x01, 0x12, 0x47, 0x0a, 0x1e, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x6d, + 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x5f, 0x63, 0x61, 0x70, 0x61, 0x63, 0x69, 0x74, 0x79, 0x5f, 0x69, + 0x6e, 0x5f, 0x70, 0x65, 0x72, 0x69, 0x6f, 0x64, 0x18, 0x20, 0x20, 0x01, 0x28, 0x01, 0x48, 0x01, + 0x52, 0x1a, 0x6e, 0x6f, 0x64, 0x65, 0x4d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x43, 0x61, 0x70, 0x61, + 0x63, 0x69, 0x74, 0x79, 0x49, 0x6e, 0x50, 0x65, 0x72, 0x69, 0x6f, 0x64, 0x88, 0x01, 0x01, 0x12, + 0x41, 0x0a, 0x1b, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x67, 0x70, 0x75, 0x5f, 0x63, 0x61, 0x70, 0x61, + 0x63, 0x69, 0x74, 0x79, 0x5f, 0x69, 0x6e, 0x5f, 0x70, 0x65, 0x72, 0x69, 0x6f, 0x64, 0x18, 0x21, + 0x20, 0x01, 0x28, 0x01, 0x48, 0x02, 0x52, 0x17, 0x6e, 0x6f, 0x64, 0x65, 0x47, 0x70, 0x75, 0x43, + 0x61, 0x70, 0x61, 0x63, 0x69, 0x74, 0x79, 0x49, 0x6e, 0x50, 0x65, 0x72, 0x69, 0x6f, 0x64, 0x88, + 0x01, 0x01, 0x12, 0x5c, 0x0a, 0x29, 0x6e, 0x6f, 0x72, 0x6d, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x64, + 0x5f, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x5f, 0x63, 0x61, 0x70, + 0x61, 0x63, 0x69, 0x74, 0x79, 0x5f, 0x69, 0x6e, 0x5f, 0x70, 0x65, 0x72, 0x69, 0x6f, 0x64, 0x18, + 0x22, 0x20, 0x01, 0x28, 0x01, 0x48, 0x03, 0x52, 0x24, 0x6e, 0x6f, 0x72, 0x6d, 0x61, 0x6c, 0x69, + 0x7a, 0x65, 0x64, 0x4e, 0x6f, 0x64, 0x65, 0x4d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x43, 0x61, 0x70, + 0x61, 0x63, 0x69, 0x74, 0x79, 0x49, 0x6e, 0x50, 0x65, 0x72, 0x69, 0x6f, 0x64, 0x88, 0x01, 0x01, + 0x12, 0x2a, 0x0a, 0x11, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x6e, 0x65, + 0x74, 0x5f, 0x72, 0x63, 0x76, 0x18, 0x29, 0x20, 0x01, 0x28, 0x01, 0x52, 0x0f, 0x63, 0x6f, 0x6e, + 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x4e, 0x65, 0x74, 0x52, 0x63, 0x76, 0x12, 0x34, 0x0a, 0x16, + 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x6e, 0x65, 0x74, 0x5f, 0x74, 0x72, + 0x61, 0x6e, 0x73, 0x6d, 0x69, 0x74, 0x18, 0x2e, 0x20, 0x01, 0x28, 0x01, 0x52, 0x14, 0x63, 0x6f, + 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x4e, 0x65, 0x74, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x6d, + 0x69, 0x74, 0x12, 0x35, 0x0a, 0x17, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, + 0x6e, 0x65, 0x74, 0x5f, 0x72, 0x63, 0x76, 0x5f, 0x70, 0x63, 0x6b, 0x74, 0x73, 0x18, 0x33, 0x20, + 0x01, 0x28, 0x01, 0x52, 0x14, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x4e, 0x65, + 0x74, 0x52, 0x63, 0x76, 0x50, 0x63, 0x6b, 0x74, 0x73, 0x12, 0x3f, 0x0a, 0x1c, 0x63, 0x6f, 0x6e, + 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x6e, 0x65, 0x74, 0x5f, 0x74, 0x72, 0x61, 0x6e, 0x73, + 0x6d, 0x69, 0x74, 0x5f, 0x70, 0x63, 0x6b, 0x74, 0x73, 0x18, 0x38, 0x20, 0x01, 0x28, 0x01, 0x52, + 0x19, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x4e, 0x65, 0x74, 0x54, 0x72, 0x61, + 0x6e, 0x73, 0x6d, 0x69, 0x74, 0x50, 0x63, 0x6b, 0x74, 0x73, 0x12, 0x35, 0x0a, 0x17, 0x63, 0x6f, + 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x66, 0x73, 0x5f, 0x72, 0x65, 0x61, 0x64, 0x5f, + 0x62, 0x79, 0x74, 0x65, 0x73, 0x18, 0x47, 0x20, 0x01, 0x28, 0x01, 0x52, 0x14, 0x63, 0x6f, 0x6e, + 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x46, 0x73, 0x52, 0x65, 0x61, 0x64, 0x42, 0x79, 0x74, 0x65, + 0x73, 0x12, 0x37, 0x0a, 0x18, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x66, + 0x73, 0x5f, 0x77, 0x72, 0x69, 0x74, 0x65, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x18, 0x4c, 0x20, + 0x01, 0x28, 0x01, 0x52, 0x15, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x46, 0x73, + 0x57, 0x72, 0x69, 0x74, 0x65, 0x42, 0x79, 0x74, 0x65, 0x73, 0x12, 0x2e, 0x0a, 0x13, 0x63, 0x6f, + 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x66, 0x73, 0x5f, 0x77, 0x72, 0x69, 0x74, 0x65, + 0x73, 0x18, 0x56, 0x20, 0x01, 0x28, 0x01, 0x52, 0x11, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, + 0x65, 0x72, 0x46, 0x73, 0x57, 0x72, 0x69, 0x74, 0x65, 0x73, 0x12, 0x2c, 0x0a, 0x12, 0x63, 0x6f, + 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x66, 0x73, 0x5f, 0x72, 0x65, 0x61, 0x64, 0x73, + 0x18, 0x51, 0x20, 0x01, 0x28, 0x01, 0x52, 0x10, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, + 0x72, 0x46, 0x73, 0x52, 0x65, 0x61, 0x64, 0x73, 0x12, 0x20, 0x0a, 0x0c, 0x6e, 0x6f, 0x64, 0x65, + 0x5f, 0x6e, 0x65, 0x74, 0x5f, 0x72, 0x63, 0x76, 0x18, 0x5b, 0x20, 0x01, 0x28, 0x01, 0x52, 0x0a, + 0x6e, 0x6f, 0x64, 0x65, 0x4e, 0x65, 0x74, 0x52, 0x63, 0x76, 0x12, 0x2a, 0x0a, 0x11, 0x6e, 0x6f, + 0x64, 0x65, 0x5f, 0x6e, 0x65, 0x74, 0x5f, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x6d, 0x69, 0x74, 0x18, + 0x5c, 0x20, 0x01, 0x28, 0x01, 0x52, 0x0f, 0x6e, 0x6f, 0x64, 0x65, 0x4e, 0x65, 0x74, 0x54, 0x72, + 0x61, 0x6e, 0x73, 0x6d, 0x69, 0x74, 0x12, 0x2b, 0x0a, 0x12, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x6e, + 0x65, 0x74, 0x5f, 0x72, 0x63, 0x76, 0x5f, 0x70, 0x63, 0x6b, 0x74, 0x73, 0x18, 0x5d, 0x20, 0x01, + 0x28, 0x01, 0x52, 0x0f, 0x6e, 0x6f, 0x64, 0x65, 0x4e, 0x65, 0x74, 0x52, 0x63, 0x76, 0x50, 0x63, + 0x6b, 0x74, 0x73, 0x12, 0x35, 0x0a, 0x17, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x6e, 0x65, 0x74, 0x5f, + 0x74, 0x72, 0x61, 0x6e, 0x73, 0x6d, 0x69, 0x74, 0x5f, 0x70, 0x63, 0x6b, 0x74, 0x73, 0x18, 0x5e, + 0x20, 0x01, 0x28, 0x01, 0x52, 0x14, 0x6e, 0x6f, 0x64, 0x65, 0x4e, 0x65, 0x74, 0x54, 0x72, 0x61, + 0x6e, 0x73, 0x6d, 0x69, 0x74, 0x50, 0x63, 0x6b, 0x74, 0x73, 0x12, 0x2d, 0x0a, 0x13, 0x6e, 0x6f, + 0x64, 0x65, 0x5f, 0x6e, 0x65, 0x74, 0x5f, 0x72, 0x63, 0x76, 0x5f, 0x65, 0x72, 0x72, 0x6f, 0x72, + 0x73, 0x18, 0x5f, 0x20, 0x01, 0x28, 0x01, 0x52, 0x10, 0x6e, 0x6f, 0x64, 0x65, 0x4e, 0x65, 0x74, + 0x52, 0x63, 0x76, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x73, 0x12, 0x37, 0x0a, 0x18, 0x6e, 0x6f, 0x64, + 0x65, 0x5f, 0x6e, 0x65, 0x74, 0x5f, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x6d, 0x69, 0x74, 0x5f, 0x65, + 0x72, 0x72, 0x6f, 0x72, 0x73, 0x18, 0x60, 0x20, 0x01, 0x28, 0x01, 0x52, 0x15, 0x6e, 0x6f, 0x64, + 0x65, 0x4e, 0x65, 0x74, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x6d, 0x69, 0x74, 0x45, 0x72, 0x72, 0x6f, + 0x72, 0x73, 0x12, 0x2f, 0x0a, 0x14, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x6e, 0x65, 0x74, 0x5f, 0x72, + 0x63, 0x76, 0x5f, 0x64, 0x72, 0x6f, 0x70, 0x70, 0x65, 0x64, 0x18, 0x61, 0x20, 0x01, 0x28, 0x01, + 0x52, 0x11, 0x6e, 0x6f, 0x64, 0x65, 0x4e, 0x65, 0x74, 0x52, 0x63, 0x76, 0x44, 0x72, 0x6f, 0x70, + 0x70, 0x65, 0x64, 0x12, 0x39, 0x0a, 0x19, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x6e, 0x65, 0x74, 0x5f, + 0x74, 0x72, 0x61, 0x6e, 0x73, 0x6d, 0x69, 0x74, 0x5f, 0x64, 0x72, 0x6f, 0x70, 0x70, 0x65, 0x64, + 0x18, 0x62, 0x20, 0x01, 0x28, 0x01, 0x52, 0x16, 0x6e, 0x6f, 0x64, 0x65, 0x4e, 0x65, 0x74, 0x54, + 0x72, 0x61, 0x6e, 0x73, 0x6d, 0x69, 0x74, 0x44, 0x72, 0x6f, 0x70, 0x70, 0x65, 0x64, 0x12, 0x2b, + 0x0a, 0x12, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x66, 0x73, 0x5f, 0x72, 0x65, 0x61, 0x64, 0x5f, 0x62, + 0x79, 0x74, 0x65, 0x73, 0x18, 0x65, 0x20, 0x01, 0x28, 0x01, 0x52, 0x0f, 0x6e, 0x6f, 0x64, 0x65, + 0x46, 0x73, 0x52, 0x65, 0x61, 0x64, 0x42, 0x79, 0x74, 0x65, 0x73, 0x12, 0x2d, 0x0a, 0x13, 0x6e, + 0x6f, 0x64, 0x65, 0x5f, 0x66, 0x73, 0x5f, 0x77, 0x72, 0x69, 0x74, 0x65, 0x5f, 0x62, 0x79, 0x74, + 0x65, 0x73, 0x18, 0x66, 0x20, 0x01, 0x28, 0x01, 0x52, 0x10, 0x6e, 0x6f, 0x64, 0x65, 0x46, 0x73, + 0x57, 0x72, 0x69, 0x74, 0x65, 0x42, 0x79, 0x74, 0x65, 0x73, 0x12, 0x22, 0x0a, 0x0d, 0x6e, 0x6f, + 0x64, 0x65, 0x5f, 0x66, 0x73, 0x5f, 0x72, 0x65, 0x61, 0x64, 0x73, 0x18, 0x67, 0x20, 0x01, 0x28, + 0x01, 0x52, 0x0b, 0x6e, 0x6f, 0x64, 0x65, 0x46, 0x73, 0x52, 0x65, 0x61, 0x64, 0x73, 0x12, 0x24, + 0x0a, 0x0e, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x66, 0x73, 0x5f, 0x77, 0x72, 0x69, 0x74, 0x65, 0x73, + 0x18, 0x68, 0x20, 0x01, 0x28, 0x01, 0x52, 0x0c, 0x6e, 0x6f, 0x64, 0x65, 0x46, 0x73, 0x57, 0x72, + 0x69, 0x74, 0x65, 0x73, 0x12, 0x36, 0x0a, 0x17, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x73, 0x74, 0x6f, + 0x72, 0x61, 0x67, 0x65, 0x5f, 0x75, 0x73, 0x65, 0x64, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x18, + 0x87, 0x01, 0x20, 0x01, 0x28, 0x01, 0x52, 0x14, 0x6e, 0x6f, 0x64, 0x65, 0x53, 0x74, 0x6f, 0x72, + 0x61, 0x67, 0x65, 0x55, 0x73, 0x65, 0x64, 0x42, 0x79, 0x74, 0x65, 0x73, 0x12, 0x3e, 0x0a, 0x1b, + 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x5f, 0x63, 0x61, 0x70, + 0x61, 0x63, 0x69, 0x74, 0x79, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x18, 0x88, 0x01, 0x20, 0x01, + 0x28, 0x01, 0x52, 0x18, 0x6e, 0x6f, 0x64, 0x65, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x43, + 0x61, 0x70, 0x61, 0x63, 0x69, 0x74, 0x79, 0x42, 0x79, 0x74, 0x65, 0x73, 0x12, 0x40, 0x0a, 0x1c, + 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x5f, 0x61, 0x76, 0x61, + 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x18, 0x89, 0x01, 0x20, + 0x01, 0x28, 0x01, 0x52, 0x19, 0x6e, 0x6f, 0x64, 0x65, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, + 0x41, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x42, 0x79, 0x74, 0x65, 0x73, 0x12, 0x38, + 0x0a, 0x18, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x5f, 0x69, + 0x6e, 0x6f, 0x64, 0x65, 0x73, 0x5f, 0x75, 0x73, 0x65, 0x64, 0x18, 0x8a, 0x01, 0x20, 0x01, 0x28, + 0x01, 0x52, 0x15, 0x6e, 0x6f, 0x64, 0x65, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x49, 0x6e, + 0x6f, 0x64, 0x65, 0x73, 0x55, 0x73, 0x65, 0x64, 0x12, 0x3a, 0x0a, 0x19, 0x6e, 0x6f, 0x64, 0x65, + 0x5f, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x5f, 0x69, 0x6e, 0x6f, 0x64, 0x65, 0x73, 0x5f, + 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x18, 0x8b, 0x01, 0x20, 0x01, 0x28, 0x01, 0x52, 0x16, 0x6e, 0x6f, + 0x64, 0x65, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x49, 0x6e, 0x6f, 0x64, 0x65, 0x73, 0x54, + 0x6f, 0x74, 0x61, 0x6c, 0x12, 0x36, 0x0a, 0x17, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x69, 0x6d, 0x61, + 0x67, 0x65, 0x66, 0x73, 0x5f, 0x75, 0x73, 0x65, 0x64, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x18, + 0x8c, 0x01, 0x20, 0x01, 0x28, 0x01, 0x52, 0x14, 0x6e, 0x6f, 0x64, 0x65, 0x49, 0x6d, 0x61, 0x67, + 0x65, 0x66, 0x73, 0x55, 0x73, 0x65, 0x64, 0x42, 0x79, 0x74, 0x65, 0x73, 0x12, 0x3e, 0x0a, 0x1b, + 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x66, 0x73, 0x5f, 0x63, 0x61, 0x70, + 0x61, 0x63, 0x69, 0x74, 0x79, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x18, 0x8d, 0x01, 0x20, 0x01, + 0x28, 0x01, 0x52, 0x18, 0x6e, 0x6f, 0x64, 0x65, 0x49, 0x6d, 0x61, 0x67, 0x65, 0x66, 0x73, 0x43, + 0x61, 0x70, 0x61, 0x63, 0x69, 0x74, 0x79, 0x42, 0x79, 0x74, 0x65, 0x73, 0x12, 0x40, 0x0a, 0x1c, + 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x66, 0x73, 0x5f, 0x61, 0x76, 0x61, + 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x18, 0x8e, 0x01, 0x20, + 0x01, 0x28, 0x01, 0x52, 0x19, 0x6e, 0x6f, 0x64, 0x65, 0x49, 0x6d, 0x61, 0x67, 0x65, 0x66, 0x73, + 0x41, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x42, 0x79, 0x74, 0x65, 0x73, 0x12, 0x3c, + 0x0a, 0x1a, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x66, 0x73, 0x5f, 0x73, + 0x68, 0x61, 0x72, 0x65, 0x64, 0x5f, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x18, 0x8f, 0x01, 0x20, + 0x01, 0x28, 0x08, 0x52, 0x17, 0x6e, 0x6f, 0x64, 0x65, 0x49, 0x6d, 0x61, 0x67, 0x65, 0x66, 0x73, + 0x53, 0x68, 0x61, 0x72, 0x65, 0x64, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x12, 0x46, 0x0a, 0x1f, + 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x65, 0x70, 0x68, 0x65, 0x6d, 0x65, 0x72, 0x61, 0x6c, 0x5f, 0x73, + 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x5f, 0x63, 0x61, 0x70, 0x61, 0x63, 0x69, 0x74, 0x79, 0x18, + 0x90, 0x01, 0x20, 0x01, 0x28, 0x01, 0x52, 0x1c, 0x6e, 0x6f, 0x64, 0x65, 0x45, 0x70, 0x68, 0x65, + 0x6d, 0x65, 0x72, 0x61, 0x6c, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x43, 0x61, 0x70, 0x61, + 0x63, 0x69, 0x74, 0x79, 0x12, 0x4c, 0x0a, 0x22, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x65, 0x70, 0x68, + 0x65, 0x6d, 0x65, 0x72, 0x61, 0x6c, 0x5f, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x5f, 0x61, + 0x6c, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x18, 0x91, 0x01, 0x20, 0x01, 0x28, + 0x01, 0x52, 0x1f, 0x6e, 0x6f, 0x64, 0x65, 0x45, 0x70, 0x68, 0x65, 0x6d, 0x65, 0x72, 0x61, 0x6c, + 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x41, 0x6c, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x61, 0x62, + 0x6c, 0x65, 0x12, 0x44, 0x0a, 0x1e, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x65, 0x70, 0x68, 0x65, 0x6d, + 0x65, 0x72, 0x61, 0x6c, 0x5f, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x5f, 0x72, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x18, 0x92, 0x01, 0x20, 0x01, 0x28, 0x01, 0x52, 0x1b, 0x6e, 0x6f, 0x64, + 0x65, 0x45, 0x70, 0x68, 0x65, 0x6d, 0x65, 0x72, 0x61, 0x6c, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, + 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x40, 0x0a, 0x1c, 0x6e, 0x6f, 0x64, 0x65, + 0x5f, 0x65, 0x70, 0x68, 0x65, 0x6d, 0x65, 0x72, 0x61, 0x6c, 0x5f, 0x73, 0x74, 0x6f, 0x72, 0x61, + 0x67, 0x65, 0x5f, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x18, 0x93, 0x01, 0x20, 0x01, 0x28, 0x01, 0x52, + 0x19, 0x6e, 0x6f, 0x64, 0x65, 0x45, 0x70, 0x68, 0x65, 0x6d, 0x65, 0x72, 0x61, 0x6c, 0x53, 0x74, + 0x6f, 0x72, 0x61, 0x67, 0x65, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x70, 0x6f, + 0x64, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x6f, 0x20, 0x01, 0x28, 0x05, 0x52, 0x08, 0x70, + 0x6f, 0x64, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x2d, 0x0a, 0x13, 0x6a, 0x76, 0x6d, 0x5f, 0x68, + 0x65, 0x61, 0x70, 0x5f, 0x75, 0x73, 0x65, 0x64, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x18, 0x79, + 0x20, 0x01, 0x28, 0x03, 0x52, 0x10, 0x6a, 0x76, 0x6d, 0x48, 0x65, 0x61, 0x70, 0x55, 0x73, 0x65, + 0x64, 0x42, 0x79, 0x74, 0x65, 0x73, 0x12, 0x2b, 0x0a, 0x12, 0x6a, 0x76, 0x6d, 0x5f, 0x68, 0x65, + 0x61, 0x70, 0x5f, 0x6d, 0x61, 0x78, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x18, 0x7a, 0x20, 0x01, + 0x28, 0x03, 0x52, 0x0f, 0x6a, 0x76, 0x6d, 0x48, 0x65, 0x61, 0x70, 0x4d, 0x61, 0x78, 0x42, 0x79, + 0x74, 0x65, 0x73, 0x12, 0x3b, 0x0a, 0x1a, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, + 0x5f, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x5f, 0x72, 0x73, 0x73, 0x5f, 0x62, 0x79, 0x74, 0x65, + 0x73, 0x18, 0x7b, 0x20, 0x01, 0x28, 0x03, 0x52, 0x17, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, + 0x65, 0x72, 0x4d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x52, 0x73, 0x73, 0x42, 0x79, 0x74, 0x65, 0x73, + 0x12, 0x3f, 0x0a, 0x1c, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x6d, 0x65, + 0x6d, 0x6f, 0x72, 0x79, 0x5f, 0x63, 0x61, 0x63, 0x68, 0x65, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, + 0x18, 0x7c, 0x20, 0x01, 0x28, 0x03, 0x52, 0x19, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, + 0x72, 0x4d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x43, 0x61, 0x63, 0x68, 0x65, 0x42, 0x79, 0x74, 0x65, + 0x73, 0x12, 0x3d, 0x0a, 0x1b, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x6d, + 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x5f, 0x73, 0x77, 0x61, 0x70, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, + 0x18, 0x7d, 0x20, 0x01, 0x28, 0x03, 0x52, 0x18, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, + 0x72, 0x4d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x53, 0x77, 0x61, 0x70, 0x42, 0x79, 0x74, 0x65, 0x73, + 0x12, 0x4c, 0x0a, 0x23, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x6d, 0x65, + 0x6d, 0x6f, 0x72, 0x79, 0x5f, 0x61, 0x63, 0x74, 0x69, 0x76, 0x65, 0x5f, 0x63, 0x61, 0x63, 0x68, + 0x65, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x18, 0x7e, 0x20, 0x01, 0x28, 0x03, 0x52, 0x1f, 0x63, + 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x4d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x41, 0x63, + 0x74, 0x69, 0x76, 0x65, 0x43, 0x61, 0x63, 0x68, 0x65, 0x42, 0x79, 0x74, 0x65, 0x73, 0x12, 0x50, + 0x0a, 0x25, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x6d, 0x65, 0x6d, 0x6f, + 0x72, 0x79, 0x5f, 0x69, 0x6e, 0x61, 0x63, 0x74, 0x69, 0x76, 0x65, 0x5f, 0x63, 0x61, 0x63, 0x68, + 0x65, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x18, 0x7f, 0x20, 0x01, 0x28, 0x03, 0x52, 0x21, 0x63, + 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x4d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x49, 0x6e, + 0x61, 0x63, 0x74, 0x69, 0x76, 0x65, 0x43, 0x61, 0x63, 0x68, 0x65, 0x42, 0x79, 0x74, 0x65, 0x73, + 0x12, 0x33, 0x0a, 0x15, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x63, 0x66, + 0x73, 0x5f, 0x70, 0x65, 0x72, 0x69, 0x6f, 0x64, 0x73, 0x18, 0x80, 0x01, 0x20, 0x01, 0x28, 0x03, + 0x52, 0x13, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x43, 0x66, 0x73, 0x50, 0x65, + 0x72, 0x69, 0x6f, 0x64, 0x73, 0x12, 0x46, 0x0a, 0x1f, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, + 0x65, 0x72, 0x5f, 0x63, 0x66, 0x73, 0x5f, 0x74, 0x68, 0x72, 0x6f, 0x74, 0x74, 0x6c, 0x65, 0x64, + 0x5f, 0x70, 0x65, 0x72, 0x69, 0x6f, 0x64, 0x73, 0x18, 0x81, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, + 0x1c, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x43, 0x66, 0x73, 0x54, 0x68, 0x72, + 0x6f, 0x74, 0x74, 0x6c, 0x65, 0x64, 0x50, 0x65, 0x72, 0x69, 0x6f, 0x64, 0x73, 0x12, 0x40, 0x0a, + 0x1c, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x63, 0x66, 0x73, 0x5f, 0x74, + 0x68, 0x72, 0x6f, 0x74, 0x74, 0x6c, 0x65, 0x64, 0x5f, 0x75, 0x73, 0x65, 0x63, 0x18, 0x82, 0x01, + 0x20, 0x01, 0x28, 0x03, 0x52, 0x19, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x43, + 0x66, 0x73, 0x54, 0x68, 0x72, 0x6f, 0x74, 0x74, 0x6c, 0x65, 0x64, 0x55, 0x73, 0x65, 0x63, 0x12, + 0x3e, 0x0a, 0x1b, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x6d, 0x65, 0x6d, + 0x6f, 0x72, 0x79, 0x5f, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x5f, 0x6d, 0x61, 0x78, 0x18, 0x83, + 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x18, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, + 0x4d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x4d, 0x61, 0x78, 0x12, + 0x47, 0x0a, 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x63, 0x70, 0x75, + 0x5f, 0x70, 0x72, 0x65, 0x73, 0x73, 0x75, 0x72, 0x65, 0x5f, 0x73, 0x6f, 0x6d, 0x65, 0x5f, 0x75, + 0x73, 0x65, 0x63, 0x18, 0x84, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x1c, 0x63, 0x6f, 0x6e, 0x74, + 0x61, 0x69, 0x6e, 0x65, 0x72, 0x43, 0x70, 0x75, 0x50, 0x72, 0x65, 0x73, 0x73, 0x75, 0x72, 0x65, + 0x53, 0x6f, 0x6d, 0x65, 0x55, 0x73, 0x65, 0x63, 0x12, 0x4d, 0x0a, 0x23, 0x63, 0x6f, 0x6e, 0x74, + 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x5f, 0x70, 0x72, 0x65, + 0x73, 0x73, 0x75, 0x72, 0x65, 0x5f, 0x73, 0x6f, 0x6d, 0x65, 0x5f, 0x75, 0x73, 0x65, 0x63, 0x18, + 0x85, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x1f, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, + 0x72, 0x4d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x50, 0x72, 0x65, 0x73, 0x73, 0x75, 0x72, 0x65, 0x53, + 0x6f, 0x6d, 0x65, 0x55, 0x73, 0x65, 0x63, 0x12, 0x4d, 0x0a, 0x23, 0x63, 0x6f, 0x6e, 0x74, 0x61, + 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x5f, 0x70, 0x72, 0x65, 0x73, + 0x73, 0x75, 0x72, 0x65, 0x5f, 0x66, 0x75, 0x6c, 0x6c, 0x5f, 0x75, 0x73, 0x65, 0x63, 0x18, 0x86, + 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x1f, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, + 0x4d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x50, 0x72, 0x65, 0x73, 0x73, 0x75, 0x72, 0x65, 0x46, 0x75, + 0x6c, 0x6c, 0x55, 0x73, 0x65, 0x63, 0x42, 0x1e, 0x0a, 0x1c, 0x5f, 0x6e, 0x6f, 0x64, 0x65, 0x5f, + 0x63, 0x70, 0x75, 0x5f, 0x63, 0x61, 0x70, 0x61, 0x63, 0x69, 0x74, 0x79, 0x5f, 0x69, 0x6e, 0x5f, + 0x70, 0x65, 0x72, 0x69, 0x6f, 0x64, 0x42, 0x21, 0x0a, 0x1f, 0x5f, 0x6e, 0x6f, 0x64, 0x65, 0x5f, + 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x5f, 0x63, 0x61, 0x70, 0x61, 0x63, 0x69, 0x74, 0x79, 0x5f, + 0x69, 0x6e, 0x5f, 0x70, 0x65, 0x72, 0x69, 0x6f, 0x64, 0x42, 0x1e, 0x0a, 0x1c, 0x5f, 0x6e, 0x6f, + 0x64, 0x65, 0x5f, 0x67, 0x70, 0x75, 0x5f, 0x63, 0x61, 0x70, 0x61, 0x63, 0x69, 0x74, 0x79, 0x5f, + 0x69, 0x6e, 0x5f, 0x70, 0x65, 0x72, 0x69, 0x6f, 0x64, 0x42, 0x2c, 0x0a, 0x2a, 0x5f, 0x6e, 0x6f, 0x72, 0x6d, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x5f, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x6d, 0x65, - 0x6d, 0x6f, 0x72, 0x79, 0x5f, 0x63, 0x61, 0x70, 0x61, 0x63, 0x69, 0x74, 0x79, 0x18, 0x1a, 0x20, - 0x01, 0x28, 0x01, 0x52, 0x1c, 0x6e, 0x6f, 0x72, 0x6d, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x4e, - 0x6f, 0x64, 0x65, 0x4d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x43, 0x61, 0x70, 0x61, 0x63, 0x69, 0x74, - 0x79, 0x12, 0x3f, 0x0a, 0x1c, 0x6e, 0x6f, 0x72, 0x6d, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x5f, - 0x67, 0x70, 0x75, 0x5f, 0x76, 0x72, 0x61, 0x6d, 0x5f, 0x63, 0x61, 0x70, 0x61, 0x63, 0x69, 0x74, - 0x79, 0x18, 0x1b, 0x20, 0x01, 0x28, 0x01, 0x52, 0x19, 0x6e, 0x6f, 0x72, 0x6d, 0x61, 0x6c, 0x69, - 0x7a, 0x65, 0x64, 0x47, 0x70, 0x75, 0x56, 0x72, 0x61, 0x6d, 0x43, 0x61, 0x70, 0x61, 0x63, 0x69, - 0x74, 0x79, 0x12, 0x41, 0x0a, 0x1b, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x63, 0x70, 0x75, 0x5f, 0x63, - 0x61, 0x70, 0x61, 0x63, 0x69, 0x74, 0x79, 0x5f, 0x69, 0x6e, 0x5f, 0x70, 0x65, 0x72, 0x69, 0x6f, - 0x64, 0x18, 0x1f, 0x20, 0x01, 0x28, 0x01, 0x48, 0x00, 0x52, 0x17, 0x6e, 0x6f, 0x64, 0x65, 0x43, - 0x70, 0x75, 0x43, 0x61, 0x70, 0x61, 0x63, 0x69, 0x74, 0x79, 0x49, 0x6e, 0x50, 0x65, 0x72, 0x69, - 0x6f, 0x64, 0x88, 0x01, 0x01, 0x12, 0x47, 0x0a, 0x1e, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x5f, 0x63, 0x61, 0x70, 0x61, 0x63, 0x69, 0x74, 0x79, 0x5f, 0x69, 0x6e, - 0x5f, 0x70, 0x65, 0x72, 0x69, 0x6f, 0x64, 0x18, 0x20, 0x20, 0x01, 0x28, 0x01, 0x48, 0x01, 0x52, - 0x1a, 0x6e, 0x6f, 0x64, 0x65, 0x4d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x43, 0x61, 0x70, 0x61, 0x63, - 0x69, 0x74, 0x79, 0x49, 0x6e, 0x50, 0x65, 0x72, 0x69, 0x6f, 0x64, 0x88, 0x01, 0x01, 0x12, 0x41, - 0x0a, 0x1b, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x67, 0x70, 0x75, 0x5f, 0x63, 0x61, 0x70, 0x61, 0x63, - 0x69, 0x74, 0x79, 0x5f, 0x69, 0x6e, 0x5f, 0x70, 0x65, 0x72, 0x69, 0x6f, 0x64, 0x18, 0x21, 0x20, - 0x01, 0x28, 0x01, 0x48, 0x02, 0x52, 0x17, 0x6e, 0x6f, 0x64, 0x65, 0x47, 0x70, 0x75, 0x43, 0x61, - 0x70, 0x61, 0x63, 0x69, 0x74, 0x79, 0x49, 0x6e, 0x50, 0x65, 0x72, 0x69, 0x6f, 0x64, 0x88, 0x01, - 0x01, 0x12, 0x5c, 0x0a, 0x29, 0x6e, 0x6f, 0x72, 0x6d, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x5f, - 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x5f, 0x63, 0x61, 0x70, 0x61, - 0x63, 0x69, 0x74, 0x79, 0x5f, 0x69, 0x6e, 0x5f, 0x70, 0x65, 0x72, 0x69, 0x6f, 0x64, 0x18, 0x22, - 0x20, 0x01, 0x28, 0x01, 0x48, 0x03, 0x52, 0x24, 0x6e, 0x6f, 0x72, 0x6d, 0x61, 0x6c, 0x69, 0x7a, - 0x65, 0x64, 0x4e, 0x6f, 0x64, 0x65, 0x4d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x43, 0x61, 0x70, 0x61, - 0x63, 0x69, 0x74, 0x79, 0x49, 0x6e, 0x50, 0x65, 0x72, 0x69, 0x6f, 0x64, 0x88, 0x01, 0x01, 0x12, - 0x2a, 0x0a, 0x11, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x6e, 0x65, 0x74, - 0x5f, 0x72, 0x63, 0x76, 0x18, 0x29, 0x20, 0x01, 0x28, 0x01, 0x52, 0x0f, 0x63, 0x6f, 0x6e, 0x74, - 0x61, 0x69, 0x6e, 0x65, 0x72, 0x4e, 0x65, 0x74, 0x52, 0x63, 0x76, 0x12, 0x34, 0x0a, 0x16, 0x63, - 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x6e, 0x65, 0x74, 0x5f, 0x74, 0x72, 0x61, - 0x6e, 0x73, 0x6d, 0x69, 0x74, 0x18, 0x2e, 0x20, 0x01, 0x28, 0x01, 0x52, 0x14, 0x63, 0x6f, 0x6e, - 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x4e, 0x65, 0x74, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x6d, 0x69, - 0x74, 0x12, 0x35, 0x0a, 0x17, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x6e, - 0x65, 0x74, 0x5f, 0x72, 0x63, 0x76, 0x5f, 0x70, 0x63, 0x6b, 0x74, 0x73, 0x18, 0x33, 0x20, 0x01, - 0x28, 0x01, 0x52, 0x14, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x4e, 0x65, 0x74, - 0x52, 0x63, 0x76, 0x50, 0x63, 0x6b, 0x74, 0x73, 0x12, 0x3f, 0x0a, 0x1c, 0x63, 0x6f, 0x6e, 0x74, - 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x6e, 0x65, 0x74, 0x5f, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x6d, - 0x69, 0x74, 0x5f, 0x70, 0x63, 0x6b, 0x74, 0x73, 0x18, 0x38, 0x20, 0x01, 0x28, 0x01, 0x52, 0x19, - 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x4e, 0x65, 0x74, 0x54, 0x72, 0x61, 0x6e, - 0x73, 0x6d, 0x69, 0x74, 0x50, 0x63, 0x6b, 0x74, 0x73, 0x12, 0x35, 0x0a, 0x17, 0x63, 0x6f, 0x6e, - 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x66, 0x73, 0x5f, 0x72, 0x65, 0x61, 0x64, 0x5f, 0x62, - 0x79, 0x74, 0x65, 0x73, 0x18, 0x47, 0x20, 0x01, 0x28, 0x01, 0x52, 0x14, 0x63, 0x6f, 0x6e, 0x74, - 0x61, 0x69, 0x6e, 0x65, 0x72, 0x46, 0x73, 0x52, 0x65, 0x61, 0x64, 0x42, 0x79, 0x74, 0x65, 0x73, - 0x12, 0x37, 0x0a, 0x18, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x66, 0x73, - 0x5f, 0x77, 0x72, 0x69, 0x74, 0x65, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x18, 0x4c, 0x20, 0x01, - 0x28, 0x01, 0x52, 0x15, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x46, 0x73, 0x57, - 0x72, 0x69, 0x74, 0x65, 0x42, 0x79, 0x74, 0x65, 0x73, 0x12, 0x2e, 0x0a, 0x13, 0x63, 0x6f, 0x6e, - 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x66, 0x73, 0x5f, 0x77, 0x72, 0x69, 0x74, 0x65, 0x73, - 0x18, 0x56, 0x20, 0x01, 0x28, 0x01, 0x52, 0x11, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, - 0x72, 0x46, 0x73, 0x57, 0x72, 0x69, 0x74, 0x65, 0x73, 0x12, 0x2c, 0x0a, 0x12, 0x63, 0x6f, 0x6e, - 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x66, 0x73, 0x5f, 0x72, 0x65, 0x61, 0x64, 0x73, 0x18, - 0x51, 0x20, 0x01, 0x28, 0x01, 0x52, 0x10, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, - 0x46, 0x73, 0x52, 0x65, 0x61, 0x64, 0x73, 0x12, 0x20, 0x0a, 0x0c, 0x6e, 0x6f, 0x64, 0x65, 0x5f, - 0x6e, 0x65, 0x74, 0x5f, 0x72, 0x63, 0x76, 0x18, 0x5b, 0x20, 0x01, 0x28, 0x01, 0x52, 0x0a, 0x6e, - 0x6f, 0x64, 0x65, 0x4e, 0x65, 0x74, 0x52, 0x63, 0x76, 0x12, 0x2a, 0x0a, 0x11, 0x6e, 0x6f, 0x64, - 0x65, 0x5f, 0x6e, 0x65, 0x74, 0x5f, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x6d, 0x69, 0x74, 0x18, 0x5c, - 0x20, 0x01, 0x28, 0x01, 0x52, 0x0f, 0x6e, 0x6f, 0x64, 0x65, 0x4e, 0x65, 0x74, 0x54, 0x72, 0x61, - 0x6e, 0x73, 0x6d, 0x69, 0x74, 0x12, 0x2b, 0x0a, 0x12, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x6e, 0x65, - 0x74, 0x5f, 0x72, 0x63, 0x76, 0x5f, 0x70, 0x63, 0x6b, 0x74, 0x73, 0x18, 0x5d, 0x20, 0x01, 0x28, - 0x01, 0x52, 0x0f, 0x6e, 0x6f, 0x64, 0x65, 0x4e, 0x65, 0x74, 0x52, 0x63, 0x76, 0x50, 0x63, 0x6b, - 0x74, 0x73, 0x12, 0x35, 0x0a, 0x17, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x6e, 0x65, 0x74, 0x5f, 0x74, - 0x72, 0x61, 0x6e, 0x73, 0x6d, 0x69, 0x74, 0x5f, 0x70, 0x63, 0x6b, 0x74, 0x73, 0x18, 0x5e, 0x20, - 0x01, 0x28, 0x01, 0x52, 0x14, 0x6e, 0x6f, 0x64, 0x65, 0x4e, 0x65, 0x74, 0x54, 0x72, 0x61, 0x6e, - 0x73, 0x6d, 0x69, 0x74, 0x50, 0x63, 0x6b, 0x74, 0x73, 0x12, 0x2d, 0x0a, 0x13, 0x6e, 0x6f, 0x64, - 0x65, 0x5f, 0x6e, 0x65, 0x74, 0x5f, 0x72, 0x63, 0x76, 0x5f, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x73, - 0x18, 0x5f, 0x20, 0x01, 0x28, 0x01, 0x52, 0x10, 0x6e, 0x6f, 0x64, 0x65, 0x4e, 0x65, 0x74, 0x52, - 0x63, 0x76, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x73, 0x12, 0x37, 0x0a, 0x18, 0x6e, 0x6f, 0x64, 0x65, - 0x5f, 0x6e, 0x65, 0x74, 0x5f, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x6d, 0x69, 0x74, 0x5f, 0x65, 0x72, - 0x72, 0x6f, 0x72, 0x73, 0x18, 0x60, 0x20, 0x01, 0x28, 0x01, 0x52, 0x15, 0x6e, 0x6f, 0x64, 0x65, - 0x4e, 0x65, 0x74, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x6d, 0x69, 0x74, 0x45, 0x72, 0x72, 0x6f, 0x72, - 0x73, 0x12, 0x2f, 0x0a, 0x14, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x6e, 0x65, 0x74, 0x5f, 0x72, 0x63, - 0x76, 0x5f, 0x64, 0x72, 0x6f, 0x70, 0x70, 0x65, 0x64, 0x18, 0x61, 0x20, 0x01, 0x28, 0x01, 0x52, - 0x11, 0x6e, 0x6f, 0x64, 0x65, 0x4e, 0x65, 0x74, 0x52, 0x63, 0x76, 0x44, 0x72, 0x6f, 0x70, 0x70, - 0x65, 0x64, 0x12, 0x39, 0x0a, 0x19, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x6e, 0x65, 0x74, 0x5f, 0x74, - 0x72, 0x61, 0x6e, 0x73, 0x6d, 0x69, 0x74, 0x5f, 0x64, 0x72, 0x6f, 0x70, 0x70, 0x65, 0x64, 0x18, - 0x62, 0x20, 0x01, 0x28, 0x01, 0x52, 0x16, 0x6e, 0x6f, 0x64, 0x65, 0x4e, 0x65, 0x74, 0x54, 0x72, - 0x61, 0x6e, 0x73, 0x6d, 0x69, 0x74, 0x44, 0x72, 0x6f, 0x70, 0x70, 0x65, 0x64, 0x12, 0x2b, 0x0a, - 0x12, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x66, 0x73, 0x5f, 0x72, 0x65, 0x61, 0x64, 0x5f, 0x62, 0x79, - 0x74, 0x65, 0x73, 0x18, 0x65, 0x20, 0x01, 0x28, 0x01, 0x52, 0x0f, 0x6e, 0x6f, 0x64, 0x65, 0x46, - 0x73, 0x52, 0x65, 0x61, 0x64, 0x42, 0x79, 0x74, 0x65, 0x73, 0x12, 0x2d, 0x0a, 0x13, 0x6e, 0x6f, - 0x64, 0x65, 0x5f, 0x66, 0x73, 0x5f, 0x77, 0x72, 0x69, 0x74, 0x65, 0x5f, 0x62, 0x79, 0x74, 0x65, - 0x73, 0x18, 0x66, 0x20, 0x01, 0x28, 0x01, 0x52, 0x10, 0x6e, 0x6f, 0x64, 0x65, 0x46, 0x73, 0x57, - 0x72, 0x69, 0x74, 0x65, 0x42, 0x79, 0x74, 0x65, 0x73, 0x12, 0x22, 0x0a, 0x0d, 0x6e, 0x6f, 0x64, - 0x65, 0x5f, 0x66, 0x73, 0x5f, 0x72, 0x65, 0x61, 0x64, 0x73, 0x18, 0x67, 0x20, 0x01, 0x28, 0x01, - 0x52, 0x0b, 0x6e, 0x6f, 0x64, 0x65, 0x46, 0x73, 0x52, 0x65, 0x61, 0x64, 0x73, 0x12, 0x24, 0x0a, - 0x0e, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x66, 0x73, 0x5f, 0x77, 0x72, 0x69, 0x74, 0x65, 0x73, 0x18, - 0x68, 0x20, 0x01, 0x28, 0x01, 0x52, 0x0c, 0x6e, 0x6f, 0x64, 0x65, 0x46, 0x73, 0x57, 0x72, 0x69, - 0x74, 0x65, 0x73, 0x12, 0x1b, 0x0a, 0x09, 0x70, 0x6f, 0x64, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, - 0x18, 0x6f, 0x20, 0x01, 0x28, 0x05, 0x52, 0x08, 0x70, 0x6f, 0x64, 0x43, 0x6f, 0x75, 0x6e, 0x74, - 0x12, 0x2d, 0x0a, 0x13, 0x6a, 0x76, 0x6d, 0x5f, 0x68, 0x65, 0x61, 0x70, 0x5f, 0x75, 0x73, 0x65, - 0x64, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x18, 0x79, 0x20, 0x01, 0x28, 0x03, 0x52, 0x10, 0x6a, - 0x76, 0x6d, 0x48, 0x65, 0x61, 0x70, 0x55, 0x73, 0x65, 0x64, 0x42, 0x79, 0x74, 0x65, 0x73, 0x12, - 0x2b, 0x0a, 0x12, 0x6a, 0x76, 0x6d, 0x5f, 0x68, 0x65, 0x61, 0x70, 0x5f, 0x6d, 0x61, 0x78, 0x5f, - 0x62, 0x79, 0x74, 0x65, 0x73, 0x18, 0x7a, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0f, 0x6a, 0x76, 0x6d, - 0x48, 0x65, 0x61, 0x70, 0x4d, 0x61, 0x78, 0x42, 0x79, 0x74, 0x65, 0x73, 0x12, 0x3b, 0x0a, 0x1a, - 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, - 0x5f, 0x72, 0x73, 0x73, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x18, 0x7b, 0x20, 0x01, 0x28, 0x03, - 0x52, 0x17, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x4d, 0x65, 0x6d, 0x6f, 0x72, - 0x79, 0x52, 0x73, 0x73, 0x42, 0x79, 0x74, 0x65, 0x73, 0x12, 0x3f, 0x0a, 0x1c, 0x63, 0x6f, 0x6e, - 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x5f, 0x63, 0x61, - 0x63, 0x68, 0x65, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x18, 0x7c, 0x20, 0x01, 0x28, 0x03, 0x52, - 0x19, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x4d, 0x65, 0x6d, 0x6f, 0x72, 0x79, - 0x43, 0x61, 0x63, 0x68, 0x65, 0x42, 0x79, 0x74, 0x65, 0x73, 0x12, 0x3d, 0x0a, 0x1b, 0x63, 0x6f, - 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x5f, 0x73, - 0x77, 0x61, 0x70, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x18, 0x7d, 0x20, 0x01, 0x28, 0x03, 0x52, - 0x18, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x4d, 0x65, 0x6d, 0x6f, 0x72, 0x79, - 0x53, 0x77, 0x61, 0x70, 0x42, 0x79, 0x74, 0x65, 0x73, 0x12, 0x4c, 0x0a, 0x23, 0x63, 0x6f, 0x6e, - 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x5f, 0x61, 0x63, - 0x74, 0x69, 0x76, 0x65, 0x5f, 0x63, 0x61, 0x63, 0x68, 0x65, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, - 0x18, 0x7e, 0x20, 0x01, 0x28, 0x03, 0x52, 0x1f, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, - 0x72, 0x4d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x41, 0x63, 0x74, 0x69, 0x76, 0x65, 0x43, 0x61, 0x63, - 0x68, 0x65, 0x42, 0x79, 0x74, 0x65, 0x73, 0x12, 0x50, 0x0a, 0x25, 0x63, 0x6f, 0x6e, 0x74, 0x61, - 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x5f, 0x69, 0x6e, 0x61, 0x63, - 0x74, 0x69, 0x76, 0x65, 0x5f, 0x63, 0x61, 0x63, 0x68, 0x65, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, - 0x18, 0x7f, 0x20, 0x01, 0x28, 0x03, 0x52, 0x21, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, - 0x72, 0x4d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x49, 0x6e, 0x61, 0x63, 0x74, 0x69, 0x76, 0x65, 0x43, - 0x61, 0x63, 0x68, 0x65, 0x42, 0x79, 0x74, 0x65, 0x73, 0x12, 0x33, 0x0a, 0x15, 0x63, 0x6f, 0x6e, - 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x63, 0x66, 0x73, 0x5f, 0x70, 0x65, 0x72, 0x69, 0x6f, - 0x64, 0x73, 0x18, 0x80, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x13, 0x63, 0x6f, 0x6e, 0x74, 0x61, - 0x69, 0x6e, 0x65, 0x72, 0x43, 0x66, 0x73, 0x50, 0x65, 0x72, 0x69, 0x6f, 0x64, 0x73, 0x12, 0x46, - 0x0a, 0x1f, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x63, 0x66, 0x73, 0x5f, - 0x74, 0x68, 0x72, 0x6f, 0x74, 0x74, 0x6c, 0x65, 0x64, 0x5f, 0x70, 0x65, 0x72, 0x69, 0x6f, 0x64, - 0x73, 0x18, 0x81, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x1c, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, - 0x6e, 0x65, 0x72, 0x43, 0x66, 0x73, 0x54, 0x68, 0x72, 0x6f, 0x74, 0x74, 0x6c, 0x65, 0x64, 0x50, - 0x65, 0x72, 0x69, 0x6f, 0x64, 0x73, 0x12, 0x40, 0x0a, 0x1c, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, - 0x6e, 0x65, 0x72, 0x5f, 0x63, 0x66, 0x73, 0x5f, 0x74, 0x68, 0x72, 0x6f, 0x74, 0x74, 0x6c, 0x65, - 0x64, 0x5f, 0x75, 0x73, 0x65, 0x63, 0x18, 0x82, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x19, 0x63, - 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x43, 0x66, 0x73, 0x54, 0x68, 0x72, 0x6f, 0x74, - 0x74, 0x6c, 0x65, 0x64, 0x55, 0x73, 0x65, 0x63, 0x12, 0x3e, 0x0a, 0x1b, 0x63, 0x6f, 0x6e, 0x74, - 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x5f, 0x65, 0x76, 0x65, - 0x6e, 0x74, 0x73, 0x5f, 0x6d, 0x61, 0x78, 0x18, 0x83, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x18, - 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x4d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x45, - 0x76, 0x65, 0x6e, 0x74, 0x73, 0x4d, 0x61, 0x78, 0x12, 0x47, 0x0a, 0x20, 0x63, 0x6f, 0x6e, 0x74, - 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x63, 0x70, 0x75, 0x5f, 0x70, 0x72, 0x65, 0x73, 0x73, 0x75, - 0x72, 0x65, 0x5f, 0x73, 0x6f, 0x6d, 0x65, 0x5f, 0x75, 0x73, 0x65, 0x63, 0x18, 0x84, 0x01, 0x20, - 0x01, 0x28, 0x03, 0x52, 0x1c, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x43, 0x70, - 0x75, 0x50, 0x72, 0x65, 0x73, 0x73, 0x75, 0x72, 0x65, 0x53, 0x6f, 0x6d, 0x65, 0x55, 0x73, 0x65, - 0x63, 0x12, 0x4d, 0x0a, 0x23, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x6d, - 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x5f, 0x70, 0x72, 0x65, 0x73, 0x73, 0x75, 0x72, 0x65, 0x5f, 0x73, - 0x6f, 0x6d, 0x65, 0x5f, 0x75, 0x73, 0x65, 0x63, 0x18, 0x85, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, - 0x1f, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x4d, 0x65, 0x6d, 0x6f, 0x72, 0x79, - 0x50, 0x72, 0x65, 0x73, 0x73, 0x75, 0x72, 0x65, 0x53, 0x6f, 0x6d, 0x65, 0x55, 0x73, 0x65, 0x63, - 0x12, 0x4d, 0x0a, 0x23, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x6d, 0x65, - 0x6d, 0x6f, 0x72, 0x79, 0x5f, 0x70, 0x72, 0x65, 0x73, 0x73, 0x75, 0x72, 0x65, 0x5f, 0x66, 0x75, - 0x6c, 0x6c, 0x5f, 0x75, 0x73, 0x65, 0x63, 0x18, 0x86, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x1f, - 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x4d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x50, - 0x72, 0x65, 0x73, 0x73, 0x75, 0x72, 0x65, 0x46, 0x75, 0x6c, 0x6c, 0x55, 0x73, 0x65, 0x63, 0x42, - 0x1e, 0x0a, 0x1c, 0x5f, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x63, 0x70, 0x75, 0x5f, 0x63, 0x61, 0x70, - 0x61, 0x63, 0x69, 0x74, 0x79, 0x5f, 0x69, 0x6e, 0x5f, 0x70, 0x65, 0x72, 0x69, 0x6f, 0x64, 0x42, - 0x21, 0x0a, 0x1f, 0x5f, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x5f, - 0x63, 0x61, 0x70, 0x61, 0x63, 0x69, 0x74, 0x79, 0x5f, 0x69, 0x6e, 0x5f, 0x70, 0x65, 0x72, 0x69, - 0x6f, 0x64, 0x42, 0x1e, 0x0a, 0x1c, 0x5f, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x67, 0x70, 0x75, 0x5f, - 0x63, 0x61, 0x70, 0x61, 0x63, 0x69, 0x74, 0x79, 0x5f, 0x69, 0x6e, 0x5f, 0x70, 0x65, 0x72, 0x69, - 0x6f, 0x64, 0x42, 0x2c, 0x0a, 0x2a, 0x5f, 0x6e, 0x6f, 0x72, 0x6d, 0x61, 0x6c, 0x69, 0x7a, 0x65, - 0x64, 0x5f, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x5f, 0x63, 0x61, - 0x70, 0x61, 0x63, 0x69, 0x74, 0x79, 0x5f, 0x69, 0x6e, 0x5f, 0x70, 0x65, 0x72, 0x69, 0x6f, 0x64, - 0x22, 0xf9, 0x03, 0x0a, 0x11, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x50, 0x65, 0x72, 0x63, 0x65, - 0x6e, 0x74, 0x69, 0x6c, 0x65, 0x73, 0x12, 0x10, 0x0a, 0x03, 0x6d, 0x69, 0x6e, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x01, 0x52, 0x03, 0x6d, 0x69, 0x6e, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x76, 0x67, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x01, 0x52, 0x03, 0x61, 0x76, 0x67, 0x12, 0x10, 0x0a, 0x03, 0x6d, 0x61, - 0x78, 0x18, 0x03, 0x20, 0x01, 0x28, 0x01, 0x52, 0x03, 0x6d, 0x61, 0x78, 0x12, 0x10, 0x0a, 0x03, - 0x70, 0x35, 0x30, 0x18, 0x04, 0x20, 0x01, 0x28, 0x01, 0x52, 0x03, 0x70, 0x35, 0x30, 0x12, 0x10, - 0x0a, 0x03, 0x70, 0x39, 0x35, 0x18, 0x05, 0x20, 0x01, 0x28, 0x01, 0x52, 0x03, 0x70, 0x39, 0x35, - 0x12, 0x10, 0x0a, 0x03, 0x70, 0x39, 0x39, 0x18, 0x06, 0x20, 0x01, 0x28, 0x01, 0x52, 0x03, 0x70, - 0x39, 0x39, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x31, 0x30, 0x30, 0x18, 0x07, 0x20, 0x01, 0x28, 0x01, - 0x52, 0x04, 0x70, 0x31, 0x30, 0x30, 0x12, 0x10, 0x0a, 0x03, 0x70, 0x31, 0x30, 0x18, 0x08, 0x20, - 0x01, 0x28, 0x01, 0x52, 0x03, 0x70, 0x31, 0x30, 0x12, 0x10, 0x0a, 0x03, 0x70, 0x32, 0x35, 0x18, - 0x09, 0x20, 0x01, 0x28, 0x01, 0x52, 0x03, 0x70, 0x32, 0x35, 0x12, 0x10, 0x0a, 0x03, 0x70, 0x37, - 0x35, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x01, 0x52, 0x03, 0x70, 0x37, 0x35, 0x12, 0x10, 0x0a, 0x03, - 0x70, 0x39, 0x30, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x01, 0x52, 0x03, 0x70, 0x39, 0x30, 0x12, 0x12, - 0x0a, 0x04, 0x70, 0x39, 0x39, 0x39, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x01, 0x52, 0x04, 0x70, 0x39, - 0x39, 0x39, 0x12, 0x0e, 0x0a, 0x02, 0x70, 0x31, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x01, 0x52, 0x02, - 0x70, 0x31, 0x12, 0x0e, 0x0a, 0x02, 0x70, 0x35, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x01, 0x52, 0x02, - 0x70, 0x35, 0x12, 0x10, 0x0a, 0x03, 0x70, 0x31, 0x35, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x01, 0x52, - 0x03, 0x70, 0x31, 0x35, 0x12, 0x10, 0x0a, 0x03, 0x70, 0x32, 0x30, 0x18, 0x10, 0x20, 0x01, 0x28, - 0x01, 0x52, 0x03, 0x70, 0x32, 0x30, 0x12, 0x10, 0x0a, 0x03, 0x70, 0x33, 0x30, 0x18, 0x11, 0x20, - 0x01, 0x28, 0x01, 0x52, 0x03, 0x70, 0x33, 0x30, 0x12, 0x10, 0x0a, 0x03, 0x70, 0x33, 0x35, 0x18, - 0x12, 0x20, 0x01, 0x28, 0x01, 0x52, 0x03, 0x70, 0x33, 0x35, 0x12, 0x10, 0x0a, 0x03, 0x70, 0x34, - 0x30, 0x18, 0x13, 0x20, 0x01, 0x28, 0x01, 0x52, 0x03, 0x70, 0x34, 0x30, 0x12, 0x10, 0x0a, 0x03, - 0x70, 0x34, 0x35, 0x18, 0x14, 0x20, 0x01, 0x28, 0x01, 0x52, 0x03, 0x70, 0x34, 0x35, 0x12, 0x10, - 0x0a, 0x03, 0x70, 0x35, 0x35, 0x18, 0x15, 0x20, 0x01, 0x28, 0x01, 0x52, 0x03, 0x70, 0x35, 0x35, - 0x12, 0x10, 0x0a, 0x03, 0x70, 0x36, 0x30, 0x18, 0x16, 0x20, 0x01, 0x28, 0x01, 0x52, 0x03, 0x70, - 0x36, 0x30, 0x12, 0x10, 0x0a, 0x03, 0x70, 0x36, 0x35, 0x18, 0x17, 0x20, 0x01, 0x28, 0x01, 0x52, - 0x03, 0x70, 0x36, 0x35, 0x12, 0x10, 0x0a, 0x03, 0x70, 0x37, 0x30, 0x18, 0x18, 0x20, 0x01, 0x28, - 0x01, 0x52, 0x03, 0x70, 0x37, 0x30, 0x12, 0x10, 0x0a, 0x03, 0x70, 0x38, 0x30, 0x18, 0x19, 0x20, - 0x01, 0x28, 0x01, 0x52, 0x03, 0x70, 0x38, 0x30, 0x12, 0x10, 0x0a, 0x03, 0x70, 0x38, 0x35, 0x18, - 0x1a, 0x20, 0x01, 0x28, 0x01, 0x52, 0x03, 0x70, 0x38, 0x35, 0x12, 0x10, 0x0a, 0x03, 0x70, 0x39, - 0x37, 0x18, 0x1b, 0x20, 0x01, 0x28, 0x01, 0x52, 0x03, 0x70, 0x39, 0x37, 0x22, 0xa2, 0x08, 0x0a, - 0x1a, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x50, 0x65, 0x72, 0x63, 0x65, 0x6e, - 0x74, 0x69, 0x6c, 0x65, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x12, 0x25, 0x0a, 0x0e, 0x63, - 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x0d, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x4e, 0x61, - 0x6d, 0x65, 0x12, 0x36, 0x0a, 0x09, 0x63, 0x70, 0x75, 0x5f, 0x75, 0x73, 0x61, 0x67, 0x65, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4d, - 0x65, 0x74, 0x72, 0x69, 0x63, 0x50, 0x65, 0x72, 0x63, 0x65, 0x6e, 0x74, 0x69, 0x6c, 0x65, 0x73, - 0x52, 0x08, 0x63, 0x70, 0x75, 0x55, 0x73, 0x61, 0x67, 0x65, 0x12, 0x3c, 0x0a, 0x0c, 0x6d, 0x65, - 0x6d, 0x6f, 0x72, 0x79, 0x5f, 0x75, 0x73, 0x61, 0x67, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x19, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, - 0x50, 0x65, 0x72, 0x63, 0x65, 0x6e, 0x74, 0x69, 0x6c, 0x65, 0x73, 0x52, 0x0b, 0x6d, 0x65, 0x6d, - 0x6f, 0x72, 0x79, 0x55, 0x73, 0x61, 0x67, 0x65, 0x12, 0x3a, 0x0a, 0x0b, 0x63, 0x70, 0x75, 0x5f, - 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, - 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x50, 0x65, 0x72, - 0x63, 0x65, 0x6e, 0x74, 0x69, 0x6c, 0x65, 0x73, 0x52, 0x0a, 0x63, 0x70, 0x75, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x12, 0x40, 0x0a, 0x0e, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x5f, 0x72, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x61, + 0x5f, 0x70, 0x65, 0x72, 0x69, 0x6f, 0x64, 0x22, 0xf9, 0x03, 0x0a, 0x11, 0x4d, 0x65, 0x74, 0x72, + 0x69, 0x63, 0x50, 0x65, 0x72, 0x63, 0x65, 0x6e, 0x74, 0x69, 0x6c, 0x65, 0x73, 0x12, 0x10, 0x0a, + 0x03, 0x6d, 0x69, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x01, 0x52, 0x03, 0x6d, 0x69, 0x6e, 0x12, + 0x10, 0x0a, 0x03, 0x61, 0x76, 0x67, 0x18, 0x02, 0x20, 0x01, 0x28, 0x01, 0x52, 0x03, 0x61, 0x76, + 0x67, 0x12, 0x10, 0x0a, 0x03, 0x6d, 0x61, 0x78, 0x18, 0x03, 0x20, 0x01, 0x28, 0x01, 0x52, 0x03, + 0x6d, 0x61, 0x78, 0x12, 0x10, 0x0a, 0x03, 0x70, 0x35, 0x30, 0x18, 0x04, 0x20, 0x01, 0x28, 0x01, + 0x52, 0x03, 0x70, 0x35, 0x30, 0x12, 0x10, 0x0a, 0x03, 0x70, 0x39, 0x35, 0x18, 0x05, 0x20, 0x01, + 0x28, 0x01, 0x52, 0x03, 0x70, 0x39, 0x35, 0x12, 0x10, 0x0a, 0x03, 0x70, 0x39, 0x39, 0x18, 0x06, + 0x20, 0x01, 0x28, 0x01, 0x52, 0x03, 0x70, 0x39, 0x39, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x31, 0x30, + 0x30, 0x18, 0x07, 0x20, 0x01, 0x28, 0x01, 0x52, 0x04, 0x70, 0x31, 0x30, 0x30, 0x12, 0x10, 0x0a, + 0x03, 0x70, 0x31, 0x30, 0x18, 0x08, 0x20, 0x01, 0x28, 0x01, 0x52, 0x03, 0x70, 0x31, 0x30, 0x12, + 0x10, 0x0a, 0x03, 0x70, 0x32, 0x35, 0x18, 0x09, 0x20, 0x01, 0x28, 0x01, 0x52, 0x03, 0x70, 0x32, + 0x35, 0x12, 0x10, 0x0a, 0x03, 0x70, 0x37, 0x35, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x01, 0x52, 0x03, + 0x70, 0x37, 0x35, 0x12, 0x10, 0x0a, 0x03, 0x70, 0x39, 0x30, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x01, + 0x52, 0x03, 0x70, 0x39, 0x30, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x39, 0x39, 0x39, 0x18, 0x0c, 0x20, + 0x01, 0x28, 0x01, 0x52, 0x04, 0x70, 0x39, 0x39, 0x39, 0x12, 0x0e, 0x0a, 0x02, 0x70, 0x31, 0x18, + 0x0d, 0x20, 0x01, 0x28, 0x01, 0x52, 0x02, 0x70, 0x31, 0x12, 0x0e, 0x0a, 0x02, 0x70, 0x35, 0x18, + 0x0e, 0x20, 0x01, 0x28, 0x01, 0x52, 0x02, 0x70, 0x35, 0x12, 0x10, 0x0a, 0x03, 0x70, 0x31, 0x35, + 0x18, 0x0f, 0x20, 0x01, 0x28, 0x01, 0x52, 0x03, 0x70, 0x31, 0x35, 0x12, 0x10, 0x0a, 0x03, 0x70, + 0x32, 0x30, 0x18, 0x10, 0x20, 0x01, 0x28, 0x01, 0x52, 0x03, 0x70, 0x32, 0x30, 0x12, 0x10, 0x0a, + 0x03, 0x70, 0x33, 0x30, 0x18, 0x11, 0x20, 0x01, 0x28, 0x01, 0x52, 0x03, 0x70, 0x33, 0x30, 0x12, + 0x10, 0x0a, 0x03, 0x70, 0x33, 0x35, 0x18, 0x12, 0x20, 0x01, 0x28, 0x01, 0x52, 0x03, 0x70, 0x33, + 0x35, 0x12, 0x10, 0x0a, 0x03, 0x70, 0x34, 0x30, 0x18, 0x13, 0x20, 0x01, 0x28, 0x01, 0x52, 0x03, + 0x70, 0x34, 0x30, 0x12, 0x10, 0x0a, 0x03, 0x70, 0x34, 0x35, 0x18, 0x14, 0x20, 0x01, 0x28, 0x01, + 0x52, 0x03, 0x70, 0x34, 0x35, 0x12, 0x10, 0x0a, 0x03, 0x70, 0x35, 0x35, 0x18, 0x15, 0x20, 0x01, + 0x28, 0x01, 0x52, 0x03, 0x70, 0x35, 0x35, 0x12, 0x10, 0x0a, 0x03, 0x70, 0x36, 0x30, 0x18, 0x16, + 0x20, 0x01, 0x28, 0x01, 0x52, 0x03, 0x70, 0x36, 0x30, 0x12, 0x10, 0x0a, 0x03, 0x70, 0x36, 0x35, + 0x18, 0x17, 0x20, 0x01, 0x28, 0x01, 0x52, 0x03, 0x70, 0x36, 0x35, 0x12, 0x10, 0x0a, 0x03, 0x70, + 0x37, 0x30, 0x18, 0x18, 0x20, 0x01, 0x28, 0x01, 0x52, 0x03, 0x70, 0x37, 0x30, 0x12, 0x10, 0x0a, + 0x03, 0x70, 0x38, 0x30, 0x18, 0x19, 0x20, 0x01, 0x28, 0x01, 0x52, 0x03, 0x70, 0x38, 0x30, 0x12, + 0x10, 0x0a, 0x03, 0x70, 0x38, 0x35, 0x18, 0x1a, 0x20, 0x01, 0x28, 0x01, 0x52, 0x03, 0x70, 0x38, + 0x35, 0x12, 0x10, 0x0a, 0x03, 0x70, 0x39, 0x37, 0x18, 0x1b, 0x20, 0x01, 0x28, 0x01, 0x52, 0x03, + 0x70, 0x39, 0x37, 0x22, 0xa2, 0x08, 0x0a, 0x1a, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, + 0x72, 0x50, 0x65, 0x72, 0x63, 0x65, 0x6e, 0x74, 0x69, 0x6c, 0x65, 0x53, 0x75, 0x6d, 0x6d, 0x61, + 0x72, 0x79, 0x12, 0x25, 0x0a, 0x0e, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, + 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x63, 0x6f, 0x6e, 0x74, + 0x61, 0x69, 0x6e, 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x36, 0x0a, 0x09, 0x63, 0x70, 0x75, + 0x5f, 0x75, 0x73, 0x61, 0x67, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x50, 0x65, 0x72, 0x63, - 0x65, 0x6e, 0x74, 0x69, 0x6c, 0x65, 0x73, 0x52, 0x0d, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x36, 0x0a, 0x09, 0x63, 0x70, 0x75, 0x5f, 0x6c, 0x69, - 0x6d, 0x69, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x61, 0x70, 0x69, 0x2e, - 0x76, 0x31, 0x2e, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x50, 0x65, 0x72, 0x63, 0x65, 0x6e, 0x74, - 0x69, 0x6c, 0x65, 0x73, 0x52, 0x08, 0x63, 0x70, 0x75, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x12, 0x3c, - 0x0a, 0x0c, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x5f, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x18, 0x07, + 0x65, 0x6e, 0x74, 0x69, 0x6c, 0x65, 0x73, 0x52, 0x08, 0x63, 0x70, 0x75, 0x55, 0x73, 0x61, 0x67, + 0x65, 0x12, 0x3c, 0x0a, 0x0c, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x5f, 0x75, 0x73, 0x61, 0x67, + 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, + 0x2e, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x50, 0x65, 0x72, 0x63, 0x65, 0x6e, 0x74, 0x69, 0x6c, + 0x65, 0x73, 0x52, 0x0b, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x55, 0x73, 0x61, 0x67, 0x65, 0x12, + 0x3a, 0x0a, 0x0b, 0x63, 0x70, 0x75, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x50, 0x65, 0x72, 0x63, 0x65, 0x6e, 0x74, 0x69, 0x6c, 0x65, 0x73, 0x52, - 0x0b, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x12, 0x36, 0x0a, 0x09, - 0x67, 0x70, 0x75, 0x5f, 0x75, 0x73, 0x61, 0x67, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x19, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x50, - 0x65, 0x72, 0x63, 0x65, 0x6e, 0x74, 0x69, 0x6c, 0x65, 0x73, 0x52, 0x08, 0x67, 0x70, 0x75, 0x55, - 0x73, 0x61, 0x67, 0x65, 0x12, 0x3f, 0x0a, 0x0e, 0x67, 0x70, 0x75, 0x5f, 0x76, 0x72, 0x61, 0x6d, - 0x5f, 0x75, 0x73, 0x61, 0x67, 0x65, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x61, - 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x50, 0x65, 0x72, 0x63, - 0x65, 0x6e, 0x74, 0x69, 0x6c, 0x65, 0x73, 0x52, 0x0c, 0x67, 0x70, 0x75, 0x56, 0x72, 0x61, 0x6d, - 0x55, 0x73, 0x61, 0x67, 0x65, 0x12, 0x32, 0x0a, 0x13, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, - 0x5f, 0x63, 0x70, 0x75, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x18, 0x0a, 0x20, 0x01, - 0x28, 0x01, 0x42, 0x02, 0x18, 0x01, 0x52, 0x11, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x43, - 0x70, 0x75, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x38, 0x0a, 0x16, 0x63, 0x75, 0x72, - 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x5f, 0x72, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x01, 0x42, 0x02, 0x18, 0x01, 0x52, 0x14, 0x63, - 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x4d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x12, 0x2e, 0x0a, 0x11, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x63, - 0x70, 0x75, 0x5f, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x01, 0x42, 0x02, - 0x18, 0x01, 0x52, 0x0f, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x43, 0x70, 0x75, 0x4c, 0x69, - 0x6d, 0x69, 0x74, 0x12, 0x34, 0x0a, 0x14, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x6d, - 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x5f, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x18, 0x0d, 0x20, 0x01, 0x28, - 0x01, 0x42, 0x02, 0x18, 0x01, 0x52, 0x12, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x4d, 0x65, - 0x6d, 0x6f, 0x72, 0x79, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x12, 0x4d, 0x0a, 0x15, 0x6e, 0x65, 0x74, - 0x77, 0x6f, 0x72, 0x6b, 0x5f, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x5f, 0x62, 0x79, 0x74, - 0x65, 0x73, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, - 0x31, 0x2e, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x50, 0x65, 0x72, 0x63, 0x65, 0x6e, 0x74, 0x69, - 0x6c, 0x65, 0x73, 0x52, 0x13, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x52, 0x65, 0x63, 0x65, - 0x69, 0x76, 0x65, 0x42, 0x79, 0x74, 0x65, 0x73, 0x12, 0x4f, 0x0a, 0x16, 0x6e, 0x65, 0x74, 0x77, - 0x6f, 0x72, 0x6b, 0x5f, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x6d, 0x69, 0x74, 0x5f, 0x62, 0x79, 0x74, - 0x65, 0x73, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, - 0x31, 0x2e, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x50, 0x65, 0x72, 0x63, 0x65, 0x6e, 0x74, 0x69, - 0x6c, 0x65, 0x73, 0x52, 0x14, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x54, 0x72, 0x61, 0x6e, - 0x73, 0x6d, 0x69, 0x74, 0x42, 0x79, 0x74, 0x65, 0x73, 0x12, 0x41, 0x0a, 0x0d, 0x66, 0x73, 0x5f, - 0x72, 0x65, 0x61, 0x64, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x18, 0x10, 0x20, 0x01, 0x28, 0x0b, + 0x0a, 0x63, 0x70, 0x75, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x40, 0x0a, 0x0e, 0x6d, + 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x18, 0x05, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x65, 0x74, + 0x72, 0x69, 0x63, 0x50, 0x65, 0x72, 0x63, 0x65, 0x6e, 0x74, 0x69, 0x6c, 0x65, 0x73, 0x52, 0x0d, + 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x36, 0x0a, + 0x09, 0x63, 0x70, 0x75, 0x5f, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, - 0x50, 0x65, 0x72, 0x63, 0x65, 0x6e, 0x74, 0x69, 0x6c, 0x65, 0x73, 0x42, 0x02, 0x18, 0x01, 0x52, - 0x0b, 0x66, 0x73, 0x52, 0x65, 0x61, 0x64, 0x42, 0x79, 0x74, 0x65, 0x73, 0x12, 0x43, 0x0a, 0x0e, - 0x66, 0x73, 0x5f, 0x77, 0x72, 0x69, 0x74, 0x65, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x18, 0x11, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x65, - 0x74, 0x72, 0x69, 0x63, 0x50, 0x65, 0x72, 0x63, 0x65, 0x6e, 0x74, 0x69, 0x6c, 0x65, 0x73, 0x42, - 0x02, 0x18, 0x01, 0x52, 0x0c, 0x66, 0x73, 0x57, 0x72, 0x69, 0x74, 0x65, 0x42, 0x79, 0x74, 0x65, - 0x73, 0x22, 0x9d, 0x05, 0x0a, 0x16, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x73, 0x12, 0x25, 0x0a, 0x0e, - 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x4e, - 0x61, 0x6d, 0x65, 0x12, 0x2e, 0x0a, 0x13, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x63, - 0x70, 0x75, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x01, - 0x52, 0x11, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x43, 0x70, 0x75, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x12, 0x34, 0x0a, 0x16, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x6d, - 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x18, 0x03, 0x20, - 0x01, 0x28, 0x01, 0x52, 0x14, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x4d, 0x65, 0x6d, 0x6f, - 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2a, 0x0a, 0x11, 0x63, 0x75, 0x72, - 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x63, 0x70, 0x75, 0x5f, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x18, 0x04, - 0x20, 0x01, 0x28, 0x01, 0x52, 0x0f, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x43, 0x70, 0x75, - 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x12, 0x30, 0x0a, 0x14, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, - 0x5f, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x5f, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x18, 0x05, 0x20, - 0x01, 0x28, 0x01, 0x52, 0x12, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x4d, 0x65, 0x6d, 0x6f, - 0x72, 0x79, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x12, 0x28, 0x0a, 0x10, 0x6a, 0x76, 0x6d, 0x5f, 0x6a, - 0x61, 0x76, 0x61, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x0a, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x0e, 0x6a, 0x76, 0x6d, 0x4a, 0x61, 0x76, 0x61, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, - 0x6e, 0x12, 0x28, 0x0a, 0x10, 0x6a, 0x76, 0x6d, 0x5f, 0x6a, 0x61, 0x76, 0x61, 0x5f, 0x63, 0x6f, - 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x6a, 0x76, 0x6d, - 0x4a, 0x61, 0x76, 0x61, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x12, 0x22, 0x0a, 0x0d, 0x6a, - 0x76, 0x6d, 0x5f, 0x78, 0x6d, 0x78, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x18, 0x0d, 0x20, 0x01, - 0x28, 0x03, 0x52, 0x0b, 0x6a, 0x76, 0x6d, 0x58, 0x6d, 0x78, 0x42, 0x79, 0x74, 0x65, 0x73, 0x12, - 0x22, 0x0a, 0x0d, 0x6a, 0x76, 0x6d, 0x5f, 0x78, 0x6d, 0x73, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, - 0x18, 0x0e, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0b, 0x6a, 0x76, 0x6d, 0x58, 0x6d, 0x73, 0x42, 0x79, - 0x74, 0x65, 0x73, 0x12, 0x33, 0x0a, 0x16, 0x6a, 0x76, 0x6d, 0x5f, 0x6d, 0x61, 0x78, 0x5f, 0x72, - 0x61, 0x6d, 0x5f, 0x70, 0x65, 0x72, 0x63, 0x65, 0x6e, 0x74, 0x61, 0x67, 0x65, 0x18, 0x0f, 0x20, - 0x01, 0x28, 0x01, 0x52, 0x13, 0x6a, 0x76, 0x6d, 0x4d, 0x61, 0x78, 0x52, 0x61, 0x6d, 0x50, 0x65, - 0x72, 0x63, 0x65, 0x6e, 0x74, 0x61, 0x67, 0x65, 0x12, 0x3e, 0x0a, 0x19, 0x6a, 0x76, 0x6d, 0x5f, - 0x75, 0x73, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x73, 0x75, - 0x70, 0x70, 0x6f, 0x72, 0x74, 0x18, 0x10, 0x20, 0x01, 0x28, 0x08, 0x48, 0x00, 0x52, 0x16, 0x6a, - 0x76, 0x6d, 0x55, 0x73, 0x65, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x53, 0x75, - 0x70, 0x70, 0x6f, 0x72, 0x74, 0x88, 0x01, 0x01, 0x12, 0x24, 0x0a, 0x0e, 0x6a, 0x76, 0x6d, 0x5f, - 0x78, 0x6d, 0x78, 0x5f, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, 0x11, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x0c, 0x6a, 0x76, 0x6d, 0x58, 0x6d, 0x78, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x43, - 0x0a, 0x11, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, - 0x73, 0x65, 0x73, 0x18, 0x12, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x61, 0x70, 0x69, 0x2e, - 0x76, 0x31, 0x2e, 0x52, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, - 0x73, 0x52, 0x10, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, - 0x73, 0x65, 0x73, 0x42, 0x1c, 0x0a, 0x1a, 0x5f, 0x6a, 0x76, 0x6d, 0x5f, 0x75, 0x73, 0x65, 0x5f, - 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, - 0x74, 0x22, 0x8a, 0x03, 0x0a, 0x0e, 0x47, 0x50, 0x55, 0x4d, 0x69, 0x67, 0x49, 0x6e, 0x73, 0x74, - 0x61, 0x6e, 0x63, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x75, - 0x75, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x64, 0x65, 0x76, 0x69, 0x63, - 0x65, 0x55, 0x75, 0x69, 0x64, 0x12, 0x1b, 0x0a, 0x09, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x5f, - 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, - 0x49, 0x64, 0x12, 0x1f, 0x0a, 0x0b, 0x6d, 0x69, 0x67, 0x5f, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, - 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x6d, 0x69, 0x67, 0x50, 0x72, 0x6f, 0x66, - 0x69, 0x6c, 0x65, 0x12, 0x26, 0x0a, 0x0f, 0x6d, 0x69, 0x67, 0x5f, 0x69, 0x6e, 0x73, 0x74, 0x61, - 0x6e, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x6d, 0x69, - 0x67, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x49, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x6d, - 0x6f, 0x64, 0x65, 0x6c, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x09, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x74, 0x65, - 0x6e, 0x73, 0x6f, 0x72, 0x5f, 0x61, 0x63, 0x74, 0x69, 0x76, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, - 0x01, 0x52, 0x0c, 0x74, 0x65, 0x6e, 0x73, 0x6f, 0x72, 0x41, 0x63, 0x74, 0x69, 0x76, 0x65, 0x12, - 0x1f, 0x0a, 0x0b, 0x64, 0x72, 0x61, 0x6d, 0x5f, 0x61, 0x63, 0x74, 0x69, 0x76, 0x65, 0x18, 0x07, - 0x20, 0x01, 0x28, 0x01, 0x52, 0x0a, 0x64, 0x72, 0x61, 0x6d, 0x41, 0x63, 0x74, 0x69, 0x76, 0x65, - 0x12, 0x34, 0x0a, 0x16, 0x67, 0x72, 0x61, 0x70, 0x68, 0x69, 0x63, 0x73, 0x5f, 0x65, 0x6e, 0x67, - 0x69, 0x6e, 0x65, 0x5f, 0x61, 0x63, 0x74, 0x69, 0x76, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x01, - 0x52, 0x14, 0x67, 0x72, 0x61, 0x70, 0x68, 0x69, 0x63, 0x73, 0x45, 0x6e, 0x67, 0x69, 0x6e, 0x65, - 0x41, 0x63, 0x74, 0x69, 0x76, 0x65, 0x12, 0x29, 0x0a, 0x10, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x62, - 0x75, 0x66, 0x66, 0x65, 0x72, 0x5f, 0x75, 0x73, 0x65, 0x64, 0x18, 0x09, 0x20, 0x01, 0x28, 0x01, - 0x52, 0x0f, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x55, 0x73, 0x65, - 0x64, 0x12, 0x2b, 0x0a, 0x11, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, - 0x5f, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x01, 0x52, 0x10, 0x66, 0x72, - 0x61, 0x6d, 0x65, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x54, 0x6f, 0x74, 0x61, 0x6c, 0x22, 0xfa, - 0x03, 0x0a, 0x13, 0x49, 0x6e, 0x64, 0x69, 0x76, 0x69, 0x64, 0x75, 0x61, 0x6c, 0x47, 0x50, 0x55, - 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x12, 0x12, 0x0a, 0x04, 0x75, 0x75, 0x69, 0x64, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x75, 0x75, 0x69, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x6d, 0x6f, - 0x64, 0x65, 0x6c, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, - 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x64, 0x65, 0x76, - 0x69, 0x63, 0x65, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x0b, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x12, 0x1f, 0x0a, 0x0b, - 0x6d, 0x69, 0x67, 0x5f, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x0a, 0x6d, 0x69, 0x67, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x12, 0x26, 0x0a, - 0x0f, 0x6d, 0x69, 0x67, 0x5f, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x5f, 0x69, 0x64, - 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x6d, 0x69, 0x67, 0x49, 0x6e, 0x73, 0x74, 0x61, - 0x6e, 0x63, 0x65, 0x49, 0x64, 0x12, 0x20, 0x0a, 0x0b, 0x75, 0x74, 0x69, 0x6c, 0x69, 0x7a, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, 0x01, 0x52, 0x0b, 0x75, 0x74, 0x69, 0x6c, - 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1f, 0x0a, 0x0b, 0x6d, 0x65, 0x6d, 0x6f, 0x72, - 0x79, 0x5f, 0x75, 0x73, 0x65, 0x64, 0x18, 0x07, 0x20, 0x01, 0x28, 0x01, 0x52, 0x0a, 0x6d, 0x65, - 0x6d, 0x6f, 0x72, 0x79, 0x55, 0x73, 0x65, 0x64, 0x12, 0x1f, 0x0a, 0x0b, 0x6d, 0x65, 0x6d, 0x6f, - 0x72, 0x79, 0x5f, 0x66, 0x72, 0x65, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x01, 0x52, 0x0a, 0x6d, - 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x46, 0x72, 0x65, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x74, 0x6f, 0x74, - 0x61, 0x6c, 0x5f, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x18, 0x09, 0x20, 0x01, 0x28, 0x01, 0x52, - 0x0b, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x4d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x12, 0x42, 0x0a, 0x1d, - 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x5f, 0x75, 0x74, 0x69, 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x5f, 0x70, 0x65, 0x72, 0x63, 0x65, 0x6e, 0x74, 0x61, 0x67, 0x65, 0x18, 0x0a, 0x20, - 0x01, 0x28, 0x01, 0x52, 0x1b, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x55, 0x74, 0x69, 0x6c, 0x69, - 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x65, 0x72, 0x63, 0x65, 0x6e, 0x74, 0x61, 0x67, 0x65, - 0x12, 0x1f, 0x0a, 0x0b, 0x70, 0x6f, 0x77, 0x65, 0x72, 0x5f, 0x75, 0x73, 0x61, 0x67, 0x65, 0x18, - 0x0b, 0x20, 0x01, 0x28, 0x01, 0x52, 0x0a, 0x70, 0x6f, 0x77, 0x65, 0x72, 0x55, 0x73, 0x61, 0x67, - 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x74, 0x65, 0x6d, 0x70, 0x65, 0x72, 0x61, 0x74, 0x75, 0x72, 0x65, - 0x18, 0x0c, 0x20, 0x01, 0x28, 0x01, 0x52, 0x0b, 0x74, 0x65, 0x6d, 0x70, 0x65, 0x72, 0x61, 0x74, - 0x75, 0x72, 0x65, 0x12, 0x19, 0x0a, 0x08, 0x73, 0x6d, 0x5f, 0x63, 0x6c, 0x6f, 0x63, 0x6b, 0x18, - 0x0d, 0x20, 0x01, 0x28, 0x01, 0x52, 0x07, 0x73, 0x6d, 0x43, 0x6c, 0x6f, 0x63, 0x6b, 0x12, 0x1b, - 0x0a, 0x09, 0x6d, 0x65, 0x6d, 0x5f, 0x63, 0x6c, 0x6f, 0x63, 0x6b, 0x18, 0x0e, 0x20, 0x01, 0x28, - 0x01, 0x52, 0x08, 0x6d, 0x65, 0x6d, 0x43, 0x6c, 0x6f, 0x63, 0x6b, 0x22, 0xd6, 0x01, 0x0a, 0x1d, - 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x49, 0x6e, 0x64, 0x69, 0x76, 0x69, 0x64, - 0x75, 0x61, 0x6c, 0x47, 0x50, 0x55, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x12, 0x25, 0x0a, - 0x0e, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, - 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x39, 0x0a, 0x09, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, - 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, - 0x2e, 0x49, 0x6e, 0x64, 0x69, 0x76, 0x69, 0x64, 0x75, 0x61, 0x6c, 0x47, 0x50, 0x55, 0x4d, 0x65, - 0x74, 0x72, 0x69, 0x63, 0x52, 0x09, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x73, 0x12, - 0x42, 0x0a, 0x0c, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, - 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, - 0x70, 0x48, 0x00, 0x52, 0x0b, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x65, 0x64, 0x41, 0x74, - 0x88, 0x01, 0x01, 0x42, 0x0f, 0x0a, 0x0d, 0x5f, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x65, - 0x64, 0x5f, 0x61, 0x74, 0x22, 0x8c, 0x01, 0x0a, 0x0e, 0x52, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, - 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x75, 0x6e, 0x74, 0x69, - 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, - 0x65, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x25, 0x0a, 0x0e, 0x76, - 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, 0x03, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x0d, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x53, 0x6f, 0x75, 0x72, - 0x63, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x72, 0x61, 0x77, 0x5f, 0x63, 0x6d, 0x64, 0x6c, 0x69, 0x6e, - 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x72, 0x61, 0x77, 0x43, 0x6d, 0x64, 0x6c, - 0x69, 0x6e, 0x65, 0x22, 0xf2, 0x0c, 0x0a, 0x17, 0x46, 0x6f, 0x72, 0x65, 0x63, 0x61, 0x73, 0x74, - 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x12, - 0x38, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, - 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x6c, 0x75, - 0x73, 0x74, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, - 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, 0x64, 0x12, 0x25, 0x0a, 0x0e, 0x61, 0x70, 0x70, 0x6c, - 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x0d, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, - 0x29, 0x0a, 0x10, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6b, - 0x69, 0x6e, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x61, 0x70, 0x70, 0x6c, 0x69, - 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4b, 0x69, 0x6e, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x6d, 0x6f, - 0x64, 0x65, 0x6c, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x6d, 0x6f, 0x64, 0x65, 0x6c, - 0x12, 0x2a, 0x0a, 0x11, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x63, 0x70, 0x75, 0x5f, 0x63, 0x61, 0x70, - 0x61, 0x63, 0x69, 0x74, 0x79, 0x18, 0x06, 0x20, 0x01, 0x28, 0x01, 0x52, 0x0f, 0x6e, 0x6f, 0x64, - 0x65, 0x43, 0x70, 0x75, 0x43, 0x61, 0x70, 0x61, 0x63, 0x69, 0x74, 0x79, 0x12, 0x30, 0x0a, 0x14, - 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x5f, 0x63, 0x61, 0x70, 0x61, - 0x63, 0x69, 0x74, 0x79, 0x18, 0x07, 0x20, 0x01, 0x28, 0x01, 0x52, 0x12, 0x6e, 0x6f, 0x64, 0x65, - 0x4d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x43, 0x61, 0x70, 0x61, 0x63, 0x69, 0x74, 0x79, 0x12, 0x24, - 0x0a, 0x0e, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x63, 0x70, 0x75, 0x5f, 0x75, 0x73, 0x61, 0x67, 0x65, - 0x18, 0x08, 0x20, 0x01, 0x28, 0x01, 0x52, 0x0c, 0x6e, 0x6f, 0x64, 0x65, 0x43, 0x70, 0x75, 0x55, - 0x73, 0x61, 0x67, 0x65, 0x12, 0x2a, 0x0a, 0x11, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x6d, 0x65, 0x6d, - 0x6f, 0x72, 0x79, 0x5f, 0x75, 0x73, 0x61, 0x67, 0x65, 0x18, 0x09, 0x20, 0x01, 0x28, 0x01, 0x52, - 0x0f, 0x6e, 0x6f, 0x64, 0x65, 0x4d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x55, 0x73, 0x61, 0x67, 0x65, - 0x12, 0x30, 0x0a, 0x14, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x63, 0x70, 0x75, 0x5f, 0x75, 0x74, 0x69, - 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x01, 0x52, 0x12, - 0x6e, 0x6f, 0x64, 0x65, 0x43, 0x70, 0x75, 0x55, 0x74, 0x69, 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x12, 0x36, 0x0a, 0x17, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x6d, 0x65, 0x6d, 0x6f, 0x72, - 0x79, 0x5f, 0x75, 0x74, 0x69, 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x0b, 0x20, - 0x01, 0x28, 0x01, 0x52, 0x15, 0x6e, 0x6f, 0x64, 0x65, 0x4d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x55, - 0x74, 0x69, 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x2e, 0x0a, 0x13, 0x63, 0x6f, - 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x63, 0x70, 0x75, 0x5f, 0x75, 0x73, 0x61, 0x67, - 0x65, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x01, 0x52, 0x11, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, - 0x65, 0x72, 0x43, 0x70, 0x75, 0x55, 0x73, 0x61, 0x67, 0x65, 0x12, 0x34, 0x0a, 0x16, 0x63, 0x6f, - 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x5f, 0x75, - 0x73, 0x61, 0x67, 0x65, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x01, 0x52, 0x14, 0x63, 0x6f, 0x6e, 0x74, - 0x61, 0x69, 0x6e, 0x65, 0x72, 0x4d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x55, 0x73, 0x61, 0x67, 0x65, - 0x12, 0x3a, 0x0a, 0x19, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x63, 0x70, - 0x75, 0x5f, 0x75, 0x74, 0x69, 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x0e, 0x20, - 0x01, 0x28, 0x01, 0x52, 0x17, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x43, 0x70, - 0x75, 0x55, 0x74, 0x69, 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x40, 0x0a, 0x1c, - 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, - 0x5f, 0x75, 0x74, 0x69, 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x0f, 0x20, 0x01, - 0x28, 0x01, 0x52, 0x1a, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x4d, 0x65, 0x6d, - 0x6f, 0x72, 0x79, 0x55, 0x74, 0x69, 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x36, - 0x0a, 0x17, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x63, 0x70, 0x75, 0x5f, - 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x65, 0x64, 0x18, 0x10, 0x20, 0x01, 0x28, 0x01, 0x52, - 0x15, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x43, 0x70, 0x75, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x65, 0x64, 0x12, 0x3c, 0x0a, 0x1a, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, - 0x6e, 0x65, 0x72, 0x5f, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x65, 0x64, 0x18, 0x11, 0x20, 0x01, 0x28, 0x01, 0x52, 0x18, 0x63, 0x6f, 0x6e, 0x74, - 0x61, 0x69, 0x6e, 0x65, 0x72, 0x4d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x65, 0x64, 0x12, 0x30, 0x0a, 0x14, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, - 0x72, 0x5f, 0x63, 0x70, 0x75, 0x5f, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x73, 0x18, 0x12, 0x20, 0x01, - 0x28, 0x01, 0x52, 0x12, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x43, 0x70, 0x75, - 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x73, 0x12, 0x36, 0x0a, 0x17, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, - 0x6e, 0x65, 0x72, 0x5f, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x5f, 0x6c, 0x69, 0x6d, 0x69, 0x74, - 0x73, 0x18, 0x13, 0x20, 0x01, 0x28, 0x01, 0x52, 0x15, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, - 0x65, 0x72, 0x4d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x73, 0x12, 0x2e, - 0x0a, 0x13, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x67, 0x70, 0x75, 0x5f, - 0x75, 0x73, 0x61, 0x67, 0x65, 0x18, 0x14, 0x20, 0x01, 0x28, 0x01, 0x52, 0x11, 0x63, 0x6f, 0x6e, - 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x47, 0x70, 0x75, 0x55, 0x73, 0x61, 0x67, 0x65, 0x12, 0x36, - 0x0a, 0x17, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x67, 0x70, 0x75, 0x5f, - 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x65, 0x64, 0x18, 0x15, 0x20, 0x01, 0x28, 0x01, 0x52, - 0x15, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x47, 0x70, 0x75, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x65, 0x64, 0x12, 0x30, 0x0a, 0x14, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, - 0x6e, 0x65, 0x72, 0x5f, 0x67, 0x70, 0x75, 0x5f, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x73, 0x18, 0x16, - 0x20, 0x01, 0x28, 0x01, 0x52, 0x12, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x47, - 0x70, 0x75, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x73, 0x12, 0x3a, 0x0a, 0x19, 0x63, 0x6f, 0x6e, 0x74, - 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x67, 0x70, 0x75, 0x5f, 0x75, 0x74, 0x69, 0x6c, 0x69, 0x7a, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x17, 0x20, 0x01, 0x28, 0x01, 0x52, 0x17, 0x63, 0x6f, 0x6e, - 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x47, 0x70, 0x75, 0x55, 0x74, 0x69, 0x6c, 0x69, 0x7a, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x2a, 0x0a, 0x11, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x67, 0x70, 0x75, - 0x5f, 0x63, 0x61, 0x70, 0x61, 0x63, 0x69, 0x74, 0x79, 0x18, 0x18, 0x20, 0x01, 0x28, 0x01, 0x52, - 0x0f, 0x6e, 0x6f, 0x64, 0x65, 0x47, 0x70, 0x75, 0x43, 0x61, 0x70, 0x61, 0x63, 0x69, 0x74, 0x79, - 0x12, 0x24, 0x0a, 0x0e, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x67, 0x70, 0x75, 0x5f, 0x75, 0x73, 0x61, - 0x67, 0x65, 0x18, 0x19, 0x20, 0x01, 0x28, 0x01, 0x52, 0x0c, 0x6e, 0x6f, 0x64, 0x65, 0x47, 0x70, - 0x75, 0x55, 0x73, 0x61, 0x67, 0x65, 0x12, 0x30, 0x0a, 0x14, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x67, - 0x70, 0x75, 0x5f, 0x75, 0x74, 0x69, 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x1a, - 0x20, 0x01, 0x28, 0x01, 0x52, 0x12, 0x6e, 0x6f, 0x64, 0x65, 0x47, 0x70, 0x75, 0x55, 0x74, 0x69, - 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x2d, 0x0a, 0x13, 0x6e, 0x6f, 0x64, 0x65, - 0x5f, 0x67, 0x70, 0x75, 0x5f, 0x76, 0x72, 0x61, 0x6d, 0x5f, 0x75, 0x73, 0x61, 0x67, 0x65, 0x18, - 0x1b, 0x20, 0x01, 0x28, 0x01, 0x52, 0x10, 0x6e, 0x6f, 0x64, 0x65, 0x47, 0x70, 0x75, 0x56, 0x72, - 0x61, 0x6d, 0x55, 0x73, 0x61, 0x67, 0x65, 0x12, 0x37, 0x0a, 0x18, 0x63, 0x6f, 0x6e, 0x74, 0x61, - 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x67, 0x70, 0x75, 0x5f, 0x76, 0x72, 0x61, 0x6d, 0x5f, 0x75, 0x73, - 0x61, 0x67, 0x65, 0x18, 0x1c, 0x20, 0x01, 0x28, 0x01, 0x52, 0x15, 0x63, 0x6f, 0x6e, 0x74, 0x61, - 0x69, 0x6e, 0x65, 0x72, 0x47, 0x70, 0x75, 0x56, 0x72, 0x61, 0x6d, 0x55, 0x73, 0x61, 0x67, 0x65, - 0x12, 0x33, 0x0a, 0x16, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x67, 0x70, 0x75, 0x5f, 0x76, 0x72, 0x61, - 0x6d, 0x5f, 0x63, 0x61, 0x70, 0x61, 0x63, 0x69, 0x74, 0x79, 0x18, 0x1d, 0x20, 0x01, 0x28, 0x01, - 0x52, 0x13, 0x6e, 0x6f, 0x64, 0x65, 0x47, 0x70, 0x75, 0x56, 0x72, 0x61, 0x6d, 0x43, 0x61, 0x70, - 0x61, 0x63, 0x69, 0x74, 0x79, 0x12, 0x39, 0x0a, 0x19, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x67, 0x70, - 0x75, 0x5f, 0x76, 0x72, 0x61, 0x6d, 0x5f, 0x75, 0x74, 0x69, 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x18, 0x1e, 0x20, 0x01, 0x28, 0x01, 0x52, 0x16, 0x6e, 0x6f, 0x64, 0x65, 0x47, 0x70, - 0x75, 0x56, 0x72, 0x61, 0x6d, 0x55, 0x74, 0x69, 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x12, 0x45, 0x0a, 0x1f, 0x6e, 0x6f, 0x72, 0x6d, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x5f, 0x6e, - 0x6f, 0x64, 0x65, 0x5f, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x5f, 0x63, 0x61, 0x70, 0x61, 0x63, - 0x69, 0x74, 0x79, 0x18, 0x1f, 0x20, 0x01, 0x28, 0x01, 0x52, 0x1c, 0x6e, 0x6f, 0x72, 0x6d, 0x61, - 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x4e, 0x6f, 0x64, 0x65, 0x4d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x43, - 0x61, 0x70, 0x61, 0x63, 0x69, 0x74, 0x79, 0x12, 0x3f, 0x0a, 0x1c, 0x6e, 0x6f, 0x72, 0x6d, 0x61, - 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x5f, 0x67, 0x70, 0x75, 0x5f, 0x76, 0x72, 0x61, 0x6d, 0x5f, 0x63, - 0x61, 0x70, 0x61, 0x63, 0x69, 0x74, 0x79, 0x18, 0x20, 0x20, 0x01, 0x28, 0x01, 0x52, 0x19, 0x6e, - 0x6f, 0x72, 0x6d, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x47, 0x70, 0x75, 0x56, 0x72, 0x61, 0x6d, - 0x43, 0x61, 0x70, 0x61, 0x63, 0x69, 0x74, 0x79, 0x22, 0xe8, 0x04, 0x0a, 0x0f, 0x52, 0x65, 0x73, - 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x12, 0x29, 0x0a, 0x10, - 0x64, 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0f, 0x64, 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x6d, 0x65, - 0x6e, 0x74, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x2c, 0x0a, 0x12, 0x73, 0x74, 0x61, 0x74, 0x65, - 0x66, 0x75, 0x6c, 0x5f, 0x73, 0x65, 0x74, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x05, 0x52, 0x10, 0x73, 0x74, 0x61, 0x74, 0x65, 0x66, 0x75, 0x6c, 0x53, 0x65, 0x74, - 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x28, 0x0a, 0x10, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x5f, - 0x73, 0x65, 0x74, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, - 0x0e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x53, 0x65, 0x74, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, - 0x1b, 0x0a, 0x09, 0x6a, 0x6f, 0x62, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x04, 0x20, 0x01, - 0x28, 0x05, 0x52, 0x08, 0x6a, 0x6f, 0x62, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x24, 0x0a, 0x0e, - 0x63, 0x72, 0x6f, 0x6e, 0x5f, 0x6a, 0x6f, 0x62, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x05, - 0x20, 0x01, 0x28, 0x05, 0x52, 0x0c, 0x63, 0x72, 0x6f, 0x6e, 0x4a, 0x6f, 0x62, 0x43, 0x6f, 0x75, - 0x6e, 0x74, 0x12, 0x2a, 0x0a, 0x11, 0x72, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x5f, 0x73, 0x65, - 0x74, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0f, 0x72, - 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x53, 0x65, 0x74, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x1b, - 0x0a, 0x09, 0x70, 0x6f, 0x64, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x07, 0x20, 0x01, 0x28, - 0x05, 0x52, 0x08, 0x70, 0x6f, 0x64, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x27, 0x0a, 0x0f, 0x63, - 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x08, - 0x20, 0x01, 0x28, 0x05, 0x52, 0x0e, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x43, - 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x21, 0x0a, 0x0c, 0x70, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x5f, - 0x70, 0x6f, 0x64, 0x73, 0x18, 0x09, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0b, 0x70, 0x65, 0x6e, 0x64, - 0x69, 0x6e, 0x67, 0x50, 0x6f, 0x64, 0x73, 0x12, 0x21, 0x0a, 0x0c, 0x72, 0x75, 0x6e, 0x6e, 0x69, - 0x6e, 0x67, 0x5f, 0x70, 0x6f, 0x64, 0x73, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0b, 0x72, - 0x75, 0x6e, 0x6e, 0x69, 0x6e, 0x67, 0x50, 0x6f, 0x64, 0x73, 0x12, 0x25, 0x0a, 0x0e, 0x73, 0x75, - 0x63, 0x63, 0x65, 0x65, 0x64, 0x65, 0x64, 0x5f, 0x70, 0x6f, 0x64, 0x73, 0x18, 0x0b, 0x20, 0x01, - 0x28, 0x05, 0x52, 0x0d, 0x73, 0x75, 0x63, 0x63, 0x65, 0x65, 0x64, 0x65, 0x64, 0x50, 0x6f, 0x64, - 0x73, 0x12, 0x1f, 0x0a, 0x0b, 0x66, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x5f, 0x70, 0x6f, 0x64, 0x73, - 0x18, 0x0c, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0a, 0x66, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x50, 0x6f, - 0x64, 0x73, 0x12, 0x21, 0x0a, 0x0c, 0x75, 0x6e, 0x6b, 0x6e, 0x6f, 0x77, 0x6e, 0x5f, 0x70, 0x6f, - 0x64, 0x73, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0b, 0x75, 0x6e, 0x6b, 0x6e, 0x6f, 0x77, - 0x6e, 0x50, 0x6f, 0x64, 0x73, 0x12, 0x36, 0x0a, 0x17, 0x75, 0x6e, 0x64, 0x65, 0x72, 0x5f, 0x70, - 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x64, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, - 0x18, 0x0e, 0x20, 0x01, 0x28, 0x05, 0x52, 0x15, 0x75, 0x6e, 0x64, 0x65, 0x72, 0x50, 0x72, 0x6f, - 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x64, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x34, 0x0a, - 0x16, 0x6f, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, - 0x64, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x05, 0x52, 0x14, 0x6f, - 0x76, 0x65, 0x72, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x64, 0x43, 0x6f, - 0x75, 0x6e, 0x74, 0x22, 0x4f, 0x0a, 0x0f, 0x4c, 0x65, 0x67, 0x61, 0x63, 0x79, 0x50, 0x6f, 0x6c, - 0x69, 0x63, 0x79, 0x52, 0x65, 0x66, 0x12, 0x1b, 0x0a, 0x09, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, - 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x6f, 0x6c, 0x69, 0x63, - 0x79, 0x49, 0x64, 0x12, 0x1f, 0x0a, 0x0b, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x5f, 0x6e, 0x61, - 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, - 0x4e, 0x61, 0x6d, 0x65, 0x22, 0xb6, 0x02, 0x0a, 0x12, 0x4f, 0x70, 0x74, 0x69, 0x6d, 0x69, 0x7a, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x21, 0x0a, 0x0c, 0x69, - 0x73, 0x5f, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x69, 0x7a, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x08, 0x52, 0x0b, 0x69, 0x73, 0x4f, 0x70, 0x74, 0x69, 0x6d, 0x69, 0x7a, 0x65, 0x64, 0x12, 0x2e, - 0x0a, 0x13, 0x69, 0x73, 0x5f, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x69, 0x7a, 0x65, 0x64, 0x5f, 0x6c, - 0x65, 0x67, 0x61, 0x63, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x11, 0x69, 0x73, 0x4f, - 0x70, 0x74, 0x69, 0x6d, 0x69, 0x7a, 0x65, 0x64, 0x4c, 0x65, 0x67, 0x61, 0x63, 0x79, 0x12, 0x26, - 0x0a, 0x0f, 0x69, 0x73, 0x5f, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x69, 0x7a, 0x65, 0x64, 0x5f, 0x76, - 0x32, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0d, 0x69, 0x73, 0x4f, 0x70, 0x74, 0x69, 0x6d, - 0x69, 0x7a, 0x65, 0x64, 0x56, 0x32, 0x12, 0x40, 0x0a, 0x0f, 0x6c, 0x65, 0x67, 0x61, 0x63, 0x79, - 0x5f, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x69, 0x65, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, - 0x17, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x65, 0x67, 0x61, 0x63, 0x79, 0x50, - 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x52, 0x65, 0x66, 0x52, 0x0e, 0x6c, 0x65, 0x67, 0x61, 0x63, 0x79, - 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x69, 0x65, 0x73, 0x12, 0x28, 0x0a, 0x10, 0x77, 0x6f, 0x72, 0x6b, - 0x6c, 0x6f, 0x61, 0x64, 0x5f, 0x72, 0x75, 0x6c, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x05, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x0e, 0x77, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x75, 0x6c, 0x65, - 0x49, 0x64, 0x12, 0x39, 0x0a, 0x19, 0x77, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x5f, 0x72, - 0x75, 0x6c, 0x65, 0x5f, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, - 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x16, 0x77, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x52, - 0x75, 0x6c, 0x65, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x4e, 0x61, 0x6d, 0x65, 0x22, 0xdb, 0x0b, - 0x0a, 0x0c, 0x57, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x49, 0x74, 0x65, 0x6d, 0x12, 0x12, - 0x0a, 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6b, 0x69, - 0x6e, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x69, 0x64, 0x18, 0x03, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x69, 0x64, 0x12, 0x1c, 0x0a, 0x09, 0x6e, 0x61, 0x6d, 0x65, - 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6e, 0x61, 0x6d, - 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x5f, - 0x6b, 0x69, 0x6e, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6f, 0x77, 0x6e, 0x65, - 0x72, 0x4b, 0x69, 0x6e, 0x64, 0x12, 0x1b, 0x0a, 0x09, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x5f, 0x75, - 0x69, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x55, - 0x69, 0x64, 0x12, 0x30, 0x0a, 0x08, 0x63, 0x68, 0x69, 0x6c, 0x64, 0x72, 0x65, 0x6e, 0x18, 0x07, - 0x20, 0x03, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x57, 0x6f, - 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x49, 0x74, 0x65, 0x6d, 0x52, 0x08, 0x63, 0x68, 0x69, 0x6c, - 0x64, 0x72, 0x65, 0x6e, 0x12, 0x38, 0x0a, 0x06, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x18, 0x08, - 0x20, 0x03, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x57, 0x6f, - 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x49, 0x74, 0x65, 0x6d, 0x2e, 0x4c, 0x61, 0x62, 0x65, 0x6c, - 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x06, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x12, 0x47, - 0x0a, 0x0b, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x09, 0x20, - 0x03, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x57, 0x6f, 0x72, - 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x49, 0x74, 0x65, 0x6d, 0x2e, 0x41, 0x6e, 0x6e, 0x6f, 0x74, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0b, 0x61, 0x6e, 0x6e, 0x6f, - 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x2d, 0x0a, 0x12, 0x63, 0x72, 0x65, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x0a, 0x20, - 0x01, 0x28, 0x03, 0x52, 0x11, 0x63, 0x72, 0x65, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x69, 0x6d, - 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x42, 0x0a, 0x10, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, - 0x63, 0x65, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x17, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, - 0x63, 0x65, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x52, 0x0f, 0x72, 0x65, 0x73, 0x6f, 0x75, - 0x72, 0x63, 0x65, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x12, 0x2d, 0x0a, 0x09, 0x63, 0x6f, - 0x73, 0x74, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, - 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x73, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, - 0x08, 0x63, 0x6f, 0x73, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x3f, 0x0a, 0x10, 0x63, 0x6f, 0x73, - 0x74, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x5f, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x18, 0x0d, 0x20, - 0x03, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x73, - 0x74, 0x44, 0x61, 0x74, 0x61, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x52, 0x0e, 0x63, 0x6f, 0x73, 0x74, - 0x44, 0x61, 0x74, 0x61, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x12, 0x4b, 0x0a, 0x14, 0x72, 0x65, - 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x5f, 0x70, 0x6f, 0x69, 0x6e, - 0x74, 0x73, 0x18, 0x0e, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, - 0x31, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x44, 0x61, 0x74, 0x61, 0x50, 0x6f, - 0x69, 0x6e, 0x74, 0x52, 0x12, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x44, 0x61, 0x74, - 0x61, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x12, 0x21, 0x0a, 0x0c, 0x69, 0x6d, 0x70, 0x61, 0x63, - 0x74, 0x5f, 0x73, 0x63, 0x6f, 0x72, 0x65, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x01, 0x52, 0x0b, 0x69, - 0x6d, 0x70, 0x61, 0x63, 0x74, 0x53, 0x63, 0x6f, 0x72, 0x65, 0x12, 0x39, 0x0a, 0x0a, 0x63, 0x72, - 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x10, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, - 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, - 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x63, 0x72, 0x65, 0x61, - 0x74, 0x65, 0x64, 0x41, 0x74, 0x12, 0x3e, 0x0a, 0x0a, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x64, - 0x5f, 0x61, 0x74, 0x18, 0x11, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, + 0x50, 0x65, 0x72, 0x63, 0x65, 0x6e, 0x74, 0x69, 0x6c, 0x65, 0x73, 0x52, 0x08, 0x63, 0x70, 0x75, + 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x12, 0x3c, 0x0a, 0x0c, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x5f, + 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x61, 0x70, + 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x50, 0x65, 0x72, 0x63, 0x65, + 0x6e, 0x74, 0x69, 0x6c, 0x65, 0x73, 0x52, 0x0b, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x4c, 0x69, + 0x6d, 0x69, 0x74, 0x12, 0x36, 0x0a, 0x09, 0x67, 0x70, 0x75, 0x5f, 0x75, 0x73, 0x61, 0x67, 0x65, + 0x18, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, + 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x50, 0x65, 0x72, 0x63, 0x65, 0x6e, 0x74, 0x69, 0x6c, 0x65, + 0x73, 0x52, 0x08, 0x67, 0x70, 0x75, 0x55, 0x73, 0x61, 0x67, 0x65, 0x12, 0x3f, 0x0a, 0x0e, 0x67, + 0x70, 0x75, 0x5f, 0x76, 0x72, 0x61, 0x6d, 0x5f, 0x75, 0x73, 0x61, 0x67, 0x65, 0x18, 0x09, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x65, 0x74, + 0x72, 0x69, 0x63, 0x50, 0x65, 0x72, 0x63, 0x65, 0x6e, 0x74, 0x69, 0x6c, 0x65, 0x73, 0x52, 0x0c, + 0x67, 0x70, 0x75, 0x56, 0x72, 0x61, 0x6d, 0x55, 0x73, 0x61, 0x67, 0x65, 0x12, 0x32, 0x0a, 0x13, + 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x63, 0x70, 0x75, 0x5f, 0x72, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x01, 0x42, 0x02, 0x18, 0x01, 0x52, 0x11, 0x63, + 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x43, 0x70, 0x75, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x12, 0x38, 0x0a, 0x16, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x6d, 0x65, 0x6d, 0x6f, + 0x72, 0x79, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x01, + 0x42, 0x02, 0x18, 0x01, 0x52, 0x14, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x4d, 0x65, 0x6d, + 0x6f, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2e, 0x0a, 0x11, 0x63, 0x75, + 0x72, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x63, 0x70, 0x75, 0x5f, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x18, + 0x0c, 0x20, 0x01, 0x28, 0x01, 0x42, 0x02, 0x18, 0x01, 0x52, 0x0f, 0x63, 0x75, 0x72, 0x72, 0x65, + 0x6e, 0x74, 0x43, 0x70, 0x75, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x12, 0x34, 0x0a, 0x14, 0x63, 0x75, + 0x72, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x5f, 0x6c, 0x69, 0x6d, + 0x69, 0x74, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x01, 0x42, 0x02, 0x18, 0x01, 0x52, 0x12, 0x63, 0x75, + 0x72, 0x72, 0x65, 0x6e, 0x74, 0x4d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x4c, 0x69, 0x6d, 0x69, 0x74, + 0x12, 0x4d, 0x0a, 0x15, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x5f, 0x72, 0x65, 0x63, 0x65, + 0x69, 0x76, 0x65, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x19, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x50, + 0x65, 0x72, 0x63, 0x65, 0x6e, 0x74, 0x69, 0x6c, 0x65, 0x73, 0x52, 0x13, 0x6e, 0x65, 0x74, 0x77, + 0x6f, 0x72, 0x6b, 0x52, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x42, 0x79, 0x74, 0x65, 0x73, 0x12, + 0x4f, 0x0a, 0x16, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x5f, 0x74, 0x72, 0x61, 0x6e, 0x73, + 0x6d, 0x69, 0x74, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x19, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x50, + 0x65, 0x72, 0x63, 0x65, 0x6e, 0x74, 0x69, 0x6c, 0x65, 0x73, 0x52, 0x14, 0x6e, 0x65, 0x74, 0x77, + 0x6f, 0x72, 0x6b, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x6d, 0x69, 0x74, 0x42, 0x79, 0x74, 0x65, 0x73, + 0x12, 0x41, 0x0a, 0x0d, 0x66, 0x73, 0x5f, 0x72, 0x65, 0x61, 0x64, 0x5f, 0x62, 0x79, 0x74, 0x65, + 0x73, 0x18, 0x10, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, + 0x2e, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x50, 0x65, 0x72, 0x63, 0x65, 0x6e, 0x74, 0x69, 0x6c, + 0x65, 0x73, 0x42, 0x02, 0x18, 0x01, 0x52, 0x0b, 0x66, 0x73, 0x52, 0x65, 0x61, 0x64, 0x42, 0x79, + 0x74, 0x65, 0x73, 0x12, 0x43, 0x0a, 0x0e, 0x66, 0x73, 0x5f, 0x77, 0x72, 0x69, 0x74, 0x65, 0x5f, + 0x62, 0x79, 0x74, 0x65, 0x73, 0x18, 0x11, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x61, 0x70, + 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x50, 0x65, 0x72, 0x63, 0x65, + 0x6e, 0x74, 0x69, 0x6c, 0x65, 0x73, 0x42, 0x02, 0x18, 0x01, 0x52, 0x0c, 0x66, 0x73, 0x57, 0x72, + 0x69, 0x74, 0x65, 0x42, 0x79, 0x74, 0x65, 0x73, 0x22, 0x9d, 0x05, 0x0a, 0x16, 0x43, 0x6f, 0x6e, + 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x4c, 0x69, 0x6d, + 0x69, 0x74, 0x73, 0x12, 0x25, 0x0a, 0x0e, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, + 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x63, 0x6f, 0x6e, + 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x2e, 0x0a, 0x13, 0x63, 0x75, + 0x72, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x63, 0x70, 0x75, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x01, 0x52, 0x11, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, + 0x43, 0x70, 0x75, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x34, 0x0a, 0x16, 0x63, 0x75, + 0x72, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x5f, 0x72, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x01, 0x52, 0x14, 0x63, 0x75, 0x72, 0x72, + 0x65, 0x6e, 0x74, 0x4d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x12, 0x2a, 0x0a, 0x11, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x63, 0x70, 0x75, 0x5f, + 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x01, 0x52, 0x0f, 0x63, 0x75, 0x72, + 0x72, 0x65, 0x6e, 0x74, 0x43, 0x70, 0x75, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x12, 0x30, 0x0a, 0x14, + 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x5f, 0x6c, + 0x69, 0x6d, 0x69, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x01, 0x52, 0x12, 0x63, 0x75, 0x72, 0x72, + 0x65, 0x6e, 0x74, 0x4d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x12, 0x28, + 0x0a, 0x10, 0x6a, 0x76, 0x6d, 0x5f, 0x6a, 0x61, 0x76, 0x61, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, + 0x6f, 0x6e, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x6a, 0x76, 0x6d, 0x4a, 0x61, 0x76, + 0x61, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x28, 0x0a, 0x10, 0x6a, 0x76, 0x6d, 0x5f, + 0x6a, 0x61, 0x76, 0x61, 0x5f, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x18, 0x0b, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x0e, 0x6a, 0x76, 0x6d, 0x4a, 0x61, 0x76, 0x61, 0x43, 0x6f, 0x6d, 0x6d, 0x61, + 0x6e, 0x64, 0x12, 0x22, 0x0a, 0x0d, 0x6a, 0x76, 0x6d, 0x5f, 0x78, 0x6d, 0x78, 0x5f, 0x62, 0x79, + 0x74, 0x65, 0x73, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0b, 0x6a, 0x76, 0x6d, 0x58, 0x6d, + 0x78, 0x42, 0x79, 0x74, 0x65, 0x73, 0x12, 0x22, 0x0a, 0x0d, 0x6a, 0x76, 0x6d, 0x5f, 0x78, 0x6d, + 0x73, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0b, 0x6a, + 0x76, 0x6d, 0x58, 0x6d, 0x73, 0x42, 0x79, 0x74, 0x65, 0x73, 0x12, 0x33, 0x0a, 0x16, 0x6a, 0x76, + 0x6d, 0x5f, 0x6d, 0x61, 0x78, 0x5f, 0x72, 0x61, 0x6d, 0x5f, 0x70, 0x65, 0x72, 0x63, 0x65, 0x6e, + 0x74, 0x61, 0x67, 0x65, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x01, 0x52, 0x13, 0x6a, 0x76, 0x6d, 0x4d, + 0x61, 0x78, 0x52, 0x61, 0x6d, 0x50, 0x65, 0x72, 0x63, 0x65, 0x6e, 0x74, 0x61, 0x67, 0x65, 0x12, + 0x3e, 0x0a, 0x19, 0x6a, 0x76, 0x6d, 0x5f, 0x75, 0x73, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x61, + 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x18, 0x10, 0x20, 0x01, + 0x28, 0x08, 0x48, 0x00, 0x52, 0x16, 0x6a, 0x76, 0x6d, 0x55, 0x73, 0x65, 0x43, 0x6f, 0x6e, 0x74, + 0x61, 0x69, 0x6e, 0x65, 0x72, 0x53, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x88, 0x01, 0x01, 0x12, + 0x24, 0x0a, 0x0e, 0x6a, 0x76, 0x6d, 0x5f, 0x78, 0x6d, 0x78, 0x5f, 0x73, 0x6f, 0x75, 0x72, 0x63, + 0x65, 0x18, 0x11, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x6a, 0x76, 0x6d, 0x58, 0x6d, 0x78, 0x53, + 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x43, 0x0a, 0x11, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, + 0x5f, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x65, 0x73, 0x18, 0x12, 0x20, 0x03, 0x28, 0x0b, + 0x32, 0x16, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x75, 0x6e, 0x74, 0x69, 0x6d, + 0x65, 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x52, 0x10, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, + 0x65, 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x65, 0x73, 0x42, 0x1c, 0x0a, 0x1a, 0x5f, 0x6a, + 0x76, 0x6d, 0x5f, 0x75, 0x73, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, + 0x5f, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x22, 0x8a, 0x03, 0x0a, 0x0e, 0x47, 0x50, 0x55, + 0x4d, 0x69, 0x67, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x64, + 0x65, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x75, 0x75, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x0a, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x55, 0x75, 0x69, 0x64, 0x12, 0x1b, 0x0a, 0x09, + 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x08, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x49, 0x64, 0x12, 0x1f, 0x0a, 0x0b, 0x6d, 0x69, 0x67, + 0x5f, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, + 0x6d, 0x69, 0x67, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x12, 0x26, 0x0a, 0x0f, 0x6d, 0x69, + 0x67, 0x5f, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x04, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x0d, 0x6d, 0x69, 0x67, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, + 0x49, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x5f, 0x6e, 0x61, 0x6d, 0x65, + 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x4e, 0x61, 0x6d, + 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x74, 0x65, 0x6e, 0x73, 0x6f, 0x72, 0x5f, 0x61, 0x63, 0x74, 0x69, + 0x76, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x01, 0x52, 0x0c, 0x74, 0x65, 0x6e, 0x73, 0x6f, 0x72, + 0x41, 0x63, 0x74, 0x69, 0x76, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x64, 0x72, 0x61, 0x6d, 0x5f, 0x61, + 0x63, 0x74, 0x69, 0x76, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x01, 0x52, 0x0a, 0x64, 0x72, 0x61, + 0x6d, 0x41, 0x63, 0x74, 0x69, 0x76, 0x65, 0x12, 0x34, 0x0a, 0x16, 0x67, 0x72, 0x61, 0x70, 0x68, + 0x69, 0x63, 0x73, 0x5f, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x5f, 0x61, 0x63, 0x74, 0x69, 0x76, + 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x01, 0x52, 0x14, 0x67, 0x72, 0x61, 0x70, 0x68, 0x69, 0x63, + 0x73, 0x45, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x41, 0x63, 0x74, 0x69, 0x76, 0x65, 0x12, 0x29, 0x0a, + 0x10, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x5f, 0x75, 0x73, 0x65, + 0x64, 0x18, 0x09, 0x20, 0x01, 0x28, 0x01, 0x52, 0x0f, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x62, 0x75, + 0x66, 0x66, 0x65, 0x72, 0x55, 0x73, 0x65, 0x64, 0x12, 0x2b, 0x0a, 0x11, 0x66, 0x72, 0x61, 0x6d, + 0x65, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x5f, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x18, 0x0a, 0x20, + 0x01, 0x28, 0x01, 0x52, 0x10, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, + 0x54, 0x6f, 0x74, 0x61, 0x6c, 0x22, 0xfa, 0x03, 0x0a, 0x13, 0x49, 0x6e, 0x64, 0x69, 0x76, 0x69, + 0x64, 0x75, 0x61, 0x6c, 0x47, 0x50, 0x55, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x12, 0x12, 0x0a, + 0x04, 0x75, 0x75, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x75, 0x75, 0x69, + 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x4e, 0x61, 0x6d, 0x65, + 0x12, 0x21, 0x0a, 0x0c, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x49, 0x6e, + 0x64, 0x65, 0x78, 0x12, 0x1f, 0x0a, 0x0b, 0x6d, 0x69, 0x67, 0x5f, 0x70, 0x72, 0x6f, 0x66, 0x69, + 0x6c, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x6d, 0x69, 0x67, 0x50, 0x72, 0x6f, + 0x66, 0x69, 0x6c, 0x65, 0x12, 0x26, 0x0a, 0x0f, 0x6d, 0x69, 0x67, 0x5f, 0x69, 0x6e, 0x73, 0x74, + 0x61, 0x6e, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x6d, + 0x69, 0x67, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x49, 0x64, 0x12, 0x20, 0x0a, 0x0b, + 0x75, 0x74, 0x69, 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, + 0x01, 0x52, 0x0b, 0x75, 0x74, 0x69, 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1f, + 0x0a, 0x0b, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x5f, 0x75, 0x73, 0x65, 0x64, 0x18, 0x07, 0x20, + 0x01, 0x28, 0x01, 0x52, 0x0a, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x55, 0x73, 0x65, 0x64, 0x12, + 0x1f, 0x0a, 0x0b, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x5f, 0x66, 0x72, 0x65, 0x65, 0x18, 0x08, + 0x20, 0x01, 0x28, 0x01, 0x52, 0x0a, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x46, 0x72, 0x65, 0x65, + 0x12, 0x21, 0x0a, 0x0c, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, + 0x18, 0x09, 0x20, 0x01, 0x28, 0x01, 0x52, 0x0b, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x4d, 0x65, 0x6d, + 0x6f, 0x72, 0x79, 0x12, 0x42, 0x0a, 0x1d, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x5f, 0x75, 0x74, + 0x69, 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x70, 0x65, 0x72, 0x63, 0x65, 0x6e, + 0x74, 0x61, 0x67, 0x65, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x01, 0x52, 0x1b, 0x6d, 0x65, 0x6d, 0x6f, + 0x72, 0x79, 0x55, 0x74, 0x69, 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x65, 0x72, + 0x63, 0x65, 0x6e, 0x74, 0x61, 0x67, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x70, 0x6f, 0x77, 0x65, 0x72, + 0x5f, 0x75, 0x73, 0x61, 0x67, 0x65, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x01, 0x52, 0x0a, 0x70, 0x6f, + 0x77, 0x65, 0x72, 0x55, 0x73, 0x61, 0x67, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x74, 0x65, 0x6d, 0x70, + 0x65, 0x72, 0x61, 0x74, 0x75, 0x72, 0x65, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x01, 0x52, 0x0b, 0x74, + 0x65, 0x6d, 0x70, 0x65, 0x72, 0x61, 0x74, 0x75, 0x72, 0x65, 0x12, 0x19, 0x0a, 0x08, 0x73, 0x6d, + 0x5f, 0x63, 0x6c, 0x6f, 0x63, 0x6b, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x01, 0x52, 0x07, 0x73, 0x6d, + 0x43, 0x6c, 0x6f, 0x63, 0x6b, 0x12, 0x1b, 0x0a, 0x09, 0x6d, 0x65, 0x6d, 0x5f, 0x63, 0x6c, 0x6f, + 0x63, 0x6b, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x01, 0x52, 0x08, 0x6d, 0x65, 0x6d, 0x43, 0x6c, 0x6f, + 0x63, 0x6b, 0x22, 0xd6, 0x01, 0x0a, 0x1d, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, + 0x49, 0x6e, 0x64, 0x69, 0x76, 0x69, 0x64, 0x75, 0x61, 0x6c, 0x47, 0x50, 0x55, 0x4d, 0x65, 0x74, + 0x72, 0x69, 0x63, 0x73, 0x12, 0x25, 0x0a, 0x0e, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, + 0x72, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x63, 0x6f, + 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x39, 0x0a, 0x09, 0x69, + 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1b, + 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x49, 0x6e, 0x64, 0x69, 0x76, 0x69, 0x64, 0x75, + 0x61, 0x6c, 0x47, 0x50, 0x55, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x52, 0x09, 0x69, 0x6e, 0x73, + 0x74, 0x61, 0x6e, 0x63, 0x65, 0x73, 0x12, 0x42, 0x0a, 0x0c, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, + 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, + 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, + 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x48, 0x00, 0x52, 0x0b, 0x63, 0x6f, 0x6c, 0x6c, + 0x65, 0x63, 0x74, 0x65, 0x64, 0x41, 0x74, 0x88, 0x01, 0x01, 0x42, 0x0f, 0x0a, 0x0d, 0x5f, 0x63, + 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x22, 0x8c, 0x01, 0x0a, 0x0e, + 0x52, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x12, 0x18, + 0x0a, 0x07, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x07, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, + 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, + 0x6f, 0x6e, 0x12, 0x25, 0x0a, 0x0e, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x73, 0x6f, + 0x75, 0x72, 0x63, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x76, 0x65, 0x72, 0x73, + 0x69, 0x6f, 0x6e, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x72, 0x61, 0x77, + 0x5f, 0x63, 0x6d, 0x64, 0x6c, 0x69, 0x6e, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, + 0x72, 0x61, 0x77, 0x43, 0x6d, 0x64, 0x6c, 0x69, 0x6e, 0x65, 0x22, 0xf2, 0x0c, 0x0a, 0x17, 0x46, + 0x6f, 0x72, 0x65, 0x63, 0x61, 0x73, 0x74, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x4d, + 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x12, 0x38, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, + 0x61, 0x6d, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, - 0x73, 0x74, 0x61, 0x6d, 0x70, 0x48, 0x00, 0x52, 0x09, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x64, - 0x41, 0x74, 0x88, 0x01, 0x01, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, - 0x5f, 0x69, 0x64, 0x18, 0x12, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x6c, 0x75, 0x73, 0x74, - 0x65, 0x72, 0x49, 0x64, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x13, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x02, 0x69, 0x64, 0x12, 0x32, 0x0a, 0x15, 0x69, 0x73, 0x5f, 0x6b, 0x61, 0x72, 0x70, 0x65, - 0x6e, 0x74, 0x65, 0x72, 0x5f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, 0x14, 0x20, - 0x01, 0x28, 0x08, 0x52, 0x13, 0x69, 0x73, 0x4b, 0x61, 0x72, 0x70, 0x65, 0x6e, 0x74, 0x65, 0x72, - 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x47, 0x0a, 0x10, 0x72, 0x65, 0x73, 0x6f, - 0x75, 0x72, 0x63, 0x65, 0x5f, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x18, 0x1e, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x73, 0x6f, - 0x75, 0x72, 0x63, 0x65, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x48, 0x01, 0x52, 0x0f, 0x72, - 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x88, 0x01, - 0x01, 0x12, 0x1d, 0x0a, 0x0a, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x5f, 0x69, 0x64, 0x73, 0x18, - 0x38, 0x20, 0x03, 0x28, 0x09, 0x52, 0x09, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x49, 0x64, 0x73, - 0x12, 0x30, 0x0a, 0x14, 0x72, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x73, 0x5f, 0x66, 0x72, 0x6f, - 0x6d, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x4c, 0x20, 0x01, 0x28, 0x05, 0x52, 0x12, - 0x72, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x73, 0x46, 0x72, 0x6f, 0x6d, 0x53, 0x74, 0x61, 0x74, - 0x75, 0x73, 0x12, 0x2c, 0x0a, 0x12, 0x72, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x73, 0x5f, 0x66, - 0x72, 0x6f, 0x6d, 0x5f, 0x73, 0x70, 0x65, 0x63, 0x18, 0x4d, 0x20, 0x01, 0x28, 0x05, 0x52, 0x10, - 0x72, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x73, 0x46, 0x72, 0x6f, 0x6d, 0x53, 0x70, 0x65, 0x63, - 0x12, 0x32, 0x0a, 0x15, 0x72, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x73, 0x5f, 0x66, 0x72, 0x6f, - 0x6d, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x18, 0x4e, 0x20, 0x01, 0x28, 0x05, 0x52, - 0x13, 0x72, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x73, 0x46, 0x72, 0x6f, 0x6d, 0x4d, 0x65, 0x74, - 0x72, 0x69, 0x63, 0x73, 0x12, 0x26, 0x0a, 0x0f, 0x72, 0x6f, 0x6f, 0x74, 0x5f, 0x6f, 0x77, 0x6e, - 0x65, 0x72, 0x5f, 0x6b, 0x69, 0x6e, 0x64, 0x18, 0x5b, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x72, - 0x6f, 0x6f, 0x74, 0x4f, 0x77, 0x6e, 0x65, 0x72, 0x4b, 0x69, 0x6e, 0x64, 0x12, 0x24, 0x0a, 0x0e, - 0x72, 0x6f, 0x6f, 0x74, 0x5f, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x5f, 0x75, 0x69, 0x64, 0x18, 0x5c, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x72, 0x6f, 0x6f, 0x74, 0x4f, 0x77, 0x6e, 0x65, 0x72, 0x55, - 0x69, 0x64, 0x12, 0x50, 0x0a, 0x13, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x69, 0x7a, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x5d, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x1a, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x70, 0x74, 0x69, 0x6d, 0x69, 0x7a, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x48, 0x02, 0x52, 0x12, 0x6f, - 0x70, 0x74, 0x69, 0x6d, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x75, - 0x73, 0x88, 0x01, 0x01, 0x1a, 0x39, 0x0a, 0x0b, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x45, 0x6e, - 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, - 0x3e, 0x0a, 0x10, 0x41, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x45, 0x6e, - 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x42, - 0x0d, 0x0a, 0x0b, 0x5f, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x42, 0x13, - 0x0a, 0x11, 0x5f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x64, 0x65, 0x74, 0x61, - 0x69, 0x6c, 0x73, 0x42, 0x16, 0x0a, 0x14, 0x5f, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x69, 0x7a, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, 0x53, 0x0a, 0x14, 0x43, - 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x52, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x49, - 0x6e, 0x66, 0x6f, 0x12, 0x21, 0x0a, 0x0c, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x6e, - 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x72, 0x75, 0x6e, 0x74, 0x69, - 0x6d, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, - 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, - 0x22, 0xdb, 0x02, 0x0a, 0x08, 0x4e, 0x6f, 0x64, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x1d, 0x0a, - 0x0a, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x05, 0x52, 0x09, 0x6e, 0x6f, 0x64, 0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x26, 0x0a, 0x0f, - 0x6f, 0x6e, 0x5f, 0x64, 0x65, 0x6d, 0x61, 0x6e, 0x64, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0d, 0x6f, 0x6e, 0x44, 0x65, 0x6d, 0x61, 0x6e, 0x64, 0x43, - 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x25, 0x0a, 0x0e, 0x72, 0x65, 0x73, 0x65, 0x72, 0x76, 0x65, 0x64, - 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0d, 0x72, 0x65, - 0x73, 0x65, 0x72, 0x76, 0x65, 0x64, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x73, - 0x70, 0x6f, 0x74, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x52, - 0x09, 0x73, 0x70, 0x6f, 0x74, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x23, 0x0a, 0x0d, 0x75, 0x6e, - 0x6b, 0x6e, 0x6f, 0x77, 0x6e, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, - 0x05, 0x52, 0x0c, 0x75, 0x6e, 0x6b, 0x6e, 0x6f, 0x77, 0x6e, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, - 0x57, 0x0a, 0x13, 0x70, 0x72, 0x69, 0x63, 0x65, 0x5f, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, - 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x61, - 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x2e, 0x50, + 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, + 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, 0x64, 0x12, + 0x25, 0x0a, 0x0e, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69, + 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x29, 0x0a, 0x10, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6b, 0x69, 0x6e, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x0f, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4b, 0x69, 0x6e, + 0x64, 0x12, 0x14, 0x0a, 0x05, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x05, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x12, 0x2a, 0x0a, 0x11, 0x6e, 0x6f, 0x64, 0x65, 0x5f, + 0x63, 0x70, 0x75, 0x5f, 0x63, 0x61, 0x70, 0x61, 0x63, 0x69, 0x74, 0x79, 0x18, 0x06, 0x20, 0x01, + 0x28, 0x01, 0x52, 0x0f, 0x6e, 0x6f, 0x64, 0x65, 0x43, 0x70, 0x75, 0x43, 0x61, 0x70, 0x61, 0x63, + 0x69, 0x74, 0x79, 0x12, 0x30, 0x0a, 0x14, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x6d, 0x65, 0x6d, 0x6f, + 0x72, 0x79, 0x5f, 0x63, 0x61, 0x70, 0x61, 0x63, 0x69, 0x74, 0x79, 0x18, 0x07, 0x20, 0x01, 0x28, + 0x01, 0x52, 0x12, 0x6e, 0x6f, 0x64, 0x65, 0x4d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x43, 0x61, 0x70, + 0x61, 0x63, 0x69, 0x74, 0x79, 0x12, 0x24, 0x0a, 0x0e, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x63, 0x70, + 0x75, 0x5f, 0x75, 0x73, 0x61, 0x67, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x01, 0x52, 0x0c, 0x6e, + 0x6f, 0x64, 0x65, 0x43, 0x70, 0x75, 0x55, 0x73, 0x61, 0x67, 0x65, 0x12, 0x2a, 0x0a, 0x11, 0x6e, + 0x6f, 0x64, 0x65, 0x5f, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x5f, 0x75, 0x73, 0x61, 0x67, 0x65, + 0x18, 0x09, 0x20, 0x01, 0x28, 0x01, 0x52, 0x0f, 0x6e, 0x6f, 0x64, 0x65, 0x4d, 0x65, 0x6d, 0x6f, + 0x72, 0x79, 0x55, 0x73, 0x61, 0x67, 0x65, 0x12, 0x30, 0x0a, 0x14, 0x6e, 0x6f, 0x64, 0x65, 0x5f, + 0x63, 0x70, 0x75, 0x5f, 0x75, 0x74, 0x69, 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, + 0x0a, 0x20, 0x01, 0x28, 0x01, 0x52, 0x12, 0x6e, 0x6f, 0x64, 0x65, 0x43, 0x70, 0x75, 0x55, 0x74, + 0x69, 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x36, 0x0a, 0x17, 0x6e, 0x6f, 0x64, + 0x65, 0x5f, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x5f, 0x75, 0x74, 0x69, 0x6c, 0x69, 0x7a, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x01, 0x52, 0x15, 0x6e, 0x6f, 0x64, 0x65, + 0x4d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x55, 0x74, 0x69, 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x12, 0x2e, 0x0a, 0x13, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x63, + 0x70, 0x75, 0x5f, 0x75, 0x73, 0x61, 0x67, 0x65, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x01, 0x52, 0x11, + 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x43, 0x70, 0x75, 0x55, 0x73, 0x61, 0x67, + 0x65, 0x12, 0x34, 0x0a, 0x16, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x6d, + 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x5f, 0x75, 0x73, 0x61, 0x67, 0x65, 0x18, 0x0d, 0x20, 0x01, 0x28, + 0x01, 0x52, 0x14, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x4d, 0x65, 0x6d, 0x6f, + 0x72, 0x79, 0x55, 0x73, 0x61, 0x67, 0x65, 0x12, 0x3a, 0x0a, 0x19, 0x63, 0x6f, 0x6e, 0x74, 0x61, + 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x63, 0x70, 0x75, 0x5f, 0x75, 0x74, 0x69, 0x6c, 0x69, 0x7a, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x01, 0x52, 0x17, 0x63, 0x6f, 0x6e, 0x74, + 0x61, 0x69, 0x6e, 0x65, 0x72, 0x43, 0x70, 0x75, 0x55, 0x74, 0x69, 0x6c, 0x69, 0x7a, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x12, 0x40, 0x0a, 0x1c, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, + 0x5f, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x5f, 0x75, 0x74, 0x69, 0x6c, 0x69, 0x7a, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x01, 0x52, 0x1a, 0x63, 0x6f, 0x6e, 0x74, 0x61, + 0x69, 0x6e, 0x65, 0x72, 0x4d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x55, 0x74, 0x69, 0x6c, 0x69, 0x7a, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x36, 0x0a, 0x17, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, + 0x65, 0x72, 0x5f, 0x63, 0x70, 0x75, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x65, 0x64, + 0x18, 0x10, 0x20, 0x01, 0x28, 0x01, 0x52, 0x15, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, + 0x72, 0x43, 0x70, 0x75, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x65, 0x64, 0x12, 0x3c, 0x0a, + 0x1a, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x6d, 0x65, 0x6d, 0x6f, 0x72, + 0x79, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x65, 0x64, 0x18, 0x11, 0x20, 0x01, 0x28, + 0x01, 0x52, 0x18, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x4d, 0x65, 0x6d, 0x6f, + 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x65, 0x64, 0x12, 0x30, 0x0a, 0x14, 0x63, + 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x63, 0x70, 0x75, 0x5f, 0x6c, 0x69, 0x6d, + 0x69, 0x74, 0x73, 0x18, 0x12, 0x20, 0x01, 0x28, 0x01, 0x52, 0x12, 0x63, 0x6f, 0x6e, 0x74, 0x61, + 0x69, 0x6e, 0x65, 0x72, 0x43, 0x70, 0x75, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x73, 0x12, 0x36, 0x0a, + 0x17, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x6d, 0x65, 0x6d, 0x6f, 0x72, + 0x79, 0x5f, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x73, 0x18, 0x13, 0x20, 0x01, 0x28, 0x01, 0x52, 0x15, + 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x4d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x4c, + 0x69, 0x6d, 0x69, 0x74, 0x73, 0x12, 0x2e, 0x0a, 0x13, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, + 0x65, 0x72, 0x5f, 0x67, 0x70, 0x75, 0x5f, 0x75, 0x73, 0x61, 0x67, 0x65, 0x18, 0x14, 0x20, 0x01, + 0x28, 0x01, 0x52, 0x11, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x47, 0x70, 0x75, + 0x55, 0x73, 0x61, 0x67, 0x65, 0x12, 0x36, 0x0a, 0x17, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, + 0x65, 0x72, 0x5f, 0x67, 0x70, 0x75, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x65, 0x64, + 0x18, 0x15, 0x20, 0x01, 0x28, 0x01, 0x52, 0x15, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, + 0x72, 0x47, 0x70, 0x75, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x65, 0x64, 0x12, 0x30, 0x0a, + 0x14, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x67, 0x70, 0x75, 0x5f, 0x6c, + 0x69, 0x6d, 0x69, 0x74, 0x73, 0x18, 0x16, 0x20, 0x01, 0x28, 0x01, 0x52, 0x12, 0x63, 0x6f, 0x6e, + 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x47, 0x70, 0x75, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x73, 0x12, + 0x3a, 0x0a, 0x19, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x67, 0x70, 0x75, + 0x5f, 0x75, 0x74, 0x69, 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x17, 0x20, 0x01, + 0x28, 0x01, 0x52, 0x17, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x47, 0x70, 0x75, + 0x55, 0x74, 0x69, 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x2a, 0x0a, 0x11, 0x6e, + 0x6f, 0x64, 0x65, 0x5f, 0x67, 0x70, 0x75, 0x5f, 0x63, 0x61, 0x70, 0x61, 0x63, 0x69, 0x74, 0x79, + 0x18, 0x18, 0x20, 0x01, 0x28, 0x01, 0x52, 0x0f, 0x6e, 0x6f, 0x64, 0x65, 0x47, 0x70, 0x75, 0x43, + 0x61, 0x70, 0x61, 0x63, 0x69, 0x74, 0x79, 0x12, 0x24, 0x0a, 0x0e, 0x6e, 0x6f, 0x64, 0x65, 0x5f, + 0x67, 0x70, 0x75, 0x5f, 0x75, 0x73, 0x61, 0x67, 0x65, 0x18, 0x19, 0x20, 0x01, 0x28, 0x01, 0x52, + 0x0c, 0x6e, 0x6f, 0x64, 0x65, 0x47, 0x70, 0x75, 0x55, 0x73, 0x61, 0x67, 0x65, 0x12, 0x30, 0x0a, + 0x14, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x67, 0x70, 0x75, 0x5f, 0x75, 0x74, 0x69, 0x6c, 0x69, 0x7a, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x1a, 0x20, 0x01, 0x28, 0x01, 0x52, 0x12, 0x6e, 0x6f, 0x64, + 0x65, 0x47, 0x70, 0x75, 0x55, 0x74, 0x69, 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, + 0x2d, 0x0a, 0x13, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x67, 0x70, 0x75, 0x5f, 0x76, 0x72, 0x61, 0x6d, + 0x5f, 0x75, 0x73, 0x61, 0x67, 0x65, 0x18, 0x1b, 0x20, 0x01, 0x28, 0x01, 0x52, 0x10, 0x6e, 0x6f, + 0x64, 0x65, 0x47, 0x70, 0x75, 0x56, 0x72, 0x61, 0x6d, 0x55, 0x73, 0x61, 0x67, 0x65, 0x12, 0x37, + 0x0a, 0x18, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x67, 0x70, 0x75, 0x5f, + 0x76, 0x72, 0x61, 0x6d, 0x5f, 0x75, 0x73, 0x61, 0x67, 0x65, 0x18, 0x1c, 0x20, 0x01, 0x28, 0x01, + 0x52, 0x15, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x47, 0x70, 0x75, 0x56, 0x72, + 0x61, 0x6d, 0x55, 0x73, 0x61, 0x67, 0x65, 0x12, 0x33, 0x0a, 0x16, 0x6e, 0x6f, 0x64, 0x65, 0x5f, + 0x67, 0x70, 0x75, 0x5f, 0x76, 0x72, 0x61, 0x6d, 0x5f, 0x63, 0x61, 0x70, 0x61, 0x63, 0x69, 0x74, + 0x79, 0x18, 0x1d, 0x20, 0x01, 0x28, 0x01, 0x52, 0x13, 0x6e, 0x6f, 0x64, 0x65, 0x47, 0x70, 0x75, + 0x56, 0x72, 0x61, 0x6d, 0x43, 0x61, 0x70, 0x61, 0x63, 0x69, 0x74, 0x79, 0x12, 0x39, 0x0a, 0x19, + 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x67, 0x70, 0x75, 0x5f, 0x76, 0x72, 0x61, 0x6d, 0x5f, 0x75, 0x74, + 0x69, 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x1e, 0x20, 0x01, 0x28, 0x01, 0x52, + 0x16, 0x6e, 0x6f, 0x64, 0x65, 0x47, 0x70, 0x75, 0x56, 0x72, 0x61, 0x6d, 0x55, 0x74, 0x69, 0x6c, + 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x45, 0x0a, 0x1f, 0x6e, 0x6f, 0x72, 0x6d, 0x61, + 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x5f, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x6d, 0x65, 0x6d, 0x6f, 0x72, + 0x79, 0x5f, 0x63, 0x61, 0x70, 0x61, 0x63, 0x69, 0x74, 0x79, 0x18, 0x1f, 0x20, 0x01, 0x28, 0x01, + 0x52, 0x1c, 0x6e, 0x6f, 0x72, 0x6d, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x4e, 0x6f, 0x64, 0x65, + 0x4d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x43, 0x61, 0x70, 0x61, 0x63, 0x69, 0x74, 0x79, 0x12, 0x3f, + 0x0a, 0x1c, 0x6e, 0x6f, 0x72, 0x6d, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x5f, 0x67, 0x70, 0x75, + 0x5f, 0x76, 0x72, 0x61, 0x6d, 0x5f, 0x63, 0x61, 0x70, 0x61, 0x63, 0x69, 0x74, 0x79, 0x18, 0x20, + 0x20, 0x01, 0x28, 0x01, 0x52, 0x19, 0x6e, 0x6f, 0x72, 0x6d, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x64, + 0x47, 0x70, 0x75, 0x56, 0x72, 0x61, 0x6d, 0x43, 0x61, 0x70, 0x61, 0x63, 0x69, 0x74, 0x79, 0x22, + 0xe8, 0x04, 0x0a, 0x0f, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x75, 0x6d, 0x6d, + 0x61, 0x72, 0x79, 0x12, 0x29, 0x0a, 0x10, 0x64, 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x6d, 0x65, 0x6e, + 0x74, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0f, 0x64, + 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x2c, + 0x0a, 0x12, 0x73, 0x74, 0x61, 0x74, 0x65, 0x66, 0x75, 0x6c, 0x5f, 0x73, 0x65, 0x74, 0x5f, 0x63, + 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x10, 0x73, 0x74, 0x61, 0x74, + 0x65, 0x66, 0x75, 0x6c, 0x53, 0x65, 0x74, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x28, 0x0a, 0x10, + 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x5f, 0x73, 0x65, 0x74, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x53, 0x65, + 0x74, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x6a, 0x6f, 0x62, 0x5f, 0x63, 0x6f, + 0x75, 0x6e, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x52, 0x08, 0x6a, 0x6f, 0x62, 0x43, 0x6f, + 0x75, 0x6e, 0x74, 0x12, 0x24, 0x0a, 0x0e, 0x63, 0x72, 0x6f, 0x6e, 0x5f, 0x6a, 0x6f, 0x62, 0x5f, + 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0c, 0x63, 0x72, 0x6f, + 0x6e, 0x4a, 0x6f, 0x62, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x2a, 0x0a, 0x11, 0x72, 0x65, 0x70, + 0x6c, 0x69, 0x63, 0x61, 0x5f, 0x73, 0x65, 0x74, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x06, + 0x20, 0x01, 0x28, 0x05, 0x52, 0x0f, 0x72, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x53, 0x65, 0x74, + 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x70, 0x6f, 0x64, 0x5f, 0x63, 0x6f, 0x75, + 0x6e, 0x74, 0x18, 0x07, 0x20, 0x01, 0x28, 0x05, 0x52, 0x08, 0x70, 0x6f, 0x64, 0x43, 0x6f, 0x75, + 0x6e, 0x74, 0x12, 0x27, 0x0a, 0x0f, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, + 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x08, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0e, 0x63, 0x6f, 0x6e, + 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x21, 0x0a, 0x0c, 0x70, + 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x5f, 0x70, 0x6f, 0x64, 0x73, 0x18, 0x09, 0x20, 0x01, 0x28, + 0x05, 0x52, 0x0b, 0x70, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x50, 0x6f, 0x64, 0x73, 0x12, 0x21, + 0x0a, 0x0c, 0x72, 0x75, 0x6e, 0x6e, 0x69, 0x6e, 0x67, 0x5f, 0x70, 0x6f, 0x64, 0x73, 0x18, 0x0a, + 0x20, 0x01, 0x28, 0x05, 0x52, 0x0b, 0x72, 0x75, 0x6e, 0x6e, 0x69, 0x6e, 0x67, 0x50, 0x6f, 0x64, + 0x73, 0x12, 0x25, 0x0a, 0x0e, 0x73, 0x75, 0x63, 0x63, 0x65, 0x65, 0x64, 0x65, 0x64, 0x5f, 0x70, + 0x6f, 0x64, 0x73, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0d, 0x73, 0x75, 0x63, 0x63, 0x65, + 0x65, 0x64, 0x65, 0x64, 0x50, 0x6f, 0x64, 0x73, 0x12, 0x1f, 0x0a, 0x0b, 0x66, 0x61, 0x69, 0x6c, + 0x65, 0x64, 0x5f, 0x70, 0x6f, 0x64, 0x73, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0a, 0x66, + 0x61, 0x69, 0x6c, 0x65, 0x64, 0x50, 0x6f, 0x64, 0x73, 0x12, 0x21, 0x0a, 0x0c, 0x75, 0x6e, 0x6b, + 0x6e, 0x6f, 0x77, 0x6e, 0x5f, 0x70, 0x6f, 0x64, 0x73, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x05, 0x52, + 0x0b, 0x75, 0x6e, 0x6b, 0x6e, 0x6f, 0x77, 0x6e, 0x50, 0x6f, 0x64, 0x73, 0x12, 0x36, 0x0a, 0x17, + 0x75, 0x6e, 0x64, 0x65, 0x72, 0x5f, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, + 0x64, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x05, 0x52, 0x15, 0x75, + 0x6e, 0x64, 0x65, 0x72, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x64, 0x43, + 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x34, 0x0a, 0x16, 0x6f, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x72, 0x6f, + 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x64, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x0f, + 0x20, 0x01, 0x28, 0x05, 0x52, 0x14, 0x6f, 0x76, 0x65, 0x72, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, + 0x69, 0x6f, 0x6e, 0x65, 0x64, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x22, 0x4f, 0x0a, 0x0f, 0x4c, 0x65, + 0x67, 0x61, 0x63, 0x79, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x52, 0x65, 0x66, 0x12, 0x1b, 0x0a, + 0x09, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x08, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x49, 0x64, 0x12, 0x1f, 0x0a, 0x0b, 0x70, 0x6f, + 0x6c, 0x69, 0x63, 0x79, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x0a, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x4e, 0x61, 0x6d, 0x65, 0x22, 0xd2, 0x02, 0x0a, 0x12, + 0x4f, 0x70, 0x74, 0x69, 0x6d, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x61, 0x74, + 0x75, 0x73, 0x12, 0x21, 0x0a, 0x0c, 0x69, 0x73, 0x5f, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x69, 0x7a, + 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, 0x69, 0x73, 0x4f, 0x70, 0x74, 0x69, + 0x6d, 0x69, 0x7a, 0x65, 0x64, 0x12, 0x2e, 0x0a, 0x13, 0x69, 0x73, 0x5f, 0x6f, 0x70, 0x74, 0x69, + 0x6d, 0x69, 0x7a, 0x65, 0x64, 0x5f, 0x6c, 0x65, 0x67, 0x61, 0x63, 0x79, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x08, 0x52, 0x11, 0x69, 0x73, 0x4f, 0x70, 0x74, 0x69, 0x6d, 0x69, 0x7a, 0x65, 0x64, 0x4c, + 0x65, 0x67, 0x61, 0x63, 0x79, 0x12, 0x26, 0x0a, 0x0f, 0x69, 0x73, 0x5f, 0x6f, 0x70, 0x74, 0x69, + 0x6d, 0x69, 0x7a, 0x65, 0x64, 0x5f, 0x76, 0x32, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0d, + 0x69, 0x73, 0x4f, 0x70, 0x74, 0x69, 0x6d, 0x69, 0x7a, 0x65, 0x64, 0x56, 0x32, 0x12, 0x40, 0x0a, + 0x0f, 0x6c, 0x65, 0x67, 0x61, 0x63, 0x79, 0x5f, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x69, 0x65, 0x73, + 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, + 0x4c, 0x65, 0x67, 0x61, 0x63, 0x79, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x52, 0x65, 0x66, 0x52, + 0x0e, 0x6c, 0x65, 0x67, 0x61, 0x63, 0x79, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x69, 0x65, 0x73, 0x12, + 0x28, 0x0a, 0x10, 0x77, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x5f, 0x72, 0x75, 0x6c, 0x65, + 0x5f, 0x69, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x77, 0x6f, 0x72, 0x6b, 0x6c, + 0x6f, 0x61, 0x64, 0x52, 0x75, 0x6c, 0x65, 0x49, 0x64, 0x12, 0x39, 0x0a, 0x19, 0x77, 0x6f, 0x72, + 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x5f, 0x72, 0x75, 0x6c, 0x65, 0x5f, 0x70, 0x6f, 0x6c, 0x69, 0x63, + 0x79, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x16, 0x77, 0x6f, + 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x75, 0x6c, 0x65, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, + 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x64, + 0x18, 0x07, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x64, + 0x22, 0xdb, 0x0b, 0x0a, 0x0c, 0x57, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x49, 0x74, 0x65, + 0x6d, 0x12, 0x12, 0x0a, 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x69, 0x64, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x69, 0x64, 0x12, 0x1c, 0x0a, 0x09, 0x6e, + 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, + 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x6f, 0x77, 0x6e, + 0x65, 0x72, 0x5f, 0x6b, 0x69, 0x6e, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6f, + 0x77, 0x6e, 0x65, 0x72, 0x4b, 0x69, 0x6e, 0x64, 0x12, 0x1b, 0x0a, 0x09, 0x6f, 0x77, 0x6e, 0x65, + 0x72, 0x5f, 0x75, 0x69, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6f, 0x77, 0x6e, + 0x65, 0x72, 0x55, 0x69, 0x64, 0x12, 0x30, 0x0a, 0x08, 0x63, 0x68, 0x69, 0x6c, 0x64, 0x72, 0x65, + 0x6e, 0x18, 0x07, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, + 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x49, 0x74, 0x65, 0x6d, 0x52, 0x08, 0x63, + 0x68, 0x69, 0x6c, 0x64, 0x72, 0x65, 0x6e, 0x12, 0x38, 0x0a, 0x06, 0x6c, 0x61, 0x62, 0x65, 0x6c, + 0x73, 0x18, 0x08, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, + 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x49, 0x74, 0x65, 0x6d, 0x2e, 0x4c, 0x61, + 0x62, 0x65, 0x6c, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x06, 0x6c, 0x61, 0x62, 0x65, 0x6c, + 0x73, 0x12, 0x47, 0x0a, 0x0b, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, + 0x18, 0x09, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, + 0x57, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x49, 0x74, 0x65, 0x6d, 0x2e, 0x41, 0x6e, 0x6e, + 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0b, 0x61, + 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x2d, 0x0a, 0x12, 0x63, 0x72, + 0x65, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, + 0x18, 0x0a, 0x20, 0x01, 0x28, 0x03, 0x52, 0x11, 0x63, 0x72, 0x65, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x42, 0x0a, 0x10, 0x72, 0x65, 0x73, + 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x18, 0x0b, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x73, + 0x6f, 0x75, 0x72, 0x63, 0x65, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x52, 0x0f, 0x72, 0x65, + 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x12, 0x2d, 0x0a, + 0x09, 0x63, 0x6f, 0x73, 0x74, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x10, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x73, 0x74, 0x49, 0x6e, + 0x66, 0x6f, 0x52, 0x08, 0x63, 0x6f, 0x73, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x3f, 0x0a, 0x10, + 0x63, 0x6f, 0x73, 0x74, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x5f, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, + 0x18, 0x0d, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, + 0x43, 0x6f, 0x73, 0x74, 0x44, 0x61, 0x74, 0x61, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x52, 0x0e, 0x63, + 0x6f, 0x73, 0x74, 0x44, 0x61, 0x74, 0x61, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x12, 0x4b, 0x0a, + 0x14, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x5f, 0x70, + 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x18, 0x0e, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x61, 0x70, + 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x44, 0x61, 0x74, + 0x61, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x52, 0x12, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, + 0x44, 0x61, 0x74, 0x61, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x12, 0x21, 0x0a, 0x0c, 0x69, 0x6d, + 0x70, 0x61, 0x63, 0x74, 0x5f, 0x73, 0x63, 0x6f, 0x72, 0x65, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x01, + 0x52, 0x0b, 0x69, 0x6d, 0x70, 0x61, 0x63, 0x74, 0x53, 0x63, 0x6f, 0x72, 0x65, 0x12, 0x39, 0x0a, + 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x10, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x63, + 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x12, 0x3e, 0x0a, 0x0a, 0x64, 0x65, 0x6c, 0x65, + 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x11, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, + 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, + 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x48, 0x00, 0x52, 0x09, 0x64, 0x65, 0x6c, 0x65, + 0x74, 0x65, 0x64, 0x41, 0x74, 0x88, 0x01, 0x01, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x6c, 0x75, 0x73, + 0x74, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x12, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x6c, + 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, 0x64, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x13, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x32, 0x0a, 0x15, 0x69, 0x73, 0x5f, 0x6b, 0x61, + 0x72, 0x70, 0x65, 0x6e, 0x74, 0x65, 0x72, 0x5f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, + 0x18, 0x14, 0x20, 0x01, 0x28, 0x08, 0x52, 0x13, 0x69, 0x73, 0x4b, 0x61, 0x72, 0x70, 0x65, 0x6e, + 0x74, 0x65, 0x72, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x47, 0x0a, 0x10, 0x72, + 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x18, + 0x1e, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x52, + 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x48, 0x01, + 0x52, 0x0f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, + 0x73, 0x88, 0x01, 0x01, 0x12, 0x1d, 0x0a, 0x0a, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x5f, 0x69, + 0x64, 0x73, 0x18, 0x38, 0x20, 0x03, 0x28, 0x09, 0x52, 0x09, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, + 0x49, 0x64, 0x73, 0x12, 0x30, 0x0a, 0x14, 0x72, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x73, 0x5f, + 0x66, 0x72, 0x6f, 0x6d, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x4c, 0x20, 0x01, 0x28, + 0x05, 0x52, 0x12, 0x72, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x73, 0x46, 0x72, 0x6f, 0x6d, 0x53, + 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x2c, 0x0a, 0x12, 0x72, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, + 0x73, 0x5f, 0x66, 0x72, 0x6f, 0x6d, 0x5f, 0x73, 0x70, 0x65, 0x63, 0x18, 0x4d, 0x20, 0x01, 0x28, + 0x05, 0x52, 0x10, 0x72, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x73, 0x46, 0x72, 0x6f, 0x6d, 0x53, + 0x70, 0x65, 0x63, 0x12, 0x32, 0x0a, 0x15, 0x72, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x73, 0x5f, + 0x66, 0x72, 0x6f, 0x6d, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x18, 0x4e, 0x20, 0x01, + 0x28, 0x05, 0x52, 0x13, 0x72, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x73, 0x46, 0x72, 0x6f, 0x6d, + 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x12, 0x26, 0x0a, 0x0f, 0x72, 0x6f, 0x6f, 0x74, 0x5f, + 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x5f, 0x6b, 0x69, 0x6e, 0x64, 0x18, 0x5b, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x0d, 0x72, 0x6f, 0x6f, 0x74, 0x4f, 0x77, 0x6e, 0x65, 0x72, 0x4b, 0x69, 0x6e, 0x64, 0x12, + 0x24, 0x0a, 0x0e, 0x72, 0x6f, 0x6f, 0x74, 0x5f, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x5f, 0x75, 0x69, + 0x64, 0x18, 0x5c, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x72, 0x6f, 0x6f, 0x74, 0x4f, 0x77, 0x6e, + 0x65, 0x72, 0x55, 0x69, 0x64, 0x12, 0x50, 0x0a, 0x13, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x69, 0x7a, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x5d, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x70, 0x74, 0x69, + 0x6d, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x48, 0x02, + 0x52, 0x12, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, + 0x61, 0x74, 0x75, 0x73, 0x88, 0x01, 0x01, 0x1a, 0x39, 0x0a, 0x0b, 0x4c, 0x61, 0x62, 0x65, 0x6c, + 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, + 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, + 0x38, 0x01, 0x1a, 0x3e, 0x0a, 0x10, 0x41, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, + 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, + 0x38, 0x01, 0x42, 0x0d, 0x0a, 0x0b, 0x5f, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x5f, 0x61, + 0x74, 0x42, 0x13, 0x0a, 0x11, 0x5f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x64, + 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x42, 0x16, 0x0a, 0x14, 0x5f, 0x6f, 0x70, 0x74, 0x69, 0x6d, + 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, 0x53, + 0x0a, 0x14, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x52, 0x75, 0x6e, 0x74, 0x69, + 0x6d, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x21, 0x0a, 0x0c, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, + 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x72, 0x75, + 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, + 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, + 0x69, 0x6f, 0x6e, 0x22, 0xdb, 0x02, 0x0a, 0x08, 0x4e, 0x6f, 0x64, 0x65, 0x49, 0x6e, 0x66, 0x6f, + 0x12, 0x1d, 0x0a, 0x0a, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x05, 0x52, 0x09, 0x6e, 0x6f, 0x64, 0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, + 0x26, 0x0a, 0x0f, 0x6f, 0x6e, 0x5f, 0x64, 0x65, 0x6d, 0x61, 0x6e, 0x64, 0x5f, 0x63, 0x6f, 0x75, + 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0d, 0x6f, 0x6e, 0x44, 0x65, 0x6d, 0x61, + 0x6e, 0x64, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x25, 0x0a, 0x0e, 0x72, 0x65, 0x73, 0x65, 0x72, + 0x76, 0x65, 0x64, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, + 0x0d, 0x72, 0x65, 0x73, 0x65, 0x72, 0x76, 0x65, 0x64, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x1d, + 0x0a, 0x0a, 0x73, 0x70, 0x6f, 0x74, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x04, 0x20, 0x01, + 0x28, 0x05, 0x52, 0x09, 0x73, 0x70, 0x6f, 0x74, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x23, 0x0a, + 0x0d, 0x75, 0x6e, 0x6b, 0x6e, 0x6f, 0x77, 0x6e, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x05, + 0x20, 0x01, 0x28, 0x05, 0x52, 0x0c, 0x75, 0x6e, 0x6b, 0x6e, 0x6f, 0x77, 0x6e, 0x43, 0x6f, 0x75, + 0x6e, 0x74, 0x12, 0x57, 0x0a, 0x13, 0x70, 0x72, 0x69, 0x63, 0x65, 0x5f, 0x73, 0x6f, 0x75, 0x72, + 0x63, 0x65, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, + 0x27, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x49, 0x6e, 0x66, + 0x6f, 0x2e, 0x50, 0x72, 0x69, 0x63, 0x65, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x43, 0x6f, 0x75, + 0x6e, 0x74, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x11, 0x70, 0x72, 0x69, 0x63, 0x65, 0x53, + 0x6f, 0x75, 0x72, 0x63, 0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x1a, 0x44, 0x0a, 0x16, 0x50, 0x72, 0x69, 0x63, 0x65, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x73, - 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x11, 0x70, 0x72, 0x69, 0x63, 0x65, 0x53, 0x6f, 0x75, 0x72, - 0x63, 0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x1a, 0x44, 0x0a, 0x16, 0x50, 0x72, 0x69, 0x63, - 0x65, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x45, 0x6e, 0x74, - 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x05, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0xe5, - 0x01, 0x0a, 0x0c, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x12, - 0x27, 0x0a, 0x0f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x5f, 0x63, 0x6f, 0x75, - 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, - 0x63, 0x65, 0x73, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x38, 0x0a, 0x18, 0x73, 0x63, 0x68, 0x65, - 0x64, 0x75, 0x6c, 0x65, 0x64, 0x5f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x63, - 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x16, 0x73, 0x63, 0x68, 0x65, - 0x64, 0x75, 0x6c, 0x65, 0x64, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x43, 0x6f, 0x75, - 0x6e, 0x74, 0x12, 0x3c, 0x0a, 0x1a, 0x75, 0x6e, 0x73, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, - 0x64, 0x5f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, - 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x18, 0x75, 0x6e, 0x73, 0x63, 0x68, 0x65, 0x64, 0x75, - 0x6c, 0x65, 0x64, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74, - 0x12, 0x34, 0x0a, 0x16, 0x75, 0x6e, 0x6b, 0x6e, 0x6f, 0x77, 0x6e, 0x5f, 0x72, 0x65, 0x73, 0x6f, - 0x75, 0x72, 0x63, 0x65, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, - 0x52, 0x14, 0x75, 0x6e, 0x6b, 0x6e, 0x6f, 0x77, 0x6e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, - 0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x22, 0x9c, 0x12, 0x0a, 0x04, 0x4e, 0x6f, 0x64, 0x65, 0x12, - 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, - 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, - 0x61, 0x6d, 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x5f, - 0x74, 0x79, 0x70, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x69, 0x6e, 0x73, 0x74, - 0x61, 0x6e, 0x63, 0x65, 0x54, 0x79, 0x70, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, - 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x16, 0x0a, 0x06, - 0x72, 0x65, 0x67, 0x69, 0x6f, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x72, 0x65, - 0x67, 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x7a, 0x6f, 0x6e, 0x65, 0x18, 0x06, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x04, 0x7a, 0x6f, 0x6e, 0x65, 0x12, 0x42, 0x0a, 0x10, 0x72, 0x65, 0x73, 0x6f, - 0x75, 0x72, 0x63, 0x65, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x18, 0x07, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x73, 0x6f, - 0x75, 0x72, 0x63, 0x65, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x52, 0x0f, 0x72, 0x65, 0x73, - 0x6f, 0x75, 0x72, 0x63, 0x65, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x12, 0x2d, 0x0a, 0x09, - 0x63, 0x6f, 0x73, 0x74, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x10, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x73, 0x74, 0x49, 0x6e, 0x66, - 0x6f, 0x52, 0x08, 0x63, 0x6f, 0x73, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x26, 0x0a, 0x0f, 0x6e, - 0x6f, 0x64, 0x65, 0x5f, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x09, - 0x20, 0x01, 0x28, 0x03, 0x52, 0x0d, 0x6e, 0x6f, 0x64, 0x65, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, - 0x64, 0x41, 0x74, 0x12, 0x19, 0x0a, 0x08, 0x69, 0x73, 0x5f, 0x72, 0x65, 0x61, 0x64, 0x79, 0x18, - 0x0a, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x69, 0x73, 0x52, 0x65, 0x61, 0x64, 0x79, 0x12, 0x4e, - 0x0a, 0x11, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x72, 0x75, 0x6e, 0x74, - 0x69, 0x6d, 0x65, 0x18, 0x14, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x61, 0x70, 0x69, 0x2e, - 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x52, 0x75, 0x6e, 0x74, - 0x69, 0x6d, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x48, 0x00, 0x52, 0x10, 0x63, 0x6f, 0x6e, 0x74, 0x61, - 0x69, 0x6e, 0x65, 0x72, 0x52, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x88, 0x01, 0x01, 0x12, 0x26, - 0x0a, 0x0f, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x6e, 0x61, 0x6d, - 0x65, 0x18, 0x1e, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x6e, 0x6f, 0x64, 0x65, 0x47, 0x72, 0x6f, - 0x75, 0x70, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x42, 0x0a, 0x10, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, - 0x63, 0x65, 0x5f, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x18, 0x28, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x17, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, - 0x63, 0x65, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x52, 0x0f, 0x72, 0x65, 0x73, 0x6f, 0x75, - 0x72, 0x63, 0x65, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x1d, 0x0a, 0x0a, 0x6f, 0x77, - 0x6e, 0x65, 0x72, 0x5f, 0x6b, 0x69, 0x6e, 0x64, 0x18, 0x29, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, - 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x4b, 0x69, 0x6e, 0x64, 0x12, 0x1b, 0x0a, 0x09, 0x6f, 0x77, 0x6e, - 0x65, 0x72, 0x5f, 0x75, 0x69, 0x64, 0x18, 0x2a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6f, 0x77, - 0x6e, 0x65, 0x72, 0x55, 0x69, 0x64, 0x12, 0x30, 0x0a, 0x08, 0x63, 0x68, 0x69, 0x6c, 0x64, 0x72, - 0x65, 0x6e, 0x18, 0x2b, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, - 0x31, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x49, 0x74, 0x65, 0x6d, 0x52, 0x08, - 0x63, 0x68, 0x69, 0x6c, 0x64, 0x72, 0x65, 0x6e, 0x12, 0x30, 0x0a, 0x06, 0x6c, 0x61, 0x62, 0x65, - 0x6c, 0x73, 0x18, 0x2c, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, - 0x31, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x2e, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x45, 0x6e, 0x74, - 0x72, 0x79, 0x52, 0x06, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x12, 0x3f, 0x0a, 0x0b, 0x61, 0x6e, - 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x2d, 0x20, 0x03, 0x28, 0x0b, 0x32, - 0x1d, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x2e, 0x41, 0x6e, - 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0b, - 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x28, 0x0a, 0x0e, 0x70, - 0x72, 0x69, 0x63, 0x65, 0x5f, 0x70, 0x65, 0x72, 0x5f, 0x68, 0x6f, 0x75, 0x72, 0x18, 0x32, 0x20, - 0x01, 0x28, 0x01, 0x42, 0x02, 0x18, 0x01, 0x52, 0x0c, 0x70, 0x72, 0x69, 0x63, 0x65, 0x50, 0x65, - 0x72, 0x48, 0x6f, 0x75, 0x72, 0x12, 0x28, 0x0a, 0x0e, 0x70, 0x72, 0x69, 0x63, 0x65, 0x5f, 0x70, - 0x65, 0x72, 0x5f, 0x76, 0x63, 0x70, 0x75, 0x18, 0x33, 0x20, 0x01, 0x28, 0x01, 0x42, 0x02, 0x18, - 0x01, 0x52, 0x0c, 0x70, 0x72, 0x69, 0x63, 0x65, 0x50, 0x65, 0x72, 0x56, 0x63, 0x70, 0x75, 0x12, - 0x26, 0x0a, 0x0d, 0x70, 0x72, 0x69, 0x63, 0x65, 0x5f, 0x70, 0x65, 0x72, 0x5f, 0x67, 0x69, 0x62, - 0x18, 0x34, 0x20, 0x01, 0x28, 0x01, 0x42, 0x02, 0x18, 0x01, 0x52, 0x0b, 0x70, 0x72, 0x69, 0x63, - 0x65, 0x50, 0x65, 0x72, 0x47, 0x69, 0x62, 0x12, 0x26, 0x0a, 0x0d, 0x70, 0x72, 0x69, 0x63, 0x65, - 0x5f, 0x70, 0x65, 0x72, 0x5f, 0x67, 0x70, 0x75, 0x18, 0x35, 0x20, 0x01, 0x28, 0x01, 0x42, 0x02, - 0x18, 0x01, 0x52, 0x0b, 0x70, 0x72, 0x69, 0x63, 0x65, 0x50, 0x65, 0x72, 0x47, 0x70, 0x75, 0x12, - 0x1f, 0x0a, 0x09, 0x63, 0x70, 0x75, 0x5f, 0x70, 0x72, 0x69, 0x63, 0x65, 0x18, 0x36, 0x20, 0x01, - 0x28, 0x01, 0x42, 0x02, 0x18, 0x01, 0x52, 0x08, 0x63, 0x70, 0x75, 0x50, 0x72, 0x69, 0x63, 0x65, - 0x12, 0x25, 0x0a, 0x0c, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x5f, 0x70, 0x72, 0x69, 0x63, 0x65, - 0x18, 0x37, 0x20, 0x01, 0x28, 0x01, 0x42, 0x02, 0x18, 0x01, 0x52, 0x0b, 0x6d, 0x65, 0x6d, 0x6f, - 0x72, 0x79, 0x50, 0x72, 0x69, 0x63, 0x65, 0x12, 0x1f, 0x0a, 0x09, 0x67, 0x70, 0x75, 0x5f, 0x70, - 0x72, 0x69, 0x63, 0x65, 0x18, 0x38, 0x20, 0x01, 0x28, 0x01, 0x42, 0x02, 0x18, 0x01, 0x52, 0x08, - 0x67, 0x70, 0x75, 0x50, 0x72, 0x69, 0x63, 0x65, 0x12, 0x43, 0x0a, 0x14, 0x6d, 0x6f, 0x6e, 0x65, - 0x79, 0x5f, 0x70, 0x72, 0x69, 0x63, 0x65, 0x5f, 0x70, 0x65, 0x72, 0x5f, 0x68, 0x6f, 0x75, 0x72, - 0x18, 0x39, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, - 0x74, 0x79, 0x70, 0x65, 0x2e, 0x4d, 0x6f, 0x6e, 0x65, 0x79, 0x52, 0x11, 0x6d, 0x6f, 0x6e, 0x65, - 0x79, 0x50, 0x72, 0x69, 0x63, 0x65, 0x50, 0x65, 0x72, 0x48, 0x6f, 0x75, 0x72, 0x12, 0x50, 0x0a, - 0x1b, 0x6d, 0x6f, 0x6e, 0x65, 0x79, 0x5f, 0x65, 0x66, 0x66, 0x5f, 0x70, 0x65, 0x72, 0x69, 0x6f, - 0x64, 0x5f, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x63, 0x6f, 0x73, 0x74, 0x18, 0x3a, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x74, 0x79, 0x70, 0x65, - 0x2e, 0x4d, 0x6f, 0x6e, 0x65, 0x79, 0x52, 0x17, 0x6d, 0x6f, 0x6e, 0x65, 0x79, 0x45, 0x66, 0x66, - 0x50, 0x65, 0x72, 0x69, 0x6f, 0x64, 0x54, 0x6f, 0x74, 0x61, 0x6c, 0x43, 0x6f, 0x73, 0x74, 0x12, - 0x43, 0x0a, 0x14, 0x6d, 0x6f, 0x6e, 0x65, 0x79, 0x5f, 0x70, 0x72, 0x69, 0x63, 0x65, 0x5f, 0x70, - 0x65, 0x72, 0x5f, 0x76, 0x63, 0x70, 0x75, 0x18, 0x3b, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, - 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x2e, 0x4d, 0x6f, 0x6e, 0x65, - 0x79, 0x52, 0x11, 0x6d, 0x6f, 0x6e, 0x65, 0x79, 0x50, 0x72, 0x69, 0x63, 0x65, 0x50, 0x65, 0x72, - 0x56, 0x63, 0x70, 0x75, 0x12, 0x41, 0x0a, 0x13, 0x6d, 0x6f, 0x6e, 0x65, 0x79, 0x5f, 0x70, 0x72, - 0x69, 0x63, 0x65, 0x5f, 0x70, 0x65, 0x72, 0x5f, 0x67, 0x69, 0x62, 0x18, 0x50, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x12, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x2e, - 0x4d, 0x6f, 0x6e, 0x65, 0x79, 0x52, 0x10, 0x6d, 0x6f, 0x6e, 0x65, 0x79, 0x50, 0x72, 0x69, 0x63, - 0x65, 0x50, 0x65, 0x72, 0x47, 0x69, 0x62, 0x12, 0x41, 0x0a, 0x13, 0x6d, 0x6f, 0x6e, 0x65, 0x79, - 0x5f, 0x70, 0x72, 0x69, 0x63, 0x65, 0x5f, 0x70, 0x65, 0x72, 0x5f, 0x67, 0x70, 0x75, 0x18, 0x51, + 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, + 0x01, 0x22, 0xe5, 0x01, 0x0a, 0x0c, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x6e, + 0x66, 0x6f, 0x12, 0x27, 0x0a, 0x0f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x5f, + 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0e, 0x72, 0x65, 0x73, + 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x38, 0x0a, 0x18, 0x73, + 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x64, 0x5f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, + 0x65, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x16, 0x73, + 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x64, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, + 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x3c, 0x0a, 0x1a, 0x75, 0x6e, 0x73, 0x63, 0x68, 0x65, 0x64, + 0x75, 0x6c, 0x65, 0x64, 0x5f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x63, 0x6f, + 0x75, 0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x18, 0x75, 0x6e, 0x73, 0x63, 0x68, + 0x65, 0x64, 0x75, 0x6c, 0x65, 0x64, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x43, 0x6f, + 0x75, 0x6e, 0x74, 0x12, 0x34, 0x0a, 0x16, 0x75, 0x6e, 0x6b, 0x6e, 0x6f, 0x77, 0x6e, 0x5f, 0x72, + 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x04, 0x20, + 0x01, 0x28, 0x05, 0x52, 0x14, 0x75, 0x6e, 0x6b, 0x6e, 0x6f, 0x77, 0x6e, 0x52, 0x65, 0x73, 0x6f, + 0x75, 0x72, 0x63, 0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x22, 0xdb, 0x14, 0x0a, 0x04, 0x4e, 0x6f, + 0x64, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, + 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, + 0x63, 0x65, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x69, + 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x54, 0x79, 0x70, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x74, + 0x79, 0x70, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, + 0x16, 0x0a, 0x06, 0x72, 0x65, 0x67, 0x69, 0x6f, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x06, 0x72, 0x65, 0x67, 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x7a, 0x6f, 0x6e, 0x65, 0x18, + 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x7a, 0x6f, 0x6e, 0x65, 0x12, 0x42, 0x0a, 0x10, 0x72, + 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x18, + 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x52, + 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x52, 0x0f, + 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x12, + 0x2d, 0x0a, 0x09, 0x63, 0x6f, 0x73, 0x74, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x08, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x73, 0x74, + 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x08, 0x63, 0x6f, 0x73, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x26, + 0x0a, 0x0f, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, + 0x74, 0x18, 0x09, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0d, 0x6e, 0x6f, 0x64, 0x65, 0x43, 0x72, 0x65, + 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x12, 0x19, 0x0a, 0x08, 0x69, 0x73, 0x5f, 0x72, 0x65, 0x61, + 0x64, 0x79, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x69, 0x73, 0x52, 0x65, 0x61, 0x64, + 0x79, 0x12, 0x4e, 0x0a, 0x11, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x72, + 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x14, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x61, + 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x52, + 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x48, 0x00, 0x52, 0x10, 0x63, 0x6f, + 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x52, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x88, 0x01, + 0x01, 0x12, 0x26, 0x0a, 0x0f, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, + 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x1e, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x6e, 0x6f, 0x64, 0x65, + 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x42, 0x0a, 0x10, 0x72, 0x65, 0x73, + 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x18, 0x28, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x73, + 0x6f, 0x75, 0x72, 0x63, 0x65, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x52, 0x0f, 0x72, 0x65, + 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x1d, 0x0a, + 0x0a, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x5f, 0x6b, 0x69, 0x6e, 0x64, 0x18, 0x29, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x09, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x4b, 0x69, 0x6e, 0x64, 0x12, 0x1b, 0x0a, 0x09, + 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x5f, 0x75, 0x69, 0x64, 0x18, 0x2a, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x08, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x55, 0x69, 0x64, 0x12, 0x30, 0x0a, 0x08, 0x63, 0x68, 0x69, + 0x6c, 0x64, 0x72, 0x65, 0x6e, 0x18, 0x2b, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x61, 0x70, + 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x49, 0x74, 0x65, + 0x6d, 0x52, 0x08, 0x63, 0x68, 0x69, 0x6c, 0x64, 0x72, 0x65, 0x6e, 0x12, 0x30, 0x0a, 0x06, 0x6c, + 0x61, 0x62, 0x65, 0x6c, 0x73, 0x18, 0x2c, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x61, 0x70, + 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x2e, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x73, + 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x06, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x12, 0x3f, 0x0a, + 0x0b, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x2d, 0x20, 0x03, + 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4e, 0x6f, 0x64, 0x65, + 0x2e, 0x41, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x45, 0x6e, 0x74, 0x72, + 0x79, 0x52, 0x0b, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x28, + 0x0a, 0x0e, 0x70, 0x72, 0x69, 0x63, 0x65, 0x5f, 0x70, 0x65, 0x72, 0x5f, 0x68, 0x6f, 0x75, 0x72, + 0x18, 0x32, 0x20, 0x01, 0x28, 0x01, 0x42, 0x02, 0x18, 0x01, 0x52, 0x0c, 0x70, 0x72, 0x69, 0x63, + 0x65, 0x50, 0x65, 0x72, 0x48, 0x6f, 0x75, 0x72, 0x12, 0x28, 0x0a, 0x0e, 0x70, 0x72, 0x69, 0x63, + 0x65, 0x5f, 0x70, 0x65, 0x72, 0x5f, 0x76, 0x63, 0x70, 0x75, 0x18, 0x33, 0x20, 0x01, 0x28, 0x01, + 0x42, 0x02, 0x18, 0x01, 0x52, 0x0c, 0x70, 0x72, 0x69, 0x63, 0x65, 0x50, 0x65, 0x72, 0x56, 0x63, + 0x70, 0x75, 0x12, 0x26, 0x0a, 0x0d, 0x70, 0x72, 0x69, 0x63, 0x65, 0x5f, 0x70, 0x65, 0x72, 0x5f, + 0x67, 0x69, 0x62, 0x18, 0x34, 0x20, 0x01, 0x28, 0x01, 0x42, 0x02, 0x18, 0x01, 0x52, 0x0b, 0x70, + 0x72, 0x69, 0x63, 0x65, 0x50, 0x65, 0x72, 0x47, 0x69, 0x62, 0x12, 0x26, 0x0a, 0x0d, 0x70, 0x72, + 0x69, 0x63, 0x65, 0x5f, 0x70, 0x65, 0x72, 0x5f, 0x67, 0x70, 0x75, 0x18, 0x35, 0x20, 0x01, 0x28, + 0x01, 0x42, 0x02, 0x18, 0x01, 0x52, 0x0b, 0x70, 0x72, 0x69, 0x63, 0x65, 0x50, 0x65, 0x72, 0x47, + 0x70, 0x75, 0x12, 0x1f, 0x0a, 0x09, 0x63, 0x70, 0x75, 0x5f, 0x70, 0x72, 0x69, 0x63, 0x65, 0x18, + 0x36, 0x20, 0x01, 0x28, 0x01, 0x42, 0x02, 0x18, 0x01, 0x52, 0x08, 0x63, 0x70, 0x75, 0x50, 0x72, + 0x69, 0x63, 0x65, 0x12, 0x25, 0x0a, 0x0c, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x5f, 0x70, 0x72, + 0x69, 0x63, 0x65, 0x18, 0x37, 0x20, 0x01, 0x28, 0x01, 0x42, 0x02, 0x18, 0x01, 0x52, 0x0b, 0x6d, + 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x50, 0x72, 0x69, 0x63, 0x65, 0x12, 0x1f, 0x0a, 0x09, 0x67, 0x70, + 0x75, 0x5f, 0x70, 0x72, 0x69, 0x63, 0x65, 0x18, 0x38, 0x20, 0x01, 0x28, 0x01, 0x42, 0x02, 0x18, + 0x01, 0x52, 0x08, 0x67, 0x70, 0x75, 0x50, 0x72, 0x69, 0x63, 0x65, 0x12, 0x43, 0x0a, 0x14, 0x6d, + 0x6f, 0x6e, 0x65, 0x79, 0x5f, 0x70, 0x72, 0x69, 0x63, 0x65, 0x5f, 0x70, 0x65, 0x72, 0x5f, 0x68, + 0x6f, 0x75, 0x72, 0x18, 0x39, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x67, 0x6f, 0x6f, 0x67, + 0x6c, 0x65, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x2e, 0x4d, 0x6f, 0x6e, 0x65, 0x79, 0x52, 0x11, 0x6d, + 0x6f, 0x6e, 0x65, 0x79, 0x50, 0x72, 0x69, 0x63, 0x65, 0x50, 0x65, 0x72, 0x48, 0x6f, 0x75, 0x72, + 0x12, 0x50, 0x0a, 0x1b, 0x6d, 0x6f, 0x6e, 0x65, 0x79, 0x5f, 0x65, 0x66, 0x66, 0x5f, 0x70, 0x65, + 0x72, 0x69, 0x6f, 0x64, 0x5f, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x63, 0x6f, 0x73, 0x74, 0x18, + 0x3a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x74, + 0x79, 0x70, 0x65, 0x2e, 0x4d, 0x6f, 0x6e, 0x65, 0x79, 0x52, 0x17, 0x6d, 0x6f, 0x6e, 0x65, 0x79, + 0x45, 0x66, 0x66, 0x50, 0x65, 0x72, 0x69, 0x6f, 0x64, 0x54, 0x6f, 0x74, 0x61, 0x6c, 0x43, 0x6f, + 0x73, 0x74, 0x12, 0x43, 0x0a, 0x14, 0x6d, 0x6f, 0x6e, 0x65, 0x79, 0x5f, 0x70, 0x72, 0x69, 0x63, + 0x65, 0x5f, 0x70, 0x65, 0x72, 0x5f, 0x76, 0x63, 0x70, 0x75, 0x18, 0x3b, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x12, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x2e, 0x4d, + 0x6f, 0x6e, 0x65, 0x79, 0x52, 0x11, 0x6d, 0x6f, 0x6e, 0x65, 0x79, 0x50, 0x72, 0x69, 0x63, 0x65, + 0x50, 0x65, 0x72, 0x56, 0x63, 0x70, 0x75, 0x12, 0x41, 0x0a, 0x13, 0x6d, 0x6f, 0x6e, 0x65, 0x79, + 0x5f, 0x70, 0x72, 0x69, 0x63, 0x65, 0x5f, 0x70, 0x65, 0x72, 0x5f, 0x67, 0x69, 0x62, 0x18, 0x50, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x2e, 0x4d, 0x6f, 0x6e, 0x65, 0x79, 0x52, 0x10, 0x6d, 0x6f, 0x6e, 0x65, 0x79, 0x50, - 0x72, 0x69, 0x63, 0x65, 0x50, 0x65, 0x72, 0x47, 0x70, 0x75, 0x12, 0x3a, 0x0a, 0x0f, 0x6d, 0x6f, - 0x6e, 0x65, 0x79, 0x5f, 0x63, 0x70, 0x75, 0x5f, 0x70, 0x72, 0x69, 0x63, 0x65, 0x18, 0x52, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x74, 0x79, 0x70, - 0x65, 0x2e, 0x4d, 0x6f, 0x6e, 0x65, 0x79, 0x52, 0x0d, 0x6d, 0x6f, 0x6e, 0x65, 0x79, 0x43, 0x70, - 0x75, 0x50, 0x72, 0x69, 0x63, 0x65, 0x12, 0x40, 0x0a, 0x12, 0x6d, 0x6f, 0x6e, 0x65, 0x79, 0x5f, - 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x5f, 0x70, 0x72, 0x69, 0x63, 0x65, 0x18, 0x53, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x74, 0x79, 0x70, 0x65, - 0x2e, 0x4d, 0x6f, 0x6e, 0x65, 0x79, 0x52, 0x10, 0x6d, 0x6f, 0x6e, 0x65, 0x79, 0x4d, 0x65, 0x6d, - 0x6f, 0x72, 0x79, 0x50, 0x72, 0x69, 0x63, 0x65, 0x12, 0x3a, 0x0a, 0x0f, 0x6d, 0x6f, 0x6e, 0x65, - 0x79, 0x5f, 0x67, 0x70, 0x75, 0x5f, 0x70, 0x72, 0x69, 0x63, 0x65, 0x18, 0x54, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x12, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x2e, - 0x4d, 0x6f, 0x6e, 0x65, 0x79, 0x52, 0x0d, 0x6d, 0x6f, 0x6e, 0x65, 0x79, 0x47, 0x70, 0x75, 0x50, - 0x72, 0x69, 0x63, 0x65, 0x12, 0x55, 0x0a, 0x1e, 0x6d, 0x6f, 0x6e, 0x65, 0x79, 0x5f, 0x6f, 0x6e, - 0x5f, 0x64, 0x65, 0x6d, 0x61, 0x6e, 0x64, 0x5f, 0x70, 0x72, 0x69, 0x63, 0x65, 0x5f, 0x70, 0x65, - 0x72, 0x5f, 0x68, 0x6f, 0x75, 0x72, 0x18, 0x55, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x67, + 0x72, 0x69, 0x63, 0x65, 0x50, 0x65, 0x72, 0x47, 0x69, 0x62, 0x12, 0x41, 0x0a, 0x13, 0x6d, 0x6f, + 0x6e, 0x65, 0x79, 0x5f, 0x70, 0x72, 0x69, 0x63, 0x65, 0x5f, 0x70, 0x65, 0x72, 0x5f, 0x67, 0x70, + 0x75, 0x18, 0x51, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, + 0x2e, 0x74, 0x79, 0x70, 0x65, 0x2e, 0x4d, 0x6f, 0x6e, 0x65, 0x79, 0x52, 0x10, 0x6d, 0x6f, 0x6e, + 0x65, 0x79, 0x50, 0x72, 0x69, 0x63, 0x65, 0x50, 0x65, 0x72, 0x47, 0x70, 0x75, 0x12, 0x3a, 0x0a, + 0x0f, 0x6d, 0x6f, 0x6e, 0x65, 0x79, 0x5f, 0x63, 0x70, 0x75, 0x5f, 0x70, 0x72, 0x69, 0x63, 0x65, + 0x18, 0x52, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, + 0x74, 0x79, 0x70, 0x65, 0x2e, 0x4d, 0x6f, 0x6e, 0x65, 0x79, 0x52, 0x0d, 0x6d, 0x6f, 0x6e, 0x65, + 0x79, 0x43, 0x70, 0x75, 0x50, 0x72, 0x69, 0x63, 0x65, 0x12, 0x40, 0x0a, 0x12, 0x6d, 0x6f, 0x6e, + 0x65, 0x79, 0x5f, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x5f, 0x70, 0x72, 0x69, 0x63, 0x65, 0x18, + 0x53, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x74, + 0x79, 0x70, 0x65, 0x2e, 0x4d, 0x6f, 0x6e, 0x65, 0x79, 0x52, 0x10, 0x6d, 0x6f, 0x6e, 0x65, 0x79, + 0x4d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x50, 0x72, 0x69, 0x63, 0x65, 0x12, 0x3a, 0x0a, 0x0f, 0x6d, + 0x6f, 0x6e, 0x65, 0x79, 0x5f, 0x67, 0x70, 0x75, 0x5f, 0x70, 0x72, 0x69, 0x63, 0x65, 0x18, 0x54, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x74, 0x79, + 0x70, 0x65, 0x2e, 0x4d, 0x6f, 0x6e, 0x65, 0x79, 0x52, 0x0d, 0x6d, 0x6f, 0x6e, 0x65, 0x79, 0x47, + 0x70, 0x75, 0x50, 0x72, 0x69, 0x63, 0x65, 0x12, 0x55, 0x0a, 0x1e, 0x6d, 0x6f, 0x6e, 0x65, 0x79, + 0x5f, 0x6f, 0x6e, 0x5f, 0x64, 0x65, 0x6d, 0x61, 0x6e, 0x64, 0x5f, 0x70, 0x72, 0x69, 0x63, 0x65, + 0x5f, 0x70, 0x65, 0x72, 0x5f, 0x68, 0x6f, 0x75, 0x72, 0x18, 0x55, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x12, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x2e, 0x4d, 0x6f, + 0x6e, 0x65, 0x79, 0x52, 0x19, 0x6d, 0x6f, 0x6e, 0x65, 0x79, 0x4f, 0x6e, 0x44, 0x65, 0x6d, 0x61, + 0x6e, 0x64, 0x50, 0x72, 0x69, 0x63, 0x65, 0x50, 0x65, 0x72, 0x48, 0x6f, 0x75, 0x72, 0x12, 0x21, + 0x0a, 0x0c, 0x70, 0x72, 0x69, 0x63, 0x65, 0x5f, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, 0x56, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x70, 0x72, 0x69, 0x63, 0x65, 0x53, 0x6f, 0x75, 0x72, 0x63, + 0x65, 0x12, 0x29, 0x0a, 0x10, 0x70, 0x72, 0x69, 0x63, 0x69, 0x6e, 0x67, 0x5f, 0x61, 0x63, 0x63, + 0x75, 0x72, 0x61, 0x63, 0x79, 0x18, 0x57, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x70, 0x72, 0x69, + 0x63, 0x69, 0x6e, 0x67, 0x41, 0x63, 0x63, 0x75, 0x72, 0x61, 0x63, 0x79, 0x12, 0x34, 0x0a, 0x16, + 0x63, 0x6f, 0x76, 0x65, 0x72, 0x69, 0x6e, 0x67, 0x5f, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x6d, + 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x58, 0x20, 0x01, 0x28, 0x09, 0x52, 0x14, 0x63, 0x6f, + 0x76, 0x65, 0x72, 0x69, 0x6e, 0x67, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x6d, 0x65, 0x6e, 0x74, + 0x49, 0x64, 0x12, 0x4b, 0x0a, 0x14, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x64, + 0x61, 0x74, 0x61, 0x5f, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x18, 0x59, 0x20, 0x03, 0x28, 0x0b, + 0x32, 0x19, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, + 0x63, 0x65, 0x44, 0x61, 0x74, 0x61, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x52, 0x12, 0x72, 0x65, 0x73, + 0x6f, 0x75, 0x72, 0x63, 0x65, 0x44, 0x61, 0x74, 0x61, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x12, + 0x20, 0x0a, 0x09, 0x70, 0x6f, 0x64, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x5a, 0x20, 0x01, + 0x28, 0x05, 0x48, 0x01, 0x52, 0x08, 0x70, 0x6f, 0x64, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x88, 0x01, + 0x01, 0x12, 0x2e, 0x0a, 0x13, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x73, 0x65, 0x74, 0x5f, 0x70, + 0x6f, 0x64, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x5b, 0x20, 0x01, 0x28, 0x05, 0x52, 0x11, + 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x73, 0x65, 0x74, 0x50, 0x6f, 0x64, 0x43, 0x6f, 0x75, 0x6e, + 0x74, 0x12, 0x2e, 0x0a, 0x13, 0x65, 0x78, 0x74, 0x72, 0x61, 0x63, 0x74, 0x65, 0x64, 0x5f, 0x6e, + 0x6f, 0x64, 0x65, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x3c, 0x20, 0x01, 0x28, 0x09, 0x52, 0x11, + 0x65, 0x78, 0x74, 0x72, 0x61, 0x63, 0x74, 0x65, 0x64, 0x4e, 0x6f, 0x64, 0x65, 0x54, 0x79, 0x70, + 0x65, 0x12, 0x2b, 0x0a, 0x11, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, + 0x79, 0x5f, 0x7a, 0x6f, 0x6e, 0x65, 0x18, 0x3d, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x61, 0x76, + 0x61, 0x69, 0x6c, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, 0x5a, 0x6f, 0x6e, 0x65, 0x12, 0x25, + 0x0a, 0x0e, 0x6b, 0x75, 0x62, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x65, 0x73, 0x5f, 0x75, 0x69, 0x64, + 0x18, 0x3e, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x6b, 0x75, 0x62, 0x65, 0x72, 0x6e, 0x65, 0x74, + 0x65, 0x73, 0x55, 0x69, 0x64, 0x12, 0x29, 0x0a, 0x10, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, + 0x65, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x3f, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x0f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, + 0x12, 0x24, 0x0a, 0x0e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x73, 0x5f, 0x69, 0x6e, 0x5f, 0x75, + 0x73, 0x65, 0x18, 0x41, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0c, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, + 0x73, 0x49, 0x6e, 0x55, 0x73, 0x65, 0x12, 0x41, 0x0a, 0x10, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, + 0x73, 0x5f, 0x61, 0x74, 0x74, 0x61, 0x63, 0x68, 0x65, 0x64, 0x18, 0x42, 0x20, 0x03, 0x28, 0x0b, + 0x32, 0x16, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x74, 0x74, 0x61, 0x63, 0x68, + 0x65, 0x64, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x52, 0x0f, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, + 0x73, 0x41, 0x74, 0x74, 0x61, 0x63, 0x68, 0x65, 0x64, 0x12, 0x37, 0x0a, 0x09, 0x6c, 0x61, 0x73, + 0x74, 0x5f, 0x73, 0x65, 0x65, 0x6e, 0x18, 0x46, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, + 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, + 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x08, 0x6c, 0x61, 0x73, 0x74, 0x53, 0x65, + 0x65, 0x6e, 0x12, 0x3d, 0x0a, 0x0c, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x65, 0x64, 0x5f, + 0x61, 0x74, 0x18, 0x47, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, + 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, + 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0b, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x65, 0x64, 0x41, + 0x74, 0x12, 0x49, 0x0a, 0x12, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x74, 0x69, + 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x48, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, + 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, + 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x11, 0x64, 0x65, 0x6c, 0x65, 0x74, + 0x69, 0x6f, 0x6e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x32, 0x0a, 0x0a, + 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x64, 0x5f, 0x62, 0x79, 0x18, 0x5c, 0x20, 0x01, 0x28, 0x0e, + 0x32, 0x13, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x4d, 0x61, + 0x6e, 0x61, 0x67, 0x65, 0x72, 0x52, 0x09, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x64, 0x42, 0x79, + 0x12, 0x26, 0x0a, 0x0f, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x64, 0x5f, 0x62, 0x79, 0x5f, 0x6e, + 0x61, 0x6d, 0x65, 0x18, 0x5d, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x6d, 0x61, 0x6e, 0x61, 0x67, + 0x65, 0x64, 0x42, 0x79, 0x4e, 0x61, 0x6d, 0x65, 0x1a, 0x39, 0x0a, 0x0b, 0x4c, 0x61, 0x62, 0x65, + 0x6c, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, + 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, + 0x02, 0x38, 0x01, 0x1a, 0x3e, 0x0a, 0x10, 0x41, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, + 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, + 0x02, 0x38, 0x01, 0x42, 0x14, 0x0a, 0x12, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, + 0x72, 0x5f, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x42, 0x0c, 0x0a, 0x0a, 0x5f, 0x70, 0x6f, + 0x64, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x22, 0x45, 0x0a, 0x0e, 0x41, 0x74, 0x74, 0x61, 0x63, + 0x68, 0x65, 0x64, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, + 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1f, 0x0a, + 0x0b, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x0a, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x50, 0x61, 0x74, 0x68, 0x22, 0xee, + 0x03, 0x0a, 0x09, 0x4e, 0x6f, 0x64, 0x65, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x12, 0x12, 0x0a, 0x04, + 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, + 0x12, 0x22, 0x0a, 0x05, 0x6e, 0x6f, 0x64, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, + 0x0c, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x05, 0x6e, + 0x6f, 0x64, 0x65, 0x73, 0x12, 0x42, 0x0a, 0x10, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, + 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, + 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, + 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x52, 0x0f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, + 0x65, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x12, 0x2d, 0x0a, 0x09, 0x63, 0x6f, 0x73, 0x74, + 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x61, 0x70, + 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x73, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x08, 0x63, + 0x6f, 0x73, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x2d, 0x0a, 0x09, 0x6e, 0x6f, 0x64, 0x65, 0x5f, + 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x61, 0x70, 0x69, + 0x2e, 0x76, 0x31, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x08, 0x6e, 0x6f, + 0x64, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x39, 0x0a, 0x0d, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, + 0x63, 0x65, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, + 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, + 0x6e, 0x66, 0x6f, 0x52, 0x0c, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x6e, 0x66, + 0x6f, 0x12, 0x3f, 0x0a, 0x10, 0x63, 0x6f, 0x73, 0x74, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x5f, 0x70, + 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x18, 0x0b, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x61, 0x70, + 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x73, 0x74, 0x44, 0x61, 0x74, 0x61, 0x50, 0x6f, 0x69, + 0x6e, 0x74, 0x52, 0x0e, 0x63, 0x6f, 0x73, 0x74, 0x44, 0x61, 0x74, 0x61, 0x50, 0x6f, 0x69, 0x6e, + 0x74, 0x73, 0x12, 0x4b, 0x0a, 0x14, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x64, + 0x61, 0x74, 0x61, 0x5f, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x18, 0x0c, 0x20, 0x03, 0x28, 0x0b, + 0x32, 0x19, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, + 0x63, 0x65, 0x44, 0x61, 0x74, 0x61, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x52, 0x12, 0x72, 0x65, 0x73, + 0x6f, 0x75, 0x72, 0x63, 0x65, 0x44, 0x61, 0x74, 0x61, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x12, + 0x3e, 0x0a, 0x0b, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x18, 0x0d, + 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1c, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4e, 0x6f, + 0x64, 0x65, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, + 0x65, 0x72, 0x52, 0x0b, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x22, + 0xdd, 0x01, 0x0a, 0x0d, 0x43, 0x6f, 0x73, 0x74, 0x44, 0x61, 0x74, 0x61, 0x50, 0x6f, 0x69, 0x6e, + 0x74, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, + 0x2d, 0x0a, 0x09, 0x63, 0x6f, 0x73, 0x74, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x73, 0x74, + 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x08, 0x63, 0x6f, 0x73, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x43, + 0x0a, 0x14, 0x6d, 0x6f, 0x6e, 0x65, 0x79, 0x5f, 0x6f, 0x6e, 0x5f, 0x64, 0x65, 0x6d, 0x61, 0x6e, + 0x64, 0x5f, 0x63, 0x6f, 0x73, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x2e, 0x4d, 0x6f, 0x6e, 0x65, 0x79, - 0x52, 0x19, 0x6d, 0x6f, 0x6e, 0x65, 0x79, 0x4f, 0x6e, 0x44, 0x65, 0x6d, 0x61, 0x6e, 0x64, 0x50, - 0x72, 0x69, 0x63, 0x65, 0x50, 0x65, 0x72, 0x48, 0x6f, 0x75, 0x72, 0x12, 0x21, 0x0a, 0x0c, 0x70, - 0x72, 0x69, 0x63, 0x65, 0x5f, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, 0x56, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x0b, 0x70, 0x72, 0x69, 0x63, 0x65, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x29, - 0x0a, 0x10, 0x70, 0x72, 0x69, 0x63, 0x69, 0x6e, 0x67, 0x5f, 0x61, 0x63, 0x63, 0x75, 0x72, 0x61, - 0x63, 0x79, 0x18, 0x57, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x70, 0x72, 0x69, 0x63, 0x69, 0x6e, - 0x67, 0x41, 0x63, 0x63, 0x75, 0x72, 0x61, 0x63, 0x79, 0x12, 0x2e, 0x0a, 0x13, 0x65, 0x78, 0x74, - 0x72, 0x61, 0x63, 0x74, 0x65, 0x64, 0x5f, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x74, 0x79, 0x70, 0x65, - 0x18, 0x3c, 0x20, 0x01, 0x28, 0x09, 0x52, 0x11, 0x65, 0x78, 0x74, 0x72, 0x61, 0x63, 0x74, 0x65, - 0x64, 0x4e, 0x6f, 0x64, 0x65, 0x54, 0x79, 0x70, 0x65, 0x12, 0x2b, 0x0a, 0x11, 0x61, 0x76, 0x61, - 0x69, 0x6c, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, 0x5f, 0x7a, 0x6f, 0x6e, 0x65, 0x18, 0x3d, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x69, 0x6c, 0x69, - 0x74, 0x79, 0x5a, 0x6f, 0x6e, 0x65, 0x12, 0x25, 0x0a, 0x0e, 0x6b, 0x75, 0x62, 0x65, 0x72, 0x6e, - 0x65, 0x74, 0x65, 0x73, 0x5f, 0x75, 0x69, 0x64, 0x18, 0x3e, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, - 0x6b, 0x75, 0x62, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x65, 0x73, 0x55, 0x69, 0x64, 0x12, 0x29, 0x0a, - 0x10, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, - 0x6e, 0x18, 0x3f, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, - 0x65, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x24, 0x0a, 0x0e, 0x76, 0x6f, 0x6c, 0x75, - 0x6d, 0x65, 0x73, 0x5f, 0x69, 0x6e, 0x5f, 0x75, 0x73, 0x65, 0x18, 0x41, 0x20, 0x03, 0x28, 0x09, - 0x52, 0x0c, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x73, 0x49, 0x6e, 0x55, 0x73, 0x65, 0x12, 0x41, - 0x0a, 0x10, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x73, 0x5f, 0x61, 0x74, 0x74, 0x61, 0x63, 0x68, - 0x65, 0x64, 0x18, 0x42, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, - 0x31, 0x2e, 0x41, 0x74, 0x74, 0x61, 0x63, 0x68, 0x65, 0x64, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, - 0x52, 0x0f, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x73, 0x41, 0x74, 0x74, 0x61, 0x63, 0x68, 0x65, - 0x64, 0x12, 0x37, 0x0a, 0x09, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x73, 0x65, 0x65, 0x6e, 0x18, 0x46, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, - 0x52, 0x08, 0x6c, 0x61, 0x73, 0x74, 0x53, 0x65, 0x65, 0x6e, 0x12, 0x3d, 0x0a, 0x0c, 0x63, 0x6f, - 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x47, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, - 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0b, 0x63, 0x6f, - 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x65, 0x64, 0x41, 0x74, 0x12, 0x49, 0x0a, 0x12, 0x64, 0x65, 0x6c, - 0x65, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, - 0x48, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, - 0x70, 0x52, 0x11, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x69, 0x6d, 0x65, 0x73, - 0x74, 0x61, 0x6d, 0x70, 0x1a, 0x39, 0x0a, 0x0b, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x45, 0x6e, - 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, - 0x3e, 0x0a, 0x10, 0x41, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x45, 0x6e, - 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x42, - 0x14, 0x0a, 0x12, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x72, 0x75, - 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x22, 0x45, 0x0a, 0x0e, 0x41, 0x74, 0x74, 0x61, 0x63, 0x68, 0x65, - 0x64, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x64, - 0x65, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x0a, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x50, 0x61, 0x74, 0x68, 0x22, 0xae, 0x03, 0x0a, - 0x09, 0x4e, 0x6f, 0x64, 0x65, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, - 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x22, - 0x0a, 0x05, 0x6e, 0x6f, 0x64, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0c, 0x2e, - 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x05, 0x6e, 0x6f, 0x64, - 0x65, 0x73, 0x12, 0x42, 0x0a, 0x10, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x6d, - 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x61, + 0x52, 0x11, 0x6d, 0x6f, 0x6e, 0x65, 0x79, 0x4f, 0x6e, 0x44, 0x65, 0x6d, 0x61, 0x6e, 0x64, 0x43, + 0x6f, 0x73, 0x74, 0x12, 0x3a, 0x0a, 0x0f, 0x6d, 0x6f, 0x6e, 0x65, 0x79, 0x5f, 0x73, 0x70, 0x6f, + 0x74, 0x5f, 0x63, 0x6f, 0x73, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x67, + 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x2e, 0x4d, 0x6f, 0x6e, 0x65, 0x79, + 0x52, 0x0d, 0x6d, 0x6f, 0x6e, 0x65, 0x79, 0x53, 0x70, 0x6f, 0x74, 0x43, 0x6f, 0x73, 0x74, 0x22, + 0x75, 0x0a, 0x11, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x44, 0x61, 0x74, 0x61, 0x50, + 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, + 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, + 0x6d, 0x70, 0x12, 0x42, 0x0a, 0x10, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x6d, + 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x52, 0x0f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x4d, - 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x12, 0x2d, 0x0a, 0x09, 0x63, 0x6f, 0x73, 0x74, 0x5f, 0x69, - 0x6e, 0x66, 0x6f, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x61, 0x70, 0x69, 0x2e, - 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x73, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x08, 0x63, 0x6f, 0x73, - 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x2d, 0x0a, 0x09, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x69, 0x6e, - 0x66, 0x6f, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, - 0x31, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x08, 0x6e, 0x6f, 0x64, 0x65, - 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x39, 0x0a, 0x0d, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, - 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x61, 0x70, - 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x6e, 0x66, - 0x6f, 0x52, 0x0c, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x12, - 0x3f, 0x0a, 0x10, 0x63, 0x6f, 0x73, 0x74, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x5f, 0x70, 0x6f, 0x69, - 0x6e, 0x74, 0x73, 0x18, 0x0b, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x61, 0x70, 0x69, 0x2e, - 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x73, 0x74, 0x44, 0x61, 0x74, 0x61, 0x50, 0x6f, 0x69, 0x6e, 0x74, - 0x52, 0x0e, 0x63, 0x6f, 0x73, 0x74, 0x44, 0x61, 0x74, 0x61, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x73, - 0x12, 0x4b, 0x0a, 0x14, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x64, 0x61, 0x74, - 0x61, 0x5f, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x18, 0x0c, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x19, - 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, - 0x44, 0x61, 0x74, 0x61, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x52, 0x12, 0x72, 0x65, 0x73, 0x6f, 0x75, - 0x72, 0x63, 0x65, 0x44, 0x61, 0x74, 0x61, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x22, 0x5c, 0x0a, - 0x0d, 0x43, 0x6f, 0x73, 0x74, 0x44, 0x61, 0x74, 0x61, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x1c, - 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x03, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x2d, 0x0a, 0x09, - 0x63, 0x6f, 0x73, 0x74, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x10, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x73, 0x74, 0x49, 0x6e, 0x66, - 0x6f, 0x52, 0x08, 0x63, 0x6f, 0x73, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x22, 0x75, 0x0a, 0x11, 0x52, - 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x44, 0x61, 0x74, 0x61, 0x50, 0x6f, 0x69, 0x6e, 0x74, - 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x03, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x42, - 0x0a, 0x10, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, - 0x63, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, - 0x31, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, - 0x73, 0x52, 0x0f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x4d, 0x65, 0x74, 0x72, 0x69, - 0x63, 0x73, 0x22, 0x89, 0x01, 0x0a, 0x13, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, - 0x54, 0x69, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x69, 0x65, 0x73, 0x12, 0x25, 0x0a, 0x0e, 0x63, 0x6f, - 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x0d, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x4e, 0x61, 0x6d, - 0x65, 0x12, 0x4b, 0x0a, 0x14, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x64, 0x61, - 0x74, 0x61, 0x5f, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, - 0x19, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, - 0x65, 0x44, 0x61, 0x74, 0x61, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x52, 0x12, 0x72, 0x65, 0x73, 0x6f, - 0x75, 0x72, 0x63, 0x65, 0x44, 0x61, 0x74, 0x61, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x22, 0xc8, - 0x03, 0x0a, 0x0b, 0x53, 0x61, 0x76, 0x69, 0x6e, 0x67, 0x73, 0x44, 0x61, 0x74, 0x61, 0x12, 0x34, - 0x0a, 0x16, 0x63, 0x70, 0x75, 0x5f, 0x73, 0x61, 0x76, 0x69, 0x6e, 0x67, 0x73, 0x5f, 0x6d, 0x69, - 0x6c, 0x6c, 0x69, 0x63, 0x6f, 0x72, 0x65, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x01, 0x52, 0x14, - 0x63, 0x70, 0x75, 0x53, 0x61, 0x76, 0x69, 0x6e, 0x67, 0x73, 0x4d, 0x69, 0x6c, 0x6c, 0x69, 0x63, - 0x6f, 0x72, 0x65, 0x73, 0x12, 0x2d, 0x0a, 0x13, 0x63, 0x70, 0x75, 0x5f, 0x68, 0x69, 0x67, 0x68, - 0x5f, 0x77, 0x61, 0x74, 0x65, 0x72, 0x5f, 0x6d, 0x61, 0x72, 0x6b, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x01, 0x52, 0x10, 0x63, 0x70, 0x75, 0x48, 0x69, 0x67, 0x68, 0x57, 0x61, 0x74, 0x65, 0x72, 0x4d, - 0x61, 0x72, 0x6b, 0x12, 0x2c, 0x0a, 0x12, 0x61, 0x63, 0x74, 0x75, 0x61, 0x6c, 0x5f, 0x63, 0x70, - 0x75, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x01, 0x52, - 0x10, 0x61, 0x63, 0x74, 0x75, 0x61, 0x6c, 0x43, 0x70, 0x75, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x12, 0x49, 0x0a, 0x21, 0x63, 0x75, 0x6d, 0x75, 0x6c, 0x61, 0x74, 0x69, 0x76, 0x65, 0x5f, + 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x22, 0x89, 0x01, 0x0a, 0x13, 0x43, 0x6f, 0x6e, 0x74, 0x61, + 0x69, 0x6e, 0x65, 0x72, 0x54, 0x69, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x69, 0x65, 0x73, 0x12, 0x25, + 0x0a, 0x0e, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x6e, 0x61, 0x6d, 0x65, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, + 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x4b, 0x0a, 0x14, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, + 0x65, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x5f, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x18, 0x02, 0x20, + 0x03, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x73, + 0x6f, 0x75, 0x72, 0x63, 0x65, 0x44, 0x61, 0x74, 0x61, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x52, 0x12, + 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x44, 0x61, 0x74, 0x61, 0x50, 0x6f, 0x69, 0x6e, + 0x74, 0x73, 0x22, 0xc8, 0x03, 0x0a, 0x0b, 0x53, 0x61, 0x76, 0x69, 0x6e, 0x67, 0x73, 0x44, 0x61, + 0x74, 0x61, 0x12, 0x34, 0x0a, 0x16, 0x63, 0x70, 0x75, 0x5f, 0x73, 0x61, 0x76, 0x69, 0x6e, 0x67, + 0x73, 0x5f, 0x6d, 0x69, 0x6c, 0x6c, 0x69, 0x63, 0x6f, 0x72, 0x65, 0x73, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x01, 0x52, 0x14, 0x63, 0x70, 0x75, 0x53, 0x61, 0x76, 0x69, 0x6e, 0x67, 0x73, 0x4d, 0x69, + 0x6c, 0x6c, 0x69, 0x63, 0x6f, 0x72, 0x65, 0x73, 0x12, 0x2d, 0x0a, 0x13, 0x63, 0x70, 0x75, 0x5f, + 0x68, 0x69, 0x67, 0x68, 0x5f, 0x77, 0x61, 0x74, 0x65, 0x72, 0x5f, 0x6d, 0x61, 0x72, 0x6b, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x01, 0x52, 0x10, 0x63, 0x70, 0x75, 0x48, 0x69, 0x67, 0x68, 0x57, 0x61, + 0x74, 0x65, 0x72, 0x4d, 0x61, 0x72, 0x6b, 0x12, 0x2c, 0x0a, 0x12, 0x61, 0x63, 0x74, 0x75, 0x61, + 0x6c, 0x5f, 0x63, 0x70, 0x75, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x01, 0x52, 0x10, 0x61, 0x63, 0x74, 0x75, 0x61, 0x6c, 0x43, 0x70, 0x75, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x49, 0x0a, 0x21, 0x63, 0x75, 0x6d, 0x75, 0x6c, 0x61, 0x74, + 0x69, 0x76, 0x65, 0x5f, 0x63, 0x70, 0x75, 0x5f, 0x73, 0x61, 0x76, 0x69, 0x6e, 0x67, 0x73, 0x5f, + 0x6d, 0x69, 0x6c, 0x6c, 0x69, 0x63, 0x6f, 0x72, 0x65, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x01, + 0x52, 0x1e, 0x63, 0x75, 0x6d, 0x75, 0x6c, 0x61, 0x74, 0x69, 0x76, 0x65, 0x43, 0x70, 0x75, 0x53, + 0x61, 0x76, 0x69, 0x6e, 0x67, 0x73, 0x4d, 0x69, 0x6c, 0x6c, 0x69, 0x63, 0x6f, 0x72, 0x65, 0x73, + 0x12, 0x3d, 0x0a, 0x1b, 0x63, 0x70, 0x75, 0x5f, 0x63, 0x6f, 0x73, 0x74, 0x5f, 0x77, 0x69, 0x74, + 0x68, 0x6f, 0x75, 0x74, 0x5f, 0x61, 0x75, 0x74, 0x6f, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x72, 0x18, + 0x05, 0x20, 0x01, 0x28, 0x01, 0x52, 0x18, 0x63, 0x70, 0x75, 0x43, 0x6f, 0x73, 0x74, 0x57, 0x69, + 0x74, 0x68, 0x6f, 0x75, 0x74, 0x41, 0x75, 0x74, 0x6f, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x72, 0x12, + 0x26, 0x0a, 0x0f, 0x61, 0x63, 0x74, 0x75, 0x61, 0x6c, 0x5f, 0x63, 0x70, 0x75, 0x5f, 0x63, 0x6f, + 0x73, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x01, 0x52, 0x0d, 0x61, 0x63, 0x74, 0x75, 0x61, 0x6c, + 0x43, 0x70, 0x75, 0x43, 0x6f, 0x73, 0x74, 0x12, 0x28, 0x0a, 0x10, 0x63, 0x70, 0x75, 0x5f, 0x63, + 0x6f, 0x73, 0x74, 0x5f, 0x73, 0x61, 0x76, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x07, 0x20, 0x01, 0x28, + 0x01, 0x52, 0x0e, 0x63, 0x70, 0x75, 0x43, 0x6f, 0x73, 0x74, 0x53, 0x61, 0x76, 0x69, 0x6e, 0x67, + 0x73, 0x12, 0x24, 0x0a, 0x0e, 0x63, 0x70, 0x75, 0x5f, 0x75, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x63, + 0x6f, 0x73, 0x74, 0x18, 0x08, 0x20, 0x01, 0x28, 0x01, 0x52, 0x0c, 0x63, 0x70, 0x75, 0x55, 0x73, + 0x61, 0x67, 0x65, 0x43, 0x6f, 0x73, 0x74, 0x12, 0x24, 0x0a, 0x0e, 0x63, 0x70, 0x75, 0x5f, 0x63, + 0x6f, 0x73, 0x74, 0x5f, 0x77, 0x61, 0x73, 0x74, 0x65, 0x18, 0x09, 0x20, 0x01, 0x28, 0x01, 0x52, + 0x0c, 0x63, 0x70, 0x75, 0x43, 0x6f, 0x73, 0x74, 0x57, 0x61, 0x73, 0x74, 0x65, 0x22, 0x68, 0x0a, + 0x10, 0x53, 0x61, 0x76, 0x69, 0x6e, 0x67, 0x73, 0x44, 0x61, 0x74, 0x61, 0x50, 0x6f, 0x69, 0x6e, + 0x74, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, + 0x36, 0x0a, 0x0c, 0x73, 0x61, 0x76, 0x69, 0x6e, 0x67, 0x73, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x53, + 0x61, 0x76, 0x69, 0x6e, 0x67, 0x73, 0x44, 0x61, 0x74, 0x61, 0x52, 0x0b, 0x73, 0x61, 0x76, 0x69, + 0x6e, 0x67, 0x73, 0x44, 0x61, 0x74, 0x61, 0x22, 0x83, 0x02, 0x0a, 0x11, 0x53, 0x61, 0x76, 0x69, + 0x6e, 0x67, 0x73, 0x54, 0x69, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x69, 0x65, 0x73, 0x12, 0x47, 0x0a, + 0x12, 0x73, 0x61, 0x76, 0x69, 0x6e, 0x67, 0x73, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x70, 0x6f, 0x69, + 0x6e, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x61, 0x70, 0x69, 0x2e, + 0x76, 0x31, 0x2e, 0x53, 0x61, 0x76, 0x69, 0x6e, 0x67, 0x73, 0x44, 0x61, 0x74, 0x61, 0x50, 0x6f, + 0x69, 0x6e, 0x74, 0x52, 0x11, 0x73, 0x61, 0x76, 0x69, 0x6e, 0x67, 0x73, 0x44, 0x61, 0x74, 0x61, + 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x12, 0x3f, 0x0a, 0x1c, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x63, 0x70, 0x75, 0x5f, 0x73, 0x61, 0x76, 0x69, 0x6e, 0x67, 0x73, 0x5f, 0x6d, 0x69, 0x6c, 0x6c, - 0x69, 0x63, 0x6f, 0x72, 0x65, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x01, 0x52, 0x1e, 0x63, 0x75, - 0x6d, 0x75, 0x6c, 0x61, 0x74, 0x69, 0x76, 0x65, 0x43, 0x70, 0x75, 0x53, 0x61, 0x76, 0x69, 0x6e, - 0x67, 0x73, 0x4d, 0x69, 0x6c, 0x6c, 0x69, 0x63, 0x6f, 0x72, 0x65, 0x73, 0x12, 0x3d, 0x0a, 0x1b, - 0x63, 0x70, 0x75, 0x5f, 0x63, 0x6f, 0x73, 0x74, 0x5f, 0x77, 0x69, 0x74, 0x68, 0x6f, 0x75, 0x74, - 0x5f, 0x61, 0x75, 0x74, 0x6f, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x72, 0x18, 0x05, 0x20, 0x01, 0x28, - 0x01, 0x52, 0x18, 0x63, 0x70, 0x75, 0x43, 0x6f, 0x73, 0x74, 0x57, 0x69, 0x74, 0x68, 0x6f, 0x75, - 0x74, 0x41, 0x75, 0x74, 0x6f, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x72, 0x12, 0x26, 0x0a, 0x0f, 0x61, - 0x63, 0x74, 0x75, 0x61, 0x6c, 0x5f, 0x63, 0x70, 0x75, 0x5f, 0x63, 0x6f, 0x73, 0x74, 0x18, 0x06, - 0x20, 0x01, 0x28, 0x01, 0x52, 0x0d, 0x61, 0x63, 0x74, 0x75, 0x61, 0x6c, 0x43, 0x70, 0x75, 0x43, - 0x6f, 0x73, 0x74, 0x12, 0x28, 0x0a, 0x10, 0x63, 0x70, 0x75, 0x5f, 0x63, 0x6f, 0x73, 0x74, 0x5f, - 0x73, 0x61, 0x76, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x07, 0x20, 0x01, 0x28, 0x01, 0x52, 0x0e, 0x63, - 0x70, 0x75, 0x43, 0x6f, 0x73, 0x74, 0x53, 0x61, 0x76, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x24, 0x0a, - 0x0e, 0x63, 0x70, 0x75, 0x5f, 0x75, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x73, 0x74, 0x18, - 0x08, 0x20, 0x01, 0x28, 0x01, 0x52, 0x0c, 0x63, 0x70, 0x75, 0x55, 0x73, 0x61, 0x67, 0x65, 0x43, - 0x6f, 0x73, 0x74, 0x12, 0x24, 0x0a, 0x0e, 0x63, 0x70, 0x75, 0x5f, 0x63, 0x6f, 0x73, 0x74, 0x5f, - 0x77, 0x61, 0x73, 0x74, 0x65, 0x18, 0x09, 0x20, 0x01, 0x28, 0x01, 0x52, 0x0c, 0x63, 0x70, 0x75, - 0x43, 0x6f, 0x73, 0x74, 0x57, 0x61, 0x73, 0x74, 0x65, 0x22, 0x68, 0x0a, 0x10, 0x53, 0x61, 0x76, - 0x69, 0x6e, 0x67, 0x73, 0x44, 0x61, 0x74, 0x61, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x1c, 0x0a, - 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, - 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x36, 0x0a, 0x0c, 0x73, - 0x61, 0x76, 0x69, 0x6e, 0x67, 0x73, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x13, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x61, 0x76, 0x69, 0x6e, - 0x67, 0x73, 0x44, 0x61, 0x74, 0x61, 0x52, 0x0b, 0x73, 0x61, 0x76, 0x69, 0x6e, 0x67, 0x73, 0x44, - 0x61, 0x74, 0x61, 0x22, 0x83, 0x02, 0x0a, 0x11, 0x53, 0x61, 0x76, 0x69, 0x6e, 0x67, 0x73, 0x54, - 0x69, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x69, 0x65, 0x73, 0x12, 0x47, 0x0a, 0x12, 0x73, 0x61, 0x76, - 0x69, 0x6e, 0x67, 0x73, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x18, - 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x53, - 0x61, 0x76, 0x69, 0x6e, 0x67, 0x73, 0x44, 0x61, 0x74, 0x61, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x52, - 0x11, 0x73, 0x61, 0x76, 0x69, 0x6e, 0x67, 0x73, 0x44, 0x61, 0x74, 0x61, 0x70, 0x6f, 0x69, 0x6e, - 0x74, 0x73, 0x12, 0x3f, 0x0a, 0x1c, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x63, 0x70, 0x75, 0x5f, - 0x73, 0x61, 0x76, 0x69, 0x6e, 0x67, 0x73, 0x5f, 0x6d, 0x69, 0x6c, 0x6c, 0x69, 0x63, 0x6f, 0x72, - 0x65, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x01, 0x52, 0x19, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x43, - 0x70, 0x75, 0x53, 0x61, 0x76, 0x69, 0x6e, 0x67, 0x73, 0x4d, 0x69, 0x6c, 0x6c, 0x69, 0x63, 0x6f, - 0x72, 0x65, 0x73, 0x12, 0x33, 0x0a, 0x16, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x63, 0x70, 0x75, - 0x5f, 0x63, 0x6f, 0x73, 0x74, 0x5f, 0x73, 0x61, 0x76, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x03, 0x20, - 0x01, 0x28, 0x01, 0x52, 0x13, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x43, 0x70, 0x75, 0x43, 0x6f, 0x73, - 0x74, 0x53, 0x61, 0x76, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x2f, 0x0a, 0x14, 0x74, 0x6f, 0x74, 0x61, - 0x6c, 0x5f, 0x63, 0x70, 0x75, 0x5f, 0x63, 0x6f, 0x73, 0x74, 0x5f, 0x77, 0x61, 0x73, 0x74, 0x65, - 0x18, 0x04, 0x20, 0x01, 0x28, 0x01, 0x52, 0x11, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x43, 0x70, 0x75, - 0x43, 0x6f, 0x73, 0x74, 0x57, 0x61, 0x73, 0x74, 0x65, 0x22, 0x7f, 0x0a, 0x18, 0x4c, 0x61, 0x62, - 0x65, 0x6c, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x52, 0x65, 0x71, 0x75, 0x69, 0x72, - 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x39, 0x0a, 0x08, 0x6f, 0x70, 0x65, 0x72, 0x61, - 0x74, 0x6f, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1d, 0x2e, 0x61, 0x70, 0x69, 0x2e, - 0x76, 0x31, 0x2e, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, - 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x52, 0x08, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, - 0x6f, 0x72, 0x12, 0x16, 0x0a, 0x06, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, - 0x28, 0x09, 0x52, 0x06, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x22, 0x2f, 0x0a, 0x05, 0x4c, 0x61, - 0x62, 0x65, 0x6c, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x94, 0x02, 0x0a, 0x0d, - 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x12, 0x29, 0x0a, - 0x06, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0d, 0x2e, - 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x42, 0x02, 0x18, 0x01, - 0x52, 0x06, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x12, 0x49, 0x0a, 0x0c, 0x6d, 0x61, 0x74, 0x63, - 0x68, 0x5f, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x26, - 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x53, 0x65, 0x6c, - 0x65, 0x63, 0x74, 0x6f, 0x72, 0x2e, 0x4d, 0x61, 0x74, 0x63, 0x68, 0x4c, 0x61, 0x62, 0x65, 0x6c, - 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0b, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x4c, 0x61, 0x62, - 0x65, 0x6c, 0x73, 0x12, 0x4d, 0x0a, 0x11, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x5f, 0x65, 0x78, 0x70, - 0x72, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x20, - 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x53, 0x65, 0x6c, - 0x65, 0x63, 0x74, 0x6f, 0x72, 0x52, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x6d, 0x65, 0x6e, 0x74, - 0x52, 0x10, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x45, 0x78, 0x70, 0x72, 0x65, 0x73, 0x73, 0x69, 0x6f, - 0x6e, 0x73, 0x1a, 0x3e, 0x0a, 0x10, 0x4d, 0x61, 0x74, 0x63, 0x68, 0x4c, 0x61, 0x62, 0x65, 0x6c, - 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, - 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, - 0x38, 0x01, 0x22, 0x3e, 0x0a, 0x0c, 0x52, 0x65, 0x67, 0x65, 0x78, 0x50, 0x61, 0x74, 0x74, 0x65, - 0x72, 0x6e, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x61, 0x74, 0x74, 0x65, 0x72, 0x6e, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x07, 0x70, 0x61, 0x74, 0x74, 0x65, 0x72, 0x6e, 0x12, 0x14, 0x0a, 0x05, - 0x66, 0x6c, 0x61, 0x67, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x66, 0x6c, 0x61, - 0x67, 0x73, 0x22, 0x93, 0x4b, 0x0a, 0x0f, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x44, - 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x35, 0x0a, 0x0b, 0x70, 0x6f, 0x64, 0x5f, 0x64, 0x65, - 0x74, 0x61, 0x69, 0x6c, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x61, 0x70, - 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x6f, 0x64, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x48, - 0x00, 0x52, 0x0a, 0x70, 0x6f, 0x64, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x4a, 0x0a, - 0x12, 0x64, 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x64, 0x65, 0x74, 0x61, - 0x69, 0x6c, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x61, 0x70, 0x69, 0x2e, - 0x76, 0x31, 0x2e, 0x44, 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x44, 0x65, 0x74, - 0x61, 0x69, 0x6c, 0x73, 0x48, 0x00, 0x52, 0x11, 0x64, 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x6d, 0x65, - 0x6e, 0x74, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x4e, 0x0a, 0x14, 0x73, 0x74, 0x61, - 0x74, 0x65, 0x66, 0x75, 0x6c, 0x5f, 0x73, 0x65, 0x74, 0x5f, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, - 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, - 0x2e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x66, 0x75, 0x6c, 0x53, 0x65, 0x74, 0x44, 0x65, 0x74, 0x61, - 0x69, 0x6c, 0x73, 0x48, 0x00, 0x52, 0x12, 0x73, 0x74, 0x61, 0x74, 0x65, 0x66, 0x75, 0x6c, 0x53, - 0x65, 0x74, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x48, 0x0a, 0x12, 0x64, 0x61, 0x65, - 0x6d, 0x6f, 0x6e, 0x5f, 0x73, 0x65, 0x74, 0x5f, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x18, - 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x44, - 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x53, 0x65, 0x74, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x48, - 0x00, 0x52, 0x10, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x53, 0x65, 0x74, 0x44, 0x65, 0x74, 0x61, - 0x69, 0x6c, 0x73, 0x12, 0x4b, 0x0a, 0x13, 0x72, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x5f, 0x73, - 0x65, 0x74, 0x5f, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x19, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, - 0x61, 0x53, 0x65, 0x74, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x48, 0x00, 0x52, 0x11, 0x72, - 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x53, 0x65, 0x74, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, - 0x12, 0x35, 0x0a, 0x0b, 0x6a, 0x6f, 0x62, 0x5f, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x18, - 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4a, - 0x6f, 0x62, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x48, 0x00, 0x52, 0x0a, 0x6a, 0x6f, 0x62, - 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x42, 0x0a, 0x10, 0x63, 0x72, 0x6f, 0x6e, 0x5f, - 0x6a, 0x6f, 0x62, 0x5f, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x18, 0x07, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x16, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x72, 0x6f, 0x6e, 0x4a, - 0x6f, 0x62, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x48, 0x00, 0x52, 0x0e, 0x63, 0x72, 0x6f, - 0x6e, 0x4a, 0x6f, 0x62, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x41, 0x0a, 0x0f, 0x73, - 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x18, 0x08, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, - 0x72, 0x76, 0x69, 0x63, 0x65, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x48, 0x00, 0x52, 0x0e, - 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x41, - 0x0a, 0x0f, 0x69, 0x6e, 0x67, 0x72, 0x65, 0x73, 0x73, 0x5f, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, - 0x73, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, - 0x2e, 0x49, 0x6e, 0x67, 0x72, 0x65, 0x73, 0x73, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x48, - 0x00, 0x52, 0x0e, 0x69, 0x6e, 0x67, 0x72, 0x65, 0x73, 0x73, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, - 0x73, 0x12, 0x48, 0x0a, 0x0b, 0x70, 0x76, 0x63, 0x5f, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, - 0x18, 0x8c, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, - 0x2e, 0x50, 0x65, 0x72, 0x73, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x74, 0x56, 0x6f, 0x6c, 0x75, 0x6d, - 0x65, 0x43, 0x6c, 0x61, 0x69, 0x6d, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x48, 0x00, 0x52, - 0x0a, 0x70, 0x76, 0x63, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x41, 0x0a, 0x0a, 0x70, - 0x76, 0x5f, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x18, 0x8d, 0x01, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x1f, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x65, 0x72, 0x73, 0x69, 0x73, - 0x74, 0x65, 0x6e, 0x74, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, - 0x73, 0x48, 0x00, 0x52, 0x09, 0x70, 0x76, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x3d, - 0x0a, 0x0a, 0x73, 0x63, 0x5f, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x18, 0x8e, 0x01, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x74, 0x6f, - 0x72, 0x61, 0x67, 0x65, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, - 0x48, 0x00, 0x52, 0x09, 0x73, 0x63, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x3a, 0x0a, - 0x0a, 0x6e, 0x73, 0x5f, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x18, 0x8f, 0x01, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4e, 0x61, 0x6d, 0x65, - 0x73, 0x70, 0x61, 0x63, 0x65, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x48, 0x00, 0x52, 0x09, - 0x6e, 0x73, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x39, 0x0a, 0x0c, 0x6e, 0x6f, 0x64, - 0x65, 0x5f, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x18, 0x90, 0x01, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x13, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x44, 0x65, - 0x74, 0x61, 0x69, 0x6c, 0x73, 0x48, 0x00, 0x52, 0x0b, 0x6e, 0x6f, 0x64, 0x65, 0x44, 0x65, 0x74, - 0x61, 0x69, 0x6c, 0x73, 0x12, 0x36, 0x0a, 0x0b, 0x68, 0x70, 0x61, 0x5f, 0x64, 0x65, 0x74, 0x61, - 0x69, 0x6c, 0x73, 0x18, 0x91, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x61, 0x70, 0x69, - 0x2e, 0x76, 0x31, 0x2e, 0x48, 0x50, 0x41, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x48, 0x00, - 0x52, 0x0a, 0x68, 0x70, 0x61, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x36, 0x0a, 0x0b, - 0x76, 0x70, 0x61, 0x5f, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x18, 0x92, 0x01, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x56, 0x50, 0x41, 0x44, - 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x48, 0x00, 0x52, 0x0a, 0x76, 0x70, 0x61, 0x44, 0x65, 0x74, - 0x61, 0x69, 0x6c, 0x73, 0x12, 0x4c, 0x0a, 0x13, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x5f, 0x72, 0x61, - 0x6e, 0x67, 0x65, 0x5f, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x18, 0x93, 0x01, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x6d, 0x69, - 0x74, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x48, 0x00, 0x52, - 0x11, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x44, 0x65, 0x74, 0x61, 0x69, - 0x6c, 0x73, 0x12, 0x58, 0x0a, 0x17, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x61, 0x63, - 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x18, 0xe8, 0x01, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, - 0x72, 0x76, 0x69, 0x63, 0x65, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x44, 0x65, 0x74, 0x61, - 0x69, 0x6c, 0x73, 0x48, 0x00, 0x52, 0x15, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x41, 0x63, - 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x39, 0x0a, 0x0c, - 0x72, 0x6f, 0x6c, 0x65, 0x5f, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x18, 0xe9, 0x01, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x6f, 0x6c, - 0x65, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x48, 0x00, 0x52, 0x0b, 0x72, 0x6f, 0x6c, 0x65, - 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x4f, 0x0a, 0x14, 0x72, 0x6f, 0x6c, 0x65, 0x5f, - 0x62, 0x69, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x5f, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x18, - 0xea, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, - 0x52, 0x6f, 0x6c, 0x65, 0x42, 0x69, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x44, 0x65, 0x74, 0x61, 0x69, - 0x6c, 0x73, 0x48, 0x00, 0x52, 0x12, 0x72, 0x6f, 0x6c, 0x65, 0x42, 0x69, 0x6e, 0x64, 0x69, 0x6e, - 0x67, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x5f, 0x0a, 0x1a, 0x6b, 0x65, 0x64, 0x61, - 0x5f, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x64, 0x5f, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x64, - 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x18, 0xeb, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, - 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4b, 0x65, 0x64, 0x61, 0x53, 0x63, 0x61, 0x6c, 0x65, - 0x64, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x48, 0x00, - 0x52, 0x17, 0x6b, 0x65, 0x64, 0x61, 0x53, 0x63, 0x61, 0x6c, 0x65, 0x64, 0x4f, 0x62, 0x6a, 0x65, - 0x63, 0x74, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x61, 0x0a, 0x1a, 0x6b, 0x61, 0x72, - 0x70, 0x65, 0x6e, 0x74, 0x65, 0x72, 0x5f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, - 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x18, 0xec, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, - 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4b, 0x61, 0x72, 0x70, 0x65, 0x6e, 0x74, 0x65, + 0x69, 0x63, 0x6f, 0x72, 0x65, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x01, 0x52, 0x19, 0x74, 0x6f, + 0x74, 0x61, 0x6c, 0x43, 0x70, 0x75, 0x53, 0x61, 0x76, 0x69, 0x6e, 0x67, 0x73, 0x4d, 0x69, 0x6c, + 0x6c, 0x69, 0x63, 0x6f, 0x72, 0x65, 0x73, 0x12, 0x33, 0x0a, 0x16, 0x74, 0x6f, 0x74, 0x61, 0x6c, + 0x5f, 0x63, 0x70, 0x75, 0x5f, 0x63, 0x6f, 0x73, 0x74, 0x5f, 0x73, 0x61, 0x76, 0x69, 0x6e, 0x67, + 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x01, 0x52, 0x13, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x43, 0x70, + 0x75, 0x43, 0x6f, 0x73, 0x74, 0x53, 0x61, 0x76, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x2f, 0x0a, 0x14, + 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x63, 0x70, 0x75, 0x5f, 0x63, 0x6f, 0x73, 0x74, 0x5f, 0x77, + 0x61, 0x73, 0x74, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x01, 0x52, 0x11, 0x74, 0x6f, 0x74, 0x61, + 0x6c, 0x43, 0x70, 0x75, 0x43, 0x6f, 0x73, 0x74, 0x57, 0x61, 0x73, 0x74, 0x65, 0x22, 0x7f, 0x0a, + 0x18, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x52, 0x65, + 0x71, 0x75, 0x69, 0x72, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x39, 0x0a, 0x08, 0x6f, + 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1d, 0x2e, + 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x53, 0x65, 0x6c, 0x65, + 0x63, 0x74, 0x6f, 0x72, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x52, 0x08, 0x6f, 0x70, + 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x12, 0x16, 0x0a, 0x06, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, + 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x06, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x22, 0x2f, + 0x0a, 0x05, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, + 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, + 0x94, 0x02, 0x0a, 0x0d, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, + 0x72, 0x12, 0x29, 0x0a, 0x06, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, + 0x0b, 0x32, 0x0d, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x61, 0x62, 0x65, 0x6c, + 0x42, 0x02, 0x18, 0x01, 0x52, 0x06, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x12, 0x49, 0x0a, 0x0c, + 0x6d, 0x61, 0x74, 0x63, 0x68, 0x5f, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x18, 0x02, 0x20, 0x03, + 0x28, 0x0b, 0x32, 0x26, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x61, 0x62, 0x65, + 0x6c, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x2e, 0x4d, 0x61, 0x74, 0x63, 0x68, 0x4c, + 0x61, 0x62, 0x65, 0x6c, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0b, 0x6d, 0x61, 0x74, 0x63, + 0x68, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x12, 0x4d, 0x0a, 0x11, 0x6d, 0x61, 0x74, 0x63, 0x68, + 0x5f, 0x65, 0x78, 0x70, 0x72, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x03, 0x20, 0x03, + 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x61, 0x62, 0x65, + 0x6c, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x52, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, + 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x10, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x45, 0x78, 0x70, 0x72, 0x65, + 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x1a, 0x3e, 0x0a, 0x10, 0x4d, 0x61, 0x74, 0x63, 0x68, 0x4c, + 0x61, 0x62, 0x65, 0x6c, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, + 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, + 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, + 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x3e, 0x0a, 0x0c, 0x52, 0x65, 0x67, 0x65, 0x78, 0x50, + 0x61, 0x74, 0x74, 0x65, 0x72, 0x6e, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x61, 0x74, 0x74, 0x65, 0x72, + 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x70, 0x61, 0x74, 0x74, 0x65, 0x72, 0x6e, + 0x12, 0x14, 0x0a, 0x05, 0x66, 0x6c, 0x61, 0x67, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x05, 0x66, 0x6c, 0x61, 0x67, 0x73, 0x22, 0x8e, 0x5d, 0x0a, 0x0f, 0x52, 0x65, 0x73, 0x6f, 0x75, + 0x72, 0x63, 0x65, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x35, 0x0a, 0x0b, 0x70, 0x6f, + 0x64, 0x5f, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x12, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x6f, 0x64, 0x44, 0x65, 0x74, 0x61, + 0x69, 0x6c, 0x73, 0x48, 0x00, 0x52, 0x0a, 0x70, 0x6f, 0x64, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, + 0x73, 0x12, 0x4a, 0x0a, 0x12, 0x64, 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x5f, + 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, + 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x6d, 0x65, 0x6e, + 0x74, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x48, 0x00, 0x52, 0x11, 0x64, 0x65, 0x70, 0x6c, + 0x6f, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x4e, 0x0a, + 0x14, 0x73, 0x74, 0x61, 0x74, 0x65, 0x66, 0x75, 0x6c, 0x5f, 0x73, 0x65, 0x74, 0x5f, 0x64, 0x65, + 0x74, 0x61, 0x69, 0x6c, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x61, 0x70, + 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x66, 0x75, 0x6c, 0x53, 0x65, 0x74, + 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x48, 0x00, 0x52, 0x12, 0x73, 0x74, 0x61, 0x74, 0x65, + 0x66, 0x75, 0x6c, 0x53, 0x65, 0x74, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x48, 0x0a, + 0x12, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x5f, 0x73, 0x65, 0x74, 0x5f, 0x64, 0x65, 0x74, 0x61, + 0x69, 0x6c, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x61, 0x70, 0x69, 0x2e, + 0x76, 0x31, 0x2e, 0x44, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x53, 0x65, 0x74, 0x44, 0x65, 0x74, 0x61, + 0x69, 0x6c, 0x73, 0x48, 0x00, 0x52, 0x10, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x53, 0x65, 0x74, + 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x4b, 0x0a, 0x13, 0x72, 0x65, 0x70, 0x6c, 0x69, + 0x63, 0x61, 0x5f, 0x73, 0x65, 0x74, 0x5f, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x18, 0x05, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, + 0x70, 0x6c, 0x69, 0x63, 0x61, 0x53, 0x65, 0x74, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x48, + 0x00, 0x52, 0x11, 0x72, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x53, 0x65, 0x74, 0x44, 0x65, 0x74, + 0x61, 0x69, 0x6c, 0x73, 0x12, 0x35, 0x0a, 0x0b, 0x6a, 0x6f, 0x62, 0x5f, 0x64, 0x65, 0x74, 0x61, + 0x69, 0x6c, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x61, 0x70, 0x69, 0x2e, + 0x76, 0x31, 0x2e, 0x4a, 0x6f, 0x62, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x48, 0x00, 0x52, + 0x0a, 0x6a, 0x6f, 0x62, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x42, 0x0a, 0x10, 0x63, + 0x72, 0x6f, 0x6e, 0x5f, 0x6a, 0x6f, 0x62, 0x5f, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x18, + 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x43, + 0x72, 0x6f, 0x6e, 0x4a, 0x6f, 0x62, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x48, 0x00, 0x52, + 0x0e, 0x63, 0x72, 0x6f, 0x6e, 0x4a, 0x6f, 0x62, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, + 0x41, 0x0a, 0x0f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x64, 0x65, 0x74, 0x61, 0x69, + 0x6c, 0x73, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, + 0x31, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, + 0x48, 0x00, 0x52, 0x0e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x44, 0x65, 0x74, 0x61, 0x69, + 0x6c, 0x73, 0x12, 0x41, 0x0a, 0x0f, 0x69, 0x6e, 0x67, 0x72, 0x65, 0x73, 0x73, 0x5f, 0x64, 0x65, + 0x74, 0x61, 0x69, 0x6c, 0x73, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x61, 0x70, + 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x49, 0x6e, 0x67, 0x72, 0x65, 0x73, 0x73, 0x44, 0x65, 0x74, 0x61, + 0x69, 0x6c, 0x73, 0x48, 0x00, 0x52, 0x0e, 0x69, 0x6e, 0x67, 0x72, 0x65, 0x73, 0x73, 0x44, 0x65, + 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x48, 0x0a, 0x0b, 0x70, 0x76, 0x63, 0x5f, 0x64, 0x65, 0x74, + 0x61, 0x69, 0x6c, 0x73, 0x18, 0x8c, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x61, 0x70, + 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x65, 0x72, 0x73, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x74, 0x56, + 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x43, 0x6c, 0x61, 0x69, 0x6d, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, + 0x73, 0x48, 0x00, 0x52, 0x0a, 0x70, 0x76, 0x63, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, + 0x41, 0x0a, 0x0a, 0x70, 0x76, 0x5f, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x18, 0x8d, 0x01, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x65, + 0x72, 0x73, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x74, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x44, 0x65, + 0x74, 0x61, 0x69, 0x6c, 0x73, 0x48, 0x00, 0x52, 0x09, 0x70, 0x76, 0x44, 0x65, 0x74, 0x61, 0x69, + 0x6c, 0x73, 0x12, 0x3d, 0x0a, 0x0a, 0x73, 0x63, 0x5f, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, + 0x18, 0x8e, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, + 0x2e, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x44, 0x65, 0x74, + 0x61, 0x69, 0x6c, 0x73, 0x48, 0x00, 0x52, 0x09, 0x73, 0x63, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, + 0x73, 0x12, 0x3a, 0x0a, 0x0a, 0x6e, 0x73, 0x5f, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x18, + 0x8f, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, + 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, + 0x48, 0x00, 0x52, 0x09, 0x6e, 0x73, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x39, 0x0a, + 0x0c, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x18, 0x90, 0x01, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4e, 0x6f, + 0x64, 0x65, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x48, 0x00, 0x52, 0x0b, 0x6e, 0x6f, 0x64, + 0x65, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x36, 0x0a, 0x0b, 0x68, 0x70, 0x61, 0x5f, + 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x18, 0x91, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, + 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x48, 0x50, 0x41, 0x44, 0x65, 0x74, 0x61, 0x69, + 0x6c, 0x73, 0x48, 0x00, 0x52, 0x0a, 0x68, 0x70, 0x61, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, + 0x12, 0x36, 0x0a, 0x0b, 0x76, 0x70, 0x61, 0x5f, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x18, + 0x92, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, + 0x56, 0x50, 0x41, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x48, 0x00, 0x52, 0x0a, 0x76, 0x70, + 0x61, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x4c, 0x0a, 0x13, 0x6c, 0x69, 0x6d, 0x69, + 0x74, 0x5f, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x5f, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x18, + 0x93, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, + 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, + 0x73, 0x48, 0x00, 0x52, 0x11, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x44, + 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x58, 0x0a, 0x17, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, + 0x65, 0x5f, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, + 0x73, 0x18, 0xe8, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, + 0x31, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, + 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x48, 0x00, 0x52, 0x15, 0x73, 0x65, 0x72, 0x76, 0x69, + 0x63, 0x65, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, + 0x12, 0x39, 0x0a, 0x0c, 0x72, 0x6f, 0x6c, 0x65, 0x5f, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, + 0x18, 0xe9, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, + 0x2e, 0x52, 0x6f, 0x6c, 0x65, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x48, 0x00, 0x52, 0x0b, + 0x72, 0x6f, 0x6c, 0x65, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x4f, 0x0a, 0x14, 0x72, + 0x6f, 0x6c, 0x65, 0x5f, 0x62, 0x69, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x5f, 0x64, 0x65, 0x74, 0x61, + 0x69, 0x6c, 0x73, 0x18, 0xea, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x61, 0x70, 0x69, + 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x6f, 0x6c, 0x65, 0x42, 0x69, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x44, + 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x48, 0x00, 0x52, 0x12, 0x72, 0x6f, 0x6c, 0x65, 0x42, 0x69, + 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x5f, 0x0a, 0x1a, + 0x6b, 0x65, 0x64, 0x61, 0x5f, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x64, 0x5f, 0x6f, 0x62, 0x6a, 0x65, + 0x63, 0x74, 0x5f, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x18, 0xeb, 0x01, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x1f, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4b, 0x65, 0x64, 0x61, 0x53, + 0x63, 0x61, 0x6c, 0x65, 0x64, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x44, 0x65, 0x74, 0x61, 0x69, + 0x6c, 0x73, 0x48, 0x00, 0x52, 0x17, 0x6b, 0x65, 0x64, 0x61, 0x53, 0x63, 0x61, 0x6c, 0x65, 0x64, + 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x61, 0x0a, + 0x1a, 0x6b, 0x61, 0x72, 0x70, 0x65, 0x6e, 0x74, 0x65, 0x72, 0x5f, 0x72, 0x65, 0x73, 0x6f, 0x75, + 0x72, 0x63, 0x65, 0x5f, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x18, 0xec, 0x01, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4b, 0x61, 0x72, 0x70, + 0x65, 0x6e, 0x74, 0x65, 0x72, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x44, 0x65, 0x74, + 0x61, 0x69, 0x6c, 0x73, 0x48, 0x00, 0x52, 0x18, 0x6b, 0x61, 0x72, 0x70, 0x65, 0x6e, 0x74, 0x65, 0x72, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, - 0x48, 0x00, 0x52, 0x18, 0x6b, 0x61, 0x72, 0x70, 0x65, 0x6e, 0x74, 0x65, 0x72, 0x52, 0x65, 0x73, - 0x6f, 0x75, 0x72, 0x63, 0x65, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x68, 0x0a, 0x1d, - 0x70, 0x6f, 0x64, 0x5f, 0x64, 0x69, 0x73, 0x72, 0x75, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x62, - 0x75, 0x64, 0x67, 0x65, 0x74, 0x5f, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x18, 0xed, 0x01, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x6f, - 0x64, 0x44, 0x69, 0x73, 0x72, 0x75, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x75, 0x64, 0x67, 0x65, - 0x74, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x48, 0x00, 0x52, 0x1a, 0x70, 0x6f, 0x64, 0x44, - 0x69, 0x73, 0x72, 0x75, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x75, 0x64, 0x67, 0x65, 0x74, 0x44, - 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x55, 0x0a, 0x16, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, - 0x63, 0x65, 0x5f, 0x71, 0x75, 0x6f, 0x74, 0x61, 0x5f, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, - 0x18, 0xee, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, - 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x51, 0x75, 0x6f, 0x74, 0x61, 0x44, 0x65, - 0x74, 0x61, 0x69, 0x6c, 0x73, 0x48, 0x00, 0x52, 0x14, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, - 0x65, 0x51, 0x75, 0x6f, 0x74, 0x61, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x4c, 0x0a, - 0x13, 0x76, 0x6f, 0x6c, 0x63, 0x61, 0x6e, 0x6f, 0x5f, 0x6a, 0x6f, 0x62, 0x5f, 0x64, 0x65, 0x74, - 0x61, 0x69, 0x6c, 0x73, 0x18, 0xef, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x61, 0x70, - 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x56, 0x6f, 0x6c, 0x63, 0x61, 0x6e, 0x6f, 0x4a, 0x6f, 0x62, 0x44, - 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x48, 0x00, 0x52, 0x11, 0x76, 0x6f, 0x6c, 0x63, 0x61, 0x6e, - 0x6f, 0x4a, 0x6f, 0x62, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x5e, 0x0a, 0x19, 0x73, - 0x70, 0x61, 0x72, 0x6b, 0x5f, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x5f, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x18, 0xf2, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x1f, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x70, 0x61, 0x72, 0x6b, 0x41, 0x70, - 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, - 0x48, 0x00, 0x52, 0x17, 0x73, 0x70, 0x61, 0x72, 0x6b, 0x41, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x7a, 0x0a, 0x23, 0x73, - 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x64, 0x5f, 0x73, 0x70, 0x61, 0x72, 0x6b, 0x5f, 0x61, - 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x64, 0x65, 0x74, 0x61, 0x69, - 0x6c, 0x73, 0x18, 0xf3, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x28, 0x2e, 0x61, 0x70, 0x69, 0x2e, - 0x76, 0x31, 0x2e, 0x53, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x64, 0x53, 0x70, 0x61, 0x72, - 0x6b, 0x41, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x44, 0x65, 0x74, 0x61, - 0x69, 0x6c, 0x73, 0x48, 0x00, 0x52, 0x20, 0x73, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x64, + 0x12, 0x68, 0x0a, 0x1d, 0x70, 0x6f, 0x64, 0x5f, 0x64, 0x69, 0x73, 0x72, 0x75, 0x70, 0x74, 0x69, + 0x6f, 0x6e, 0x5f, 0x62, 0x75, 0x64, 0x67, 0x65, 0x74, 0x5f, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, + 0x73, 0x18, 0xed, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, + 0x31, 0x2e, 0x50, 0x6f, 0x64, 0x44, 0x69, 0x73, 0x72, 0x75, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x42, + 0x75, 0x64, 0x67, 0x65, 0x74, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x48, 0x00, 0x52, 0x1a, + 0x70, 0x6f, 0x64, 0x44, 0x69, 0x73, 0x72, 0x75, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x75, 0x64, + 0x67, 0x65, 0x74, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x55, 0x0a, 0x16, 0x72, 0x65, + 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x71, 0x75, 0x6f, 0x74, 0x61, 0x5f, 0x64, 0x65, 0x74, + 0x61, 0x69, 0x6c, 0x73, 0x18, 0xee, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x61, 0x70, + 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x51, 0x75, 0x6f, + 0x74, 0x61, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x48, 0x00, 0x52, 0x14, 0x72, 0x65, 0x73, + 0x6f, 0x75, 0x72, 0x63, 0x65, 0x51, 0x75, 0x6f, 0x74, 0x61, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, + 0x73, 0x12, 0x4c, 0x0a, 0x13, 0x76, 0x6f, 0x6c, 0x63, 0x61, 0x6e, 0x6f, 0x5f, 0x6a, 0x6f, 0x62, + 0x5f, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x18, 0xef, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x19, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x56, 0x6f, 0x6c, 0x63, 0x61, 0x6e, 0x6f, + 0x4a, 0x6f, 0x62, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x48, 0x00, 0x52, 0x11, 0x76, 0x6f, + 0x6c, 0x63, 0x61, 0x6e, 0x6f, 0x4a, 0x6f, 0x62, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, + 0x5e, 0x0a, 0x19, 0x73, 0x70, 0x61, 0x72, 0x6b, 0x5f, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x18, 0xf2, 0x01, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x70, 0x61, + 0x72, 0x6b, 0x41, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x44, 0x65, 0x74, + 0x61, 0x69, 0x6c, 0x73, 0x48, 0x00, 0x52, 0x17, 0x73, 0x70, 0x61, 0x72, 0x6b, 0x41, 0x70, 0x70, + 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, + 0x7a, 0x0a, 0x23, 0x73, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x64, 0x5f, 0x73, 0x70, 0x61, + 0x72, 0x6b, 0x5f, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x64, + 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x18, 0xf3, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x28, 0x2e, + 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x64, 0x53, 0x70, 0x61, 0x72, 0x6b, 0x41, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x55, 0x0a, 0x16, 0x6b, 0x79, 0x76, 0x65, 0x72, - 0x6e, 0x6f, 0x5f, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x5f, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, - 0x73, 0x18, 0xf4, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, - 0x31, 0x2e, 0x4b, 0x79, 0x76, 0x65, 0x72, 0x6e, 0x6f, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x44, - 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x48, 0x00, 0x52, 0x14, 0x6b, 0x79, 0x76, 0x65, 0x72, 0x6e, - 0x6f, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x68, - 0x0a, 0x1d, 0x6b, 0x79, 0x76, 0x65, 0x72, 0x6e, 0x6f, 0x5f, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, - 0x5f, 0x72, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x5f, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x18, - 0xf5, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, - 0x4b, 0x79, 0x76, 0x65, 0x72, 0x6e, 0x6f, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x52, 0x65, 0x70, - 0x6f, 0x72, 0x74, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x48, 0x00, 0x52, 0x1a, 0x6b, 0x79, - 0x76, 0x65, 0x72, 0x6e, 0x6f, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x52, 0x65, 0x70, 0x6f, 0x72, - 0x74, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x83, 0x01, 0x0a, 0x26, 0x67, 0x61, 0x74, - 0x65, 0x6b, 0x65, 0x65, 0x70, 0x65, 0x72, 0x5f, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x72, 0x61, 0x69, - 0x6e, 0x74, 0x5f, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x5f, 0x64, 0x65, 0x74, 0x61, - 0x69, 0x6c, 0x73, 0x18, 0xf6, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x61, 0x70, 0x69, - 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x61, 0x74, 0x65, 0x6b, 0x65, 0x65, 0x70, 0x65, 0x72, 0x43, 0x6f, - 0x6e, 0x73, 0x74, 0x72, 0x61, 0x69, 0x6e, 0x74, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, - 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x48, 0x00, 0x52, 0x23, 0x67, 0x61, 0x74, 0x65, 0x6b, - 0x65, 0x65, 0x70, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x73, 0x74, 0x72, 0x61, 0x69, 0x6e, 0x74, 0x54, - 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x6a, - 0x0a, 0x1d, 0x67, 0x61, 0x74, 0x65, 0x6b, 0x65, 0x65, 0x70, 0x65, 0x72, 0x5f, 0x63, 0x6f, 0x6e, - 0x73, 0x74, 0x72, 0x61, 0x69, 0x6e, 0x74, 0x5f, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x18, - 0xf7, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, - 0x47, 0x61, 0x74, 0x65, 0x6b, 0x65, 0x65, 0x70, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x73, 0x74, 0x72, - 0x61, 0x69, 0x6e, 0x74, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x48, 0x00, 0x52, 0x1b, 0x67, + 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x48, 0x00, 0x52, 0x20, 0x73, 0x63, 0x68, 0x65, 0x64, + 0x75, 0x6c, 0x65, 0x64, 0x53, 0x70, 0x61, 0x72, 0x6b, 0x41, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x55, 0x0a, 0x16, 0x6b, + 0x79, 0x76, 0x65, 0x72, 0x6e, 0x6f, 0x5f, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x5f, 0x64, 0x65, + 0x74, 0x61, 0x69, 0x6c, 0x73, 0x18, 0xf4, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x61, + 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4b, 0x79, 0x76, 0x65, 0x72, 0x6e, 0x6f, 0x50, 0x6f, 0x6c, + 0x69, 0x63, 0x79, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x48, 0x00, 0x52, 0x14, 0x6b, 0x79, + 0x76, 0x65, 0x72, 0x6e, 0x6f, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x44, 0x65, 0x74, 0x61, 0x69, + 0x6c, 0x73, 0x12, 0x68, 0x0a, 0x1d, 0x6b, 0x79, 0x76, 0x65, 0x72, 0x6e, 0x6f, 0x5f, 0x70, 0x6f, + 0x6c, 0x69, 0x63, 0x79, 0x5f, 0x72, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x5f, 0x64, 0x65, 0x74, 0x61, + 0x69, 0x6c, 0x73, 0x18, 0xf5, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x61, 0x70, 0x69, + 0x2e, 0x76, 0x31, 0x2e, 0x4b, 0x79, 0x76, 0x65, 0x72, 0x6e, 0x6f, 0x50, 0x6f, 0x6c, 0x69, 0x63, + 0x79, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x48, 0x00, + 0x52, 0x1a, 0x6b, 0x79, 0x76, 0x65, 0x72, 0x6e, 0x6f, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x52, + 0x65, 0x70, 0x6f, 0x72, 0x74, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x83, 0x01, 0x0a, + 0x26, 0x67, 0x61, 0x74, 0x65, 0x6b, 0x65, 0x65, 0x70, 0x65, 0x72, 0x5f, 0x63, 0x6f, 0x6e, 0x73, + 0x74, 0x72, 0x61, 0x69, 0x6e, 0x74, 0x5f, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x5f, + 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x18, 0xf6, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2b, + 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x61, 0x74, 0x65, 0x6b, 0x65, 0x65, 0x70, + 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x73, 0x74, 0x72, 0x61, 0x69, 0x6e, 0x74, 0x54, 0x65, 0x6d, 0x70, + 0x6c, 0x61, 0x74, 0x65, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x48, 0x00, 0x52, 0x23, 0x67, 0x61, 0x74, 0x65, 0x6b, 0x65, 0x65, 0x70, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x73, 0x74, 0x72, 0x61, - 0x69, 0x6e, 0x74, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x70, 0x68, - 0x61, 0x73, 0x65, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x70, 0x68, 0x61, 0x73, 0x65, - 0x12, 0x15, 0x0a, 0x06, 0x70, 0x6f, 0x64, 0x5f, 0x69, 0x70, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x05, 0x70, 0x6f, 0x64, 0x49, 0x70, 0x12, 0x17, 0x0a, 0x07, 0x68, 0x6f, 0x73, 0x74, 0x5f, - 0x69, 0x70, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x68, 0x6f, 0x73, 0x74, 0x49, 0x70, - 0x12, 0x1b, 0x0a, 0x09, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x0d, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x08, 0x6e, 0x6f, 0x64, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x14, 0x0a, - 0x05, 0x72, 0x65, 0x61, 0x64, 0x79, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, 0x72, 0x65, - 0x61, 0x64, 0x79, 0x12, 0x1b, 0x0a, 0x09, 0x71, 0x6f, 0x73, 0x5f, 0x63, 0x6c, 0x61, 0x73, 0x73, - 0x18, 0x0f, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x71, 0x6f, 0x73, 0x43, 0x6c, 0x61, 0x73, 0x73, - 0x12, 0x27, 0x0a, 0x0f, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x63, 0x6f, - 0x75, 0x6e, 0x74, 0x18, 0x10, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0e, 0x63, 0x6f, 0x6e, 0x74, 0x61, - 0x69, 0x6e, 0x65, 0x72, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x23, 0x0a, 0x0d, 0x72, 0x65, 0x73, - 0x74, 0x61, 0x72, 0x74, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x11, 0x20, 0x01, 0x28, 0x05, - 0x52, 0x0c, 0x72, 0x65, 0x73, 0x74, 0x61, 0x72, 0x74, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x36, - 0x0a, 0x17, 0x63, 0x70, 0x75, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x73, 0x5f, 0x6d, - 0x69, 0x6c, 0x6c, 0x69, 0x63, 0x6f, 0x72, 0x65, 0x73, 0x18, 0x14, 0x20, 0x01, 0x28, 0x03, 0x52, - 0x15, 0x63, 0x70, 0x75, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x73, 0x4d, 0x69, 0x6c, 0x6c, - 0x69, 0x63, 0x6f, 0x72, 0x65, 0x73, 0x12, 0x32, 0x0a, 0x15, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, - 0x5f, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x73, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x18, - 0x15, 0x20, 0x01, 0x28, 0x03, 0x52, 0x13, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x73, 0x42, 0x79, 0x74, 0x65, 0x73, 0x12, 0x32, 0x0a, 0x15, 0x63, 0x70, - 0x75, 0x5f, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x73, 0x5f, 0x6d, 0x69, 0x6c, 0x6c, 0x69, 0x63, 0x6f, - 0x72, 0x65, 0x73, 0x18, 0x16, 0x20, 0x01, 0x28, 0x03, 0x52, 0x13, 0x63, 0x70, 0x75, 0x4c, 0x69, - 0x6d, 0x69, 0x74, 0x73, 0x4d, 0x69, 0x6c, 0x6c, 0x69, 0x63, 0x6f, 0x72, 0x65, 0x73, 0x12, 0x2e, - 0x0a, 0x13, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x5f, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x73, 0x5f, - 0x62, 0x79, 0x74, 0x65, 0x73, 0x18, 0x17, 0x20, 0x01, 0x28, 0x03, 0x52, 0x11, 0x6d, 0x65, 0x6d, - 0x6f, 0x72, 0x79, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x73, 0x42, 0x79, 0x74, 0x65, 0x73, 0x12, 0x23, - 0x0a, 0x0d, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x5f, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x18, - 0x1e, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x61, - 0x73, 0x6f, 0x6e, 0x12, 0x23, 0x0a, 0x0d, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, - 0x64, 0x5f, 0x62, 0x79, 0x18, 0x1f, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x63, 0x6f, 0x6e, 0x74, - 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x64, 0x42, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x67, 0x65, 0x18, - 0x20, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x67, 0x65, 0x12, 0x29, 0x0a, 0x10, 0x72, 0x65, - 0x70, 0x6c, 0x69, 0x63, 0x61, 0x73, 0x5f, 0x64, 0x65, 0x73, 0x69, 0x72, 0x65, 0x64, 0x18, 0x28, - 0x20, 0x01, 0x28, 0x05, 0x52, 0x0f, 0x72, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x73, 0x44, 0x65, - 0x73, 0x69, 0x72, 0x65, 0x64, 0x12, 0x25, 0x0a, 0x0e, 0x72, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, - 0x73, 0x5f, 0x72, 0x65, 0x61, 0x64, 0x79, 0x18, 0x29, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0d, 0x72, - 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x73, 0x52, 0x65, 0x61, 0x64, 0x79, 0x12, 0x2d, 0x0a, 0x12, - 0x72, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x73, 0x5f, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, - 0x6c, 0x65, 0x18, 0x2a, 0x20, 0x01, 0x28, 0x05, 0x52, 0x11, 0x72, 0x65, 0x70, 0x6c, 0x69, 0x63, - 0x61, 0x73, 0x41, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x12, 0x29, 0x0a, 0x10, 0x72, - 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x73, 0x5f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x18, - 0x2b, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0f, 0x72, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x73, 0x55, - 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x12, 0x23, 0x0a, 0x0d, 0x73, 0x74, 0x72, 0x61, 0x74, 0x65, - 0x67, 0x79, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x2c, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x73, - 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x54, 0x79, 0x70, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x61, - 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x18, 0x2d, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, - 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x70, 0x72, 0x6f, - 0x67, 0x72, 0x65, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x18, 0x2e, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, - 0x70, 0x72, 0x6f, 0x67, 0x72, 0x65, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x12, 0x2b, 0x0a, 0x11, 0x64, - 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, - 0x18, 0x2f, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x64, 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x6d, 0x65, - 0x6e, 0x74, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x69, 0x6d, 0x61, 0x67, - 0x65, 0x73, 0x18, 0x30, 0x20, 0x03, 0x28, 0x09, 0x52, 0x06, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x73, - 0x12, 0x21, 0x0a, 0x0c, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, - 0x18, 0x3c, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x4e, - 0x61, 0x6d, 0x65, 0x12, 0x32, 0x0a, 0x15, 0x70, 0x6f, 0x64, 0x5f, 0x6d, 0x61, 0x6e, 0x61, 0x67, - 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x18, 0x3d, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x13, 0x70, 0x6f, 0x64, 0x4d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, - 0x74, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x12, 0x30, 0x0a, 0x14, 0x75, 0x70, 0x64, 0x61, 0x74, - 0x65, 0x5f, 0x73, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, - 0x3e, 0x20, 0x01, 0x28, 0x09, 0x52, 0x12, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x53, 0x74, 0x72, - 0x61, 0x74, 0x65, 0x67, 0x79, 0x54, 0x79, 0x70, 0x65, 0x12, 0x3d, 0x0a, 0x1b, 0x76, 0x6f, 0x6c, - 0x75, 0x6d, 0x65, 0x5f, 0x63, 0x6c, 0x61, 0x69, 0x6d, 0x5f, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, - 0x74, 0x65, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x3f, 0x20, 0x01, 0x28, 0x05, 0x52, 0x18, - 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x43, 0x6c, 0x61, 0x69, 0x6d, 0x54, 0x65, 0x6d, 0x70, 0x6c, - 0x61, 0x74, 0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x30, 0x0a, 0x14, 0x70, 0x76, 0x63, 0x5f, - 0x72, 0x65, 0x74, 0x65, 0x6e, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, - 0x18, 0x40, 0x20, 0x01, 0x28, 0x09, 0x52, 0x12, 0x70, 0x76, 0x63, 0x52, 0x65, 0x74, 0x65, 0x6e, - 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x12, 0x29, 0x0a, 0x10, 0x63, 0x75, - 0x72, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x72, 0x65, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x41, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x76, - 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x27, 0x0a, 0x0f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x5f, - 0x72, 0x65, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x42, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, - 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x23, - 0x0a, 0x0d, 0x6e, 0x6f, 0x64, 0x65, 0x73, 0x5f, 0x64, 0x65, 0x73, 0x69, 0x72, 0x65, 0x64, 0x18, - 0x46, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0c, 0x6e, 0x6f, 0x64, 0x65, 0x73, 0x44, 0x65, 0x73, 0x69, - 0x72, 0x65, 0x64, 0x12, 0x1f, 0x0a, 0x0b, 0x6e, 0x6f, 0x64, 0x65, 0x73, 0x5f, 0x72, 0x65, 0x61, - 0x64, 0x79, 0x18, 0x47, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0a, 0x6e, 0x6f, 0x64, 0x65, 0x73, 0x52, - 0x65, 0x61, 0x64, 0x79, 0x12, 0x27, 0x0a, 0x0f, 0x6e, 0x6f, 0x64, 0x65, 0x73, 0x5f, 0x61, 0x76, - 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x18, 0x48, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0e, 0x6e, - 0x6f, 0x64, 0x65, 0x73, 0x41, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x12, 0x23, 0x0a, - 0x0d, 0x6e, 0x6f, 0x64, 0x65, 0x73, 0x5f, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x18, 0x49, - 0x20, 0x01, 0x28, 0x05, 0x52, 0x0c, 0x6e, 0x6f, 0x64, 0x65, 0x73, 0x43, 0x75, 0x72, 0x72, 0x65, - 0x6e, 0x74, 0x12, 0x23, 0x0a, 0x0d, 0x6e, 0x6f, 0x64, 0x65, 0x73, 0x5f, 0x75, 0x70, 0x64, 0x61, - 0x74, 0x65, 0x64, 0x18, 0x4a, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0c, 0x6e, 0x6f, 0x64, 0x65, 0x73, - 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x12, 0x2d, 0x0a, 0x12, 0x6e, 0x6f, 0x64, 0x65, 0x73, - 0x5f, 0x6d, 0x69, 0x73, 0x73, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x64, 0x18, 0x4b, 0x20, - 0x01, 0x28, 0x05, 0x52, 0x11, 0x6e, 0x6f, 0x64, 0x65, 0x73, 0x4d, 0x69, 0x73, 0x73, 0x63, 0x68, - 0x65, 0x64, 0x75, 0x6c, 0x65, 0x64, 0x12, 0x27, 0x0a, 0x0f, 0x6d, 0x61, 0x78, 0x5f, 0x75, 0x6e, - 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x18, 0x4c, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x0e, 0x6d, 0x61, 0x78, 0x55, 0x6e, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x12, - 0x25, 0x0a, 0x0e, 0x70, 0x72, 0x69, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x5f, 0x63, 0x6c, 0x61, 0x73, - 0x73, 0x18, 0x4d, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x70, 0x72, 0x69, 0x6f, 0x72, 0x69, 0x74, - 0x79, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x12, 0x36, 0x0a, 0x17, 0x6a, 0x6f, 0x62, 0x5f, 0x63, 0x6f, - 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x5f, 0x64, 0x65, 0x73, 0x69, 0x72, 0x65, - 0x64, 0x18, 0x50, 0x20, 0x01, 0x28, 0x05, 0x52, 0x15, 0x6a, 0x6f, 0x62, 0x43, 0x6f, 0x6d, 0x70, - 0x6c, 0x65, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x44, 0x65, 0x73, 0x69, 0x72, 0x65, 0x64, 0x12, 0x3a, - 0x0a, 0x19, 0x6a, 0x6f, 0x62, 0x5f, 0x63, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x69, 0x6f, 0x6e, - 0x73, 0x5f, 0x73, 0x75, 0x63, 0x63, 0x65, 0x65, 0x64, 0x65, 0x64, 0x18, 0x51, 0x20, 0x01, 0x28, - 0x05, 0x52, 0x17, 0x6a, 0x6f, 0x62, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x69, 0x6f, 0x6e, - 0x73, 0x53, 0x75, 0x63, 0x63, 0x65, 0x65, 0x64, 0x65, 0x64, 0x12, 0x27, 0x0a, 0x0f, 0x6a, 0x6f, - 0x62, 0x5f, 0x70, 0x61, 0x72, 0x61, 0x6c, 0x6c, 0x65, 0x6c, 0x69, 0x73, 0x6d, 0x18, 0x52, 0x20, - 0x01, 0x28, 0x05, 0x52, 0x0e, 0x6a, 0x6f, 0x62, 0x50, 0x61, 0x72, 0x61, 0x6c, 0x6c, 0x65, 0x6c, - 0x69, 0x73, 0x6d, 0x12, 0x2a, 0x0a, 0x11, 0x6a, 0x6f, 0x62, 0x5f, 0x62, 0x61, 0x63, 0x6b, 0x6f, - 0x66, 0x66, 0x5f, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x18, 0x53, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0f, - 0x6a, 0x6f, 0x62, 0x42, 0x61, 0x63, 0x6b, 0x6f, 0x66, 0x66, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x12, - 0x2e, 0x0a, 0x13, 0x6a, 0x6f, 0x62, 0x5f, 0x63, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x69, 0x6f, - 0x6e, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x18, 0x54, 0x20, 0x01, 0x28, 0x09, 0x52, 0x11, 0x6a, 0x6f, - 0x62, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x69, 0x6f, 0x6e, 0x4d, 0x6f, 0x64, 0x65, 0x12, - 0x23, 0x0a, 0x0d, 0x6a, 0x6f, 0x62, 0x5f, 0x73, 0x75, 0x73, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x64, - 0x18, 0x55, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x6a, 0x6f, 0x62, 0x53, 0x75, 0x73, 0x70, 0x65, - 0x6e, 0x64, 0x65, 0x64, 0x12, 0x21, 0x0a, 0x0c, 0x6a, 0x6f, 0x62, 0x5f, 0x64, 0x75, 0x72, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x56, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x6a, 0x6f, 0x62, 0x44, - 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1d, 0x0a, 0x0a, 0x6a, 0x6f, 0x62, 0x5f, 0x73, - 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x57, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6a, 0x6f, 0x62, - 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x26, 0x0a, 0x0f, 0x6a, 0x6f, 0x62, 0x5f, 0x61, 0x63, - 0x74, 0x69, 0x76, 0x65, 0x5f, 0x70, 0x6f, 0x64, 0x73, 0x18, 0x58, 0x20, 0x01, 0x28, 0x05, 0x52, - 0x0d, 0x6a, 0x6f, 0x62, 0x41, 0x63, 0x74, 0x69, 0x76, 0x65, 0x50, 0x6f, 0x64, 0x73, 0x12, 0x26, - 0x0a, 0x0f, 0x6a, 0x6f, 0x62, 0x5f, 0x66, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x5f, 0x70, 0x6f, 0x64, - 0x73, 0x18, 0x59, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0d, 0x6a, 0x6f, 0x62, 0x46, 0x61, 0x69, 0x6c, - 0x65, 0x64, 0x50, 0x6f, 0x64, 0x73, 0x12, 0x23, 0x0a, 0x0d, 0x63, 0x72, 0x6f, 0x6e, 0x5f, 0x73, - 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x18, 0x5a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x63, - 0x72, 0x6f, 0x6e, 0x53, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x12, 0x25, 0x0a, 0x0e, 0x63, - 0x72, 0x6f, 0x6e, 0x5f, 0x73, 0x75, 0x73, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x64, 0x18, 0x5b, 0x20, - 0x01, 0x28, 0x08, 0x52, 0x0d, 0x63, 0x72, 0x6f, 0x6e, 0x53, 0x75, 0x73, 0x70, 0x65, 0x6e, 0x64, - 0x65, 0x64, 0x12, 0x28, 0x0a, 0x10, 0x63, 0x72, 0x6f, 0x6e, 0x5f, 0x61, 0x63, 0x74, 0x69, 0x76, - 0x65, 0x5f, 0x6a, 0x6f, 0x62, 0x73, 0x18, 0x5c, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0e, 0x63, 0x72, - 0x6f, 0x6e, 0x41, 0x63, 0x74, 0x69, 0x76, 0x65, 0x4a, 0x6f, 0x62, 0x73, 0x12, 0x35, 0x0a, 0x17, - 0x63, 0x72, 0x6f, 0x6e, 0x5f, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x73, 0x63, 0x68, 0x65, 0x64, 0x75, - 0x6c, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x5d, 0x20, 0x01, 0x28, 0x09, 0x52, 0x14, 0x63, - 0x72, 0x6f, 0x6e, 0x4c, 0x61, 0x73, 0x74, 0x53, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x54, - 0x69, 0x6d, 0x65, 0x12, 0x35, 0x0a, 0x17, 0x63, 0x72, 0x6f, 0x6e, 0x5f, 0x6e, 0x65, 0x78, 0x74, - 0x5f, 0x73, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x5e, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x14, 0x63, 0x72, 0x6f, 0x6e, 0x4e, 0x65, 0x78, 0x74, 0x53, 0x63, - 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x36, 0x0a, 0x17, 0x63, 0x72, - 0x6f, 0x6e, 0x5f, 0x63, 0x6f, 0x6e, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x5f, 0x70, - 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x18, 0x5f, 0x20, 0x01, 0x28, 0x09, 0x52, 0x15, 0x63, 0x72, 0x6f, - 0x6e, 0x43, 0x6f, 0x6e, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x50, 0x6f, 0x6c, 0x69, - 0x63, 0x79, 0x12, 0x3b, 0x0a, 0x1a, 0x63, 0x72, 0x6f, 0x6e, 0x5f, 0x73, 0x75, 0x63, 0x63, 0x65, - 0x73, 0x73, 0x66, 0x75, 0x6c, 0x5f, 0x6a, 0x6f, 0x62, 0x73, 0x5f, 0x6c, 0x69, 0x6d, 0x69, 0x74, - 0x18, 0x60, 0x20, 0x01, 0x28, 0x05, 0x52, 0x17, 0x63, 0x72, 0x6f, 0x6e, 0x53, 0x75, 0x63, 0x63, - 0x65, 0x73, 0x73, 0x66, 0x75, 0x6c, 0x4a, 0x6f, 0x62, 0x73, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x12, - 0x33, 0x0a, 0x16, 0x63, 0x72, 0x6f, 0x6e, 0x5f, 0x66, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x5f, 0x6a, - 0x6f, 0x62, 0x73, 0x5f, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x18, 0x61, 0x20, 0x01, 0x28, 0x05, 0x52, - 0x13, 0x63, 0x72, 0x6f, 0x6e, 0x46, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x4a, 0x6f, 0x62, 0x73, 0x4c, - 0x69, 0x6d, 0x69, 0x74, 0x12, 0x43, 0x0a, 0x1e, 0x63, 0x72, 0x6f, 0x6e, 0x5f, 0x73, 0x74, 0x61, - 0x72, 0x74, 0x69, 0x6e, 0x67, 0x5f, 0x64, 0x65, 0x61, 0x64, 0x6c, 0x69, 0x6e, 0x65, 0x5f, 0x73, - 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x18, 0x62, 0x20, 0x01, 0x28, 0x05, 0x52, 0x1b, 0x63, 0x72, - 0x6f, 0x6e, 0x53, 0x74, 0x61, 0x72, 0x74, 0x69, 0x6e, 0x67, 0x44, 0x65, 0x61, 0x64, 0x6c, 0x69, - 0x6e, 0x65, 0x53, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x12, 0x21, 0x0a, 0x0c, 0x73, 0x65, 0x72, - 0x76, 0x69, 0x63, 0x65, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x64, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x0b, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x54, 0x79, 0x70, 0x65, 0x12, 0x2c, 0x0a, 0x12, - 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, - 0x69, 0x70, 0x18, 0x65, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, - 0x65, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, 0x70, 0x12, 0x30, 0x0a, 0x14, 0x73, 0x65, - 0x72, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x5f, 0x69, - 0x70, 0x73, 0x18, 0x66, 0x20, 0x03, 0x28, 0x09, 0x52, 0x12, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, - 0x65, 0x45, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x49, 0x70, 0x73, 0x12, 0x38, 0x0a, 0x0d, - 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x70, 0x6f, 0x72, 0x74, 0x73, 0x18, 0x67, 0x20, - 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x72, - 0x76, 0x69, 0x63, 0x65, 0x50, 0x6f, 0x72, 0x74, 0x52, 0x0c, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, - 0x65, 0x50, 0x6f, 0x72, 0x74, 0x73, 0x12, 0x57, 0x0a, 0x10, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, - 0x65, 0x5f, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x18, 0x68, 0x20, 0x03, 0x28, 0x0b, - 0x32, 0x2c, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, - 0x63, 0x65, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, - 0x65, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0f, - 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x12, - 0x38, 0x0a, 0x18, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x73, 0x65, 0x73, 0x73, 0x69, - 0x6f, 0x6e, 0x5f, 0x61, 0x66, 0x66, 0x69, 0x6e, 0x69, 0x74, 0x79, 0x18, 0x69, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x16, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, - 0x6e, 0x41, 0x66, 0x66, 0x69, 0x6e, 0x69, 0x74, 0x79, 0x12, 0x45, 0x0a, 0x1f, 0x73, 0x65, 0x72, - 0x76, 0x69, 0x63, 0x65, 0x5f, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x5f, 0x74, 0x72, - 0x61, 0x66, 0x66, 0x69, 0x63, 0x5f, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x18, 0x6a, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x1c, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x45, 0x78, 0x74, 0x65, 0x72, - 0x6e, 0x61, 0x6c, 0x54, 0x72, 0x61, 0x66, 0x66, 0x69, 0x63, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, - 0x12, 0x45, 0x0a, 0x1f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x69, 0x6e, 0x74, 0x65, - 0x72, 0x6e, 0x61, 0x6c, 0x5f, 0x74, 0x72, 0x61, 0x66, 0x66, 0x69, 0x63, 0x5f, 0x70, 0x6f, 0x6c, - 0x69, 0x63, 0x79, 0x18, 0x6b, 0x20, 0x01, 0x28, 0x09, 0x52, 0x1c, 0x73, 0x65, 0x72, 0x76, 0x69, - 0x63, 0x65, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x54, 0x72, 0x61, 0x66, 0x66, 0x69, - 0x63, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x12, 0x4c, 0x0a, 0x23, 0x73, 0x65, 0x72, 0x76, 0x69, - 0x63, 0x65, 0x5f, 0x6c, 0x6f, 0x61, 0x64, 0x5f, 0x62, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x72, - 0x5f, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x18, 0x6c, - 0x20, 0x03, 0x28, 0x09, 0x52, 0x1f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x4c, 0x6f, 0x61, - 0x64, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x72, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, - 0x61, 0x6e, 0x67, 0x65, 0x73, 0x12, 0x3d, 0x0a, 0x1b, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, - 0x5f, 0x6c, 0x6f, 0x61, 0x64, 0x5f, 0x62, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x72, 0x5f, 0x63, - 0x6c, 0x61, 0x73, 0x73, 0x18, 0x6d, 0x20, 0x01, 0x28, 0x09, 0x52, 0x18, 0x73, 0x65, 0x72, 0x76, - 0x69, 0x63, 0x65, 0x4c, 0x6f, 0x61, 0x64, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x72, 0x43, - 0x6c, 0x61, 0x73, 0x73, 0x12, 0x2e, 0x0a, 0x13, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x5f, - 0x69, 0x70, 0x5f, 0x66, 0x61, 0x6d, 0x69, 0x6c, 0x69, 0x65, 0x73, 0x18, 0x6e, 0x20, 0x03, 0x28, - 0x09, 0x52, 0x11, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x49, 0x70, 0x46, 0x61, 0x6d, 0x69, - 0x6c, 0x69, 0x65, 0x73, 0x12, 0x37, 0x0a, 0x18, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x5f, - 0x69, 0x70, 0x5f, 0x66, 0x61, 0x6d, 0x69, 0x6c, 0x79, 0x5f, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, - 0x18, 0x6f, 0x20, 0x01, 0x28, 0x09, 0x52, 0x15, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x49, - 0x70, 0x46, 0x61, 0x6d, 0x69, 0x6c, 0x79, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x12, 0x2c, 0x0a, - 0x12, 0x69, 0x6e, 0x67, 0x72, 0x65, 0x73, 0x73, 0x5f, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x5f, 0x6e, - 0x61, 0x6d, 0x65, 0x18, 0x78, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x69, 0x6e, 0x67, 0x72, 0x65, - 0x73, 0x73, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x69, - 0x6e, 0x67, 0x72, 0x65, 0x73, 0x73, 0x5f, 0x68, 0x6f, 0x73, 0x74, 0x73, 0x18, 0x79, 0x20, 0x03, - 0x28, 0x09, 0x52, 0x0c, 0x69, 0x6e, 0x67, 0x72, 0x65, 0x73, 0x73, 0x48, 0x6f, 0x73, 0x74, 0x73, - 0x12, 0x23, 0x0a, 0x0d, 0x69, 0x6e, 0x67, 0x72, 0x65, 0x73, 0x73, 0x5f, 0x70, 0x61, 0x74, 0x68, - 0x73, 0x18, 0x7a, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0c, 0x69, 0x6e, 0x67, 0x72, 0x65, 0x73, 0x73, - 0x50, 0x61, 0x74, 0x68, 0x73, 0x12, 0x29, 0x0a, 0x10, 0x69, 0x6e, 0x67, 0x72, 0x65, 0x73, 0x73, - 0x5f, 0x62, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x73, 0x18, 0x7b, 0x20, 0x03, 0x28, 0x09, 0x52, - 0x0f, 0x69, 0x6e, 0x67, 0x72, 0x65, 0x73, 0x73, 0x42, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x73, - 0x12, 0x2a, 0x0a, 0x11, 0x69, 0x6e, 0x67, 0x72, 0x65, 0x73, 0x73, 0x5f, 0x74, 0x6c, 0x73, 0x5f, - 0x68, 0x6f, 0x73, 0x74, 0x73, 0x18, 0x7c, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0f, 0x69, 0x6e, 0x67, - 0x72, 0x65, 0x73, 0x73, 0x54, 0x6c, 0x73, 0x48, 0x6f, 0x73, 0x74, 0x73, 0x12, 0x2e, 0x0a, 0x13, - 0x69, 0x6e, 0x67, 0x72, 0x65, 0x73, 0x73, 0x5f, 0x74, 0x6c, 0x73, 0x5f, 0x73, 0x65, 0x63, 0x72, - 0x65, 0x74, 0x73, 0x18, 0x7d, 0x20, 0x03, 0x28, 0x09, 0x52, 0x11, 0x69, 0x6e, 0x67, 0x72, 0x65, - 0x73, 0x73, 0x54, 0x6c, 0x73, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x73, 0x12, 0x39, 0x0a, 0x19, - 0x69, 0x6e, 0x67, 0x72, 0x65, 0x73, 0x73, 0x5f, 0x6c, 0x6f, 0x61, 0x64, 0x5f, 0x62, 0x61, 0x6c, - 0x61, 0x6e, 0x63, 0x65, 0x72, 0x5f, 0x69, 0x70, 0x73, 0x18, 0x7e, 0x20, 0x03, 0x28, 0x09, 0x52, - 0x16, 0x69, 0x6e, 0x67, 0x72, 0x65, 0x73, 0x73, 0x4c, 0x6f, 0x61, 0x64, 0x42, 0x61, 0x6c, 0x61, - 0x6e, 0x63, 0x65, 0x72, 0x49, 0x70, 0x73, 0x12, 0x2e, 0x0a, 0x13, 0x69, 0x6e, 0x67, 0x72, 0x65, - 0x73, 0x73, 0x5f, 0x72, 0x75, 0x6c, 0x65, 0x73, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x7f, - 0x20, 0x01, 0x28, 0x05, 0x52, 0x11, 0x69, 0x6e, 0x67, 0x72, 0x65, 0x73, 0x73, 0x52, 0x75, 0x6c, - 0x65, 0x73, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x2b, 0x0a, 0x11, 0x69, 0x6e, 0x67, 0x72, 0x65, - 0x73, 0x73, 0x5f, 0x74, 0x6c, 0x73, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x80, 0x01, 0x20, - 0x01, 0x28, 0x05, 0x52, 0x0f, 0x69, 0x6e, 0x67, 0x72, 0x65, 0x73, 0x73, 0x54, 0x6c, 0x73, 0x43, - 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x3e, 0x0a, 0x1b, 0x69, 0x6e, 0x67, 0x72, 0x65, 0x73, 0x73, 0x5f, - 0x68, 0x61, 0x73, 0x5f, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x5f, 0x62, 0x61, 0x63, 0x6b, - 0x65, 0x6e, 0x64, 0x18, 0x81, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x18, 0x69, 0x6e, 0x67, 0x72, - 0x65, 0x73, 0x73, 0x48, 0x61, 0x73, 0x44, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x42, 0x61, 0x63, - 0x6b, 0x65, 0x6e, 0x64, 0x12, 0x34, 0x0a, 0x16, 0x70, 0x76, 0x63, 0x5f, 0x73, 0x74, 0x6f, 0x72, - 0x61, 0x67, 0x65, 0x5f, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x82, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x13, 0x70, 0x76, 0x63, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, - 0x65, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x35, 0x0a, 0x16, 0x70, 0x76, - 0x63, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x65, 0x64, 0x5f, 0x63, 0x61, 0x70, 0x61, - 0x63, 0x69, 0x74, 0x79, 0x18, 0x83, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x14, 0x70, 0x76, 0x63, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x65, 0x64, 0x43, 0x61, 0x70, 0x61, 0x63, 0x69, 0x74, - 0x79, 0x12, 0x2f, 0x0a, 0x13, 0x70, 0x76, 0x63, 0x5f, 0x61, 0x63, 0x74, 0x75, 0x61, 0x6c, 0x5f, - 0x63, 0x61, 0x70, 0x61, 0x63, 0x69, 0x74, 0x79, 0x18, 0x84, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x11, 0x70, 0x76, 0x63, 0x41, 0x63, 0x74, 0x75, 0x61, 0x6c, 0x43, 0x61, 0x70, 0x61, 0x63, 0x69, - 0x74, 0x79, 0x12, 0x29, 0x0a, 0x10, 0x70, 0x76, 0x63, 0x5f, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, - 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x73, 0x18, 0x85, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0e, 0x70, - 0x76, 0x63, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x4d, 0x6f, 0x64, 0x65, 0x73, 0x12, 0x27, 0x0a, - 0x0f, 0x70, 0x76, 0x63, 0x5f, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x6d, 0x6f, 0x64, 0x65, - 0x18, 0x86, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x70, 0x76, 0x63, 0x56, 0x6f, 0x6c, 0x75, - 0x6d, 0x65, 0x4d, 0x6f, 0x64, 0x65, 0x12, 0x27, 0x0a, 0x0f, 0x70, 0x76, 0x63, 0x5f, 0x76, 0x6f, - 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x87, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x0d, 0x70, 0x76, 0x63, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, - 0x1e, 0x0a, 0x0a, 0x70, 0x76, 0x63, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x88, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x70, 0x76, 0x63, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, - 0x21, 0x0a, 0x0c, 0x70, 0x76, 0x63, 0x5f, 0x69, 0x73, 0x5f, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x18, - 0x89, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x70, 0x76, 0x63, 0x49, 0x73, 0x42, 0x6f, 0x75, - 0x6e, 0x64, 0x12, 0x3a, 0x0a, 0x19, 0x70, 0x76, 0x63, 0x5f, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, - 0x6f, 0x72, 0x5f, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x5f, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x18, - 0x8a, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x16, 0x70, 0x76, 0x63, 0x53, 0x65, 0x6c, 0x65, 0x63, - 0x74, 0x6f, 0x72, 0x4d, 0x61, 0x74, 0x63, 0x68, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x12, 0x35, - 0x0a, 0x16, 0x70, 0x76, 0x63, 0x5f, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x5f, 0x74, 0x6f, - 0x70, 0x6f, 0x6c, 0x6f, 0x67, 0x69, 0x65, 0x73, 0x18, 0x8b, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, - 0x14, 0x70, 0x76, 0x63, 0x41, 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x54, 0x6f, 0x70, 0x6f, 0x6c, - 0x6f, 0x67, 0x69, 0x65, 0x73, 0x12, 0x32, 0x0a, 0x15, 0x70, 0x76, 0x5f, 0x73, 0x74, 0x6f, 0x72, - 0x61, 0x67, 0x65, 0x5f, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x96, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x12, 0x70, 0x76, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, - 0x43, 0x6c, 0x61, 0x73, 0x73, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x70, 0x76, 0x5f, - 0x63, 0x61, 0x70, 0x61, 0x63, 0x69, 0x74, 0x79, 0x18, 0x97, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x0a, 0x70, 0x76, 0x43, 0x61, 0x70, 0x61, 0x63, 0x69, 0x74, 0x79, 0x12, 0x27, 0x0a, 0x0f, 0x70, - 0x76, 0x5f, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x73, 0x18, 0x98, - 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0d, 0x70, 0x76, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x4d, - 0x6f, 0x64, 0x65, 0x73, 0x12, 0x2b, 0x0a, 0x11, 0x70, 0x76, 0x5f, 0x72, 0x65, 0x63, 0x6c, 0x61, - 0x69, 0x6d, 0x5f, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x18, 0x99, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x0f, 0x70, 0x76, 0x52, 0x65, 0x63, 0x6c, 0x61, 0x69, 0x6d, 0x50, 0x6f, 0x6c, 0x69, 0x63, - 0x79, 0x12, 0x2a, 0x0a, 0x11, 0x70, 0x76, 0x5f, 0x63, 0x6c, 0x61, 0x69, 0x6d, 0x5f, 0x72, 0x65, - 0x66, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x9a, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x70, - 0x76, 0x43, 0x6c, 0x61, 0x69, 0x6d, 0x52, 0x65, 0x66, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x34, 0x0a, - 0x16, 0x70, 0x76, 0x5f, 0x63, 0x6c, 0x61, 0x69, 0x6d, 0x5f, 0x72, 0x65, 0x66, 0x5f, 0x6e, 0x61, - 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x9b, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x13, - 0x70, 0x76, 0x43, 0x6c, 0x61, 0x69, 0x6d, 0x52, 0x65, 0x66, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, - 0x61, 0x63, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x70, 0x76, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, - 0x18, 0x9c, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x76, 0x53, 0x74, 0x61, 0x74, 0x75, - 0x73, 0x12, 0x1f, 0x0a, 0x0b, 0x70, 0x76, 0x5f, 0x69, 0x73, 0x5f, 0x62, 0x6f, 0x75, 0x6e, 0x64, - 0x18, 0x9d, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x70, 0x76, 0x49, 0x73, 0x42, 0x6f, 0x75, - 0x6e, 0x64, 0x12, 0x25, 0x0a, 0x0e, 0x70, 0x76, 0x5f, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, - 0x6d, 0x6f, 0x64, 0x65, 0x18, 0x9e, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x70, 0x76, 0x56, - 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x4d, 0x6f, 0x64, 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x70, 0x76, 0x5f, - 0x63, 0x73, 0x69, 0x5f, 0x64, 0x72, 0x69, 0x76, 0x65, 0x72, 0x18, 0x9f, 0x01, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x0b, 0x70, 0x76, 0x43, 0x73, 0x69, 0x44, 0x72, 0x69, 0x76, 0x65, 0x72, 0x12, 0x32, - 0x0a, 0x15, 0x70, 0x76, 0x5f, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x6f, 0x75, 0x72, - 0x63, 0x65, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0xa0, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x12, - 0x70, 0x76, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x54, 0x79, - 0x70, 0x65, 0x12, 0x34, 0x0a, 0x16, 0x70, 0x76, 0x5f, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x61, 0x66, - 0x66, 0x69, 0x6e, 0x69, 0x74, 0x79, 0x5f, 0x7a, 0x6f, 0x6e, 0x65, 0x73, 0x18, 0xa1, 0x01, 0x20, - 0x03, 0x28, 0x09, 0x52, 0x13, 0x70, 0x76, 0x4e, 0x6f, 0x64, 0x65, 0x41, 0x66, 0x66, 0x69, 0x6e, - 0x69, 0x74, 0x79, 0x5a, 0x6f, 0x6e, 0x65, 0x73, 0x12, 0x26, 0x0a, 0x0e, 0x73, 0x63, 0x5f, 0x70, - 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x18, 0xaa, 0x01, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x0d, 0x73, 0x63, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, - 0x12, 0x2b, 0x0a, 0x11, 0x73, 0x63, 0x5f, 0x72, 0x65, 0x63, 0x6c, 0x61, 0x69, 0x6d, 0x5f, 0x70, - 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x18, 0xab, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x73, 0x63, - 0x52, 0x65, 0x63, 0x6c, 0x61, 0x69, 0x6d, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x12, 0x34, 0x0a, - 0x16, 0x73, 0x63, 0x5f, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x62, 0x69, 0x6e, 0x64, 0x69, - 0x6e, 0x67, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x18, 0xac, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x13, - 0x73, 0x63, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x42, 0x69, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x4d, - 0x6f, 0x64, 0x65, 0x12, 0x3a, 0x0a, 0x19, 0x73, 0x63, 0x5f, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x5f, - 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x65, 0x78, 0x70, 0x61, 0x6e, 0x73, 0x69, 0x6f, 0x6e, - 0x18, 0xad, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x16, 0x73, 0x63, 0x41, 0x6c, 0x6c, 0x6f, 0x77, - 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x45, 0x78, 0x70, 0x61, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x12, - 0x4f, 0x0a, 0x0d, 0x73, 0x63, 0x5f, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, - 0x18, 0xae, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x29, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, - 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, - 0x2e, 0x53, 0x63, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x45, 0x6e, 0x74, - 0x72, 0x79, 0x52, 0x0c, 0x73, 0x63, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, - 0x12, 0x33, 0x0a, 0x15, 0x73, 0x63, 0x5f, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x5f, 0x74, - 0x6f, 0x70, 0x6f, 0x6c, 0x6f, 0x67, 0x69, 0x65, 0x73, 0x18, 0xaf, 0x01, 0x20, 0x03, 0x28, 0x09, - 0x52, 0x13, 0x73, 0x63, 0x41, 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x54, 0x6f, 0x70, 0x6f, 0x6c, - 0x6f, 0x67, 0x69, 0x65, 0x73, 0x12, 0x29, 0x0a, 0x10, 0x73, 0x63, 0x5f, 0x6d, 0x6f, 0x75, 0x6e, - 0x74, 0x5f, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0xb0, 0x01, 0x20, 0x03, 0x28, 0x09, - 0x52, 0x0e, 0x73, 0x63, 0x4d, 0x6f, 0x75, 0x6e, 0x74, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, - 0x12, 0x1c, 0x0a, 0x09, 0x6e, 0x73, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0xb1, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6e, 0x73, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x24, - 0x0a, 0x0d, 0x6e, 0x73, 0x5f, 0x66, 0x69, 0x6e, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x72, 0x73, 0x18, - 0xb2, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0c, 0x6e, 0x73, 0x46, 0x69, 0x6e, 0x61, 0x6c, 0x69, - 0x7a, 0x65, 0x72, 0x73, 0x12, 0x2b, 0x0a, 0x11, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x63, 0x70, 0x75, - 0x5f, 0x63, 0x61, 0x70, 0x61, 0x63, 0x69, 0x74, 0x79, 0x18, 0xb3, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x0f, 0x6e, 0x6f, 0x64, 0x65, 0x43, 0x70, 0x75, 0x43, 0x61, 0x70, 0x61, 0x63, 0x69, 0x74, - 0x79, 0x12, 0x31, 0x0a, 0x14, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, - 0x5f, 0x63, 0x61, 0x70, 0x61, 0x63, 0x69, 0x74, 0x79, 0x18, 0xb4, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x12, 0x6e, 0x6f, 0x64, 0x65, 0x4d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x43, 0x61, 0x70, 0x61, - 0x63, 0x69, 0x74, 0x79, 0x12, 0x1e, 0x0a, 0x0a, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x72, 0x65, 0x61, - 0x64, 0x79, 0x18, 0xb5, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x6e, 0x6f, 0x64, 0x65, 0x52, - 0x65, 0x61, 0x64, 0x79, 0x12, 0x33, 0x0a, 0x0b, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x74, 0x61, 0x69, - 0x6e, 0x74, 0x73, 0x18, 0xb6, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x61, 0x70, 0x69, - 0x2e, 0x76, 0x31, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x54, 0x61, 0x69, 0x6e, 0x74, 0x52, 0x0a, 0x6e, - 0x6f, 0x64, 0x65, 0x54, 0x61, 0x69, 0x6e, 0x74, 0x73, 0x12, 0x1e, 0x0a, 0x0a, 0x6e, 0x6f, 0x64, - 0x65, 0x5f, 0x72, 0x6f, 0x6c, 0x65, 0x73, 0x18, 0xb7, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x09, - 0x6e, 0x6f, 0x64, 0x65, 0x52, 0x6f, 0x6c, 0x65, 0x73, 0x12, 0x29, 0x0a, 0x10, 0x6e, 0x6f, 0x64, - 0x65, 0x5f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x5f, 0x69, 0x70, 0x18, 0xb8, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x6e, 0x6f, 0x64, 0x65, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, - 0x61, 0x6c, 0x49, 0x70, 0x12, 0x29, 0x0a, 0x10, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x65, 0x78, 0x74, - 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x5f, 0x69, 0x70, 0x18, 0xb9, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x0e, 0x6e, 0x6f, 0x64, 0x65, 0x45, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x49, 0x70, 0x12, - 0x22, 0x0a, 0x0c, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, - 0xba, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x6e, 0x6f, 0x64, 0x65, 0x56, 0x65, 0x72, 0x73, - 0x69, 0x6f, 0x6e, 0x12, 0x2d, 0x0a, 0x12, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x69, 0x6e, 0x73, 0x74, - 0x61, 0x6e, 0x63, 0x65, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0xbb, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x10, 0x6e, 0x6f, 0x64, 0x65, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x54, 0x79, - 0x70, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x6f, 0x73, 0x18, 0xbc, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x6e, 0x6f, 0x64, 0x65, 0x4f, 0x73, 0x12, 0x35, 0x0a, 0x16, - 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x72, - 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x18, 0xbd, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x14, 0x6e, - 0x6f, 0x64, 0x65, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x52, 0x75, 0x6e, 0x74, - 0x69, 0x6d, 0x65, 0x12, 0x29, 0x0a, 0x10, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x69, 0x6d, 0x61, 0x67, - 0x65, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0xbe, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0e, - 0x6e, 0x6f, 0x64, 0x65, 0x49, 0x6d, 0x61, 0x67, 0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x29, - 0x0a, 0x10, 0x68, 0x70, 0x61, 0x5f, 0x6d, 0x69, 0x6e, 0x5f, 0x72, 0x65, 0x70, 0x6c, 0x69, 0x63, - 0x61, 0x73, 0x18, 0xc8, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0e, 0x68, 0x70, 0x61, 0x4d, 0x69, - 0x6e, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x73, 0x12, 0x29, 0x0a, 0x10, 0x68, 0x70, 0x61, - 0x5f, 0x6d, 0x61, 0x78, 0x5f, 0x72, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x73, 0x18, 0xc9, 0x01, - 0x20, 0x01, 0x28, 0x05, 0x52, 0x0e, 0x68, 0x70, 0x61, 0x4d, 0x61, 0x78, 0x52, 0x65, 0x70, 0x6c, - 0x69, 0x63, 0x61, 0x73, 0x12, 0x31, 0x0a, 0x14, 0x68, 0x70, 0x61, 0x5f, 0x63, 0x75, 0x72, 0x72, - 0x65, 0x6e, 0x74, 0x5f, 0x72, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x73, 0x18, 0xca, 0x01, 0x20, - 0x01, 0x28, 0x05, 0x52, 0x12, 0x68, 0x70, 0x61, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x52, - 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x73, 0x12, 0x31, 0x0a, 0x14, 0x68, 0x70, 0x61, 0x5f, 0x64, - 0x65, 0x73, 0x69, 0x72, 0x65, 0x64, 0x5f, 0x72, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x73, 0x18, - 0xcb, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x12, 0x68, 0x70, 0x61, 0x44, 0x65, 0x73, 0x69, 0x72, - 0x65, 0x64, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x73, 0x12, 0x2b, 0x0a, 0x11, 0x68, 0x70, - 0x61, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, - 0xcc, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0f, 0x68, 0x70, 0x61, 0x4d, 0x65, 0x74, 0x72, 0x69, - 0x63, 0x73, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x1e, 0x0a, 0x0a, 0x68, 0x70, 0x61, 0x5f, 0x73, - 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0xcd, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x68, 0x70, - 0x61, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x3c, 0x0a, 0x1a, 0x68, 0x70, 0x61, 0x5f, 0x73, - 0x63, 0x61, 0x6c, 0x65, 0x5f, 0x75, 0x70, 0x5f, 0x73, 0x74, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x7a, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0xce, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x17, 0x68, 0x70, - 0x61, 0x53, 0x63, 0x61, 0x6c, 0x65, 0x55, 0x70, 0x53, 0x74, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x7a, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x40, 0x0a, 0x1c, 0x68, 0x70, 0x61, 0x5f, 0x73, 0x63, 0x61, - 0x6c, 0x65, 0x5f, 0x64, 0x6f, 0x77, 0x6e, 0x5f, 0x73, 0x74, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x7a, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0xcf, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x19, 0x68, 0x70, - 0x61, 0x53, 0x63, 0x61, 0x6c, 0x65, 0x44, 0x6f, 0x77, 0x6e, 0x53, 0x74, 0x61, 0x62, 0x69, 0x6c, - 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x27, 0x0a, 0x0f, 0x76, 0x70, 0x61, 0x5f, 0x75, - 0x70, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x18, 0xd2, 0x01, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x0d, 0x76, 0x70, 0x61, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4d, 0x6f, 0x64, 0x65, - 0x12, 0x2d, 0x0a, 0x12, 0x76, 0x70, 0x61, 0x5f, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x69, 0x65, 0x73, - 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0xd3, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x10, 0x76, - 0x70, 0x61, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x69, 0x65, 0x73, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, - 0x3b, 0x0a, 0x19, 0x76, 0x70, 0x61, 0x5f, 0x72, 0x65, 0x63, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x64, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0xd4, 0x01, 0x20, - 0x01, 0x28, 0x05, 0x52, 0x17, 0x76, 0x70, 0x61, 0x52, 0x65, 0x63, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, - 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x25, 0x0a, 0x0e, - 0x76, 0x70, 0x61, 0x5f, 0x63, 0x70, 0x75, 0x5f, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x18, 0xd5, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x76, 0x70, 0x61, 0x43, 0x70, 0x75, 0x54, 0x61, 0x72, - 0x67, 0x65, 0x74, 0x12, 0x2b, 0x0a, 0x11, 0x76, 0x70, 0x61, 0x5f, 0x6d, 0x65, 0x6d, 0x6f, 0x72, - 0x79, 0x5f, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x18, 0xd6, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x0f, 0x76, 0x70, 0x61, 0x4d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, - 0x12, 0x1e, 0x0a, 0x0a, 0x76, 0x70, 0x61, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0xd7, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x76, 0x70, 0x61, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, - 0x12, 0x36, 0x0a, 0x17, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x5f, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x5f, - 0x69, 0x74, 0x65, 0x6d, 0x73, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0xdc, 0x01, 0x20, 0x01, - 0x28, 0x05, 0x52, 0x14, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x49, 0x74, - 0x65, 0x6d, 0x73, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x2b, 0x0a, 0x11, 0x6c, 0x69, 0x6d, 0x69, - 0x74, 0x5f, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x73, 0x18, 0xdd, 0x01, - 0x20, 0x03, 0x28, 0x09, 0x52, 0x0f, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x52, 0x61, 0x6e, 0x67, 0x65, - 0x54, 0x79, 0x70, 0x65, 0x73, 0x12, 0x38, 0x0a, 0x18, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x5f, 0x72, - 0x61, 0x6e, 0x67, 0x65, 0x5f, 0x68, 0x61, 0x73, 0x5f, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, - 0x73, 0x18, 0xde, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x15, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x52, - 0x61, 0x6e, 0x67, 0x65, 0x48, 0x61, 0x73, 0x44, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x73, 0x12, - 0x2e, 0x0a, 0x13, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x5f, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x5f, 0x6d, - 0x61, 0x78, 0x5f, 0x63, 0x70, 0x75, 0x18, 0xdf, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x6c, - 0x69, 0x6d, 0x69, 0x74, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x4d, 0x61, 0x78, 0x43, 0x70, 0x75, 0x12, - 0x34, 0x0a, 0x16, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x5f, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x5f, 0x6d, - 0x61, 0x78, 0x5f, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x18, 0xe0, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x13, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x4d, 0x61, 0x78, 0x4d, - 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x12, 0x2e, 0x0a, 0x13, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x5f, 0x72, - 0x61, 0x6e, 0x67, 0x65, 0x5f, 0x6d, 0x69, 0x6e, 0x5f, 0x63, 0x70, 0x75, 0x18, 0xe1, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x10, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x4d, - 0x69, 0x6e, 0x43, 0x70, 0x75, 0x12, 0x34, 0x0a, 0x16, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x5f, 0x72, - 0x61, 0x6e, 0x67, 0x65, 0x5f, 0x6d, 0x69, 0x6e, 0x5f, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x18, - 0xe2, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x13, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x52, 0x61, 0x6e, - 0x67, 0x65, 0x4d, 0x69, 0x6e, 0x4d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x12, 0x41, 0x0a, 0x1d, 0x6c, - 0x69, 0x6d, 0x69, 0x74, 0x5f, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x5f, 0x64, 0x65, 0x66, 0x61, 0x75, - 0x6c, 0x74, 0x5f, 0x63, 0x70, 0x75, 0x5f, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x18, 0xe3, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x19, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x44, - 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x43, 0x70, 0x75, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x12, 0x47, - 0x0a, 0x20, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x5f, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x5f, 0x64, 0x65, - 0x66, 0x61, 0x75, 0x6c, 0x74, 0x5f, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x5f, 0x6c, 0x69, 0x6d, - 0x69, 0x74, 0x18, 0xe4, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x1c, 0x6c, 0x69, 0x6d, 0x69, 0x74, - 0x52, 0x61, 0x6e, 0x67, 0x65, 0x44, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x4d, 0x65, 0x6d, 0x6f, - 0x72, 0x79, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x12, 0x45, 0x0a, 0x1f, 0x6c, 0x69, 0x6d, 0x69, 0x74, - 0x5f, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x5f, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x5f, 0x63, - 0x70, 0x75, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x18, 0xe5, 0x01, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x1b, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x44, 0x65, 0x66, - 0x61, 0x75, 0x6c, 0x74, 0x43, 0x70, 0x75, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x4b, - 0x0a, 0x22, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x5f, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x5f, 0x64, 0x65, - 0x66, 0x61, 0x75, 0x6c, 0x74, 0x5f, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x5f, 0x72, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x18, 0xe6, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x1e, 0x6c, 0x69, 0x6d, - 0x69, 0x74, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x44, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x4d, 0x65, - 0x6d, 0x6f, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2d, 0x0a, 0x12, 0x6c, - 0x69, 0x6d, 0x69, 0x74, 0x5f, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x75, - 0x73, 0x18, 0xe7, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x52, - 0x61, 0x6e, 0x67, 0x65, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x2a, 0x0a, 0x10, 0x72, 0x65, - 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0xf0, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x56, - 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x20, 0x0a, 0x0b, 0x61, 0x70, 0x69, 0x5f, 0x76, 0x65, - 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0xf1, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x61, 0x70, - 0x69, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x1a, 0x42, 0x0a, 0x14, 0x53, 0x65, 0x72, 0x76, - 0x69, 0x63, 0x65, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x45, 0x6e, 0x74, 0x72, 0x79, + 0x69, 0x6e, 0x74, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x44, 0x65, 0x74, 0x61, 0x69, + 0x6c, 0x73, 0x12, 0x6a, 0x0a, 0x1d, 0x67, 0x61, 0x74, 0x65, 0x6b, 0x65, 0x65, 0x70, 0x65, 0x72, + 0x5f, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x72, 0x61, 0x69, 0x6e, 0x74, 0x5f, 0x64, 0x65, 0x74, 0x61, + 0x69, 0x6c, 0x73, 0x18, 0xf7, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x61, 0x70, 0x69, + 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x61, 0x74, 0x65, 0x6b, 0x65, 0x65, 0x70, 0x65, 0x72, 0x43, 0x6f, + 0x6e, 0x73, 0x74, 0x72, 0x61, 0x69, 0x6e, 0x74, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x48, + 0x00, 0x52, 0x1b, 0x67, 0x61, 0x74, 0x65, 0x6b, 0x65, 0x65, 0x70, 0x65, 0x72, 0x43, 0x6f, 0x6e, + 0x73, 0x74, 0x72, 0x61, 0x69, 0x6e, 0x74, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x5b, + 0x0a, 0x18, 0x61, 0x72, 0x67, 0x6f, 0x5f, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x5f, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x18, 0xf8, 0x01, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x1e, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x72, 0x67, 0x6f, 0x41, + 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, + 0x73, 0x48, 0x00, 0x52, 0x16, 0x61, 0x72, 0x67, 0x6f, 0x41, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x65, 0x0a, 0x1c, 0x61, + 0x72, 0x67, 0x6f, 0x5f, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, + 0x73, 0x65, 0x74, 0x5f, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x18, 0xf9, 0x01, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x72, 0x67, 0x6f, + 0x41, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x65, 0x74, 0x44, 0x65, + 0x74, 0x61, 0x69, 0x6c, 0x73, 0x48, 0x00, 0x52, 0x19, 0x61, 0x72, 0x67, 0x6f, 0x41, 0x70, 0x70, + 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x65, 0x74, 0x44, 0x65, 0x74, 0x61, 0x69, + 0x6c, 0x73, 0x12, 0x77, 0x0a, 0x22, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x5f, 0x72, 0x65, 0x73, + 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x6f, 0x6e, + 0x5f, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x18, 0xfa, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x27, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x52, + 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x44, 0x65, 0x66, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x6f, + 0x6e, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x48, 0x00, 0x52, 0x1f, 0x63, 0x75, 0x73, 0x74, + 0x6f, 0x6d, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x44, 0x65, 0x66, 0x69, 0x6e, 0x69, + 0x74, 0x69, 0x6f, 0x6e, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x62, 0x0a, 0x1b, 0x66, + 0x6c, 0x75, 0x78, 0x5f, 0x67, 0x69, 0x74, 0x5f, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, + 0x72, 0x79, 0x5f, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x18, 0xfb, 0x01, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x20, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x46, 0x6c, 0x75, 0x78, 0x47, + 0x69, 0x74, 0x52, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x44, 0x65, 0x74, 0x61, + 0x69, 0x6c, 0x73, 0x48, 0x00, 0x52, 0x18, 0x66, 0x6c, 0x75, 0x78, 0x47, 0x69, 0x74, 0x52, 0x65, + 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, + 0x62, 0x0a, 0x1b, 0x66, 0x6c, 0x75, 0x78, 0x5f, 0x6f, 0x63, 0x69, 0x5f, 0x72, 0x65, 0x70, 0x6f, + 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x5f, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x18, 0xfc, + 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x46, + 0x6c, 0x75, 0x78, 0x4f, 0x43, 0x49, 0x52, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, + 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x48, 0x00, 0x52, 0x18, 0x66, 0x6c, 0x75, 0x78, 0x4f, + 0x63, 0x69, 0x52, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x44, 0x65, 0x74, 0x61, + 0x69, 0x6c, 0x73, 0x12, 0x65, 0x0a, 0x1c, 0x66, 0x6c, 0x75, 0x78, 0x5f, 0x68, 0x65, 0x6c, 0x6d, + 0x5f, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x5f, 0x64, 0x65, 0x74, 0x61, + 0x69, 0x6c, 0x73, 0x18, 0xfd, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x61, 0x70, 0x69, + 0x2e, 0x76, 0x31, 0x2e, 0x46, 0x6c, 0x75, 0x78, 0x48, 0x65, 0x6c, 0x6d, 0x52, 0x65, 0x70, 0x6f, + 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x48, 0x00, 0x52, + 0x19, 0x66, 0x6c, 0x75, 0x78, 0x48, 0x65, 0x6c, 0x6d, 0x52, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, + 0x6f, 0x72, 0x79, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x56, 0x0a, 0x17, 0x66, 0x6c, + 0x75, 0x78, 0x5f, 0x68, 0x65, 0x6c, 0x6d, 0x5f, 0x63, 0x68, 0x61, 0x72, 0x74, 0x5f, 0x64, 0x65, + 0x74, 0x61, 0x69, 0x6c, 0x73, 0x18, 0xfe, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x61, + 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x46, 0x6c, 0x75, 0x78, 0x48, 0x65, 0x6c, 0x6d, 0x43, 0x68, + 0x61, 0x72, 0x74, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x48, 0x00, 0x52, 0x14, 0x66, 0x6c, + 0x75, 0x78, 0x48, 0x65, 0x6c, 0x6d, 0x43, 0x68, 0x61, 0x72, 0x74, 0x44, 0x65, 0x74, 0x61, 0x69, + 0x6c, 0x73, 0x12, 0x4c, 0x0a, 0x13, 0x66, 0x6c, 0x75, 0x78, 0x5f, 0x62, 0x75, 0x63, 0x6b, 0x65, + 0x74, 0x5f, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x18, 0xff, 0x01, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x19, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x46, 0x6c, 0x75, 0x78, 0x42, 0x75, + 0x63, 0x6b, 0x65, 0x74, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x48, 0x00, 0x52, 0x11, 0x66, + 0x6c, 0x75, 0x78, 0x42, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, + 0x12, 0x61, 0x0a, 0x1a, 0x66, 0x6c, 0x75, 0x78, 0x5f, 0x6b, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x69, + 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x18, 0x80, + 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x46, + 0x6c, 0x75, 0x78, 0x4b, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x48, 0x00, 0x52, 0x18, 0x66, 0x6c, 0x75, 0x78, 0x4b, + 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x44, 0x65, 0x74, 0x61, + 0x69, 0x6c, 0x73, 0x12, 0x5c, 0x0a, 0x19, 0x66, 0x6c, 0x75, 0x78, 0x5f, 0x68, 0x65, 0x6c, 0x6d, + 0x5f, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x5f, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, + 0x18, 0x81, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, + 0x2e, 0x46, 0x6c, 0x75, 0x78, 0x48, 0x65, 0x6c, 0x6d, 0x52, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, + 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x48, 0x00, 0x52, 0x16, 0x66, 0x6c, 0x75, 0x78, 0x48, + 0x65, 0x6c, 0x6d, 0x52, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, + 0x73, 0x12, 0x62, 0x0a, 0x1e, 0x66, 0x6c, 0x75, 0x78, 0x5f, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, + 0x61, 0x6c, 0x5f, 0x61, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, 0x74, 0x5f, 0x64, 0x65, 0x74, 0x61, + 0x69, 0x6c, 0x73, 0x18, 0x82, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x61, 0x70, 0x69, + 0x2e, 0x76, 0x31, 0x2e, 0x46, 0x6c, 0x75, 0x78, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x44, + 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x48, 0x00, 0x52, 0x1b, 0x66, 0x6c, 0x75, 0x78, 0x45, 0x78, + 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x41, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, 0x74, 0x44, 0x65, + 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x4b, 0x0a, 0x12, 0x66, 0x6c, 0x75, 0x78, 0x5f, 0x61, 0x6c, + 0x65, 0x72, 0x74, 0x5f, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x18, 0x83, 0x02, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x46, 0x6c, 0x75, 0x78, + 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x48, 0x00, + 0x52, 0x10, 0x66, 0x6c, 0x75, 0x78, 0x41, 0x6c, 0x65, 0x72, 0x74, 0x44, 0x65, 0x74, 0x61, 0x69, + 0x6c, 0x73, 0x12, 0x51, 0x0a, 0x15, 0x66, 0x6c, 0x75, 0x78, 0x5f, 0x70, 0x72, 0x6f, 0x76, 0x69, + 0x64, 0x65, 0x72, 0x5f, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x18, 0x84, 0x02, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x46, 0x6c, 0x75, 0x78, + 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x48, 0x00, + 0x52, 0x13, 0x66, 0x6c, 0x75, 0x78, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x44, 0x65, + 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x51, 0x0a, 0x15, 0x66, 0x6c, 0x75, 0x78, 0x5f, 0x72, 0x65, + 0x63, 0x65, 0x69, 0x76, 0x65, 0x72, 0x5f, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x18, 0x85, + 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x46, + 0x6c, 0x75, 0x78, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, + 0x73, 0x48, 0x00, 0x52, 0x13, 0x66, 0x6c, 0x75, 0x78, 0x52, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, + 0x72, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x51, 0x0a, 0x15, 0x66, 0x6c, 0x75, 0x78, + 0x5f, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x5f, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, + 0x73, 0x18, 0x86, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, + 0x31, 0x2e, 0x46, 0x6c, 0x75, 0x78, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x44, 0x65, 0x74, + 0x61, 0x69, 0x6c, 0x73, 0x48, 0x00, 0x52, 0x13, 0x66, 0x6c, 0x75, 0x78, 0x49, 0x6e, 0x73, 0x74, + 0x61, 0x6e, 0x63, 0x65, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x4d, 0x0a, 0x13, 0x66, + 0x6c, 0x75, 0x78, 0x5f, 0x72, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x5f, 0x64, 0x65, 0x74, 0x61, 0x69, + 0x6c, 0x73, 0x18, 0x87, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x61, 0x70, 0x69, 0x2e, + 0x76, 0x31, 0x2e, 0x46, 0x6c, 0x75, 0x78, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x44, 0x65, + 0x74, 0x61, 0x69, 0x6c, 0x73, 0x48, 0x00, 0x52, 0x11, 0x66, 0x6c, 0x75, 0x78, 0x52, 0x65, 0x70, + 0x6f, 0x72, 0x74, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x74, 0x0a, 0x28, 0x66, 0x6c, + 0x75, 0x78, 0x5f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x73, 0x65, 0x74, 0x5f, + 0x69, 0x6e, 0x70, 0x75, 0x74, 0x5f, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x5f, 0x64, + 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x18, 0x88, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, + 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x46, 0x6c, 0x75, 0x78, 0x47, 0x65, 0x6e, 0x65, 0x72, + 0x69, 0x63, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x48, 0x00, 0x52, 0x23, 0x66, 0x6c, 0x75, + 0x78, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x65, 0x74, 0x49, 0x6e, 0x70, 0x75, + 0x74, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, + 0x12, 0x58, 0x0a, 0x19, 0x66, 0x6c, 0x75, 0x78, 0x5f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, + 0x65, 0x5f, 0x73, 0x65, 0x74, 0x5f, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x18, 0x89, 0x02, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x46, 0x6c, + 0x75, 0x78, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, + 0x48, 0x00, 0x52, 0x16, 0x66, 0x6c, 0x75, 0x78, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, + 0x53, 0x65, 0x74, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x60, 0x0a, 0x1d, 0x66, 0x6c, + 0x75, 0x78, 0x5f, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x5f, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, + 0x6f, 0x72, 0x79, 0x5f, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x18, 0x8a, 0x02, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x46, 0x6c, 0x75, 0x78, + 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x48, 0x00, + 0x52, 0x1a, 0x66, 0x6c, 0x75, 0x78, 0x49, 0x6d, 0x61, 0x67, 0x65, 0x52, 0x65, 0x70, 0x6f, 0x73, + 0x69, 0x74, 0x6f, 0x72, 0x79, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x58, 0x0a, 0x19, + 0x66, 0x6c, 0x75, 0x78, 0x5f, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x5f, 0x70, 0x6f, 0x6c, 0x69, 0x63, + 0x79, 0x5f, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x18, 0x8b, 0x02, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x1a, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x46, 0x6c, 0x75, 0x78, 0x47, 0x65, + 0x6e, 0x65, 0x72, 0x69, 0x63, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x48, 0x00, 0x52, 0x16, + 0x66, 0x6c, 0x75, 0x78, 0x49, 0x6d, 0x61, 0x67, 0x65, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x44, + 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x6d, 0x0a, 0x24, 0x66, 0x6c, 0x75, 0x78, 0x5f, 0x69, + 0x6d, 0x61, 0x67, 0x65, 0x5f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x61, 0x75, 0x74, 0x6f, + 0x6d, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x18, 0x8c, + 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x46, + 0x6c, 0x75, 0x78, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, + 0x73, 0x48, 0x00, 0x52, 0x20, 0x66, 0x6c, 0x75, 0x78, 0x49, 0x6d, 0x61, 0x67, 0x65, 0x55, 0x70, + 0x64, 0x61, 0x74, 0x65, 0x41, 0x75, 0x74, 0x6f, 0x6d, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x44, 0x65, + 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x55, 0x0a, 0x16, 0x66, 0x6c, 0x61, 0x67, 0x67, 0x65, 0x72, + 0x5f, 0x63, 0x61, 0x6e, 0x61, 0x72, 0x79, 0x5f, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x18, + 0x8d, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, + 0x46, 0x6c, 0x61, 0x67, 0x67, 0x65, 0x72, 0x43, 0x61, 0x6e, 0x61, 0x72, 0x79, 0x44, 0x65, 0x74, + 0x61, 0x69, 0x6c, 0x73, 0x48, 0x00, 0x52, 0x14, 0x66, 0x6c, 0x61, 0x67, 0x67, 0x65, 0x72, 0x43, + 0x61, 0x6e, 0x61, 0x72, 0x79, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x6e, 0x0a, 0x1f, + 0x66, 0x6c, 0x61, 0x67, 0x67, 0x65, 0x72, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x5f, 0x74, + 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x5f, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x18, + 0x8e, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, + 0x46, 0x6c, 0x61, 0x67, 0x67, 0x65, 0x72, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x54, 0x65, 0x6d, + 0x70, 0x6c, 0x61, 0x74, 0x65, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x48, 0x00, 0x52, 0x1c, + 0x66, 0x6c, 0x61, 0x67, 0x67, 0x65, 0x72, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x54, 0x65, 0x6d, + 0x70, 0x6c, 0x61, 0x74, 0x65, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x6b, 0x0a, 0x1e, + 0x66, 0x6c, 0x61, 0x67, 0x67, 0x65, 0x72, 0x5f, 0x61, 0x6c, 0x65, 0x72, 0x74, 0x5f, 0x70, 0x72, + 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x5f, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x18, 0x8f, + 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x46, + 0x6c, 0x61, 0x67, 0x67, 0x65, 0x72, 0x41, 0x6c, 0x65, 0x72, 0x74, 0x50, 0x72, 0x6f, 0x76, 0x69, + 0x64, 0x65, 0x72, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x48, 0x00, 0x52, 0x1b, 0x66, 0x6c, + 0x61, 0x67, 0x67, 0x65, 0x72, 0x41, 0x6c, 0x65, 0x72, 0x74, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, + 0x65, 0x72, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x70, 0x68, 0x61, + 0x73, 0x65, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x70, 0x68, 0x61, 0x73, 0x65, 0x12, + 0x15, 0x0a, 0x06, 0x70, 0x6f, 0x64, 0x5f, 0x69, 0x70, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x05, 0x70, 0x6f, 0x64, 0x49, 0x70, 0x12, 0x17, 0x0a, 0x07, 0x68, 0x6f, 0x73, 0x74, 0x5f, 0x69, + 0x70, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x68, 0x6f, 0x73, 0x74, 0x49, 0x70, 0x12, + 0x1b, 0x0a, 0x09, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x0d, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x08, 0x6e, 0x6f, 0x64, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x14, 0x0a, 0x05, + 0x72, 0x65, 0x61, 0x64, 0x79, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, 0x72, 0x65, 0x61, + 0x64, 0x79, 0x12, 0x1b, 0x0a, 0x09, 0x71, 0x6f, 0x73, 0x5f, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x18, + 0x0f, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x71, 0x6f, 0x73, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x12, + 0x27, 0x0a, 0x0f, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x63, 0x6f, 0x75, + 0x6e, 0x74, 0x18, 0x10, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0e, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, + 0x6e, 0x65, 0x72, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x23, 0x0a, 0x0d, 0x72, 0x65, 0x73, 0x74, + 0x61, 0x72, 0x74, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x11, 0x20, 0x01, 0x28, 0x05, 0x52, + 0x0c, 0x72, 0x65, 0x73, 0x74, 0x61, 0x72, 0x74, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x36, 0x0a, + 0x17, 0x63, 0x70, 0x75, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x73, 0x5f, 0x6d, 0x69, + 0x6c, 0x6c, 0x69, 0x63, 0x6f, 0x72, 0x65, 0x73, 0x18, 0x14, 0x20, 0x01, 0x28, 0x03, 0x52, 0x15, + 0x63, 0x70, 0x75, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x73, 0x4d, 0x69, 0x6c, 0x6c, 0x69, + 0x63, 0x6f, 0x72, 0x65, 0x73, 0x12, 0x32, 0x0a, 0x15, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x5f, + 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x73, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x18, 0x15, + 0x20, 0x01, 0x28, 0x03, 0x52, 0x13, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x73, 0x42, 0x79, 0x74, 0x65, 0x73, 0x12, 0x32, 0x0a, 0x15, 0x63, 0x70, 0x75, + 0x5f, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x73, 0x5f, 0x6d, 0x69, 0x6c, 0x6c, 0x69, 0x63, 0x6f, 0x72, + 0x65, 0x73, 0x18, 0x16, 0x20, 0x01, 0x28, 0x03, 0x52, 0x13, 0x63, 0x70, 0x75, 0x4c, 0x69, 0x6d, + 0x69, 0x74, 0x73, 0x4d, 0x69, 0x6c, 0x6c, 0x69, 0x63, 0x6f, 0x72, 0x65, 0x73, 0x12, 0x2e, 0x0a, + 0x13, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x5f, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x73, 0x5f, 0x62, + 0x79, 0x74, 0x65, 0x73, 0x18, 0x17, 0x20, 0x01, 0x28, 0x03, 0x52, 0x11, 0x6d, 0x65, 0x6d, 0x6f, + 0x72, 0x79, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x73, 0x42, 0x79, 0x74, 0x65, 0x73, 0x12, 0x23, 0x0a, + 0x0d, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x5f, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x18, 0x1e, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x61, 0x73, + 0x6f, 0x6e, 0x12, 0x23, 0x0a, 0x0d, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x64, + 0x5f, 0x62, 0x79, 0x18, 0x1f, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x63, 0x6f, 0x6e, 0x74, 0x72, + 0x6f, 0x6c, 0x6c, 0x65, 0x64, 0x42, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x67, 0x65, 0x18, 0x20, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x67, 0x65, 0x12, 0x29, 0x0a, 0x10, 0x72, 0x65, 0x70, + 0x6c, 0x69, 0x63, 0x61, 0x73, 0x5f, 0x64, 0x65, 0x73, 0x69, 0x72, 0x65, 0x64, 0x18, 0x28, 0x20, + 0x01, 0x28, 0x05, 0x52, 0x0f, 0x72, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x73, 0x44, 0x65, 0x73, + 0x69, 0x72, 0x65, 0x64, 0x12, 0x25, 0x0a, 0x0e, 0x72, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x73, + 0x5f, 0x72, 0x65, 0x61, 0x64, 0x79, 0x18, 0x29, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0d, 0x72, 0x65, + 0x70, 0x6c, 0x69, 0x63, 0x61, 0x73, 0x52, 0x65, 0x61, 0x64, 0x79, 0x12, 0x2d, 0x0a, 0x12, 0x72, + 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x73, 0x5f, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, + 0x65, 0x18, 0x2a, 0x20, 0x01, 0x28, 0x05, 0x52, 0x11, 0x72, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, + 0x73, 0x41, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x12, 0x29, 0x0a, 0x10, 0x72, 0x65, + 0x70, 0x6c, 0x69, 0x63, 0x61, 0x73, 0x5f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x18, 0x2b, + 0x20, 0x01, 0x28, 0x05, 0x52, 0x0f, 0x72, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x73, 0x55, 0x70, + 0x64, 0x61, 0x74, 0x65, 0x64, 0x12, 0x23, 0x0a, 0x0d, 0x73, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, + 0x79, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x2c, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x73, 0x74, + 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x54, 0x79, 0x70, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x61, 0x76, + 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x18, 0x2d, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x61, + 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x70, 0x72, 0x6f, 0x67, + 0x72, 0x65, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x18, 0x2e, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, 0x70, + 0x72, 0x6f, 0x67, 0x72, 0x65, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x12, 0x2b, 0x0a, 0x11, 0x64, 0x65, + 0x70, 0x6c, 0x6f, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, + 0x2f, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x64, 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x6d, 0x65, 0x6e, + 0x74, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x69, 0x6d, 0x61, 0x67, 0x65, + 0x73, 0x18, 0x30, 0x20, 0x03, 0x28, 0x09, 0x52, 0x06, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x73, 0x12, + 0x21, 0x0a, 0x0c, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, + 0x3c, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x4e, 0x61, + 0x6d, 0x65, 0x12, 0x32, 0x0a, 0x15, 0x70, 0x6f, 0x64, 0x5f, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, + 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x18, 0x3d, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x13, 0x70, 0x6f, 0x64, 0x4d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, + 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x12, 0x30, 0x0a, 0x14, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, + 0x5f, 0x73, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x3e, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x12, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x53, 0x74, 0x72, 0x61, + 0x74, 0x65, 0x67, 0x79, 0x54, 0x79, 0x70, 0x65, 0x12, 0x3d, 0x0a, 0x1b, 0x76, 0x6f, 0x6c, 0x75, + 0x6d, 0x65, 0x5f, 0x63, 0x6c, 0x61, 0x69, 0x6d, 0x5f, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, + 0x65, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x3f, 0x20, 0x01, 0x28, 0x05, 0x52, 0x18, 0x76, + 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x43, 0x6c, 0x61, 0x69, 0x6d, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, + 0x74, 0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x30, 0x0a, 0x14, 0x70, 0x76, 0x63, 0x5f, 0x72, + 0x65, 0x74, 0x65, 0x6e, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x18, + 0x40, 0x20, 0x01, 0x28, 0x09, 0x52, 0x12, 0x70, 0x76, 0x63, 0x52, 0x65, 0x74, 0x65, 0x6e, 0x74, + 0x69, 0x6f, 0x6e, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x12, 0x29, 0x0a, 0x10, 0x63, 0x75, 0x72, + 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x72, 0x65, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x41, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x0f, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x76, 0x69, + 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x27, 0x0a, 0x0f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x72, + 0x65, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x42, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x75, + 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x23, 0x0a, + 0x0d, 0x6e, 0x6f, 0x64, 0x65, 0x73, 0x5f, 0x64, 0x65, 0x73, 0x69, 0x72, 0x65, 0x64, 0x18, 0x46, + 0x20, 0x01, 0x28, 0x05, 0x52, 0x0c, 0x6e, 0x6f, 0x64, 0x65, 0x73, 0x44, 0x65, 0x73, 0x69, 0x72, + 0x65, 0x64, 0x12, 0x1f, 0x0a, 0x0b, 0x6e, 0x6f, 0x64, 0x65, 0x73, 0x5f, 0x72, 0x65, 0x61, 0x64, + 0x79, 0x18, 0x47, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0a, 0x6e, 0x6f, 0x64, 0x65, 0x73, 0x52, 0x65, + 0x61, 0x64, 0x79, 0x12, 0x27, 0x0a, 0x0f, 0x6e, 0x6f, 0x64, 0x65, 0x73, 0x5f, 0x61, 0x76, 0x61, + 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x18, 0x48, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0e, 0x6e, 0x6f, + 0x64, 0x65, 0x73, 0x41, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x12, 0x23, 0x0a, 0x0d, + 0x6e, 0x6f, 0x64, 0x65, 0x73, 0x5f, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x18, 0x49, 0x20, + 0x01, 0x28, 0x05, 0x52, 0x0c, 0x6e, 0x6f, 0x64, 0x65, 0x73, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, + 0x74, 0x12, 0x23, 0x0a, 0x0d, 0x6e, 0x6f, 0x64, 0x65, 0x73, 0x5f, 0x75, 0x70, 0x64, 0x61, 0x74, + 0x65, 0x64, 0x18, 0x4a, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0c, 0x6e, 0x6f, 0x64, 0x65, 0x73, 0x55, + 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x12, 0x2d, 0x0a, 0x12, 0x6e, 0x6f, 0x64, 0x65, 0x73, 0x5f, + 0x6d, 0x69, 0x73, 0x73, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x64, 0x18, 0x4b, 0x20, 0x01, + 0x28, 0x05, 0x52, 0x11, 0x6e, 0x6f, 0x64, 0x65, 0x73, 0x4d, 0x69, 0x73, 0x73, 0x63, 0x68, 0x65, + 0x64, 0x75, 0x6c, 0x65, 0x64, 0x12, 0x27, 0x0a, 0x0f, 0x6d, 0x61, 0x78, 0x5f, 0x75, 0x6e, 0x61, + 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x18, 0x4c, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, + 0x6d, 0x61, 0x78, 0x55, 0x6e, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x12, 0x25, + 0x0a, 0x0e, 0x70, 0x72, 0x69, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x5f, 0x63, 0x6c, 0x61, 0x73, 0x73, + 0x18, 0x4d, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x70, 0x72, 0x69, 0x6f, 0x72, 0x69, 0x74, 0x79, + 0x43, 0x6c, 0x61, 0x73, 0x73, 0x12, 0x36, 0x0a, 0x17, 0x6a, 0x6f, 0x62, 0x5f, 0x63, 0x6f, 0x6d, + 0x70, 0x6c, 0x65, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x5f, 0x64, 0x65, 0x73, 0x69, 0x72, 0x65, 0x64, + 0x18, 0x50, 0x20, 0x01, 0x28, 0x05, 0x52, 0x15, 0x6a, 0x6f, 0x62, 0x43, 0x6f, 0x6d, 0x70, 0x6c, + 0x65, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x44, 0x65, 0x73, 0x69, 0x72, 0x65, 0x64, 0x12, 0x3a, 0x0a, + 0x19, 0x6a, 0x6f, 0x62, 0x5f, 0x63, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x69, 0x6f, 0x6e, 0x73, + 0x5f, 0x73, 0x75, 0x63, 0x63, 0x65, 0x65, 0x64, 0x65, 0x64, 0x18, 0x51, 0x20, 0x01, 0x28, 0x05, + 0x52, 0x17, 0x6a, 0x6f, 0x62, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x69, 0x6f, 0x6e, 0x73, + 0x53, 0x75, 0x63, 0x63, 0x65, 0x65, 0x64, 0x65, 0x64, 0x12, 0x27, 0x0a, 0x0f, 0x6a, 0x6f, 0x62, + 0x5f, 0x70, 0x61, 0x72, 0x61, 0x6c, 0x6c, 0x65, 0x6c, 0x69, 0x73, 0x6d, 0x18, 0x52, 0x20, 0x01, + 0x28, 0x05, 0x52, 0x0e, 0x6a, 0x6f, 0x62, 0x50, 0x61, 0x72, 0x61, 0x6c, 0x6c, 0x65, 0x6c, 0x69, + 0x73, 0x6d, 0x12, 0x2a, 0x0a, 0x11, 0x6a, 0x6f, 0x62, 0x5f, 0x62, 0x61, 0x63, 0x6b, 0x6f, 0x66, + 0x66, 0x5f, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x18, 0x53, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0f, 0x6a, + 0x6f, 0x62, 0x42, 0x61, 0x63, 0x6b, 0x6f, 0x66, 0x66, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x12, 0x2e, + 0x0a, 0x13, 0x6a, 0x6f, 0x62, 0x5f, 0x63, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x69, 0x6f, 0x6e, + 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x18, 0x54, 0x20, 0x01, 0x28, 0x09, 0x52, 0x11, 0x6a, 0x6f, 0x62, + 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x69, 0x6f, 0x6e, 0x4d, 0x6f, 0x64, 0x65, 0x12, 0x23, + 0x0a, 0x0d, 0x6a, 0x6f, 0x62, 0x5f, 0x73, 0x75, 0x73, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x64, 0x18, + 0x55, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x6a, 0x6f, 0x62, 0x53, 0x75, 0x73, 0x70, 0x65, 0x6e, + 0x64, 0x65, 0x64, 0x12, 0x21, 0x0a, 0x0c, 0x6a, 0x6f, 0x62, 0x5f, 0x64, 0x75, 0x72, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x18, 0x56, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x6a, 0x6f, 0x62, 0x44, 0x75, + 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1d, 0x0a, 0x0a, 0x6a, 0x6f, 0x62, 0x5f, 0x73, 0x74, + 0x61, 0x74, 0x75, 0x73, 0x18, 0x57, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6a, 0x6f, 0x62, 0x53, + 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x26, 0x0a, 0x0f, 0x6a, 0x6f, 0x62, 0x5f, 0x61, 0x63, 0x74, + 0x69, 0x76, 0x65, 0x5f, 0x70, 0x6f, 0x64, 0x73, 0x18, 0x58, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0d, + 0x6a, 0x6f, 0x62, 0x41, 0x63, 0x74, 0x69, 0x76, 0x65, 0x50, 0x6f, 0x64, 0x73, 0x12, 0x26, 0x0a, + 0x0f, 0x6a, 0x6f, 0x62, 0x5f, 0x66, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x5f, 0x70, 0x6f, 0x64, 0x73, + 0x18, 0x59, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0d, 0x6a, 0x6f, 0x62, 0x46, 0x61, 0x69, 0x6c, 0x65, + 0x64, 0x50, 0x6f, 0x64, 0x73, 0x12, 0x23, 0x0a, 0x0d, 0x63, 0x72, 0x6f, 0x6e, 0x5f, 0x73, 0x63, + 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x18, 0x5a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x63, 0x72, + 0x6f, 0x6e, 0x53, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x12, 0x25, 0x0a, 0x0e, 0x63, 0x72, + 0x6f, 0x6e, 0x5f, 0x73, 0x75, 0x73, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x64, 0x18, 0x5b, 0x20, 0x01, + 0x28, 0x08, 0x52, 0x0d, 0x63, 0x72, 0x6f, 0x6e, 0x53, 0x75, 0x73, 0x70, 0x65, 0x6e, 0x64, 0x65, + 0x64, 0x12, 0x28, 0x0a, 0x10, 0x63, 0x72, 0x6f, 0x6e, 0x5f, 0x61, 0x63, 0x74, 0x69, 0x76, 0x65, + 0x5f, 0x6a, 0x6f, 0x62, 0x73, 0x18, 0x5c, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0e, 0x63, 0x72, 0x6f, + 0x6e, 0x41, 0x63, 0x74, 0x69, 0x76, 0x65, 0x4a, 0x6f, 0x62, 0x73, 0x12, 0x35, 0x0a, 0x17, 0x63, + 0x72, 0x6f, 0x6e, 0x5f, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x73, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, + 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x5d, 0x20, 0x01, 0x28, 0x09, 0x52, 0x14, 0x63, 0x72, + 0x6f, 0x6e, 0x4c, 0x61, 0x73, 0x74, 0x53, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x54, 0x69, + 0x6d, 0x65, 0x12, 0x35, 0x0a, 0x17, 0x63, 0x72, 0x6f, 0x6e, 0x5f, 0x6e, 0x65, 0x78, 0x74, 0x5f, + 0x73, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x5e, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x14, 0x63, 0x72, 0x6f, 0x6e, 0x4e, 0x65, 0x78, 0x74, 0x53, 0x63, 0x68, + 0x65, 0x64, 0x75, 0x6c, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x36, 0x0a, 0x17, 0x63, 0x72, 0x6f, + 0x6e, 0x5f, 0x63, 0x6f, 0x6e, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x5f, 0x70, 0x6f, + 0x6c, 0x69, 0x63, 0x79, 0x18, 0x5f, 0x20, 0x01, 0x28, 0x09, 0x52, 0x15, 0x63, 0x72, 0x6f, 0x6e, + 0x43, 0x6f, 0x6e, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x50, 0x6f, 0x6c, 0x69, 0x63, + 0x79, 0x12, 0x3b, 0x0a, 0x1a, 0x63, 0x72, 0x6f, 0x6e, 0x5f, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, + 0x73, 0x66, 0x75, 0x6c, 0x5f, 0x6a, 0x6f, 0x62, 0x73, 0x5f, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x18, + 0x60, 0x20, 0x01, 0x28, 0x05, 0x52, 0x17, 0x63, 0x72, 0x6f, 0x6e, 0x53, 0x75, 0x63, 0x63, 0x65, + 0x73, 0x73, 0x66, 0x75, 0x6c, 0x4a, 0x6f, 0x62, 0x73, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x12, 0x33, + 0x0a, 0x16, 0x63, 0x72, 0x6f, 0x6e, 0x5f, 0x66, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x5f, 0x6a, 0x6f, + 0x62, 0x73, 0x5f, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x18, 0x61, 0x20, 0x01, 0x28, 0x05, 0x52, 0x13, + 0x63, 0x72, 0x6f, 0x6e, 0x46, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x4a, 0x6f, 0x62, 0x73, 0x4c, 0x69, + 0x6d, 0x69, 0x74, 0x12, 0x43, 0x0a, 0x1e, 0x63, 0x72, 0x6f, 0x6e, 0x5f, 0x73, 0x74, 0x61, 0x72, + 0x74, 0x69, 0x6e, 0x67, 0x5f, 0x64, 0x65, 0x61, 0x64, 0x6c, 0x69, 0x6e, 0x65, 0x5f, 0x73, 0x65, + 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x18, 0x62, 0x20, 0x01, 0x28, 0x05, 0x52, 0x1b, 0x63, 0x72, 0x6f, + 0x6e, 0x53, 0x74, 0x61, 0x72, 0x74, 0x69, 0x6e, 0x67, 0x44, 0x65, 0x61, 0x64, 0x6c, 0x69, 0x6e, + 0x65, 0x53, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x12, 0x21, 0x0a, 0x0c, 0x73, 0x65, 0x72, 0x76, + 0x69, 0x63, 0x65, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x64, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, + 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x54, 0x79, 0x70, 0x65, 0x12, 0x2c, 0x0a, 0x12, 0x73, + 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x69, + 0x70, 0x18, 0x65, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, + 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, 0x70, 0x12, 0x30, 0x0a, 0x14, 0x73, 0x65, 0x72, + 0x76, 0x69, 0x63, 0x65, 0x5f, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x5f, 0x69, 0x70, + 0x73, 0x18, 0x66, 0x20, 0x03, 0x28, 0x09, 0x52, 0x12, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, + 0x45, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x49, 0x70, 0x73, 0x12, 0x38, 0x0a, 0x0d, 0x73, + 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x70, 0x6f, 0x72, 0x74, 0x73, 0x18, 0x67, 0x20, 0x03, + 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x72, 0x76, + 0x69, 0x63, 0x65, 0x50, 0x6f, 0x72, 0x74, 0x52, 0x0c, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, + 0x50, 0x6f, 0x72, 0x74, 0x73, 0x12, 0x57, 0x0a, 0x10, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, + 0x5f, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x18, 0x68, 0x20, 0x03, 0x28, 0x0b, 0x32, + 0x2c, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, + 0x65, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, + 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0f, 0x73, + 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x12, 0x38, + 0x0a, 0x18, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, + 0x6e, 0x5f, 0x61, 0x66, 0x66, 0x69, 0x6e, 0x69, 0x74, 0x79, 0x18, 0x69, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x16, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, + 0x41, 0x66, 0x66, 0x69, 0x6e, 0x69, 0x74, 0x79, 0x12, 0x45, 0x0a, 0x1f, 0x73, 0x65, 0x72, 0x76, + 0x69, 0x63, 0x65, 0x5f, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x5f, 0x74, 0x72, 0x61, + 0x66, 0x66, 0x69, 0x63, 0x5f, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x18, 0x6a, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x1c, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x45, 0x78, 0x74, 0x65, 0x72, 0x6e, + 0x61, 0x6c, 0x54, 0x72, 0x61, 0x66, 0x66, 0x69, 0x63, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x12, + 0x45, 0x0a, 0x1f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x69, 0x6e, 0x74, 0x65, 0x72, + 0x6e, 0x61, 0x6c, 0x5f, 0x74, 0x72, 0x61, 0x66, 0x66, 0x69, 0x63, 0x5f, 0x70, 0x6f, 0x6c, 0x69, + 0x63, 0x79, 0x18, 0x6b, 0x20, 0x01, 0x28, 0x09, 0x52, 0x1c, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, + 0x65, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x54, 0x72, 0x61, 0x66, 0x66, 0x69, 0x63, + 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x12, 0x4c, 0x0a, 0x23, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, + 0x65, 0x5f, 0x6c, 0x6f, 0x61, 0x64, 0x5f, 0x62, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x72, 0x5f, + 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x18, 0x6c, 0x20, + 0x03, 0x28, 0x09, 0x52, 0x1f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x4c, 0x6f, 0x61, 0x64, + 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x72, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x61, + 0x6e, 0x67, 0x65, 0x73, 0x12, 0x3d, 0x0a, 0x1b, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x5f, + 0x6c, 0x6f, 0x61, 0x64, 0x5f, 0x62, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x72, 0x5f, 0x63, 0x6c, + 0x61, 0x73, 0x73, 0x18, 0x6d, 0x20, 0x01, 0x28, 0x09, 0x52, 0x18, 0x73, 0x65, 0x72, 0x76, 0x69, + 0x63, 0x65, 0x4c, 0x6f, 0x61, 0x64, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x72, 0x43, 0x6c, + 0x61, 0x73, 0x73, 0x12, 0x2e, 0x0a, 0x13, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x69, + 0x70, 0x5f, 0x66, 0x61, 0x6d, 0x69, 0x6c, 0x69, 0x65, 0x73, 0x18, 0x6e, 0x20, 0x03, 0x28, 0x09, + 0x52, 0x11, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x49, 0x70, 0x46, 0x61, 0x6d, 0x69, 0x6c, + 0x69, 0x65, 0x73, 0x12, 0x37, 0x0a, 0x18, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x69, + 0x70, 0x5f, 0x66, 0x61, 0x6d, 0x69, 0x6c, 0x79, 0x5f, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x18, + 0x6f, 0x20, 0x01, 0x28, 0x09, 0x52, 0x15, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x49, 0x70, + 0x46, 0x61, 0x6d, 0x69, 0x6c, 0x79, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x12, 0x2c, 0x0a, 0x12, + 0x69, 0x6e, 0x67, 0x72, 0x65, 0x73, 0x73, 0x5f, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x5f, 0x6e, 0x61, + 0x6d, 0x65, 0x18, 0x78, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x69, 0x6e, 0x67, 0x72, 0x65, 0x73, + 0x73, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x69, 0x6e, + 0x67, 0x72, 0x65, 0x73, 0x73, 0x5f, 0x68, 0x6f, 0x73, 0x74, 0x73, 0x18, 0x79, 0x20, 0x03, 0x28, + 0x09, 0x52, 0x0c, 0x69, 0x6e, 0x67, 0x72, 0x65, 0x73, 0x73, 0x48, 0x6f, 0x73, 0x74, 0x73, 0x12, + 0x23, 0x0a, 0x0d, 0x69, 0x6e, 0x67, 0x72, 0x65, 0x73, 0x73, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x73, + 0x18, 0x7a, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0c, 0x69, 0x6e, 0x67, 0x72, 0x65, 0x73, 0x73, 0x50, + 0x61, 0x74, 0x68, 0x73, 0x12, 0x29, 0x0a, 0x10, 0x69, 0x6e, 0x67, 0x72, 0x65, 0x73, 0x73, 0x5f, + 0x62, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x73, 0x18, 0x7b, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0f, + 0x69, 0x6e, 0x67, 0x72, 0x65, 0x73, 0x73, 0x42, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x73, 0x12, + 0x2a, 0x0a, 0x11, 0x69, 0x6e, 0x67, 0x72, 0x65, 0x73, 0x73, 0x5f, 0x74, 0x6c, 0x73, 0x5f, 0x68, + 0x6f, 0x73, 0x74, 0x73, 0x18, 0x7c, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0f, 0x69, 0x6e, 0x67, 0x72, + 0x65, 0x73, 0x73, 0x54, 0x6c, 0x73, 0x48, 0x6f, 0x73, 0x74, 0x73, 0x12, 0x2e, 0x0a, 0x13, 0x69, + 0x6e, 0x67, 0x72, 0x65, 0x73, 0x73, 0x5f, 0x74, 0x6c, 0x73, 0x5f, 0x73, 0x65, 0x63, 0x72, 0x65, + 0x74, 0x73, 0x18, 0x7d, 0x20, 0x03, 0x28, 0x09, 0x52, 0x11, 0x69, 0x6e, 0x67, 0x72, 0x65, 0x73, + 0x73, 0x54, 0x6c, 0x73, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x73, 0x12, 0x39, 0x0a, 0x19, 0x69, + 0x6e, 0x67, 0x72, 0x65, 0x73, 0x73, 0x5f, 0x6c, 0x6f, 0x61, 0x64, 0x5f, 0x62, 0x61, 0x6c, 0x61, + 0x6e, 0x63, 0x65, 0x72, 0x5f, 0x69, 0x70, 0x73, 0x18, 0x7e, 0x20, 0x03, 0x28, 0x09, 0x52, 0x16, + 0x69, 0x6e, 0x67, 0x72, 0x65, 0x73, 0x73, 0x4c, 0x6f, 0x61, 0x64, 0x42, 0x61, 0x6c, 0x61, 0x6e, + 0x63, 0x65, 0x72, 0x49, 0x70, 0x73, 0x12, 0x2e, 0x0a, 0x13, 0x69, 0x6e, 0x67, 0x72, 0x65, 0x73, + 0x73, 0x5f, 0x72, 0x75, 0x6c, 0x65, 0x73, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x7f, 0x20, + 0x01, 0x28, 0x05, 0x52, 0x11, 0x69, 0x6e, 0x67, 0x72, 0x65, 0x73, 0x73, 0x52, 0x75, 0x6c, 0x65, + 0x73, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x2b, 0x0a, 0x11, 0x69, 0x6e, 0x67, 0x72, 0x65, 0x73, + 0x73, 0x5f, 0x74, 0x6c, 0x73, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x80, 0x01, 0x20, 0x01, + 0x28, 0x05, 0x52, 0x0f, 0x69, 0x6e, 0x67, 0x72, 0x65, 0x73, 0x73, 0x54, 0x6c, 0x73, 0x43, 0x6f, + 0x75, 0x6e, 0x74, 0x12, 0x3e, 0x0a, 0x1b, 0x69, 0x6e, 0x67, 0x72, 0x65, 0x73, 0x73, 0x5f, 0x68, + 0x61, 0x73, 0x5f, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x5f, 0x62, 0x61, 0x63, 0x6b, 0x65, + 0x6e, 0x64, 0x18, 0x81, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x18, 0x69, 0x6e, 0x67, 0x72, 0x65, + 0x73, 0x73, 0x48, 0x61, 0x73, 0x44, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x42, 0x61, 0x63, 0x6b, + 0x65, 0x6e, 0x64, 0x12, 0x34, 0x0a, 0x16, 0x70, 0x76, 0x63, 0x5f, 0x73, 0x74, 0x6f, 0x72, 0x61, + 0x67, 0x65, 0x5f, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x82, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x13, 0x70, 0x76, 0x63, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, + 0x43, 0x6c, 0x61, 0x73, 0x73, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x35, 0x0a, 0x16, 0x70, 0x76, 0x63, + 0x5f, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x65, 0x64, 0x5f, 0x63, 0x61, 0x70, 0x61, 0x63, + 0x69, 0x74, 0x79, 0x18, 0x83, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x14, 0x70, 0x76, 0x63, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x65, 0x64, 0x43, 0x61, 0x70, 0x61, 0x63, 0x69, 0x74, 0x79, + 0x12, 0x2f, 0x0a, 0x13, 0x70, 0x76, 0x63, 0x5f, 0x61, 0x63, 0x74, 0x75, 0x61, 0x6c, 0x5f, 0x63, + 0x61, 0x70, 0x61, 0x63, 0x69, 0x74, 0x79, 0x18, 0x84, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x11, + 0x70, 0x76, 0x63, 0x41, 0x63, 0x74, 0x75, 0x61, 0x6c, 0x43, 0x61, 0x70, 0x61, 0x63, 0x69, 0x74, + 0x79, 0x12, 0x29, 0x0a, 0x10, 0x70, 0x76, 0x63, 0x5f, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x5f, + 0x6d, 0x6f, 0x64, 0x65, 0x73, 0x18, 0x85, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0e, 0x70, 0x76, + 0x63, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x4d, 0x6f, 0x64, 0x65, 0x73, 0x12, 0x27, 0x0a, 0x0f, + 0x70, 0x76, 0x63, 0x5f, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x18, + 0x86, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x70, 0x76, 0x63, 0x56, 0x6f, 0x6c, 0x75, 0x6d, + 0x65, 0x4d, 0x6f, 0x64, 0x65, 0x12, 0x27, 0x0a, 0x0f, 0x70, 0x76, 0x63, 0x5f, 0x76, 0x6f, 0x6c, + 0x75, 0x6d, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x87, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x0d, 0x70, 0x76, 0x63, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x1e, + 0x0a, 0x0a, 0x70, 0x76, 0x63, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x88, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x09, 0x70, 0x76, 0x63, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x21, + 0x0a, 0x0c, 0x70, 0x76, 0x63, 0x5f, 0x69, 0x73, 0x5f, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x18, 0x89, + 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x70, 0x76, 0x63, 0x49, 0x73, 0x42, 0x6f, 0x75, 0x6e, + 0x64, 0x12, 0x3a, 0x0a, 0x19, 0x70, 0x76, 0x63, 0x5f, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, + 0x72, 0x5f, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x5f, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x18, 0x8a, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x16, 0x70, 0x76, 0x63, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, + 0x6f, 0x72, 0x4d, 0x61, 0x74, 0x63, 0x68, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x12, 0x35, 0x0a, + 0x16, 0x70, 0x76, 0x63, 0x5f, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x5f, 0x74, 0x6f, 0x70, + 0x6f, 0x6c, 0x6f, 0x67, 0x69, 0x65, 0x73, 0x18, 0x8b, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x14, + 0x70, 0x76, 0x63, 0x41, 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x54, 0x6f, 0x70, 0x6f, 0x6c, 0x6f, + 0x67, 0x69, 0x65, 0x73, 0x12, 0x32, 0x0a, 0x15, 0x70, 0x76, 0x5f, 0x73, 0x74, 0x6f, 0x72, 0x61, + 0x67, 0x65, 0x5f, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x96, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x12, 0x70, 0x76, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x43, + 0x6c, 0x61, 0x73, 0x73, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x70, 0x76, 0x5f, 0x63, + 0x61, 0x70, 0x61, 0x63, 0x69, 0x74, 0x79, 0x18, 0x97, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, + 0x70, 0x76, 0x43, 0x61, 0x70, 0x61, 0x63, 0x69, 0x74, 0x79, 0x12, 0x27, 0x0a, 0x0f, 0x70, 0x76, + 0x5f, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x73, 0x18, 0x98, 0x01, + 0x20, 0x03, 0x28, 0x09, 0x52, 0x0d, 0x70, 0x76, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x4d, 0x6f, + 0x64, 0x65, 0x73, 0x12, 0x2b, 0x0a, 0x11, 0x70, 0x76, 0x5f, 0x72, 0x65, 0x63, 0x6c, 0x61, 0x69, + 0x6d, 0x5f, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x18, 0x99, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x0f, 0x70, 0x76, 0x52, 0x65, 0x63, 0x6c, 0x61, 0x69, 0x6d, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, + 0x12, 0x2a, 0x0a, 0x11, 0x70, 0x76, 0x5f, 0x63, 0x6c, 0x61, 0x69, 0x6d, 0x5f, 0x72, 0x65, 0x66, + 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x9a, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x70, 0x76, + 0x43, 0x6c, 0x61, 0x69, 0x6d, 0x52, 0x65, 0x66, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x34, 0x0a, 0x16, + 0x70, 0x76, 0x5f, 0x63, 0x6c, 0x61, 0x69, 0x6d, 0x5f, 0x72, 0x65, 0x66, 0x5f, 0x6e, 0x61, 0x6d, + 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x9b, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x13, 0x70, + 0x76, 0x43, 0x6c, 0x61, 0x69, 0x6d, 0x52, 0x65, 0x66, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, + 0x63, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x70, 0x76, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, + 0x9c, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x76, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, + 0x12, 0x1f, 0x0a, 0x0b, 0x70, 0x76, 0x5f, 0x69, 0x73, 0x5f, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x18, + 0x9d, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x70, 0x76, 0x49, 0x73, 0x42, 0x6f, 0x75, 0x6e, + 0x64, 0x12, 0x25, 0x0a, 0x0e, 0x70, 0x76, 0x5f, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x6d, + 0x6f, 0x64, 0x65, 0x18, 0x9e, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x70, 0x76, 0x56, 0x6f, + 0x6c, 0x75, 0x6d, 0x65, 0x4d, 0x6f, 0x64, 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x70, 0x76, 0x5f, 0x63, + 0x73, 0x69, 0x5f, 0x64, 0x72, 0x69, 0x76, 0x65, 0x72, 0x18, 0x9f, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x0b, 0x70, 0x76, 0x43, 0x73, 0x69, 0x44, 0x72, 0x69, 0x76, 0x65, 0x72, 0x12, 0x32, 0x0a, + 0x15, 0x70, 0x76, 0x5f, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x6f, 0x75, 0x72, 0x63, + 0x65, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0xa0, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x12, 0x70, + 0x76, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x54, 0x79, 0x70, + 0x65, 0x12, 0x34, 0x0a, 0x16, 0x70, 0x76, 0x5f, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x61, 0x66, 0x66, + 0x69, 0x6e, 0x69, 0x74, 0x79, 0x5f, 0x7a, 0x6f, 0x6e, 0x65, 0x73, 0x18, 0xa1, 0x01, 0x20, 0x03, + 0x28, 0x09, 0x52, 0x13, 0x70, 0x76, 0x4e, 0x6f, 0x64, 0x65, 0x41, 0x66, 0x66, 0x69, 0x6e, 0x69, + 0x74, 0x79, 0x5a, 0x6f, 0x6e, 0x65, 0x73, 0x12, 0x26, 0x0a, 0x0e, 0x73, 0x63, 0x5f, 0x70, 0x72, + 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x18, 0xaa, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x0d, 0x73, 0x63, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x12, + 0x2b, 0x0a, 0x11, 0x73, 0x63, 0x5f, 0x72, 0x65, 0x63, 0x6c, 0x61, 0x69, 0x6d, 0x5f, 0x70, 0x6f, + 0x6c, 0x69, 0x63, 0x79, 0x18, 0xab, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x73, 0x63, 0x52, + 0x65, 0x63, 0x6c, 0x61, 0x69, 0x6d, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x12, 0x34, 0x0a, 0x16, + 0x73, 0x63, 0x5f, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x62, 0x69, 0x6e, 0x64, 0x69, 0x6e, + 0x67, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x18, 0xac, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x13, 0x73, + 0x63, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x42, 0x69, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x4d, 0x6f, + 0x64, 0x65, 0x12, 0x3a, 0x0a, 0x19, 0x73, 0x63, 0x5f, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x5f, 0x76, + 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x65, 0x78, 0x70, 0x61, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x18, + 0xad, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x16, 0x73, 0x63, 0x41, 0x6c, 0x6c, 0x6f, 0x77, 0x56, + 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x45, 0x78, 0x70, 0x61, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x4f, + 0x0a, 0x0d, 0x73, 0x63, 0x5f, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x18, + 0xae, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x29, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, + 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x2e, + 0x53, 0x63, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x45, 0x6e, 0x74, 0x72, + 0x79, 0x52, 0x0c, 0x73, 0x63, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x12, + 0x33, 0x0a, 0x15, 0x73, 0x63, 0x5f, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x5f, 0x74, 0x6f, + 0x70, 0x6f, 0x6c, 0x6f, 0x67, 0x69, 0x65, 0x73, 0x18, 0xaf, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, + 0x13, 0x73, 0x63, 0x41, 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x54, 0x6f, 0x70, 0x6f, 0x6c, 0x6f, + 0x67, 0x69, 0x65, 0x73, 0x12, 0x29, 0x0a, 0x10, 0x73, 0x63, 0x5f, 0x6d, 0x6f, 0x75, 0x6e, 0x74, + 0x5f, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0xb0, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, + 0x0e, 0x73, 0x63, 0x4d, 0x6f, 0x75, 0x6e, 0x74, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, + 0x1c, 0x0a, 0x09, 0x6e, 0x73, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0xb1, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x08, 0x6e, 0x73, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x24, 0x0a, + 0x0d, 0x6e, 0x73, 0x5f, 0x66, 0x69, 0x6e, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x72, 0x73, 0x18, 0xb2, + 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0c, 0x6e, 0x73, 0x46, 0x69, 0x6e, 0x61, 0x6c, 0x69, 0x7a, + 0x65, 0x72, 0x73, 0x12, 0x2b, 0x0a, 0x11, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x63, 0x70, 0x75, 0x5f, + 0x63, 0x61, 0x70, 0x61, 0x63, 0x69, 0x74, 0x79, 0x18, 0xb3, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x0f, 0x6e, 0x6f, 0x64, 0x65, 0x43, 0x70, 0x75, 0x43, 0x61, 0x70, 0x61, 0x63, 0x69, 0x74, 0x79, + 0x12, 0x31, 0x0a, 0x14, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x5f, + 0x63, 0x61, 0x70, 0x61, 0x63, 0x69, 0x74, 0x79, 0x18, 0xb4, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x12, 0x6e, 0x6f, 0x64, 0x65, 0x4d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x43, 0x61, 0x70, 0x61, 0x63, + 0x69, 0x74, 0x79, 0x12, 0x1e, 0x0a, 0x0a, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x72, 0x65, 0x61, 0x64, + 0x79, 0x18, 0xb5, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x6e, 0x6f, 0x64, 0x65, 0x52, 0x65, + 0x61, 0x64, 0x79, 0x12, 0x33, 0x0a, 0x0b, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x74, 0x61, 0x69, 0x6e, + 0x74, 0x73, 0x18, 0xb6, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x61, 0x70, 0x69, 0x2e, + 0x76, 0x31, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x54, 0x61, 0x69, 0x6e, 0x74, 0x52, 0x0a, 0x6e, 0x6f, + 0x64, 0x65, 0x54, 0x61, 0x69, 0x6e, 0x74, 0x73, 0x12, 0x1e, 0x0a, 0x0a, 0x6e, 0x6f, 0x64, 0x65, + 0x5f, 0x72, 0x6f, 0x6c, 0x65, 0x73, 0x18, 0xb7, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x09, 0x6e, + 0x6f, 0x64, 0x65, 0x52, 0x6f, 0x6c, 0x65, 0x73, 0x12, 0x29, 0x0a, 0x10, 0x6e, 0x6f, 0x64, 0x65, + 0x5f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x5f, 0x69, 0x70, 0x18, 0xb8, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x0e, 0x6e, 0x6f, 0x64, 0x65, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, + 0x6c, 0x49, 0x70, 0x12, 0x29, 0x0a, 0x10, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x65, 0x78, 0x74, 0x65, + 0x72, 0x6e, 0x61, 0x6c, 0x5f, 0x69, 0x70, 0x18, 0xb9, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, + 0x6e, 0x6f, 0x64, 0x65, 0x45, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x49, 0x70, 0x12, 0x22, + 0x0a, 0x0c, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0xba, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x6e, 0x6f, 0x64, 0x65, 0x56, 0x65, 0x72, 0x73, 0x69, + 0x6f, 0x6e, 0x12, 0x2d, 0x0a, 0x12, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x69, 0x6e, 0x73, 0x74, 0x61, + 0x6e, 0x63, 0x65, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0xbb, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x10, 0x6e, 0x6f, 0x64, 0x65, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x54, 0x79, 0x70, + 0x65, 0x12, 0x18, 0x0a, 0x07, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x6f, 0x73, 0x18, 0xbc, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x06, 0x6e, 0x6f, 0x64, 0x65, 0x4f, 0x73, 0x12, 0x35, 0x0a, 0x16, 0x6e, + 0x6f, 0x64, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x72, 0x75, + 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x18, 0xbd, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x14, 0x6e, 0x6f, + 0x64, 0x65, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x52, 0x75, 0x6e, 0x74, 0x69, + 0x6d, 0x65, 0x12, 0x29, 0x0a, 0x10, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x69, 0x6d, 0x61, 0x67, 0x65, + 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0xbe, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0e, 0x6e, + 0x6f, 0x64, 0x65, 0x49, 0x6d, 0x61, 0x67, 0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x29, 0x0a, + 0x10, 0x68, 0x70, 0x61, 0x5f, 0x6d, 0x69, 0x6e, 0x5f, 0x72, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, + 0x73, 0x18, 0xc8, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0e, 0x68, 0x70, 0x61, 0x4d, 0x69, 0x6e, + 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x73, 0x12, 0x29, 0x0a, 0x10, 0x68, 0x70, 0x61, 0x5f, + 0x6d, 0x61, 0x78, 0x5f, 0x72, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x73, 0x18, 0xc9, 0x01, 0x20, + 0x01, 0x28, 0x05, 0x52, 0x0e, 0x68, 0x70, 0x61, 0x4d, 0x61, 0x78, 0x52, 0x65, 0x70, 0x6c, 0x69, + 0x63, 0x61, 0x73, 0x12, 0x31, 0x0a, 0x14, 0x68, 0x70, 0x61, 0x5f, 0x63, 0x75, 0x72, 0x72, 0x65, + 0x6e, 0x74, 0x5f, 0x72, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x73, 0x18, 0xca, 0x01, 0x20, 0x01, + 0x28, 0x05, 0x52, 0x12, 0x68, 0x70, 0x61, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x52, 0x65, + 0x70, 0x6c, 0x69, 0x63, 0x61, 0x73, 0x12, 0x31, 0x0a, 0x14, 0x68, 0x70, 0x61, 0x5f, 0x64, 0x65, + 0x73, 0x69, 0x72, 0x65, 0x64, 0x5f, 0x72, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x73, 0x18, 0xcb, + 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x12, 0x68, 0x70, 0x61, 0x44, 0x65, 0x73, 0x69, 0x72, 0x65, + 0x64, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x73, 0x12, 0x2b, 0x0a, 0x11, 0x68, 0x70, 0x61, + 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0xcc, + 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0f, 0x68, 0x70, 0x61, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, + 0x73, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x1e, 0x0a, 0x0a, 0x68, 0x70, 0x61, 0x5f, 0x73, 0x74, + 0x61, 0x74, 0x75, 0x73, 0x18, 0xcd, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x68, 0x70, 0x61, + 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x3c, 0x0a, 0x1a, 0x68, 0x70, 0x61, 0x5f, 0x73, 0x63, + 0x61, 0x6c, 0x65, 0x5f, 0x75, 0x70, 0x5f, 0x73, 0x74, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x7a, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x18, 0xce, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x17, 0x68, 0x70, 0x61, + 0x53, 0x63, 0x61, 0x6c, 0x65, 0x55, 0x70, 0x53, 0x74, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x7a, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x40, 0x0a, 0x1c, 0x68, 0x70, 0x61, 0x5f, 0x73, 0x63, 0x61, 0x6c, + 0x65, 0x5f, 0x64, 0x6f, 0x77, 0x6e, 0x5f, 0x73, 0x74, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x7a, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x18, 0xcf, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x19, 0x68, 0x70, 0x61, + 0x53, 0x63, 0x61, 0x6c, 0x65, 0x44, 0x6f, 0x77, 0x6e, 0x53, 0x74, 0x61, 0x62, 0x69, 0x6c, 0x69, + 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x27, 0x0a, 0x0f, 0x76, 0x70, 0x61, 0x5f, 0x75, 0x70, + 0x64, 0x61, 0x74, 0x65, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x18, 0xd2, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x0d, 0x76, 0x70, 0x61, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4d, 0x6f, 0x64, 0x65, 0x12, + 0x2d, 0x0a, 0x12, 0x76, 0x70, 0x61, 0x5f, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x69, 0x65, 0x73, 0x5f, + 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0xd3, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x10, 0x76, 0x70, + 0x61, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x69, 0x65, 0x73, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x3b, + 0x0a, 0x19, 0x76, 0x70, 0x61, 0x5f, 0x72, 0x65, 0x63, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x64, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0xd4, 0x01, 0x20, 0x01, + 0x28, 0x05, 0x52, 0x17, 0x76, 0x70, 0x61, 0x52, 0x65, 0x63, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x64, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x25, 0x0a, 0x0e, 0x76, + 0x70, 0x61, 0x5f, 0x63, 0x70, 0x75, 0x5f, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x18, 0xd5, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x76, 0x70, 0x61, 0x43, 0x70, 0x75, 0x54, 0x61, 0x72, 0x67, + 0x65, 0x74, 0x12, 0x2b, 0x0a, 0x11, 0x76, 0x70, 0x61, 0x5f, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, + 0x5f, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x18, 0xd6, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, + 0x76, 0x70, 0x61, 0x4d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x12, + 0x1e, 0x0a, 0x0a, 0x76, 0x70, 0x61, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0xd7, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x76, 0x70, 0x61, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, + 0x36, 0x0a, 0x17, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x5f, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x5f, 0x69, + 0x74, 0x65, 0x6d, 0x73, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0xdc, 0x01, 0x20, 0x01, 0x28, + 0x05, 0x52, 0x14, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x49, 0x74, 0x65, + 0x6d, 0x73, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x2b, 0x0a, 0x11, 0x6c, 0x69, 0x6d, 0x69, 0x74, + 0x5f, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x73, 0x18, 0xdd, 0x01, 0x20, + 0x03, 0x28, 0x09, 0x52, 0x0f, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x54, + 0x79, 0x70, 0x65, 0x73, 0x12, 0x38, 0x0a, 0x18, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x5f, 0x72, 0x61, + 0x6e, 0x67, 0x65, 0x5f, 0x68, 0x61, 0x73, 0x5f, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x73, + 0x18, 0xde, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x15, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x52, 0x61, + 0x6e, 0x67, 0x65, 0x48, 0x61, 0x73, 0x44, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x73, 0x12, 0x2e, + 0x0a, 0x13, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x5f, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x5f, 0x6d, 0x61, + 0x78, 0x5f, 0x63, 0x70, 0x75, 0x18, 0xdf, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x6c, 0x69, + 0x6d, 0x69, 0x74, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x4d, 0x61, 0x78, 0x43, 0x70, 0x75, 0x12, 0x34, + 0x0a, 0x16, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x5f, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x5f, 0x6d, 0x61, + 0x78, 0x5f, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x18, 0xe0, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x13, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x4d, 0x61, 0x78, 0x4d, 0x65, + 0x6d, 0x6f, 0x72, 0x79, 0x12, 0x2e, 0x0a, 0x13, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x5f, 0x72, 0x61, + 0x6e, 0x67, 0x65, 0x5f, 0x6d, 0x69, 0x6e, 0x5f, 0x63, 0x70, 0x75, 0x18, 0xe1, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x10, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x4d, 0x69, + 0x6e, 0x43, 0x70, 0x75, 0x12, 0x34, 0x0a, 0x16, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x5f, 0x72, 0x61, + 0x6e, 0x67, 0x65, 0x5f, 0x6d, 0x69, 0x6e, 0x5f, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x18, 0xe2, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x13, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x52, 0x61, 0x6e, 0x67, + 0x65, 0x4d, 0x69, 0x6e, 0x4d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x12, 0x41, 0x0a, 0x1d, 0x6c, 0x69, + 0x6d, 0x69, 0x74, 0x5f, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x5f, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, + 0x74, 0x5f, 0x63, 0x70, 0x75, 0x5f, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x18, 0xe3, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x19, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x44, 0x65, + 0x66, 0x61, 0x75, 0x6c, 0x74, 0x43, 0x70, 0x75, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x12, 0x47, 0x0a, + 0x20, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x5f, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x5f, 0x64, 0x65, 0x66, + 0x61, 0x75, 0x6c, 0x74, 0x5f, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x5f, 0x6c, 0x69, 0x6d, 0x69, + 0x74, 0x18, 0xe4, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x1c, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x52, + 0x61, 0x6e, 0x67, 0x65, 0x44, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x4d, 0x65, 0x6d, 0x6f, 0x72, + 0x79, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x12, 0x45, 0x0a, 0x1f, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x5f, + 0x72, 0x61, 0x6e, 0x67, 0x65, 0x5f, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x5f, 0x63, 0x70, + 0x75, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x18, 0xe5, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x1b, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x44, 0x65, 0x66, 0x61, + 0x75, 0x6c, 0x74, 0x43, 0x70, 0x75, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x4b, 0x0a, + 0x22, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x5f, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x5f, 0x64, 0x65, 0x66, + 0x61, 0x75, 0x6c, 0x74, 0x5f, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x5f, 0x72, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x18, 0xe6, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x1e, 0x6c, 0x69, 0x6d, 0x69, + 0x74, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x44, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x4d, 0x65, 0x6d, + 0x6f, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2d, 0x0a, 0x12, 0x6c, 0x69, + 0x6d, 0x69, 0x74, 0x5f, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, + 0x18, 0xe7, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x52, 0x61, + 0x6e, 0x67, 0x65, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x2a, 0x0a, 0x10, 0x72, 0x65, 0x73, + 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0xf0, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x56, 0x65, + 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x20, 0x0a, 0x0b, 0x61, 0x70, 0x69, 0x5f, 0x76, 0x65, 0x72, + 0x73, 0x69, 0x6f, 0x6e, 0x18, 0xf1, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x61, 0x70, 0x69, + 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x1a, 0x42, 0x0a, 0x14, 0x53, 0x65, 0x72, 0x76, 0x69, + 0x63, 0x65, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, + 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, + 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x3f, 0x0a, 0x11, 0x53, + 0x63, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x3f, 0x0a, 0x11, - 0x53, 0x63, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x45, 0x6e, 0x74, 0x72, - 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, - 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x42, 0x09, 0x0a, - 0x07, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x22, 0x46, 0x0a, 0x0a, 0x50, 0x6f, 0x64, 0x44, - 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x38, 0x0a, 0x0a, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, - 0x6e, 0x65, 0x72, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x61, 0x70, 0x69, - 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x53, 0x75, 0x6d, - 0x6d, 0x61, 0x72, 0x79, 0x52, 0x0a, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x73, - 0x22, 0xb0, 0x01, 0x0a, 0x10, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x53, 0x75, - 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x69, 0x6d, 0x61, - 0x67, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x12, - 0x14, 0x0a, 0x05, 0x72, 0x65, 0x61, 0x64, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, - 0x72, 0x65, 0x61, 0x64, 0x79, 0x12, 0x23, 0x0a, 0x0d, 0x72, 0x65, 0x73, 0x74, 0x61, 0x72, 0x74, - 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0c, 0x72, 0x65, - 0x73, 0x74, 0x61, 0x72, 0x74, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x74, - 0x61, 0x74, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, - 0x12, 0x21, 0x0a, 0x0c, 0x73, 0x74, 0x61, 0x74, 0x65, 0x5f, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, - 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x73, 0x74, 0x61, 0x74, 0x65, 0x52, 0x65, 0x61, - 0x73, 0x6f, 0x6e, 0x22, 0x8b, 0x01, 0x0a, 0x11, 0x44, 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x6d, 0x65, - 0x6e, 0x74, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x39, 0x0a, 0x0a, 0x63, 0x6f, 0x6e, - 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x19, 0x2e, - 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, - 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x52, 0x0a, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, - 0x6e, 0x65, 0x72, 0x73, 0x12, 0x3b, 0x0a, 0x0a, 0x63, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, - 0x6e, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, - 0x31, 0x2e, 0x44, 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x43, 0x6f, 0x6e, 0x64, - 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0a, 0x63, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, - 0x73, 0x22, 0x8d, 0x02, 0x0a, 0x11, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x54, - 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x69, - 0x6d, 0x61, 0x67, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x69, 0x6d, 0x61, 0x67, - 0x65, 0x12, 0x36, 0x0a, 0x17, 0x63, 0x70, 0x75, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x73, 0x5f, 0x6d, 0x69, 0x6c, 0x6c, 0x69, 0x63, 0x6f, 0x72, 0x65, 0x73, 0x18, 0x03, 0x20, 0x01, - 0x28, 0x03, 0x52, 0x15, 0x63, 0x70, 0x75, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x73, 0x4d, - 0x69, 0x6c, 0x6c, 0x69, 0x63, 0x6f, 0x72, 0x65, 0x73, 0x12, 0x32, 0x0a, 0x15, 0x6d, 0x65, 0x6d, - 0x6f, 0x72, 0x79, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x73, 0x5f, 0x62, 0x79, 0x74, - 0x65, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x13, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x73, 0x42, 0x79, 0x74, 0x65, 0x73, 0x12, 0x32, 0x0a, - 0x15, 0x63, 0x70, 0x75, 0x5f, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x73, 0x5f, 0x6d, 0x69, 0x6c, 0x6c, - 0x69, 0x63, 0x6f, 0x72, 0x65, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x13, 0x63, 0x70, - 0x75, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x73, 0x4d, 0x69, 0x6c, 0x6c, 0x69, 0x63, 0x6f, 0x72, 0x65, - 0x73, 0x12, 0x2e, 0x0a, 0x13, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x5f, 0x6c, 0x69, 0x6d, 0x69, - 0x74, 0x73, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x03, 0x52, 0x11, - 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x73, 0x42, 0x79, 0x74, 0x65, - 0x73, 0x22, 0x9d, 0x01, 0x0a, 0x13, 0x44, 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x6d, 0x65, 0x6e, 0x74, - 0x43, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, - 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x16, 0x0a, - 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, - 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x18, - 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x12, 0x18, 0x0a, - 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, - 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x28, 0x0a, 0x10, 0x6c, 0x61, 0x73, 0x74, 0x5f, - 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, - 0x03, 0x52, 0x0e, 0x6c, 0x61, 0x73, 0x74, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x54, 0x69, 0x6d, - 0x65, 0x22, 0xc0, 0x01, 0x0a, 0x12, 0x53, 0x74, 0x61, 0x74, 0x65, 0x66, 0x75, 0x6c, 0x53, 0x65, + 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x42, 0x09, 0x0a, 0x07, + 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x22, 0x46, 0x0a, 0x0a, 0x50, 0x6f, 0x64, 0x44, 0x65, + 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x38, 0x0a, 0x0a, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, + 0x65, 0x72, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x61, 0x70, 0x69, 0x2e, + 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x53, 0x75, 0x6d, 0x6d, + 0x61, 0x72, 0x79, 0x52, 0x0a, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x73, 0x22, + 0xb0, 0x01, 0x0a, 0x10, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x53, 0x75, 0x6d, + 0x6d, 0x61, 0x72, 0x79, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x69, 0x6d, 0x61, 0x67, + 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x12, 0x14, + 0x0a, 0x05, 0x72, 0x65, 0x61, 0x64, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, 0x72, + 0x65, 0x61, 0x64, 0x79, 0x12, 0x23, 0x0a, 0x0d, 0x72, 0x65, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, + 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0c, 0x72, 0x65, 0x73, + 0x74, 0x61, 0x72, 0x74, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x74, 0x61, + 0x74, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x12, + 0x21, 0x0a, 0x0c, 0x73, 0x74, 0x61, 0x74, 0x65, 0x5f, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x18, + 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x73, 0x74, 0x61, 0x74, 0x65, 0x52, 0x65, 0x61, 0x73, + 0x6f, 0x6e, 0x22, 0x8b, 0x01, 0x0a, 0x11, 0x44, 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x39, 0x0a, 0x0a, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x52, 0x0a, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, - 0x65, 0x72, 0x73, 0x12, 0x51, 0x0a, 0x16, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x63, 0x6c, - 0x61, 0x69, 0x6d, 0x5f, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x73, 0x18, 0x02, 0x20, - 0x03, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x56, 0x6f, 0x6c, - 0x75, 0x6d, 0x65, 0x43, 0x6c, 0x61, 0x69, 0x6d, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, - 0x52, 0x14, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x43, 0x6c, 0x61, 0x69, 0x6d, 0x54, 0x65, 0x6d, - 0x70, 0x6c, 0x61, 0x74, 0x65, 0x73, 0x12, 0x1c, 0x0a, 0x09, 0x70, 0x61, 0x72, 0x74, 0x69, 0x74, - 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x09, 0x70, 0x61, 0x72, 0x74, 0x69, - 0x74, 0x69, 0x6f, 0x6e, 0x22, 0xbd, 0x01, 0x0a, 0x13, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x43, - 0x6c, 0x61, 0x69, 0x6d, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x12, 0x12, 0x0a, 0x04, - 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, - 0x12, 0x21, 0x0a, 0x0c, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x73, - 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0b, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x4d, 0x6f, - 0x64, 0x65, 0x73, 0x12, 0x1f, 0x0a, 0x0b, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x6d, 0x6f, - 0x64, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, - 0x4d, 0x6f, 0x64, 0x65, 0x12, 0x29, 0x0a, 0x10, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x5f, - 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0f, - 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x73, 0x12, - 0x23, 0x0a, 0x0d, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x5f, 0x63, 0x6c, 0x61, 0x73, 0x73, - 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x43, - 0x6c, 0x61, 0x73, 0x73, 0x22, 0xc2, 0x01, 0x0a, 0x10, 0x44, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x53, - 0x65, 0x74, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x39, 0x0a, 0x0a, 0x63, 0x6f, 0x6e, - 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x19, 0x2e, - 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, - 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x52, 0x0a, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, - 0x6e, 0x65, 0x72, 0x73, 0x12, 0x38, 0x0a, 0x0b, 0x74, 0x6f, 0x6c, 0x65, 0x72, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x61, 0x70, 0x69, 0x2e, - 0x76, 0x31, 0x2e, 0x54, 0x6f, 0x6c, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x6e, 0x66, - 0x6f, 0x52, 0x0b, 0x74, 0x6f, 0x6c, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x39, - 0x0a, 0x0d, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x18, - 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4e, - 0x6f, 0x64, 0x65, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x52, 0x0c, 0x6e, 0x6f, 0x64, - 0x65, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x22, 0x81, 0x01, 0x0a, 0x11, 0x52, 0x65, - 0x70, 0x6c, 0x69, 0x63, 0x61, 0x53, 0x65, 0x74, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, - 0x39, 0x0a, 0x0a, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x73, 0x18, 0x01, 0x20, - 0x03, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6e, - 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x52, 0x0a, - 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x73, 0x12, 0x31, 0x0a, 0x08, 0x73, 0x65, - 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x61, - 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x53, 0x65, 0x6c, 0x65, 0x63, - 0x74, 0x6f, 0x72, 0x52, 0x08, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x22, 0xb0, 0x01, - 0x0a, 0x0a, 0x4a, 0x6f, 0x62, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x39, 0x0a, 0x0a, - 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, - 0x32, 0x19, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, - 0x6e, 0x65, 0x72, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x52, 0x0a, 0x63, 0x6f, 0x6e, - 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x73, 0x12, 0x34, 0x0a, 0x0a, 0x63, 0x6f, 0x6e, 0x64, 0x69, - 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x61, 0x70, - 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4a, 0x6f, 0x62, 0x43, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, - 0x6e, 0x52, 0x0a, 0x63, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x31, 0x0a, - 0x08, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x15, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x53, 0x65, - 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x52, 0x08, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, - 0x22, 0xc6, 0x01, 0x0a, 0x0c, 0x4a, 0x6f, 0x62, 0x43, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, - 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x16, 0x0a, - 0x06, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x72, - 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, - 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, - 0x26, 0x0a, 0x0f, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x70, 0x72, 0x6f, 0x62, 0x65, 0x5f, 0x74, 0x69, - 0x6d, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0d, 0x6c, 0x61, 0x73, 0x74, 0x50, 0x72, - 0x6f, 0x62, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x30, 0x0a, 0x14, 0x6c, 0x61, 0x73, 0x74, 0x5f, - 0x74, 0x72, 0x61, 0x6e, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, - 0x06, 0x20, 0x01, 0x28, 0x03, 0x52, 0x12, 0x6c, 0x61, 0x73, 0x74, 0x54, 0x72, 0x61, 0x6e, 0x73, - 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x69, 0x6d, 0x65, 0x22, 0xc0, 0x01, 0x0a, 0x0e, 0x43, 0x72, - 0x6f, 0x6e, 0x4a, 0x6f, 0x62, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x39, 0x0a, 0x0a, - 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, - 0x32, 0x19, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, - 0x6e, 0x65, 0x72, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x52, 0x0a, 0x63, 0x6f, 0x6e, - 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x73, 0x12, 0x3b, 0x0a, 0x0b, 0x61, 0x63, 0x74, 0x69, 0x76, - 0x65, 0x5f, 0x6a, 0x6f, 0x62, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x61, - 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x63, 0x74, 0x69, 0x76, 0x65, 0x4a, 0x6f, 0x62, 0x52, - 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x52, 0x0a, 0x61, 0x63, 0x74, 0x69, 0x76, 0x65, - 0x4a, 0x6f, 0x62, 0x73, 0x12, 0x36, 0x0a, 0x0c, 0x6a, 0x6f, 0x62, 0x5f, 0x74, 0x65, 0x6d, 0x70, - 0x6c, 0x61, 0x74, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x61, 0x70, 0x69, - 0x2e, 0x76, 0x31, 0x2e, 0x4a, 0x6f, 0x62, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x52, - 0x0b, 0x6a, 0x6f, 0x62, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x22, 0x7d, 0x0a, 0x12, - 0x41, 0x63, 0x74, 0x69, 0x76, 0x65, 0x4a, 0x6f, 0x62, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, - 0x63, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x69, 0x64, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x69, 0x64, 0x12, 0x1c, 0x0a, 0x09, 0x6e, 0x61, 0x6d, 0x65, - 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6e, 0x61, 0x6d, - 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x63, 0x72, 0x65, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0c, 0x63, - 0x72, 0x65, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x69, 0x6d, 0x65, 0x22, 0xda, 0x01, 0x0a, 0x0b, - 0x4a, 0x6f, 0x62, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x12, 0x39, 0x0a, 0x0a, 0x63, + 0x65, 0x72, 0x73, 0x12, 0x3b, 0x0a, 0x0a, 0x63, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, + 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, + 0x2e, 0x44, 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x43, 0x6f, 0x6e, 0x64, 0x69, + 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0a, 0x63, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, + 0x22, 0x8d, 0x02, 0x0a, 0x11, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x54, 0x65, + 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x69, 0x6d, + 0x61, 0x67, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x69, 0x6d, 0x61, 0x67, 0x65, + 0x12, 0x36, 0x0a, 0x17, 0x63, 0x70, 0x75, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x73, + 0x5f, 0x6d, 0x69, 0x6c, 0x6c, 0x69, 0x63, 0x6f, 0x72, 0x65, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x03, 0x52, 0x15, 0x63, 0x70, 0x75, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x73, 0x4d, 0x69, + 0x6c, 0x6c, 0x69, 0x63, 0x6f, 0x72, 0x65, 0x73, 0x12, 0x32, 0x0a, 0x15, 0x6d, 0x65, 0x6d, 0x6f, + 0x72, 0x79, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x73, 0x5f, 0x62, 0x79, 0x74, 0x65, + 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x13, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x73, 0x42, 0x79, 0x74, 0x65, 0x73, 0x12, 0x32, 0x0a, 0x15, + 0x63, 0x70, 0x75, 0x5f, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x73, 0x5f, 0x6d, 0x69, 0x6c, 0x6c, 0x69, + 0x63, 0x6f, 0x72, 0x65, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x13, 0x63, 0x70, 0x75, + 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x73, 0x4d, 0x69, 0x6c, 0x6c, 0x69, 0x63, 0x6f, 0x72, 0x65, 0x73, + 0x12, 0x2e, 0x0a, 0x13, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x5f, 0x6c, 0x69, 0x6d, 0x69, 0x74, + 0x73, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x03, 0x52, 0x11, 0x6d, + 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x73, 0x42, 0x79, 0x74, 0x65, 0x73, + 0x22, 0x9d, 0x01, 0x0a, 0x13, 0x44, 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x43, + 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x16, 0x0a, 0x06, + 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x74, + 0x61, 0x74, 0x75, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x12, 0x18, 0x0a, 0x07, + 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, + 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x28, 0x0a, 0x10, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x75, + 0x70, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, + 0x52, 0x0e, 0x6c, 0x61, 0x73, 0x74, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, + 0x22, 0xc0, 0x01, 0x0a, 0x12, 0x53, 0x74, 0x61, 0x74, 0x65, 0x66, 0x75, 0x6c, 0x53, 0x65, 0x74, + 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x39, 0x0a, 0x0a, 0x63, 0x6f, 0x6e, 0x74, 0x61, + 0x69, 0x6e, 0x65, 0x72, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x61, 0x70, + 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x54, 0x65, + 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x52, 0x0a, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, + 0x72, 0x73, 0x12, 0x51, 0x0a, 0x16, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x63, 0x6c, 0x61, + 0x69, 0x6d, 0x5f, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, + 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x56, 0x6f, 0x6c, 0x75, + 0x6d, 0x65, 0x43, 0x6c, 0x61, 0x69, 0x6d, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x52, + 0x14, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x43, 0x6c, 0x61, 0x69, 0x6d, 0x54, 0x65, 0x6d, 0x70, + 0x6c, 0x61, 0x74, 0x65, 0x73, 0x12, 0x1c, 0x0a, 0x09, 0x70, 0x61, 0x72, 0x74, 0x69, 0x74, 0x69, + 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x09, 0x70, 0x61, 0x72, 0x74, 0x69, 0x74, + 0x69, 0x6f, 0x6e, 0x22, 0xbd, 0x01, 0x0a, 0x13, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x43, 0x6c, + 0x61, 0x69, 0x6d, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, + 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, + 0x21, 0x0a, 0x0c, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x73, 0x18, + 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0b, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x4d, 0x6f, 0x64, + 0x65, 0x73, 0x12, 0x1f, 0x0a, 0x0b, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x6d, 0x6f, 0x64, + 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x4d, + 0x6f, 0x64, 0x65, 0x12, 0x29, 0x0a, 0x10, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x5f, 0x72, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0f, 0x73, + 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x73, 0x12, 0x23, + 0x0a, 0x0d, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x5f, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x18, + 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x43, 0x6c, + 0x61, 0x73, 0x73, 0x22, 0xc2, 0x01, 0x0a, 0x10, 0x44, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x53, 0x65, + 0x74, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x39, 0x0a, 0x0a, 0x63, 0x6f, 0x6e, 0x74, + 0x61, 0x69, 0x6e, 0x65, 0x72, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x61, + 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x54, + 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x52, 0x0a, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, + 0x65, 0x72, 0x73, 0x12, 0x38, 0x0a, 0x0b, 0x74, 0x6f, 0x6c, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, + 0x31, 0x2e, 0x54, 0x6f, 0x6c, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x6e, 0x66, 0x6f, + 0x52, 0x0b, 0x74, 0x6f, 0x6c, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x39, 0x0a, + 0x0d, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4e, 0x6f, + 0x64, 0x65, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x52, 0x0c, 0x6e, 0x6f, 0x64, 0x65, + 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x22, 0x81, 0x01, 0x0a, 0x11, 0x52, 0x65, 0x70, + 0x6c, 0x69, 0x63, 0x61, 0x53, 0x65, 0x74, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x39, + 0x0a, 0x0a, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x73, 0x18, 0x01, 0x20, 0x03, + 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x74, + 0x61, 0x69, 0x6e, 0x65, 0x72, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x52, 0x0a, 0x63, + 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x73, 0x12, 0x31, 0x0a, 0x08, 0x73, 0x65, 0x6c, + 0x65, 0x63, 0x74, 0x6f, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x61, 0x70, + 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, + 0x6f, 0x72, 0x52, 0x08, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x22, 0xb0, 0x01, 0x0a, + 0x0a, 0x4a, 0x6f, 0x62, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x39, 0x0a, 0x0a, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x52, 0x0a, 0x63, 0x6f, 0x6e, 0x74, - 0x61, 0x69, 0x6e, 0x65, 0x72, 0x73, 0x12, 0x23, 0x0a, 0x0d, 0x62, 0x61, 0x63, 0x6b, 0x6f, 0x66, - 0x66, 0x5f, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0c, 0x62, - 0x61, 0x63, 0x6b, 0x6f, 0x66, 0x66, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x12, 0x20, 0x0a, 0x0b, 0x63, - 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, - 0x52, 0x0b, 0x63, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x20, 0x0a, - 0x0b, 0x70, 0x61, 0x72, 0x61, 0x6c, 0x6c, 0x65, 0x6c, 0x69, 0x73, 0x6d, 0x18, 0x04, 0x20, 0x01, - 0x28, 0x05, 0x52, 0x0b, 0x70, 0x61, 0x72, 0x61, 0x6c, 0x6c, 0x65, 0x6c, 0x69, 0x73, 0x6d, 0x12, - 0x27, 0x0a, 0x0f, 0x63, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6d, 0x6f, - 0x64, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x63, 0x6f, 0x6d, 0x70, 0x6c, 0x65, - 0x74, 0x69, 0x6f, 0x6e, 0x4d, 0x6f, 0x64, 0x65, 0x22, 0xba, 0x02, 0x0a, 0x0e, 0x53, 0x65, 0x72, - 0x76, 0x69, 0x63, 0x65, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x29, 0x0a, 0x05, 0x70, - 0x6f, 0x72, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x61, 0x70, 0x69, - 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x50, 0x6f, 0x72, 0x74, 0x52, - 0x05, 0x70, 0x6f, 0x72, 0x74, 0x73, 0x12, 0x40, 0x0a, 0x08, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, - 0x6f, 0x72, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, - 0x31, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, - 0x2e, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x08, - 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x12, 0x4f, 0x0a, 0x15, 0x6c, 0x6f, 0x61, 0x64, - 0x5f, 0x62, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x72, 0x5f, 0x69, 0x6e, 0x67, 0x72, 0x65, 0x73, - 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, - 0x2e, 0x4c, 0x6f, 0x61, 0x64, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x72, 0x49, 0x6e, 0x67, - 0x72, 0x65, 0x73, 0x73, 0x52, 0x13, 0x6c, 0x6f, 0x61, 0x64, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, - 0x65, 0x72, 0x49, 0x6e, 0x67, 0x72, 0x65, 0x73, 0x73, 0x12, 0x2d, 0x0a, 0x12, 0x65, 0x6e, 0x64, - 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x18, - 0x04, 0x20, 0x03, 0x28, 0x09, 0x52, 0x11, 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x41, - 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x1a, 0x3b, 0x0a, 0x0d, 0x53, 0x65, 0x6c, 0x65, - 0x63, 0x74, 0x6f, 0x72, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, - 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, - 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0xdc, 0x01, 0x0a, 0x0b, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, - 0x65, 0x50, 0x6f, 0x72, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x6f, 0x72, - 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x04, 0x70, 0x6f, 0x72, 0x74, 0x12, 0x1f, 0x0a, - 0x0b, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x5f, 0x70, 0x6f, 0x72, 0x74, 0x18, 0x03, 0x20, 0x01, - 0x28, 0x05, 0x52, 0x0a, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x50, 0x6f, 0x72, 0x74, 0x12, 0x28, - 0x0a, 0x10, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x5f, 0x70, 0x6f, 0x72, 0x74, 0x5f, 0x6e, 0x61, - 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, - 0x50, 0x6f, 0x72, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x63, 0x6f, 0x6c, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x63, 0x6f, 0x6c, 0x12, 0x1b, 0x0a, 0x09, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x70, 0x6f, 0x72, - 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x05, 0x52, 0x08, 0x6e, 0x6f, 0x64, 0x65, 0x50, 0x6f, 0x72, - 0x74, 0x12, 0x21, 0x0a, 0x0c, 0x61, 0x70, 0x70, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, - 0x6c, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x61, 0x70, 0x70, 0x50, 0x72, 0x6f, 0x74, - 0x6f, 0x63, 0x6f, 0x6c, 0x22, 0x70, 0x0a, 0x13, 0x4c, 0x6f, 0x61, 0x64, 0x42, 0x61, 0x6c, 0x61, - 0x6e, 0x63, 0x65, 0x72, 0x49, 0x6e, 0x67, 0x72, 0x65, 0x73, 0x73, 0x12, 0x0e, 0x0a, 0x02, 0x69, - 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x70, 0x12, 0x1a, 0x0a, 0x08, 0x68, - 0x6f, 0x73, 0x74, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x68, - 0x6f, 0x73, 0x74, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x70, 0x6f, 0x72, 0x74, 0x73, - 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x05, 0x70, 0x6f, 0x72, 0x74, 0x73, 0x12, 0x17, 0x0a, - 0x07, 0x69, 0x70, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, - 0x69, 0x70, 0x4d, 0x6f, 0x64, 0x65, 0x22, 0xf3, 0x01, 0x0a, 0x0e, 0x49, 0x6e, 0x67, 0x72, 0x65, - 0x73, 0x73, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x29, 0x0a, 0x05, 0x72, 0x75, 0x6c, - 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, - 0x31, 0x2e, 0x49, 0x6e, 0x67, 0x72, 0x65, 0x73, 0x73, 0x52, 0x75, 0x6c, 0x65, 0x52, 0x05, 0x72, - 0x75, 0x6c, 0x65, 0x73, 0x12, 0x24, 0x0a, 0x03, 0x74, 0x6c, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, - 0x0b, 0x32, 0x12, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x49, 0x6e, 0x67, 0x72, 0x65, - 0x73, 0x73, 0x54, 0x4c, 0x53, 0x52, 0x03, 0x74, 0x6c, 0x73, 0x12, 0x3f, 0x0a, 0x0f, 0x64, 0x65, - 0x66, 0x61, 0x75, 0x6c, 0x74, 0x5f, 0x62, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x18, 0x03, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x49, 0x6e, 0x67, - 0x72, 0x65, 0x73, 0x73, 0x42, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x52, 0x0e, 0x64, 0x65, 0x66, - 0x61, 0x75, 0x6c, 0x74, 0x42, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x12, 0x4f, 0x0a, 0x15, 0x6c, - 0x6f, 0x61, 0x64, 0x5f, 0x62, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x72, 0x5f, 0x69, 0x6e, 0x67, - 0x72, 0x65, 0x73, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x61, 0x70, 0x69, - 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x6f, 0x61, 0x64, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x72, - 0x49, 0x6e, 0x67, 0x72, 0x65, 0x73, 0x73, 0x52, 0x13, 0x6c, 0x6f, 0x61, 0x64, 0x42, 0x61, 0x6c, - 0x61, 0x6e, 0x63, 0x65, 0x72, 0x49, 0x6e, 0x67, 0x72, 0x65, 0x73, 0x73, 0x22, 0x4c, 0x0a, 0x0b, - 0x49, 0x6e, 0x67, 0x72, 0x65, 0x73, 0x73, 0x52, 0x75, 0x6c, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x68, - 0x6f, 0x73, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x68, 0x6f, 0x73, 0x74, 0x12, - 0x29, 0x0a, 0x05, 0x70, 0x61, 0x74, 0x68, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x13, - 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x49, 0x6e, 0x67, 0x72, 0x65, 0x73, 0x73, 0x50, - 0x61, 0x74, 0x68, 0x52, 0x05, 0x70, 0x61, 0x74, 0x68, 0x73, 0x22, 0x70, 0x0a, 0x0b, 0x49, 0x6e, - 0x67, 0x72, 0x65, 0x73, 0x73, 0x50, 0x61, 0x74, 0x68, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x61, 0x74, - 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x70, 0x61, 0x74, 0x68, 0x12, 0x1b, 0x0a, - 0x09, 0x70, 0x61, 0x74, 0x68, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x08, 0x70, 0x61, 0x74, 0x68, 0x54, 0x79, 0x70, 0x65, 0x12, 0x30, 0x0a, 0x07, 0x62, 0x61, - 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x61, 0x70, - 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x49, 0x6e, 0x67, 0x72, 0x65, 0x73, 0x73, 0x42, 0x61, 0x63, 0x6b, - 0x65, 0x6e, 0x64, 0x52, 0x07, 0x62, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x22, 0xfe, 0x01, 0x0a, - 0x0e, 0x49, 0x6e, 0x67, 0x72, 0x65, 0x73, 0x73, 0x42, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x12, - 0x21, 0x0a, 0x0c, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x4e, 0x61, - 0x6d, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x70, 0x6f, - 0x72, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0b, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, - 0x65, 0x50, 0x6f, 0x72, 0x74, 0x12, 0x2a, 0x0a, 0x11, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, - 0x5f, 0x70, 0x6f, 0x72, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x0f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x50, 0x6f, 0x72, 0x74, 0x4e, 0x61, 0x6d, - 0x65, 0x12, 0x30, 0x0a, 0x14, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x61, 0x70, - 0x69, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x12, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x41, 0x70, 0x69, 0x56, 0x65, 0x72, 0x73, - 0x69, 0x6f, 0x6e, 0x12, 0x23, 0x0a, 0x0d, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, - 0x6b, 0x69, 0x6e, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x72, 0x65, 0x73, 0x6f, - 0x75, 0x72, 0x63, 0x65, 0x4b, 0x69, 0x6e, 0x64, 0x12, 0x23, 0x0a, 0x0d, 0x72, 0x65, 0x73, 0x6f, - 0x75, 0x72, 0x63, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x0c, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x22, 0x43, 0x0a, - 0x0a, 0x49, 0x6e, 0x67, 0x72, 0x65, 0x73, 0x73, 0x54, 0x4c, 0x53, 0x12, 0x14, 0x0a, 0x05, 0x68, - 0x6f, 0x73, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x05, 0x68, 0x6f, 0x73, 0x74, - 0x73, 0x12, 0x1f, 0x0a, 0x0b, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x4e, 0x61, - 0x6d, 0x65, 0x22, 0xd0, 0x02, 0x0a, 0x1c, 0x50, 0x65, 0x72, 0x73, 0x69, 0x73, 0x74, 0x65, 0x6e, - 0x74, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x43, 0x6c, 0x61, 0x69, 0x6d, 0x44, 0x65, 0x74, 0x61, - 0x69, 0x6c, 0x73, 0x12, 0x51, 0x0a, 0x15, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, - 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x73, 0x6f, - 0x75, 0x72, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x73, - 0x52, 0x14, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x69, 0x72, - 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x31, 0x0a, 0x08, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, - 0x6f, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, - 0x31, 0x2e, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x52, - 0x08, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x12, 0x4c, 0x0a, 0x14, 0x76, 0x6f, 0x6c, - 0x75, 0x6d, 0x65, 0x5f, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x61, 0x66, 0x66, 0x69, 0x6e, 0x69, 0x74, - 0x79, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, - 0x2e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x4e, 0x6f, 0x64, 0x65, 0x41, 0x66, 0x66, 0x69, 0x6e, - 0x69, 0x74, 0x79, 0x52, 0x12, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x4e, 0x6f, 0x64, 0x65, 0x41, - 0x66, 0x66, 0x69, 0x6e, 0x69, 0x74, 0x79, 0x12, 0x34, 0x0a, 0x0a, 0x63, 0x6f, 0x6e, 0x64, 0x69, - 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x61, 0x70, - 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x56, 0x43, 0x43, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, - 0x6e, 0x52, 0x0a, 0x63, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x26, 0x0a, - 0x0f, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x64, 0x5f, 0x62, 0x79, 0x5f, 0x70, 0x6f, 0x64, 0x73, - 0x18, 0x05, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0d, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x64, 0x42, - 0x79, 0x50, 0x6f, 0x64, 0x73, 0x22, 0x98, 0x02, 0x0a, 0x14, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, - 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x46, - 0x0a, 0x08, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, - 0x32, 0x2a, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, - 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x2e, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x08, 0x72, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x73, 0x12, 0x40, 0x0a, 0x06, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x73, - 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x28, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, - 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x6d, - 0x65, 0x6e, 0x74, 0x73, 0x2e, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, - 0x52, 0x06, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x73, 0x1a, 0x3b, 0x0a, 0x0d, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, - 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, - 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x39, 0x0a, 0x0b, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x73, 0x45, + 0x61, 0x69, 0x6e, 0x65, 0x72, 0x73, 0x12, 0x34, 0x0a, 0x0a, 0x63, 0x6f, 0x6e, 0x64, 0x69, 0x74, + 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x61, 0x70, 0x69, + 0x2e, 0x76, 0x31, 0x2e, 0x4a, 0x6f, 0x62, 0x43, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, + 0x52, 0x0a, 0x63, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x31, 0x0a, 0x08, + 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, + 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x53, 0x65, 0x6c, + 0x65, 0x63, 0x74, 0x6f, 0x72, 0x52, 0x08, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x22, + 0xc6, 0x01, 0x0a, 0x0c, 0x4a, 0x6f, 0x62, 0x43, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, + 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, + 0x74, 0x79, 0x70, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x16, 0x0a, 0x06, + 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x72, 0x65, + 0x61, 0x73, 0x6f, 0x6e, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, + 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x26, + 0x0a, 0x0f, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x70, 0x72, 0x6f, 0x62, 0x65, 0x5f, 0x74, 0x69, 0x6d, + 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0d, 0x6c, 0x61, 0x73, 0x74, 0x50, 0x72, 0x6f, + 0x62, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x30, 0x0a, 0x14, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x74, + 0x72, 0x61, 0x6e, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x06, + 0x20, 0x01, 0x28, 0x03, 0x52, 0x12, 0x6c, 0x61, 0x73, 0x74, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x69, + 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x69, 0x6d, 0x65, 0x22, 0xc0, 0x01, 0x0a, 0x0e, 0x43, 0x72, 0x6f, + 0x6e, 0x4a, 0x6f, 0x62, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x39, 0x0a, 0x0a, 0x63, + 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, + 0x19, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, + 0x65, 0x72, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x52, 0x0a, 0x63, 0x6f, 0x6e, 0x74, + 0x61, 0x69, 0x6e, 0x65, 0x72, 0x73, 0x12, 0x3b, 0x0a, 0x0b, 0x61, 0x63, 0x74, 0x69, 0x76, 0x65, + 0x5f, 0x6a, 0x6f, 0x62, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x61, 0x70, + 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x63, 0x74, 0x69, 0x76, 0x65, 0x4a, 0x6f, 0x62, 0x52, 0x65, + 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x52, 0x0a, 0x61, 0x63, 0x74, 0x69, 0x76, 0x65, 0x4a, + 0x6f, 0x62, 0x73, 0x12, 0x36, 0x0a, 0x0c, 0x6a, 0x6f, 0x62, 0x5f, 0x74, 0x65, 0x6d, 0x70, 0x6c, + 0x61, 0x74, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x61, 0x70, 0x69, 0x2e, + 0x76, 0x31, 0x2e, 0x4a, 0x6f, 0x62, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x52, 0x0b, + 0x6a, 0x6f, 0x62, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x22, 0x7d, 0x0a, 0x12, 0x41, + 0x63, 0x74, 0x69, 0x76, 0x65, 0x4a, 0x6f, 0x62, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, + 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x03, 0x75, 0x69, 0x64, 0x12, 0x1c, 0x0a, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, + 0x70, 0x61, 0x63, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6e, 0x61, 0x6d, 0x65, + 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x63, 0x72, 0x65, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0c, 0x63, 0x72, + 0x65, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x69, 0x6d, 0x65, 0x22, 0xda, 0x01, 0x0a, 0x0b, 0x4a, + 0x6f, 0x62, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x12, 0x39, 0x0a, 0x0a, 0x63, 0x6f, + 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x19, + 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, + 0x72, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x52, 0x0a, 0x63, 0x6f, 0x6e, 0x74, 0x61, + 0x69, 0x6e, 0x65, 0x72, 0x73, 0x12, 0x23, 0x0a, 0x0d, 0x62, 0x61, 0x63, 0x6b, 0x6f, 0x66, 0x66, + 0x5f, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0c, 0x62, 0x61, + 0x63, 0x6b, 0x6f, 0x66, 0x66, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x12, 0x20, 0x0a, 0x0b, 0x63, 0x6f, + 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, + 0x0b, 0x63, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x20, 0x0a, 0x0b, + 0x70, 0x61, 0x72, 0x61, 0x6c, 0x6c, 0x65, 0x6c, 0x69, 0x73, 0x6d, 0x18, 0x04, 0x20, 0x01, 0x28, + 0x05, 0x52, 0x0b, 0x70, 0x61, 0x72, 0x61, 0x6c, 0x6c, 0x65, 0x6c, 0x69, 0x73, 0x6d, 0x12, 0x27, + 0x0a, 0x0f, 0x63, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6d, 0x6f, 0x64, + 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x63, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, + 0x69, 0x6f, 0x6e, 0x4d, 0x6f, 0x64, 0x65, 0x22, 0xba, 0x02, 0x0a, 0x0e, 0x53, 0x65, 0x72, 0x76, + 0x69, 0x63, 0x65, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x29, 0x0a, 0x05, 0x70, 0x6f, + 0x72, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x61, 0x70, 0x69, 0x2e, + 0x76, 0x31, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x50, 0x6f, 0x72, 0x74, 0x52, 0x05, + 0x70, 0x6f, 0x72, 0x74, 0x73, 0x12, 0x40, 0x0a, 0x08, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, + 0x72, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, + 0x2e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x2e, + 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x08, 0x73, + 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x12, 0x4f, 0x0a, 0x15, 0x6c, 0x6f, 0x61, 0x64, 0x5f, + 0x62, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x72, 0x5f, 0x69, 0x6e, 0x67, 0x72, 0x65, 0x73, 0x73, + 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, + 0x4c, 0x6f, 0x61, 0x64, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x72, 0x49, 0x6e, 0x67, 0x72, + 0x65, 0x73, 0x73, 0x52, 0x13, 0x6c, 0x6f, 0x61, 0x64, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, + 0x72, 0x49, 0x6e, 0x67, 0x72, 0x65, 0x73, 0x73, 0x12, 0x2d, 0x0a, 0x12, 0x65, 0x6e, 0x64, 0x70, + 0x6f, 0x69, 0x6e, 0x74, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x18, 0x04, + 0x20, 0x03, 0x28, 0x09, 0x52, 0x11, 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x41, 0x64, + 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x1a, 0x3b, 0x0a, 0x0d, 0x53, 0x65, 0x6c, 0x65, 0x63, + 0x74, 0x6f, 0x72, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, + 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, + 0x3a, 0x02, 0x38, 0x01, 0x22, 0xdc, 0x01, 0x0a, 0x0b, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, + 0x50, 0x6f, 0x72, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x6f, 0x72, 0x74, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x04, 0x70, 0x6f, 0x72, 0x74, 0x12, 0x1f, 0x0a, 0x0b, + 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x5f, 0x70, 0x6f, 0x72, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x05, 0x52, 0x0a, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x50, 0x6f, 0x72, 0x74, 0x12, 0x28, 0x0a, + 0x10, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x5f, 0x70, 0x6f, 0x72, 0x74, 0x5f, 0x6e, 0x61, 0x6d, + 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x50, + 0x6f, 0x72, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x63, 0x6f, 0x6c, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x63, 0x6f, 0x6c, 0x12, 0x1b, 0x0a, 0x09, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x70, 0x6f, 0x72, 0x74, + 0x18, 0x06, 0x20, 0x01, 0x28, 0x05, 0x52, 0x08, 0x6e, 0x6f, 0x64, 0x65, 0x50, 0x6f, 0x72, 0x74, + 0x12, 0x21, 0x0a, 0x0c, 0x61, 0x70, 0x70, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, + 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x61, 0x70, 0x70, 0x50, 0x72, 0x6f, 0x74, 0x6f, + 0x63, 0x6f, 0x6c, 0x22, 0x70, 0x0a, 0x13, 0x4c, 0x6f, 0x61, 0x64, 0x42, 0x61, 0x6c, 0x61, 0x6e, + 0x63, 0x65, 0x72, 0x49, 0x6e, 0x67, 0x72, 0x65, 0x73, 0x73, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x70, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x70, 0x12, 0x1a, 0x0a, 0x08, 0x68, 0x6f, + 0x73, 0x74, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x68, 0x6f, + 0x73, 0x74, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x70, 0x6f, 0x72, 0x74, 0x73, 0x18, + 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x05, 0x70, 0x6f, 0x72, 0x74, 0x73, 0x12, 0x17, 0x0a, 0x07, + 0x69, 0x70, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x69, + 0x70, 0x4d, 0x6f, 0x64, 0x65, 0x22, 0xf3, 0x01, 0x0a, 0x0e, 0x49, 0x6e, 0x67, 0x72, 0x65, 0x73, + 0x73, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x29, 0x0a, 0x05, 0x72, 0x75, 0x6c, 0x65, + 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, + 0x2e, 0x49, 0x6e, 0x67, 0x72, 0x65, 0x73, 0x73, 0x52, 0x75, 0x6c, 0x65, 0x52, 0x05, 0x72, 0x75, + 0x6c, 0x65, 0x73, 0x12, 0x24, 0x0a, 0x03, 0x74, 0x6c, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, + 0x32, 0x12, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x49, 0x6e, 0x67, 0x72, 0x65, 0x73, + 0x73, 0x54, 0x4c, 0x53, 0x52, 0x03, 0x74, 0x6c, 0x73, 0x12, 0x3f, 0x0a, 0x0f, 0x64, 0x65, 0x66, + 0x61, 0x75, 0x6c, 0x74, 0x5f, 0x62, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x49, 0x6e, 0x67, 0x72, + 0x65, 0x73, 0x73, 0x42, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x52, 0x0e, 0x64, 0x65, 0x66, 0x61, + 0x75, 0x6c, 0x74, 0x42, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x12, 0x4f, 0x0a, 0x15, 0x6c, 0x6f, + 0x61, 0x64, 0x5f, 0x62, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x72, 0x5f, 0x69, 0x6e, 0x67, 0x72, + 0x65, 0x73, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x61, 0x70, 0x69, 0x2e, + 0x76, 0x31, 0x2e, 0x4c, 0x6f, 0x61, 0x64, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x72, 0x49, + 0x6e, 0x67, 0x72, 0x65, 0x73, 0x73, 0x52, 0x13, 0x6c, 0x6f, 0x61, 0x64, 0x42, 0x61, 0x6c, 0x61, + 0x6e, 0x63, 0x65, 0x72, 0x49, 0x6e, 0x67, 0x72, 0x65, 0x73, 0x73, 0x22, 0x4c, 0x0a, 0x0b, 0x49, + 0x6e, 0x67, 0x72, 0x65, 0x73, 0x73, 0x52, 0x75, 0x6c, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x68, 0x6f, + 0x73, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x68, 0x6f, 0x73, 0x74, 0x12, 0x29, + 0x0a, 0x05, 0x70, 0x61, 0x74, 0x68, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, + 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x49, 0x6e, 0x67, 0x72, 0x65, 0x73, 0x73, 0x50, 0x61, + 0x74, 0x68, 0x52, 0x05, 0x70, 0x61, 0x74, 0x68, 0x73, 0x22, 0x70, 0x0a, 0x0b, 0x49, 0x6e, 0x67, + 0x72, 0x65, 0x73, 0x73, 0x50, 0x61, 0x74, 0x68, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x61, 0x74, 0x68, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x70, 0x61, 0x74, 0x68, 0x12, 0x1b, 0x0a, 0x09, + 0x70, 0x61, 0x74, 0x68, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x08, 0x70, 0x61, 0x74, 0x68, 0x54, 0x79, 0x70, 0x65, 0x12, 0x30, 0x0a, 0x07, 0x62, 0x61, 0x63, + 0x6b, 0x65, 0x6e, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x61, 0x70, 0x69, + 0x2e, 0x76, 0x31, 0x2e, 0x49, 0x6e, 0x67, 0x72, 0x65, 0x73, 0x73, 0x42, 0x61, 0x63, 0x6b, 0x65, + 0x6e, 0x64, 0x52, 0x07, 0x62, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x22, 0xfe, 0x01, 0x0a, 0x0e, + 0x49, 0x6e, 0x67, 0x72, 0x65, 0x73, 0x73, 0x42, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x12, 0x21, + 0x0a, 0x0c, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x4e, 0x61, 0x6d, + 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x70, 0x6f, 0x72, + 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0b, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, + 0x50, 0x6f, 0x72, 0x74, 0x12, 0x2a, 0x0a, 0x11, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x5f, + 0x70, 0x6f, 0x72, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x0f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x50, 0x6f, 0x72, 0x74, 0x4e, 0x61, 0x6d, 0x65, + 0x12, 0x30, 0x0a, 0x14, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x61, 0x70, 0x69, + 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x12, + 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x41, 0x70, 0x69, 0x56, 0x65, 0x72, 0x73, 0x69, + 0x6f, 0x6e, 0x12, 0x23, 0x0a, 0x0d, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x6b, + 0x69, 0x6e, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x72, 0x65, 0x73, 0x6f, 0x75, + 0x72, 0x63, 0x65, 0x4b, 0x69, 0x6e, 0x64, 0x12, 0x23, 0x0a, 0x0d, 0x72, 0x65, 0x73, 0x6f, 0x75, + 0x72, 0x63, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, + 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x22, 0x43, 0x0a, 0x0a, + 0x49, 0x6e, 0x67, 0x72, 0x65, 0x73, 0x73, 0x54, 0x4c, 0x53, 0x12, 0x14, 0x0a, 0x05, 0x68, 0x6f, + 0x73, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x05, 0x68, 0x6f, 0x73, 0x74, 0x73, + 0x12, 0x1f, 0x0a, 0x0b, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x4e, 0x61, 0x6d, + 0x65, 0x22, 0xd0, 0x02, 0x0a, 0x1c, 0x50, 0x65, 0x72, 0x73, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x74, + 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x43, 0x6c, 0x61, 0x69, 0x6d, 0x44, 0x65, 0x74, 0x61, 0x69, + 0x6c, 0x73, 0x12, 0x51, 0x0a, 0x15, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x72, + 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x1c, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, + 0x72, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x52, + 0x14, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, + 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x31, 0x0a, 0x08, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, + 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, + 0x2e, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x52, 0x08, + 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x12, 0x4c, 0x0a, 0x14, 0x76, 0x6f, 0x6c, 0x75, + 0x6d, 0x65, 0x5f, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x61, 0x66, 0x66, 0x69, 0x6e, 0x69, 0x74, 0x79, + 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, + 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x4e, 0x6f, 0x64, 0x65, 0x41, 0x66, 0x66, 0x69, 0x6e, 0x69, + 0x74, 0x79, 0x52, 0x12, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x4e, 0x6f, 0x64, 0x65, 0x41, 0x66, + 0x66, 0x69, 0x6e, 0x69, 0x74, 0x79, 0x12, 0x34, 0x0a, 0x0a, 0x63, 0x6f, 0x6e, 0x64, 0x69, 0x74, + 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x61, 0x70, 0x69, + 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x56, 0x43, 0x43, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, + 0x52, 0x0a, 0x63, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x26, 0x0a, 0x0f, + 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x64, 0x5f, 0x62, 0x79, 0x5f, 0x70, 0x6f, 0x64, 0x73, 0x18, + 0x05, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0d, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x64, 0x42, 0x79, + 0x50, 0x6f, 0x64, 0x73, 0x22, 0x98, 0x02, 0x0a, 0x14, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, + 0x65, 0x52, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x46, 0x0a, + 0x08, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, + 0x2a, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, + 0x65, 0x52, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x2e, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x08, 0x72, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x73, 0x12, 0x40, 0x0a, 0x06, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x73, 0x18, + 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x28, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x52, + 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x6d, 0x65, + 0x6e, 0x74, 0x73, 0x2e, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, + 0x06, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x73, 0x1a, 0x3b, 0x0a, 0x0d, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, + 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, + 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x39, 0x0a, 0x0b, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x73, 0x45, 0x6e, + 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, + 0x90, 0x01, 0x0a, 0x12, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x4e, 0x6f, 0x64, 0x65, 0x41, 0x66, + 0x66, 0x69, 0x6e, 0x69, 0x74, 0x79, 0x12, 0x3b, 0x0a, 0x08, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, + 0x65, 0x64, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, + 0x31, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x52, 0x65, + 0x71, 0x75, 0x69, 0x72, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x08, 0x72, 0x65, 0x71, 0x75, 0x69, + 0x72, 0x65, 0x64, 0x12, 0x3d, 0x0a, 0x09, 0x70, 0x72, 0x65, 0x66, 0x65, 0x72, 0x72, 0x65, 0x64, + 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, + 0x4e, 0x6f, 0x64, 0x65, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x52, 0x65, 0x71, 0x75, + 0x69, 0x72, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x09, 0x70, 0x72, 0x65, 0x66, 0x65, 0x72, 0x72, + 0x65, 0x64, 0x22, 0xc6, 0x01, 0x0a, 0x0c, 0x50, 0x56, 0x43, 0x43, 0x6f, 0x6e, 0x64, 0x69, 0x74, + 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, + 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, + 0x16, 0x0a, 0x06, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x06, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, + 0x67, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, + 0x65, 0x12, 0x26, 0x0a, 0x0f, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x70, 0x72, 0x6f, 0x62, 0x65, 0x5f, + 0x74, 0x69, 0x6d, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0d, 0x6c, 0x61, 0x73, 0x74, + 0x50, 0x72, 0x6f, 0x62, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x30, 0x0a, 0x14, 0x6c, 0x61, 0x73, + 0x74, 0x5f, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x74, 0x69, 0x6d, + 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x03, 0x52, 0x12, 0x6c, 0x61, 0x73, 0x74, 0x54, 0x72, 0x61, + 0x6e, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x69, 0x6d, 0x65, 0x22, 0xf5, 0x02, 0x0a, 0x17, + 0x50, 0x65, 0x72, 0x73, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x74, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, + 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x49, 0x0a, 0x08, 0x63, 0x61, 0x70, 0x61, 0x63, + 0x69, 0x74, 0x79, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2d, 0x2e, 0x61, 0x70, 0x69, 0x2e, + 0x76, 0x31, 0x2e, 0x50, 0x65, 0x72, 0x73, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x74, 0x56, 0x6f, 0x6c, + 0x75, 0x6d, 0x65, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x2e, 0x43, 0x61, 0x70, 0x61, 0x63, + 0x69, 0x74, 0x79, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x08, 0x63, 0x61, 0x70, 0x61, 0x63, 0x69, + 0x74, 0x79, 0x12, 0x35, 0x0a, 0x09, 0x63, 0x6c, 0x61, 0x69, 0x6d, 0x5f, 0x72, 0x65, 0x66, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x50, + 0x56, 0x43, 0x6c, 0x61, 0x69, 0x6d, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x52, + 0x08, 0x63, 0x6c, 0x61, 0x69, 0x6d, 0x52, 0x65, 0x66, 0x12, 0x3b, 0x0a, 0x0d, 0x76, 0x6f, 0x6c, + 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x16, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x56, 0x56, 0x6f, 0x6c, 0x75, + 0x6d, 0x65, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x0c, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, + 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x39, 0x0a, 0x0d, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x61, + 0x66, 0x66, 0x69, 0x6e, 0x69, 0x74, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, + 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x53, 0x65, 0x6c, 0x65, 0x63, + 0x74, 0x6f, 0x72, 0x52, 0x0c, 0x6e, 0x6f, 0x64, 0x65, 0x41, 0x66, 0x66, 0x69, 0x6e, 0x69, 0x74, + 0x79, 0x12, 0x23, 0x0a, 0x0d, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x6f, 0x70, 0x74, 0x69, 0x6f, + 0x6e, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0c, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x4f, + 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x1a, 0x3b, 0x0a, 0x0d, 0x43, 0x61, 0x70, 0x61, 0x63, 0x69, + 0x74, 0x79, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, + 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, + 0x02, 0x38, 0x01, 0x22, 0xa2, 0x01, 0x0a, 0x10, 0x50, 0x56, 0x43, 0x6c, 0x61, 0x69, 0x6d, 0x52, + 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1c, 0x0a, 0x09, + 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x69, + 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x69, 0x64, 0x12, 0x1f, 0x0a, 0x0b, + 0x61, 0x70, 0x69, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x0a, 0x61, 0x70, 0x69, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x29, 0x0a, + 0x10, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, + 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, + 0x65, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0xd5, 0x02, 0x0a, 0x0e, 0x50, 0x56, 0x56, + 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x74, + 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, + 0x29, 0x0a, 0x03, 0x63, 0x73, 0x69, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x61, + 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x53, 0x49, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x53, + 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x03, 0x63, 0x73, 0x69, 0x12, 0x39, 0x0a, 0x09, 0x68, 0x6f, + 0x73, 0x74, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, + 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x48, 0x6f, 0x73, 0x74, 0x50, 0x61, 0x74, 0x68, 0x56, + 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x08, 0x68, 0x6f, 0x73, + 0x74, 0x50, 0x61, 0x74, 0x68, 0x12, 0x29, 0x0a, 0x03, 0x6e, 0x66, 0x73, 0x18, 0x04, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4e, 0x46, 0x53, 0x56, + 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x03, 0x6e, 0x66, 0x73, + 0x12, 0x59, 0x0a, 0x11, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, + 0x62, 0x75, 0x74, 0x65, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2c, 0x2e, 0x61, 0x70, + 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x56, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x53, 0x6f, 0x75, + 0x72, 0x63, 0x65, 0x2e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, + 0x75, 0x74, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x10, 0x76, 0x6f, 0x6c, 0x75, 0x6d, + 0x65, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x1a, 0x43, 0x0a, 0x15, 0x56, + 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, - 0x22, 0x90, 0x01, 0x0a, 0x12, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x4e, 0x6f, 0x64, 0x65, 0x41, - 0x66, 0x66, 0x69, 0x6e, 0x69, 0x74, 0x79, 0x12, 0x3b, 0x0a, 0x08, 0x72, 0x65, 0x71, 0x75, 0x69, - 0x72, 0x65, 0x64, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x61, 0x70, 0x69, 0x2e, - 0x76, 0x31, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x52, - 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x08, 0x72, 0x65, 0x71, 0x75, - 0x69, 0x72, 0x65, 0x64, 0x12, 0x3d, 0x0a, 0x09, 0x70, 0x72, 0x65, 0x66, 0x65, 0x72, 0x72, 0x65, - 0x64, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, - 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x52, 0x65, 0x71, - 0x75, 0x69, 0x72, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x09, 0x70, 0x72, 0x65, 0x66, 0x65, 0x72, - 0x72, 0x65, 0x64, 0x22, 0xc6, 0x01, 0x0a, 0x0c, 0x50, 0x56, 0x43, 0x43, 0x6f, 0x6e, 0x64, 0x69, - 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, - 0x75, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, - 0x12, 0x16, 0x0a, 0x06, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x06, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, - 0x61, 0x67, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, - 0x67, 0x65, 0x12, 0x26, 0x0a, 0x0f, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x70, 0x72, 0x6f, 0x62, 0x65, - 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0d, 0x6c, 0x61, 0x73, - 0x74, 0x50, 0x72, 0x6f, 0x62, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x30, 0x0a, 0x14, 0x6c, 0x61, - 0x73, 0x74, 0x5f, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x74, 0x69, - 0x6d, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x03, 0x52, 0x12, 0x6c, 0x61, 0x73, 0x74, 0x54, 0x72, - 0x61, 0x6e, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x69, 0x6d, 0x65, 0x22, 0xf5, 0x02, 0x0a, - 0x17, 0x50, 0x65, 0x72, 0x73, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x74, 0x56, 0x6f, 0x6c, 0x75, 0x6d, - 0x65, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x49, 0x0a, 0x08, 0x63, 0x61, 0x70, 0x61, - 0x63, 0x69, 0x74, 0x79, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2d, 0x2e, 0x61, 0x70, 0x69, - 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x65, 0x72, 0x73, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x74, 0x56, 0x6f, - 0x6c, 0x75, 0x6d, 0x65, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x2e, 0x43, 0x61, 0x70, 0x61, - 0x63, 0x69, 0x74, 0x79, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x08, 0x63, 0x61, 0x70, 0x61, 0x63, - 0x69, 0x74, 0x79, 0x12, 0x35, 0x0a, 0x09, 0x63, 0x6c, 0x61, 0x69, 0x6d, 0x5f, 0x72, 0x65, 0x66, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, - 0x50, 0x56, 0x43, 0x6c, 0x61, 0x69, 0x6d, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, - 0x52, 0x08, 0x63, 0x6c, 0x61, 0x69, 0x6d, 0x52, 0x65, 0x66, 0x12, 0x3b, 0x0a, 0x0d, 0x76, 0x6f, - 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x16, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x56, 0x56, 0x6f, 0x6c, - 0x75, 0x6d, 0x65, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x0c, 0x76, 0x6f, 0x6c, 0x75, 0x6d, - 0x65, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x39, 0x0a, 0x0d, 0x6e, 0x6f, 0x64, 0x65, 0x5f, - 0x61, 0x66, 0x66, 0x69, 0x6e, 0x69, 0x74, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, - 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x53, 0x65, 0x6c, 0x65, - 0x63, 0x74, 0x6f, 0x72, 0x52, 0x0c, 0x6e, 0x6f, 0x64, 0x65, 0x41, 0x66, 0x66, 0x69, 0x6e, 0x69, - 0x74, 0x79, 0x12, 0x23, 0x0a, 0x0d, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x6f, 0x70, 0x74, 0x69, - 0x6f, 0x6e, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0c, 0x6d, 0x6f, 0x75, 0x6e, 0x74, - 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x1a, 0x3b, 0x0a, 0x0d, 0x43, 0x61, 0x70, 0x61, 0x63, - 0x69, 0x74, 0x79, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, - 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, - 0x3a, 0x02, 0x38, 0x01, 0x22, 0xa2, 0x01, 0x0a, 0x10, 0x50, 0x56, 0x43, 0x6c, 0x61, 0x69, 0x6d, - 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, - 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1c, 0x0a, - 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x75, - 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x69, 0x64, 0x12, 0x1f, 0x0a, - 0x0b, 0x61, 0x70, 0x69, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x0a, 0x61, 0x70, 0x69, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x29, - 0x0a, 0x10, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, - 0x6f, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, - 0x63, 0x65, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0xd5, 0x02, 0x0a, 0x0e, 0x50, 0x56, - 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x12, 0x0a, 0x04, - 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, - 0x12, 0x29, 0x0a, 0x03, 0x63, 0x73, 0x69, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, - 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x53, 0x49, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, - 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x03, 0x63, 0x73, 0x69, 0x12, 0x39, 0x0a, 0x09, 0x68, - 0x6f, 0x73, 0x74, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, - 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x48, 0x6f, 0x73, 0x74, 0x50, 0x61, 0x74, 0x68, - 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x08, 0x68, 0x6f, - 0x73, 0x74, 0x50, 0x61, 0x74, 0x68, 0x12, 0x29, 0x0a, 0x03, 0x6e, 0x66, 0x73, 0x18, 0x04, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4e, 0x46, 0x53, - 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x03, 0x6e, 0x66, - 0x73, 0x12, 0x59, 0x0a, 0x11, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x61, 0x74, 0x74, 0x72, - 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2c, 0x2e, 0x61, - 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x56, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x53, 0x6f, - 0x75, 0x72, 0x63, 0x65, 0x2e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x41, 0x74, 0x74, 0x72, 0x69, - 0x62, 0x75, 0x74, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x10, 0x76, 0x6f, 0x6c, 0x75, - 0x6d, 0x65, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x1a, 0x43, 0x0a, 0x15, - 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, - 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, - 0x01, 0x22, 0xa5, 0x02, 0x0a, 0x0f, 0x43, 0x53, 0x49, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x53, - 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x64, 0x72, 0x69, 0x76, 0x65, 0x72, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x64, 0x72, 0x69, 0x76, 0x65, 0x72, 0x12, 0x23, 0x0a, - 0x0d, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x48, 0x61, 0x6e, 0x64, - 0x6c, 0x65, 0x12, 0x17, 0x0a, 0x07, 0x66, 0x73, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x03, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x06, 0x66, 0x73, 0x54, 0x79, 0x70, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x72, - 0x65, 0x61, 0x64, 0x5f, 0x6f, 0x6e, 0x6c, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, - 0x72, 0x65, 0x61, 0x64, 0x4f, 0x6e, 0x6c, 0x79, 0x12, 0x5a, 0x0a, 0x11, 0x76, 0x6f, 0x6c, 0x75, - 0x6d, 0x65, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x18, 0x05, 0x20, - 0x03, 0x28, 0x0b, 0x32, 0x2d, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x53, 0x49, - 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x56, 0x6f, 0x6c, - 0x75, 0x6d, 0x65, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x45, 0x6e, 0x74, - 0x72, 0x79, 0x52, 0x10, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, - 0x75, 0x74, 0x65, 0x73, 0x1a, 0x43, 0x0a, 0x15, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x41, 0x74, - 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, + 0x22, 0xa5, 0x02, 0x0a, 0x0f, 0x43, 0x53, 0x49, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x53, 0x6f, + 0x75, 0x72, 0x63, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x64, 0x72, 0x69, 0x76, 0x65, 0x72, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x64, 0x72, 0x69, 0x76, 0x65, 0x72, 0x12, 0x23, 0x0a, 0x0d, + 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x0c, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x48, 0x61, 0x6e, 0x64, 0x6c, + 0x65, 0x12, 0x17, 0x0a, 0x07, 0x66, 0x73, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x06, 0x66, 0x73, 0x54, 0x79, 0x70, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x72, 0x65, + 0x61, 0x64, 0x5f, 0x6f, 0x6e, 0x6c, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x72, + 0x65, 0x61, 0x64, 0x4f, 0x6e, 0x6c, 0x79, 0x12, 0x5a, 0x0a, 0x11, 0x76, 0x6f, 0x6c, 0x75, 0x6d, + 0x65, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x18, 0x05, 0x20, 0x03, + 0x28, 0x0b, 0x32, 0x2d, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x53, 0x49, 0x56, + 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x56, 0x6f, 0x6c, 0x75, + 0x6d, 0x65, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, + 0x79, 0x52, 0x10, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, + 0x74, 0x65, 0x73, 0x1a, 0x43, 0x0a, 0x15, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x41, 0x74, 0x74, + 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, + 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, + 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, + 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x3e, 0x0a, 0x14, 0x48, 0x6f, 0x73, 0x74, + 0x50, 0x61, 0x74, 0x68, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, + 0x12, 0x12, 0x0a, 0x04, 0x70, 0x61, 0x74, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, + 0x70, 0x61, 0x74, 0x68, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x22, 0x5a, 0x0a, 0x0f, 0x4e, 0x46, 0x53, 0x56, + 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x73, + 0x65, 0x72, 0x76, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x65, 0x72, + 0x76, 0x65, 0x72, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x61, 0x74, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x04, 0x70, 0x61, 0x74, 0x68, 0x12, 0x1b, 0x0a, 0x09, 0x72, 0x65, 0x61, 0x64, 0x5f, + 0x6f, 0x6e, 0x6c, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x72, 0x65, 0x61, 0x64, + 0x4f, 0x6e, 0x6c, 0x79, 0x22, 0xbe, 0x03, 0x0a, 0x13, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, + 0x43, 0x6c, 0x61, 0x73, 0x73, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x20, 0x0a, 0x0b, + 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x0b, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x12, 0x4b, + 0x0a, 0x0a, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x18, 0x02, 0x20, 0x03, + 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x74, 0x6f, 0x72, + 0x61, 0x67, 0x65, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x2e, + 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, + 0x0a, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x12, 0x47, 0x0a, 0x12, 0x61, + 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x5f, 0x74, 0x6f, 0x70, 0x6f, 0x6c, 0x6f, 0x67, 0x69, 0x65, + 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, + 0x2e, 0x54, 0x6f, 0x70, 0x6f, 0x6c, 0x6f, 0x67, 0x79, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, + 0x72, 0x52, 0x11, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x54, 0x6f, 0x70, 0x6f, 0x6c, 0x6f, + 0x67, 0x69, 0x65, 0x73, 0x12, 0x23, 0x0a, 0x0d, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x6f, 0x70, + 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0c, 0x6d, 0x6f, 0x75, + 0x6e, 0x74, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x2e, 0x0a, 0x13, 0x76, 0x6f, 0x6c, + 0x75, 0x6d, 0x65, 0x5f, 0x62, 0x69, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x5f, 0x6d, 0x6f, 0x64, 0x65, + 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x11, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x42, 0x69, + 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x4d, 0x6f, 0x64, 0x65, 0x12, 0x25, 0x0a, 0x0e, 0x72, 0x65, 0x63, + 0x6c, 0x61, 0x69, 0x6d, 0x5f, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x18, 0x06, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x0d, 0x72, 0x65, 0x63, 0x6c, 0x61, 0x69, 0x6d, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, + 0x12, 0x34, 0x0a, 0x16, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x5f, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, + 0x5f, 0x65, 0x78, 0x70, 0x61, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x07, 0x20, 0x01, 0x28, 0x08, + 0x52, 0x14, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x45, 0x78, 0x70, + 0x61, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x1a, 0x3d, 0x0a, 0x0f, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, + 0x74, 0x65, 0x72, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, + 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, + 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0xd1, 0x01, 0x0a, 0x10, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, + 0x61, 0x63, 0x65, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x70, 0x68, + 0x61, 0x73, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x70, 0x68, 0x61, 0x73, 0x65, + 0x12, 0x1e, 0x0a, 0x0a, 0x66, 0x69, 0x6e, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x72, 0x73, 0x18, 0x02, + 0x20, 0x03, 0x28, 0x09, 0x52, 0x0a, 0x66, 0x69, 0x6e, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x72, 0x73, + 0x12, 0x48, 0x0a, 0x0a, 0x63, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x03, + 0x20, 0x03, 0x28, 0x0b, 0x32, 0x28, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4e, 0x61, + 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x2e, 0x43, + 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0a, + 0x63, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x1a, 0x3d, 0x0a, 0x0f, 0x43, 0x6f, + 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, - 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x3e, 0x0a, 0x14, 0x48, 0x6f, 0x73, - 0x74, 0x50, 0x61, 0x74, 0x68, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x53, 0x6f, 0x75, 0x72, 0x63, - 0x65, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x61, 0x74, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x04, 0x70, 0x61, 0x74, 0x68, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x22, 0x5a, 0x0a, 0x0f, 0x4e, 0x46, 0x53, - 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x16, 0x0a, 0x06, - 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x65, - 0x72, 0x76, 0x65, 0x72, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x61, 0x74, 0x68, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x04, 0x70, 0x61, 0x74, 0x68, 0x12, 0x1b, 0x0a, 0x09, 0x72, 0x65, 0x61, 0x64, - 0x5f, 0x6f, 0x6e, 0x6c, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x72, 0x65, 0x61, - 0x64, 0x4f, 0x6e, 0x6c, 0x79, 0x22, 0xbe, 0x03, 0x0a, 0x13, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, - 0x65, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x20, 0x0a, - 0x0b, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x0b, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x12, - 0x4b, 0x0a, 0x0a, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x18, 0x02, 0x20, - 0x03, 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x74, 0x6f, - 0x72, 0x61, 0x67, 0x65, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, - 0x2e, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, - 0x52, 0x0a, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x12, 0x47, 0x0a, 0x12, - 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x5f, 0x74, 0x6f, 0x70, 0x6f, 0x6c, 0x6f, 0x67, 0x69, - 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, - 0x31, 0x2e, 0x54, 0x6f, 0x70, 0x6f, 0x6c, 0x6f, 0x67, 0x79, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, - 0x6f, 0x72, 0x52, 0x11, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x54, 0x6f, 0x70, 0x6f, 0x6c, - 0x6f, 0x67, 0x69, 0x65, 0x73, 0x12, 0x23, 0x0a, 0x0d, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x6f, - 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0c, 0x6d, 0x6f, - 0x75, 0x6e, 0x74, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x2e, 0x0a, 0x13, 0x76, 0x6f, - 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x62, 0x69, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x5f, 0x6d, 0x6f, 0x64, - 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x11, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x42, - 0x69, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x4d, 0x6f, 0x64, 0x65, 0x12, 0x25, 0x0a, 0x0e, 0x72, 0x65, - 0x63, 0x6c, 0x61, 0x69, 0x6d, 0x5f, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x18, 0x06, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x0d, 0x72, 0x65, 0x63, 0x6c, 0x61, 0x69, 0x6d, 0x50, 0x6f, 0x6c, 0x69, 0x63, - 0x79, 0x12, 0x34, 0x0a, 0x16, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x5f, 0x76, 0x6f, 0x6c, 0x75, 0x6d, - 0x65, 0x5f, 0x65, 0x78, 0x70, 0x61, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x07, 0x20, 0x01, 0x28, - 0x08, 0x52, 0x14, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x45, 0x78, - 0x70, 0x61, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x1a, 0x3d, 0x0a, 0x0f, 0x50, 0x61, 0x72, 0x61, 0x6d, - 0x65, 0x74, 0x65, 0x72, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, - 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, - 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, - 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0xd1, 0x01, 0x0a, 0x10, 0x4e, 0x61, 0x6d, 0x65, 0x73, - 0x70, 0x61, 0x63, 0x65, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x70, - 0x68, 0x61, 0x73, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x70, 0x68, 0x61, 0x73, - 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x66, 0x69, 0x6e, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x72, 0x73, 0x18, - 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0a, 0x66, 0x69, 0x6e, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x72, - 0x73, 0x12, 0x48, 0x0a, 0x0a, 0x63, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, - 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x28, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4e, - 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x2e, - 0x43, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, - 0x0a, 0x63, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x1a, 0x3d, 0x0a, 0x0f, 0x43, - 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, - 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, - 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0xb7, 0x04, 0x0a, 0x0b, 0x4e, - 0x6f, 0x64, 0x65, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x31, 0x0a, 0x09, 0x61, 0x64, - 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, - 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x41, 0x64, 0x64, 0x72, 0x65, - 0x73, 0x73, 0x52, 0x09, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x12, 0x35, 0x0a, - 0x0a, 0x63, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, - 0x0b, 0x32, 0x15, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x43, - 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0a, 0x63, 0x6f, 0x6e, 0x64, 0x69, 0x74, - 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x37, 0x0a, 0x0b, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x5f, 0x69, - 0x6e, 0x66, 0x6f, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x61, 0x70, 0x69, 0x2e, - 0x76, 0x31, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x49, 0x6e, 0x66, - 0x6f, 0x52, 0x0a, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x3d, 0x0a, - 0x08, 0x63, 0x61, 0x70, 0x61, 0x63, 0x69, 0x74, 0x79, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, - 0x21, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x44, 0x65, 0x74, - 0x61, 0x69, 0x6c, 0x73, 0x2e, 0x43, 0x61, 0x70, 0x61, 0x63, 0x69, 0x74, 0x79, 0x45, 0x6e, 0x74, - 0x72, 0x79, 0x52, 0x08, 0x63, 0x61, 0x70, 0x61, 0x63, 0x69, 0x74, 0x79, 0x12, 0x46, 0x0a, 0x0b, - 0x61, 0x6c, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x18, 0x05, 0x20, 0x03, 0x28, - 0x0b, 0x32, 0x24, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x44, - 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x2e, 0x41, 0x6c, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x61, 0x62, - 0x6c, 0x65, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0b, 0x61, 0x6c, 0x6c, 0x6f, 0x63, 0x61, 0x74, - 0x61, 0x62, 0x6c, 0x65, 0x12, 0x29, 0x0a, 0x06, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x73, 0x18, 0x06, - 0x20, 0x03, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4e, 0x6f, - 0x64, 0x65, 0x49, 0x6d, 0x61, 0x67, 0x65, 0x52, 0x06, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x73, 0x12, - 0x2c, 0x0a, 0x12, 0x6d, 0x69, 0x67, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x64, 0x65, - 0x73, 0x69, 0x72, 0x65, 0x64, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x6d, 0x69, 0x67, - 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x44, 0x65, 0x73, 0x69, 0x72, 0x65, 0x64, 0x12, 0x28, 0x0a, - 0x10, 0x6d, 0x69, 0x67, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x73, 0x74, 0x61, 0x74, - 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x6d, 0x69, 0x67, 0x43, 0x6f, 0x6e, 0x66, - 0x69, 0x67, 0x53, 0x74, 0x61, 0x74, 0x65, 0x1a, 0x3b, 0x0a, 0x0d, 0x43, 0x61, 0x70, 0x61, 0x63, - 0x69, 0x74, 0x79, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, - 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, - 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x3e, 0x0a, 0x10, 0x41, 0x6c, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x61, - 0x62, 0x6c, 0x65, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, - 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, - 0x3a, 0x02, 0x38, 0x01, 0x22, 0x6a, 0x0a, 0x09, 0x4e, 0x6f, 0x64, 0x65, 0x54, 0x61, 0x69, 0x6e, - 0x74, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, - 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x65, 0x66, 0x66, - 0x65, 0x63, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x65, 0x66, 0x66, 0x65, 0x63, - 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x61, 0x64, 0x64, 0x65, 0x64, 0x18, - 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x41, 0x64, 0x64, 0x65, 0x64, - 0x22, 0x3b, 0x0a, 0x0b, 0x4e, 0x6f, 0x64, 0x65, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, - 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, - 0x79, 0x70, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x22, 0xcf, 0x01, - 0x0a, 0x0d, 0x4e, 0x6f, 0x64, 0x65, 0x43, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x12, - 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, - 0x79, 0x70, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x72, - 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x72, 0x65, 0x61, - 0x73, 0x6f, 0x6e, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x04, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x2e, 0x0a, - 0x13, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x68, 0x65, 0x61, 0x72, 0x74, 0x62, 0x65, 0x61, 0x74, 0x5f, - 0x74, 0x69, 0x6d, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x11, 0x6c, 0x61, 0x73, 0x74, - 0x48, 0x65, 0x61, 0x72, 0x74, 0x62, 0x65, 0x61, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x30, 0x0a, - 0x14, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, - 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x03, 0x52, 0x12, 0x6c, 0x61, 0x73, - 0x74, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x69, 0x6d, 0x65, 0x22, - 0x8d, 0x03, 0x0a, 0x0e, 0x4e, 0x6f, 0x64, 0x65, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x49, 0x6e, - 0x66, 0x6f, 0x12, 0x1d, 0x0a, 0x0a, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x5f, 0x69, 0x64, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x49, - 0x64, 0x12, 0x1f, 0x0a, 0x0b, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x5f, 0x75, 0x75, 0x69, 0x64, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x55, 0x75, - 0x69, 0x64, 0x12, 0x17, 0x0a, 0x07, 0x62, 0x6f, 0x6f, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x06, 0x62, 0x6f, 0x6f, 0x74, 0x49, 0x64, 0x12, 0x25, 0x0a, 0x0e, 0x6b, - 0x65, 0x72, 0x6e, 0x65, 0x6c, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x0d, 0x6b, 0x65, 0x72, 0x6e, 0x65, 0x6c, 0x56, 0x65, 0x72, 0x73, 0x69, - 0x6f, 0x6e, 0x12, 0x19, 0x0a, 0x08, 0x6f, 0x73, 0x5f, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x18, 0x05, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6f, 0x73, 0x49, 0x6d, 0x61, 0x67, 0x65, 0x12, 0x3a, 0x0a, - 0x19, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x72, 0x75, 0x6e, 0x74, 0x69, - 0x6d, 0x65, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x17, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x52, 0x75, 0x6e, 0x74, 0x69, - 0x6d, 0x65, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x27, 0x0a, 0x0f, 0x6b, 0x75, 0x62, - 0x65, 0x6c, 0x65, 0x74, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x07, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x0e, 0x6b, 0x75, 0x62, 0x65, 0x6c, 0x65, 0x74, 0x56, 0x65, 0x72, 0x73, 0x69, - 0x6f, 0x6e, 0x12, 0x2c, 0x0a, 0x12, 0x6b, 0x75, 0x62, 0x65, 0x5f, 0x70, 0x72, 0x6f, 0x78, 0x79, - 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, - 0x6b, 0x75, 0x62, 0x65, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, - 0x12, 0x29, 0x0a, 0x10, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6e, 0x67, 0x5f, 0x73, 0x79, - 0x73, 0x74, 0x65, 0x6d, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x6f, 0x70, 0x65, 0x72, - 0x61, 0x74, 0x69, 0x6e, 0x67, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x12, 0x22, 0x0a, 0x0c, 0x61, - 0x72, 0x63, 0x68, 0x69, 0x74, 0x65, 0x63, 0x74, 0x75, 0x72, 0x65, 0x18, 0x0a, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x0c, 0x61, 0x72, 0x63, 0x68, 0x69, 0x74, 0x65, 0x63, 0x74, 0x75, 0x72, 0x65, 0x22, - 0x40, 0x0a, 0x09, 0x4e, 0x6f, 0x64, 0x65, 0x49, 0x6d, 0x61, 0x67, 0x65, 0x12, 0x14, 0x0a, 0x05, - 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x05, 0x6e, 0x61, 0x6d, - 0x65, 0x73, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x69, 0x7a, 0x65, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x73, 0x69, 0x7a, 0x65, 0x42, 0x79, 0x74, 0x65, - 0x73, 0x22, 0x68, 0x0a, 0x10, 0x54, 0x6f, 0x70, 0x6f, 0x6c, 0x6f, 0x67, 0x79, 0x53, 0x65, 0x6c, - 0x65, 0x63, 0x74, 0x6f, 0x72, 0x12, 0x54, 0x0a, 0x17, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x5f, 0x6c, - 0x61, 0x62, 0x65, 0x6c, 0x5f, 0x65, 0x78, 0x70, 0x72, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, - 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, - 0x54, 0x6f, 0x70, 0x6f, 0x6c, 0x6f, 0x67, 0x79, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, - 0x54, 0x65, 0x72, 0x6d, 0x52, 0x15, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x4c, 0x61, 0x62, 0x65, 0x6c, - 0x45, 0x78, 0x70, 0x72, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0xa8, 0x01, 0x0a, 0x14, - 0x54, 0x6f, 0x70, 0x6f, 0x6c, 0x6f, 0x67, 0x79, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, - 0x54, 0x65, 0x72, 0x6d, 0x12, 0x50, 0x0a, 0x0c, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x5f, 0x6c, 0x61, - 0x62, 0x65, 0x6c, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2d, 0x2e, 0x61, 0x70, 0x69, - 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x6f, 0x70, 0x6f, 0x6c, 0x6f, 0x67, 0x79, 0x53, 0x65, 0x6c, 0x65, - 0x63, 0x74, 0x6f, 0x72, 0x54, 0x65, 0x72, 0x6d, 0x2e, 0x4d, 0x61, 0x74, 0x63, 0x68, 0x4c, 0x61, - 0x62, 0x65, 0x6c, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0b, 0x6d, 0x61, 0x74, 0x63, 0x68, - 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x1a, 0x3e, 0x0a, 0x10, 0x4d, 0x61, 0x74, 0x63, 0x68, 0x4c, - 0x61, 0x62, 0x65, 0x6c, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, - 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, - 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, - 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x9b, 0x01, 0x0a, 0x0e, 0x54, 0x6f, 0x6c, 0x65, 0x72, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x1a, 0x0a, 0x08, 0x6f, - 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6f, - 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, - 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x16, 0x0a, - 0x06, 0x65, 0x66, 0x66, 0x65, 0x63, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x65, - 0x66, 0x66, 0x65, 0x63, 0x74, 0x12, 0x2d, 0x0a, 0x12, 0x74, 0x6f, 0x6c, 0x65, 0x72, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x5f, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, - 0x03, 0x52, 0x11, 0x74, 0x6f, 0x6c, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x65, 0x63, - 0x6f, 0x6e, 0x64, 0x73, 0x22, 0x3e, 0x0a, 0x0c, 0x4e, 0x6f, 0x64, 0x65, 0x53, 0x65, 0x6c, 0x65, - 0x63, 0x74, 0x6f, 0x72, 0x12, 0x2e, 0x0a, 0x05, 0x74, 0x65, 0x72, 0x6d, 0x73, 0x18, 0x01, 0x20, - 0x03, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4e, 0x6f, 0x64, - 0x65, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x54, 0x65, 0x72, 0x6d, 0x52, 0x05, 0x74, - 0x65, 0x72, 0x6d, 0x73, 0x22, 0xa4, 0x01, 0x0a, 0x10, 0x4e, 0x6f, 0x64, 0x65, 0x53, 0x65, 0x6c, - 0x65, 0x63, 0x74, 0x6f, 0x72, 0x54, 0x65, 0x72, 0x6d, 0x12, 0x4c, 0x0a, 0x11, 0x6d, 0x61, 0x74, - 0x63, 0x68, 0x5f, 0x65, 0x78, 0x70, 0x72, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x01, - 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4e, 0x6f, + 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0xb7, 0x04, 0x0a, 0x0b, 0x4e, 0x6f, + 0x64, 0x65, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x31, 0x0a, 0x09, 0x61, 0x64, 0x64, + 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x61, + 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, + 0x73, 0x52, 0x09, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x12, 0x35, 0x0a, 0x0a, + 0x63, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, + 0x32, 0x15, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x43, 0x6f, + 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0a, 0x63, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, + 0x6f, 0x6e, 0x73, 0x12, 0x37, 0x0a, 0x0b, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x5f, 0x69, 0x6e, + 0x66, 0x6f, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, + 0x31, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x49, 0x6e, 0x66, 0x6f, + 0x52, 0x0a, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x3d, 0x0a, 0x08, + 0x63, 0x61, 0x70, 0x61, 0x63, 0x69, 0x74, 0x79, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x21, + 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x44, 0x65, 0x74, 0x61, + 0x69, 0x6c, 0x73, 0x2e, 0x43, 0x61, 0x70, 0x61, 0x63, 0x69, 0x74, 0x79, 0x45, 0x6e, 0x74, 0x72, + 0x79, 0x52, 0x08, 0x63, 0x61, 0x70, 0x61, 0x63, 0x69, 0x74, 0x79, 0x12, 0x46, 0x0a, 0x0b, 0x61, + 0x6c, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, + 0x32, 0x24, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x44, 0x65, + 0x74, 0x61, 0x69, 0x6c, 0x73, 0x2e, 0x41, 0x6c, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x61, 0x62, 0x6c, + 0x65, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0b, 0x61, 0x6c, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x61, + 0x62, 0x6c, 0x65, 0x12, 0x29, 0x0a, 0x06, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x73, 0x18, 0x06, 0x20, + 0x03, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4e, 0x6f, 0x64, + 0x65, 0x49, 0x6d, 0x61, 0x67, 0x65, 0x52, 0x06, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x73, 0x12, 0x2c, + 0x0a, 0x12, 0x6d, 0x69, 0x67, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x64, 0x65, 0x73, + 0x69, 0x72, 0x65, 0x64, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x6d, 0x69, 0x67, 0x43, + 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x44, 0x65, 0x73, 0x69, 0x72, 0x65, 0x64, 0x12, 0x28, 0x0a, 0x10, + 0x6d, 0x69, 0x67, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x65, + 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x6d, 0x69, 0x67, 0x43, 0x6f, 0x6e, 0x66, 0x69, + 0x67, 0x53, 0x74, 0x61, 0x74, 0x65, 0x1a, 0x3b, 0x0a, 0x0d, 0x43, 0x61, 0x70, 0x61, 0x63, 0x69, + 0x74, 0x79, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, + 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, + 0x02, 0x38, 0x01, 0x1a, 0x3e, 0x0a, 0x10, 0x41, 0x6c, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x61, 0x62, + 0x6c, 0x65, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, + 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, + 0x02, 0x38, 0x01, 0x22, 0x6a, 0x0a, 0x09, 0x4e, 0x6f, 0x64, 0x65, 0x54, 0x61, 0x69, 0x6e, 0x74, + 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, + 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x65, 0x66, 0x66, 0x65, + 0x63, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x65, 0x66, 0x66, 0x65, 0x63, 0x74, + 0x12, 0x1d, 0x0a, 0x0a, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x61, 0x64, 0x64, 0x65, 0x64, 0x18, 0x04, + 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x41, 0x64, 0x64, 0x65, 0x64, 0x22, + 0x3b, 0x0a, 0x0b, 0x4e, 0x6f, 0x64, 0x65, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x12, + 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, + 0x70, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x22, 0xcf, 0x01, 0x0a, + 0x0d, 0x4e, 0x6f, 0x64, 0x65, 0x43, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x12, + 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, + 0x70, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x72, 0x65, + 0x61, 0x73, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x72, 0x65, 0x61, 0x73, + 0x6f, 0x6e, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x04, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x2e, 0x0a, 0x13, + 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x68, 0x65, 0x61, 0x72, 0x74, 0x62, 0x65, 0x61, 0x74, 0x5f, 0x74, + 0x69, 0x6d, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x11, 0x6c, 0x61, 0x73, 0x74, 0x48, + 0x65, 0x61, 0x72, 0x74, 0x62, 0x65, 0x61, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x30, 0x0a, 0x14, + 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x5f, + 0x74, 0x69, 0x6d, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x03, 0x52, 0x12, 0x6c, 0x61, 0x73, 0x74, + 0x54, 0x72, 0x61, 0x6e, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x69, 0x6d, 0x65, 0x22, 0x8d, + 0x03, 0x0a, 0x0e, 0x4e, 0x6f, 0x64, 0x65, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x49, 0x6e, 0x66, + 0x6f, 0x12, 0x1d, 0x0a, 0x0a, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x5f, 0x69, 0x64, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x49, 0x64, + 0x12, 0x1f, 0x0a, 0x0b, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x5f, 0x75, 0x75, 0x69, 0x64, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x55, 0x75, 0x69, + 0x64, 0x12, 0x17, 0x0a, 0x07, 0x62, 0x6f, 0x6f, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x06, 0x62, 0x6f, 0x6f, 0x74, 0x49, 0x64, 0x12, 0x25, 0x0a, 0x0e, 0x6b, 0x65, + 0x72, 0x6e, 0x65, 0x6c, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x0d, 0x6b, 0x65, 0x72, 0x6e, 0x65, 0x6c, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, + 0x6e, 0x12, 0x19, 0x0a, 0x08, 0x6f, 0x73, 0x5f, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x18, 0x05, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x07, 0x6f, 0x73, 0x49, 0x6d, 0x61, 0x67, 0x65, 0x12, 0x3a, 0x0a, 0x19, + 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, + 0x65, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x17, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x52, 0x75, 0x6e, 0x74, 0x69, 0x6d, + 0x65, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x27, 0x0a, 0x0f, 0x6b, 0x75, 0x62, 0x65, + 0x6c, 0x65, 0x74, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x07, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x0e, 0x6b, 0x75, 0x62, 0x65, 0x6c, 0x65, 0x74, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, + 0x6e, 0x12, 0x2c, 0x0a, 0x12, 0x6b, 0x75, 0x62, 0x65, 0x5f, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x5f, + 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x6b, + 0x75, 0x62, 0x65, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, + 0x29, 0x0a, 0x10, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6e, 0x67, 0x5f, 0x73, 0x79, 0x73, + 0x74, 0x65, 0x6d, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x6f, 0x70, 0x65, 0x72, 0x61, + 0x74, 0x69, 0x6e, 0x67, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x12, 0x22, 0x0a, 0x0c, 0x61, 0x72, + 0x63, 0x68, 0x69, 0x74, 0x65, 0x63, 0x74, 0x75, 0x72, 0x65, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x0c, 0x61, 0x72, 0x63, 0x68, 0x69, 0x74, 0x65, 0x63, 0x74, 0x75, 0x72, 0x65, 0x22, 0x40, + 0x0a, 0x09, 0x4e, 0x6f, 0x64, 0x65, 0x49, 0x6d, 0x61, 0x67, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x6e, + 0x61, 0x6d, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x05, 0x6e, 0x61, 0x6d, 0x65, + 0x73, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x69, 0x7a, 0x65, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x73, 0x69, 0x7a, 0x65, 0x42, 0x79, 0x74, 0x65, 0x73, + 0x22, 0x68, 0x0a, 0x10, 0x54, 0x6f, 0x70, 0x6f, 0x6c, 0x6f, 0x67, 0x79, 0x53, 0x65, 0x6c, 0x65, + 0x63, 0x74, 0x6f, 0x72, 0x12, 0x54, 0x0a, 0x17, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x5f, 0x6c, 0x61, + 0x62, 0x65, 0x6c, 0x5f, 0x65, 0x78, 0x70, 0x72, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x18, + 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x54, + 0x6f, 0x70, 0x6f, 0x6c, 0x6f, 0x67, 0x79, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x54, + 0x65, 0x72, 0x6d, 0x52, 0x15, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x45, + 0x78, 0x70, 0x72, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0xa8, 0x01, 0x0a, 0x14, 0x54, + 0x6f, 0x70, 0x6f, 0x6c, 0x6f, 0x67, 0x79, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x54, + 0x65, 0x72, 0x6d, 0x12, 0x50, 0x0a, 0x0c, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x5f, 0x6c, 0x61, 0x62, + 0x65, 0x6c, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2d, 0x2e, 0x61, 0x70, 0x69, 0x2e, + 0x76, 0x31, 0x2e, 0x54, 0x6f, 0x70, 0x6f, 0x6c, 0x6f, 0x67, 0x79, 0x53, 0x65, 0x6c, 0x65, 0x63, + 0x74, 0x6f, 0x72, 0x54, 0x65, 0x72, 0x6d, 0x2e, 0x4d, 0x61, 0x74, 0x63, 0x68, 0x4c, 0x61, 0x62, + 0x65, 0x6c, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0b, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x4c, + 0x61, 0x62, 0x65, 0x6c, 0x73, 0x1a, 0x3e, 0x0a, 0x10, 0x4d, 0x61, 0x74, 0x63, 0x68, 0x4c, 0x61, + 0x62, 0x65, 0x6c, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, + 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, + 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x9b, 0x01, 0x0a, 0x0e, 0x54, 0x6f, 0x6c, 0x65, 0x72, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x1a, 0x0a, 0x08, 0x6f, 0x70, + 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6f, 0x70, + 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x16, 0x0a, 0x06, + 0x65, 0x66, 0x66, 0x65, 0x63, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x65, 0x66, + 0x66, 0x65, 0x63, 0x74, 0x12, 0x2d, 0x0a, 0x12, 0x74, 0x6f, 0x6c, 0x65, 0x72, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x5f, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, + 0x52, 0x11, 0x74, 0x6f, 0x6c, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x65, 0x63, 0x6f, + 0x6e, 0x64, 0x73, 0x22, 0x3e, 0x0a, 0x0c, 0x4e, 0x6f, 0x64, 0x65, 0x53, 0x65, 0x6c, 0x65, 0x63, + 0x74, 0x6f, 0x72, 0x12, 0x2e, 0x0a, 0x05, 0x74, 0x65, 0x72, 0x6d, 0x73, 0x18, 0x01, 0x20, 0x03, + 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4e, 0x6f, 0x64, 0x65, + 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x54, 0x65, 0x72, 0x6d, 0x52, 0x05, 0x74, 0x65, + 0x72, 0x6d, 0x73, 0x22, 0xa4, 0x01, 0x0a, 0x10, 0x4e, 0x6f, 0x64, 0x65, 0x53, 0x65, 0x6c, 0x65, + 0x63, 0x74, 0x6f, 0x72, 0x54, 0x65, 0x72, 0x6d, 0x12, 0x4c, 0x0a, 0x11, 0x6d, 0x61, 0x74, 0x63, + 0x68, 0x5f, 0x65, 0x78, 0x70, 0x72, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x01, 0x20, + 0x03, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4e, 0x6f, 0x64, + 0x65, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x52, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, + 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x10, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x45, 0x78, 0x70, 0x72, 0x65, + 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x42, 0x0a, 0x0c, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x5f, + 0x66, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x61, + 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, + 0x6f, 0x72, 0x52, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x0b, 0x6d, + 0x61, 0x74, 0x63, 0x68, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x22, 0x5f, 0x0a, 0x17, 0x4e, 0x6f, 0x64, 0x65, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x52, 0x65, 0x71, 0x75, 0x69, 0x72, - 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x10, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x45, 0x78, 0x70, 0x72, - 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x42, 0x0a, 0x0c, 0x6d, 0x61, 0x74, 0x63, 0x68, - 0x5f, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1f, 0x2e, - 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x53, 0x65, 0x6c, 0x65, 0x63, - 0x74, 0x6f, 0x72, 0x52, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x0b, - 0x6d, 0x61, 0x74, 0x63, 0x68, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x22, 0x5f, 0x0a, 0x17, 0x4e, - 0x6f, 0x64, 0x65, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x52, 0x65, 0x71, 0x75, 0x69, - 0x72, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x1a, 0x0a, 0x08, 0x6f, 0x70, 0x65, 0x72, - 0x61, 0x74, 0x6f, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6f, 0x70, 0x65, 0x72, - 0x61, 0x74, 0x6f, 0x72, 0x12, 0x16, 0x0a, 0x06, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x18, 0x03, - 0x20, 0x03, 0x28, 0x09, 0x52, 0x06, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x22, 0xa5, 0x02, 0x0a, - 0x0a, 0x48, 0x50, 0x41, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x40, 0x0a, 0x10, 0x73, - 0x63, 0x61, 0x6c, 0x65, 0x5f, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x5f, 0x72, 0x65, 0x66, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x53, - 0x63, 0x61, 0x6c, 0x65, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x52, 0x65, 0x66, 0x52, 0x0e, 0x73, - 0x63, 0x61, 0x6c, 0x65, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x52, 0x65, 0x66, 0x12, 0x2b, 0x0a, - 0x07, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x11, - 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x48, 0x50, 0x41, 0x4d, 0x65, 0x74, 0x72, 0x69, - 0x63, 0x52, 0x07, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x12, 0x34, 0x0a, 0x0a, 0x63, 0x6f, - 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x14, - 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x48, 0x50, 0x41, 0x43, 0x6f, 0x6e, 0x64, 0x69, - 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0a, 0x63, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, - 0x12, 0x2f, 0x0a, 0x08, 0x62, 0x65, 0x68, 0x61, 0x76, 0x69, 0x6f, 0x72, 0x18, 0x04, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x48, 0x50, 0x41, 0x42, - 0x65, 0x68, 0x61, 0x76, 0x69, 0x6f, 0x72, 0x52, 0x08, 0x62, 0x65, 0x68, 0x61, 0x76, 0x69, 0x6f, - 0x72, 0x12, 0x41, 0x0a, 0x0f, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x6d, 0x65, 0x74, - 0x72, 0x69, 0x63, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x61, 0x70, 0x69, - 0x2e, 0x76, 0x31, 0x2e, 0x48, 0x50, 0x41, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x4d, 0x65, - 0x74, 0x72, 0x69, 0x63, 0x52, 0x0e, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x4d, 0x65, 0x74, - 0x72, 0x69, 0x63, 0x73, 0x22, 0x59, 0x0a, 0x0e, 0x53, 0x63, 0x61, 0x6c, 0x65, 0x54, 0x61, 0x72, - 0x67, 0x65, 0x74, 0x52, 0x65, 0x66, 0x12, 0x12, 0x0a, 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, - 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1f, - 0x0a, 0x0b, 0x61, 0x70, 0x69, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x0a, 0x61, 0x70, 0x69, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x22, - 0xe9, 0x01, 0x0a, 0x09, 0x48, 0x50, 0x41, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x12, 0x12, 0x0a, - 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, - 0x65, 0x12, 0x35, 0x0a, 0x08, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x48, 0x50, 0x41, - 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x52, 0x08, - 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x29, 0x0a, 0x04, 0x70, 0x6f, 0x64, 0x73, - 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, - 0x48, 0x50, 0x41, 0x50, 0x6f, 0x64, 0x73, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x52, 0x04, 0x70, - 0x6f, 0x64, 0x73, 0x12, 0x2f, 0x0a, 0x06, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x18, 0x04, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x48, 0x50, 0x41, - 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x52, 0x06, 0x6f, 0x62, - 0x6a, 0x65, 0x63, 0x74, 0x12, 0x35, 0x0a, 0x08, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, - 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, - 0x48, 0x50, 0x41, 0x45, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x4d, 0x65, 0x74, 0x72, 0x69, - 0x63, 0x52, 0x08, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x22, 0x6b, 0x0a, 0x11, 0x48, - 0x50, 0x41, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, - 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, - 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x5f, 0x74, - 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x74, 0x61, 0x72, 0x67, 0x65, - 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x5f, - 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x74, 0x61, 0x72, - 0x67, 0x65, 0x74, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x86, 0x01, 0x0a, 0x0d, 0x48, 0x50, 0x41, - 0x50, 0x6f, 0x64, 0x73, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x12, 0x31, 0x0a, 0x06, 0x6d, 0x65, - 0x74, 0x72, 0x69, 0x63, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x61, 0x70, 0x69, - 0x2e, 0x76, 0x31, 0x2e, 0x48, 0x50, 0x41, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x53, 0x65, 0x6c, - 0x65, 0x63, 0x74, 0x6f, 0x72, 0x52, 0x06, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x12, 0x1f, 0x0a, - 0x0b, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x0a, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x21, - 0x0a, 0x0c, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x03, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x56, 0x61, 0x6c, 0x75, - 0x65, 0x22, 0xcf, 0x01, 0x0a, 0x0f, 0x48, 0x50, 0x41, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x4d, - 0x65, 0x74, 0x72, 0x69, 0x63, 0x12, 0x45, 0x0a, 0x10, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x62, - 0x65, 0x64, 0x5f, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x1a, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x48, 0x50, 0x41, 0x4f, 0x62, 0x6a, 0x65, - 0x63, 0x74, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x52, 0x0f, 0x64, 0x65, 0x73, - 0x63, 0x72, 0x69, 0x62, 0x65, 0x64, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x12, 0x31, 0x0a, 0x06, - 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x61, - 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x48, 0x50, 0x41, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x53, - 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x52, 0x06, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x12, - 0x1f, 0x0a, 0x0b, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x03, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x54, 0x79, 0x70, 0x65, - 0x12, 0x21, 0x0a, 0x0c, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, - 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x56, 0x61, - 0x6c, 0x75, 0x65, 0x22, 0x8a, 0x01, 0x0a, 0x11, 0x48, 0x50, 0x41, 0x45, 0x78, 0x74, 0x65, 0x72, - 0x6e, 0x61, 0x6c, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x12, 0x31, 0x0a, 0x06, 0x6d, 0x65, 0x74, + 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x1a, 0x0a, 0x08, 0x6f, 0x70, 0x65, 0x72, 0x61, + 0x74, 0x6f, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6f, 0x70, 0x65, 0x72, 0x61, + 0x74, 0x6f, 0x72, 0x12, 0x16, 0x0a, 0x06, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x18, 0x03, 0x20, + 0x03, 0x28, 0x09, 0x52, 0x06, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x22, 0xa5, 0x02, 0x0a, 0x0a, + 0x48, 0x50, 0x41, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x40, 0x0a, 0x10, 0x73, 0x63, + 0x61, 0x6c, 0x65, 0x5f, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x5f, 0x72, 0x65, 0x66, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x63, + 0x61, 0x6c, 0x65, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x52, 0x65, 0x66, 0x52, 0x0e, 0x73, 0x63, + 0x61, 0x6c, 0x65, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x52, 0x65, 0x66, 0x12, 0x2b, 0x0a, 0x07, + 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x11, 0x2e, + 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x48, 0x50, 0x41, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, + 0x52, 0x07, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x12, 0x34, 0x0a, 0x0a, 0x63, 0x6f, 0x6e, + 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x14, 0x2e, + 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x48, 0x50, 0x41, 0x43, 0x6f, 0x6e, 0x64, 0x69, 0x74, + 0x69, 0x6f, 0x6e, 0x52, 0x0a, 0x63, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, + 0x2f, 0x0a, 0x08, 0x62, 0x65, 0x68, 0x61, 0x76, 0x69, 0x6f, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x13, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x48, 0x50, 0x41, 0x42, 0x65, + 0x68, 0x61, 0x76, 0x69, 0x6f, 0x72, 0x52, 0x08, 0x62, 0x65, 0x68, 0x61, 0x76, 0x69, 0x6f, 0x72, + 0x12, 0x41, 0x0a, 0x0f, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x6d, 0x65, 0x74, 0x72, + 0x69, 0x63, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x61, 0x70, 0x69, 0x2e, + 0x76, 0x31, 0x2e, 0x48, 0x50, 0x41, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x4d, 0x65, 0x74, + 0x72, 0x69, 0x63, 0x52, 0x0e, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x4d, 0x65, 0x74, 0x72, + 0x69, 0x63, 0x73, 0x22, 0x59, 0x0a, 0x0e, 0x53, 0x63, 0x61, 0x6c, 0x65, 0x54, 0x61, 0x72, 0x67, + 0x65, 0x74, 0x52, 0x65, 0x66, 0x12, 0x12, 0x0a, 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, + 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1f, 0x0a, + 0x0b, 0x61, 0x70, 0x69, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x0a, 0x61, 0x70, 0x69, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0xe9, + 0x01, 0x0a, 0x09, 0x48, 0x50, 0x41, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x12, 0x12, 0x0a, 0x04, + 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, + 0x12, 0x35, 0x0a, 0x08, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x48, 0x50, 0x41, 0x52, + 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x52, 0x08, 0x72, + 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x29, 0x0a, 0x04, 0x70, 0x6f, 0x64, 0x73, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x48, + 0x50, 0x41, 0x50, 0x6f, 0x64, 0x73, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x52, 0x04, 0x70, 0x6f, + 0x64, 0x73, 0x12, 0x2f, 0x0a, 0x06, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x18, 0x04, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x48, 0x50, 0x41, 0x4f, + 0x62, 0x6a, 0x65, 0x63, 0x74, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x52, 0x06, 0x6f, 0x62, 0x6a, + 0x65, 0x63, 0x74, 0x12, 0x35, 0x0a, 0x08, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x18, + 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x48, + 0x50, 0x41, 0x45, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, + 0x52, 0x08, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x22, 0x6b, 0x0a, 0x11, 0x48, 0x50, + 0x41, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x12, + 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, + 0x61, 0x6d, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x5f, 0x74, 0x79, + 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, + 0x54, 0x79, 0x70, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x5f, 0x76, + 0x61, 0x6c, 0x75, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x74, 0x61, 0x72, 0x67, + 0x65, 0x74, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x86, 0x01, 0x0a, 0x0d, 0x48, 0x50, 0x41, 0x50, + 0x6f, 0x64, 0x73, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x12, 0x31, 0x0a, 0x06, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x48, 0x50, 0x41, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x52, 0x06, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x12, 0x1f, 0x0a, 0x0b, @@ -18286,418 +22942,584 @@ var file_api_v1_common_proto_rawDesc = []byte{ 0x09, 0x52, 0x0a, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x56, 0x61, 0x6c, 0x75, 0x65, - 0x22, 0xa9, 0x01, 0x0a, 0x11, 0x48, 0x50, 0x41, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x53, 0x65, - 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x43, 0x0a, 0x08, 0x73, 0x65, - 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x61, - 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x48, 0x50, 0x41, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x53, - 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x2e, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, - 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x08, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x1a, - 0x3b, 0x0a, 0x0d, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x45, 0x6e, 0x74, 0x72, 0x79, - 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, - 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x7b, 0x0a, 0x12, - 0x48, 0x50, 0x41, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, - 0x63, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x61, 0x70, - 0x69, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x0a, 0x61, 0x70, 0x69, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x1c, 0x0a, 0x09, 0x6e, - 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, - 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x22, 0x8c, 0x02, 0x0a, 0x10, 0x48, 0x50, - 0x41, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x12, 0x12, - 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, - 0x70, 0x65, 0x12, 0x3c, 0x0a, 0x08, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x48, 0x50, - 0x41, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, - 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x52, 0x08, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, - 0x12, 0x30, 0x0a, 0x04, 0x70, 0x6f, 0x64, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, - 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x48, 0x50, 0x41, 0x43, 0x75, 0x72, 0x72, 0x65, - 0x6e, 0x74, 0x50, 0x6f, 0x64, 0x73, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x52, 0x04, 0x70, 0x6f, - 0x64, 0x73, 0x12, 0x36, 0x0a, 0x06, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x18, 0x04, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x48, 0x50, 0x41, 0x43, - 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x4d, 0x65, 0x74, 0x72, - 0x69, 0x63, 0x52, 0x06, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x12, 0x3c, 0x0a, 0x08, 0x65, 0x78, - 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x61, - 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x48, 0x50, 0x41, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, - 0x45, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x52, 0x08, - 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x22, 0x93, 0x01, 0x0a, 0x18, 0x48, 0x50, 0x41, - 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x4d, - 0x65, 0x74, 0x72, 0x69, 0x63, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x2f, 0x0a, 0x13, 0x63, 0x75, 0x72, - 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x75, 0x74, 0x69, 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x12, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x55, - 0x74, 0x69, 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x32, 0x0a, 0x15, 0x63, 0x75, - 0x72, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x61, 0x76, 0x65, 0x72, 0x61, 0x67, 0x65, 0x5f, 0x76, 0x61, - 0x6c, 0x75, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x13, 0x63, 0x75, 0x72, 0x72, 0x65, - 0x6e, 0x74, 0x41, 0x76, 0x65, 0x72, 0x61, 0x67, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x7d, - 0x0a, 0x14, 0x48, 0x50, 0x41, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x50, 0x6f, 0x64, 0x73, - 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x12, 0x31, 0x0a, 0x06, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, - 0x48, 0x50, 0x41, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, - 0x72, 0x52, 0x06, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x12, 0x32, 0x0a, 0x15, 0x63, 0x75, 0x72, - 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x61, 0x76, 0x65, 0x72, 0x61, 0x67, 0x65, 0x5f, 0x76, 0x61, 0x6c, - 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x13, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, - 0x74, 0x41, 0x76, 0x65, 0x72, 0x61, 0x67, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x22, 0xb7, 0x01, - 0x0a, 0x16, 0x48, 0x50, 0x41, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x4f, 0x62, 0x6a, 0x65, - 0x63, 0x74, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x12, 0x45, 0x0a, 0x10, 0x64, 0x65, 0x73, 0x63, - 0x72, 0x69, 0x62, 0x65, 0x64, 0x5f, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x48, 0x50, 0x41, 0x4f, - 0x62, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x52, 0x0f, - 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x64, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x12, - 0x31, 0x0a, 0x06, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x19, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x48, 0x50, 0x41, 0x4d, 0x65, 0x74, 0x72, - 0x69, 0x63, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x52, 0x06, 0x6d, 0x65, 0x74, 0x72, - 0x69, 0x63, 0x12, 0x23, 0x0a, 0x0d, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x76, 0x61, - 0x6c, 0x75, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x63, 0x75, 0x72, 0x72, 0x65, - 0x6e, 0x74, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x22, 0xa6, 0x01, 0x0a, 0x18, 0x48, 0x50, 0x41, 0x43, - 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x45, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x4d, 0x65, - 0x74, 0x72, 0x69, 0x63, 0x12, 0x31, 0x0a, 0x06, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x48, 0x50, - 0x41, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x52, - 0x06, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x12, 0x23, 0x0a, 0x0d, 0x63, 0x75, 0x72, 0x72, 0x65, - 0x6e, 0x74, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, - 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x32, 0x0a, 0x15, - 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x61, 0x76, 0x65, 0x72, 0x61, 0x67, 0x65, 0x5f, - 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x13, 0x63, 0x75, 0x72, - 0x72, 0x65, 0x6e, 0x74, 0x41, 0x76, 0x65, 0x72, 0x61, 0x67, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, - 0x22, 0x9e, 0x01, 0x0a, 0x0c, 0x48, 0x50, 0x41, 0x43, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, - 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x16, 0x0a, - 0x06, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x72, - 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, - 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, - 0x30, 0x0a, 0x14, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x69, 0x74, 0x69, - 0x6f, 0x6e, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x12, 0x6c, - 0x61, 0x73, 0x74, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x69, 0x6d, - 0x65, 0x22, 0x79, 0x0a, 0x0b, 0x48, 0x50, 0x41, 0x42, 0x65, 0x68, 0x61, 0x76, 0x69, 0x6f, 0x72, - 0x12, 0x32, 0x0a, 0x08, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x5f, 0x75, 0x70, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x48, 0x50, 0x41, 0x53, - 0x63, 0x61, 0x6c, 0x69, 0x6e, 0x67, 0x52, 0x75, 0x6c, 0x65, 0x73, 0x52, 0x07, 0x73, 0x63, 0x61, - 0x6c, 0x65, 0x55, 0x70, 0x12, 0x36, 0x0a, 0x0a, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x5f, 0x64, 0x6f, - 0x77, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, - 0x31, 0x2e, 0x48, 0x50, 0x41, 0x53, 0x63, 0x61, 0x6c, 0x69, 0x6e, 0x67, 0x52, 0x75, 0x6c, 0x65, - 0x73, 0x52, 0x09, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x44, 0x6f, 0x77, 0x6e, 0x22, 0xae, 0x01, 0x0a, - 0x0f, 0x48, 0x50, 0x41, 0x53, 0x63, 0x61, 0x6c, 0x69, 0x6e, 0x67, 0x52, 0x75, 0x6c, 0x65, 0x73, - 0x12, 0x40, 0x0a, 0x1c, 0x73, 0x74, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x5f, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x5f, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x1a, 0x73, 0x74, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x7a, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x57, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x53, 0x65, 0x63, 0x6f, 0x6e, - 0x64, 0x73, 0x12, 0x23, 0x0a, 0x0d, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x5f, 0x70, 0x6f, 0x6c, - 0x69, 0x63, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x73, 0x65, 0x6c, 0x65, 0x63, - 0x74, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x12, 0x34, 0x0a, 0x08, 0x70, 0x6f, 0x6c, 0x69, 0x63, - 0x69, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x61, 0x70, 0x69, 0x2e, - 0x76, 0x31, 0x2e, 0x48, 0x50, 0x41, 0x53, 0x63, 0x61, 0x6c, 0x69, 0x6e, 0x67, 0x50, 0x6f, 0x6c, - 0x69, 0x63, 0x79, 0x52, 0x08, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x69, 0x65, 0x73, 0x22, 0x63, 0x0a, - 0x10, 0x48, 0x50, 0x41, 0x53, 0x63, 0x61, 0x6c, 0x69, 0x6e, 0x67, 0x50, 0x6f, 0x6c, 0x69, 0x63, - 0x79, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x25, 0x0a, 0x0e, 0x70, - 0x65, 0x72, 0x69, 0x6f, 0x64, 0x5f, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x18, 0x03, 0x20, - 0x01, 0x28, 0x05, 0x52, 0x0d, 0x70, 0x65, 0x72, 0x69, 0x6f, 0x64, 0x53, 0x65, 0x63, 0x6f, 0x6e, - 0x64, 0x73, 0x22, 0xbc, 0x02, 0x0a, 0x0a, 0x56, 0x50, 0x41, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, - 0x73, 0x12, 0x33, 0x0a, 0x0a, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x5f, 0x72, 0x65, 0x66, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x56, - 0x50, 0x41, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x52, 0x65, 0x66, 0x52, 0x09, 0x74, 0x61, 0x72, - 0x67, 0x65, 0x74, 0x52, 0x65, 0x66, 0x12, 0x3c, 0x0a, 0x0d, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, - 0x5f, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, - 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x56, 0x50, 0x41, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, - 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x52, 0x0c, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x50, 0x6f, - 0x6c, 0x69, 0x63, 0x79, 0x12, 0x42, 0x0a, 0x0f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, - 0x5f, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, - 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x56, 0x50, 0x41, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, - 0x63, 0x65, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x52, 0x0e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, - 0x63, 0x65, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x12, 0x41, 0x0a, 0x0e, 0x72, 0x65, 0x63, 0x6f, - 0x6d, 0x6d, 0x65, 0x6e, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x19, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x56, 0x50, 0x41, 0x52, 0x65, 0x63, - 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0e, 0x72, 0x65, 0x63, - 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x34, 0x0a, 0x0a, 0x63, - 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, - 0x14, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x56, 0x50, 0x41, 0x43, 0x6f, 0x6e, 0x64, - 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0a, 0x63, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, - 0x73, 0x22, 0x57, 0x0a, 0x0c, 0x56, 0x50, 0x41, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x52, 0x65, - 0x66, 0x12, 0x12, 0x0a, 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x22, 0xcf, 0x01, 0x0a, 0x0f, 0x48, 0x50, 0x41, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x4d, 0x65, + 0x74, 0x72, 0x69, 0x63, 0x12, 0x45, 0x0a, 0x10, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, + 0x64, 0x5f, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, + 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x48, 0x50, 0x41, 0x4f, 0x62, 0x6a, 0x65, 0x63, + 0x74, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x52, 0x0f, 0x64, 0x65, 0x73, 0x63, + 0x72, 0x69, 0x62, 0x65, 0x64, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x12, 0x31, 0x0a, 0x06, 0x6d, + 0x65, 0x74, 0x72, 0x69, 0x63, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x61, 0x70, + 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x48, 0x50, 0x41, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x53, 0x65, + 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x52, 0x06, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x12, 0x1f, + 0x0a, 0x0b, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x0a, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, + 0x21, 0x0a, 0x0c, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, + 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x56, 0x61, 0x6c, + 0x75, 0x65, 0x22, 0x8a, 0x01, 0x0a, 0x11, 0x48, 0x50, 0x41, 0x45, 0x78, 0x74, 0x65, 0x72, 0x6e, + 0x61, 0x6c, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x12, 0x31, 0x0a, 0x06, 0x6d, 0x65, 0x74, 0x72, + 0x69, 0x63, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, + 0x31, 0x2e, 0x48, 0x50, 0x41, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x53, 0x65, 0x6c, 0x65, 0x63, + 0x74, 0x6f, 0x72, 0x52, 0x06, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x12, 0x1f, 0x0a, 0x0b, 0x74, + 0x61, 0x72, 0x67, 0x65, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x0a, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x21, 0x0a, 0x0c, + 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x0b, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x22, + 0xa9, 0x01, 0x0a, 0x11, 0x48, 0x50, 0x41, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x53, 0x65, 0x6c, + 0x65, 0x63, 0x74, 0x6f, 0x72, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x43, 0x0a, 0x08, 0x73, 0x65, 0x6c, + 0x65, 0x63, 0x74, 0x6f, 0x72, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x61, 0x70, + 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x48, 0x50, 0x41, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x53, 0x65, + 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x2e, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x45, + 0x6e, 0x74, 0x72, 0x79, 0x52, 0x08, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x1a, 0x3b, + 0x0a, 0x0d, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, + 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, + 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x7b, 0x0a, 0x12, 0x48, + 0x50, 0x41, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, + 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x61, 0x70, 0x69, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, - 0x61, 0x70, 0x69, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x55, 0x0a, 0x0f, 0x56, 0x50, - 0x41, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x12, 0x1f, 0x0a, - 0x0b, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x0a, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4d, 0x6f, 0x64, 0x65, 0x12, 0x21, - 0x0a, 0x0c, 0x6d, 0x69, 0x6e, 0x5f, 0x72, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x73, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x05, 0x52, 0x0b, 0x6d, 0x69, 0x6e, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, - 0x73, 0x22, 0x66, 0x0a, 0x11, 0x56, 0x50, 0x41, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, - 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x12, 0x51, 0x0a, 0x12, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, - 0x6e, 0x65, 0x72, 0x5f, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x69, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, - 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x56, 0x50, 0x41, 0x43, - 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, - 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x52, 0x11, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, - 0x72, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x69, 0x65, 0x73, 0x22, 0xdf, 0x03, 0x0a, 0x1a, 0x56, 0x50, - 0x41, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, - 0x63, 0x65, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x12, 0x25, 0x0a, 0x0e, 0x63, 0x6f, 0x6e, 0x74, - 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x0d, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x12, - 0x12, 0x0a, 0x04, 0x6d, 0x6f, 0x64, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6d, - 0x6f, 0x64, 0x65, 0x12, 0x53, 0x0a, 0x0b, 0x6d, 0x69, 0x6e, 0x5f, 0x61, 0x6c, 0x6c, 0x6f, 0x77, - 0x65, 0x64, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x32, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, - 0x31, 0x2e, 0x56, 0x50, 0x41, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x52, 0x65, - 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x4d, 0x69, 0x6e, - 0x41, 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0a, 0x6d, 0x69, - 0x6e, 0x41, 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x12, 0x53, 0x0a, 0x0b, 0x6d, 0x61, 0x78, 0x5f, - 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x32, 0x2e, - 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x56, 0x50, 0x41, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, - 0x6e, 0x65, 0x72, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x50, 0x6f, 0x6c, 0x69, 0x63, - 0x79, 0x2e, 0x4d, 0x61, 0x78, 0x41, 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x45, 0x6e, 0x74, 0x72, - 0x79, 0x52, 0x0a, 0x6d, 0x61, 0x78, 0x41, 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x12, 0x31, 0x0a, - 0x14, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x64, 0x5f, 0x72, 0x65, 0x73, 0x6f, - 0x75, 0x72, 0x63, 0x65, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x09, 0x52, 0x13, 0x63, 0x6f, 0x6e, - 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x64, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, - 0x12, 0x2b, 0x0a, 0x11, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x64, 0x5f, 0x76, - 0x61, 0x6c, 0x75, 0x65, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x63, 0x6f, 0x6e, - 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x64, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x1a, 0x3d, 0x0a, - 0x0f, 0x4d, 0x69, 0x6e, 0x41, 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x45, 0x6e, 0x74, 0x72, 0x79, - 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, - 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x3d, 0x0a, 0x0f, - 0x4d, 0x61, 0x78, 0x41, 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, + 0x61, 0x70, 0x69, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x1c, 0x0a, 0x09, 0x6e, 0x61, + 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6e, + 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x22, 0x8c, 0x02, 0x0a, 0x10, 0x48, 0x50, 0x41, + 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x12, 0x12, 0x0a, + 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, + 0x65, 0x12, 0x3c, 0x0a, 0x08, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x48, 0x50, 0x41, + 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x4d, + 0x65, 0x74, 0x72, 0x69, 0x63, 0x52, 0x08, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, + 0x30, 0x0a, 0x04, 0x70, 0x6f, 0x64, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, + 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x48, 0x50, 0x41, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, + 0x74, 0x50, 0x6f, 0x64, 0x73, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x52, 0x04, 0x70, 0x6f, 0x64, + 0x73, 0x12, 0x36, 0x0a, 0x06, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x1e, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x48, 0x50, 0x41, 0x43, 0x75, + 0x72, 0x72, 0x65, 0x6e, 0x74, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x4d, 0x65, 0x74, 0x72, 0x69, + 0x63, 0x52, 0x06, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x12, 0x3c, 0x0a, 0x08, 0x65, 0x78, 0x74, + 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x61, 0x70, + 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x48, 0x50, 0x41, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x45, + 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x52, 0x08, 0x65, + 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x22, 0x93, 0x01, 0x0a, 0x18, 0x48, 0x50, 0x41, 0x43, + 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x4d, 0x65, + 0x74, 0x72, 0x69, 0x63, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x2f, 0x0a, 0x13, 0x63, 0x75, 0x72, 0x72, + 0x65, 0x6e, 0x74, 0x5f, 0x75, 0x74, 0x69, 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x12, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x55, 0x74, + 0x69, 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x32, 0x0a, 0x15, 0x63, 0x75, 0x72, + 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x61, 0x76, 0x65, 0x72, 0x61, 0x67, 0x65, 0x5f, 0x76, 0x61, 0x6c, + 0x75, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x13, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, + 0x74, 0x41, 0x76, 0x65, 0x72, 0x61, 0x67, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x7d, 0x0a, + 0x14, 0x48, 0x50, 0x41, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x50, 0x6f, 0x64, 0x73, 0x4d, + 0x65, 0x74, 0x72, 0x69, 0x63, 0x12, 0x31, 0x0a, 0x06, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x48, + 0x50, 0x41, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, + 0x52, 0x06, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x12, 0x32, 0x0a, 0x15, 0x63, 0x75, 0x72, 0x72, + 0x65, 0x6e, 0x74, 0x5f, 0x61, 0x76, 0x65, 0x72, 0x61, 0x67, 0x65, 0x5f, 0x76, 0x61, 0x6c, 0x75, + 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x13, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, + 0x41, 0x76, 0x65, 0x72, 0x61, 0x67, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x22, 0xb7, 0x01, 0x0a, + 0x16, 0x48, 0x50, 0x41, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x4f, 0x62, 0x6a, 0x65, 0x63, + 0x74, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x12, 0x45, 0x0a, 0x10, 0x64, 0x65, 0x73, 0x63, 0x72, + 0x69, 0x62, 0x65, 0x64, 0x5f, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x1a, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x48, 0x50, 0x41, 0x4f, 0x62, + 0x6a, 0x65, 0x63, 0x74, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x52, 0x0f, 0x64, + 0x65, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x64, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x12, 0x31, + 0x0a, 0x06, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, + 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x48, 0x50, 0x41, 0x4d, 0x65, 0x74, 0x72, 0x69, + 0x63, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x52, 0x06, 0x6d, 0x65, 0x74, 0x72, 0x69, + 0x63, 0x12, 0x23, 0x0a, 0x0d, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x76, 0x61, 0x6c, + 0x75, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, + 0x74, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x22, 0xa6, 0x01, 0x0a, 0x18, 0x48, 0x50, 0x41, 0x43, 0x75, + 0x72, 0x72, 0x65, 0x6e, 0x74, 0x45, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x4d, 0x65, 0x74, + 0x72, 0x69, 0x63, 0x12, 0x31, 0x0a, 0x06, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x48, 0x50, 0x41, + 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x52, 0x06, + 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x12, 0x23, 0x0a, 0x0d, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, + 0x74, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x63, + 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x32, 0x0a, 0x15, 0x63, + 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x61, 0x76, 0x65, 0x72, 0x61, 0x67, 0x65, 0x5f, 0x76, + 0x61, 0x6c, 0x75, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x13, 0x63, 0x75, 0x72, 0x72, + 0x65, 0x6e, 0x74, 0x41, 0x76, 0x65, 0x72, 0x61, 0x67, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x22, + 0x9e, 0x01, 0x0a, 0x0c, 0x48, 0x50, 0x41, 0x43, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, + 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, + 0x74, 0x79, 0x70, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x16, 0x0a, 0x06, + 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x72, 0x65, + 0x61, 0x73, 0x6f, 0x6e, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, + 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x30, + 0x0a, 0x14, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x69, 0x74, 0x69, 0x6f, + 0x6e, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x12, 0x6c, 0x61, + 0x73, 0x74, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x69, 0x6d, 0x65, + 0x22, 0x79, 0x0a, 0x0b, 0x48, 0x50, 0x41, 0x42, 0x65, 0x68, 0x61, 0x76, 0x69, 0x6f, 0x72, 0x12, + 0x32, 0x0a, 0x08, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x5f, 0x75, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x17, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x48, 0x50, 0x41, 0x53, 0x63, + 0x61, 0x6c, 0x69, 0x6e, 0x67, 0x52, 0x75, 0x6c, 0x65, 0x73, 0x52, 0x07, 0x73, 0x63, 0x61, 0x6c, + 0x65, 0x55, 0x70, 0x12, 0x36, 0x0a, 0x0a, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x5f, 0x64, 0x6f, 0x77, + 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, + 0x2e, 0x48, 0x50, 0x41, 0x53, 0x63, 0x61, 0x6c, 0x69, 0x6e, 0x67, 0x52, 0x75, 0x6c, 0x65, 0x73, + 0x52, 0x09, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x44, 0x6f, 0x77, 0x6e, 0x22, 0xae, 0x01, 0x0a, 0x0f, + 0x48, 0x50, 0x41, 0x53, 0x63, 0x61, 0x6c, 0x69, 0x6e, 0x67, 0x52, 0x75, 0x6c, 0x65, 0x73, 0x12, + 0x40, 0x0a, 0x1c, 0x73, 0x74, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x5f, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x5f, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x1a, 0x73, 0x74, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x7a, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x57, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x53, 0x65, 0x63, 0x6f, 0x6e, 0x64, + 0x73, 0x12, 0x23, 0x0a, 0x0d, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x5f, 0x70, 0x6f, 0x6c, 0x69, + 0x63, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, + 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x12, 0x34, 0x0a, 0x08, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x69, + 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, + 0x31, 0x2e, 0x48, 0x50, 0x41, 0x53, 0x63, 0x61, 0x6c, 0x69, 0x6e, 0x67, 0x50, 0x6f, 0x6c, 0x69, + 0x63, 0x79, 0x52, 0x08, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x69, 0x65, 0x73, 0x22, 0x63, 0x0a, 0x10, + 0x48, 0x50, 0x41, 0x53, 0x63, 0x61, 0x6c, 0x69, 0x6e, 0x67, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, + 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, + 0x74, 0x79, 0x70, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x05, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x25, 0x0a, 0x0e, 0x70, 0x65, + 0x72, 0x69, 0x6f, 0x64, 0x5f, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x05, 0x52, 0x0d, 0x70, 0x65, 0x72, 0x69, 0x6f, 0x64, 0x53, 0x65, 0x63, 0x6f, 0x6e, 0x64, + 0x73, 0x22, 0xbc, 0x02, 0x0a, 0x0a, 0x56, 0x50, 0x41, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, + 0x12, 0x33, 0x0a, 0x0a, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x5f, 0x72, 0x65, 0x66, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x56, 0x50, + 0x41, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x52, 0x65, 0x66, 0x52, 0x09, 0x74, 0x61, 0x72, 0x67, + 0x65, 0x74, 0x52, 0x65, 0x66, 0x12, 0x3c, 0x0a, 0x0d, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x5f, + 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x61, + 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x56, 0x50, 0x41, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x50, + 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x52, 0x0c, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x50, 0x6f, 0x6c, + 0x69, 0x63, 0x79, 0x12, 0x42, 0x0a, 0x0f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, + 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x61, + 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x56, 0x50, 0x41, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, + 0x65, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x52, 0x0e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, + 0x65, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x12, 0x41, 0x0a, 0x0e, 0x72, 0x65, 0x63, 0x6f, 0x6d, + 0x6d, 0x65, 0x6e, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x19, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x56, 0x50, 0x41, 0x52, 0x65, 0x63, 0x6f, + 0x6d, 0x6d, 0x65, 0x6e, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0e, 0x72, 0x65, 0x63, 0x6f, + 0x6d, 0x6d, 0x65, 0x6e, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x34, 0x0a, 0x0a, 0x63, 0x6f, + 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x14, + 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x56, 0x50, 0x41, 0x43, 0x6f, 0x6e, 0x64, 0x69, + 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0a, 0x63, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, + 0x22, 0x57, 0x0a, 0x0c, 0x56, 0x50, 0x41, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x52, 0x65, 0x66, + 0x12, 0x12, 0x0a, 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, + 0x6b, 0x69, 0x6e, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x61, 0x70, 0x69, 0x5f, + 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x61, + 0x70, 0x69, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x55, 0x0a, 0x0f, 0x56, 0x50, 0x41, + 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x12, 0x1f, 0x0a, 0x0b, + 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x0a, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4d, 0x6f, 0x64, 0x65, 0x12, 0x21, 0x0a, + 0x0c, 0x6d, 0x69, 0x6e, 0x5f, 0x72, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x73, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x05, 0x52, 0x0b, 0x6d, 0x69, 0x6e, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x73, + 0x22, 0x66, 0x0a, 0x11, 0x56, 0x50, 0x41, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x50, + 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x12, 0x51, 0x0a, 0x12, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, + 0x65, 0x72, 0x5f, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x69, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, + 0x0b, 0x32, 0x22, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x56, 0x50, 0x41, 0x43, 0x6f, + 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x50, + 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x52, 0x11, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, + 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x69, 0x65, 0x73, 0x22, 0xdf, 0x03, 0x0a, 0x1a, 0x56, 0x50, 0x41, + 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, + 0x65, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x12, 0x25, 0x0a, 0x0e, 0x63, 0x6f, 0x6e, 0x74, 0x61, + 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x0d, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x12, + 0x0a, 0x04, 0x6d, 0x6f, 0x64, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6d, 0x6f, + 0x64, 0x65, 0x12, 0x53, 0x0a, 0x0b, 0x6d, 0x69, 0x6e, 0x5f, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x65, + 0x64, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x32, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, + 0x2e, 0x56, 0x50, 0x41, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x52, 0x65, 0x73, + 0x6f, 0x75, 0x72, 0x63, 0x65, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x4d, 0x69, 0x6e, 0x41, + 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0a, 0x6d, 0x69, 0x6e, + 0x41, 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x12, 0x53, 0x0a, 0x0b, 0x6d, 0x61, 0x78, 0x5f, 0x61, + 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x32, 0x2e, 0x61, + 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x56, 0x50, 0x41, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, + 0x65, 0x72, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, + 0x2e, 0x4d, 0x61, 0x78, 0x41, 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x45, 0x6e, 0x74, 0x72, 0x79, + 0x52, 0x0a, 0x6d, 0x61, 0x78, 0x41, 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x12, 0x31, 0x0a, 0x14, + 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x64, 0x5f, 0x72, 0x65, 0x73, 0x6f, 0x75, + 0x72, 0x63, 0x65, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x09, 0x52, 0x13, 0x63, 0x6f, 0x6e, 0x74, + 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x64, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x12, + 0x2b, 0x0a, 0x11, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x64, 0x5f, 0x76, 0x61, + 0x6c, 0x75, 0x65, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x63, 0x6f, 0x6e, 0x74, + 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x64, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x1a, 0x3d, 0x0a, 0x0f, + 0x4d, 0x69, 0x6e, 0x41, 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x74, 0x0a, 0x11, 0x56, - 0x50, 0x41, 0x52, 0x65, 0x63, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x12, 0x5f, 0x0a, 0x19, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x72, 0x65, - 0x63, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x01, 0x20, - 0x03, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x56, 0x50, 0x41, - 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x52, 0x65, 0x63, 0x6f, 0x6d, 0x6d, 0x65, - 0x6e, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x18, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, - 0x65, 0x72, 0x52, 0x65, 0x63, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x73, 0x22, 0x92, 0x05, 0x0a, 0x1a, 0x56, 0x50, 0x41, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, - 0x65, 0x72, 0x52, 0x65, 0x63, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x12, 0x25, 0x0a, 0x0e, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x6e, 0x61, - 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, - 0x6e, 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x46, 0x0a, 0x06, 0x74, 0x61, 0x72, 0x67, 0x65, - 0x74, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2e, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, - 0x2e, 0x56, 0x50, 0x41, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x52, 0x65, 0x63, - 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x54, 0x61, 0x72, 0x67, - 0x65, 0x74, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x06, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x12, - 0x53, 0x0a, 0x0b, 0x6c, 0x6f, 0x77, 0x65, 0x72, 0x5f, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x18, 0x03, - 0x20, 0x03, 0x28, 0x0b, 0x32, 0x32, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x56, 0x50, - 0x41, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x52, 0x65, 0x63, 0x6f, 0x6d, 0x6d, - 0x65, 0x6e, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x4c, 0x6f, 0x77, 0x65, 0x72, 0x42, 0x6f, - 0x75, 0x6e, 0x64, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0a, 0x6c, 0x6f, 0x77, 0x65, 0x72, 0x42, - 0x6f, 0x75, 0x6e, 0x64, 0x12, 0x53, 0x0a, 0x0b, 0x75, 0x70, 0x70, 0x65, 0x72, 0x5f, 0x62, 0x6f, - 0x75, 0x6e, 0x64, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x32, 0x2e, 0x61, 0x70, 0x69, 0x2e, - 0x76, 0x31, 0x2e, 0x56, 0x50, 0x41, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x52, - 0x65, 0x63, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x55, 0x70, - 0x70, 0x65, 0x72, 0x42, 0x6f, 0x75, 0x6e, 0x64, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0a, 0x75, - 0x70, 0x70, 0x65, 0x72, 0x42, 0x6f, 0x75, 0x6e, 0x64, 0x12, 0x5f, 0x0a, 0x0f, 0x75, 0x6e, 0x63, - 0x61, 0x70, 0x70, 0x65, 0x64, 0x5f, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x18, 0x05, 0x20, 0x03, - 0x28, 0x0b, 0x32, 0x36, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x56, 0x50, 0x41, 0x43, + 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x3d, 0x0a, 0x0f, 0x4d, + 0x61, 0x78, 0x41, 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, + 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, + 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x74, 0x0a, 0x11, 0x56, 0x50, + 0x41, 0x52, 0x65, 0x63, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, + 0x5f, 0x0a, 0x19, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x72, 0x65, 0x63, + 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x01, 0x20, 0x03, + 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x56, 0x50, 0x41, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x52, 0x65, 0x63, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, - 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x55, 0x6e, 0x63, 0x61, 0x70, 0x70, 0x65, 0x64, 0x54, - 0x61, 0x72, 0x67, 0x65, 0x74, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0e, 0x75, 0x6e, 0x63, 0x61, - 0x70, 0x70, 0x65, 0x64, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x1a, 0x39, 0x0a, 0x0b, 0x54, 0x61, - 0x72, 0x67, 0x65, 0x74, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, - 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, - 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x3d, 0x0a, 0x0f, 0x4c, 0x6f, 0x77, 0x65, 0x72, 0x42, 0x6f, - 0x75, 0x6e, 0x64, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, + 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x18, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, + 0x72, 0x52, 0x65, 0x63, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, + 0x22, 0x92, 0x05, 0x0a, 0x1a, 0x56, 0x50, 0x41, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, + 0x72, 0x52, 0x65, 0x63, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, + 0x25, 0x0a, 0x0e, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x6e, 0x61, 0x6d, + 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, + 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x46, 0x0a, 0x06, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, + 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2e, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, + 0x56, 0x50, 0x41, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x52, 0x65, 0x63, 0x6f, + 0x6d, 0x6d, 0x65, 0x6e, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x54, 0x61, 0x72, 0x67, 0x65, + 0x74, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x06, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x12, 0x53, + 0x0a, 0x0b, 0x6c, 0x6f, 0x77, 0x65, 0x72, 0x5f, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x18, 0x03, 0x20, + 0x03, 0x28, 0x0b, 0x32, 0x32, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x56, 0x50, 0x41, + 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x52, 0x65, 0x63, 0x6f, 0x6d, 0x6d, 0x65, + 0x6e, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x4c, 0x6f, 0x77, 0x65, 0x72, 0x42, 0x6f, 0x75, + 0x6e, 0x64, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0a, 0x6c, 0x6f, 0x77, 0x65, 0x72, 0x42, 0x6f, + 0x75, 0x6e, 0x64, 0x12, 0x53, 0x0a, 0x0b, 0x75, 0x70, 0x70, 0x65, 0x72, 0x5f, 0x62, 0x6f, 0x75, + 0x6e, 0x64, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x32, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, + 0x31, 0x2e, 0x56, 0x50, 0x41, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x52, 0x65, + 0x63, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x55, 0x70, 0x70, + 0x65, 0x72, 0x42, 0x6f, 0x75, 0x6e, 0x64, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0a, 0x75, 0x70, + 0x70, 0x65, 0x72, 0x42, 0x6f, 0x75, 0x6e, 0x64, 0x12, 0x5f, 0x0a, 0x0f, 0x75, 0x6e, 0x63, 0x61, + 0x70, 0x70, 0x65, 0x64, 0x5f, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x18, 0x05, 0x20, 0x03, 0x28, + 0x0b, 0x32, 0x36, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x56, 0x50, 0x41, 0x43, 0x6f, + 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x52, 0x65, 0x63, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x64, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x55, 0x6e, 0x63, 0x61, 0x70, 0x70, 0x65, 0x64, 0x54, 0x61, + 0x72, 0x67, 0x65, 0x74, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0e, 0x75, 0x6e, 0x63, 0x61, 0x70, + 0x70, 0x65, 0x64, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x1a, 0x39, 0x0a, 0x0b, 0x54, 0x61, 0x72, + 0x67, 0x65, 0x74, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, - 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x3d, 0x0a, 0x0f, 0x55, 0x70, 0x70, 0x65, 0x72, 0x42, 0x6f, 0x75, + 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x3d, 0x0a, 0x0f, 0x4c, 0x6f, 0x77, 0x65, 0x72, 0x42, 0x6f, 0x75, 0x6e, 0x64, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, - 0x02, 0x38, 0x01, 0x1a, 0x41, 0x0a, 0x13, 0x55, 0x6e, 0x63, 0x61, 0x70, 0x70, 0x65, 0x64, 0x54, - 0x61, 0x72, 0x67, 0x65, 0x74, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, - 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, - 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, - 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x9e, 0x01, 0x0a, 0x0c, 0x56, 0x50, 0x41, 0x43, 0x6f, - 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x73, - 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x74, 0x61, - 0x74, 0x75, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x18, 0x03, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x06, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x12, 0x18, 0x0a, 0x07, 0x6d, - 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, - 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x30, 0x0a, 0x14, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x74, 0x72, - 0x61, 0x6e, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x05, 0x20, - 0x01, 0x28, 0x03, 0x52, 0x12, 0x6c, 0x61, 0x73, 0x74, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x69, 0x74, - 0x69, 0x6f, 0x6e, 0x54, 0x69, 0x6d, 0x65, 0x22, 0x43, 0x0a, 0x11, 0x4c, 0x69, 0x6d, 0x69, 0x74, - 0x52, 0x61, 0x6e, 0x67, 0x65, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x2e, 0x0a, 0x06, - 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x61, - 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x52, 0x61, 0x6e, 0x67, 0x65, - 0x49, 0x74, 0x65, 0x6d, 0x52, 0x06, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x73, 0x22, 0xd8, 0x05, 0x0a, - 0x0e, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x49, 0x74, 0x65, 0x6d, 0x12, - 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, - 0x79, 0x70, 0x65, 0x12, 0x50, 0x0a, 0x0e, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x5f, 0x6c, - 0x69, 0x6d, 0x69, 0x74, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x29, 0x2e, 0x61, 0x70, + 0x02, 0x38, 0x01, 0x1a, 0x3d, 0x0a, 0x0f, 0x55, 0x70, 0x70, 0x65, 0x72, 0x42, 0x6f, 0x75, 0x6e, + 0x64, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, + 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, + 0x38, 0x01, 0x1a, 0x41, 0x0a, 0x13, 0x55, 0x6e, 0x63, 0x61, 0x70, 0x70, 0x65, 0x64, 0x54, 0x61, + 0x72, 0x67, 0x65, 0x74, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, + 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, + 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x9e, 0x01, 0x0a, 0x0c, 0x56, 0x50, 0x41, 0x43, 0x6f, 0x6e, + 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, + 0x61, 0x74, 0x75, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, + 0x75, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x06, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, + 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, + 0x73, 0x61, 0x67, 0x65, 0x12, 0x30, 0x0a, 0x14, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x74, 0x72, 0x61, + 0x6e, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x05, 0x20, 0x01, + 0x28, 0x03, 0x52, 0x12, 0x6c, 0x61, 0x73, 0x74, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x69, 0x74, 0x69, + 0x6f, 0x6e, 0x54, 0x69, 0x6d, 0x65, 0x22, 0x43, 0x0a, 0x11, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x52, + 0x61, 0x6e, 0x67, 0x65, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x2e, 0x0a, 0x06, 0x6c, + 0x69, 0x6d, 0x69, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x49, - 0x74, 0x65, 0x6d, 0x2e, 0x44, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x4c, 0x69, 0x6d, 0x69, 0x74, - 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0d, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x4c, - 0x69, 0x6d, 0x69, 0x74, 0x73, 0x12, 0x53, 0x0a, 0x0f, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, - 0x5f, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2a, - 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x52, 0x61, 0x6e, - 0x67, 0x65, 0x49, 0x74, 0x65, 0x6d, 0x2e, 0x44, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0e, 0x64, 0x65, 0x66, 0x61, - 0x75, 0x6c, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x31, 0x0a, 0x03, 0x6d, 0x61, - 0x78, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, - 0x2e, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x49, 0x74, 0x65, 0x6d, 0x2e, - 0x4d, 0x61, 0x78, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x03, 0x6d, 0x61, 0x78, 0x12, 0x31, 0x0a, - 0x03, 0x6d, 0x69, 0x6e, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x61, 0x70, 0x69, + 0x74, 0x65, 0x6d, 0x52, 0x06, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x73, 0x22, 0xd8, 0x05, 0x0a, 0x0e, + 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x49, 0x74, 0x65, 0x6d, 0x12, 0x12, + 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, + 0x70, 0x65, 0x12, 0x50, 0x0a, 0x0e, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x5f, 0x6c, 0x69, + 0x6d, 0x69, 0x74, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x29, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x49, 0x74, - 0x65, 0x6d, 0x2e, 0x4d, 0x69, 0x6e, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x03, 0x6d, 0x69, 0x6e, - 0x12, 0x67, 0x0a, 0x17, 0x6d, 0x61, 0x78, 0x5f, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x5f, 0x72, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x5f, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x18, 0x06, 0x20, 0x03, 0x28, - 0x0b, 0x32, 0x30, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x6d, 0x69, 0x74, - 0x52, 0x61, 0x6e, 0x67, 0x65, 0x49, 0x74, 0x65, 0x6d, 0x2e, 0x4d, 0x61, 0x78, 0x4c, 0x69, 0x6d, - 0x69, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x52, 0x61, 0x74, 0x69, 0x6f, 0x45, 0x6e, - 0x74, 0x72, 0x79, 0x52, 0x14, 0x6d, 0x61, 0x78, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x52, 0x61, 0x74, 0x69, 0x6f, 0x1a, 0x40, 0x0a, 0x12, 0x44, 0x65, 0x66, - 0x61, 0x75, 0x6c, 0x74, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, - 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, - 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x41, 0x0a, 0x13, 0x44, - 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x45, 0x6e, 0x74, - 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x36, - 0x0a, 0x08, 0x4d, 0x61, 0x78, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, + 0x65, 0x6d, 0x2e, 0x44, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x73, + 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0d, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x4c, 0x69, + 0x6d, 0x69, 0x74, 0x73, 0x12, 0x53, 0x0a, 0x0f, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x5f, + 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2a, 0x2e, + 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x52, 0x61, 0x6e, 0x67, + 0x65, 0x49, 0x74, 0x65, 0x6d, 0x2e, 0x44, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0e, 0x64, 0x65, 0x66, 0x61, 0x75, + 0x6c, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x31, 0x0a, 0x03, 0x6d, 0x61, 0x78, + 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, + 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x49, 0x74, 0x65, 0x6d, 0x2e, 0x4d, + 0x61, 0x78, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x03, 0x6d, 0x61, 0x78, 0x12, 0x31, 0x0a, 0x03, + 0x6d, 0x69, 0x6e, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x61, 0x70, 0x69, 0x2e, + 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x49, 0x74, 0x65, + 0x6d, 0x2e, 0x4d, 0x69, 0x6e, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x03, 0x6d, 0x69, 0x6e, 0x12, + 0x67, 0x0a, 0x17, 0x6d, 0x61, 0x78, 0x5f, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x5f, 0x72, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x5f, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, + 0x32, 0x30, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x52, + 0x61, 0x6e, 0x67, 0x65, 0x49, 0x74, 0x65, 0x6d, 0x2e, 0x4d, 0x61, 0x78, 0x4c, 0x69, 0x6d, 0x69, + 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x52, 0x61, 0x74, 0x69, 0x6f, 0x45, 0x6e, 0x74, + 0x72, 0x79, 0x52, 0x14, 0x6d, 0x61, 0x78, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x52, 0x61, 0x74, 0x69, 0x6f, 0x1a, 0x40, 0x0a, 0x12, 0x44, 0x65, 0x66, 0x61, + 0x75, 0x6c, 0x74, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, + 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, + 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x41, 0x0a, 0x13, 0x44, 0x65, + 0x66, 0x61, 0x75, 0x6c, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x45, 0x6e, 0x74, 0x72, + 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, + 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x36, 0x0a, + 0x08, 0x4d, 0x61, 0x78, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, + 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, + 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x36, 0x0a, 0x08, 0x4d, 0x69, 0x6e, 0x45, 0x6e, 0x74, 0x72, + 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, + 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x47, 0x0a, + 0x19, 0x4d, 0x61, 0x78, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x52, 0x61, 0x74, 0x69, 0x6f, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, - 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x36, 0x0a, 0x08, 0x4d, 0x69, 0x6e, 0x45, 0x6e, 0x74, - 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x47, - 0x0a, 0x19, 0x4d, 0x61, 0x78, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x52, 0x61, 0x74, 0x69, 0x6f, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, - 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, - 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, - 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0xd1, 0x01, 0x0a, 0x15, 0x53, 0x65, 0x72, 0x76, - 0x69, 0x63, 0x65, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, - 0x73, 0x12, 0x45, 0x0a, 0x1f, 0x61, 0x75, 0x74, 0x6f, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x73, - 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x74, - 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x1c, 0x61, 0x75, 0x74, 0x6f, - 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x41, 0x63, 0x63, 0x6f, - 0x75, 0x6e, 0x74, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x18, 0x0a, 0x07, 0x73, 0x65, 0x63, 0x72, - 0x65, 0x74, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x07, 0x73, 0x65, 0x63, 0x72, 0x65, - 0x74, 0x73, 0x12, 0x2c, 0x0a, 0x12, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x5f, 0x70, 0x75, 0x6c, 0x6c, - 0x5f, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x10, - 0x69, 0x6d, 0x61, 0x67, 0x65, 0x50, 0x75, 0x6c, 0x6c, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x73, - 0x12, 0x29, 0x0a, 0x10, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x76, 0x65, 0x72, - 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x72, 0x65, 0x73, 0x6f, - 0x75, 0x72, 0x63, 0x65, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x35, 0x0a, 0x0b, 0x52, - 0x6f, 0x6c, 0x65, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x26, 0x0a, 0x05, 0x72, 0x75, - 0x6c, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x61, 0x70, 0x69, 0x2e, - 0x76, 0x31, 0x2e, 0x52, 0x6f, 0x6c, 0x65, 0x52, 0x75, 0x6c, 0x65, 0x52, 0x05, 0x72, 0x75, 0x6c, - 0x65, 0x73, 0x22, 0x84, 0x01, 0x0a, 0x08, 0x52, 0x6f, 0x6c, 0x65, 0x52, 0x75, 0x6c, 0x65, 0x12, - 0x1d, 0x0a, 0x0a, 0x61, 0x70, 0x69, 0x5f, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x18, 0x01, 0x20, - 0x03, 0x28, 0x09, 0x52, 0x09, 0x61, 0x70, 0x69, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x12, 0x1c, - 0x0a, 0x09, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, - 0x09, 0x52, 0x09, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x12, 0x14, 0x0a, 0x05, - 0x76, 0x65, 0x72, 0x62, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x05, 0x76, 0x65, 0x72, - 0x62, 0x73, 0x12, 0x25, 0x0a, 0x0e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x6e, - 0x61, 0x6d, 0x65, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0d, 0x72, 0x65, 0x73, 0x6f, - 0x75, 0x72, 0x63, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x22, 0x7e, 0x0a, 0x12, 0x52, 0x6f, 0x6c, - 0x65, 0x42, 0x69, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, - 0x36, 0x0a, 0x08, 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, - 0x0b, 0x32, 0x1a, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x6f, 0x6c, 0x65, 0x42, - 0x69, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x53, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x08, 0x73, - 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x12, 0x30, 0x0a, 0x08, 0x72, 0x6f, 0x6c, 0x65, 0x5f, - 0x72, 0x65, 0x66, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x61, 0x70, 0x69, 0x2e, - 0x76, 0x31, 0x2e, 0x52, 0x6f, 0x6c, 0x65, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, - 0x52, 0x07, 0x72, 0x6f, 0x6c, 0x65, 0x52, 0x65, 0x66, 0x22, 0x77, 0x0a, 0x12, 0x52, 0x6f, 0x6c, - 0x65, 0x42, 0x69, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x53, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x12, - 0x12, 0x0a, 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6b, - 0x69, 0x6e, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, - 0x70, 0x61, 0x63, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6e, 0x61, 0x6d, 0x65, - 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x61, 0x70, 0x69, 0x5f, 0x67, 0x72, 0x6f, - 0x75, 0x70, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x61, 0x70, 0x69, 0x47, 0x72, 0x6f, - 0x75, 0x70, 0x22, 0x54, 0x0a, 0x0d, 0x52, 0x6f, 0x6c, 0x65, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, - 0x6e, 0x63, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x61, - 0x70, 0x69, 0x5f, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, - 0x61, 0x70, 0x69, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x22, 0xe4, 0x02, 0x0a, 0x17, 0x4b, 0x65, 0x64, - 0x61, 0x53, 0x63, 0x61, 0x6c, 0x65, 0x64, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x44, 0x65, 0x74, - 0x61, 0x69, 0x6c, 0x73, 0x12, 0x1f, 0x0a, 0x0b, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x5f, 0x6e, - 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x74, 0x61, 0x72, 0x67, 0x65, - 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x5f, - 0x6b, 0x69, 0x6e, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x74, 0x61, 0x72, 0x67, - 0x65, 0x74, 0x4b, 0x69, 0x6e, 0x64, 0x12, 0x21, 0x0a, 0x0c, 0x6d, 0x69, 0x6e, 0x5f, 0x72, 0x65, - 0x70, 0x6c, 0x69, 0x63, 0x61, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0b, 0x6d, 0x69, - 0x6e, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x73, 0x12, 0x21, 0x0a, 0x0c, 0x6d, 0x61, 0x78, - 0x5f, 0x72, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x52, - 0x0b, 0x6d, 0x61, 0x78, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x73, 0x12, 0x29, 0x0a, 0x10, - 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x72, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x73, - 0x18, 0x05, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0f, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x52, - 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x73, 0x12, 0x3b, 0x0a, 0x08, 0x74, 0x72, 0x69, 0x67, 0x67, - 0x65, 0x72, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x61, 0x70, 0x69, 0x2e, - 0x76, 0x31, 0x2e, 0x4b, 0x65, 0x64, 0x61, 0x53, 0x63, 0x61, 0x6c, 0x65, 0x64, 0x4f, 0x62, 0x6a, - 0x65, 0x63, 0x74, 0x54, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x52, 0x08, 0x74, 0x72, 0x69, 0x67, - 0x67, 0x65, 0x72, 0x73, 0x12, 0x41, 0x0a, 0x0a, 0x63, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, - 0x6e, 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, + 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0xd1, 0x01, 0x0a, 0x15, 0x53, 0x65, 0x72, 0x76, 0x69, + 0x63, 0x65, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, + 0x12, 0x45, 0x0a, 0x1f, 0x61, 0x75, 0x74, 0x6f, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x73, 0x65, + 0x72, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x74, 0x6f, + 0x6b, 0x65, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x1c, 0x61, 0x75, 0x74, 0x6f, 0x6d, + 0x6f, 0x75, 0x6e, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x41, 0x63, 0x63, 0x6f, 0x75, + 0x6e, 0x74, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x18, 0x0a, 0x07, 0x73, 0x65, 0x63, 0x72, 0x65, + 0x74, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x07, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, + 0x73, 0x12, 0x2c, 0x0a, 0x12, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x5f, 0x70, 0x75, 0x6c, 0x6c, 0x5f, + 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x10, 0x69, + 0x6d, 0x61, 0x67, 0x65, 0x50, 0x75, 0x6c, 0x6c, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x73, 0x12, + 0x29, 0x0a, 0x10, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x76, 0x65, 0x72, 0x73, + 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x72, 0x65, 0x73, 0x6f, 0x75, + 0x72, 0x63, 0x65, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x35, 0x0a, 0x0b, 0x52, 0x6f, + 0x6c, 0x65, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x26, 0x0a, 0x05, 0x72, 0x75, 0x6c, + 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, + 0x31, 0x2e, 0x52, 0x6f, 0x6c, 0x65, 0x52, 0x75, 0x6c, 0x65, 0x52, 0x05, 0x72, 0x75, 0x6c, 0x65, + 0x73, 0x22, 0x84, 0x01, 0x0a, 0x08, 0x52, 0x6f, 0x6c, 0x65, 0x52, 0x75, 0x6c, 0x65, 0x12, 0x1d, + 0x0a, 0x0a, 0x61, 0x70, 0x69, 0x5f, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x18, 0x01, 0x20, 0x03, + 0x28, 0x09, 0x52, 0x09, 0x61, 0x70, 0x69, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x12, 0x1c, 0x0a, + 0x09, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, + 0x52, 0x09, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x76, + 0x65, 0x72, 0x62, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x05, 0x76, 0x65, 0x72, 0x62, + 0x73, 0x12, 0x25, 0x0a, 0x0e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x6e, 0x61, + 0x6d, 0x65, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0d, 0x72, 0x65, 0x73, 0x6f, 0x75, + 0x72, 0x63, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x22, 0x7e, 0x0a, 0x12, 0x52, 0x6f, 0x6c, 0x65, + 0x42, 0x69, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x36, + 0x0a, 0x08, 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, + 0x32, 0x1a, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x6f, 0x6c, 0x65, 0x42, 0x69, + 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x53, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x08, 0x73, 0x75, + 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x12, 0x30, 0x0a, 0x08, 0x72, 0x6f, 0x6c, 0x65, 0x5f, 0x72, + 0x65, 0x66, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, + 0x31, 0x2e, 0x52, 0x6f, 0x6c, 0x65, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x52, + 0x07, 0x72, 0x6f, 0x6c, 0x65, 0x52, 0x65, 0x66, 0x22, 0x77, 0x0a, 0x12, 0x52, 0x6f, 0x6c, 0x65, + 0x42, 0x69, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x53, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x12, 0x12, + 0x0a, 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6b, 0x69, + 0x6e, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, + 0x61, 0x63, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, + 0x70, 0x61, 0x63, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x61, 0x70, 0x69, 0x5f, 0x67, 0x72, 0x6f, 0x75, + 0x70, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x61, 0x70, 0x69, 0x47, 0x72, 0x6f, 0x75, + 0x70, 0x22, 0x54, 0x0a, 0x0d, 0x52, 0x6f, 0x6c, 0x65, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, + 0x63, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x61, 0x70, + 0x69, 0x5f, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x61, + 0x70, 0x69, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x22, 0xe4, 0x02, 0x0a, 0x17, 0x4b, 0x65, 0x64, 0x61, + 0x53, 0x63, 0x61, 0x6c, 0x65, 0x64, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x44, 0x65, 0x74, 0x61, + 0x69, 0x6c, 0x73, 0x12, 0x1f, 0x0a, 0x0b, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x5f, 0x6e, 0x61, + 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, + 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x5f, 0x6b, + 0x69, 0x6e, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x74, 0x61, 0x72, 0x67, 0x65, + 0x74, 0x4b, 0x69, 0x6e, 0x64, 0x12, 0x21, 0x0a, 0x0c, 0x6d, 0x69, 0x6e, 0x5f, 0x72, 0x65, 0x70, + 0x6c, 0x69, 0x63, 0x61, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0b, 0x6d, 0x69, 0x6e, + 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x73, 0x12, 0x21, 0x0a, 0x0c, 0x6d, 0x61, 0x78, 0x5f, + 0x72, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0b, + 0x6d, 0x61, 0x78, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x73, 0x12, 0x29, 0x0a, 0x10, 0x63, + 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x72, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x73, 0x18, + 0x05, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0f, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x52, 0x65, + 0x70, 0x6c, 0x69, 0x63, 0x61, 0x73, 0x12, 0x3b, 0x0a, 0x08, 0x74, 0x72, 0x69, 0x67, 0x67, 0x65, + 0x72, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4b, 0x65, 0x64, 0x61, 0x53, 0x63, 0x61, 0x6c, 0x65, 0x64, 0x4f, 0x62, 0x6a, 0x65, - 0x63, 0x74, 0x43, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0a, 0x63, 0x6f, 0x6e, - 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, - 0x73, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, - 0xb5, 0x01, 0x0a, 0x17, 0x4b, 0x65, 0x64, 0x61, 0x53, 0x63, 0x61, 0x6c, 0x65, 0x64, 0x4f, 0x62, - 0x6a, 0x65, 0x63, 0x74, 0x54, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x12, 0x12, 0x0a, 0x04, 0x74, - 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, - 0x49, 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x02, 0x20, 0x03, 0x28, - 0x0b, 0x32, 0x2d, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4b, 0x65, 0x64, 0x61, 0x53, - 0x63, 0x61, 0x6c, 0x65, 0x64, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x54, 0x72, 0x69, 0x67, 0x67, - 0x65, 0x72, 0x2e, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x45, 0x6e, 0x74, 0x72, 0x79, - 0x52, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x1a, 0x3b, 0x0a, 0x0d, 0x4d, 0x65, - 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, - 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, - 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, - 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0xab, 0x01, 0x0a, 0x19, 0x4b, 0x65, 0x64, 0x61, - 0x53, 0x63, 0x61, 0x6c, 0x65, 0x64, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x43, 0x6f, 0x6e, 0x64, - 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, 0x61, - 0x74, 0x75, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, - 0x73, 0x12, 0x16, 0x0a, 0x06, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x06, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, - 0x73, 0x61, 0x67, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, - 0x61, 0x67, 0x65, 0x12, 0x30, 0x0a, 0x14, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x74, 0x72, 0x61, 0x6e, - 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, - 0x03, 0x52, 0x12, 0x6c, 0x61, 0x73, 0x74, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x69, 0x74, 0x69, 0x6f, - 0x6e, 0x54, 0x69, 0x6d, 0x65, 0x22, 0x85, 0x04, 0x0a, 0x18, 0x4b, 0x61, 0x72, 0x70, 0x65, 0x6e, - 0x74, 0x65, 0x72, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x44, 0x65, 0x74, 0x61, 0x69, - 0x6c, 0x73, 0x12, 0x23, 0x0a, 0x0d, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x74, - 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x72, 0x65, 0x73, 0x6f, 0x75, - 0x72, 0x63, 0x65, 0x54, 0x79, 0x70, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, - 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, - 0x42, 0x0a, 0x0a, 0x63, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x03, 0x20, - 0x03, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4b, 0x61, 0x72, - 0x70, 0x65, 0x6e, 0x74, 0x65, 0x72, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x43, 0x6f, - 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0a, 0x63, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, - 0x6f, 0x6e, 0x73, 0x12, 0x35, 0x0a, 0x08, 0x63, 0x61, 0x70, 0x61, 0x63, 0x69, 0x74, 0x79, 0x18, - 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4b, - 0x61, 0x72, 0x70, 0x65, 0x6e, 0x74, 0x65, 0x72, 0x43, 0x61, 0x70, 0x61, 0x63, 0x69, 0x74, 0x79, - 0x52, 0x08, 0x63, 0x61, 0x70, 0x61, 0x63, 0x69, 0x74, 0x79, 0x12, 0x40, 0x0a, 0x0c, 0x72, 0x65, - 0x71, 0x75, 0x69, 0x72, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, - 0x32, 0x1c, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4b, 0x61, 0x72, 0x70, 0x65, 0x6e, - 0x74, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x0c, - 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x24, 0x0a, 0x0e, - 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x5f, 0x72, 0x65, 0x66, 0x18, 0x06, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x6e, 0x6f, 0x64, 0x65, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x52, - 0x65, 0x66, 0x12, 0x1b, 0x0a, 0x09, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, - 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6e, 0x6f, 0x64, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, - 0x44, 0x0a, 0x06, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x73, 0x18, 0x08, 0x20, 0x03, 0x28, 0x0b, 0x32, - 0x2c, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4b, 0x61, 0x72, 0x70, 0x65, 0x6e, 0x74, + 0x63, 0x74, 0x54, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x52, 0x08, 0x74, 0x72, 0x69, 0x67, 0x67, + 0x65, 0x72, 0x73, 0x12, 0x41, 0x0a, 0x0a, 0x63, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, + 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, + 0x2e, 0x4b, 0x65, 0x64, 0x61, 0x53, 0x63, 0x61, 0x6c, 0x65, 0x64, 0x4f, 0x62, 0x6a, 0x65, 0x63, + 0x74, 0x43, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0a, 0x63, 0x6f, 0x6e, 0x64, + 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, + 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, 0xb5, + 0x01, 0x0a, 0x17, 0x4b, 0x65, 0x64, 0x61, 0x53, 0x63, 0x61, 0x6c, 0x65, 0x64, 0x4f, 0x62, 0x6a, + 0x65, 0x63, 0x74, 0x54, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, + 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x49, + 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, + 0x32, 0x2d, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4b, 0x65, 0x64, 0x61, 0x53, 0x63, + 0x61, 0x6c, 0x65, 0x64, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x54, 0x72, 0x69, 0x67, 0x67, 0x65, + 0x72, 0x2e, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, + 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x1a, 0x3b, 0x0a, 0x0d, 0x4d, 0x65, 0x74, + 0x61, 0x64, 0x61, 0x74, 0x61, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, + 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, + 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, + 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0xab, 0x01, 0x0a, 0x19, 0x4b, 0x65, 0x64, 0x61, 0x53, + 0x63, 0x61, 0x6c, 0x65, 0x64, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x43, 0x6f, 0x6e, 0x64, 0x69, + 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, + 0x75, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, + 0x12, 0x16, 0x0a, 0x06, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x06, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, + 0x61, 0x67, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, + 0x67, 0x65, 0x12, 0x30, 0x0a, 0x14, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x74, 0x72, 0x61, 0x6e, 0x73, + 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, + 0x52, 0x12, 0x6c, 0x61, 0x73, 0x74, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, + 0x54, 0x69, 0x6d, 0x65, 0x22, 0x85, 0x04, 0x0a, 0x18, 0x4b, 0x61, 0x72, 0x70, 0x65, 0x6e, 0x74, 0x65, 0x72, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, - 0x73, 0x2e, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x06, 0x6c, - 0x69, 0x6d, 0x69, 0x74, 0x73, 0x12, 0x2b, 0x0a, 0x11, 0x6b, 0x61, 0x72, 0x70, 0x65, 0x6e, 0x74, - 0x65, 0x72, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x10, 0x6b, 0x61, 0x72, 0x70, 0x65, 0x6e, 0x74, 0x65, 0x72, 0x56, 0x65, 0x72, 0x73, 0x69, - 0x6f, 0x6e, 0x1a, 0x39, 0x0a, 0x0b, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x73, 0x45, 0x6e, 0x74, 0x72, - 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, - 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0xac, 0x01, - 0x0a, 0x1a, 0x4b, 0x61, 0x72, 0x70, 0x65, 0x6e, 0x74, 0x65, 0x72, 0x52, 0x65, 0x73, 0x6f, 0x75, - 0x72, 0x63, 0x65, 0x43, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, + 0x73, 0x12, 0x23, 0x0a, 0x0d, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x74, 0x79, + 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, + 0x63, 0x65, 0x54, 0x79, 0x70, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x42, + 0x0a, 0x0a, 0x63, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x03, 0x20, 0x03, + 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4b, 0x61, 0x72, 0x70, + 0x65, 0x6e, 0x74, 0x65, 0x72, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x43, 0x6f, 0x6e, + 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0a, 0x63, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, + 0x6e, 0x73, 0x12, 0x35, 0x0a, 0x08, 0x63, 0x61, 0x70, 0x61, 0x63, 0x69, 0x74, 0x79, 0x18, 0x04, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4b, 0x61, + 0x72, 0x70, 0x65, 0x6e, 0x74, 0x65, 0x72, 0x43, 0x61, 0x70, 0x61, 0x63, 0x69, 0x74, 0x79, 0x52, + 0x08, 0x63, 0x61, 0x70, 0x61, 0x63, 0x69, 0x74, 0x79, 0x12, 0x40, 0x0a, 0x0c, 0x72, 0x65, 0x71, + 0x75, 0x69, 0x72, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, + 0x1c, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4b, 0x61, 0x72, 0x70, 0x65, 0x6e, 0x74, + 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x0c, 0x72, + 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x24, 0x0a, 0x0e, 0x6e, + 0x6f, 0x64, 0x65, 0x5f, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x5f, 0x72, 0x65, 0x66, 0x18, 0x06, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x0c, 0x6e, 0x6f, 0x64, 0x65, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x52, 0x65, + 0x66, 0x12, 0x1b, 0x0a, 0x09, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x07, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6e, 0x6f, 0x64, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x44, + 0x0a, 0x06, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x73, 0x18, 0x08, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2c, + 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4b, 0x61, 0x72, 0x70, 0x65, 0x6e, 0x74, 0x65, + 0x72, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, + 0x2e, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x06, 0x6c, 0x69, + 0x6d, 0x69, 0x74, 0x73, 0x12, 0x2b, 0x0a, 0x11, 0x6b, 0x61, 0x72, 0x70, 0x65, 0x6e, 0x74, 0x65, + 0x72, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x10, 0x6b, 0x61, 0x72, 0x70, 0x65, 0x6e, 0x74, 0x65, 0x72, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, + 0x6e, 0x1a, 0x39, 0x0a, 0x0b, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, + 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, + 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0xac, 0x01, 0x0a, + 0x1a, 0x4b, 0x61, 0x72, 0x70, 0x65, 0x6e, 0x74, 0x65, 0x72, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, + 0x63, 0x65, 0x43, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x74, + 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, + 0x16, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x72, 0x65, 0x61, 0x73, 0x6f, + 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x12, + 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x30, 0x0a, 0x14, 0x6c, 0x61, 0x73, + 0x74, 0x5f, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x74, 0x69, 0x6d, + 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x12, 0x6c, 0x61, 0x73, 0x74, 0x54, 0x72, 0x61, + 0x6e, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x69, 0x6d, 0x65, 0x22, 0xb3, 0x01, 0x0a, 0x11, + 0x4b, 0x61, 0x72, 0x70, 0x65, 0x6e, 0x74, 0x65, 0x72, 0x43, 0x61, 0x70, 0x61, 0x63, 0x69, 0x74, + 0x79, 0x12, 0x10, 0x0a, 0x03, 0x63, 0x70, 0x75, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, + 0x63, 0x70, 0x75, 0x12, 0x16, 0x0a, 0x06, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x06, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x12, 0x3a, 0x0a, 0x05, 0x6f, + 0x74, 0x68, 0x65, 0x72, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x61, 0x70, 0x69, + 0x2e, 0x76, 0x31, 0x2e, 0x4b, 0x61, 0x72, 0x70, 0x65, 0x6e, 0x74, 0x65, 0x72, 0x43, 0x61, 0x70, + 0x61, 0x63, 0x69, 0x74, 0x79, 0x2e, 0x4f, 0x74, 0x68, 0x65, 0x72, 0x45, 0x6e, 0x74, 0x72, 0x79, + 0x52, 0x05, 0x6f, 0x74, 0x68, 0x65, 0x72, 0x1a, 0x38, 0x0a, 0x0a, 0x4f, 0x74, 0x68, 0x65, 0x72, + 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, + 0x01, 0x22, 0x5c, 0x0a, 0x14, 0x4b, 0x61, 0x72, 0x70, 0x65, 0x6e, 0x74, 0x65, 0x72, 0x52, 0x65, + 0x71, 0x75, 0x69, 0x72, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x1a, 0x0a, 0x08, 0x6f, + 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6f, + 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x12, 0x16, 0x0a, 0x06, 0x76, 0x61, 0x6c, 0x75, 0x65, + 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x06, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x22, + 0x43, 0x0a, 0x13, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x46, 0x69, 0x65, 0x6c, 0x64, + 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x12, 0x14, 0x0a, 0x05, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x12, 0x16, 0x0a, 0x06, + 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x06, 0x76, 0x61, + 0x6c, 0x75, 0x65, 0x73, 0x22, 0x7e, 0x0a, 0x1a, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, + 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x4f, 0x70, 0x74, 0x69, 0x6f, + 0x6e, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x05, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x6c, 0x61, 0x62, 0x65, + 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x12, 0x16, + 0x0a, 0x06, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x06, + 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x72, 0x75, 0x6e, 0x63, 0x61, + 0x74, 0x65, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x74, 0x72, 0x75, 0x6e, 0x63, + 0x61, 0x74, 0x65, 0x64, 0x22, 0x94, 0x07, 0x0a, 0x0f, 0x57, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, + 0x64, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x73, 0x12, 0x49, 0x0a, 0x12, 0x6e, 0x61, 0x6d, 0x65, + 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x18, 0x0b, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x61, + 0x62, 0x65, 0x6c, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x48, 0x00, 0x52, 0x11, 0x6e, + 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, + 0x88, 0x01, 0x01, 0x12, 0x47, 0x0a, 0x11, 0x77, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x5f, + 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, + 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x53, 0x65, 0x6c, + 0x65, 0x63, 0x74, 0x6f, 0x72, 0x48, 0x01, 0x52, 0x10, 0x77, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, + 0x64, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x88, 0x01, 0x01, 0x12, 0x36, 0x0a, 0x0b, + 0x6b, 0x69, 0x6e, 0x64, 0x5f, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x18, 0x0d, 0x20, 0x03, 0x28, + 0x0e, 0x32, 0x15, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4b, 0x38, 0x73, 0x4f, 0x62, + 0x6a, 0x65, 0x63, 0x74, 0x4b, 0x69, 0x6e, 0x64, 0x52, 0x0a, 0x6b, 0x69, 0x6e, 0x64, 0x46, 0x69, + 0x6c, 0x74, 0x65, 0x72, 0x12, 0x3c, 0x0a, 0x0c, 0x6e, 0x61, 0x6d, 0x65, 0x5f, 0x70, 0x61, 0x74, + 0x74, 0x65, 0x72, 0x6e, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x61, 0x70, 0x69, + 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x67, 0x65, 0x78, 0x50, 0x61, 0x74, 0x74, 0x65, 0x72, 0x6e, + 0x48, 0x02, 0x52, 0x0b, 0x6e, 0x61, 0x6d, 0x65, 0x50, 0x61, 0x74, 0x74, 0x65, 0x72, 0x6e, 0x88, + 0x01, 0x01, 0x12, 0x4b, 0x0a, 0x13, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x5f, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x15, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x53, 0x65, + 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x48, 0x03, 0x52, 0x12, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x88, 0x01, 0x01, 0x12, + 0x25, 0x0a, 0x0e, 0x77, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x5f, 0x6e, 0x61, 0x6d, 0x65, + 0x73, 0x18, 0x10, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0d, 0x77, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, + 0x64, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x12, 0x27, 0x0a, 0x0f, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, + 0x67, 0x65, 0x5f, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x18, 0x11, 0x20, 0x03, 0x28, 0x09, 0x52, + 0x0e, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x12, + 0x36, 0x0a, 0x17, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x5f, 0x76, 0x65, 0x72, 0x73, + 0x69, 0x6f, 0x6e, 0x5f, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x18, 0x12, 0x20, 0x03, 0x28, 0x09, + 0x52, 0x15, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, + 0x6e, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x12, 0x28, 0x0a, 0x10, 0x6e, 0x6f, 0x64, 0x65, 0x5f, + 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x18, 0x15, 0x20, 0x03, 0x28, + 0x09, 0x52, 0x0e, 0x6e, 0x6f, 0x64, 0x65, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4e, 0x61, 0x6d, 0x65, + 0x73, 0x12, 0x27, 0x0a, 0x0f, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x6e, + 0x61, 0x6d, 0x65, 0x73, 0x18, 0x16, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0e, 0x6e, 0x61, 0x6d, 0x65, + 0x73, 0x70, 0x61, 0x63, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x12, 0x46, 0x0a, 0x11, 0x6e, 0x61, + 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x70, 0x61, 0x74, 0x74, 0x65, 0x72, 0x6e, 0x18, + 0x17, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x52, + 0x65, 0x67, 0x65, 0x78, 0x50, 0x61, 0x74, 0x74, 0x65, 0x72, 0x6e, 0x48, 0x04, 0x52, 0x10, 0x6e, + 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x50, 0x61, 0x74, 0x74, 0x65, 0x72, 0x6e, 0x88, + 0x01, 0x01, 0x12, 0x39, 0x0a, 0x06, 0x68, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x18, 0x18, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x57, 0x6f, 0x72, 0x6b, + 0x6c, 0x6f, 0x61, 0x64, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, + 0x48, 0x05, 0x52, 0x06, 0x68, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x88, 0x01, 0x01, 0x12, 0x1f, 0x0a, + 0x0b, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x5f, 0x6b, 0x69, 0x6e, 0x64, 0x73, 0x18, 0x19, 0x20, 0x03, + 0x28, 0x09, 0x52, 0x0a, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x4b, 0x69, 0x6e, 0x64, 0x73, 0x12, 0x34, + 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x1e, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1c, + 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, + 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x52, 0x06, 0x73, 0x74, + 0x61, 0x74, 0x75, 0x73, 0x42, 0x15, 0x0a, 0x13, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, + 0x63, 0x65, 0x5f, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x42, 0x14, 0x0a, 0x12, 0x5f, + 0x77, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x5f, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, + 0x72, 0x42, 0x0f, 0x0a, 0x0d, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x5f, 0x70, 0x61, 0x74, 0x74, 0x65, + 0x72, 0x6e, 0x42, 0x16, 0x0a, 0x14, 0x5f, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x5f, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x42, 0x14, 0x0a, 0x12, 0x5f, 0x6e, + 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x70, 0x61, 0x74, 0x74, 0x65, 0x72, 0x6e, + 0x42, 0x09, 0x0a, 0x07, 0x5f, 0x68, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x22, 0xc6, 0x01, 0x0a, 0x14, + 0x57, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x46, 0x69, + 0x6c, 0x74, 0x65, 0x72, 0x12, 0x3a, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x01, + 0x20, 0x03, 0x28, 0x0e, 0x32, 0x22, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x57, 0x6f, + 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x53, 0x74, 0x61, 0x74, + 0x75, 0x73, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, + 0x12, 0x33, 0x0a, 0x06, 0x69, 0x73, 0x73, 0x75, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0e, + 0x32, 0x1b, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, + 0x61, 0x64, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x49, 0x73, 0x73, 0x75, 0x65, 0x52, 0x06, 0x69, + 0x73, 0x73, 0x75, 0x65, 0x73, 0x12, 0x3d, 0x0a, 0x0b, 0x69, 0x73, 0x73, 0x75, 0x65, 0x5f, 0x6d, + 0x61, 0x74, 0x63, 0x68, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1c, 0x2e, 0x61, 0x70, 0x69, + 0x2e, 0x76, 0x31, 0x2e, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x49, 0x73, 0x73, 0x75, 0x65, 0x4d, + 0x61, 0x74, 0x63, 0x68, 0x4d, 0x6f, 0x64, 0x65, 0x52, 0x0a, 0x69, 0x73, 0x73, 0x75, 0x65, 0x4d, + 0x61, 0x74, 0x63, 0x68, 0x22, 0x94, 0x04, 0x0a, 0x1a, 0x50, 0x6f, 0x64, 0x44, 0x69, 0x73, 0x72, + 0x75, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x75, 0x64, 0x67, 0x65, 0x74, 0x44, 0x65, 0x74, 0x61, + 0x69, 0x6c, 0x73, 0x12, 0x23, 0x0a, 0x0d, 0x6d, 0x69, 0x6e, 0x5f, 0x61, 0x76, 0x61, 0x69, 0x6c, + 0x61, 0x62, 0x6c, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0c, 0x6d, 0x69, 0x6e, 0x41, + 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x12, 0x27, 0x0a, 0x0f, 0x6d, 0x61, 0x78, 0x5f, + 0x75, 0x6e, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x05, 0x52, 0x0e, 0x6d, 0x61, 0x78, 0x55, 0x6e, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, + 0x65, 0x12, 0x27, 0x0a, 0x0f, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x68, 0x65, 0x61, + 0x6c, 0x74, 0x68, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0e, 0x63, 0x75, 0x72, 0x72, + 0x65, 0x6e, 0x74, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x79, 0x12, 0x27, 0x0a, 0x0f, 0x64, 0x65, + 0x73, 0x69, 0x72, 0x65, 0x64, 0x5f, 0x68, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x79, 0x18, 0x04, 0x20, + 0x01, 0x28, 0x05, 0x52, 0x0e, 0x64, 0x65, 0x73, 0x69, 0x72, 0x65, 0x64, 0x48, 0x65, 0x61, 0x6c, + 0x74, 0x68, 0x79, 0x12, 0x2f, 0x0a, 0x13, 0x64, 0x69, 0x73, 0x72, 0x75, 0x70, 0x74, 0x69, 0x6f, + 0x6e, 0x73, 0x5f, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x05, + 0x52, 0x12, 0x64, 0x69, 0x73, 0x72, 0x75, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x41, 0x6c, 0x6c, + 0x6f, 0x77, 0x65, 0x64, 0x12, 0x23, 0x0a, 0x0d, 0x65, 0x78, 0x70, 0x65, 0x63, 0x74, 0x65, 0x64, + 0x5f, 0x70, 0x6f, 0x64, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0c, 0x65, 0x78, 0x70, + 0x65, 0x63, 0x74, 0x65, 0x64, 0x50, 0x6f, 0x64, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, 0x61, + 0x74, 0x75, 0x73, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, + 0x73, 0x12, 0x5f, 0x0a, 0x0f, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x5f, 0x6c, 0x61, + 0x62, 0x65, 0x6c, 0x73, 0x18, 0x08, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x36, 0x2e, 0x61, 0x70, 0x69, + 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x6f, 0x64, 0x44, 0x69, 0x73, 0x72, 0x75, 0x70, 0x74, 0x69, 0x6f, + 0x6e, 0x42, 0x75, 0x64, 0x67, 0x65, 0x74, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x2e, 0x53, + 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x45, 0x6e, 0x74, + 0x72, 0x79, 0x52, 0x0e, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x4c, 0x61, 0x62, 0x65, + 0x6c, 0x73, 0x12, 0x44, 0x0a, 0x0a, 0x63, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, + 0x18, 0x09, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, + 0x50, 0x6f, 0x64, 0x44, 0x69, 0x73, 0x72, 0x75, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x75, 0x64, + 0x67, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0a, 0x63, 0x6f, + 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x1a, 0x41, 0x0a, 0x13, 0x53, 0x65, 0x6c, 0x65, + 0x63, 0x74, 0x6f, 0x72, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, + 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, + 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0xae, 0x01, 0x0a, 0x1c, + 0x50, 0x6f, 0x64, 0x44, 0x69, 0x73, 0x72, 0x75, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x75, 0x64, + 0x67, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x72, 0x65, 0x61, 0x73, @@ -18706,123 +23528,43 @@ var file_api_v1_common_proto_rawDesc = []byte{ 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x30, 0x0a, 0x14, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x12, 0x6c, 0x61, 0x73, 0x74, 0x54, 0x72, - 0x61, 0x6e, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x69, 0x6d, 0x65, 0x22, 0xb3, 0x01, 0x0a, - 0x11, 0x4b, 0x61, 0x72, 0x70, 0x65, 0x6e, 0x74, 0x65, 0x72, 0x43, 0x61, 0x70, 0x61, 0x63, 0x69, - 0x74, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x63, 0x70, 0x75, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x03, 0x63, 0x70, 0x75, 0x12, 0x16, 0x0a, 0x06, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x12, 0x3a, 0x0a, 0x05, - 0x6f, 0x74, 0x68, 0x65, 0x72, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x61, 0x70, - 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4b, 0x61, 0x72, 0x70, 0x65, 0x6e, 0x74, 0x65, 0x72, 0x43, 0x61, - 0x70, 0x61, 0x63, 0x69, 0x74, 0x79, 0x2e, 0x4f, 0x74, 0x68, 0x65, 0x72, 0x45, 0x6e, 0x74, 0x72, - 0x79, 0x52, 0x05, 0x6f, 0x74, 0x68, 0x65, 0x72, 0x1a, 0x38, 0x0a, 0x0a, 0x4f, 0x74, 0x68, 0x65, + 0x61, 0x6e, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x69, 0x6d, 0x65, 0x22, 0xa3, 0x04, 0x0a, + 0x14, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x51, 0x75, 0x6f, 0x74, 0x61, 0x44, 0x65, + 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x4d, 0x0a, 0x0b, 0x68, 0x61, 0x72, 0x64, 0x5f, 0x6c, 0x69, + 0x6d, 0x69, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2c, 0x2e, 0x61, 0x70, 0x69, + 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x51, 0x75, 0x6f, 0x74, + 0x61, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x2e, 0x48, 0x61, 0x72, 0x64, 0x4c, 0x69, 0x6d, + 0x69, 0x74, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0a, 0x68, 0x61, 0x72, 0x64, 0x4c, 0x69, + 0x6d, 0x69, 0x74, 0x73, 0x12, 0x3a, 0x0a, 0x04, 0x75, 0x73, 0x65, 0x64, 0x18, 0x02, 0x20, 0x03, + 0x28, 0x0b, 0x32, 0x26, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x73, 0x6f, + 0x75, 0x72, 0x63, 0x65, 0x51, 0x75, 0x6f, 0x74, 0x61, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, + 0x2e, 0x55, 0x73, 0x65, 0x64, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x04, 0x75, 0x73, 0x65, 0x64, + 0x12, 0x16, 0x0a, 0x06, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, + 0x52, 0x06, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x73, 0x12, 0x56, 0x0a, 0x0e, 0x73, 0x63, 0x6f, 0x70, + 0x65, 0x5f, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, + 0x32, 0x2f, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, + 0x63, 0x65, 0x51, 0x75, 0x6f, 0x74, 0x61, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x2e, 0x53, + 0x63, 0x6f, 0x70, 0x65, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x45, 0x6e, 0x74, 0x72, + 0x79, 0x52, 0x0d, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, + 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x3e, 0x0a, 0x0a, 0x63, 0x6f, 0x6e, 0x64, + 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x61, + 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x51, 0x75, + 0x6f, 0x74, 0x61, 0x43, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0a, 0x63, 0x6f, + 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x1a, 0x3d, 0x0a, 0x0f, 0x48, 0x61, 0x72, 0x64, + 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, + 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, + 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, + 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x37, 0x0a, 0x09, 0x55, 0x73, 0x65, 0x64, 0x45, + 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, + 0x1a, 0x40, 0x0a, 0x12, 0x53, 0x63, 0x6f, 0x70, 0x65, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, - 0x38, 0x01, 0x22, 0x5c, 0x0a, 0x14, 0x4b, 0x61, 0x72, 0x70, 0x65, 0x6e, 0x74, 0x65, 0x72, 0x52, - 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, - 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x1a, 0x0a, 0x08, - 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, - 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x12, 0x16, 0x0a, 0x06, 0x76, 0x61, 0x6c, 0x75, - 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x06, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, - 0x22, 0x43, 0x0a, 0x13, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x46, 0x69, 0x65, 0x6c, - 0x64, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x12, 0x14, 0x0a, 0x05, 0x66, 0x69, 0x65, 0x6c, 0x64, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x12, 0x16, 0x0a, - 0x06, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x06, 0x76, - 0x61, 0x6c, 0x75, 0x65, 0x73, 0x22, 0x7e, 0x0a, 0x1a, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, - 0x65, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x4f, 0x70, 0x74, 0x69, - 0x6f, 0x6e, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x05, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x6c, 0x61, 0x62, - 0x65, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x12, - 0x16, 0x0a, 0x06, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, - 0x06, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x72, 0x75, 0x6e, 0x63, - 0x61, 0x74, 0x65, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x74, 0x72, 0x75, 0x6e, - 0x63, 0x61, 0x74, 0x65, 0x64, 0x22, 0xad, 0x06, 0x0a, 0x0f, 0x57, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, - 0x61, 0x64, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x73, 0x12, 0x49, 0x0a, 0x12, 0x6e, 0x61, 0x6d, - 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x18, - 0x0b, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4c, - 0x61, 0x62, 0x65, 0x6c, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x48, 0x00, 0x52, 0x11, - 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, - 0x72, 0x88, 0x01, 0x01, 0x12, 0x47, 0x0a, 0x11, 0x77, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, - 0x5f, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x15, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x53, 0x65, - 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x48, 0x01, 0x52, 0x10, 0x77, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, - 0x61, 0x64, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x88, 0x01, 0x01, 0x12, 0x36, 0x0a, - 0x0b, 0x6b, 0x69, 0x6e, 0x64, 0x5f, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x18, 0x0d, 0x20, 0x03, - 0x28, 0x0e, 0x32, 0x15, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4b, 0x38, 0x73, 0x4f, - 0x62, 0x6a, 0x65, 0x63, 0x74, 0x4b, 0x69, 0x6e, 0x64, 0x52, 0x0a, 0x6b, 0x69, 0x6e, 0x64, 0x46, - 0x69, 0x6c, 0x74, 0x65, 0x72, 0x12, 0x3c, 0x0a, 0x0c, 0x6e, 0x61, 0x6d, 0x65, 0x5f, 0x70, 0x61, - 0x74, 0x74, 0x65, 0x72, 0x6e, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x61, 0x70, - 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x67, 0x65, 0x78, 0x50, 0x61, 0x74, 0x74, 0x65, 0x72, - 0x6e, 0x48, 0x02, 0x52, 0x0b, 0x6e, 0x61, 0x6d, 0x65, 0x50, 0x61, 0x74, 0x74, 0x65, 0x72, 0x6e, - 0x88, 0x01, 0x01, 0x12, 0x4b, 0x0a, 0x13, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x5f, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x15, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x53, - 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x48, 0x03, 0x52, 0x12, 0x61, 0x6e, 0x6e, 0x6f, 0x74, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x88, 0x01, 0x01, - 0x12, 0x25, 0x0a, 0x0e, 0x77, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x5f, 0x6e, 0x61, 0x6d, - 0x65, 0x73, 0x18, 0x10, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0d, 0x77, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, - 0x61, 0x64, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x12, 0x27, 0x0a, 0x0f, 0x6c, 0x61, 0x6e, 0x67, 0x75, - 0x61, 0x67, 0x65, 0x5f, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x18, 0x11, 0x20, 0x03, 0x28, 0x09, - 0x52, 0x0e, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, - 0x12, 0x36, 0x0a, 0x17, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x5f, 0x76, 0x65, 0x72, - 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x18, 0x12, 0x20, 0x03, 0x28, - 0x09, 0x52, 0x15, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x56, 0x65, 0x72, 0x73, 0x69, - 0x6f, 0x6e, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x12, 0x28, 0x0a, 0x10, 0x6e, 0x6f, 0x64, 0x65, - 0x5f, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x18, 0x15, 0x20, 0x03, - 0x28, 0x09, 0x52, 0x0e, 0x6e, 0x6f, 0x64, 0x65, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4e, 0x61, 0x6d, - 0x65, 0x73, 0x12, 0x27, 0x0a, 0x0f, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, - 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x18, 0x16, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0e, 0x6e, 0x61, 0x6d, - 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x12, 0x46, 0x0a, 0x11, 0x6e, - 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x70, 0x61, 0x74, 0x74, 0x65, 0x72, 0x6e, - 0x18, 0x17, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, - 0x52, 0x65, 0x67, 0x65, 0x78, 0x50, 0x61, 0x74, 0x74, 0x65, 0x72, 0x6e, 0x48, 0x04, 0x52, 0x10, - 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x50, 0x61, 0x74, 0x74, 0x65, 0x72, 0x6e, - 0x88, 0x01, 0x01, 0x12, 0x34, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x1e, 0x20, - 0x01, 0x28, 0x0e, 0x32, 0x1c, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x57, 0x6f, 0x72, - 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x46, 0x69, 0x6c, 0x74, 0x65, - 0x72, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x42, 0x15, 0x0a, 0x13, 0x5f, 0x6e, 0x61, - 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, - 0x42, 0x14, 0x0a, 0x12, 0x5f, 0x77, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x5f, 0x73, 0x65, - 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x42, 0x0f, 0x0a, 0x0d, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x5f, - 0x70, 0x61, 0x74, 0x74, 0x65, 0x72, 0x6e, 0x42, 0x16, 0x0a, 0x14, 0x5f, 0x61, 0x6e, 0x6e, 0x6f, - 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x42, - 0x14, 0x0a, 0x12, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x70, 0x61, - 0x74, 0x74, 0x65, 0x72, 0x6e, 0x22, 0x94, 0x04, 0x0a, 0x1a, 0x50, 0x6f, 0x64, 0x44, 0x69, 0x73, - 0x72, 0x75, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x75, 0x64, 0x67, 0x65, 0x74, 0x44, 0x65, 0x74, - 0x61, 0x69, 0x6c, 0x73, 0x12, 0x23, 0x0a, 0x0d, 0x6d, 0x69, 0x6e, 0x5f, 0x61, 0x76, 0x61, 0x69, - 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0c, 0x6d, 0x69, 0x6e, - 0x41, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x12, 0x27, 0x0a, 0x0f, 0x6d, 0x61, 0x78, - 0x5f, 0x75, 0x6e, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x05, 0x52, 0x0e, 0x6d, 0x61, 0x78, 0x55, 0x6e, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, - 0x6c, 0x65, 0x12, 0x27, 0x0a, 0x0f, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x68, 0x65, - 0x61, 0x6c, 0x74, 0x68, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0e, 0x63, 0x75, 0x72, - 0x72, 0x65, 0x6e, 0x74, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x79, 0x12, 0x27, 0x0a, 0x0f, 0x64, - 0x65, 0x73, 0x69, 0x72, 0x65, 0x64, 0x5f, 0x68, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x79, 0x18, 0x04, - 0x20, 0x01, 0x28, 0x05, 0x52, 0x0e, 0x64, 0x65, 0x73, 0x69, 0x72, 0x65, 0x64, 0x48, 0x65, 0x61, - 0x6c, 0x74, 0x68, 0x79, 0x12, 0x2f, 0x0a, 0x13, 0x64, 0x69, 0x73, 0x72, 0x75, 0x70, 0x74, 0x69, - 0x6f, 0x6e, 0x73, 0x5f, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, - 0x05, 0x52, 0x12, 0x64, 0x69, 0x73, 0x72, 0x75, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x41, 0x6c, - 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x12, 0x23, 0x0a, 0x0d, 0x65, 0x78, 0x70, 0x65, 0x63, 0x74, 0x65, - 0x64, 0x5f, 0x70, 0x6f, 0x64, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0c, 0x65, 0x78, - 0x70, 0x65, 0x63, 0x74, 0x65, 0x64, 0x50, 0x6f, 0x64, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, - 0x61, 0x74, 0x75, 0x73, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, - 0x75, 0x73, 0x12, 0x5f, 0x0a, 0x0f, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x5f, 0x6c, - 0x61, 0x62, 0x65, 0x6c, 0x73, 0x18, 0x08, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x36, 0x2e, 0x61, 0x70, - 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x6f, 0x64, 0x44, 0x69, 0x73, 0x72, 0x75, 0x70, 0x74, 0x69, - 0x6f, 0x6e, 0x42, 0x75, 0x64, 0x67, 0x65, 0x74, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x2e, - 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x45, 0x6e, - 0x74, 0x72, 0x79, 0x52, 0x0e, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x4c, 0x61, 0x62, - 0x65, 0x6c, 0x73, 0x12, 0x44, 0x0a, 0x0a, 0x63, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, - 0x73, 0x18, 0x09, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, - 0x2e, 0x50, 0x6f, 0x64, 0x44, 0x69, 0x73, 0x72, 0x75, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x75, - 0x64, 0x67, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0a, 0x63, - 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x1a, 0x41, 0x0a, 0x13, 0x53, 0x65, 0x6c, - 0x65, 0x63, 0x74, 0x6f, 0x72, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, - 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, - 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0xae, 0x01, 0x0a, - 0x1c, 0x50, 0x6f, 0x64, 0x44, 0x69, 0x73, 0x72, 0x75, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x75, - 0x64, 0x67, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a, + 0x38, 0x01, 0x22, 0xa8, 0x01, 0x0a, 0x16, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x51, + 0x75, 0x6f, 0x74, 0x61, 0x43, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x72, 0x65, 0x61, @@ -18831,646 +23573,1453 @@ var file_api_v1_common_proto_rawDesc = []byte{ 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x30, 0x0a, 0x14, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x12, 0x6c, 0x61, 0x73, 0x74, 0x54, - 0x72, 0x61, 0x6e, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x69, 0x6d, 0x65, 0x22, 0xa3, 0x04, - 0x0a, 0x14, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x51, 0x75, 0x6f, 0x74, 0x61, 0x44, - 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x4d, 0x0a, 0x0b, 0x68, 0x61, 0x72, 0x64, 0x5f, 0x6c, - 0x69, 0x6d, 0x69, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2c, 0x2e, 0x61, 0x70, - 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x51, 0x75, 0x6f, - 0x74, 0x61, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x2e, 0x48, 0x61, 0x72, 0x64, 0x4c, 0x69, - 0x6d, 0x69, 0x74, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0a, 0x68, 0x61, 0x72, 0x64, 0x4c, - 0x69, 0x6d, 0x69, 0x74, 0x73, 0x12, 0x3a, 0x0a, 0x04, 0x75, 0x73, 0x65, 0x64, 0x18, 0x02, 0x20, - 0x03, 0x28, 0x0b, 0x32, 0x26, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x73, - 0x6f, 0x75, 0x72, 0x63, 0x65, 0x51, 0x75, 0x6f, 0x74, 0x61, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, - 0x73, 0x2e, 0x55, 0x73, 0x65, 0x64, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x04, 0x75, 0x73, 0x65, - 0x64, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, - 0x09, 0x52, 0x06, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x73, 0x12, 0x56, 0x0a, 0x0e, 0x73, 0x63, 0x6f, - 0x70, 0x65, 0x5f, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x18, 0x04, 0x20, 0x03, 0x28, - 0x0b, 0x32, 0x2f, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, - 0x72, 0x63, 0x65, 0x51, 0x75, 0x6f, 0x74, 0x61, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x2e, - 0x53, 0x63, 0x6f, 0x70, 0x65, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x45, 0x6e, 0x74, - 0x72, 0x79, 0x52, 0x0d, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, - 0x72, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x3e, 0x0a, 0x0a, 0x63, 0x6f, 0x6e, - 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1e, 0x2e, - 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x51, - 0x75, 0x6f, 0x74, 0x61, 0x43, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0a, 0x63, - 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x1a, 0x3d, 0x0a, 0x0f, 0x48, 0x61, 0x72, - 0x64, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, - 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, - 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, - 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x37, 0x0a, 0x09, 0x55, 0x73, 0x65, 0x64, - 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, - 0x01, 0x1a, 0x40, 0x0a, 0x12, 0x53, 0x63, 0x6f, 0x70, 0x65, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, - 0x6f, 0x72, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, - 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, - 0x02, 0x38, 0x01, 0x22, 0xa8, 0x01, 0x0a, 0x16, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, - 0x51, 0x75, 0x6f, 0x74, 0x61, 0x43, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x12, - 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, - 0x70, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x72, 0x65, - 0x61, 0x73, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x72, 0x65, 0x61, 0x73, - 0x6f, 0x6e, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x04, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x30, 0x0a, 0x14, - 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x5f, - 0x74, 0x69, 0x6d, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x12, 0x6c, 0x61, 0x73, 0x74, - 0x54, 0x72, 0x61, 0x6e, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x69, 0x6d, 0x65, 0x22, 0xc1, - 0x02, 0x0a, 0x11, 0x56, 0x6f, 0x6c, 0x63, 0x61, 0x6e, 0x6f, 0x4a, 0x6f, 0x62, 0x44, 0x65, 0x74, - 0x61, 0x69, 0x6c, 0x73, 0x12, 0x39, 0x0a, 0x0a, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, - 0x72, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, - 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x54, 0x65, 0x6d, 0x70, 0x6c, - 0x61, 0x74, 0x65, 0x52, 0x0a, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x73, 0x12, - 0x3b, 0x0a, 0x0a, 0x63, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x02, 0x20, - 0x03, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x56, 0x6f, 0x6c, - 0x63, 0x61, 0x6e, 0x6f, 0x4a, 0x6f, 0x62, 0x43, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, - 0x52, 0x0a, 0x63, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x14, 0x0a, 0x05, - 0x71, 0x75, 0x65, 0x75, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x71, 0x75, 0x65, - 0x75, 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x6d, 0x69, 0x6e, 0x5f, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, - 0x62, 0x6c, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0c, 0x6d, 0x69, 0x6e, 0x41, 0x76, - 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x12, 0x27, 0x0a, 0x0f, 0x73, 0x63, 0x68, 0x65, 0x64, - 0x75, 0x6c, 0x65, 0x5f, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x0e, 0x73, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, - 0x12, 0x25, 0x0a, 0x0e, 0x70, 0x72, 0x69, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x5f, 0x63, 0x6c, 0x61, - 0x73, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x70, 0x72, 0x69, 0x6f, 0x72, 0x69, - 0x74, 0x79, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x12, 0x29, 0x0a, 0x05, 0x74, 0x61, 0x73, 0x6b, 0x73, - 0x18, 0x07, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, - 0x56, 0x6f, 0x6c, 0x63, 0x61, 0x6e, 0x6f, 0x54, 0x61, 0x73, 0x6b, 0x52, 0x05, 0x74, 0x61, 0x73, - 0x6b, 0x73, 0x22, 0xa5, 0x01, 0x0a, 0x13, 0x56, 0x6f, 0x6c, 0x63, 0x61, 0x6e, 0x6f, 0x4a, 0x6f, - 0x62, 0x43, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, - 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x16, - 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, - 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, - 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x12, 0x18, - 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x30, 0x0a, 0x14, 0x6c, 0x61, 0x73, 0x74, - 0x5f, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x74, 0x69, 0x6d, 0x65, - 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x12, 0x6c, 0x61, 0x73, 0x74, 0x54, 0x72, 0x61, 0x6e, - 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x69, 0x6d, 0x65, 0x22, 0x78, 0x0a, 0x0b, 0x56, 0x6f, - 0x6c, 0x63, 0x61, 0x6e, 0x6f, 0x54, 0x61, 0x73, 0x6b, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, - 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1a, 0x0a, - 0x08, 0x72, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, - 0x08, 0x72, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x73, 0x12, 0x39, 0x0a, 0x0a, 0x63, 0x6f, 0x6e, - 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x19, 0x2e, - 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, - 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x52, 0x0a, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, - 0x6e, 0x65, 0x72, 0x73, 0x22, 0x8f, 0x06, 0x0a, 0x17, 0x53, 0x70, 0x61, 0x72, 0x6b, 0x41, 0x70, - 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, - 0x12, 0x39, 0x0a, 0x0a, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x73, 0x18, 0x01, - 0x20, 0x03, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, - 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x52, - 0x0a, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x73, 0x12, 0x23, 0x0a, 0x0d, 0x73, - 0x70, 0x61, 0x72, 0x6b, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x0c, 0x73, 0x70, 0x61, 0x72, 0x6b, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, - 0x12, 0x29, 0x0a, 0x10, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, - 0x74, 0x79, 0x70, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x61, 0x70, 0x70, 0x6c, - 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x79, 0x70, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x64, - 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x0a, 0x64, 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x4d, 0x6f, 0x64, 0x65, 0x12, 0x1d, 0x0a, 0x0a, - 0x6d, 0x61, 0x69, 0x6e, 0x5f, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x09, 0x6d, 0x61, 0x69, 0x6e, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x12, 0x32, 0x0a, 0x15, 0x6d, - 0x61, 0x69, 0x6e, 0x5f, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, - 0x66, 0x69, 0x6c, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x13, 0x6d, 0x61, 0x69, 0x6e, - 0x41, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x46, 0x69, 0x6c, 0x65, 0x12, - 0x1c, 0x0a, 0x09, 0x61, 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x07, 0x20, 0x03, - 0x28, 0x09, 0x52, 0x09, 0x61, 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x43, 0x0a, - 0x11, 0x73, 0x70, 0x61, 0x72, 0x6b, 0x5f, 0x64, 0x72, 0x69, 0x76, 0x65, 0x72, 0x5f, 0x69, 0x6e, - 0x66, 0x6f, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, - 0x31, 0x2e, 0x53, 0x70, 0x61, 0x72, 0x6b, 0x44, 0x72, 0x69, 0x76, 0x65, 0x72, 0x49, 0x6e, 0x66, - 0x6f, 0x52, 0x0f, 0x73, 0x70, 0x61, 0x72, 0x6b, 0x44, 0x72, 0x69, 0x76, 0x65, 0x72, 0x49, 0x6e, - 0x66, 0x6f, 0x12, 0x2d, 0x0a, 0x12, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x6f, 0x72, 0x5f, 0x69, - 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x73, 0x18, 0x09, 0x20, 0x01, 0x28, 0x05, 0x52, 0x11, - 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x6f, 0x72, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, - 0x73, 0x12, 0x27, 0x0a, 0x0f, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x6f, 0x72, 0x73, 0x5f, 0x72, - 0x65, 0x61, 0x64, 0x79, 0x18, 0x10, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0e, 0x65, 0x78, 0x65, 0x63, - 0x75, 0x74, 0x6f, 0x72, 0x73, 0x52, 0x65, 0x61, 0x64, 0x79, 0x12, 0x2b, 0x0a, 0x11, 0x61, 0x70, - 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, - 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x30, 0x0a, 0x14, 0x73, 0x70, 0x61, 0x72, 0x6b, - 0x5f, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, - 0x0b, 0x20, 0x01, 0x28, 0x09, 0x52, 0x12, 0x73, 0x70, 0x61, 0x72, 0x6b, 0x41, 0x70, 0x70, 0x6c, - 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x23, 0x0a, 0x0d, 0x73, 0x75, 0x62, - 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x0c, 0x73, 0x75, 0x62, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x1c, - 0x0a, 0x09, 0x73, 0x75, 0x73, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x64, 0x18, 0x0d, 0x20, 0x01, 0x28, - 0x08, 0x52, 0x09, 0x73, 0x75, 0x73, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x64, 0x12, 0x45, 0x0a, 0x0e, - 0x72, 0x65, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x18, 0x0e, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x70, - 0x61, 0x72, 0x6b, 0x52, 0x65, 0x73, 0x74, 0x61, 0x72, 0x74, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, - 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x0d, 0x72, 0x65, 0x73, 0x74, 0x61, 0x72, 0x74, 0x50, 0x6f, 0x6c, - 0x69, 0x63, 0x79, 0x12, 0x51, 0x0a, 0x12, 0x64, 0x79, 0x6e, 0x61, 0x6d, 0x69, 0x63, 0x5f, 0x61, - 0x6c, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x22, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x70, 0x61, 0x72, 0x6b, 0x44, 0x79, - 0x6e, 0x61, 0x6d, 0x69, 0x63, 0x41, 0x6c, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, - 0x6e, 0x66, 0x6f, 0x52, 0x11, 0x64, 0x79, 0x6e, 0x61, 0x6d, 0x69, 0x63, 0x41, 0x6c, 0x6c, 0x6f, - 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0xdc, 0x02, 0x0a, 0x0f, 0x53, 0x70, 0x61, 0x72, 0x6b, - 0x44, 0x72, 0x69, 0x76, 0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x19, 0x0a, 0x08, 0x70, 0x6f, - 0x64, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x70, 0x6f, - 0x64, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x2d, 0x0a, 0x13, 0x77, 0x65, 0x62, 0x5f, 0x75, 0x69, 0x5f, - 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x10, 0x77, 0x65, 0x62, 0x55, 0x69, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, - 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x24, 0x0a, 0x0e, 0x77, 0x65, 0x62, 0x5f, 0x75, 0x69, 0x5f, 0x61, - 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x77, 0x65, - 0x62, 0x55, 0x69, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x1e, 0x0a, 0x0b, 0x77, 0x65, - 0x62, 0x5f, 0x75, 0x69, 0x5f, 0x70, 0x6f, 0x72, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x52, - 0x09, 0x77, 0x65, 0x62, 0x55, 0x69, 0x50, 0x6f, 0x72, 0x74, 0x12, 0x2d, 0x0a, 0x13, 0x77, 0x65, - 0x62, 0x5f, 0x75, 0x69, 0x5f, 0x69, 0x6e, 0x67, 0x72, 0x65, 0x73, 0x73, 0x5f, 0x6e, 0x61, 0x6d, - 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x77, 0x65, 0x62, 0x55, 0x69, 0x49, 0x6e, - 0x67, 0x72, 0x65, 0x73, 0x73, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x33, 0x0a, 0x16, 0x77, 0x65, 0x62, - 0x5f, 0x75, 0x69, 0x5f, 0x69, 0x6e, 0x67, 0x72, 0x65, 0x73, 0x73, 0x5f, 0x61, 0x64, 0x64, 0x72, - 0x65, 0x73, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x13, 0x77, 0x65, 0x62, 0x55, 0x69, - 0x49, 0x6e, 0x67, 0x72, 0x65, 0x73, 0x73, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x14, - 0x0a, 0x05, 0x63, 0x6f, 0x72, 0x65, 0x73, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x63, - 0x6f, 0x72, 0x65, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x18, 0x08, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x12, 0x27, 0x0a, 0x0f, - 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, - 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x41, 0x63, - 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x22, 0xa8, 0x02, 0x0a, 0x16, 0x53, 0x70, 0x61, 0x72, 0x6b, 0x52, - 0x65, 0x73, 0x74, 0x61, 0x72, 0x74, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x49, 0x6e, 0x66, 0x6f, - 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, - 0x74, 0x79, 0x70, 0x65, 0x12, 0x2c, 0x0a, 0x12, 0x6f, 0x6e, 0x5f, 0x66, 0x61, 0x69, 0x6c, 0x75, - 0x72, 0x65, 0x5f, 0x72, 0x65, 0x74, 0x72, 0x69, 0x65, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, - 0x52, 0x10, 0x6f, 0x6e, 0x46, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x52, 0x65, 0x74, 0x72, 0x69, - 0x65, 0x73, 0x12, 0x39, 0x0a, 0x19, 0x6f, 0x6e, 0x5f, 0x66, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, - 0x5f, 0x72, 0x65, 0x74, 0x72, 0x79, 0x5f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x18, - 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x16, 0x6f, 0x6e, 0x46, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, - 0x52, 0x65, 0x74, 0x72, 0x79, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x12, 0x41, 0x0a, - 0x1d, 0x6f, 0x6e, 0x5f, 0x73, 0x75, 0x62, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x66, - 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x5f, 0x72, 0x65, 0x74, 0x72, 0x69, 0x65, 0x73, 0x18, 0x04, - 0x20, 0x01, 0x28, 0x05, 0x52, 0x1a, 0x6f, 0x6e, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x73, 0x73, 0x69, - 0x6f, 0x6e, 0x46, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x52, 0x65, 0x74, 0x72, 0x69, 0x65, 0x73, - 0x12, 0x4e, 0x0a, 0x24, 0x6f, 0x6e, 0x5f, 0x73, 0x75, 0x62, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, - 0x6e, 0x5f, 0x66, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x5f, 0x72, 0x65, 0x74, 0x72, 0x79, 0x5f, - 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x20, - 0x6f, 0x6e, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x46, 0x61, 0x69, 0x6c, - 0x75, 0x72, 0x65, 0x52, 0x65, 0x74, 0x72, 0x79, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, - 0x22, 0xa1, 0x02, 0x0a, 0x1a, 0x53, 0x70, 0x61, 0x72, 0x6b, 0x44, 0x79, 0x6e, 0x61, 0x6d, 0x69, - 0x63, 0x41, 0x6c, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x6e, 0x66, 0x6f, 0x12, - 0x18, 0x0a, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, - 0x52, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x2b, 0x0a, 0x11, 0x69, 0x6e, 0x69, - 0x74, 0x69, 0x61, 0x6c, 0x5f, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x6f, 0x72, 0x73, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x05, 0x52, 0x10, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x45, 0x78, 0x65, - 0x63, 0x75, 0x74, 0x6f, 0x72, 0x73, 0x12, 0x23, 0x0a, 0x0d, 0x6d, 0x69, 0x6e, 0x5f, 0x65, 0x78, - 0x65, 0x63, 0x75, 0x74, 0x6f, 0x72, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0c, 0x6d, - 0x69, 0x6e, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x6f, 0x72, 0x73, 0x12, 0x23, 0x0a, 0x0d, 0x6d, - 0x61, 0x78, 0x5f, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x6f, 0x72, 0x73, 0x18, 0x04, 0x20, 0x01, - 0x28, 0x05, 0x52, 0x0c, 0x6d, 0x61, 0x78, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x6f, 0x72, 0x73, - 0x12, 0x38, 0x0a, 0x18, 0x73, 0x68, 0x75, 0x66, 0x66, 0x6c, 0x65, 0x5f, 0x74, 0x72, 0x61, 0x63, - 0x6b, 0x69, 0x6e, 0x67, 0x5f, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x05, 0x20, 0x01, - 0x28, 0x08, 0x52, 0x16, 0x73, 0x68, 0x75, 0x66, 0x66, 0x6c, 0x65, 0x54, 0x72, 0x61, 0x63, 0x6b, - 0x69, 0x6e, 0x67, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x38, 0x0a, 0x18, 0x73, 0x68, - 0x75, 0x66, 0x66, 0x6c, 0x65, 0x5f, 0x74, 0x72, 0x61, 0x63, 0x6b, 0x69, 0x6e, 0x67, 0x5f, 0x74, - 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x03, 0x52, 0x16, 0x73, 0x68, - 0x75, 0x66, 0x66, 0x6c, 0x65, 0x54, 0x72, 0x61, 0x63, 0x6b, 0x69, 0x6e, 0x67, 0x54, 0x69, 0x6d, - 0x65, 0x6f, 0x75, 0x74, 0x22, 0xad, 0x05, 0x0a, 0x20, 0x53, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, - 0x65, 0x64, 0x53, 0x70, 0x61, 0x72, 0x6b, 0x41, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x73, 0x63, 0x68, - 0x65, 0x64, 0x75, 0x6c, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x73, 0x63, 0x68, - 0x65, 0x64, 0x75, 0x6c, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x7a, 0x6f, - 0x6e, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x74, 0x69, 0x6d, 0x65, 0x5a, 0x6f, - 0x6e, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x73, 0x75, 0x73, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x64, 0x18, - 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x73, 0x75, 0x73, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x64, - 0x12, 0x2d, 0x0a, 0x12, 0x63, 0x6f, 0x6e, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x5f, - 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x11, 0x63, 0x6f, - 0x6e, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x12, - 0x3f, 0x0a, 0x1c, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x66, 0x75, 0x6c, 0x5f, 0x72, 0x75, - 0x6e, 0x5f, 0x68, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x5f, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x18, - 0x05, 0x20, 0x01, 0x28, 0x05, 0x52, 0x19, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x66, 0x75, - 0x6c, 0x52, 0x75, 0x6e, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4c, 0x69, 0x6d, 0x69, 0x74, - 0x12, 0x37, 0x0a, 0x18, 0x66, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x5f, 0x72, 0x75, 0x6e, 0x5f, 0x68, - 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x5f, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x18, 0x06, 0x20, 0x01, - 0x28, 0x05, 0x52, 0x15, 0x66, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x52, 0x75, 0x6e, 0x48, 0x69, 0x73, - 0x74, 0x6f, 0x72, 0x79, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x12, 0x25, 0x0a, 0x0e, 0x73, 0x63, 0x68, - 0x65, 0x64, 0x75, 0x6c, 0x65, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x0d, 0x73, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x53, 0x74, 0x61, 0x74, 0x65, - 0x12, 0x22, 0x0a, 0x0d, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x72, 0x75, 0x6e, 0x5f, 0x6e, 0x61, 0x6d, - 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x6c, 0x61, 0x73, 0x74, 0x52, 0x75, 0x6e, - 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x35, 0x0a, 0x08, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x72, 0x75, 0x6e, - 0x18, 0x09, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, - 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, - 0x6d, 0x70, 0x52, 0x07, 0x6c, 0x61, 0x73, 0x74, 0x52, 0x75, 0x6e, 0x12, 0x35, 0x0a, 0x08, 0x6e, - 0x65, 0x78, 0x74, 0x5f, 0x72, 0x75, 0x6e, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, - 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, - 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x07, 0x6e, 0x65, 0x78, 0x74, 0x52, - 0x75, 0x6e, 0x12, 0x39, 0x0a, 0x19, 0x70, 0x61, 0x73, 0x74, 0x5f, 0x73, 0x75, 0x63, 0x63, 0x65, - 0x73, 0x73, 0x66, 0x75, 0x6c, 0x5f, 0x72, 0x75, 0x6e, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x18, - 0x0b, 0x20, 0x03, 0x28, 0x09, 0x52, 0x16, 0x70, 0x61, 0x73, 0x74, 0x53, 0x75, 0x63, 0x63, 0x65, - 0x73, 0x73, 0x66, 0x75, 0x6c, 0x52, 0x75, 0x6e, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x12, 0x31, 0x0a, - 0x15, 0x70, 0x61, 0x73, 0x74, 0x5f, 0x66, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x5f, 0x72, 0x75, 0x6e, - 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x18, 0x0c, 0x20, 0x03, 0x28, 0x09, 0x52, 0x12, 0x70, 0x61, - 0x73, 0x74, 0x46, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x52, 0x75, 0x6e, 0x4e, 0x61, 0x6d, 0x65, 0x73, - 0x12, 0x16, 0x0a, 0x06, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x06, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x12, 0x4a, 0x0a, 0x10, 0x74, 0x65, 0x6d, 0x70, - 0x6c, 0x61, 0x74, 0x65, 0x5f, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x18, 0x0e, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x70, 0x61, 0x72, - 0x6b, 0x41, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x44, 0x65, 0x74, 0x61, - 0x69, 0x6c, 0x73, 0x52, 0x0f, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x44, 0x65, 0x74, - 0x61, 0x69, 0x6c, 0x73, 0x22, 0xe1, 0x01, 0x0a, 0x14, 0x4b, 0x79, 0x76, 0x65, 0x72, 0x6e, 0x6f, - 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x12, 0x0a, - 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6b, 0x69, 0x6e, - 0x64, 0x12, 0x1e, 0x0a, 0x0a, 0x62, 0x61, 0x63, 0x6b, 0x67, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x62, 0x61, 0x63, 0x6b, 0x67, 0x72, 0x6f, 0x75, 0x6e, - 0x64, 0x12, 0x3a, 0x0a, 0x19, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, - 0x66, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x5f, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x17, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x46, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1d, 0x0a, - 0x0a, 0x72, 0x75, 0x6c, 0x65, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, - 0x05, 0x52, 0x09, 0x72, 0x75, 0x6c, 0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x1d, 0x0a, 0x0a, - 0x72, 0x75, 0x6c, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x09, - 0x52, 0x09, 0x72, 0x75, 0x6c, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x12, 0x1b, 0x0a, 0x09, 0x73, - 0x70, 0x65, 0x63, 0x5f, 0x6a, 0x73, 0x6f, 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, - 0x73, 0x70, 0x65, 0x63, 0x4a, 0x73, 0x6f, 0x6e, 0x22, 0x87, 0x02, 0x0a, 0x12, 0x50, 0x6f, 0x6c, - 0x69, 0x63, 0x79, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x12, - 0x16, 0x0a, 0x06, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x06, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x12, 0x12, 0x0a, 0x04, 0x72, 0x75, 0x6c, 0x65, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x72, 0x75, 0x6c, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x72, - 0x65, 0x73, 0x75, 0x6c, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x72, 0x65, 0x73, - 0x75, 0x6c, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x73, 0x65, 0x76, 0x65, 0x72, 0x69, 0x74, 0x79, 0x18, - 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x73, 0x65, 0x76, 0x65, 0x72, 0x69, 0x74, 0x79, 0x12, - 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x72, 0x65, 0x73, - 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x6b, 0x69, 0x6e, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x0c, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x4b, 0x69, 0x6e, 0x64, 0x12, 0x2d, - 0x0a, 0x12, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x73, - 0x70, 0x61, 0x63, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x11, 0x72, 0x65, 0x73, 0x6f, - 0x75, 0x72, 0x63, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x23, 0x0a, - 0x0d, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x08, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x4e, 0x61, - 0x6d, 0x65, 0x22, 0xd8, 0x02, 0x0a, 0x1a, 0x4b, 0x79, 0x76, 0x65, 0x72, 0x6e, 0x6f, 0x50, 0x6f, - 0x6c, 0x69, 0x63, 0x79, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, - 0x73, 0x12, 0x12, 0x0a, 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x5f, 0x6b, - 0x69, 0x6e, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x73, 0x63, 0x6f, 0x70, 0x65, - 0x4b, 0x69, 0x6e, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x5f, 0x6e, 0x61, - 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x4e, - 0x61, 0x6d, 0x65, 0x12, 0x27, 0x0a, 0x0f, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x5f, 0x6e, 0x61, 0x6d, - 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x73, 0x63, - 0x6f, 0x70, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x12, 0x0a, 0x04, - 0x70, 0x61, 0x73, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x05, 0x52, 0x04, 0x70, 0x61, 0x73, 0x73, - 0x12, 0x12, 0x0a, 0x04, 0x66, 0x61, 0x69, 0x6c, 0x18, 0x06, 0x20, 0x01, 0x28, 0x05, 0x52, 0x04, - 0x66, 0x61, 0x69, 0x6c, 0x12, 0x12, 0x0a, 0x04, 0x77, 0x61, 0x72, 0x6e, 0x18, 0x07, 0x20, 0x01, - 0x28, 0x05, 0x52, 0x04, 0x77, 0x61, 0x72, 0x6e, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, - 0x72, 0x18, 0x08, 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x12, - 0x0a, 0x04, 0x73, 0x6b, 0x69, 0x70, 0x18, 0x09, 0x20, 0x01, 0x28, 0x05, 0x52, 0x04, 0x73, 0x6b, - 0x69, 0x70, 0x12, 0x34, 0x0a, 0x07, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x18, 0x0a, 0x20, - 0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x6f, 0x6c, - 0x69, 0x63, 0x79, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x52, - 0x07, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x12, 0x23, 0x0a, 0x0d, 0x74, 0x6f, 0x74, 0x61, - 0x6c, 0x5f, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x05, 0x52, - 0x0c, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x22, 0x91, 0x01, - 0x0a, 0x23, 0x47, 0x61, 0x74, 0x65, 0x6b, 0x65, 0x65, 0x70, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x73, - 0x74, 0x72, 0x61, 0x69, 0x6e, 0x74, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x44, 0x65, - 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x19, 0x0a, 0x08, 0x63, 0x72, 0x64, 0x5f, 0x6b, 0x69, 0x6e, - 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x63, 0x72, 0x64, 0x4b, 0x69, 0x6e, 0x64, - 0x12, 0x18, 0x0a, 0x07, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, - 0x09, 0x52, 0x07, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x63, 0x72, - 0x65, 0x61, 0x74, 0x65, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x63, 0x72, 0x65, - 0x61, 0x74, 0x65, 0x64, 0x12, 0x1b, 0x0a, 0x09, 0x73, 0x70, 0x65, 0x63, 0x5f, 0x6a, 0x73, 0x6f, - 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x73, 0x70, 0x65, 0x63, 0x4a, 0x73, 0x6f, - 0x6e, 0x22, 0xd7, 0x01, 0x0a, 0x13, 0x43, 0x6f, 0x6e, 0x73, 0x74, 0x72, 0x61, 0x69, 0x6e, 0x74, - 0x56, 0x69, 0x6f, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x23, 0x0a, 0x0d, 0x72, 0x65, 0x73, - 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x6b, 0x69, 0x6e, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x0c, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x4b, 0x69, 0x6e, 0x64, 0x12, 0x2d, - 0x0a, 0x12, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x73, - 0x70, 0x61, 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x11, 0x72, 0x65, 0x73, 0x6f, - 0x75, 0x72, 0x63, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x23, 0x0a, - 0x0d, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x4e, 0x61, - 0x6d, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x04, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x2d, 0x0a, 0x12, - 0x65, 0x6e, 0x66, 0x6f, 0x72, 0x63, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x61, 0x63, 0x74, 0x69, - 0x6f, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x11, 0x65, 0x6e, 0x66, 0x6f, 0x72, 0x63, - 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0xd5, 0x02, 0x0a, 0x1b, - 0x47, 0x61, 0x74, 0x65, 0x6b, 0x65, 0x65, 0x70, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x73, 0x74, 0x72, - 0x61, 0x69, 0x6e, 0x74, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x6b, - 0x69, 0x6e, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x12, - 0x2d, 0x0a, 0x12, 0x65, 0x6e, 0x66, 0x6f, 0x72, 0x63, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x61, - 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x11, 0x65, 0x6e, 0x66, - 0x6f, 0x72, 0x63, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1d, - 0x0a, 0x0a, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x5f, 0x6a, 0x73, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x09, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x4a, 0x73, 0x6f, 0x6e, 0x12, 0x27, 0x0a, - 0x0f, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x5f, 0x6a, 0x73, 0x6f, 0x6e, - 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, - 0x72, 0x73, 0x4a, 0x73, 0x6f, 0x6e, 0x12, 0x29, 0x0a, 0x10, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, - 0x76, 0x69, 0x6f, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, - 0x52, 0x0f, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x56, 0x69, 0x6f, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x73, 0x12, 0x43, 0x0a, 0x0f, 0x61, 0x75, 0x64, 0x69, 0x74, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x73, - 0x74, 0x61, 0x6d, 0x70, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, - 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, - 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0e, 0x61, 0x75, 0x64, 0x69, 0x74, 0x54, 0x69, 0x6d, - 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x3b, 0x0a, 0x0a, 0x76, 0x69, 0x6f, 0x6c, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x61, 0x70, 0x69, - 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x73, 0x74, 0x72, 0x61, 0x69, 0x6e, 0x74, 0x56, 0x69, - 0x6f, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0a, 0x76, 0x69, 0x6f, 0x6c, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x73, 0x22, 0xc9, 0x0b, 0x0a, 0x05, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x0e, 0x0a, - 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x12, 0x0a, - 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6b, 0x69, 0x6e, - 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, - 0x63, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, - 0x61, 0x63, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x69, - 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, - 0x49, 0x64, 0x12, 0x1b, 0x0a, 0x09, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x5f, 0x75, 0x69, 0x64, 0x18, - 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x55, 0x69, 0x64, 0x12, - 0x1d, 0x0a, 0x0a, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x5f, 0x6b, 0x69, 0x6e, 0x64, 0x18, 0x08, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x09, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x4b, 0x69, 0x6e, 0x64, 0x12, 0x1d, - 0x0a, 0x0a, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x09, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x09, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x1f, 0x0a, - 0x0b, 0x61, 0x70, 0x69, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x0a, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x0a, 0x61, 0x70, 0x69, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x29, - 0x0a, 0x10, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, - 0x6f, 0x6e, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, - 0x63, 0x65, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x14, 0x0a, 0x05, 0x63, 0x6f, 0x75, - 0x6e, 0x74, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x12, + 0x72, 0x61, 0x6e, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x69, 0x6d, 0x65, 0x22, 0xc1, 0x02, + 0x0a, 0x11, 0x56, 0x6f, 0x6c, 0x63, 0x61, 0x6e, 0x6f, 0x4a, 0x6f, 0x62, 0x44, 0x65, 0x74, 0x61, + 0x69, 0x6c, 0x73, 0x12, 0x39, 0x0a, 0x0a, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, + 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, + 0x2e, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, + 0x74, 0x65, 0x52, 0x0a, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x73, 0x12, 0x3b, + 0x0a, 0x0a, 0x63, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x03, + 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x56, 0x6f, 0x6c, 0x63, + 0x61, 0x6e, 0x6f, 0x4a, 0x6f, 0x62, 0x43, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x52, + 0x0a, 0x63, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x71, + 0x75, 0x65, 0x75, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x71, 0x75, 0x65, 0x75, + 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x6d, 0x69, 0x6e, 0x5f, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, + 0x6c, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0c, 0x6d, 0x69, 0x6e, 0x41, 0x76, 0x61, + 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x12, 0x27, 0x0a, 0x0f, 0x73, 0x63, 0x68, 0x65, 0x64, 0x75, + 0x6c, 0x65, 0x5f, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x0e, 0x73, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x12, + 0x25, 0x0a, 0x0e, 0x70, 0x72, 0x69, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x5f, 0x63, 0x6c, 0x61, 0x73, + 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x70, 0x72, 0x69, 0x6f, 0x72, 0x69, 0x74, + 0x79, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x12, 0x29, 0x0a, 0x05, 0x74, 0x61, 0x73, 0x6b, 0x73, 0x18, + 0x07, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x56, + 0x6f, 0x6c, 0x63, 0x61, 0x6e, 0x6f, 0x54, 0x61, 0x73, 0x6b, 0x52, 0x05, 0x74, 0x61, 0x73, 0x6b, + 0x73, 0x22, 0xa5, 0x01, 0x0a, 0x13, 0x56, 0x6f, 0x6c, 0x63, 0x61, 0x6e, 0x6f, 0x4a, 0x6f, 0x62, + 0x43, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, + 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x16, 0x0a, + 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, + 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x12, 0x18, 0x0a, + 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, + 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x30, 0x0a, 0x14, 0x6c, 0x61, 0x73, 0x74, 0x5f, + 0x74, 0x72, 0x61, 0x6e, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, + 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x12, 0x6c, 0x61, 0x73, 0x74, 0x54, 0x72, 0x61, 0x6e, 0x73, + 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x69, 0x6d, 0x65, 0x22, 0x78, 0x0a, 0x0b, 0x56, 0x6f, 0x6c, + 0x63, 0x61, 0x6e, 0x6f, 0x54, 0x61, 0x73, 0x6b, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1a, 0x0a, 0x08, + 0x72, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x08, + 0x72, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x73, 0x12, 0x39, 0x0a, 0x0a, 0x63, 0x6f, 0x6e, 0x74, + 0x61, 0x69, 0x6e, 0x65, 0x72, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x61, + 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x54, + 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x52, 0x0a, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, + 0x65, 0x72, 0x73, 0x22, 0x8f, 0x06, 0x0a, 0x17, 0x53, 0x70, 0x61, 0x72, 0x6b, 0x41, 0x70, 0x70, + 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, + 0x39, 0x0a, 0x0a, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x73, 0x18, 0x01, 0x20, + 0x03, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6e, + 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x52, 0x0a, + 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x73, 0x12, 0x23, 0x0a, 0x0d, 0x73, 0x70, + 0x61, 0x72, 0x6b, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x0c, 0x73, 0x70, 0x61, 0x72, 0x6b, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, + 0x29, 0x0a, 0x10, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x74, + 0x79, 0x70, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x61, 0x70, 0x70, 0x6c, 0x69, + 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x79, 0x70, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x64, 0x65, + 0x70, 0x6c, 0x6f, 0x79, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x0a, 0x64, 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x4d, 0x6f, 0x64, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x6d, + 0x61, 0x69, 0x6e, 0x5f, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x09, 0x6d, 0x61, 0x69, 0x6e, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x12, 0x32, 0x0a, 0x15, 0x6d, 0x61, + 0x69, 0x6e, 0x5f, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x66, + 0x69, 0x6c, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x13, 0x6d, 0x61, 0x69, 0x6e, 0x41, + 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x46, 0x69, 0x6c, 0x65, 0x12, 0x1c, + 0x0a, 0x09, 0x61, 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, + 0x09, 0x52, 0x09, 0x61, 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x43, 0x0a, 0x11, + 0x73, 0x70, 0x61, 0x72, 0x6b, 0x5f, 0x64, 0x72, 0x69, 0x76, 0x65, 0x72, 0x5f, 0x69, 0x6e, 0x66, + 0x6f, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, + 0x2e, 0x53, 0x70, 0x61, 0x72, 0x6b, 0x44, 0x72, 0x69, 0x76, 0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f, + 0x52, 0x0f, 0x73, 0x70, 0x61, 0x72, 0x6b, 0x44, 0x72, 0x69, 0x76, 0x65, 0x72, 0x49, 0x6e, 0x66, + 0x6f, 0x12, 0x2d, 0x0a, 0x12, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x6f, 0x72, 0x5f, 0x69, 0x6e, + 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x73, 0x18, 0x09, 0x20, 0x01, 0x28, 0x05, 0x52, 0x11, 0x65, + 0x78, 0x65, 0x63, 0x75, 0x74, 0x6f, 0x72, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x73, + 0x12, 0x27, 0x0a, 0x0f, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x6f, 0x72, 0x73, 0x5f, 0x72, 0x65, + 0x61, 0x64, 0x79, 0x18, 0x10, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0e, 0x65, 0x78, 0x65, 0x63, 0x75, + 0x74, 0x6f, 0x72, 0x73, 0x52, 0x65, 0x61, 0x64, 0x79, 0x12, 0x2b, 0x0a, 0x11, 0x61, 0x70, 0x70, + 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x0a, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x30, 0x0a, 0x14, 0x73, 0x70, 0x61, 0x72, 0x6b, 0x5f, + 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x0b, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x12, 0x73, 0x70, 0x61, 0x72, 0x6b, 0x41, 0x70, 0x70, 0x6c, 0x69, + 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x23, 0x0a, 0x0d, 0x73, 0x75, 0x62, 0x6d, + 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x0c, 0x73, 0x75, 0x62, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x1c, 0x0a, + 0x09, 0x73, 0x75, 0x73, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x64, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x08, + 0x52, 0x09, 0x73, 0x75, 0x73, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x64, 0x12, 0x45, 0x0a, 0x0e, 0x72, + 0x65, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x18, 0x0e, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x70, 0x61, + 0x72, 0x6b, 0x52, 0x65, 0x73, 0x74, 0x61, 0x72, 0x74, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x49, + 0x6e, 0x66, 0x6f, 0x52, 0x0d, 0x72, 0x65, 0x73, 0x74, 0x61, 0x72, 0x74, 0x50, 0x6f, 0x6c, 0x69, + 0x63, 0x79, 0x12, 0x51, 0x0a, 0x12, 0x64, 0x79, 0x6e, 0x61, 0x6d, 0x69, 0x63, 0x5f, 0x61, 0x6c, + 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, + 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x70, 0x61, 0x72, 0x6b, 0x44, 0x79, 0x6e, + 0x61, 0x6d, 0x69, 0x63, 0x41, 0x6c, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x6e, + 0x66, 0x6f, 0x52, 0x11, 0x64, 0x79, 0x6e, 0x61, 0x6d, 0x69, 0x63, 0x41, 0x6c, 0x6c, 0x6f, 0x63, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0xdc, 0x02, 0x0a, 0x0f, 0x53, 0x70, 0x61, 0x72, 0x6b, 0x44, + 0x72, 0x69, 0x76, 0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x19, 0x0a, 0x08, 0x70, 0x6f, 0x64, + 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x70, 0x6f, 0x64, + 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x2d, 0x0a, 0x13, 0x77, 0x65, 0x62, 0x5f, 0x75, 0x69, 0x5f, 0x73, + 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x10, 0x77, 0x65, 0x62, 0x55, 0x69, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x4e, + 0x61, 0x6d, 0x65, 0x12, 0x24, 0x0a, 0x0e, 0x77, 0x65, 0x62, 0x5f, 0x75, 0x69, 0x5f, 0x61, 0x64, + 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x77, 0x65, 0x62, + 0x55, 0x69, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x1e, 0x0a, 0x0b, 0x77, 0x65, 0x62, + 0x5f, 0x75, 0x69, 0x5f, 0x70, 0x6f, 0x72, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x52, 0x09, + 0x77, 0x65, 0x62, 0x55, 0x69, 0x50, 0x6f, 0x72, 0x74, 0x12, 0x2d, 0x0a, 0x13, 0x77, 0x65, 0x62, + 0x5f, 0x75, 0x69, 0x5f, 0x69, 0x6e, 0x67, 0x72, 0x65, 0x73, 0x73, 0x5f, 0x6e, 0x61, 0x6d, 0x65, + 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x77, 0x65, 0x62, 0x55, 0x69, 0x49, 0x6e, 0x67, + 0x72, 0x65, 0x73, 0x73, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x33, 0x0a, 0x16, 0x77, 0x65, 0x62, 0x5f, + 0x75, 0x69, 0x5f, 0x69, 0x6e, 0x67, 0x72, 0x65, 0x73, 0x73, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, + 0x73, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x13, 0x77, 0x65, 0x62, 0x55, 0x69, 0x49, + 0x6e, 0x67, 0x72, 0x65, 0x73, 0x73, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x14, 0x0a, + 0x05, 0x63, 0x6f, 0x72, 0x65, 0x73, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x63, 0x6f, + 0x72, 0x65, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x18, 0x08, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x06, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x12, 0x27, 0x0a, 0x0f, 0x73, + 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x09, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x41, 0x63, 0x63, + 0x6f, 0x75, 0x6e, 0x74, 0x22, 0xa8, 0x02, 0x0a, 0x16, 0x53, 0x70, 0x61, 0x72, 0x6b, 0x52, 0x65, + 0x73, 0x74, 0x61, 0x72, 0x74, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x49, 0x6e, 0x66, 0x6f, 0x12, + 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, + 0x79, 0x70, 0x65, 0x12, 0x2c, 0x0a, 0x12, 0x6f, 0x6e, 0x5f, 0x66, 0x61, 0x69, 0x6c, 0x75, 0x72, + 0x65, 0x5f, 0x72, 0x65, 0x74, 0x72, 0x69, 0x65, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, + 0x10, 0x6f, 0x6e, 0x46, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x52, 0x65, 0x74, 0x72, 0x69, 0x65, + 0x73, 0x12, 0x39, 0x0a, 0x19, 0x6f, 0x6e, 0x5f, 0x66, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x5f, + 0x72, 0x65, 0x74, 0x72, 0x79, 0x5f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x03, 0x52, 0x16, 0x6f, 0x6e, 0x46, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x52, + 0x65, 0x74, 0x72, 0x79, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x12, 0x41, 0x0a, 0x1d, + 0x6f, 0x6e, 0x5f, 0x73, 0x75, 0x62, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x66, 0x61, + 0x69, 0x6c, 0x75, 0x72, 0x65, 0x5f, 0x72, 0x65, 0x74, 0x72, 0x69, 0x65, 0x73, 0x18, 0x04, 0x20, + 0x01, 0x28, 0x05, 0x52, 0x1a, 0x6f, 0x6e, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, + 0x6e, 0x46, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x52, 0x65, 0x74, 0x72, 0x69, 0x65, 0x73, 0x12, + 0x4e, 0x0a, 0x24, 0x6f, 0x6e, 0x5f, 0x73, 0x75, 0x62, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, + 0x5f, 0x66, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x5f, 0x72, 0x65, 0x74, 0x72, 0x79, 0x5f, 0x69, + 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x20, 0x6f, + 0x6e, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x46, 0x61, 0x69, 0x6c, 0x75, + 0x72, 0x65, 0x52, 0x65, 0x74, 0x72, 0x79, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x22, + 0xa1, 0x02, 0x0a, 0x1a, 0x53, 0x70, 0x61, 0x72, 0x6b, 0x44, 0x79, 0x6e, 0x61, 0x6d, 0x69, 0x63, + 0x41, 0x6c, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x18, + 0x0a, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, + 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x2b, 0x0a, 0x11, 0x69, 0x6e, 0x69, 0x74, + 0x69, 0x61, 0x6c, 0x5f, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x6f, 0x72, 0x73, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x05, 0x52, 0x10, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x45, 0x78, 0x65, 0x63, + 0x75, 0x74, 0x6f, 0x72, 0x73, 0x12, 0x23, 0x0a, 0x0d, 0x6d, 0x69, 0x6e, 0x5f, 0x65, 0x78, 0x65, + 0x63, 0x75, 0x74, 0x6f, 0x72, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0c, 0x6d, 0x69, + 0x6e, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x6f, 0x72, 0x73, 0x12, 0x23, 0x0a, 0x0d, 0x6d, 0x61, + 0x78, 0x5f, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x6f, 0x72, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, + 0x05, 0x52, 0x0c, 0x6d, 0x61, 0x78, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x6f, 0x72, 0x73, 0x12, + 0x38, 0x0a, 0x18, 0x73, 0x68, 0x75, 0x66, 0x66, 0x6c, 0x65, 0x5f, 0x74, 0x72, 0x61, 0x63, 0x6b, + 0x69, 0x6e, 0x67, 0x5f, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, + 0x08, 0x52, 0x16, 0x73, 0x68, 0x75, 0x66, 0x66, 0x6c, 0x65, 0x54, 0x72, 0x61, 0x63, 0x6b, 0x69, + 0x6e, 0x67, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x38, 0x0a, 0x18, 0x73, 0x68, 0x75, + 0x66, 0x66, 0x6c, 0x65, 0x5f, 0x74, 0x72, 0x61, 0x63, 0x6b, 0x69, 0x6e, 0x67, 0x5f, 0x74, 0x69, + 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x03, 0x52, 0x16, 0x73, 0x68, 0x75, + 0x66, 0x66, 0x6c, 0x65, 0x54, 0x72, 0x61, 0x63, 0x6b, 0x69, 0x6e, 0x67, 0x54, 0x69, 0x6d, 0x65, + 0x6f, 0x75, 0x74, 0x22, 0xad, 0x05, 0x0a, 0x20, 0x53, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, + 0x64, 0x53, 0x70, 0x61, 0x72, 0x6b, 0x41, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x73, 0x63, 0x68, 0x65, + 0x64, 0x75, 0x6c, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x73, 0x63, 0x68, 0x65, + 0x64, 0x75, 0x6c, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x7a, 0x6f, 0x6e, + 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x74, 0x69, 0x6d, 0x65, 0x5a, 0x6f, 0x6e, + 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x73, 0x75, 0x73, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x64, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x73, 0x75, 0x73, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x64, 0x12, + 0x2d, 0x0a, 0x12, 0x63, 0x6f, 0x6e, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x5f, 0x70, + 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x11, 0x63, 0x6f, 0x6e, + 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x12, 0x3f, + 0x0a, 0x1c, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x66, 0x75, 0x6c, 0x5f, 0x72, 0x75, 0x6e, + 0x5f, 0x68, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x5f, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x18, 0x05, + 0x20, 0x01, 0x28, 0x05, 0x52, 0x19, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x66, 0x75, 0x6c, + 0x52, 0x75, 0x6e, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x12, + 0x37, 0x0a, 0x18, 0x66, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x5f, 0x72, 0x75, 0x6e, 0x5f, 0x68, 0x69, + 0x73, 0x74, 0x6f, 0x72, 0x79, 0x5f, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, + 0x05, 0x52, 0x15, 0x66, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x52, 0x75, 0x6e, 0x48, 0x69, 0x73, 0x74, + 0x6f, 0x72, 0x79, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x12, 0x25, 0x0a, 0x0e, 0x73, 0x63, 0x68, 0x65, + 0x64, 0x75, 0x6c, 0x65, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x0d, 0x73, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, + 0x22, 0x0a, 0x0d, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x72, 0x75, 0x6e, 0x5f, 0x6e, 0x61, 0x6d, 0x65, + 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x6c, 0x61, 0x73, 0x74, 0x52, 0x75, 0x6e, 0x4e, + 0x61, 0x6d, 0x65, 0x12, 0x35, 0x0a, 0x08, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x72, 0x75, 0x6e, 0x18, + 0x09, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, + 0x70, 0x52, 0x07, 0x6c, 0x61, 0x73, 0x74, 0x52, 0x75, 0x6e, 0x12, 0x35, 0x0a, 0x08, 0x6e, 0x65, + 0x78, 0x74, 0x5f, 0x72, 0x75, 0x6e, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, + 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, + 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x07, 0x6e, 0x65, 0x78, 0x74, 0x52, 0x75, + 0x6e, 0x12, 0x39, 0x0a, 0x19, 0x70, 0x61, 0x73, 0x74, 0x5f, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, + 0x73, 0x66, 0x75, 0x6c, 0x5f, 0x72, 0x75, 0x6e, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x18, 0x0b, + 0x20, 0x03, 0x28, 0x09, 0x52, 0x16, 0x70, 0x61, 0x73, 0x74, 0x53, 0x75, 0x63, 0x63, 0x65, 0x73, + 0x73, 0x66, 0x75, 0x6c, 0x52, 0x75, 0x6e, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x12, 0x31, 0x0a, 0x15, + 0x70, 0x61, 0x73, 0x74, 0x5f, 0x66, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x5f, 0x72, 0x75, 0x6e, 0x5f, + 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x18, 0x0c, 0x20, 0x03, 0x28, 0x09, 0x52, 0x12, 0x70, 0x61, 0x73, + 0x74, 0x46, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x52, 0x75, 0x6e, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x06, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, - 0x67, 0x65, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, - 0x65, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, - 0x10, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x16, 0x0a, - 0x06, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, 0x11, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, - 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x30, 0x0a, 0x14, 0x69, 0x6e, 0x76, 0x6f, 0x6c, 0x76, 0x65, - 0x64, 0x5f, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x6b, 0x69, 0x6e, 0x64, 0x18, 0x12, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x12, 0x69, 0x6e, 0x76, 0x6f, 0x6c, 0x76, 0x65, 0x64, 0x4f, 0x62, 0x6a, - 0x65, 0x63, 0x74, 0x4b, 0x69, 0x6e, 0x64, 0x12, 0x30, 0x0a, 0x14, 0x69, 0x6e, 0x76, 0x6f, 0x6c, - 0x76, 0x65, 0x64, 0x5f, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, - 0x13, 0x20, 0x01, 0x28, 0x09, 0x52, 0x12, 0x69, 0x6e, 0x76, 0x6f, 0x6c, 0x76, 0x65, 0x64, 0x4f, - 0x62, 0x6a, 0x65, 0x63, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x3a, 0x0a, 0x19, 0x69, 0x6e, 0x76, - 0x6f, 0x6c, 0x76, 0x65, 0x64, 0x5f, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x6e, 0x61, 0x6d, - 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x14, 0x20, 0x01, 0x28, 0x09, 0x52, 0x17, 0x69, 0x6e, - 0x76, 0x6f, 0x6c, 0x76, 0x65, 0x64, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x4e, 0x61, 0x6d, 0x65, - 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x31, 0x0a, 0x14, 0x72, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x69, - 0x6e, 0x67, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x18, 0x15, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x13, 0x72, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x69, 0x6e, 0x67, 0x43, 0x6f, - 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x12, 0x2d, 0x0a, 0x12, 0x72, 0x65, 0x70, 0x6f, - 0x72, 0x74, 0x69, 0x6e, 0x67, 0x5f, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x18, 0x16, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x11, 0x72, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x69, 0x6e, 0x67, 0x49, - 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x12, 0x40, 0x0a, 0x0b, 0x61, 0x6e, 0x6e, 0x6f, 0x74, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x17, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x61, - 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x2e, 0x41, 0x6e, 0x6e, 0x6f, - 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0b, 0x61, 0x6e, - 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x31, 0x0a, 0x06, 0x6c, 0x61, 0x62, - 0x65, 0x6c, 0x73, 0x18, 0x18, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x61, 0x70, 0x69, 0x2e, - 0x76, 0x31, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x2e, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x45, - 0x6e, 0x74, 0x72, 0x79, 0x52, 0x06, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x12, 0x2e, 0x0a, 0x13, - 0x69, 0x6e, 0x76, 0x6f, 0x6c, 0x76, 0x65, 0x64, 0x5f, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x5f, - 0x75, 0x69, 0x64, 0x18, 0x19, 0x20, 0x01, 0x28, 0x09, 0x52, 0x11, 0x69, 0x6e, 0x76, 0x6f, 0x6c, - 0x76, 0x65, 0x64, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x55, 0x69, 0x64, 0x12, 0x55, 0x0a, 0x17, - 0x64, 0x69, 0x73, 0x72, 0x75, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, - 0x5f, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x18, 0x1a, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1d, 0x2e, - 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x69, 0x73, 0x72, 0x75, 0x70, 0x74, 0x69, 0x6f, - 0x6e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x52, 0x15, 0x64, 0x69, - 0x73, 0x72, 0x75, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x65, 0x61, - 0x73, 0x6f, 0x6e, 0x12, 0x2f, 0x0a, 0x11, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x69, 0x6e, 0x67, 0x5f, - 0x70, 0x64, 0x62, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x1b, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, - 0x52, 0x0f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x69, 0x6e, 0x67, 0x50, 0x64, 0x62, 0x4e, 0x61, 0x6d, - 0x65, 0x88, 0x01, 0x01, 0x12, 0x39, 0x0a, 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, - 0x61, 0x74, 0x18, 0x29, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, + 0x06, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x12, 0x4a, 0x0a, 0x10, 0x74, 0x65, 0x6d, 0x70, 0x6c, + 0x61, 0x74, 0x65, 0x5f, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x18, 0x0e, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x1f, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x70, 0x61, 0x72, 0x6b, + 0x41, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x44, 0x65, 0x74, 0x61, 0x69, + 0x6c, 0x73, 0x52, 0x0f, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x44, 0x65, 0x74, 0x61, + 0x69, 0x6c, 0x73, 0x22, 0xe1, 0x01, 0x0a, 0x14, 0x4b, 0x79, 0x76, 0x65, 0x72, 0x6e, 0x6f, 0x50, + 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x12, 0x0a, 0x04, + 0x6b, 0x69, 0x6e, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6b, 0x69, 0x6e, 0x64, + 0x12, 0x1e, 0x0a, 0x0a, 0x62, 0x61, 0x63, 0x6b, 0x67, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x62, 0x61, 0x63, 0x6b, 0x67, 0x72, 0x6f, 0x75, 0x6e, 0x64, + 0x12, 0x3a, 0x0a, 0x19, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x66, + 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x5f, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x17, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x46, + 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1d, 0x0a, 0x0a, + 0x72, 0x75, 0x6c, 0x65, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, + 0x52, 0x09, 0x72, 0x75, 0x6c, 0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x72, + 0x75, 0x6c, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x09, 0x52, + 0x09, 0x72, 0x75, 0x6c, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x12, 0x1b, 0x0a, 0x09, 0x73, 0x70, + 0x65, 0x63, 0x5f, 0x6a, 0x73, 0x6f, 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x73, + 0x70, 0x65, 0x63, 0x4a, 0x73, 0x6f, 0x6e, 0x22, 0x87, 0x02, 0x0a, 0x12, 0x50, 0x6f, 0x6c, 0x69, + 0x63, 0x79, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x12, 0x16, + 0x0a, 0x06, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, + 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x12, 0x12, 0x0a, 0x04, 0x72, 0x75, 0x6c, 0x65, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x72, 0x75, 0x6c, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x72, 0x65, + 0x73, 0x75, 0x6c, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x72, 0x65, 0x73, 0x75, + 0x6c, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x73, 0x65, 0x76, 0x65, 0x72, 0x69, 0x74, 0x79, 0x18, 0x04, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x73, 0x65, 0x76, 0x65, 0x72, 0x69, 0x74, 0x79, 0x12, 0x18, + 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x72, 0x65, 0x73, 0x6f, + 0x75, 0x72, 0x63, 0x65, 0x5f, 0x6b, 0x69, 0x6e, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x0c, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x4b, 0x69, 0x6e, 0x64, 0x12, 0x2d, 0x0a, + 0x12, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, + 0x61, 0x63, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x11, 0x72, 0x65, 0x73, 0x6f, 0x75, + 0x72, 0x63, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x23, 0x0a, 0x0d, + 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x08, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x0c, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x4e, 0x61, 0x6d, + 0x65, 0x22, 0xd8, 0x02, 0x0a, 0x1a, 0x4b, 0x79, 0x76, 0x65, 0x72, 0x6e, 0x6f, 0x50, 0x6f, 0x6c, + 0x69, 0x63, 0x79, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, + 0x12, 0x12, 0x0a, 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, + 0x6b, 0x69, 0x6e, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x5f, 0x6b, 0x69, + 0x6e, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x4b, + 0x69, 0x6e, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x5f, 0x6e, 0x61, 0x6d, + 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x4e, 0x61, + 0x6d, 0x65, 0x12, 0x27, 0x0a, 0x0f, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, + 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x73, 0x63, 0x6f, + 0x70, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x70, + 0x61, 0x73, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x05, 0x52, 0x04, 0x70, 0x61, 0x73, 0x73, 0x12, + 0x12, 0x0a, 0x04, 0x66, 0x61, 0x69, 0x6c, 0x18, 0x06, 0x20, 0x01, 0x28, 0x05, 0x52, 0x04, 0x66, + 0x61, 0x69, 0x6c, 0x12, 0x12, 0x0a, 0x04, 0x77, 0x61, 0x72, 0x6e, 0x18, 0x07, 0x20, 0x01, 0x28, + 0x05, 0x52, 0x04, 0x77, 0x61, 0x72, 0x6e, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, + 0x18, 0x08, 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x12, 0x0a, + 0x04, 0x73, 0x6b, 0x69, 0x70, 0x18, 0x09, 0x20, 0x01, 0x28, 0x05, 0x52, 0x04, 0x73, 0x6b, 0x69, + 0x70, 0x12, 0x34, 0x0a, 0x07, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x18, 0x0a, 0x20, 0x03, + 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x6f, 0x6c, 0x69, + 0x63, 0x79, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x52, 0x07, + 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x12, 0x23, 0x0a, 0x0d, 0x74, 0x6f, 0x74, 0x61, 0x6c, + 0x5f, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0c, + 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x22, 0x91, 0x01, 0x0a, + 0x23, 0x47, 0x61, 0x74, 0x65, 0x6b, 0x65, 0x65, 0x70, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x73, 0x74, + 0x72, 0x61, 0x69, 0x6e, 0x74, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x44, 0x65, 0x74, + 0x61, 0x69, 0x6c, 0x73, 0x12, 0x19, 0x0a, 0x08, 0x63, 0x72, 0x64, 0x5f, 0x6b, 0x69, 0x6e, 0x64, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x63, 0x72, 0x64, 0x4b, 0x69, 0x6e, 0x64, 0x12, + 0x18, 0x0a, 0x07, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, + 0x52, 0x07, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x63, 0x72, 0x65, + 0x61, 0x74, 0x65, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x63, 0x72, 0x65, 0x61, + 0x74, 0x65, 0x64, 0x12, 0x1b, 0x0a, 0x09, 0x73, 0x70, 0x65, 0x63, 0x5f, 0x6a, 0x73, 0x6f, 0x6e, + 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x73, 0x70, 0x65, 0x63, 0x4a, 0x73, 0x6f, 0x6e, + 0x22, 0xd7, 0x01, 0x0a, 0x13, 0x43, 0x6f, 0x6e, 0x73, 0x74, 0x72, 0x61, 0x69, 0x6e, 0x74, 0x56, + 0x69, 0x6f, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x23, 0x0a, 0x0d, 0x72, 0x65, 0x73, 0x6f, + 0x75, 0x72, 0x63, 0x65, 0x5f, 0x6b, 0x69, 0x6e, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x0c, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x4b, 0x69, 0x6e, 0x64, 0x12, 0x2d, 0x0a, + 0x12, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, + 0x61, 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x11, 0x72, 0x65, 0x73, 0x6f, 0x75, + 0x72, 0x63, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x23, 0x0a, 0x0d, + 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x0c, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x4e, 0x61, 0x6d, + 0x65, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x04, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x2d, 0x0a, 0x12, 0x65, + 0x6e, 0x66, 0x6f, 0x72, 0x63, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x61, 0x63, 0x74, 0x69, 0x6f, + 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x11, 0x65, 0x6e, 0x66, 0x6f, 0x72, 0x63, 0x65, + 0x6d, 0x65, 0x6e, 0x74, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0xd5, 0x02, 0x0a, 0x1b, 0x47, + 0x61, 0x74, 0x65, 0x6b, 0x65, 0x65, 0x70, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x73, 0x74, 0x72, 0x61, + 0x69, 0x6e, 0x74, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x6b, 0x69, + 0x6e, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x12, 0x2d, + 0x0a, 0x12, 0x65, 0x6e, 0x66, 0x6f, 0x72, 0x63, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x61, 0x63, + 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x11, 0x65, 0x6e, 0x66, 0x6f, + 0x72, 0x63, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1d, 0x0a, + 0x0a, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x5f, 0x6a, 0x73, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x09, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x4a, 0x73, 0x6f, 0x6e, 0x12, 0x27, 0x0a, 0x0f, + 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x5f, 0x6a, 0x73, 0x6f, 0x6e, 0x18, + 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, + 0x73, 0x4a, 0x73, 0x6f, 0x6e, 0x12, 0x29, 0x0a, 0x10, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x76, + 0x69, 0x6f, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, + 0x0f, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x56, 0x69, 0x6f, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, + 0x12, 0x43, 0x0a, 0x0f, 0x61, 0x75, 0x64, 0x69, 0x74, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, + 0x61, 0x6d, 0x70, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, + 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, + 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0e, 0x61, 0x75, 0x64, 0x69, 0x74, 0x54, 0x69, 0x6d, 0x65, + 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x3b, 0x0a, 0x0a, 0x76, 0x69, 0x6f, 0x6c, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x61, 0x70, 0x69, 0x2e, + 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x73, 0x74, 0x72, 0x61, 0x69, 0x6e, 0x74, 0x56, 0x69, 0x6f, + 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0a, 0x76, 0x69, 0x6f, 0x6c, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x73, 0x22, 0xdf, 0x06, 0x0a, 0x16, 0x41, 0x72, 0x67, 0x6f, 0x41, 0x70, 0x70, 0x6c, 0x69, + 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x1f, 0x0a, + 0x0b, 0x73, 0x79, 0x6e, 0x63, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x0a, 0x73, 0x79, 0x6e, 0x63, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x23, + 0x0a, 0x0d, 0x68, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x68, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x53, 0x74, 0x61, + 0x74, 0x75, 0x73, 0x12, 0x23, 0x0a, 0x0d, 0x73, 0x79, 0x6e, 0x63, 0x5f, 0x72, 0x65, 0x76, 0x69, + 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x73, 0x79, 0x6e, 0x63, + 0x52, 0x65, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x34, 0x0a, 0x16, 0x72, 0x65, 0x73, 0x6f, + 0x75, 0x72, 0x63, 0x65, 0x5f, 0x68, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x5f, 0x73, 0x6f, 0x75, 0x72, + 0x63, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x14, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, + 0x63, 0x65, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x18, + 0x0a, 0x07, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x07, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x12, 0x2d, 0x0a, 0x12, 0x64, 0x65, 0x73, 0x74, + 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x18, 0x06, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x11, 0x64, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x12, 0x29, 0x0a, 0x10, 0x64, 0x65, 0x73, 0x74, 0x69, + 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x0f, 0x64, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4e, 0x61, + 0x6d, 0x65, 0x12, 0x33, 0x0a, 0x15, 0x64, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x14, 0x64, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4e, 0x61, + 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x19, 0x0a, 0x08, 0x72, 0x65, 0x70, 0x6f, 0x5f, + 0x75, 0x72, 0x6c, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x72, 0x65, 0x70, 0x6f, 0x55, + 0x72, 0x6c, 0x12, 0x1f, 0x0a, 0x0b, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x70, 0x61, 0x74, + 0x68, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x50, + 0x61, 0x74, 0x68, 0x12, 0x27, 0x0a, 0x0f, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x5f, 0x72, 0x65, + 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x74, 0x61, + 0x72, 0x67, 0x65, 0x74, 0x52, 0x65, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x21, 0x0a, 0x0c, + 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x0c, 0x20, 0x01, + 0x28, 0x05, 0x52, 0x0b, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, + 0x25, 0x0a, 0x0e, 0x61, 0x75, 0x74, 0x6f, 0x6d, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x73, 0x79, 0x6e, + 0x63, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0d, 0x61, 0x75, 0x74, 0x6f, 0x6d, 0x61, 0x74, + 0x65, 0x64, 0x53, 0x79, 0x6e, 0x63, 0x12, 0x1b, 0x0a, 0x09, 0x73, 0x65, 0x6c, 0x66, 0x5f, 0x68, + 0x65, 0x61, 0x6c, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x73, 0x65, 0x6c, 0x66, 0x48, + 0x65, 0x61, 0x6c, 0x12, 0x14, 0x0a, 0x05, 0x70, 0x72, 0x75, 0x6e, 0x65, 0x18, 0x0f, 0x20, 0x01, + 0x28, 0x08, 0x52, 0x05, 0x70, 0x72, 0x75, 0x6e, 0x65, 0x12, 0x34, 0x0a, 0x16, 0x6d, 0x61, 0x6e, + 0x61, 0x67, 0x65, 0x64, 0x5f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x63, 0x6f, + 0x75, 0x6e, 0x74, 0x18, 0x10, 0x20, 0x01, 0x28, 0x05, 0x52, 0x14, 0x6d, 0x61, 0x6e, 0x61, 0x67, + 0x65, 0x64, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, + 0x3f, 0x0a, 0x0d, 0x72, 0x65, 0x63, 0x6f, 0x6e, 0x63, 0x69, 0x6c, 0x65, 0x64, 0x5f, 0x61, 0x74, + 0x18, 0x11, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, + 0x6d, 0x70, 0x52, 0x0c, 0x72, 0x65, 0x63, 0x6f, 0x6e, 0x63, 0x69, 0x6c, 0x65, 0x64, 0x41, 0x74, + 0x12, 0x3c, 0x0a, 0x0c, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x73, 0x79, 0x6e, 0x63, 0x5f, 0x61, 0x74, + 0x18, 0x12, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, + 0x6d, 0x70, 0x52, 0x0a, 0x6c, 0x61, 0x73, 0x74, 0x53, 0x79, 0x6e, 0x63, 0x41, 0x74, 0x12, 0x26, + 0x0a, 0x0f, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x73, 0x79, 0x6e, 0x63, 0x5f, 0x70, 0x68, 0x61, 0x73, + 0x65, 0x18, 0x13, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x6c, 0x61, 0x73, 0x74, 0x53, 0x79, 0x6e, + 0x63, 0x50, 0x68, 0x61, 0x73, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x73, 0x70, 0x65, 0x63, 0x5f, 0x6a, + 0x73, 0x6f, 0x6e, 0x18, 0x14, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x73, 0x70, 0x65, 0x63, 0x4a, + 0x73, 0x6f, 0x6e, 0x12, 0x1f, 0x0a, 0x0b, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x5f, 0x6a, 0x73, + 0x6f, 0x6e, 0x18, 0x15, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, + 0x4a, 0x73, 0x6f, 0x6e, 0x22, 0xcc, 0x04, 0x0a, 0x19, 0x41, 0x72, 0x67, 0x6f, 0x41, 0x70, 0x70, + 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x65, 0x74, 0x44, 0x65, 0x74, 0x61, 0x69, + 0x6c, 0x73, 0x12, 0x27, 0x0a, 0x0f, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x5f, + 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0e, 0x67, 0x65, 0x6e, + 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x27, 0x0a, 0x0f, 0x67, + 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x73, 0x18, 0x02, + 0x20, 0x03, 0x28, 0x09, 0x52, 0x0e, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x54, + 0x79, 0x70, 0x65, 0x73, 0x12, 0x1f, 0x0a, 0x0b, 0x67, 0x6f, 0x5f, 0x74, 0x65, 0x6d, 0x70, 0x6c, + 0x61, 0x74, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x67, 0x6f, 0x54, 0x65, 0x6d, + 0x70, 0x6c, 0x61, 0x74, 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x73, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, + 0x79, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x73, 0x74, + 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x54, 0x79, 0x70, 0x65, 0x12, 0x29, 0x0a, 0x10, 0x74, 0x65, + 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x5f, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x18, 0x05, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x50, 0x72, + 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x12, 0x2a, 0x0a, 0x11, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, + 0x65, 0x5f, 0x72, 0x65, 0x70, 0x6f, 0x5f, 0x75, 0x72, 0x6c, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x0f, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x52, 0x65, 0x70, 0x6f, 0x55, 0x72, + 0x6c, 0x12, 0x44, 0x0a, 0x1e, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x5f, 0x64, 0x65, + 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, + 0x61, 0x63, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x1c, 0x74, 0x65, 0x6d, 0x70, 0x6c, + 0x61, 0x74, 0x65, 0x44, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4e, 0x61, + 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x27, 0x0a, 0x0f, 0x72, 0x65, 0x73, 0x6f, 0x75, + 0x72, 0x63, 0x65, 0x73, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x08, 0x20, 0x01, 0x28, 0x03, + 0x52, 0x0e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x43, 0x6f, 0x75, 0x6e, 0x74, + 0x12, 0x36, 0x0a, 0x17, 0x72, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x5f, 0x72, 0x65, 0x73, + 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x09, 0x20, 0x01, 0x28, + 0x05, 0x52, 0x15, 0x72, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x52, 0x65, 0x73, 0x6f, 0x75, + 0x72, 0x63, 0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x2e, 0x0a, 0x13, 0x68, 0x61, 0x73, 0x5f, + 0x65, 0x72, 0x72, 0x6f, 0x72, 0x5f, 0x63, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x18, + 0x0a, 0x20, 0x01, 0x28, 0x08, 0x52, 0x11, 0x68, 0x61, 0x73, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x43, + 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x2b, 0x0a, 0x11, 0x63, 0x6f, 0x6e, 0x64, + 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x0b, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x10, 0x63, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x4d, 0x65, + 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x73, 0x70, 0x65, 0x63, 0x5f, 0x6a, 0x73, + 0x6f, 0x6e, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x73, 0x70, 0x65, 0x63, 0x4a, 0x73, + 0x6f, 0x6e, 0x12, 0x1f, 0x0a, 0x0b, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x5f, 0x6a, 0x73, 0x6f, + 0x6e, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x4a, + 0x73, 0x6f, 0x6e, 0x22, 0x87, 0x01, 0x0a, 0x1f, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x52, 0x65, + 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x44, 0x65, 0x66, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x6f, 0x6e, + 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x73, + 0x65, 0x72, 0x76, 0x65, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x73, 0x65, 0x72, + 0x76, 0x65, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x12, 0x1e, 0x0a, + 0x0a, 0x64, 0x65, 0x70, 0x72, 0x65, 0x63, 0x61, 0x74, 0x65, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, + 0x08, 0x52, 0x0a, 0x64, 0x65, 0x70, 0x72, 0x65, 0x63, 0x61, 0x74, 0x65, 0x64, 0x22, 0xfb, 0x03, + 0x0a, 0x1f, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, + 0x44, 0x65, 0x66, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, + 0x73, 0x12, 0x14, 0x0a, 0x05, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x05, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x63, 0x6f, 0x70, 0x65, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x12, 0x12, 0x0a, + 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6b, 0x69, 0x6e, + 0x64, 0x12, 0x16, 0x0a, 0x06, 0x70, 0x6c, 0x75, 0x72, 0x61, 0x6c, 0x18, 0x04, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x06, 0x70, 0x6c, 0x75, 0x72, 0x61, 0x6c, 0x12, 0x1a, 0x0a, 0x08, 0x73, 0x69, 0x6e, + 0x67, 0x75, 0x6c, 0x61, 0x72, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x73, 0x69, 0x6e, + 0x67, 0x75, 0x6c, 0x61, 0x72, 0x12, 0x1b, 0x0a, 0x09, 0x6c, 0x69, 0x73, 0x74, 0x5f, 0x6b, 0x69, + 0x6e, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6c, 0x69, 0x73, 0x74, 0x4b, 0x69, + 0x6e, 0x64, 0x12, 0x1f, 0x0a, 0x0b, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, + 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0a, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x4e, 0x61, + 0x6d, 0x65, 0x73, 0x12, 0x43, 0x0a, 0x08, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x18, + 0x08, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x43, + 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x44, 0x65, 0x66, + 0x69, 0x6e, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x08, + 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x27, 0x0a, 0x0f, 0x73, 0x74, 0x6f, 0x72, + 0x61, 0x67, 0x65, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x09, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x0e, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, + 0x6e, 0x12, 0x27, 0x0a, 0x0f, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x64, 0x5f, 0x76, 0x65, 0x72, 0x73, + 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x0a, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0e, 0x73, 0x74, 0x6f, 0x72, + 0x65, 0x64, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x20, 0x0a, 0x0b, 0x65, 0x73, + 0x74, 0x61, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x65, 0x64, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x08, 0x52, + 0x0b, 0x65, 0x73, 0x74, 0x61, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x65, 0x64, 0x12, 0x2f, 0x0a, 0x13, + 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x73, 0x74, 0x72, 0x61, 0x74, + 0x65, 0x67, 0x79, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x09, 0x52, 0x12, 0x63, 0x6f, 0x6e, 0x76, 0x65, + 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x12, 0x1b, 0x0a, + 0x09, 0x73, 0x70, 0x65, 0x63, 0x5f, 0x6a, 0x73, 0x6f, 0x6e, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x08, 0x73, 0x70, 0x65, 0x63, 0x4a, 0x73, 0x6f, 0x6e, 0x12, 0x1f, 0x0a, 0x0b, 0x73, 0x74, + 0x61, 0x74, 0x75, 0x73, 0x5f, 0x6a, 0x73, 0x6f, 0x6e, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x0a, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x4a, 0x73, 0x6f, 0x6e, 0x22, 0xf5, 0x04, 0x0a, 0x18, + 0x46, 0x6c, 0x75, 0x78, 0x47, 0x69, 0x74, 0x52, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, + 0x79, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x72, 0x6c, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x72, 0x6c, 0x12, 0x17, 0x0a, 0x07, 0x67, 0x69, + 0x74, 0x5f, 0x72, 0x65, 0x66, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x67, 0x69, 0x74, + 0x52, 0x65, 0x66, 0x12, 0x29, 0x0a, 0x10, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x5f, + 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0f, 0x69, + 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x53, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x12, 0x1c, + 0x0a, 0x09, 0x73, 0x75, 0x73, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, + 0x08, 0x52, 0x09, 0x73, 0x75, 0x73, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x64, 0x12, 0x14, 0x0a, 0x05, + 0x72, 0x65, 0x61, 0x64, 0x79, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, 0x72, 0x65, 0x61, + 0x64, 0x79, 0x12, 0x21, 0x0a, 0x0c, 0x72, 0x65, 0x61, 0x64, 0x79, 0x5f, 0x72, 0x65, 0x61, 0x73, + 0x6f, 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x72, 0x65, 0x61, 0x64, 0x79, 0x52, + 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x12, 0x23, 0x0a, 0x0d, 0x72, 0x65, 0x61, 0x64, 0x79, 0x5f, 0x6d, + 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x72, 0x65, + 0x61, 0x64, 0x79, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x2f, 0x0a, 0x13, 0x6f, 0x62, + 0x73, 0x65, 0x72, 0x76, 0x65, 0x64, 0x5f, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x18, 0x08, 0x20, 0x01, 0x28, 0x03, 0x52, 0x12, 0x6f, 0x62, 0x73, 0x65, 0x72, 0x76, 0x65, + 0x64, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x2b, 0x0a, 0x11, 0x61, + 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, 0x74, 0x5f, 0x72, 0x65, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, + 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x61, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, 0x74, + 0x52, 0x65, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x27, 0x0a, 0x0f, 0x61, 0x72, 0x74, 0x69, + 0x66, 0x61, 0x63, 0x74, 0x5f, 0x64, 0x69, 0x67, 0x65, 0x73, 0x74, 0x18, 0x0a, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x0e, 0x61, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, 0x74, 0x44, 0x69, 0x67, 0x65, 0x73, + 0x74, 0x12, 0x55, 0x0a, 0x19, 0x61, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, 0x74, 0x5f, 0x6c, 0x61, + 0x73, 0x74, 0x5f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x0b, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, + 0x52, 0x16, 0x61, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, 0x74, 0x4c, 0x61, 0x73, 0x74, 0x55, 0x70, + 0x64, 0x61, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x61, 0x72, 0x74, 0x69, + 0x66, 0x61, 0x63, 0x74, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x03, 0x52, + 0x0c, 0x61, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, 0x74, 0x53, 0x69, 0x7a, 0x65, 0x12, 0x23, 0x0a, + 0x0d, 0x61, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, 0x74, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x0d, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x61, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, 0x74, 0x50, 0x61, + 0x74, 0x68, 0x12, 0x21, 0x0a, 0x0c, 0x61, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, 0x74, 0x5f, 0x75, + 0x72, 0x6c, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x61, 0x72, 0x74, 0x69, 0x66, 0x61, + 0x63, 0x74, 0x55, 0x72, 0x6c, 0x12, 0x1b, 0x0a, 0x09, 0x73, 0x70, 0x65, 0x63, 0x5f, 0x6a, 0x73, + 0x6f, 0x6e, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x73, 0x70, 0x65, 0x63, 0x4a, 0x73, + 0x6f, 0x6e, 0x12, 0x1f, 0x0a, 0x0b, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x5f, 0x6a, 0x73, 0x6f, + 0x6e, 0x18, 0x10, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x4a, + 0x73, 0x6f, 0x6e, 0x22, 0x91, 0x05, 0x0a, 0x18, 0x46, 0x6c, 0x75, 0x78, 0x4f, 0x43, 0x49, 0x52, + 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, + 0x12, 0x10, 0x0a, 0x03, 0x75, 0x72, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, + 0x72, 0x6c, 0x12, 0x17, 0x0a, 0x07, 0x6f, 0x63, 0x69, 0x5f, 0x72, 0x65, 0x66, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x06, 0x6f, 0x63, 0x69, 0x52, 0x65, 0x66, 0x12, 0x1a, 0x0a, 0x08, 0x70, + 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, + 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x12, 0x29, 0x0a, 0x10, 0x69, 0x6e, 0x74, 0x65, 0x72, + 0x76, 0x61, 0x6c, 0x5f, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, + 0x03, 0x52, 0x0f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x53, 0x65, 0x63, 0x6f, 0x6e, + 0x64, 0x73, 0x12, 0x1c, 0x0a, 0x09, 0x73, 0x75, 0x73, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x64, 0x18, + 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x73, 0x75, 0x73, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x64, + 0x12, 0x14, 0x0a, 0x05, 0x72, 0x65, 0x61, 0x64, 0x79, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, + 0x05, 0x72, 0x65, 0x61, 0x64, 0x79, 0x12, 0x21, 0x0a, 0x0c, 0x72, 0x65, 0x61, 0x64, 0x79, 0x5f, + 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x72, 0x65, + 0x61, 0x64, 0x79, 0x52, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x12, 0x23, 0x0a, 0x0d, 0x72, 0x65, 0x61, + 0x64, 0x79, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x0c, 0x72, 0x65, 0x61, 0x64, 0x79, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x2f, + 0x0a, 0x13, 0x6f, 0x62, 0x73, 0x65, 0x72, 0x76, 0x65, 0x64, 0x5f, 0x67, 0x65, 0x6e, 0x65, 0x72, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x09, 0x20, 0x01, 0x28, 0x03, 0x52, 0x12, 0x6f, 0x62, 0x73, + 0x65, 0x72, 0x76, 0x65, 0x64, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, + 0x2b, 0x0a, 0x11, 0x61, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, 0x74, 0x5f, 0x72, 0x65, 0x76, 0x69, + 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x61, 0x72, 0x74, 0x69, + 0x66, 0x61, 0x63, 0x74, 0x52, 0x65, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x27, 0x0a, 0x0f, + 0x61, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, 0x74, 0x5f, 0x64, 0x69, 0x67, 0x65, 0x73, 0x74, 0x18, + 0x0b, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x61, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, 0x74, 0x44, + 0x69, 0x67, 0x65, 0x73, 0x74, 0x12, 0x55, 0x0a, 0x19, 0x61, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, + 0x74, 0x5f, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x74, 0x69, + 0x6d, 0x65, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, - 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x12, - 0x39, 0x0a, 0x0a, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x2a, 0x20, + 0x74, 0x61, 0x6d, 0x70, 0x52, 0x16, 0x61, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, 0x74, 0x4c, 0x61, + 0x73, 0x74, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x23, 0x0a, 0x0d, + 0x61, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, 0x74, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x0d, 0x20, + 0x01, 0x28, 0x03, 0x52, 0x0c, 0x61, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, 0x74, 0x53, 0x69, 0x7a, + 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x61, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, 0x74, 0x5f, 0x70, 0x61, + 0x74, 0x68, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x61, 0x72, 0x74, 0x69, 0x66, 0x61, + 0x63, 0x74, 0x50, 0x61, 0x74, 0x68, 0x12, 0x21, 0x0a, 0x0c, 0x61, 0x72, 0x74, 0x69, 0x66, 0x61, + 0x63, 0x74, 0x5f, 0x75, 0x72, 0x6c, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x61, 0x72, + 0x74, 0x69, 0x66, 0x61, 0x63, 0x74, 0x55, 0x72, 0x6c, 0x12, 0x1b, 0x0a, 0x09, 0x73, 0x70, 0x65, + 0x63, 0x5f, 0x6a, 0x73, 0x6f, 0x6e, 0x18, 0x10, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x73, 0x70, + 0x65, 0x63, 0x4a, 0x73, 0x6f, 0x6e, 0x12, 0x1f, 0x0a, 0x0b, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, + 0x5f, 0x6a, 0x73, 0x6f, 0x6e, 0x18, 0x11, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x73, 0x74, 0x61, + 0x74, 0x75, 0x73, 0x4a, 0x73, 0x6f, 0x6e, 0x22, 0x96, 0x05, 0x0a, 0x19, 0x46, 0x6c, 0x75, 0x78, + 0x48, 0x65, 0x6c, 0x6d, 0x52, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x44, 0x65, + 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x72, 0x6c, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x03, 0x75, 0x72, 0x6c, 0x12, 0x1b, 0x0a, 0x09, 0x72, 0x65, 0x70, 0x6f, 0x5f, + 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x72, 0x65, 0x70, 0x6f, + 0x54, 0x79, 0x70, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, + 0x12, 0x29, 0x0a, 0x10, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x5f, 0x73, 0x65, 0x63, + 0x6f, 0x6e, 0x64, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0f, 0x69, 0x6e, 0x74, 0x65, + 0x72, 0x76, 0x61, 0x6c, 0x53, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x12, 0x1c, 0x0a, 0x09, 0x73, + 0x75, 0x73, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, + 0x73, 0x75, 0x73, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x72, 0x65, 0x61, + 0x64, 0x79, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, 0x72, 0x65, 0x61, 0x64, 0x79, 0x12, + 0x21, 0x0a, 0x0c, 0x72, 0x65, 0x61, 0x64, 0x79, 0x5f, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x18, + 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x72, 0x65, 0x61, 0x64, 0x79, 0x52, 0x65, 0x61, 0x73, + 0x6f, 0x6e, 0x12, 0x23, 0x0a, 0x0d, 0x72, 0x65, 0x61, 0x64, 0x79, 0x5f, 0x6d, 0x65, 0x73, 0x73, + 0x61, 0x67, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x72, 0x65, 0x61, 0x64, 0x79, + 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x2f, 0x0a, 0x13, 0x6f, 0x62, 0x73, 0x65, 0x72, + 0x76, 0x65, 0x64, 0x5f, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x09, + 0x20, 0x01, 0x28, 0x03, 0x52, 0x12, 0x6f, 0x62, 0x73, 0x65, 0x72, 0x76, 0x65, 0x64, 0x47, 0x65, + 0x6e, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x2b, 0x0a, 0x11, 0x61, 0x72, 0x74, 0x69, + 0x66, 0x61, 0x63, 0x74, 0x5f, 0x72, 0x65, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x0a, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x10, 0x61, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, 0x74, 0x52, 0x65, 0x76, + 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x27, 0x0a, 0x0f, 0x61, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, + 0x74, 0x5f, 0x64, 0x69, 0x67, 0x65, 0x73, 0x74, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, + 0x61, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, 0x74, 0x44, 0x69, 0x67, 0x65, 0x73, 0x74, 0x12, 0x55, + 0x0a, 0x19, 0x61, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, 0x74, 0x5f, 0x6c, 0x61, 0x73, 0x74, 0x5f, + 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x0c, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x16, 0x61, + 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, 0x74, 0x4c, 0x61, 0x73, 0x74, 0x55, 0x70, 0x64, 0x61, 0x74, + 0x65, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x61, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, + 0x74, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0c, 0x61, 0x72, + 0x74, 0x69, 0x66, 0x61, 0x63, 0x74, 0x53, 0x69, 0x7a, 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x61, 0x72, + 0x74, 0x69, 0x66, 0x61, 0x63, 0x74, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x0e, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x0c, 0x61, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, 0x74, 0x50, 0x61, 0x74, 0x68, 0x12, + 0x21, 0x0a, 0x0c, 0x61, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, 0x74, 0x5f, 0x75, 0x72, 0x6c, 0x18, + 0x0f, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x61, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, 0x74, 0x55, + 0x72, 0x6c, 0x12, 0x1b, 0x0a, 0x09, 0x73, 0x70, 0x65, 0x63, 0x5f, 0x6a, 0x73, 0x6f, 0x6e, 0x18, + 0x10, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x73, 0x70, 0x65, 0x63, 0x4a, 0x73, 0x6f, 0x6e, 0x12, + 0x1f, 0x0a, 0x0b, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x5f, 0x6a, 0x73, 0x6f, 0x6e, 0x18, 0x11, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x4a, 0x73, 0x6f, 0x6e, + 0x22, 0x8c, 0x06, 0x0a, 0x14, 0x46, 0x6c, 0x75, 0x78, 0x48, 0x65, 0x6c, 0x6d, 0x43, 0x68, 0x61, + 0x72, 0x74, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x68, 0x61, + 0x72, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, + 0x68, 0x61, 0x72, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x63, 0x68, 0x61, 0x72, + 0x74, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x0c, 0x63, 0x68, 0x61, 0x72, 0x74, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x26, 0x0a, + 0x0f, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x72, 0x65, 0x66, 0x5f, 0x6b, 0x69, 0x6e, 0x64, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x65, + 0x66, 0x4b, 0x69, 0x6e, 0x64, 0x12, 0x26, 0x0a, 0x0f, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, + 0x72, 0x65, 0x66, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, + 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x65, 0x66, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x30, 0x0a, + 0x14, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x72, 0x65, 0x66, 0x5f, 0x6e, 0x61, 0x6d, 0x65, + 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x12, 0x73, 0x6f, 0x75, + 0x72, 0x63, 0x65, 0x52, 0x65, 0x66, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, + 0x29, 0x0a, 0x10, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x5f, 0x73, 0x65, 0x63, 0x6f, + 0x6e, 0x64, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0f, 0x69, 0x6e, 0x74, 0x65, 0x72, + 0x76, 0x61, 0x6c, 0x53, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x12, 0x1c, 0x0a, 0x09, 0x73, 0x75, + 0x73, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x64, 0x18, 0x07, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x73, + 0x75, 0x73, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x72, 0x65, 0x61, 0x64, + 0x79, 0x18, 0x08, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, 0x72, 0x65, 0x61, 0x64, 0x79, 0x12, 0x21, + 0x0a, 0x0c, 0x72, 0x65, 0x61, 0x64, 0x79, 0x5f, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x18, 0x09, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x72, 0x65, 0x61, 0x64, 0x79, 0x52, 0x65, 0x61, 0x73, 0x6f, + 0x6e, 0x12, 0x23, 0x0a, 0x0d, 0x72, 0x65, 0x61, 0x64, 0x79, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, + 0x67, 0x65, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x72, 0x65, 0x61, 0x64, 0x79, 0x4d, + 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x2f, 0x0a, 0x13, 0x6f, 0x62, 0x73, 0x65, 0x72, 0x76, + 0x65, 0x64, 0x5f, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x0b, 0x20, + 0x01, 0x28, 0x03, 0x52, 0x12, 0x6f, 0x62, 0x73, 0x65, 0x72, 0x76, 0x65, 0x64, 0x47, 0x65, 0x6e, + 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x2b, 0x0a, 0x11, 0x61, 0x72, 0x74, 0x69, 0x66, + 0x61, 0x63, 0x74, 0x5f, 0x72, 0x65, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x0c, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x10, 0x61, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, 0x74, 0x52, 0x65, 0x76, 0x69, + 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x27, 0x0a, 0x0f, 0x61, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, 0x74, + 0x5f, 0x64, 0x69, 0x67, 0x65, 0x73, 0x74, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x61, + 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, 0x74, 0x44, 0x69, 0x67, 0x65, 0x73, 0x74, 0x12, 0x55, 0x0a, + 0x19, 0x61, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, 0x74, 0x5f, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x75, + 0x70, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, + 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x16, 0x61, 0x72, + 0x74, 0x69, 0x66, 0x61, 0x63, 0x74, 0x4c, 0x61, 0x73, 0x74, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, + 0x54, 0x69, 0x6d, 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x61, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, 0x74, + 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0c, 0x61, 0x72, 0x74, + 0x69, 0x66, 0x61, 0x63, 0x74, 0x53, 0x69, 0x7a, 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x61, 0x72, 0x74, + 0x69, 0x66, 0x61, 0x63, 0x74, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x10, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x0c, 0x61, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, 0x74, 0x50, 0x61, 0x74, 0x68, 0x12, 0x21, + 0x0a, 0x0c, 0x61, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, 0x74, 0x5f, 0x75, 0x72, 0x6c, 0x18, 0x11, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x61, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, 0x74, 0x55, 0x72, + 0x6c, 0x12, 0x1b, 0x0a, 0x09, 0x73, 0x70, 0x65, 0x63, 0x5f, 0x6a, 0x73, 0x6f, 0x6e, 0x18, 0x12, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x73, 0x70, 0x65, 0x63, 0x4a, 0x73, 0x6f, 0x6e, 0x12, 0x1f, + 0x0a, 0x0b, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x5f, 0x6a, 0x73, 0x6f, 0x6e, 0x18, 0x13, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x0a, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x4a, 0x73, 0x6f, 0x6e, 0x22, + 0xb4, 0x05, 0x0a, 0x11, 0x46, 0x6c, 0x75, 0x78, 0x42, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x44, 0x65, + 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x1f, 0x0a, 0x0b, 0x62, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x5f, + 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x62, 0x75, 0x63, 0x6b, + 0x65, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, + 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, + 0x65, 0x72, 0x12, 0x16, 0x0a, 0x06, 0x72, 0x65, 0x67, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x06, 0x72, 0x65, 0x67, 0x69, 0x6f, 0x6e, 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x6e, + 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x65, 0x6e, + 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x29, 0x0a, 0x10, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, + 0x61, 0x6c, 0x5f, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, + 0x52, 0x0f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x53, 0x65, 0x63, 0x6f, 0x6e, 0x64, + 0x73, 0x12, 0x1c, 0x0a, 0x09, 0x73, 0x75, 0x73, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x64, 0x18, 0x06, + 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x73, 0x75, 0x73, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x64, 0x12, + 0x14, 0x0a, 0x05, 0x72, 0x65, 0x61, 0x64, 0x79, 0x18, 0x07, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, + 0x72, 0x65, 0x61, 0x64, 0x79, 0x12, 0x21, 0x0a, 0x0c, 0x72, 0x65, 0x61, 0x64, 0x79, 0x5f, 0x72, + 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x72, 0x65, 0x61, + 0x64, 0x79, 0x52, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x12, 0x23, 0x0a, 0x0d, 0x72, 0x65, 0x61, 0x64, + 0x79, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x0c, 0x72, 0x65, 0x61, 0x64, 0x79, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x2f, 0x0a, + 0x13, 0x6f, 0x62, 0x73, 0x65, 0x72, 0x76, 0x65, 0x64, 0x5f, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x03, 0x52, 0x12, 0x6f, 0x62, 0x73, 0x65, + 0x72, 0x76, 0x65, 0x64, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x2b, + 0x0a, 0x11, 0x61, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, 0x74, 0x5f, 0x72, 0x65, 0x76, 0x69, 0x73, + 0x69, 0x6f, 0x6e, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x61, 0x72, 0x74, 0x69, 0x66, + 0x61, 0x63, 0x74, 0x52, 0x65, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x27, 0x0a, 0x0f, 0x61, + 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, 0x74, 0x5f, 0x64, 0x69, 0x67, 0x65, 0x73, 0x74, 0x18, 0x0c, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x61, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, 0x74, 0x44, 0x69, + 0x67, 0x65, 0x73, 0x74, 0x12, 0x55, 0x0a, 0x19, 0x61, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, 0x74, + 0x5f, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x74, 0x69, 0x6d, + 0x65, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, + 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, + 0x61, 0x6d, 0x70, 0x52, 0x16, 0x61, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, 0x74, 0x4c, 0x61, 0x73, + 0x74, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x61, + 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, 0x74, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x0e, 0x20, 0x01, + 0x28, 0x03, 0x52, 0x0c, 0x61, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, 0x74, 0x53, 0x69, 0x7a, 0x65, + 0x12, 0x23, 0x0a, 0x0d, 0x61, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, 0x74, 0x5f, 0x70, 0x61, 0x74, + 0x68, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x61, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, + 0x74, 0x50, 0x61, 0x74, 0x68, 0x12, 0x21, 0x0a, 0x0c, 0x61, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, + 0x74, 0x5f, 0x75, 0x72, 0x6c, 0x18, 0x10, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x61, 0x72, 0x74, + 0x69, 0x66, 0x61, 0x63, 0x74, 0x55, 0x72, 0x6c, 0x12, 0x1b, 0x0a, 0x09, 0x73, 0x70, 0x65, 0x63, + 0x5f, 0x6a, 0x73, 0x6f, 0x6e, 0x18, 0x11, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x73, 0x70, 0x65, + 0x63, 0x4a, 0x73, 0x6f, 0x6e, 0x12, 0x1f, 0x0a, 0x0b, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x5f, + 0x6a, 0x73, 0x6f, 0x6e, 0x18, 0x12, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x73, 0x74, 0x61, 0x74, + 0x75, 0x73, 0x4a, 0x73, 0x6f, 0x6e, 0x22, 0x90, 0x06, 0x0a, 0x18, 0x46, 0x6c, 0x75, 0x78, 0x4b, + 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x44, 0x65, 0x74, 0x61, + 0x69, 0x6c, 0x73, 0x12, 0x26, 0x0a, 0x0f, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x72, 0x65, + 0x66, 0x5f, 0x6b, 0x69, 0x6e, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x73, 0x6f, + 0x75, 0x72, 0x63, 0x65, 0x52, 0x65, 0x66, 0x4b, 0x69, 0x6e, 0x64, 0x12, 0x26, 0x0a, 0x0f, 0x73, + 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x72, 0x65, 0x66, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x65, 0x66, 0x4e, + 0x61, 0x6d, 0x65, 0x12, 0x30, 0x0a, 0x14, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x72, 0x65, + 0x66, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x12, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x65, 0x66, 0x4e, 0x61, 0x6d, 0x65, + 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x61, 0x74, 0x68, 0x18, 0x04, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x04, 0x70, 0x61, 0x74, 0x68, 0x12, 0x14, 0x0a, 0x05, 0x70, 0x72, 0x75, + 0x6e, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, 0x70, 0x72, 0x75, 0x6e, 0x65, 0x12, + 0x14, 0x0a, 0x05, 0x66, 0x6f, 0x72, 0x63, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, + 0x66, 0x6f, 0x72, 0x63, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x77, 0x61, 0x69, 0x74, 0x18, 0x07, 0x20, + 0x01, 0x28, 0x08, 0x52, 0x04, 0x77, 0x61, 0x69, 0x74, 0x12, 0x29, 0x0a, 0x10, 0x69, 0x6e, 0x74, + 0x65, 0x72, 0x76, 0x61, 0x6c, 0x5f, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x18, 0x08, 0x20, + 0x01, 0x28, 0x03, 0x52, 0x0f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x53, 0x65, 0x63, + 0x6f, 0x6e, 0x64, 0x73, 0x12, 0x27, 0x0a, 0x0f, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x5f, + 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x18, 0x09, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0e, 0x74, + 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x53, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x12, 0x1c, 0x0a, + 0x09, 0x73, 0x75, 0x73, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x64, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x08, + 0x52, 0x09, 0x73, 0x75, 0x73, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x72, + 0x65, 0x61, 0x64, 0x79, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, 0x72, 0x65, 0x61, 0x64, + 0x79, 0x12, 0x21, 0x0a, 0x0c, 0x72, 0x65, 0x61, 0x64, 0x79, 0x5f, 0x72, 0x65, 0x61, 0x73, 0x6f, + 0x6e, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x72, 0x65, 0x61, 0x64, 0x79, 0x52, 0x65, + 0x61, 0x73, 0x6f, 0x6e, 0x12, 0x23, 0x0a, 0x0d, 0x72, 0x65, 0x61, 0x64, 0x79, 0x5f, 0x6d, 0x65, + 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x72, 0x65, 0x61, + 0x64, 0x79, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x68, 0x65, 0x61, + 0x6c, 0x74, 0x68, 0x79, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x68, 0x65, 0x61, 0x6c, + 0x74, 0x68, 0x79, 0x12, 0x25, 0x0a, 0x0e, 0x68, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x79, 0x5f, 0x72, + 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x68, 0x65, 0x61, + 0x6c, 0x74, 0x68, 0x79, 0x52, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x12, 0x32, 0x0a, 0x15, 0x6c, 0x61, + 0x73, 0x74, 0x5f, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x65, 0x64, 0x5f, 0x72, 0x65, 0x76, 0x69, 0x73, + 0x69, 0x6f, 0x6e, 0x18, 0x10, 0x20, 0x01, 0x28, 0x09, 0x52, 0x13, 0x6c, 0x61, 0x73, 0x74, 0x41, + 0x70, 0x70, 0x6c, 0x69, 0x65, 0x64, 0x52, 0x65, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x36, + 0x0a, 0x17, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x61, 0x74, 0x74, 0x65, 0x6d, 0x70, 0x74, 0x65, 0x64, + 0x5f, 0x72, 0x65, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x11, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x15, 0x6c, 0x61, 0x73, 0x74, 0x41, 0x74, 0x74, 0x65, 0x6d, 0x70, 0x74, 0x65, 0x64, 0x52, 0x65, + 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x32, 0x0a, 0x15, 0x69, 0x6e, 0x76, 0x65, 0x6e, 0x74, + 0x6f, 0x72, 0x79, 0x5f, 0x65, 0x6e, 0x74, 0x72, 0x79, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, + 0x12, 0x20, 0x01, 0x28, 0x05, 0x52, 0x13, 0x69, 0x6e, 0x76, 0x65, 0x6e, 0x74, 0x6f, 0x72, 0x79, + 0x45, 0x6e, 0x74, 0x72, 0x79, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x2f, 0x0a, 0x13, 0x6f, 0x62, + 0x73, 0x65, 0x72, 0x76, 0x65, 0x64, 0x5f, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x18, 0x13, 0x20, 0x01, 0x28, 0x03, 0x52, 0x12, 0x6f, 0x62, 0x73, 0x65, 0x72, 0x76, 0x65, + 0x64, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1b, 0x0a, 0x09, 0x73, + 0x70, 0x65, 0x63, 0x5f, 0x6a, 0x73, 0x6f, 0x6e, 0x18, 0x14, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, + 0x73, 0x70, 0x65, 0x63, 0x4a, 0x73, 0x6f, 0x6e, 0x12, 0x1f, 0x0a, 0x0b, 0x73, 0x74, 0x61, 0x74, + 0x75, 0x73, 0x5f, 0x6a, 0x73, 0x6f, 0x6e, 0x18, 0x15, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x73, + 0x74, 0x61, 0x74, 0x75, 0x73, 0x4a, 0x73, 0x6f, 0x6e, 0x22, 0x81, 0x08, 0x0a, 0x16, 0x46, 0x6c, + 0x75, 0x78, 0x48, 0x65, 0x6c, 0x6d, 0x52, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x44, 0x65, 0x74, + 0x61, 0x69, 0x6c, 0x73, 0x12, 0x24, 0x0a, 0x0e, 0x63, 0x68, 0x61, 0x72, 0x74, 0x5f, 0x72, 0x65, + 0x66, 0x5f, 0x6b, 0x69, 0x6e, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x63, 0x68, + 0x61, 0x72, 0x74, 0x52, 0x65, 0x66, 0x4b, 0x69, 0x6e, 0x64, 0x12, 0x24, 0x0a, 0x0e, 0x63, 0x68, + 0x61, 0x72, 0x74, 0x5f, 0x72, 0x65, 0x66, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x0c, 0x63, 0x68, 0x61, 0x72, 0x74, 0x52, 0x65, 0x66, 0x4e, 0x61, 0x6d, 0x65, + 0x12, 0x2e, 0x0a, 0x13, 0x63, 0x68, 0x61, 0x72, 0x74, 0x5f, 0x72, 0x65, 0x66, 0x5f, 0x6e, 0x61, + 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x11, 0x63, + 0x68, 0x61, 0x72, 0x74, 0x52, 0x65, 0x66, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, + 0x12, 0x31, 0x0a, 0x15, 0x63, 0x68, 0x61, 0x72, 0x74, 0x5f, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, + 0x5f, 0x72, 0x65, 0x66, 0x5f, 0x6b, 0x69, 0x6e, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x12, 0x63, 0x68, 0x61, 0x72, 0x74, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x65, 0x66, 0x4b, + 0x69, 0x6e, 0x64, 0x12, 0x31, 0x0a, 0x15, 0x63, 0x68, 0x61, 0x72, 0x74, 0x5f, 0x73, 0x6f, 0x75, + 0x72, 0x63, 0x65, 0x5f, 0x72, 0x65, 0x66, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x05, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x12, 0x63, 0x68, 0x61, 0x72, 0x74, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, + 0x65, 0x66, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x3b, 0x0a, 0x1a, 0x63, 0x68, 0x61, 0x72, 0x74, 0x5f, + 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x72, 0x65, 0x66, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x73, + 0x70, 0x61, 0x63, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x17, 0x63, 0x68, 0x61, 0x72, + 0x74, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x65, 0x66, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, + 0x61, 0x63, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x68, 0x61, 0x72, 0x74, 0x5f, 0x6e, 0x61, 0x6d, + 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x68, 0x61, 0x72, 0x74, 0x4e, 0x61, + 0x6d, 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x63, 0x68, 0x61, 0x72, 0x74, 0x5f, 0x76, 0x65, 0x72, 0x73, + 0x69, 0x6f, 0x6e, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x63, 0x68, 0x61, 0x72, 0x74, + 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x21, 0x0a, 0x0c, 0x72, 0x65, 0x6c, 0x65, 0x61, + 0x73, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x72, + 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x29, 0x0a, 0x10, 0x74, 0x61, + 0x72, 0x67, 0x65, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x0a, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x4e, 0x61, 0x6d, 0x65, + 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x2b, 0x0a, 0x11, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, + 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x10, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, + 0x63, 0x65, 0x12, 0x29, 0x0a, 0x10, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x5f, 0x73, + 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0f, 0x69, 0x6e, + 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x53, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x12, 0x1c, 0x0a, + 0x09, 0x73, 0x75, 0x73, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x64, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x08, + 0x52, 0x09, 0x73, 0x75, 0x73, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x72, + 0x65, 0x61, 0x64, 0x79, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, 0x72, 0x65, 0x61, 0x64, + 0x79, 0x12, 0x21, 0x0a, 0x0c, 0x72, 0x65, 0x61, 0x64, 0x79, 0x5f, 0x72, 0x65, 0x61, 0x73, 0x6f, + 0x6e, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x72, 0x65, 0x61, 0x64, 0x79, 0x52, 0x65, + 0x61, 0x73, 0x6f, 0x6e, 0x12, 0x23, 0x0a, 0x0d, 0x72, 0x65, 0x61, 0x64, 0x79, 0x5f, 0x6d, 0x65, + 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x10, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x72, 0x65, 0x61, + 0x64, 0x79, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x36, 0x0a, 0x17, 0x6c, 0x61, 0x73, + 0x74, 0x5f, 0x61, 0x74, 0x74, 0x65, 0x6d, 0x70, 0x74, 0x65, 0x64, 0x5f, 0x72, 0x65, 0x76, 0x69, + 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x11, 0x20, 0x01, 0x28, 0x09, 0x52, 0x15, 0x6c, 0x61, 0x73, 0x74, + 0x41, 0x74, 0x74, 0x65, 0x6d, 0x70, 0x74, 0x65, 0x64, 0x52, 0x65, 0x76, 0x69, 0x73, 0x69, 0x6f, + 0x6e, 0x12, 0x43, 0x0a, 0x1e, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x61, 0x74, 0x74, 0x65, 0x6d, 0x70, + 0x74, 0x65, 0x64, 0x5f, 0x72, 0x65, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x64, 0x69, 0x67, + 0x65, 0x73, 0x74, 0x18, 0x12, 0x20, 0x01, 0x28, 0x09, 0x52, 0x1b, 0x6c, 0x61, 0x73, 0x74, 0x41, + 0x74, 0x74, 0x65, 0x6d, 0x70, 0x74, 0x65, 0x64, 0x52, 0x65, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, + 0x44, 0x69, 0x67, 0x65, 0x73, 0x74, 0x12, 0x32, 0x0a, 0x15, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x72, + 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x5f, 0x72, 0x65, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x18, + 0x13, 0x20, 0x01, 0x28, 0x09, 0x52, 0x13, 0x6c, 0x61, 0x73, 0x74, 0x52, 0x65, 0x6c, 0x65, 0x61, + 0x73, 0x65, 0x52, 0x65, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x1d, 0x0a, 0x0a, 0x68, 0x65, + 0x6c, 0x6d, 0x5f, 0x63, 0x68, 0x61, 0x72, 0x74, 0x18, 0x14, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, + 0x68, 0x65, 0x6c, 0x6d, 0x43, 0x68, 0x61, 0x72, 0x74, 0x12, 0x23, 0x0a, 0x0d, 0x68, 0x69, 0x73, + 0x74, 0x6f, 0x72, 0x79, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x15, 0x20, 0x01, 0x28, 0x05, + 0x52, 0x0c, 0x68, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x2f, + 0x0a, 0x13, 0x6f, 0x62, 0x73, 0x65, 0x72, 0x76, 0x65, 0x64, 0x5f, 0x67, 0x65, 0x6e, 0x65, 0x72, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x16, 0x20, 0x01, 0x28, 0x03, 0x52, 0x12, 0x6f, 0x62, 0x73, + 0x65, 0x72, 0x76, 0x65, 0x64, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, + 0x1b, 0x0a, 0x09, 0x73, 0x70, 0x65, 0x63, 0x5f, 0x6a, 0x73, 0x6f, 0x6e, 0x18, 0x17, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x08, 0x73, 0x70, 0x65, 0x63, 0x4a, 0x73, 0x6f, 0x6e, 0x12, 0x1f, 0x0a, 0x0b, + 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x5f, 0x6a, 0x73, 0x6f, 0x6e, 0x18, 0x18, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x0a, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x4a, 0x73, 0x6f, 0x6e, 0x22, 0xff, 0x01, + 0x0a, 0x12, 0x46, 0x6c, 0x75, 0x78, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x44, 0x65, 0x74, + 0x61, 0x69, 0x6c, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x72, 0x65, 0x61, 0x64, 0x79, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x08, 0x52, 0x05, 0x72, 0x65, 0x61, 0x64, 0x79, 0x12, 0x21, 0x0a, 0x0c, 0x72, 0x65, + 0x61, 0x64, 0x79, 0x5f, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x0b, 0x72, 0x65, 0x61, 0x64, 0x79, 0x52, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x12, 0x23, 0x0a, + 0x0d, 0x72, 0x65, 0x61, 0x64, 0x79, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x72, 0x65, 0x61, 0x64, 0x79, 0x4d, 0x65, 0x73, 0x73, 0x61, + 0x67, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x73, 0x75, 0x73, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x64, 0x18, + 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x73, 0x75, 0x73, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x64, + 0x12, 0x2f, 0x0a, 0x13, 0x6f, 0x62, 0x73, 0x65, 0x72, 0x76, 0x65, 0x64, 0x5f, 0x67, 0x65, 0x6e, + 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x12, 0x6f, + 0x62, 0x73, 0x65, 0x72, 0x76, 0x65, 0x64, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x12, 0x1b, 0x0a, 0x09, 0x73, 0x70, 0x65, 0x63, 0x5f, 0x6a, 0x73, 0x6f, 0x6e, 0x18, 0x06, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x73, 0x70, 0x65, 0x63, 0x4a, 0x73, 0x6f, 0x6e, 0x12, 0x1f, + 0x0a, 0x0b, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x5f, 0x6a, 0x73, 0x6f, 0x6e, 0x18, 0x07, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x0a, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x4a, 0x73, 0x6f, 0x6e, 0x22, + 0x7f, 0x0a, 0x16, 0x46, 0x6c, 0x61, 0x67, 0x67, 0x65, 0x72, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, + 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x61, 0x70, 0x69, + 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, + 0x61, 0x70, 0x69, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x6b, 0x69, + 0x6e, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x12, 0x12, + 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, + 0x6d, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, + 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, + 0x22, 0xa6, 0x01, 0x0a, 0x11, 0x46, 0x6c, 0x61, 0x67, 0x67, 0x65, 0x72, 0x47, 0x61, 0x74, 0x65, + 0x77, 0x61, 0x79, 0x52, 0x65, 0x66, 0x12, 0x14, 0x0a, 0x05, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x12, 0x12, 0x0a, 0x04, + 0x6b, 0x69, 0x6e, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6b, 0x69, 0x6e, 0x64, + 0x12, 0x1c, 0x0a, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x12, + 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, + 0x6d, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x73, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6e, 0x61, + 0x6d, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x73, 0x65, 0x63, 0x74, 0x69, 0x6f, + 0x6e, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x6f, 0x72, 0x74, 0x18, 0x06, 0x20, + 0x01, 0x28, 0x05, 0x52, 0x04, 0x70, 0x6f, 0x72, 0x74, 0x22, 0x80, 0x02, 0x0a, 0x13, 0x46, 0x6c, + 0x61, 0x67, 0x67, 0x65, 0x72, 0x43, 0x61, 0x6e, 0x61, 0x72, 0x79, 0x4d, 0x65, 0x74, 0x72, 0x69, + 0x63, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, + 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, + 0x6c, 0x12, 0x28, 0x0a, 0x0d, 0x74, 0x68, 0x72, 0x65, 0x73, 0x68, 0x6f, 0x6c, 0x64, 0x5f, 0x6d, + 0x69, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x01, 0x48, 0x00, 0x52, 0x0c, 0x74, 0x68, 0x72, 0x65, + 0x73, 0x68, 0x6f, 0x6c, 0x64, 0x4d, 0x69, 0x6e, 0x88, 0x01, 0x01, 0x12, 0x28, 0x0a, 0x0d, 0x74, + 0x68, 0x72, 0x65, 0x73, 0x68, 0x6f, 0x6c, 0x64, 0x5f, 0x6d, 0x61, 0x78, 0x18, 0x04, 0x20, 0x01, + 0x28, 0x01, 0x48, 0x01, 0x52, 0x0c, 0x74, 0x68, 0x72, 0x65, 0x73, 0x68, 0x6f, 0x6c, 0x64, 0x4d, + 0x61, 0x78, 0x88, 0x01, 0x01, 0x12, 0x41, 0x0a, 0x0c, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, + 0x65, 0x5f, 0x72, 0x65, 0x66, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x61, 0x70, + 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x46, 0x6c, 0x61, 0x67, 0x67, 0x65, 0x72, 0x4f, 0x62, 0x6a, 0x65, + 0x63, 0x74, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x52, 0x0b, 0x74, 0x65, 0x6d, + 0x70, 0x6c, 0x61, 0x74, 0x65, 0x52, 0x65, 0x66, 0x42, 0x10, 0x0a, 0x0e, 0x5f, 0x74, 0x68, 0x72, + 0x65, 0x73, 0x68, 0x6f, 0x6c, 0x64, 0x5f, 0x6d, 0x69, 0x6e, 0x42, 0x10, 0x0a, 0x0e, 0x5f, 0x74, + 0x68, 0x72, 0x65, 0x73, 0x68, 0x6f, 0x6c, 0x64, 0x5f, 0x6d, 0x61, 0x78, 0x22, 0x87, 0x01, 0x0a, + 0x12, 0x46, 0x6c, 0x61, 0x67, 0x67, 0x65, 0x72, 0x43, 0x61, 0x6e, 0x61, 0x72, 0x79, 0x41, 0x6c, + 0x65, 0x72, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x73, 0x65, 0x76, 0x65, 0x72, + 0x69, 0x74, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x73, 0x65, 0x76, 0x65, 0x72, + 0x69, 0x74, 0x79, 0x12, 0x41, 0x0a, 0x0c, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x5f, + 0x72, 0x65, 0x66, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x61, 0x70, 0x69, 0x2e, + 0x76, 0x31, 0x2e, 0x46, 0x6c, 0x61, 0x67, 0x67, 0x65, 0x72, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, + 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x52, 0x0b, 0x70, 0x72, 0x6f, 0x76, 0x69, + 0x64, 0x65, 0x72, 0x52, 0x65, 0x66, 0x22, 0xc4, 0x01, 0x0a, 0x14, 0x46, 0x6c, 0x61, 0x67, 0x67, + 0x65, 0x72, 0x43, 0x61, 0x6e, 0x61, 0x72, 0x79, 0x57, 0x65, 0x62, 0x68, 0x6f, 0x6f, 0x6b, 0x12, + 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, + 0x61, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x72, 0x6c, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x72, 0x6c, 0x12, 0x18, 0x0a, 0x07, 0x74, 0x69, 0x6d, + 0x65, 0x6f, 0x75, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x74, 0x69, 0x6d, 0x65, + 0x6f, 0x75, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x6d, 0x75, 0x74, 0x65, 0x5f, 0x61, 0x6c, 0x65, 0x72, + 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x6d, 0x75, 0x74, 0x65, 0x41, 0x6c, 0x65, + 0x72, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, 0x74, 0x72, 0x69, 0x65, 0x73, 0x18, 0x06, 0x20, + 0x01, 0x28, 0x05, 0x52, 0x07, 0x72, 0x65, 0x74, 0x72, 0x69, 0x65, 0x73, 0x12, 0x1f, 0x0a, 0x0b, + 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x74, 0x6c, 0x73, 0x18, 0x07, 0x20, 0x01, 0x28, + 0x08, 0x52, 0x0a, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x54, 0x6c, 0x73, 0x22, 0xc7, 0x10, + 0x0a, 0x14, 0x46, 0x6c, 0x61, 0x67, 0x67, 0x65, 0x72, 0x43, 0x61, 0x6e, 0x61, 0x72, 0x79, 0x44, + 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x70, 0x68, 0x61, 0x73, 0x65, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x70, 0x68, 0x61, 0x73, 0x65, 0x12, 0x27, 0x0a, 0x0f, + 0x70, 0x72, 0x6f, 0x6d, 0x6f, 0x74, 0x65, 0x64, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x70, 0x72, 0x6f, 0x6d, 0x6f, 0x74, 0x65, 0x64, 0x53, + 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x27, 0x0a, 0x0f, 0x70, 0x72, 0x6f, 0x6d, 0x6f, 0x74, 0x65, + 0x64, 0x5f, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, + 0x70, 0x72, 0x6f, 0x6d, 0x6f, 0x74, 0x65, 0x64, 0x52, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x12, 0x29, + 0x0a, 0x10, 0x70, 0x72, 0x6f, 0x6d, 0x6f, 0x74, 0x65, 0x64, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, + 0x67, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x70, 0x72, 0x6f, 0x6d, 0x6f, 0x74, + 0x65, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x4c, 0x0a, 0x14, 0x6c, 0x61, 0x73, + 0x74, 0x5f, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x74, 0x69, 0x6d, + 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, + 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, + 0x61, 0x6d, 0x70, 0x52, 0x12, 0x6c, 0x61, 0x73, 0x74, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x69, 0x74, + 0x69, 0x6f, 0x6e, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x63, 0x61, 0x6e, 0x61, 0x72, + 0x79, 0x5f, 0x77, 0x65, 0x69, 0x67, 0x68, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0c, + 0x63, 0x61, 0x6e, 0x61, 0x72, 0x79, 0x57, 0x65, 0x69, 0x67, 0x68, 0x74, 0x12, 0x23, 0x0a, 0x0d, + 0x66, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x5f, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x73, 0x18, 0x07, 0x20, + 0x01, 0x28, 0x05, 0x52, 0x0c, 0x66, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x43, 0x68, 0x65, 0x63, 0x6b, + 0x73, 0x12, 0x1e, 0x0a, 0x0a, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, + 0x08, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0a, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x73, 0x12, 0x2a, 0x0a, 0x11, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x65, + 0x64, 0x5f, 0x73, 0x70, 0x65, 0x63, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x6c, 0x61, + 0x73, 0x74, 0x41, 0x70, 0x70, 0x6c, 0x69, 0x65, 0x64, 0x53, 0x70, 0x65, 0x63, 0x12, 0x2c, 0x0a, + 0x12, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x70, 0x72, 0x6f, 0x6d, 0x6f, 0x74, 0x65, 0x64, 0x5f, 0x73, + 0x70, 0x65, 0x63, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x6c, 0x61, 0x73, 0x74, 0x50, + 0x72, 0x6f, 0x6d, 0x6f, 0x74, 0x65, 0x64, 0x53, 0x70, 0x65, 0x63, 0x12, 0x36, 0x0a, 0x17, 0x73, + 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x61, 0x66, 0x66, 0x69, 0x6e, 0x69, 0x74, 0x79, 0x5f, + 0x63, 0x6f, 0x6f, 0x6b, 0x69, 0x65, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x09, 0x52, 0x15, 0x73, 0x65, + 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x41, 0x66, 0x66, 0x69, 0x6e, 0x69, 0x74, 0x79, 0x43, 0x6f, 0x6f, + 0x6b, 0x69, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x18, + 0x0c, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x12, + 0x3f, 0x0a, 0x19, 0x70, 0x72, 0x6f, 0x67, 0x72, 0x65, 0x73, 0x73, 0x5f, 0x64, 0x65, 0x61, 0x64, + 0x6c, 0x69, 0x6e, 0x65, 0x5f, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x18, 0x0d, 0x20, 0x01, + 0x28, 0x05, 0x48, 0x00, 0x52, 0x17, 0x70, 0x72, 0x6f, 0x67, 0x72, 0x65, 0x73, 0x73, 0x44, 0x65, + 0x61, 0x64, 0x6c, 0x69, 0x6e, 0x65, 0x53, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x88, 0x01, 0x01, + 0x12, 0x23, 0x0a, 0x0d, 0x73, 0x6b, 0x69, 0x70, 0x5f, 0x61, 0x6e, 0x61, 0x6c, 0x79, 0x73, 0x69, + 0x73, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x73, 0x6b, 0x69, 0x70, 0x41, 0x6e, 0x61, + 0x6c, 0x79, 0x73, 0x69, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x73, 0x75, 0x73, 0x70, 0x65, 0x6e, 0x64, + 0x18, 0x0f, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x73, 0x75, 0x73, 0x70, 0x65, 0x6e, 0x64, 0x12, + 0x2c, 0x0a, 0x12, 0x72, 0x65, 0x76, 0x65, 0x72, 0x74, 0x5f, 0x6f, 0x6e, 0x5f, 0x64, 0x65, 0x6c, + 0x65, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x10, 0x20, 0x01, 0x28, 0x08, 0x52, 0x10, 0x72, 0x65, 0x76, + 0x65, 0x72, 0x74, 0x4f, 0x6e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x3d, 0x0a, + 0x0a, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x5f, 0x72, 0x65, 0x66, 0x18, 0x11, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x1e, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x46, 0x6c, 0x61, 0x67, 0x67, + 0x65, 0x72, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, + 0x65, 0x52, 0x09, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x52, 0x65, 0x66, 0x12, 0x45, 0x0a, 0x0e, + 0x61, 0x75, 0x74, 0x6f, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x72, 0x5f, 0x72, 0x65, 0x66, 0x18, 0x12, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x46, 0x6c, + 0x61, 0x67, 0x67, 0x65, 0x72, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x65, 0x66, 0x65, 0x72, + 0x65, 0x6e, 0x63, 0x65, 0x52, 0x0d, 0x61, 0x75, 0x74, 0x6f, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x72, + 0x52, 0x65, 0x66, 0x12, 0x26, 0x0a, 0x0c, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x70, + 0x6f, 0x72, 0x74, 0x18, 0x13, 0x20, 0x01, 0x28, 0x05, 0x48, 0x01, 0x52, 0x0b, 0x73, 0x65, 0x72, + 0x76, 0x69, 0x63, 0x65, 0x50, 0x6f, 0x72, 0x74, 0x88, 0x01, 0x01, 0x12, 0x2e, 0x0a, 0x13, 0x73, + 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x5f, 0x70, 0x6f, + 0x72, 0x74, 0x18, 0x14, 0x20, 0x01, 0x28, 0x09, 0x52, 0x11, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, + 0x65, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x50, 0x6f, 0x72, 0x74, 0x12, 0x2a, 0x0a, 0x11, 0x73, + 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x70, 0x6f, 0x72, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, + 0x18, 0x15, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x50, + 0x6f, 0x72, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x73, 0x65, 0x72, 0x76, 0x69, + 0x63, 0x65, 0x5f, 0x68, 0x6f, 0x73, 0x74, 0x73, 0x18, 0x16, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0c, + 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x48, 0x6f, 0x73, 0x74, 0x73, 0x12, 0x29, 0x0a, 0x10, + 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x68, 0x65, 0x61, 0x64, 0x6c, 0x65, 0x73, 0x73, + 0x18, 0x17, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x48, + 0x65, 0x61, 0x64, 0x6c, 0x65, 0x73, 0x73, 0x12, 0x34, 0x0a, 0x16, 0x73, 0x65, 0x72, 0x76, 0x69, + 0x63, 0x65, 0x5f, 0x70, 0x6f, 0x72, 0x74, 0x5f, 0x64, 0x69, 0x73, 0x63, 0x6f, 0x76, 0x65, 0x72, + 0x79, 0x18, 0x18, 0x20, 0x01, 0x28, 0x08, 0x52, 0x14, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, + 0x50, 0x6f, 0x72, 0x74, 0x44, 0x69, 0x73, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x79, 0x12, 0x27, 0x0a, + 0x0f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, + 0x18, 0x19, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x54, + 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x12, 0x3c, 0x0a, 0x0c, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, + 0x79, 0x5f, 0x72, 0x65, 0x66, 0x73, 0x18, 0x1a, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x61, + 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x46, 0x6c, 0x61, 0x67, 0x67, 0x65, 0x72, 0x47, 0x61, 0x74, + 0x65, 0x77, 0x61, 0x79, 0x52, 0x65, 0x66, 0x52, 0x0b, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, + 0x52, 0x65, 0x66, 0x73, 0x12, 0x2b, 0x0a, 0x11, 0x61, 0x6e, 0x61, 0x6c, 0x79, 0x73, 0x69, 0x73, + 0x5f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x18, 0x1b, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x10, 0x61, 0x6e, 0x61, 0x6c, 0x79, 0x73, 0x69, 0x73, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, + 0x6c, 0x12, 0x2d, 0x0a, 0x12, 0x61, 0x6e, 0x61, 0x6c, 0x79, 0x73, 0x69, 0x73, 0x5f, 0x74, 0x68, + 0x72, 0x65, 0x73, 0x68, 0x6f, 0x6c, 0x64, 0x18, 0x1c, 0x20, 0x01, 0x28, 0x05, 0x52, 0x11, 0x61, + 0x6e, 0x61, 0x6c, 0x79, 0x73, 0x69, 0x73, 0x54, 0x68, 0x72, 0x65, 0x73, 0x68, 0x6f, 0x6c, 0x64, + 0x12, 0x2f, 0x0a, 0x13, 0x61, 0x6e, 0x61, 0x6c, 0x79, 0x73, 0x69, 0x73, 0x5f, 0x69, 0x74, 0x65, + 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x1d, 0x20, 0x01, 0x28, 0x05, 0x52, 0x12, 0x61, + 0x6e, 0x61, 0x6c, 0x79, 0x73, 0x69, 0x73, 0x49, 0x74, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x73, 0x12, 0x2e, 0x0a, 0x13, 0x61, 0x6e, 0x61, 0x6c, 0x79, 0x73, 0x69, 0x73, 0x5f, 0x6d, 0x61, + 0x78, 0x5f, 0x77, 0x65, 0x69, 0x67, 0x68, 0x74, 0x18, 0x1e, 0x20, 0x01, 0x28, 0x05, 0x52, 0x11, + 0x61, 0x6e, 0x61, 0x6c, 0x79, 0x73, 0x69, 0x73, 0x4d, 0x61, 0x78, 0x57, 0x65, 0x69, 0x67, 0x68, + 0x74, 0x12, 0x30, 0x0a, 0x14, 0x61, 0x6e, 0x61, 0x6c, 0x79, 0x73, 0x69, 0x73, 0x5f, 0x73, 0x74, + 0x65, 0x70, 0x5f, 0x77, 0x65, 0x69, 0x67, 0x68, 0x74, 0x18, 0x1f, 0x20, 0x01, 0x28, 0x05, 0x52, + 0x12, 0x61, 0x6e, 0x61, 0x6c, 0x79, 0x73, 0x69, 0x73, 0x53, 0x74, 0x65, 0x70, 0x57, 0x65, 0x69, + 0x67, 0x68, 0x74, 0x12, 0x32, 0x0a, 0x15, 0x61, 0x6e, 0x61, 0x6c, 0x79, 0x73, 0x69, 0x73, 0x5f, + 0x73, 0x74, 0x65, 0x70, 0x5f, 0x77, 0x65, 0x69, 0x67, 0x68, 0x74, 0x73, 0x18, 0x20, 0x20, 0x03, + 0x28, 0x05, 0x52, 0x13, 0x61, 0x6e, 0x61, 0x6c, 0x79, 0x73, 0x69, 0x73, 0x53, 0x74, 0x65, 0x70, + 0x57, 0x65, 0x69, 0x67, 0x68, 0x74, 0x73, 0x12, 0x43, 0x0a, 0x1e, 0x61, 0x6e, 0x61, 0x6c, 0x79, + 0x73, 0x69, 0x73, 0x5f, 0x73, 0x74, 0x65, 0x70, 0x5f, 0x77, 0x65, 0x69, 0x67, 0x68, 0x74, 0x5f, + 0x70, 0x72, 0x6f, 0x6d, 0x6f, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x21, 0x20, 0x01, 0x28, 0x05, 0x52, + 0x1b, 0x61, 0x6e, 0x61, 0x6c, 0x79, 0x73, 0x69, 0x73, 0x53, 0x74, 0x65, 0x70, 0x57, 0x65, 0x69, + 0x67, 0x68, 0x74, 0x50, 0x72, 0x6f, 0x6d, 0x6f, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x27, 0x0a, 0x0f, + 0x61, 0x6e, 0x61, 0x6c, 0x79, 0x73, 0x69, 0x73, 0x5f, 0x6d, 0x69, 0x72, 0x72, 0x6f, 0x72, 0x18, + 0x22, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0e, 0x61, 0x6e, 0x61, 0x6c, 0x79, 0x73, 0x69, 0x73, 0x4d, + 0x69, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x3b, 0x0a, 0x17, 0x70, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, + 0x5f, 0x72, 0x65, 0x61, 0x64, 0x79, 0x5f, 0x74, 0x68, 0x72, 0x65, 0x73, 0x68, 0x6f, 0x6c, 0x64, + 0x18, 0x23, 0x20, 0x01, 0x28, 0x05, 0x48, 0x02, 0x52, 0x15, 0x70, 0x72, 0x69, 0x6d, 0x61, 0x72, + 0x79, 0x52, 0x65, 0x61, 0x64, 0x79, 0x54, 0x68, 0x72, 0x65, 0x73, 0x68, 0x6f, 0x6c, 0x64, 0x88, + 0x01, 0x01, 0x12, 0x39, 0x0a, 0x16, 0x63, 0x61, 0x6e, 0x61, 0x72, 0x79, 0x5f, 0x72, 0x65, 0x61, + 0x64, 0x79, 0x5f, 0x74, 0x68, 0x72, 0x65, 0x73, 0x68, 0x6f, 0x6c, 0x64, 0x18, 0x24, 0x20, 0x01, + 0x28, 0x05, 0x48, 0x03, 0x52, 0x14, 0x63, 0x61, 0x6e, 0x61, 0x72, 0x79, 0x52, 0x65, 0x61, 0x64, + 0x79, 0x54, 0x68, 0x72, 0x65, 0x73, 0x68, 0x6f, 0x6c, 0x64, 0x88, 0x01, 0x01, 0x12, 0x3f, 0x0a, + 0x1c, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x61, 0x66, 0x66, 0x69, 0x6e, 0x69, 0x74, + 0x79, 0x5f, 0x63, 0x6f, 0x6f, 0x6b, 0x69, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x25, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x19, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x41, 0x66, 0x66, 0x69, + 0x6e, 0x69, 0x74, 0x79, 0x43, 0x6f, 0x6f, 0x6b, 0x69, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x35, + 0x0a, 0x07, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x18, 0x26, 0x20, 0x03, 0x28, 0x0b, 0x32, + 0x1b, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x46, 0x6c, 0x61, 0x67, 0x67, 0x65, 0x72, + 0x43, 0x61, 0x6e, 0x61, 0x72, 0x79, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x52, 0x07, 0x6d, 0x65, + 0x74, 0x72, 0x69, 0x63, 0x73, 0x12, 0x32, 0x0a, 0x06, 0x61, 0x6c, 0x65, 0x72, 0x74, 0x73, 0x18, + 0x27, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x46, + 0x6c, 0x61, 0x67, 0x67, 0x65, 0x72, 0x43, 0x61, 0x6e, 0x61, 0x72, 0x79, 0x41, 0x6c, 0x65, 0x72, + 0x74, 0x52, 0x06, 0x61, 0x6c, 0x65, 0x72, 0x74, 0x73, 0x12, 0x38, 0x0a, 0x08, 0x77, 0x65, 0x62, + 0x68, 0x6f, 0x6f, 0x6b, 0x73, 0x18, 0x28, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x61, 0x70, + 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x46, 0x6c, 0x61, 0x67, 0x67, 0x65, 0x72, 0x43, 0x61, 0x6e, 0x61, + 0x72, 0x79, 0x57, 0x65, 0x62, 0x68, 0x6f, 0x6f, 0x6b, 0x52, 0x08, 0x77, 0x65, 0x62, 0x68, 0x6f, + 0x6f, 0x6b, 0x73, 0x12, 0x1b, 0x0a, 0x09, 0x73, 0x70, 0x65, 0x63, 0x5f, 0x6a, 0x73, 0x6f, 0x6e, + 0x18, 0x29, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x73, 0x70, 0x65, 0x63, 0x4a, 0x73, 0x6f, 0x6e, + 0x12, 0x1f, 0x0a, 0x0b, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x5f, 0x6a, 0x73, 0x6f, 0x6e, 0x18, + 0x2a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x4a, 0x73, 0x6f, + 0x6e, 0x42, 0x1c, 0x0a, 0x1a, 0x5f, 0x70, 0x72, 0x6f, 0x67, 0x72, 0x65, 0x73, 0x73, 0x5f, 0x64, + 0x65, 0x61, 0x64, 0x6c, 0x69, 0x6e, 0x65, 0x5f, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x42, + 0x0f, 0x0a, 0x0d, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x70, 0x6f, 0x72, 0x74, + 0x42, 0x1a, 0x0a, 0x18, 0x5f, 0x70, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x5f, 0x72, 0x65, 0x61, + 0x64, 0x79, 0x5f, 0x74, 0x68, 0x72, 0x65, 0x73, 0x68, 0x6f, 0x6c, 0x64, 0x42, 0x19, 0x0a, 0x17, + 0x5f, 0x63, 0x61, 0x6e, 0x61, 0x72, 0x79, 0x5f, 0x72, 0x65, 0x61, 0x64, 0x79, 0x5f, 0x74, 0x68, + 0x72, 0x65, 0x73, 0x68, 0x6f, 0x6c, 0x64, 0x22, 0xcb, 0x03, 0x0a, 0x1c, 0x46, 0x6c, 0x61, 0x67, + 0x67, 0x65, 0x72, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, + 0x65, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x23, 0x0a, 0x0d, 0x70, 0x72, 0x6f, 0x76, + 0x69, 0x64, 0x65, 0x72, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x0c, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x54, 0x79, 0x70, 0x65, 0x12, 0x29, 0x0a, + 0x10, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, + 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, + 0x72, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x27, 0x0a, 0x0f, 0x70, 0x72, 0x6f, 0x76, + 0x69, 0x64, 0x65, 0x72, 0x5f, 0x72, 0x65, 0x67, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x0e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x52, 0x65, 0x67, 0x69, 0x6f, + 0x6e, 0x12, 0x41, 0x0a, 0x1d, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x5f, 0x69, 0x6e, + 0x73, 0x65, 0x63, 0x75, 0x72, 0x65, 0x5f, 0x73, 0x6b, 0x69, 0x70, 0x5f, 0x76, 0x65, 0x72, 0x69, + 0x66, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x1a, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, + 0x65, 0x72, 0x49, 0x6e, 0x73, 0x65, 0x63, 0x75, 0x72, 0x65, 0x53, 0x6b, 0x69, 0x70, 0x56, 0x65, + 0x72, 0x69, 0x66, 0x79, 0x12, 0x30, 0x0a, 0x14, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, + 0x5f, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x05, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x12, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x53, 0x65, 0x63, 0x72, + 0x65, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x71, 0x75, 0x65, 0x72, 0x79, 0x18, + 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x71, 0x75, 0x65, 0x72, 0x79, 0x12, 0x21, 0x0a, 0x0c, + 0x72, 0x65, 0x61, 0x64, 0x79, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x07, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x0b, 0x72, 0x65, 0x61, 0x64, 0x79, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, + 0x21, 0x0a, 0x0c, 0x72, 0x65, 0x61, 0x64, 0x79, 0x5f, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x18, + 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x72, 0x65, 0x61, 0x64, 0x79, 0x52, 0x65, 0x61, 0x73, + 0x6f, 0x6e, 0x12, 0x23, 0x0a, 0x0d, 0x72, 0x65, 0x61, 0x64, 0x79, 0x5f, 0x6d, 0x65, 0x73, 0x73, + 0x61, 0x67, 0x65, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x72, 0x65, 0x61, 0x64, 0x79, + 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x73, 0x70, 0x65, 0x63, 0x5f, + 0x6a, 0x73, 0x6f, 0x6e, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x73, 0x70, 0x65, 0x63, + 0x4a, 0x73, 0x6f, 0x6e, 0x12, 0x1f, 0x0a, 0x0b, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x5f, 0x6a, + 0x73, 0x6f, 0x6e, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x73, 0x74, 0x61, 0x74, 0x75, + 0x73, 0x4a, 0x73, 0x6f, 0x6e, 0x22, 0xe1, 0x02, 0x0a, 0x1b, 0x46, 0x6c, 0x61, 0x67, 0x67, 0x65, + 0x72, 0x41, 0x6c, 0x65, 0x72, 0x74, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x44, 0x65, + 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x63, 0x68, 0x61, + 0x6e, 0x6e, 0x65, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x63, 0x68, 0x61, 0x6e, + 0x6e, 0x65, 0x6c, 0x12, 0x1a, 0x0a, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x12, + 0x18, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x70, 0x72, 0x6f, + 0x78, 0x79, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x12, + 0x1f, 0x0a, 0x0b, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x06, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x4e, 0x61, 0x6d, 0x65, + 0x12, 0x21, 0x0a, 0x0c, 0x72, 0x65, 0x61, 0x64, 0x79, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, + 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x72, 0x65, 0x61, 0x64, 0x79, 0x53, 0x74, 0x61, + 0x74, 0x75, 0x73, 0x12, 0x21, 0x0a, 0x0c, 0x72, 0x65, 0x61, 0x64, 0x79, 0x5f, 0x72, 0x65, 0x61, + 0x73, 0x6f, 0x6e, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x72, 0x65, 0x61, 0x64, 0x79, + 0x52, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x12, 0x23, 0x0a, 0x0d, 0x72, 0x65, 0x61, 0x64, 0x79, 0x5f, + 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x72, + 0x65, 0x61, 0x64, 0x79, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x73, + 0x70, 0x65, 0x63, 0x5f, 0x6a, 0x73, 0x6f, 0x6e, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, + 0x73, 0x70, 0x65, 0x63, 0x4a, 0x73, 0x6f, 0x6e, 0x12, 0x1f, 0x0a, 0x0b, 0x73, 0x74, 0x61, 0x74, + 0x75, 0x73, 0x5f, 0x6a, 0x73, 0x6f, 0x6e, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x73, + 0x74, 0x61, 0x74, 0x75, 0x73, 0x4a, 0x73, 0x6f, 0x6e, 0x22, 0x8d, 0x0c, 0x0a, 0x05, 0x45, 0x76, + 0x65, 0x6e, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x02, 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x6e, + 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, + 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x6c, 0x75, + 0x73, 0x74, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, + 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, 0x64, 0x12, 0x1b, 0x0a, 0x09, 0x6f, 0x77, 0x6e, 0x65, + 0x72, 0x5f, 0x75, 0x69, 0x64, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6f, 0x77, 0x6e, + 0x65, 0x72, 0x55, 0x69, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x5f, 0x6b, + 0x69, 0x6e, 0x64, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6f, 0x77, 0x6e, 0x65, 0x72, + 0x4b, 0x69, 0x6e, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x5f, 0x6e, 0x61, + 0x6d, 0x65, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x4e, + 0x61, 0x6d, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x61, 0x70, 0x69, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, + 0x6f, 0x6e, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x61, 0x70, 0x69, 0x56, 0x65, 0x72, + 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x29, 0x0a, 0x10, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, + 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, + 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, + 0x14, 0x0a, 0x05, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, + 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x18, + 0x0d, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x12, 0x18, 0x0a, + 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, + 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, + 0x0f, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x61, + 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x10, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x61, 0x63, 0x74, + 0x69, 0x6f, 0x6e, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, 0x11, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x30, 0x0a, 0x14, 0x69, + 0x6e, 0x76, 0x6f, 0x6c, 0x76, 0x65, 0x64, 0x5f, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x6b, + 0x69, 0x6e, 0x64, 0x18, 0x12, 0x20, 0x01, 0x28, 0x09, 0x52, 0x12, 0x69, 0x6e, 0x76, 0x6f, 0x6c, + 0x76, 0x65, 0x64, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x4b, 0x69, 0x6e, 0x64, 0x12, 0x30, 0x0a, + 0x14, 0x69, 0x6e, 0x76, 0x6f, 0x6c, 0x76, 0x65, 0x64, 0x5f, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, + 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x13, 0x20, 0x01, 0x28, 0x09, 0x52, 0x12, 0x69, 0x6e, 0x76, + 0x6f, 0x6c, 0x76, 0x65, 0x64, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x12, + 0x3a, 0x0a, 0x19, 0x69, 0x6e, 0x76, 0x6f, 0x6c, 0x76, 0x65, 0x64, 0x5f, 0x6f, 0x62, 0x6a, 0x65, + 0x63, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x14, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x17, 0x69, 0x6e, 0x76, 0x6f, 0x6c, 0x76, 0x65, 0x64, 0x4f, 0x62, 0x6a, 0x65, + 0x63, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x31, 0x0a, 0x14, 0x72, + 0x65, 0x70, 0x6f, 0x72, 0x74, 0x69, 0x6e, 0x67, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, + 0x6c, 0x65, 0x72, 0x18, 0x15, 0x20, 0x01, 0x28, 0x09, 0x52, 0x13, 0x72, 0x65, 0x70, 0x6f, 0x72, + 0x74, 0x69, 0x6e, 0x67, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x12, 0x2d, + 0x0a, 0x12, 0x72, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x69, 0x6e, 0x67, 0x5f, 0x69, 0x6e, 0x73, 0x74, + 0x61, 0x6e, 0x63, 0x65, 0x18, 0x16, 0x20, 0x01, 0x28, 0x09, 0x52, 0x11, 0x72, 0x65, 0x70, 0x6f, + 0x72, 0x74, 0x69, 0x6e, 0x67, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x12, 0x40, 0x0a, + 0x0b, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x17, 0x20, 0x03, + 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x76, 0x65, 0x6e, + 0x74, 0x2e, 0x41, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x45, 0x6e, 0x74, + 0x72, 0x79, 0x52, 0x0b, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, + 0x31, 0x0a, 0x06, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x18, 0x18, 0x20, 0x03, 0x28, 0x0b, 0x32, + 0x19, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x2e, 0x4c, + 0x61, 0x62, 0x65, 0x6c, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x06, 0x6c, 0x61, 0x62, 0x65, + 0x6c, 0x73, 0x12, 0x2e, 0x0a, 0x13, 0x69, 0x6e, 0x76, 0x6f, 0x6c, 0x76, 0x65, 0x64, 0x5f, 0x6f, + 0x62, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x75, 0x69, 0x64, 0x18, 0x19, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x11, 0x69, 0x6e, 0x76, 0x6f, 0x6c, 0x76, 0x65, 0x64, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x55, + 0x69, 0x64, 0x12, 0x55, 0x0a, 0x17, 0x64, 0x69, 0x73, 0x72, 0x75, 0x70, 0x74, 0x69, 0x6f, 0x6e, + 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x18, 0x1a, 0x20, + 0x01, 0x28, 0x0e, 0x32, 0x1d, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x69, 0x73, + 0x72, 0x75, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x65, 0x61, 0x73, + 0x6f, 0x6e, 0x52, 0x15, 0x64, 0x69, 0x73, 0x72, 0x75, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x6c, + 0x6f, 0x63, 0x6b, 0x52, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x12, 0x2f, 0x0a, 0x11, 0x62, 0x6c, 0x6f, + 0x63, 0x6b, 0x69, 0x6e, 0x67, 0x5f, 0x70, 0x64, 0x62, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x1b, + 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x0f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x69, 0x6e, 0x67, + 0x50, 0x64, 0x62, 0x4e, 0x61, 0x6d, 0x65, 0x88, 0x01, 0x01, 0x12, 0x39, 0x0a, 0x0a, 0x63, 0x72, + 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x29, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, + 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, + 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x63, 0x72, 0x65, 0x61, + 0x74, 0x65, 0x64, 0x41, 0x74, 0x12, 0x39, 0x0a, 0x0a, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, + 0x5f, 0x61, 0x74, 0x18, 0x2a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, + 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, + 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, + 0x12, 0x37, 0x0a, 0x09, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x73, 0x65, 0x65, 0x6e, 0x18, 0x2b, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, - 0x09, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x12, 0x37, 0x0a, 0x09, 0x6c, 0x61, - 0x73, 0x74, 0x5f, 0x73, 0x65, 0x65, 0x6e, 0x18, 0x2b, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, + 0x08, 0x6c, 0x61, 0x73, 0x74, 0x53, 0x65, 0x65, 0x6e, 0x12, 0x24, 0x0a, 0x0e, 0x63, 0x72, 0x65, + 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x5f, 0x61, 0x67, 0x65, 0x18, 0x2c, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x0c, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x41, 0x67, 0x65, 0x12, + 0x24, 0x0a, 0x0e, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x5f, 0x61, 0x67, + 0x65, 0x18, 0x2d, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, + 0x41, 0x74, 0x41, 0x67, 0x65, 0x12, 0x22, 0x0a, 0x0d, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x73, 0x65, + 0x65, 0x6e, 0x5f, 0x61, 0x67, 0x65, 0x18, 0x2e, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x6c, 0x61, + 0x73, 0x74, 0x53, 0x65, 0x65, 0x6e, 0x41, 0x67, 0x65, 0x12, 0x39, 0x0a, 0x0a, 0x64, 0x65, 0x6c, + 0x65, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x2f, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, - 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x08, 0x6c, 0x61, 0x73, 0x74, 0x53, - 0x65, 0x65, 0x6e, 0x12, 0x24, 0x0a, 0x0e, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, - 0x74, 0x5f, 0x61, 0x67, 0x65, 0x18, 0x2c, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x63, 0x72, 0x65, - 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x41, 0x67, 0x65, 0x12, 0x24, 0x0a, 0x0e, 0x75, 0x70, 0x64, - 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x5f, 0x61, 0x67, 0x65, 0x18, 0x2d, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x0c, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x41, 0x67, 0x65, 0x12, - 0x22, 0x0a, 0x0d, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x73, 0x65, 0x65, 0x6e, 0x5f, 0x61, 0x67, 0x65, - 0x18, 0x2e, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x6c, 0x61, 0x73, 0x74, 0x53, 0x65, 0x65, 0x6e, - 0x41, 0x67, 0x65, 0x12, 0x39, 0x0a, 0x0a, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x5f, 0x61, - 0x74, 0x18, 0x2f, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, - 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, - 0x61, 0x6d, 0x70, 0x52, 0x09, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x41, 0x74, 0x1a, 0x3e, - 0x0a, 0x10, 0x41, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x45, 0x6e, 0x74, + 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x64, 0x65, 0x6c, 0x65, 0x74, + 0x65, 0x64, 0x41, 0x74, 0x12, 0x2d, 0x0a, 0x10, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x65, 0x64, 0x5f, + 0x70, 0x6f, 0x64, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x30, 0x20, 0x01, 0x28, 0x09, 0x48, 0x01, + 0x52, 0x0e, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x65, 0x64, 0x50, 0x6f, 0x64, 0x4e, 0x61, 0x6d, 0x65, + 0x88, 0x01, 0x01, 0x1a, 0x3e, 0x0a, 0x10, 0x41, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, + 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, + 0x02, 0x38, 0x01, 0x1a, 0x39, 0x0a, 0x0b, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x39, - 0x0a, 0x0b, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, - 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, - 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, - 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x42, 0x14, 0x0a, 0x12, 0x5f, 0x62, 0x6c, - 0x6f, 0x63, 0x6b, 0x69, 0x6e, 0x67, 0x5f, 0x70, 0x64, 0x62, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x22, - 0xc9, 0x01, 0x0a, 0x12, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x44, 0x61, 0x74, 0x61, 0x70, 0x6f, 0x69, - 0x6e, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, - 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, - 0x73, 0x61, 0x67, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x5f, 0x75, 0x69, - 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x55, 0x69, - 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x5f, 0x6b, 0x69, 0x6e, 0x64, 0x18, - 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x4b, 0x69, 0x6e, 0x64, - 0x12, 0x1d, 0x0a, 0x0a, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x05, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x12, - 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, - 0x79, 0x70, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x18, 0x07, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x06, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x22, 0xe4, 0x01, 0x0a, 0x0e, - 0x45, 0x76, 0x65, 0x6e, 0x74, 0x44, 0x61, 0x74, 0x61, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x19, - 0x0a, 0x08, 0x75, 0x74, 0x63, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x07, 0x75, 0x74, 0x63, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x63, 0x6f, 0x75, - 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x12, - 0x32, 0x0a, 0x06, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, - 0x1a, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x44, 0x61, - 0x74, 0x61, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x06, 0x65, 0x76, 0x65, - 0x6e, 0x74, 0x73, 0x12, 0x21, 0x0a, 0x0c, 0x6e, 0x6f, 0x72, 0x6d, 0x61, 0x6c, 0x5f, 0x63, 0x6f, - 0x75, 0x6e, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0b, 0x6e, 0x6f, 0x72, 0x6d, 0x61, - 0x6c, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x23, 0x0a, 0x0d, 0x77, 0x61, 0x72, 0x6e, 0x69, 0x6e, - 0x67, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0c, 0x77, - 0x61, 0x72, 0x6e, 0x69, 0x6e, 0x67, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x25, 0x0a, 0x0e, 0x63, - 0x72, 0x69, 0x74, 0x69, 0x63, 0x61, 0x6c, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x06, 0x20, - 0x01, 0x28, 0x05, 0x52, 0x0d, 0x63, 0x72, 0x69, 0x74, 0x69, 0x63, 0x61, 0x6c, 0x43, 0x6f, 0x75, - 0x6e, 0x74, 0x22, 0x5c, 0x0a, 0x0e, 0x44, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x43, - 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x63, 0x6f, - 0x75, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x63, 0x6f, 0x75, 0x6e, 0x74, - 0x12, 0x1e, 0x0a, 0x0a, 0x70, 0x65, 0x72, 0x63, 0x65, 0x6e, 0x74, 0x61, 0x67, 0x65, 0x18, 0x03, - 0x20, 0x01, 0x28, 0x01, 0x52, 0x0a, 0x70, 0x65, 0x72, 0x63, 0x65, 0x6e, 0x74, 0x61, 0x67, 0x65, - 0x22, 0xe9, 0x02, 0x0a, 0x0a, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x12, - 0x16, 0x0a, 0x06, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x06, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x12, 0x1c, 0x0a, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, - 0x70, 0x61, 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6e, 0x61, 0x6d, 0x65, - 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x77, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, - 0x64, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x77, 0x6f, - 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x77, 0x6f, - 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x5f, 0x6b, 0x69, 0x6e, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x0c, 0x77, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x4b, 0x69, 0x6e, 0x64, 0x12, - 0x12, 0x0a, 0x04, 0x6e, 0x6f, 0x64, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, - 0x6f, 0x64, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x06, 0x20, 0x01, - 0x28, 0x05, 0x52, 0x05, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x29, 0x0a, 0x10, 0x61, 0x66, 0x66, - 0x65, 0x63, 0x74, 0x65, 0x64, 0x5f, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x18, 0x07, 0x20, - 0x01, 0x28, 0x05, 0x52, 0x0f, 0x61, 0x66, 0x66, 0x65, 0x63, 0x74, 0x65, 0x64, 0x4f, 0x62, 0x6a, - 0x65, 0x63, 0x74, 0x73, 0x12, 0x39, 0x0a, 0x0a, 0x66, 0x69, 0x72, 0x73, 0x74, 0x5f, 0x73, 0x65, - 0x65, 0x6e, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, - 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, - 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x66, 0x69, 0x72, 0x73, 0x74, 0x53, 0x65, 0x65, 0x6e, 0x12, - 0x37, 0x0a, 0x09, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x73, 0x65, 0x65, 0x6e, 0x18, 0x09, 0x20, 0x01, + 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x42, 0x14, + 0x0a, 0x12, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x69, 0x6e, 0x67, 0x5f, 0x70, 0x64, 0x62, 0x5f, + 0x6e, 0x61, 0x6d, 0x65, 0x42, 0x13, 0x0a, 0x11, 0x5f, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x65, 0x64, + 0x5f, 0x70, 0x6f, 0x64, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0xc9, 0x01, 0x0a, 0x12, 0x45, 0x76, + 0x65, 0x6e, 0x74, 0x44, 0x61, 0x74, 0x61, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x49, 0x6e, 0x66, 0x6f, + 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, + 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x1b, + 0x0a, 0x09, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x5f, 0x75, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x08, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x55, 0x69, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x6f, + 0x77, 0x6e, 0x65, 0x72, 0x5f, 0x6b, 0x69, 0x6e, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x09, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x4b, 0x69, 0x6e, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x6f, 0x77, + 0x6e, 0x65, 0x72, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, + 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, + 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x16, 0x0a, + 0x06, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x72, + 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x22, 0xe4, 0x01, 0x0a, 0x0e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x44, + 0x61, 0x74, 0x61, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x19, 0x0a, 0x08, 0x75, 0x74, 0x63, 0x5f, + 0x74, 0x69, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x75, 0x74, 0x63, 0x54, + 0x69, 0x6d, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x05, 0x52, 0x05, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x32, 0x0a, 0x06, 0x65, 0x76, 0x65, + 0x6e, 0x74, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x61, 0x70, 0x69, 0x2e, + 0x76, 0x31, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x44, 0x61, 0x74, 0x61, 0x70, 0x6f, 0x69, 0x6e, + 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x06, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x21, 0x0a, + 0x0c, 0x6e, 0x6f, 0x72, 0x6d, 0x61, 0x6c, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x04, 0x20, + 0x01, 0x28, 0x05, 0x52, 0x0b, 0x6e, 0x6f, 0x72, 0x6d, 0x61, 0x6c, 0x43, 0x6f, 0x75, 0x6e, 0x74, + 0x12, 0x23, 0x0a, 0x0d, 0x77, 0x61, 0x72, 0x6e, 0x69, 0x6e, 0x67, 0x5f, 0x63, 0x6f, 0x75, 0x6e, + 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0c, 0x77, 0x61, 0x72, 0x6e, 0x69, 0x6e, 0x67, + 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x25, 0x0a, 0x0e, 0x63, 0x72, 0x69, 0x74, 0x69, 0x63, 0x61, + 0x6c, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0d, 0x63, + 0x72, 0x69, 0x74, 0x69, 0x63, 0x61, 0x6c, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x22, 0x5c, 0x0a, 0x0e, + 0x44, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x14, + 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, + 0x61, 0x6c, 0x75, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x05, 0x52, 0x05, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x1e, 0x0a, 0x0a, 0x70, 0x65, + 0x72, 0x63, 0x65, 0x6e, 0x74, 0x61, 0x67, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x01, 0x52, 0x0a, + 0x70, 0x65, 0x72, 0x63, 0x65, 0x6e, 0x74, 0x61, 0x67, 0x65, 0x22, 0xe9, 0x02, 0x0a, 0x0a, 0x45, + 0x76, 0x65, 0x6e, 0x74, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x12, 0x16, 0x0a, 0x06, 0x72, 0x65, 0x61, + 0x73, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x72, 0x65, 0x61, 0x73, 0x6f, + 0x6e, 0x12, 0x1c, 0x0a, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, + 0x23, 0x0a, 0x0d, 0x77, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x5f, 0x6e, 0x61, 0x6d, 0x65, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x77, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, + 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x77, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, + 0x5f, 0x6b, 0x69, 0x6e, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x77, 0x6f, 0x72, + 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x4b, 0x69, 0x6e, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x6f, 0x64, + 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x6f, 0x64, 0x65, 0x12, 0x14, 0x0a, + 0x05, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x63, 0x6f, + 0x75, 0x6e, 0x74, 0x12, 0x29, 0x0a, 0x10, 0x61, 0x66, 0x66, 0x65, 0x63, 0x74, 0x65, 0x64, 0x5f, + 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x18, 0x07, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0f, 0x61, + 0x66, 0x66, 0x65, 0x63, 0x74, 0x65, 0x64, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x12, 0x39, + 0x0a, 0x0a, 0x66, 0x69, 0x72, 0x73, 0x74, 0x5f, 0x73, 0x65, 0x65, 0x6e, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x08, - 0x6c, 0x61, 0x73, 0x74, 0x53, 0x65, 0x65, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, - 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x2a, 0xf3, 0x0b, 0x0a, - 0x0d, 0x4b, 0x38, 0x73, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x4b, 0x69, 0x6e, 0x64, 0x12, 0x1f, - 0x0a, 0x1b, 0x4b, 0x38, 0x53, 0x5f, 0x4f, 0x42, 0x4a, 0x45, 0x43, 0x54, 0x5f, 0x4b, 0x49, 0x4e, - 0x44, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, - 0x1e, 0x0a, 0x1a, 0x4b, 0x38, 0x53, 0x5f, 0x4f, 0x42, 0x4a, 0x45, 0x43, 0x54, 0x5f, 0x4b, 0x49, - 0x4e, 0x44, 0x5f, 0x44, 0x45, 0x50, 0x4c, 0x4f, 0x59, 0x4d, 0x45, 0x4e, 0x54, 0x10, 0x01, 0x12, - 0x1f, 0x0a, 0x1b, 0x4b, 0x38, 0x53, 0x5f, 0x4f, 0x42, 0x4a, 0x45, 0x43, 0x54, 0x5f, 0x4b, 0x49, - 0x4e, 0x44, 0x5f, 0x52, 0x45, 0x50, 0x4c, 0x49, 0x43, 0x41, 0x5f, 0x53, 0x45, 0x54, 0x10, 0x02, - 0x12, 0x20, 0x0a, 0x1c, 0x4b, 0x38, 0x53, 0x5f, 0x4f, 0x42, 0x4a, 0x45, 0x43, 0x54, 0x5f, 0x4b, - 0x49, 0x4e, 0x44, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x45, 0x46, 0x55, 0x4c, 0x5f, 0x53, 0x45, 0x54, - 0x10, 0x03, 0x12, 0x1e, 0x0a, 0x1a, 0x4b, 0x38, 0x53, 0x5f, 0x4f, 0x42, 0x4a, 0x45, 0x43, 0x54, - 0x5f, 0x4b, 0x49, 0x4e, 0x44, 0x5f, 0x44, 0x41, 0x45, 0x4d, 0x4f, 0x4e, 0x5f, 0x53, 0x45, 0x54, - 0x10, 0x04, 0x12, 0x17, 0x0a, 0x13, 0x4b, 0x38, 0x53, 0x5f, 0x4f, 0x42, 0x4a, 0x45, 0x43, 0x54, - 0x5f, 0x4b, 0x49, 0x4e, 0x44, 0x5f, 0x4a, 0x4f, 0x42, 0x10, 0x05, 0x12, 0x1c, 0x0a, 0x18, 0x4b, - 0x38, 0x53, 0x5f, 0x4f, 0x42, 0x4a, 0x45, 0x43, 0x54, 0x5f, 0x4b, 0x49, 0x4e, 0x44, 0x5f, 0x43, - 0x52, 0x4f, 0x4e, 0x5f, 0x4a, 0x4f, 0x42, 0x10, 0x06, 0x12, 0x2a, 0x0a, 0x26, 0x4b, 0x38, 0x53, + 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, + 0x66, 0x69, 0x72, 0x73, 0x74, 0x53, 0x65, 0x65, 0x6e, 0x12, 0x37, 0x0a, 0x09, 0x6c, 0x61, 0x73, + 0x74, 0x5f, 0x73, 0x65, 0x65, 0x6e, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, + 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, + 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x08, 0x6c, 0x61, 0x73, 0x74, 0x53, 0x65, + 0x65, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x2a, 0xdb, 0x13, 0x0a, 0x0d, 0x4b, 0x38, 0x73, 0x4f, 0x62, + 0x6a, 0x65, 0x63, 0x74, 0x4b, 0x69, 0x6e, 0x64, 0x12, 0x1f, 0x0a, 0x1b, 0x4b, 0x38, 0x53, 0x5f, + 0x4f, 0x42, 0x4a, 0x45, 0x43, 0x54, 0x5f, 0x4b, 0x49, 0x4e, 0x44, 0x5f, 0x55, 0x4e, 0x53, 0x50, + 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x1e, 0x0a, 0x1a, 0x4b, 0x38, 0x53, + 0x5f, 0x4f, 0x42, 0x4a, 0x45, 0x43, 0x54, 0x5f, 0x4b, 0x49, 0x4e, 0x44, 0x5f, 0x44, 0x45, 0x50, + 0x4c, 0x4f, 0x59, 0x4d, 0x45, 0x4e, 0x54, 0x10, 0x01, 0x12, 0x1f, 0x0a, 0x1b, 0x4b, 0x38, 0x53, 0x5f, 0x4f, 0x42, 0x4a, 0x45, 0x43, 0x54, 0x5f, 0x4b, 0x49, 0x4e, 0x44, 0x5f, 0x52, 0x45, 0x50, - 0x4c, 0x49, 0x43, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x43, 0x4f, 0x4e, 0x54, 0x52, 0x4f, 0x4c, - 0x4c, 0x45, 0x52, 0x10, 0x07, 0x12, 0x17, 0x0a, 0x13, 0x4b, 0x38, 0x53, 0x5f, 0x4f, 0x42, 0x4a, - 0x45, 0x43, 0x54, 0x5f, 0x4b, 0x49, 0x4e, 0x44, 0x5f, 0x50, 0x4f, 0x44, 0x10, 0x08, 0x12, 0x1d, + 0x4c, 0x49, 0x43, 0x41, 0x5f, 0x53, 0x45, 0x54, 0x10, 0x02, 0x12, 0x20, 0x0a, 0x1c, 0x4b, 0x38, + 0x53, 0x5f, 0x4f, 0x42, 0x4a, 0x45, 0x43, 0x54, 0x5f, 0x4b, 0x49, 0x4e, 0x44, 0x5f, 0x53, 0x54, + 0x41, 0x54, 0x45, 0x46, 0x55, 0x4c, 0x5f, 0x53, 0x45, 0x54, 0x10, 0x03, 0x12, 0x1e, 0x0a, 0x1a, + 0x4b, 0x38, 0x53, 0x5f, 0x4f, 0x42, 0x4a, 0x45, 0x43, 0x54, 0x5f, 0x4b, 0x49, 0x4e, 0x44, 0x5f, + 0x44, 0x41, 0x45, 0x4d, 0x4f, 0x4e, 0x5f, 0x53, 0x45, 0x54, 0x10, 0x04, 0x12, 0x17, 0x0a, 0x13, + 0x4b, 0x38, 0x53, 0x5f, 0x4f, 0x42, 0x4a, 0x45, 0x43, 0x54, 0x5f, 0x4b, 0x49, 0x4e, 0x44, 0x5f, + 0x4a, 0x4f, 0x42, 0x10, 0x05, 0x12, 0x1c, 0x0a, 0x18, 0x4b, 0x38, 0x53, 0x5f, 0x4f, 0x42, 0x4a, + 0x45, 0x43, 0x54, 0x5f, 0x4b, 0x49, 0x4e, 0x44, 0x5f, 0x43, 0x52, 0x4f, 0x4e, 0x5f, 0x4a, 0x4f, + 0x42, 0x10, 0x06, 0x12, 0x2a, 0x0a, 0x26, 0x4b, 0x38, 0x53, 0x5f, 0x4f, 0x42, 0x4a, 0x45, 0x43, + 0x54, 0x5f, 0x4b, 0x49, 0x4e, 0x44, 0x5f, 0x52, 0x45, 0x50, 0x4c, 0x49, 0x43, 0x41, 0x54, 0x49, + 0x4f, 0x4e, 0x5f, 0x43, 0x4f, 0x4e, 0x54, 0x52, 0x4f, 0x4c, 0x4c, 0x45, 0x52, 0x10, 0x07, 0x12, + 0x17, 0x0a, 0x13, 0x4b, 0x38, 0x53, 0x5f, 0x4f, 0x42, 0x4a, 0x45, 0x43, 0x54, 0x5f, 0x4b, 0x49, + 0x4e, 0x44, 0x5f, 0x50, 0x4f, 0x44, 0x10, 0x08, 0x12, 0x1d, 0x0a, 0x19, 0x4b, 0x38, 0x53, 0x5f, + 0x4f, 0x42, 0x4a, 0x45, 0x43, 0x54, 0x5f, 0x4b, 0x49, 0x4e, 0x44, 0x5f, 0x4e, 0x41, 0x4d, 0x45, + 0x53, 0x50, 0x41, 0x43, 0x45, 0x10, 0x09, 0x12, 0x18, 0x0a, 0x14, 0x4b, 0x38, 0x53, 0x5f, 0x4f, + 0x42, 0x4a, 0x45, 0x43, 0x54, 0x5f, 0x4b, 0x49, 0x4e, 0x44, 0x5f, 0x4e, 0x4f, 0x44, 0x45, 0x10, + 0x0a, 0x12, 0x1b, 0x0a, 0x17, 0x4b, 0x38, 0x53, 0x5f, 0x4f, 0x42, 0x4a, 0x45, 0x43, 0x54, 0x5f, + 0x4b, 0x49, 0x4e, 0x44, 0x5f, 0x49, 0x4e, 0x47, 0x52, 0x45, 0x53, 0x53, 0x10, 0x0b, 0x12, 0x1d, 0x0a, 0x19, 0x4b, 0x38, 0x53, 0x5f, 0x4f, 0x42, 0x4a, 0x45, 0x43, 0x54, 0x5f, 0x4b, 0x49, 0x4e, - 0x44, 0x5f, 0x4e, 0x41, 0x4d, 0x45, 0x53, 0x50, 0x41, 0x43, 0x45, 0x10, 0x09, 0x12, 0x18, 0x0a, - 0x14, 0x4b, 0x38, 0x53, 0x5f, 0x4f, 0x42, 0x4a, 0x45, 0x43, 0x54, 0x5f, 0x4b, 0x49, 0x4e, 0x44, - 0x5f, 0x4e, 0x4f, 0x44, 0x45, 0x10, 0x0a, 0x12, 0x1b, 0x0a, 0x17, 0x4b, 0x38, 0x53, 0x5f, 0x4f, - 0x42, 0x4a, 0x45, 0x43, 0x54, 0x5f, 0x4b, 0x49, 0x4e, 0x44, 0x5f, 0x49, 0x4e, 0x47, 0x52, 0x45, - 0x53, 0x53, 0x10, 0x0b, 0x12, 0x1d, 0x0a, 0x19, 0x4b, 0x38, 0x53, 0x5f, 0x4f, 0x42, 0x4a, 0x45, - 0x43, 0x54, 0x5f, 0x4b, 0x49, 0x4e, 0x44, 0x5f, 0x43, 0x4f, 0x4e, 0x54, 0x41, 0x49, 0x4e, 0x45, - 0x52, 0x10, 0x0c, 0x12, 0x1b, 0x0a, 0x17, 0x4b, 0x38, 0x53, 0x5f, 0x4f, 0x42, 0x4a, 0x45, 0x43, - 0x54, 0x5f, 0x4b, 0x49, 0x4e, 0x44, 0x5f, 0x53, 0x45, 0x52, 0x56, 0x49, 0x43, 0x45, 0x10, 0x0d, - 0x12, 0x1e, 0x0a, 0x1a, 0x4b, 0x38, 0x53, 0x5f, 0x4f, 0x42, 0x4a, 0x45, 0x43, 0x54, 0x5f, 0x4b, - 0x49, 0x4e, 0x44, 0x5f, 0x43, 0x4f, 0x4e, 0x46, 0x49, 0x47, 0x5f, 0x4d, 0x41, 0x50, 0x10, 0x0e, + 0x44, 0x5f, 0x43, 0x4f, 0x4e, 0x54, 0x41, 0x49, 0x4e, 0x45, 0x52, 0x10, 0x0c, 0x12, 0x1b, 0x0a, + 0x17, 0x4b, 0x38, 0x53, 0x5f, 0x4f, 0x42, 0x4a, 0x45, 0x43, 0x54, 0x5f, 0x4b, 0x49, 0x4e, 0x44, + 0x5f, 0x53, 0x45, 0x52, 0x56, 0x49, 0x43, 0x45, 0x10, 0x0d, 0x12, 0x1e, 0x0a, 0x1a, 0x4b, 0x38, + 0x53, 0x5f, 0x4f, 0x42, 0x4a, 0x45, 0x43, 0x54, 0x5f, 0x4b, 0x49, 0x4e, 0x44, 0x5f, 0x43, 0x4f, + 0x4e, 0x46, 0x49, 0x47, 0x5f, 0x4d, 0x41, 0x50, 0x10, 0x0e, 0x12, 0x25, 0x0a, 0x21, 0x4b, 0x38, + 0x53, 0x5f, 0x4f, 0x42, 0x4a, 0x45, 0x43, 0x54, 0x5f, 0x4b, 0x49, 0x4e, 0x44, 0x5f, 0x50, 0x45, + 0x52, 0x53, 0x49, 0x53, 0x54, 0x45, 0x4e, 0x54, 0x5f, 0x56, 0x4f, 0x4c, 0x55, 0x4d, 0x45, 0x10, + 0x0f, 0x12, 0x2b, 0x0a, 0x27, 0x4b, 0x38, 0x53, 0x5f, 0x4f, 0x42, 0x4a, 0x45, 0x43, 0x54, 0x5f, + 0x4b, 0x49, 0x4e, 0x44, 0x5f, 0x50, 0x45, 0x52, 0x53, 0x49, 0x53, 0x54, 0x45, 0x4e, 0x54, 0x5f, + 0x56, 0x4f, 0x4c, 0x55, 0x4d, 0x45, 0x5f, 0x43, 0x4c, 0x41, 0x49, 0x4d, 0x10, 0x10, 0x12, 0x21, + 0x0a, 0x1d, 0x4b, 0x38, 0x53, 0x5f, 0x4f, 0x42, 0x4a, 0x45, 0x43, 0x54, 0x5f, 0x4b, 0x49, 0x4e, + 0x44, 0x5f, 0x53, 0x54, 0x4f, 0x52, 0x41, 0x47, 0x45, 0x5f, 0x43, 0x4c, 0x41, 0x53, 0x53, 0x10, + 0x11, 0x12, 0x20, 0x0a, 0x1c, 0x4b, 0x38, 0x53, 0x5f, 0x4f, 0x42, 0x4a, 0x45, 0x43, 0x54, 0x5f, + 0x4b, 0x49, 0x4e, 0x44, 0x5f, 0x41, 0x52, 0x47, 0x4f, 0x5f, 0x52, 0x4f, 0x4c, 0x4c, 0x4f, 0x55, + 0x54, 0x10, 0x12, 0x12, 0x2d, 0x0a, 0x29, 0x4b, 0x38, 0x53, 0x5f, 0x4f, 0x42, 0x4a, 0x45, 0x43, + 0x54, 0x5f, 0x4b, 0x49, 0x4e, 0x44, 0x5f, 0x48, 0x4f, 0x52, 0x49, 0x5a, 0x4f, 0x4e, 0x54, 0x41, + 0x4c, 0x5f, 0x50, 0x4f, 0x44, 0x5f, 0x41, 0x55, 0x54, 0x4f, 0x53, 0x43, 0x41, 0x4c, 0x45, 0x52, + 0x10, 0x13, 0x12, 0x2b, 0x0a, 0x27, 0x4b, 0x38, 0x53, 0x5f, 0x4f, 0x42, 0x4a, 0x45, 0x43, 0x54, + 0x5f, 0x4b, 0x49, 0x4e, 0x44, 0x5f, 0x56, 0x45, 0x52, 0x54, 0x49, 0x43, 0x41, 0x4c, 0x5f, 0x50, + 0x4f, 0x44, 0x5f, 0x41, 0x55, 0x54, 0x4f, 0x53, 0x43, 0x41, 0x4c, 0x45, 0x52, 0x10, 0x14, 0x12, + 0x1f, 0x0a, 0x1b, 0x4b, 0x38, 0x53, 0x5f, 0x4f, 0x42, 0x4a, 0x45, 0x43, 0x54, 0x5f, 0x4b, 0x49, + 0x4e, 0x44, 0x5f, 0x4c, 0x49, 0x4d, 0x49, 0x54, 0x5f, 0x52, 0x41, 0x4e, 0x47, 0x45, 0x10, 0x15, + 0x12, 0x23, 0x0a, 0x1f, 0x4b, 0x38, 0x53, 0x5f, 0x4f, 0x42, 0x4a, 0x45, 0x43, 0x54, 0x5f, 0x4b, + 0x49, 0x4e, 0x44, 0x5f, 0x53, 0x45, 0x52, 0x56, 0x49, 0x43, 0x45, 0x5f, 0x41, 0x43, 0x43, 0x4f, + 0x55, 0x4e, 0x54, 0x10, 0x16, 0x12, 0x18, 0x0a, 0x14, 0x4b, 0x38, 0x53, 0x5f, 0x4f, 0x42, 0x4a, + 0x45, 0x43, 0x54, 0x5f, 0x4b, 0x49, 0x4e, 0x44, 0x5f, 0x52, 0x4f, 0x4c, 0x45, 0x10, 0x17, 0x12, + 0x20, 0x0a, 0x1c, 0x4b, 0x38, 0x53, 0x5f, 0x4f, 0x42, 0x4a, 0x45, 0x43, 0x54, 0x5f, 0x4b, 0x49, + 0x4e, 0x44, 0x5f, 0x52, 0x4f, 0x4c, 0x45, 0x5f, 0x42, 0x49, 0x4e, 0x44, 0x49, 0x4e, 0x47, 0x10, + 0x18, 0x12, 0x26, 0x0a, 0x22, 0x4b, 0x38, 0x53, 0x5f, 0x4f, 0x42, 0x4a, 0x45, 0x43, 0x54, 0x5f, + 0x4b, 0x49, 0x4e, 0x44, 0x5f, 0x4b, 0x45, 0x44, 0x41, 0x5f, 0x53, 0x43, 0x41, 0x4c, 0x45, 0x44, + 0x5f, 0x4f, 0x42, 0x4a, 0x45, 0x43, 0x54, 0x10, 0x19, 0x12, 0x26, 0x0a, 0x22, 0x4b, 0x38, 0x53, + 0x5f, 0x4f, 0x42, 0x4a, 0x45, 0x43, 0x54, 0x5f, 0x4b, 0x49, 0x4e, 0x44, 0x5f, 0x4b, 0x41, 0x52, + 0x50, 0x45, 0x4e, 0x54, 0x45, 0x52, 0x5f, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x10, + 0x1a, 0x12, 0x29, 0x0a, 0x25, 0x4b, 0x38, 0x53, 0x5f, 0x4f, 0x42, 0x4a, 0x45, 0x43, 0x54, 0x5f, + 0x4b, 0x49, 0x4e, 0x44, 0x5f, 0x50, 0x4f, 0x44, 0x5f, 0x44, 0x49, 0x53, 0x52, 0x55, 0x50, 0x54, + 0x49, 0x4f, 0x4e, 0x5f, 0x42, 0x55, 0x44, 0x47, 0x45, 0x54, 0x10, 0x1b, 0x12, 0x22, 0x0a, 0x1e, + 0x4b, 0x38, 0x53, 0x5f, 0x4f, 0x42, 0x4a, 0x45, 0x43, 0x54, 0x5f, 0x4b, 0x49, 0x4e, 0x44, 0x5f, + 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, 0x51, 0x55, 0x4f, 0x54, 0x41, 0x10, 0x1c, 0x12, 0x25, 0x0a, 0x21, 0x4b, 0x38, 0x53, 0x5f, 0x4f, 0x42, 0x4a, 0x45, 0x43, 0x54, 0x5f, 0x4b, - 0x49, 0x4e, 0x44, 0x5f, 0x50, 0x45, 0x52, 0x53, 0x49, 0x53, 0x54, 0x45, 0x4e, 0x54, 0x5f, 0x56, - 0x4f, 0x4c, 0x55, 0x4d, 0x45, 0x10, 0x0f, 0x12, 0x2b, 0x0a, 0x27, 0x4b, 0x38, 0x53, 0x5f, 0x4f, - 0x42, 0x4a, 0x45, 0x43, 0x54, 0x5f, 0x4b, 0x49, 0x4e, 0x44, 0x5f, 0x50, 0x45, 0x52, 0x53, 0x49, - 0x53, 0x54, 0x45, 0x4e, 0x54, 0x5f, 0x56, 0x4f, 0x4c, 0x55, 0x4d, 0x45, 0x5f, 0x43, 0x4c, 0x41, - 0x49, 0x4d, 0x10, 0x10, 0x12, 0x21, 0x0a, 0x1d, 0x4b, 0x38, 0x53, 0x5f, 0x4f, 0x42, 0x4a, 0x45, - 0x43, 0x54, 0x5f, 0x4b, 0x49, 0x4e, 0x44, 0x5f, 0x53, 0x54, 0x4f, 0x52, 0x41, 0x47, 0x45, 0x5f, - 0x43, 0x4c, 0x41, 0x53, 0x53, 0x10, 0x11, 0x12, 0x20, 0x0a, 0x1c, 0x4b, 0x38, 0x53, 0x5f, 0x4f, - 0x42, 0x4a, 0x45, 0x43, 0x54, 0x5f, 0x4b, 0x49, 0x4e, 0x44, 0x5f, 0x41, 0x52, 0x47, 0x4f, 0x5f, - 0x52, 0x4f, 0x4c, 0x4c, 0x4f, 0x55, 0x54, 0x10, 0x12, 0x12, 0x2d, 0x0a, 0x29, 0x4b, 0x38, 0x53, - 0x5f, 0x4f, 0x42, 0x4a, 0x45, 0x43, 0x54, 0x5f, 0x4b, 0x49, 0x4e, 0x44, 0x5f, 0x48, 0x4f, 0x52, - 0x49, 0x5a, 0x4f, 0x4e, 0x54, 0x41, 0x4c, 0x5f, 0x50, 0x4f, 0x44, 0x5f, 0x41, 0x55, 0x54, 0x4f, - 0x53, 0x43, 0x41, 0x4c, 0x45, 0x52, 0x10, 0x13, 0x12, 0x2b, 0x0a, 0x27, 0x4b, 0x38, 0x53, 0x5f, - 0x4f, 0x42, 0x4a, 0x45, 0x43, 0x54, 0x5f, 0x4b, 0x49, 0x4e, 0x44, 0x5f, 0x56, 0x45, 0x52, 0x54, - 0x49, 0x43, 0x41, 0x4c, 0x5f, 0x50, 0x4f, 0x44, 0x5f, 0x41, 0x55, 0x54, 0x4f, 0x53, 0x43, 0x41, - 0x4c, 0x45, 0x52, 0x10, 0x14, 0x12, 0x1f, 0x0a, 0x1b, 0x4b, 0x38, 0x53, 0x5f, 0x4f, 0x42, 0x4a, - 0x45, 0x43, 0x54, 0x5f, 0x4b, 0x49, 0x4e, 0x44, 0x5f, 0x4c, 0x49, 0x4d, 0x49, 0x54, 0x5f, 0x52, - 0x41, 0x4e, 0x47, 0x45, 0x10, 0x15, 0x12, 0x23, 0x0a, 0x1f, 0x4b, 0x38, 0x53, 0x5f, 0x4f, 0x42, - 0x4a, 0x45, 0x43, 0x54, 0x5f, 0x4b, 0x49, 0x4e, 0x44, 0x5f, 0x53, 0x45, 0x52, 0x56, 0x49, 0x43, - 0x45, 0x5f, 0x41, 0x43, 0x43, 0x4f, 0x55, 0x4e, 0x54, 0x10, 0x16, 0x12, 0x18, 0x0a, 0x14, 0x4b, - 0x38, 0x53, 0x5f, 0x4f, 0x42, 0x4a, 0x45, 0x43, 0x54, 0x5f, 0x4b, 0x49, 0x4e, 0x44, 0x5f, 0x52, - 0x4f, 0x4c, 0x45, 0x10, 0x17, 0x12, 0x20, 0x0a, 0x1c, 0x4b, 0x38, 0x53, 0x5f, 0x4f, 0x42, 0x4a, - 0x45, 0x43, 0x54, 0x5f, 0x4b, 0x49, 0x4e, 0x44, 0x5f, 0x52, 0x4f, 0x4c, 0x45, 0x5f, 0x42, 0x49, - 0x4e, 0x44, 0x49, 0x4e, 0x47, 0x10, 0x18, 0x12, 0x26, 0x0a, 0x22, 0x4b, 0x38, 0x53, 0x5f, 0x4f, - 0x42, 0x4a, 0x45, 0x43, 0x54, 0x5f, 0x4b, 0x49, 0x4e, 0x44, 0x5f, 0x4b, 0x45, 0x44, 0x41, 0x5f, - 0x53, 0x43, 0x41, 0x4c, 0x45, 0x44, 0x5f, 0x4f, 0x42, 0x4a, 0x45, 0x43, 0x54, 0x10, 0x19, 0x12, - 0x26, 0x0a, 0x22, 0x4b, 0x38, 0x53, 0x5f, 0x4f, 0x42, 0x4a, 0x45, 0x43, 0x54, 0x5f, 0x4b, 0x49, - 0x4e, 0x44, 0x5f, 0x4b, 0x41, 0x52, 0x50, 0x45, 0x4e, 0x54, 0x45, 0x52, 0x5f, 0x52, 0x45, 0x53, - 0x4f, 0x55, 0x52, 0x43, 0x45, 0x10, 0x1a, 0x12, 0x29, 0x0a, 0x25, 0x4b, 0x38, 0x53, 0x5f, 0x4f, - 0x42, 0x4a, 0x45, 0x43, 0x54, 0x5f, 0x4b, 0x49, 0x4e, 0x44, 0x5f, 0x50, 0x4f, 0x44, 0x5f, 0x44, - 0x49, 0x53, 0x52, 0x55, 0x50, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x42, 0x55, 0x44, 0x47, 0x45, 0x54, - 0x10, 0x1b, 0x12, 0x22, 0x0a, 0x1e, 0x4b, 0x38, 0x53, 0x5f, 0x4f, 0x42, 0x4a, 0x45, 0x43, 0x54, - 0x5f, 0x4b, 0x49, 0x4e, 0x44, 0x5f, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, 0x51, - 0x55, 0x4f, 0x54, 0x41, 0x10, 0x1c, 0x12, 0x25, 0x0a, 0x21, 0x4b, 0x38, 0x53, 0x5f, 0x4f, 0x42, - 0x4a, 0x45, 0x43, 0x54, 0x5f, 0x4b, 0x49, 0x4e, 0x44, 0x5f, 0x4b, 0x55, 0x42, 0x45, 0x46, 0x4c, - 0x4f, 0x57, 0x5f, 0x4e, 0x4f, 0x54, 0x45, 0x42, 0x4f, 0x4f, 0x4b, 0x10, 0x1d, 0x12, 0x1f, 0x0a, - 0x1b, 0x4b, 0x38, 0x53, 0x5f, 0x4f, 0x42, 0x4a, 0x45, 0x43, 0x54, 0x5f, 0x4b, 0x49, 0x4e, 0x44, - 0x5f, 0x56, 0x4f, 0x4c, 0x43, 0x41, 0x4e, 0x4f, 0x5f, 0x4a, 0x4f, 0x42, 0x10, 0x1e, 0x12, 0x1a, - 0x0a, 0x16, 0x4b, 0x38, 0x53, 0x5f, 0x4f, 0x42, 0x4a, 0x45, 0x43, 0x54, 0x5f, 0x4b, 0x49, 0x4e, - 0x44, 0x5f, 0x53, 0x45, 0x43, 0x52, 0x45, 0x54, 0x10, 0x1f, 0x12, 0x20, 0x0a, 0x1c, 0x4b, 0x38, - 0x53, 0x5f, 0x4f, 0x42, 0x4a, 0x45, 0x43, 0x54, 0x5f, 0x4b, 0x49, 0x4e, 0x44, 0x5f, 0x43, 0x4c, - 0x55, 0x53, 0x54, 0x45, 0x52, 0x5f, 0x52, 0x4f, 0x4c, 0x45, 0x10, 0x20, 0x12, 0x28, 0x0a, 0x24, + 0x49, 0x4e, 0x44, 0x5f, 0x4b, 0x55, 0x42, 0x45, 0x46, 0x4c, 0x4f, 0x57, 0x5f, 0x4e, 0x4f, 0x54, + 0x45, 0x42, 0x4f, 0x4f, 0x4b, 0x10, 0x1d, 0x12, 0x1f, 0x0a, 0x1b, 0x4b, 0x38, 0x53, 0x5f, 0x4f, + 0x42, 0x4a, 0x45, 0x43, 0x54, 0x5f, 0x4b, 0x49, 0x4e, 0x44, 0x5f, 0x56, 0x4f, 0x4c, 0x43, 0x41, + 0x4e, 0x4f, 0x5f, 0x4a, 0x4f, 0x42, 0x10, 0x1e, 0x12, 0x1a, 0x0a, 0x16, 0x4b, 0x38, 0x53, 0x5f, + 0x4f, 0x42, 0x4a, 0x45, 0x43, 0x54, 0x5f, 0x4b, 0x49, 0x4e, 0x44, 0x5f, 0x53, 0x45, 0x43, 0x52, + 0x45, 0x54, 0x10, 0x1f, 0x12, 0x20, 0x0a, 0x1c, 0x4b, 0x38, 0x53, 0x5f, 0x4f, 0x42, 0x4a, 0x45, + 0x43, 0x54, 0x5f, 0x4b, 0x49, 0x4e, 0x44, 0x5f, 0x43, 0x4c, 0x55, 0x53, 0x54, 0x45, 0x52, 0x5f, + 0x52, 0x4f, 0x4c, 0x45, 0x10, 0x20, 0x12, 0x28, 0x0a, 0x24, 0x4b, 0x38, 0x53, 0x5f, 0x4f, 0x42, + 0x4a, 0x45, 0x43, 0x54, 0x5f, 0x4b, 0x49, 0x4e, 0x44, 0x5f, 0x43, 0x4c, 0x55, 0x53, 0x54, 0x45, + 0x52, 0x5f, 0x52, 0x4f, 0x4c, 0x45, 0x5f, 0x42, 0x49, 0x4e, 0x44, 0x49, 0x4e, 0x47, 0x10, 0x21, + 0x12, 0x22, 0x0a, 0x1e, 0x4b, 0x38, 0x53, 0x5f, 0x4f, 0x42, 0x4a, 0x45, 0x43, 0x54, 0x5f, 0x4b, + 0x49, 0x4e, 0x44, 0x5f, 0x4e, 0x45, 0x54, 0x57, 0x4f, 0x52, 0x4b, 0x5f, 0x50, 0x4f, 0x4c, 0x49, + 0x43, 0x59, 0x10, 0x22, 0x12, 0x20, 0x0a, 0x1c, 0x4b, 0x38, 0x53, 0x5f, 0x4f, 0x42, 0x4a, 0x45, + 0x43, 0x54, 0x5f, 0x4b, 0x49, 0x4e, 0x44, 0x5f, 0x43, 0x4e, 0x50, 0x47, 0x5f, 0x43, 0x4c, 0x55, + 0x53, 0x54, 0x45, 0x52, 0x10, 0x23, 0x12, 0x25, 0x0a, 0x21, 0x4b, 0x38, 0x53, 0x5f, 0x4f, 0x42, + 0x4a, 0x45, 0x43, 0x54, 0x5f, 0x4b, 0x49, 0x4e, 0x44, 0x5f, 0x53, 0x50, 0x41, 0x52, 0x4b, 0x5f, + 0x41, 0x50, 0x50, 0x4c, 0x49, 0x43, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x10, 0x24, 0x12, 0x2f, 0x0a, + 0x2b, 0x4b, 0x38, 0x53, 0x5f, 0x4f, 0x42, 0x4a, 0x45, 0x43, 0x54, 0x5f, 0x4b, 0x49, 0x4e, 0x44, + 0x5f, 0x53, 0x43, 0x48, 0x45, 0x44, 0x55, 0x4c, 0x45, 0x44, 0x5f, 0x53, 0x50, 0x41, 0x52, 0x4b, + 0x5f, 0x41, 0x50, 0x50, 0x4c, 0x49, 0x43, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x10, 0x25, 0x12, 0x22, + 0x0a, 0x1e, 0x4b, 0x38, 0x53, 0x5f, 0x4f, 0x42, 0x4a, 0x45, 0x43, 0x54, 0x5f, 0x4b, 0x49, 0x4e, + 0x44, 0x5f, 0x4b, 0x59, 0x56, 0x45, 0x52, 0x4e, 0x4f, 0x5f, 0x50, 0x4f, 0x4c, 0x49, 0x43, 0x59, + 0x10, 0x26, 0x12, 0x29, 0x0a, 0x25, 0x4b, 0x38, 0x53, 0x5f, 0x4f, 0x42, 0x4a, 0x45, 0x43, 0x54, + 0x5f, 0x4b, 0x49, 0x4e, 0x44, 0x5f, 0x4b, 0x59, 0x56, 0x45, 0x52, 0x4e, 0x4f, 0x5f, 0x50, 0x4f, + 0x4c, 0x49, 0x43, 0x59, 0x5f, 0x52, 0x45, 0x50, 0x4f, 0x52, 0x54, 0x10, 0x27, 0x12, 0x32, 0x0a, + 0x2e, 0x4b, 0x38, 0x53, 0x5f, 0x4f, 0x42, 0x4a, 0x45, 0x43, 0x54, 0x5f, 0x4b, 0x49, 0x4e, 0x44, + 0x5f, 0x47, 0x41, 0x54, 0x45, 0x4b, 0x45, 0x45, 0x50, 0x45, 0x52, 0x5f, 0x43, 0x4f, 0x4e, 0x53, + 0x54, 0x52, 0x41, 0x49, 0x4e, 0x54, 0x5f, 0x54, 0x45, 0x4d, 0x50, 0x4c, 0x41, 0x54, 0x45, 0x10, + 0x28, 0x12, 0x29, 0x0a, 0x25, 0x4b, 0x38, 0x53, 0x5f, 0x4f, 0x42, 0x4a, 0x45, 0x43, 0x54, 0x5f, + 0x4b, 0x49, 0x4e, 0x44, 0x5f, 0x47, 0x41, 0x54, 0x45, 0x4b, 0x45, 0x45, 0x50, 0x45, 0x52, 0x5f, + 0x43, 0x4f, 0x4e, 0x53, 0x54, 0x52, 0x41, 0x49, 0x4e, 0x54, 0x10, 0x29, 0x12, 0x24, 0x0a, 0x20, 0x4b, 0x38, 0x53, 0x5f, 0x4f, 0x42, 0x4a, 0x45, 0x43, 0x54, 0x5f, 0x4b, 0x49, 0x4e, 0x44, 0x5f, - 0x43, 0x4c, 0x55, 0x53, 0x54, 0x45, 0x52, 0x5f, 0x52, 0x4f, 0x4c, 0x45, 0x5f, 0x42, 0x49, 0x4e, - 0x44, 0x49, 0x4e, 0x47, 0x10, 0x21, 0x12, 0x22, 0x0a, 0x1e, 0x4b, 0x38, 0x53, 0x5f, 0x4f, 0x42, - 0x4a, 0x45, 0x43, 0x54, 0x5f, 0x4b, 0x49, 0x4e, 0x44, 0x5f, 0x4e, 0x45, 0x54, 0x57, 0x4f, 0x52, - 0x4b, 0x5f, 0x50, 0x4f, 0x4c, 0x49, 0x43, 0x59, 0x10, 0x22, 0x12, 0x20, 0x0a, 0x1c, 0x4b, 0x38, - 0x53, 0x5f, 0x4f, 0x42, 0x4a, 0x45, 0x43, 0x54, 0x5f, 0x4b, 0x49, 0x4e, 0x44, 0x5f, 0x43, 0x4e, - 0x50, 0x47, 0x5f, 0x43, 0x4c, 0x55, 0x53, 0x54, 0x45, 0x52, 0x10, 0x23, 0x12, 0x25, 0x0a, 0x21, + 0x41, 0x52, 0x47, 0x4f, 0x5f, 0x41, 0x50, 0x50, 0x4c, 0x49, 0x43, 0x41, 0x54, 0x49, 0x4f, 0x4e, + 0x10, 0x2a, 0x12, 0x28, 0x0a, 0x24, 0x4b, 0x38, 0x53, 0x5f, 0x4f, 0x42, 0x4a, 0x45, 0x43, 0x54, + 0x5f, 0x4b, 0x49, 0x4e, 0x44, 0x5f, 0x41, 0x52, 0x47, 0x4f, 0x5f, 0x41, 0x50, 0x50, 0x4c, 0x49, + 0x43, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x53, 0x45, 0x54, 0x10, 0x2b, 0x12, 0x2e, 0x0a, 0x2a, 0x4b, 0x38, 0x53, 0x5f, 0x4f, 0x42, 0x4a, 0x45, 0x43, 0x54, 0x5f, 0x4b, 0x49, 0x4e, 0x44, 0x5f, - 0x53, 0x50, 0x41, 0x52, 0x4b, 0x5f, 0x41, 0x50, 0x50, 0x4c, 0x49, 0x43, 0x41, 0x54, 0x49, 0x4f, - 0x4e, 0x10, 0x24, 0x12, 0x2f, 0x0a, 0x2b, 0x4b, 0x38, 0x53, 0x5f, 0x4f, 0x42, 0x4a, 0x45, 0x43, - 0x54, 0x5f, 0x4b, 0x49, 0x4e, 0x44, 0x5f, 0x53, 0x43, 0x48, 0x45, 0x44, 0x55, 0x4c, 0x45, 0x44, - 0x5f, 0x53, 0x50, 0x41, 0x52, 0x4b, 0x5f, 0x41, 0x50, 0x50, 0x4c, 0x49, 0x43, 0x41, 0x54, 0x49, - 0x4f, 0x4e, 0x10, 0x25, 0x12, 0x22, 0x0a, 0x1e, 0x4b, 0x38, 0x53, 0x5f, 0x4f, 0x42, 0x4a, 0x45, - 0x43, 0x54, 0x5f, 0x4b, 0x49, 0x4e, 0x44, 0x5f, 0x4b, 0x59, 0x56, 0x45, 0x52, 0x4e, 0x4f, 0x5f, - 0x50, 0x4f, 0x4c, 0x49, 0x43, 0x59, 0x10, 0x26, 0x12, 0x29, 0x0a, 0x25, 0x4b, 0x38, 0x53, 0x5f, - 0x4f, 0x42, 0x4a, 0x45, 0x43, 0x54, 0x5f, 0x4b, 0x49, 0x4e, 0x44, 0x5f, 0x4b, 0x59, 0x56, 0x45, - 0x52, 0x4e, 0x4f, 0x5f, 0x50, 0x4f, 0x4c, 0x49, 0x43, 0x59, 0x5f, 0x52, 0x45, 0x50, 0x4f, 0x52, - 0x54, 0x10, 0x27, 0x12, 0x32, 0x0a, 0x2e, 0x4b, 0x38, 0x53, 0x5f, 0x4f, 0x42, 0x4a, 0x45, 0x43, - 0x54, 0x5f, 0x4b, 0x49, 0x4e, 0x44, 0x5f, 0x47, 0x41, 0x54, 0x45, 0x4b, 0x45, 0x45, 0x50, 0x45, - 0x52, 0x5f, 0x43, 0x4f, 0x4e, 0x53, 0x54, 0x52, 0x41, 0x49, 0x4e, 0x54, 0x5f, 0x54, 0x45, 0x4d, - 0x50, 0x4c, 0x41, 0x54, 0x45, 0x10, 0x28, 0x12, 0x29, 0x0a, 0x25, 0x4b, 0x38, 0x53, 0x5f, 0x4f, - 0x42, 0x4a, 0x45, 0x43, 0x54, 0x5f, 0x4b, 0x49, 0x4e, 0x44, 0x5f, 0x47, 0x41, 0x54, 0x45, 0x4b, - 0x45, 0x45, 0x50, 0x45, 0x52, 0x5f, 0x43, 0x4f, 0x4e, 0x53, 0x54, 0x52, 0x41, 0x49, 0x4e, 0x54, - 0x10, 0x29, 0x2a, 0x48, 0x0a, 0x0b, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x42, 0x79, 0x45, 0x6e, 0x75, - 0x6d, 0x12, 0x22, 0x0a, 0x1e, 0x4f, 0x52, 0x44, 0x45, 0x52, 0x5f, 0x42, 0x59, 0x5f, 0x45, 0x4e, - 0x55, 0x4d, 0x5f, 0x44, 0x45, 0x53, 0x43, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, - 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x15, 0x0a, 0x11, 0x4f, 0x52, 0x44, 0x45, 0x52, 0x5f, 0x42, - 0x59, 0x5f, 0x45, 0x4e, 0x55, 0x4d, 0x5f, 0x41, 0x53, 0x43, 0x10, 0x01, 0x2a, 0x94, 0x02, 0x0a, - 0x15, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x4f, 0x70, - 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x12, 0x27, 0x0a, 0x23, 0x4c, 0x41, 0x42, 0x45, 0x4c, 0x5f, - 0x53, 0x45, 0x4c, 0x45, 0x43, 0x54, 0x4f, 0x52, 0x5f, 0x4f, 0x50, 0x45, 0x52, 0x41, 0x54, 0x4f, + 0x43, 0x55, 0x53, 0x54, 0x4f, 0x4d, 0x5f, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, + 0x44, 0x45, 0x46, 0x49, 0x4e, 0x49, 0x54, 0x49, 0x4f, 0x4e, 0x10, 0x2c, 0x12, 0x27, 0x0a, 0x23, + 0x4b, 0x38, 0x53, 0x5f, 0x4f, 0x42, 0x4a, 0x45, 0x43, 0x54, 0x5f, 0x4b, 0x49, 0x4e, 0x44, 0x5f, + 0x46, 0x4c, 0x55, 0x58, 0x5f, 0x47, 0x49, 0x54, 0x5f, 0x52, 0x45, 0x50, 0x4f, 0x53, 0x49, 0x54, + 0x4f, 0x52, 0x59, 0x10, 0x2d, 0x12, 0x27, 0x0a, 0x23, 0x4b, 0x38, 0x53, 0x5f, 0x4f, 0x42, 0x4a, + 0x45, 0x43, 0x54, 0x5f, 0x4b, 0x49, 0x4e, 0x44, 0x5f, 0x46, 0x4c, 0x55, 0x58, 0x5f, 0x4f, 0x43, + 0x49, 0x5f, 0x52, 0x45, 0x50, 0x4f, 0x53, 0x49, 0x54, 0x4f, 0x52, 0x59, 0x10, 0x2e, 0x12, 0x28, + 0x0a, 0x24, 0x4b, 0x38, 0x53, 0x5f, 0x4f, 0x42, 0x4a, 0x45, 0x43, 0x54, 0x5f, 0x4b, 0x49, 0x4e, + 0x44, 0x5f, 0x46, 0x4c, 0x55, 0x58, 0x5f, 0x48, 0x45, 0x4c, 0x4d, 0x5f, 0x52, 0x45, 0x50, 0x4f, + 0x53, 0x49, 0x54, 0x4f, 0x52, 0x59, 0x10, 0x2f, 0x12, 0x23, 0x0a, 0x1f, 0x4b, 0x38, 0x53, 0x5f, + 0x4f, 0x42, 0x4a, 0x45, 0x43, 0x54, 0x5f, 0x4b, 0x49, 0x4e, 0x44, 0x5f, 0x46, 0x4c, 0x55, 0x58, + 0x5f, 0x48, 0x45, 0x4c, 0x4d, 0x5f, 0x43, 0x48, 0x41, 0x52, 0x54, 0x10, 0x30, 0x12, 0x1f, 0x0a, + 0x1b, 0x4b, 0x38, 0x53, 0x5f, 0x4f, 0x42, 0x4a, 0x45, 0x43, 0x54, 0x5f, 0x4b, 0x49, 0x4e, 0x44, + 0x5f, 0x46, 0x4c, 0x55, 0x58, 0x5f, 0x42, 0x55, 0x43, 0x4b, 0x45, 0x54, 0x10, 0x31, 0x12, 0x26, + 0x0a, 0x22, 0x4b, 0x38, 0x53, 0x5f, 0x4f, 0x42, 0x4a, 0x45, 0x43, 0x54, 0x5f, 0x4b, 0x49, 0x4e, + 0x44, 0x5f, 0x46, 0x4c, 0x55, 0x58, 0x5f, 0x4b, 0x55, 0x53, 0x54, 0x4f, 0x4d, 0x49, 0x5a, 0x41, + 0x54, 0x49, 0x4f, 0x4e, 0x10, 0x32, 0x12, 0x25, 0x0a, 0x21, 0x4b, 0x38, 0x53, 0x5f, 0x4f, 0x42, + 0x4a, 0x45, 0x43, 0x54, 0x5f, 0x4b, 0x49, 0x4e, 0x44, 0x5f, 0x46, 0x4c, 0x55, 0x58, 0x5f, 0x48, + 0x45, 0x4c, 0x4d, 0x5f, 0x52, 0x45, 0x4c, 0x45, 0x41, 0x53, 0x45, 0x10, 0x33, 0x12, 0x2a, 0x0a, + 0x26, 0x4b, 0x38, 0x53, 0x5f, 0x4f, 0x42, 0x4a, 0x45, 0x43, 0x54, 0x5f, 0x4b, 0x49, 0x4e, 0x44, + 0x5f, 0x46, 0x4c, 0x55, 0x58, 0x5f, 0x45, 0x58, 0x54, 0x45, 0x52, 0x4e, 0x41, 0x4c, 0x5f, 0x41, + 0x52, 0x54, 0x49, 0x46, 0x41, 0x43, 0x54, 0x10, 0x34, 0x12, 0x1e, 0x0a, 0x1a, 0x4b, 0x38, 0x53, + 0x5f, 0x4f, 0x42, 0x4a, 0x45, 0x43, 0x54, 0x5f, 0x4b, 0x49, 0x4e, 0x44, 0x5f, 0x46, 0x4c, 0x55, + 0x58, 0x5f, 0x41, 0x4c, 0x45, 0x52, 0x54, 0x10, 0x35, 0x12, 0x21, 0x0a, 0x1d, 0x4b, 0x38, 0x53, + 0x5f, 0x4f, 0x42, 0x4a, 0x45, 0x43, 0x54, 0x5f, 0x4b, 0x49, 0x4e, 0x44, 0x5f, 0x46, 0x4c, 0x55, + 0x58, 0x5f, 0x50, 0x52, 0x4f, 0x56, 0x49, 0x44, 0x45, 0x52, 0x10, 0x36, 0x12, 0x21, 0x0a, 0x1d, + 0x4b, 0x38, 0x53, 0x5f, 0x4f, 0x42, 0x4a, 0x45, 0x43, 0x54, 0x5f, 0x4b, 0x49, 0x4e, 0x44, 0x5f, + 0x46, 0x4c, 0x55, 0x58, 0x5f, 0x52, 0x45, 0x43, 0x45, 0x49, 0x56, 0x45, 0x52, 0x10, 0x37, 0x12, + 0x21, 0x0a, 0x1d, 0x4b, 0x38, 0x53, 0x5f, 0x4f, 0x42, 0x4a, 0x45, 0x43, 0x54, 0x5f, 0x4b, 0x49, + 0x4e, 0x44, 0x5f, 0x46, 0x4c, 0x55, 0x58, 0x5f, 0x49, 0x4e, 0x53, 0x54, 0x41, 0x4e, 0x43, 0x45, + 0x10, 0x38, 0x12, 0x1f, 0x0a, 0x1b, 0x4b, 0x38, 0x53, 0x5f, 0x4f, 0x42, 0x4a, 0x45, 0x43, 0x54, + 0x5f, 0x4b, 0x49, 0x4e, 0x44, 0x5f, 0x46, 0x4c, 0x55, 0x58, 0x5f, 0x52, 0x45, 0x50, 0x4f, 0x52, + 0x54, 0x10, 0x39, 0x12, 0x34, 0x0a, 0x30, 0x4b, 0x38, 0x53, 0x5f, 0x4f, 0x42, 0x4a, 0x45, 0x43, + 0x54, 0x5f, 0x4b, 0x49, 0x4e, 0x44, 0x5f, 0x46, 0x4c, 0x55, 0x58, 0x5f, 0x52, 0x45, 0x53, 0x4f, + 0x55, 0x52, 0x43, 0x45, 0x5f, 0x53, 0x45, 0x54, 0x5f, 0x49, 0x4e, 0x50, 0x55, 0x54, 0x5f, 0x50, + 0x52, 0x4f, 0x56, 0x49, 0x44, 0x45, 0x52, 0x10, 0x3a, 0x12, 0x25, 0x0a, 0x21, 0x4b, 0x38, 0x53, + 0x5f, 0x4f, 0x42, 0x4a, 0x45, 0x43, 0x54, 0x5f, 0x4b, 0x49, 0x4e, 0x44, 0x5f, 0x46, 0x4c, 0x55, + 0x58, 0x5f, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, 0x53, 0x45, 0x54, 0x10, 0x3b, + 0x12, 0x29, 0x0a, 0x25, 0x4b, 0x38, 0x53, 0x5f, 0x4f, 0x42, 0x4a, 0x45, 0x43, 0x54, 0x5f, 0x4b, + 0x49, 0x4e, 0x44, 0x5f, 0x46, 0x4c, 0x55, 0x58, 0x5f, 0x49, 0x4d, 0x41, 0x47, 0x45, 0x5f, 0x52, + 0x45, 0x50, 0x4f, 0x53, 0x49, 0x54, 0x4f, 0x52, 0x59, 0x10, 0x3c, 0x12, 0x25, 0x0a, 0x21, 0x4b, + 0x38, 0x53, 0x5f, 0x4f, 0x42, 0x4a, 0x45, 0x43, 0x54, 0x5f, 0x4b, 0x49, 0x4e, 0x44, 0x5f, 0x46, + 0x4c, 0x55, 0x58, 0x5f, 0x49, 0x4d, 0x41, 0x47, 0x45, 0x5f, 0x50, 0x4f, 0x4c, 0x49, 0x43, 0x59, + 0x10, 0x3d, 0x12, 0x30, 0x0a, 0x2c, 0x4b, 0x38, 0x53, 0x5f, 0x4f, 0x42, 0x4a, 0x45, 0x43, 0x54, + 0x5f, 0x4b, 0x49, 0x4e, 0x44, 0x5f, 0x46, 0x4c, 0x55, 0x58, 0x5f, 0x49, 0x4d, 0x41, 0x47, 0x45, + 0x5f, 0x55, 0x50, 0x44, 0x41, 0x54, 0x45, 0x5f, 0x41, 0x55, 0x54, 0x4f, 0x4d, 0x41, 0x54, 0x49, + 0x4f, 0x4e, 0x10, 0x3e, 0x12, 0x21, 0x0a, 0x1d, 0x4b, 0x38, 0x53, 0x5f, 0x4f, 0x42, 0x4a, 0x45, + 0x43, 0x54, 0x5f, 0x4b, 0x49, 0x4e, 0x44, 0x5f, 0x46, 0x4c, 0x55, 0x58, 0x5f, 0x52, 0x45, 0x53, + 0x4f, 0x55, 0x52, 0x43, 0x45, 0x10, 0x3f, 0x12, 0x22, 0x0a, 0x1e, 0x4b, 0x38, 0x53, 0x5f, 0x4f, + 0x42, 0x4a, 0x45, 0x43, 0x54, 0x5f, 0x4b, 0x49, 0x4e, 0x44, 0x5f, 0x46, 0x4c, 0x41, 0x47, 0x47, + 0x45, 0x52, 0x5f, 0x43, 0x41, 0x4e, 0x41, 0x52, 0x59, 0x10, 0x40, 0x12, 0x2b, 0x0a, 0x27, 0x4b, + 0x38, 0x53, 0x5f, 0x4f, 0x42, 0x4a, 0x45, 0x43, 0x54, 0x5f, 0x4b, 0x49, 0x4e, 0x44, 0x5f, 0x46, + 0x4c, 0x41, 0x47, 0x47, 0x45, 0x52, 0x5f, 0x4d, 0x45, 0x54, 0x52, 0x49, 0x43, 0x5f, 0x54, 0x45, + 0x4d, 0x50, 0x4c, 0x41, 0x54, 0x45, 0x10, 0x41, 0x12, 0x2a, 0x0a, 0x26, 0x4b, 0x38, 0x53, 0x5f, + 0x4f, 0x42, 0x4a, 0x45, 0x43, 0x54, 0x5f, 0x4b, 0x49, 0x4e, 0x44, 0x5f, 0x46, 0x4c, 0x41, 0x47, + 0x47, 0x45, 0x52, 0x5f, 0x41, 0x4c, 0x45, 0x52, 0x54, 0x5f, 0x50, 0x52, 0x4f, 0x56, 0x49, 0x44, + 0x45, 0x52, 0x10, 0x42, 0x2a, 0x48, 0x0a, 0x0b, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x42, 0x79, 0x45, + 0x6e, 0x75, 0x6d, 0x12, 0x22, 0x0a, 0x1e, 0x4f, 0x52, 0x44, 0x45, 0x52, 0x5f, 0x42, 0x59, 0x5f, + 0x45, 0x4e, 0x55, 0x4d, 0x5f, 0x44, 0x45, 0x53, 0x43, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, + 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x15, 0x0a, 0x11, 0x4f, 0x52, 0x44, 0x45, 0x52, + 0x5f, 0x42, 0x59, 0x5f, 0x45, 0x4e, 0x55, 0x4d, 0x5f, 0x41, 0x53, 0x43, 0x10, 0x01, 0x2a, 0xc3, + 0x01, 0x0a, 0x14, 0x4e, 0x6f, 0x64, 0x65, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x50, 0x72, 0x6f, 0x76, + 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x12, 0x26, 0x0a, 0x22, 0x4e, 0x4f, 0x44, 0x45, 0x5f, + 0x47, 0x52, 0x4f, 0x55, 0x50, 0x5f, 0x50, 0x52, 0x4f, 0x56, 0x49, 0x53, 0x49, 0x4f, 0x4e, 0x45, 0x52, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, - 0x1e, 0x0a, 0x1a, 0x4c, 0x41, 0x42, 0x45, 0x4c, 0x5f, 0x53, 0x45, 0x4c, 0x45, 0x43, 0x54, 0x4f, - 0x52, 0x5f, 0x4f, 0x50, 0x45, 0x52, 0x41, 0x54, 0x4f, 0x52, 0x5f, 0x49, 0x4e, 0x10, 0x01, 0x12, - 0x22, 0x0a, 0x1e, 0x4c, 0x41, 0x42, 0x45, 0x4c, 0x5f, 0x53, 0x45, 0x4c, 0x45, 0x43, 0x54, 0x4f, - 0x52, 0x5f, 0x4f, 0x50, 0x45, 0x52, 0x41, 0x54, 0x4f, 0x52, 0x5f, 0x4e, 0x4f, 0x54, 0x5f, 0x49, - 0x4e, 0x10, 0x02, 0x12, 0x22, 0x0a, 0x1e, 0x4c, 0x41, 0x42, 0x45, 0x4c, 0x5f, 0x53, 0x45, 0x4c, - 0x45, 0x43, 0x54, 0x4f, 0x52, 0x5f, 0x4f, 0x50, 0x45, 0x52, 0x41, 0x54, 0x4f, 0x52, 0x5f, 0x45, - 0x58, 0x49, 0x53, 0x54, 0x53, 0x10, 0x03, 0x12, 0x2a, 0x0a, 0x26, 0x4c, 0x41, 0x42, 0x45, 0x4c, - 0x5f, 0x53, 0x45, 0x4c, 0x45, 0x43, 0x54, 0x4f, 0x52, 0x5f, 0x4f, 0x50, 0x45, 0x52, 0x41, 0x54, - 0x4f, 0x52, 0x5f, 0x44, 0x4f, 0x45, 0x53, 0x5f, 0x4e, 0x4f, 0x54, 0x5f, 0x45, 0x58, 0x49, 0x53, - 0x54, 0x10, 0x04, 0x12, 0x1e, 0x0a, 0x1a, 0x4c, 0x41, 0x42, 0x45, 0x4c, 0x5f, 0x53, 0x45, 0x4c, - 0x45, 0x43, 0x54, 0x4f, 0x52, 0x5f, 0x4f, 0x50, 0x45, 0x52, 0x41, 0x54, 0x4f, 0x52, 0x5f, 0x47, - 0x54, 0x10, 0x05, 0x12, 0x1e, 0x0a, 0x1a, 0x4c, 0x41, 0x42, 0x45, 0x4c, 0x5f, 0x53, 0x45, 0x4c, - 0x45, 0x43, 0x54, 0x4f, 0x52, 0x5f, 0x4f, 0x50, 0x45, 0x52, 0x41, 0x54, 0x4f, 0x52, 0x5f, 0x4c, - 0x54, 0x10, 0x06, 0x2a, 0xa6, 0x01, 0x0a, 0x14, 0x57, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, - 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x12, 0x26, 0x0a, 0x22, - 0x57, 0x4f, 0x52, 0x4b, 0x4c, 0x4f, 0x41, 0x44, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, - 0x46, 0x49, 0x4c, 0x54, 0x45, 0x52, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, - 0x45, 0x44, 0x10, 0x00, 0x12, 0x21, 0x0a, 0x1d, 0x57, 0x4f, 0x52, 0x4b, 0x4c, 0x4f, 0x41, 0x44, - 0x5f, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x46, 0x49, 0x4c, 0x54, 0x45, 0x52, 0x5f, 0x41, - 0x43, 0x54, 0x49, 0x56, 0x45, 0x10, 0x01, 0x12, 0x22, 0x0a, 0x1e, 0x57, 0x4f, 0x52, 0x4b, 0x4c, - 0x4f, 0x41, 0x44, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x46, 0x49, 0x4c, 0x54, 0x45, - 0x52, 0x5f, 0x44, 0x45, 0x4c, 0x45, 0x54, 0x45, 0x44, 0x10, 0x02, 0x12, 0x1f, 0x0a, 0x1b, 0x57, + 0x2d, 0x0a, 0x29, 0x4e, 0x4f, 0x44, 0x45, 0x5f, 0x47, 0x52, 0x4f, 0x55, 0x50, 0x5f, 0x50, 0x52, + 0x4f, 0x56, 0x49, 0x53, 0x49, 0x4f, 0x4e, 0x45, 0x52, 0x5f, 0x43, 0x4c, 0x55, 0x53, 0x54, 0x45, + 0x52, 0x5f, 0x41, 0x55, 0x54, 0x4f, 0x53, 0x43, 0x41, 0x4c, 0x45, 0x52, 0x10, 0x01, 0x12, 0x24, + 0x0a, 0x20, 0x4e, 0x4f, 0x44, 0x45, 0x5f, 0x47, 0x52, 0x4f, 0x55, 0x50, 0x5f, 0x50, 0x52, 0x4f, + 0x56, 0x49, 0x53, 0x49, 0x4f, 0x4e, 0x45, 0x52, 0x5f, 0x4b, 0x41, 0x52, 0x50, 0x45, 0x4e, 0x54, + 0x45, 0x52, 0x10, 0x02, 0x12, 0x2e, 0x0a, 0x2a, 0x4e, 0x4f, 0x44, 0x45, 0x5f, 0x47, 0x52, 0x4f, + 0x55, 0x50, 0x5f, 0x50, 0x52, 0x4f, 0x56, 0x49, 0x53, 0x49, 0x4f, 0x4e, 0x45, 0x52, 0x5f, 0x44, + 0x45, 0x56, 0x5a, 0x45, 0x52, 0x4f, 0x5f, 0x4e, 0x4f, 0x44, 0x45, 0x5f, 0x50, 0x4f, 0x4c, 0x49, + 0x43, 0x59, 0x10, 0x03, 0x2a, 0xdc, 0x01, 0x0a, 0x0b, 0x4e, 0x6f, 0x64, 0x65, 0x4d, 0x61, 0x6e, + 0x61, 0x67, 0x65, 0x72, 0x12, 0x1c, 0x0a, 0x18, 0x4e, 0x4f, 0x44, 0x45, 0x5f, 0x4d, 0x41, 0x4e, + 0x41, 0x47, 0x45, 0x52, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, + 0x10, 0x00, 0x12, 0x1a, 0x0a, 0x16, 0x4e, 0x4f, 0x44, 0x45, 0x5f, 0x4d, 0x41, 0x4e, 0x41, 0x47, + 0x45, 0x52, 0x5f, 0x4b, 0x41, 0x52, 0x50, 0x45, 0x4e, 0x54, 0x45, 0x52, 0x10, 0x01, 0x12, 0x1f, + 0x0a, 0x1b, 0x4e, 0x4f, 0x44, 0x45, 0x5f, 0x4d, 0x41, 0x4e, 0x41, 0x47, 0x45, 0x52, 0x5f, 0x45, + 0x4b, 0x53, 0x5f, 0x4e, 0x4f, 0x44, 0x45, 0x5f, 0x47, 0x52, 0x4f, 0x55, 0x50, 0x10, 0x02, 0x12, + 0x17, 0x0a, 0x13, 0x4e, 0x4f, 0x44, 0x45, 0x5f, 0x4d, 0x41, 0x4e, 0x41, 0x47, 0x45, 0x52, 0x5f, + 0x45, 0x4b, 0x53, 0x43, 0x54, 0x4c, 0x10, 0x03, 0x12, 0x1e, 0x0a, 0x1a, 0x4e, 0x4f, 0x44, 0x45, + 0x5f, 0x4d, 0x41, 0x4e, 0x41, 0x47, 0x45, 0x52, 0x5f, 0x47, 0x4b, 0x45, 0x5f, 0x4e, 0x4f, 0x44, + 0x45, 0x5f, 0x50, 0x4f, 0x4f, 0x4c, 0x10, 0x04, 0x12, 0x1f, 0x0a, 0x1b, 0x4e, 0x4f, 0x44, 0x45, + 0x5f, 0x4d, 0x41, 0x4e, 0x41, 0x47, 0x45, 0x52, 0x5f, 0x41, 0x4b, 0x53, 0x5f, 0x41, 0x47, 0x45, + 0x4e, 0x54, 0x5f, 0x50, 0x4f, 0x4f, 0x4c, 0x10, 0x05, 0x12, 0x18, 0x0a, 0x14, 0x4e, 0x4f, 0x44, + 0x45, 0x5f, 0x4d, 0x41, 0x4e, 0x41, 0x47, 0x45, 0x52, 0x5f, 0x43, 0x41, 0x53, 0x54, 0x5f, 0x41, + 0x49, 0x10, 0x06, 0x2a, 0x94, 0x02, 0x0a, 0x15, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x53, 0x65, 0x6c, + 0x65, 0x63, 0x74, 0x6f, 0x72, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x12, 0x27, 0x0a, + 0x23, 0x4c, 0x41, 0x42, 0x45, 0x4c, 0x5f, 0x53, 0x45, 0x4c, 0x45, 0x43, 0x54, 0x4f, 0x52, 0x5f, + 0x4f, 0x50, 0x45, 0x52, 0x41, 0x54, 0x4f, 0x52, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, + 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x1e, 0x0a, 0x1a, 0x4c, 0x41, 0x42, 0x45, 0x4c, 0x5f, + 0x53, 0x45, 0x4c, 0x45, 0x43, 0x54, 0x4f, 0x52, 0x5f, 0x4f, 0x50, 0x45, 0x52, 0x41, 0x54, 0x4f, + 0x52, 0x5f, 0x49, 0x4e, 0x10, 0x01, 0x12, 0x22, 0x0a, 0x1e, 0x4c, 0x41, 0x42, 0x45, 0x4c, 0x5f, + 0x53, 0x45, 0x4c, 0x45, 0x43, 0x54, 0x4f, 0x52, 0x5f, 0x4f, 0x50, 0x45, 0x52, 0x41, 0x54, 0x4f, + 0x52, 0x5f, 0x4e, 0x4f, 0x54, 0x5f, 0x49, 0x4e, 0x10, 0x02, 0x12, 0x22, 0x0a, 0x1e, 0x4c, 0x41, + 0x42, 0x45, 0x4c, 0x5f, 0x53, 0x45, 0x4c, 0x45, 0x43, 0x54, 0x4f, 0x52, 0x5f, 0x4f, 0x50, 0x45, + 0x52, 0x41, 0x54, 0x4f, 0x52, 0x5f, 0x45, 0x58, 0x49, 0x53, 0x54, 0x53, 0x10, 0x03, 0x12, 0x2a, + 0x0a, 0x26, 0x4c, 0x41, 0x42, 0x45, 0x4c, 0x5f, 0x53, 0x45, 0x4c, 0x45, 0x43, 0x54, 0x4f, 0x52, + 0x5f, 0x4f, 0x50, 0x45, 0x52, 0x41, 0x54, 0x4f, 0x52, 0x5f, 0x44, 0x4f, 0x45, 0x53, 0x5f, 0x4e, + 0x4f, 0x54, 0x5f, 0x45, 0x58, 0x49, 0x53, 0x54, 0x10, 0x04, 0x12, 0x1e, 0x0a, 0x1a, 0x4c, 0x41, + 0x42, 0x45, 0x4c, 0x5f, 0x53, 0x45, 0x4c, 0x45, 0x43, 0x54, 0x4f, 0x52, 0x5f, 0x4f, 0x50, 0x45, + 0x52, 0x41, 0x54, 0x4f, 0x52, 0x5f, 0x47, 0x54, 0x10, 0x05, 0x12, 0x1e, 0x0a, 0x1a, 0x4c, 0x41, + 0x42, 0x45, 0x4c, 0x5f, 0x53, 0x45, 0x4c, 0x45, 0x43, 0x54, 0x4f, 0x52, 0x5f, 0x4f, 0x50, 0x45, + 0x52, 0x41, 0x54, 0x4f, 0x52, 0x5f, 0x4c, 0x54, 0x10, 0x06, 0x2a, 0xa6, 0x01, 0x0a, 0x14, 0x57, + 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x46, 0x69, 0x6c, + 0x74, 0x65, 0x72, 0x12, 0x26, 0x0a, 0x22, 0x57, 0x4f, 0x52, 0x4b, 0x4c, 0x4f, 0x41, 0x44, 0x5f, + 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x46, 0x49, 0x4c, 0x54, 0x45, 0x52, 0x5f, 0x55, 0x4e, + 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x21, 0x0a, 0x1d, 0x57, 0x4f, 0x52, 0x4b, 0x4c, 0x4f, 0x41, 0x44, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x46, - 0x49, 0x4c, 0x54, 0x45, 0x52, 0x5f, 0x42, 0x4f, 0x54, 0x48, 0x10, 0x03, 0x2a, 0xdb, 0x02, 0x0a, - 0x15, 0x44, 0x69, 0x73, 0x72, 0x75, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, - 0x52, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x12, 0x27, 0x0a, 0x23, 0x44, 0x49, 0x53, 0x52, 0x55, 0x50, - 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x42, 0x4c, 0x4f, 0x43, 0x4b, 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, - 0x4e, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, - 0x29, 0x0a, 0x25, 0x44, 0x49, 0x53, 0x52, 0x55, 0x50, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x42, 0x4c, - 0x4f, 0x43, 0x4b, 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x50, 0x44, 0x42, 0x5f, 0x56, - 0x49, 0x4f, 0x4c, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x10, 0x01, 0x12, 0x35, 0x0a, 0x31, 0x44, 0x49, + 0x49, 0x4c, 0x54, 0x45, 0x52, 0x5f, 0x41, 0x43, 0x54, 0x49, 0x56, 0x45, 0x10, 0x01, 0x12, 0x22, + 0x0a, 0x1e, 0x57, 0x4f, 0x52, 0x4b, 0x4c, 0x4f, 0x41, 0x44, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x55, + 0x53, 0x5f, 0x46, 0x49, 0x4c, 0x54, 0x45, 0x52, 0x5f, 0x44, 0x45, 0x4c, 0x45, 0x54, 0x45, 0x44, + 0x10, 0x02, 0x12, 0x1f, 0x0a, 0x1b, 0x57, 0x4f, 0x52, 0x4b, 0x4c, 0x4f, 0x41, 0x44, 0x5f, 0x53, + 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x46, 0x49, 0x4c, 0x54, 0x45, 0x52, 0x5f, 0x42, 0x4f, 0x54, + 0x48, 0x10, 0x03, 0x2a, 0xfa, 0x01, 0x0a, 0x1a, 0x57, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, + 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x46, 0x69, 0x6c, 0x74, + 0x65, 0x72, 0x12, 0x2d, 0x0a, 0x29, 0x57, 0x4f, 0x52, 0x4b, 0x4c, 0x4f, 0x41, 0x44, 0x5f, 0x48, + 0x45, 0x41, 0x4c, 0x54, 0x48, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x46, 0x49, 0x4c, + 0x54, 0x45, 0x52, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, + 0x00, 0x12, 0x29, 0x0a, 0x25, 0x57, 0x4f, 0x52, 0x4b, 0x4c, 0x4f, 0x41, 0x44, 0x5f, 0x48, 0x45, + 0x41, 0x4c, 0x54, 0x48, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x46, 0x49, 0x4c, 0x54, + 0x45, 0x52, 0x5f, 0x48, 0x45, 0x41, 0x4c, 0x54, 0x48, 0x59, 0x10, 0x01, 0x12, 0x29, 0x0a, 0x25, + 0x57, 0x4f, 0x52, 0x4b, 0x4c, 0x4f, 0x41, 0x44, 0x5f, 0x48, 0x45, 0x41, 0x4c, 0x54, 0x48, 0x5f, + 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x46, 0x49, 0x4c, 0x54, 0x45, 0x52, 0x5f, 0x57, 0x41, + 0x52, 0x4e, 0x49, 0x4e, 0x47, 0x10, 0x02, 0x12, 0x2a, 0x0a, 0x26, 0x57, 0x4f, 0x52, 0x4b, 0x4c, + 0x4f, 0x41, 0x44, 0x5f, 0x48, 0x45, 0x41, 0x4c, 0x54, 0x48, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x55, + 0x53, 0x5f, 0x46, 0x49, 0x4c, 0x54, 0x45, 0x52, 0x5f, 0x43, 0x52, 0x49, 0x54, 0x49, 0x43, 0x41, + 0x4c, 0x10, 0x03, 0x12, 0x2b, 0x0a, 0x27, 0x57, 0x4f, 0x52, 0x4b, 0x4c, 0x4f, 0x41, 0x44, 0x5f, + 0x48, 0x45, 0x41, 0x4c, 0x54, 0x48, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x46, 0x49, + 0x4c, 0x54, 0x45, 0x52, 0x5f, 0x52, 0x45, 0x43, 0x4f, 0x56, 0x45, 0x52, 0x45, 0x44, 0x10, 0x04, + 0x2a, 0xa8, 0x01, 0x0a, 0x13, 0x57, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x48, 0x65, 0x61, + 0x6c, 0x74, 0x68, 0x49, 0x73, 0x73, 0x75, 0x65, 0x12, 0x25, 0x0a, 0x21, 0x57, 0x4f, 0x52, 0x4b, + 0x4c, 0x4f, 0x41, 0x44, 0x5f, 0x48, 0x45, 0x41, 0x4c, 0x54, 0x48, 0x5f, 0x49, 0x53, 0x53, 0x55, + 0x45, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, + 0x1d, 0x0a, 0x19, 0x57, 0x4f, 0x52, 0x4b, 0x4c, 0x4f, 0x41, 0x44, 0x5f, 0x48, 0x45, 0x41, 0x4c, + 0x54, 0x48, 0x5f, 0x49, 0x53, 0x53, 0x55, 0x45, 0x5f, 0x4f, 0x4f, 0x4d, 0x10, 0x01, 0x12, 0x26, + 0x0a, 0x22, 0x57, 0x4f, 0x52, 0x4b, 0x4c, 0x4f, 0x41, 0x44, 0x5f, 0x48, 0x45, 0x41, 0x4c, 0x54, + 0x48, 0x5f, 0x49, 0x53, 0x53, 0x55, 0x45, 0x5f, 0x43, 0x50, 0x55, 0x5f, 0x54, 0x48, 0x52, 0x4f, + 0x54, 0x54, 0x4c, 0x45, 0x10, 0x02, 0x12, 0x23, 0x0a, 0x1f, 0x57, 0x4f, 0x52, 0x4b, 0x4c, 0x4f, + 0x41, 0x44, 0x5f, 0x48, 0x45, 0x41, 0x4c, 0x54, 0x48, 0x5f, 0x49, 0x53, 0x53, 0x55, 0x45, 0x5f, + 0x43, 0x52, 0x41, 0x53, 0x48, 0x4c, 0x4f, 0x4f, 0x50, 0x10, 0x03, 0x2a, 0x81, 0x01, 0x0a, 0x14, + 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x49, 0x73, 0x73, 0x75, 0x65, 0x4d, 0x61, 0x74, 0x63, 0x68, + 0x4d, 0x6f, 0x64, 0x65, 0x12, 0x27, 0x0a, 0x23, 0x48, 0x45, 0x41, 0x4c, 0x54, 0x48, 0x5f, 0x49, + 0x53, 0x53, 0x55, 0x45, 0x5f, 0x4d, 0x41, 0x54, 0x43, 0x48, 0x5f, 0x4d, 0x4f, 0x44, 0x45, 0x5f, + 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x1f, 0x0a, + 0x1b, 0x48, 0x45, 0x41, 0x4c, 0x54, 0x48, 0x5f, 0x49, 0x53, 0x53, 0x55, 0x45, 0x5f, 0x4d, 0x41, + 0x54, 0x43, 0x48, 0x5f, 0x4d, 0x4f, 0x44, 0x45, 0x5f, 0x41, 0x4e, 0x59, 0x10, 0x01, 0x12, 0x1f, + 0x0a, 0x1b, 0x48, 0x45, 0x41, 0x4c, 0x54, 0x48, 0x5f, 0x49, 0x53, 0x53, 0x55, 0x45, 0x5f, 0x4d, + 0x41, 0x54, 0x43, 0x48, 0x5f, 0x4d, 0x4f, 0x44, 0x45, 0x5f, 0x41, 0x4c, 0x4c, 0x10, 0x02, 0x2a, + 0xea, 0x03, 0x0a, 0x15, 0x44, 0x69, 0x73, 0x72, 0x75, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x6c, + 0x6f, 0x63, 0x6b, 0x52, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x12, 0x27, 0x0a, 0x23, 0x44, 0x49, 0x53, + 0x52, 0x55, 0x50, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x42, 0x4c, 0x4f, 0x43, 0x4b, 0x5f, 0x52, 0x45, + 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, + 0x10, 0x00, 0x12, 0x29, 0x0a, 0x25, 0x44, 0x49, 0x53, 0x52, 0x55, 0x50, 0x54, 0x49, 0x4f, 0x4e, + 0x5f, 0x42, 0x4c, 0x4f, 0x43, 0x4b, 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x50, 0x44, + 0x42, 0x5f, 0x56, 0x49, 0x4f, 0x4c, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x10, 0x01, 0x12, 0x35, 0x0a, + 0x31, 0x44, 0x49, 0x53, 0x52, 0x55, 0x50, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x42, 0x4c, 0x4f, 0x43, + 0x4b, 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x44, 0x4f, 0x5f, 0x4e, 0x4f, 0x54, 0x5f, + 0x44, 0x49, 0x53, 0x52, 0x55, 0x50, 0x54, 0x5f, 0x41, 0x4e, 0x4e, 0x4f, 0x54, 0x41, 0x54, 0x49, + 0x4f, 0x4e, 0x10, 0x02, 0x12, 0x2b, 0x0a, 0x27, 0x44, 0x49, 0x53, 0x52, 0x55, 0x50, 0x54, 0x49, + 0x4f, 0x4e, 0x5f, 0x42, 0x4c, 0x4f, 0x43, 0x4b, 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, + 0x46, 0x4f, 0x52, 0x43, 0x45, 0x5f, 0x44, 0x49, 0x53, 0x52, 0x55, 0x50, 0x54, 0x45, 0x44, 0x10, + 0x03, 0x12, 0x21, 0x0a, 0x1d, 0x44, 0x49, 0x53, 0x52, 0x55, 0x50, 0x54, 0x49, 0x4f, 0x4e, 0x5f, + 0x42, 0x4c, 0x4f, 0x43, 0x4b, 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x4f, 0x54, 0x48, + 0x45, 0x52, 0x10, 0x04, 0x12, 0x34, 0x0a, 0x30, 0x44, 0x49, 0x53, 0x52, 0x55, 0x50, 0x54, 0x49, + 0x4f, 0x4e, 0x5f, 0x42, 0x4c, 0x4f, 0x43, 0x4b, 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, + 0x4e, 0x4f, 0x54, 0x5f, 0x55, 0x4e, 0x4e, 0x45, 0x45, 0x44, 0x45, 0x44, 0x5f, 0x4c, 0x4f, 0x4e, + 0x47, 0x5f, 0x45, 0x4e, 0x4f, 0x55, 0x47, 0x48, 0x10, 0x05, 0x12, 0x31, 0x0a, 0x2d, 0x44, 0x49, 0x53, 0x52, 0x55, 0x50, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x42, 0x4c, 0x4f, 0x43, 0x4b, 0x5f, 0x52, - 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x44, 0x4f, 0x5f, 0x4e, 0x4f, 0x54, 0x5f, 0x44, 0x49, 0x53, - 0x52, 0x55, 0x50, 0x54, 0x5f, 0x41, 0x4e, 0x4e, 0x4f, 0x54, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x10, - 0x02, 0x12, 0x2b, 0x0a, 0x27, 0x44, 0x49, 0x53, 0x52, 0x55, 0x50, 0x54, 0x49, 0x4f, 0x4e, 0x5f, - 0x42, 0x4c, 0x4f, 0x43, 0x4b, 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x46, 0x4f, 0x52, - 0x43, 0x45, 0x5f, 0x44, 0x49, 0x53, 0x52, 0x55, 0x50, 0x54, 0x45, 0x44, 0x10, 0x03, 0x12, 0x21, - 0x0a, 0x1d, 0x44, 0x49, 0x53, 0x52, 0x55, 0x50, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x42, 0x4c, 0x4f, - 0x43, 0x4b, 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x4f, 0x54, 0x48, 0x45, 0x52, 0x10, - 0x04, 0x12, 0x34, 0x0a, 0x30, 0x44, 0x49, 0x53, 0x52, 0x55, 0x50, 0x54, 0x49, 0x4f, 0x4e, 0x5f, - 0x42, 0x4c, 0x4f, 0x43, 0x4b, 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x4e, 0x4f, 0x54, - 0x5f, 0x55, 0x4e, 0x4e, 0x45, 0x45, 0x44, 0x45, 0x44, 0x5f, 0x4c, 0x4f, 0x4e, 0x47, 0x5f, 0x45, - 0x4e, 0x4f, 0x55, 0x47, 0x48, 0x10, 0x05, 0x12, 0x31, 0x0a, 0x2d, 0x44, 0x49, 0x53, 0x52, 0x55, + 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x4e, 0x4f, 0x5f, 0x50, 0x4c, 0x41, 0x43, 0x45, 0x5f, 0x54, + 0x4f, 0x5f, 0x4d, 0x4f, 0x56, 0x45, 0x5f, 0x50, 0x4f, 0x44, 0x53, 0x10, 0x06, 0x12, 0x29, 0x0a, + 0x25, 0x44, 0x49, 0x53, 0x52, 0x55, 0x50, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x42, 0x4c, 0x4f, 0x43, + 0x4b, 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x50, 0x4f, 0x44, 0x5f, 0x4e, 0x4f, 0x4d, + 0x49, 0x4e, 0x41, 0x54, 0x45, 0x44, 0x10, 0x07, 0x12, 0x2d, 0x0a, 0x29, 0x44, 0x49, 0x53, 0x52, + 0x55, 0x50, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x42, 0x4c, 0x4f, 0x43, 0x4b, 0x5f, 0x52, 0x45, 0x41, + 0x53, 0x4f, 0x4e, 0x5f, 0x44, 0x49, 0x53, 0x52, 0x55, 0x50, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x42, + 0x55, 0x44, 0x47, 0x45, 0x54, 0x10, 0x08, 0x12, 0x33, 0x0a, 0x2f, 0x44, 0x49, 0x53, 0x52, 0x55, 0x50, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x42, 0x4c, 0x4f, 0x43, 0x4b, 0x5f, 0x52, 0x45, 0x41, 0x53, - 0x4f, 0x4e, 0x5f, 0x4e, 0x4f, 0x5f, 0x50, 0x4c, 0x41, 0x43, 0x45, 0x5f, 0x54, 0x4f, 0x5f, 0x4d, - 0x4f, 0x56, 0x45, 0x5f, 0x50, 0x4f, 0x44, 0x53, 0x10, 0x06, 0x42, 0x84, 0x01, 0x0a, 0x0a, 0x63, - 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x42, 0x0b, 0x43, 0x6f, 0x6d, 0x6d, 0x6f, - 0x6e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x30, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, - 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x64, 0x65, 0x76, 0x7a, 0x65, 0x72, 0x6f, 0x2d, 0x69, 0x6e, 0x63, - 0x2f, 0x7a, 0x78, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x72, 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x61, 0x70, - 0x69, 0x2f, 0x76, 0x31, 0x3b, 0x61, 0x70, 0x69, 0x76, 0x31, 0xa2, 0x02, 0x03, 0x41, 0x58, 0x58, - 0xaa, 0x02, 0x06, 0x41, 0x70, 0x69, 0x2e, 0x56, 0x31, 0xca, 0x02, 0x06, 0x41, 0x70, 0x69, 0x5c, - 0x56, 0x31, 0xe2, 0x02, 0x12, 0x41, 0x70, 0x69, 0x5c, 0x56, 0x31, 0x5c, 0x47, 0x50, 0x42, 0x4d, - 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x07, 0x41, 0x70, 0x69, 0x3a, 0x3a, 0x56, - 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x4f, 0x4e, 0x5f, 0x50, 0x4f, 0x44, 0x53, 0x5f, 0x57, 0x4f, 0x55, 0x4c, 0x44, 0x5f, 0x4e, 0x4f, + 0x54, 0x5f, 0x53, 0x43, 0x48, 0x45, 0x44, 0x55, 0x4c, 0x45, 0x10, 0x09, 0x42, 0x89, 0x01, 0x0a, + 0x0a, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x42, 0x0b, 0x43, 0x6f, 0x6d, + 0x6d, 0x6f, 0x6e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x35, 0x67, 0x69, 0x74, 0x68, + 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x64, 0x65, 0x76, 0x7a, 0x65, 0x72, 0x6f, 0x2d, 0x69, + 0x6e, 0x63, 0x2f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2f, 0x64, 0x61, 0x6b, 0x72, + 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x3b, 0x61, 0x70, 0x69, 0x76, + 0x31, 0xa2, 0x02, 0x03, 0x41, 0x58, 0x58, 0xaa, 0x02, 0x06, 0x41, 0x70, 0x69, 0x2e, 0x56, 0x31, + 0xca, 0x02, 0x06, 0x41, 0x70, 0x69, 0x5c, 0x56, 0x31, 0xe2, 0x02, 0x12, 0x41, 0x70, 0x69, 0x5c, + 0x56, 0x31, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, + 0x07, 0x41, 0x70, 0x69, 0x3a, 0x3a, 0x56, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -19485,485 +25034,564 @@ func file_api_v1_common_proto_rawDescGZIP() []byte { return file_api_v1_common_proto_rawDescData } -var file_api_v1_common_proto_enumTypes = make([]protoimpl.EnumInfo, 5) -var file_api_v1_common_proto_msgTypes = make([]protoimpl.MessageInfo, 186) +var file_api_v1_common_proto_enumTypes = make([]protoimpl.EnumInfo, 10) +var file_api_v1_common_proto_msgTypes = make([]protoimpl.MessageInfo, 207) var file_api_v1_common_proto_goTypes = []interface{}{ (K8SObjectKind)(0), // 0: api.v1.K8sObjectKind (OrderByEnum)(0), // 1: api.v1.OrderByEnum - (LabelSelectorOperator)(0), // 2: api.v1.LabelSelectorOperator - (WorkloadStatusFilter)(0), // 3: api.v1.WorkloadStatusFilter - (DisruptionBlockReason)(0), // 4: api.v1.DisruptionBlockReason - (*AuditLogEntry)(nil), // 5: api.v1.AuditLogEntry - (*Pagination)(nil), // 6: api.v1.Pagination - (*CostInfo)(nil), // 7: api.v1.CostInfo - (*ResourceMetrics)(nil), // 8: api.v1.ResourceMetrics - (*MetricPercentiles)(nil), // 9: api.v1.MetricPercentiles - (*ContainerPercentileSummary)(nil), // 10: api.v1.ContainerPercentileSummary - (*ContainerRequestLimits)(nil), // 11: api.v1.ContainerRequestLimits - (*GPUMigInstance)(nil), // 12: api.v1.GPUMigInstance - (*IndividualGPUMetric)(nil), // 13: api.v1.IndividualGPUMetric - (*ContainerIndividualGPUMetrics)(nil), // 14: api.v1.ContainerIndividualGPUMetrics - (*RuntimeProcess)(nil), // 15: api.v1.RuntimeProcess - (*ForecastResourceMetrics)(nil), // 16: api.v1.ForecastResourceMetrics - (*ResourceSummary)(nil), // 17: api.v1.ResourceSummary - (*LegacyPolicyRef)(nil), // 18: api.v1.LegacyPolicyRef - (*OptimizationStatus)(nil), // 19: api.v1.OptimizationStatus - (*WorkloadItem)(nil), // 20: api.v1.WorkloadItem - (*ContainerRuntimeInfo)(nil), // 21: api.v1.ContainerRuntimeInfo - (*NodeInfo)(nil), // 22: api.v1.NodeInfo - (*ResourceInfo)(nil), // 23: api.v1.ResourceInfo - (*Node)(nil), // 24: api.v1.Node - (*AttachedVolume)(nil), // 25: api.v1.AttachedVolume - (*NodeGroup)(nil), // 26: api.v1.NodeGroup - (*CostDataPoint)(nil), // 27: api.v1.CostDataPoint - (*ResourceDataPoint)(nil), // 28: api.v1.ResourceDataPoint - (*ContainerTimeSeries)(nil), // 29: api.v1.ContainerTimeSeries - (*SavingsData)(nil), // 30: api.v1.SavingsData - (*SavingsDataPoint)(nil), // 31: api.v1.SavingsDataPoint - (*SavingsTimeSeries)(nil), // 32: api.v1.SavingsTimeSeries - (*LabelSelectorRequirement)(nil), // 33: api.v1.LabelSelectorRequirement - (*Label)(nil), // 34: api.v1.Label - (*LabelSelector)(nil), // 35: api.v1.LabelSelector - (*RegexPattern)(nil), // 36: api.v1.RegexPattern - (*ResourceDetails)(nil), // 37: api.v1.ResourceDetails - (*PodDetails)(nil), // 38: api.v1.PodDetails - (*ContainerSummary)(nil), // 39: api.v1.ContainerSummary - (*DeploymentDetails)(nil), // 40: api.v1.DeploymentDetails - (*ContainerTemplate)(nil), // 41: api.v1.ContainerTemplate - (*DeploymentCondition)(nil), // 42: api.v1.DeploymentCondition - (*StatefulSetDetails)(nil), // 43: api.v1.StatefulSetDetails - (*VolumeClaimTemplate)(nil), // 44: api.v1.VolumeClaimTemplate - (*DaemonSetDetails)(nil), // 45: api.v1.DaemonSetDetails - (*ReplicaSetDetails)(nil), // 46: api.v1.ReplicaSetDetails - (*JobDetails)(nil), // 47: api.v1.JobDetails - (*JobCondition)(nil), // 48: api.v1.JobCondition - (*CronJobDetails)(nil), // 49: api.v1.CronJobDetails - (*ActiveJobReference)(nil), // 50: api.v1.ActiveJobReference - (*JobTemplate)(nil), // 51: api.v1.JobTemplate - (*ServiceDetails)(nil), // 52: api.v1.ServiceDetails - (*ServicePort)(nil), // 53: api.v1.ServicePort - (*LoadBalancerIngress)(nil), // 54: api.v1.LoadBalancerIngress - (*IngressDetails)(nil), // 55: api.v1.IngressDetails - (*IngressRule)(nil), // 56: api.v1.IngressRule - (*IngressPath)(nil), // 57: api.v1.IngressPath - (*IngressBackend)(nil), // 58: api.v1.IngressBackend - (*IngressTLS)(nil), // 59: api.v1.IngressTLS - (*PersistentVolumeClaimDetails)(nil), // 60: api.v1.PersistentVolumeClaimDetails - (*ResourceRequirements)(nil), // 61: api.v1.ResourceRequirements - (*VolumeNodeAffinity)(nil), // 62: api.v1.VolumeNodeAffinity - (*PVCCondition)(nil), // 63: api.v1.PVCCondition - (*PersistentVolumeDetails)(nil), // 64: api.v1.PersistentVolumeDetails - (*PVClaimReference)(nil), // 65: api.v1.PVClaimReference - (*PVVolumeSource)(nil), // 66: api.v1.PVVolumeSource - (*CSIVolumeSource)(nil), // 67: api.v1.CSIVolumeSource - (*HostPathVolumeSource)(nil), // 68: api.v1.HostPathVolumeSource - (*NFSVolumeSource)(nil), // 69: api.v1.NFSVolumeSource - (*StorageClassDetails)(nil), // 70: api.v1.StorageClassDetails - (*NamespaceDetails)(nil), // 71: api.v1.NamespaceDetails - (*NodeDetails)(nil), // 72: api.v1.NodeDetails - (*NodeTaint)(nil), // 73: api.v1.NodeTaint - (*NodeAddress)(nil), // 74: api.v1.NodeAddress - (*NodeCondition)(nil), // 75: api.v1.NodeCondition - (*NodeSystemInfo)(nil), // 76: api.v1.NodeSystemInfo - (*NodeImage)(nil), // 77: api.v1.NodeImage - (*TopologySelector)(nil), // 78: api.v1.TopologySelector - (*TopologySelectorTerm)(nil), // 79: api.v1.TopologySelectorTerm - (*TolerationInfo)(nil), // 80: api.v1.TolerationInfo - (*NodeSelector)(nil), // 81: api.v1.NodeSelector - (*NodeSelectorTerm)(nil), // 82: api.v1.NodeSelectorTerm - (*NodeSelectorRequirement)(nil), // 83: api.v1.NodeSelectorRequirement - (*HPADetails)(nil), // 84: api.v1.HPADetails - (*ScaleTargetRef)(nil), // 85: api.v1.ScaleTargetRef - (*HPAMetric)(nil), // 86: api.v1.HPAMetric - (*HPAResourceMetric)(nil), // 87: api.v1.HPAResourceMetric - (*HPAPodsMetric)(nil), // 88: api.v1.HPAPodsMetric - (*HPAObjectMetric)(nil), // 89: api.v1.HPAObjectMetric - (*HPAExternalMetric)(nil), // 90: api.v1.HPAExternalMetric - (*HPAMetricSelector)(nil), // 91: api.v1.HPAMetricSelector - (*HPAObjectReference)(nil), // 92: api.v1.HPAObjectReference - (*HPACurrentMetric)(nil), // 93: api.v1.HPACurrentMetric - (*HPACurrentResourceMetric)(nil), // 94: api.v1.HPACurrentResourceMetric - (*HPACurrentPodsMetric)(nil), // 95: api.v1.HPACurrentPodsMetric - (*HPACurrentObjectMetric)(nil), // 96: api.v1.HPACurrentObjectMetric - (*HPACurrentExternalMetric)(nil), // 97: api.v1.HPACurrentExternalMetric - (*HPACondition)(nil), // 98: api.v1.HPACondition - (*HPABehavior)(nil), // 99: api.v1.HPABehavior - (*HPAScalingRules)(nil), // 100: api.v1.HPAScalingRules - (*HPAScalingPolicy)(nil), // 101: api.v1.HPAScalingPolicy - (*VPADetails)(nil), // 102: api.v1.VPADetails - (*VPATargetRef)(nil), // 103: api.v1.VPATargetRef - (*VPAUpdatePolicy)(nil), // 104: api.v1.VPAUpdatePolicy - (*VPAResourcePolicy)(nil), // 105: api.v1.VPAResourcePolicy - (*VPAContainerResourcePolicy)(nil), // 106: api.v1.VPAContainerResourcePolicy - (*VPARecommendation)(nil), // 107: api.v1.VPARecommendation - (*VPAContainerRecommendation)(nil), // 108: api.v1.VPAContainerRecommendation - (*VPACondition)(nil), // 109: api.v1.VPACondition - (*LimitRangeDetails)(nil), // 110: api.v1.LimitRangeDetails - (*LimitRangeItem)(nil), // 111: api.v1.LimitRangeItem - (*ServiceAccountDetails)(nil), // 112: api.v1.ServiceAccountDetails - (*RoleDetails)(nil), // 113: api.v1.RoleDetails - (*RoleRule)(nil), // 114: api.v1.RoleRule - (*RoleBindingDetails)(nil), // 115: api.v1.RoleBindingDetails - (*RoleBindingSubject)(nil), // 116: api.v1.RoleBindingSubject - (*RoleReference)(nil), // 117: api.v1.RoleReference - (*KedaScaledObjectDetails)(nil), // 118: api.v1.KedaScaledObjectDetails - (*KedaScaledObjectTrigger)(nil), // 119: api.v1.KedaScaledObjectTrigger - (*KedaScaledObjectCondition)(nil), // 120: api.v1.KedaScaledObjectCondition - (*KarpenterResourceDetails)(nil), // 121: api.v1.KarpenterResourceDetails - (*KarpenterResourceCondition)(nil), // 122: api.v1.KarpenterResourceCondition - (*KarpenterCapacity)(nil), // 123: api.v1.KarpenterCapacity - (*KarpenterRequirement)(nil), // 124: api.v1.KarpenterRequirement - (*ResourceFieldFilter)(nil), // 125: api.v1.ResourceFieldFilter - (*ResourceFilterFieldOptions)(nil), // 126: api.v1.ResourceFilterFieldOptions - (*WorkloadFilters)(nil), // 127: api.v1.WorkloadFilters - (*PodDisruptionBudgetDetails)(nil), // 128: api.v1.PodDisruptionBudgetDetails - (*PodDisruptionBudgetCondition)(nil), // 129: api.v1.PodDisruptionBudgetCondition - (*ResourceQuotaDetails)(nil), // 130: api.v1.ResourceQuotaDetails - (*ResourceQuotaCondition)(nil), // 131: api.v1.ResourceQuotaCondition - (*VolcanoJobDetails)(nil), // 132: api.v1.VolcanoJobDetails - (*VolcanoJobCondition)(nil), // 133: api.v1.VolcanoJobCondition - (*VolcanoTask)(nil), // 134: api.v1.VolcanoTask - (*SparkApplicationDetails)(nil), // 135: api.v1.SparkApplicationDetails - (*SparkDriverInfo)(nil), // 136: api.v1.SparkDriverInfo - (*SparkRestartPolicyInfo)(nil), // 137: api.v1.SparkRestartPolicyInfo - (*SparkDynamicAllocationInfo)(nil), // 138: api.v1.SparkDynamicAllocationInfo - (*ScheduledSparkApplicationDetails)(nil), // 139: api.v1.ScheduledSparkApplicationDetails - (*KyvernoPolicyDetails)(nil), // 140: api.v1.KyvernoPolicyDetails - (*PolicyReportResult)(nil), // 141: api.v1.PolicyReportResult - (*KyvernoPolicyReportDetails)(nil), // 142: api.v1.KyvernoPolicyReportDetails - (*GatekeeperConstraintTemplateDetails)(nil), // 143: api.v1.GatekeeperConstraintTemplateDetails - (*ConstraintViolation)(nil), // 144: api.v1.ConstraintViolation - (*GatekeeperConstraintDetails)(nil), // 145: api.v1.GatekeeperConstraintDetails - (*Event)(nil), // 146: api.v1.Event - (*EventDatapointInfo)(nil), // 147: api.v1.EventDatapointInfo - (*EventDatapoint)(nil), // 148: api.v1.EventDatapoint - (*DimensionCount)(nil), // 149: api.v1.DimensionCount - (*EventGroup)(nil), // 150: api.v1.EventGroup - nil, // 151: api.v1.WorkloadItem.LabelsEntry - nil, // 152: api.v1.WorkloadItem.AnnotationsEntry - nil, // 153: api.v1.NodeInfo.PriceSourceCountsEntry - nil, // 154: api.v1.Node.LabelsEntry - nil, // 155: api.v1.Node.AnnotationsEntry - nil, // 156: api.v1.LabelSelector.MatchLabelsEntry - nil, // 157: api.v1.ResourceDetails.ServiceSelectorEntry - nil, // 158: api.v1.ResourceDetails.ScParametersEntry - nil, // 159: api.v1.ServiceDetails.SelectorEntry - nil, // 160: api.v1.ResourceRequirements.RequestsEntry - nil, // 161: api.v1.ResourceRequirements.LimitsEntry - nil, // 162: api.v1.PersistentVolumeDetails.CapacityEntry - nil, // 163: api.v1.PVVolumeSource.VolumeAttributesEntry - nil, // 164: api.v1.CSIVolumeSource.VolumeAttributesEntry - nil, // 165: api.v1.StorageClassDetails.ParametersEntry - nil, // 166: api.v1.NamespaceDetails.ConditionsEntry - nil, // 167: api.v1.NodeDetails.CapacityEntry - nil, // 168: api.v1.NodeDetails.AllocatableEntry - nil, // 169: api.v1.TopologySelectorTerm.MatchLabelsEntry - nil, // 170: api.v1.HPAMetricSelector.SelectorEntry - nil, // 171: api.v1.VPAContainerResourcePolicy.MinAllowedEntry - nil, // 172: api.v1.VPAContainerResourcePolicy.MaxAllowedEntry - nil, // 173: api.v1.VPAContainerRecommendation.TargetEntry - nil, // 174: api.v1.VPAContainerRecommendation.LowerBoundEntry - nil, // 175: api.v1.VPAContainerRecommendation.UpperBoundEntry - nil, // 176: api.v1.VPAContainerRecommendation.UncappedTargetEntry - nil, // 177: api.v1.LimitRangeItem.DefaultLimitsEntry - nil, // 178: api.v1.LimitRangeItem.DefaultRequestEntry - nil, // 179: api.v1.LimitRangeItem.MaxEntry - nil, // 180: api.v1.LimitRangeItem.MinEntry - nil, // 181: api.v1.LimitRangeItem.MaxLimitRequestRatioEntry - nil, // 182: api.v1.KedaScaledObjectTrigger.MetadataEntry - nil, // 183: api.v1.KarpenterResourceDetails.LimitsEntry - nil, // 184: api.v1.KarpenterCapacity.OtherEntry - nil, // 185: api.v1.PodDisruptionBudgetDetails.SelectorLabelsEntry - nil, // 186: api.v1.ResourceQuotaDetails.HardLimitsEntry - nil, // 187: api.v1.ResourceQuotaDetails.UsedEntry - nil, // 188: api.v1.ResourceQuotaDetails.ScopeSelectorEntry - nil, // 189: api.v1.Event.AnnotationsEntry - nil, // 190: api.v1.Event.LabelsEntry - (*timestamppb.Timestamp)(nil), // 191: google.protobuf.Timestamp - (*money.Money)(nil), // 192: google.type.Money + (NodeGroupProvisioner)(0), // 2: api.v1.NodeGroupProvisioner + (NodeManager)(0), // 3: api.v1.NodeManager + (LabelSelectorOperator)(0), // 4: api.v1.LabelSelectorOperator + (WorkloadStatusFilter)(0), // 5: api.v1.WorkloadStatusFilter + (WorkloadHealthStatusFilter)(0), // 6: api.v1.WorkloadHealthStatusFilter + (WorkloadHealthIssue)(0), // 7: api.v1.WorkloadHealthIssue + (HealthIssueMatchMode)(0), // 8: api.v1.HealthIssueMatchMode + (DisruptionBlockReason)(0), // 9: api.v1.DisruptionBlockReason + (*AuditLogEntry)(nil), // 10: api.v1.AuditLogEntry + (*Pagination)(nil), // 11: api.v1.Pagination + (*CostInfo)(nil), // 12: api.v1.CostInfo + (*ResourceMetrics)(nil), // 13: api.v1.ResourceMetrics + (*MetricPercentiles)(nil), // 14: api.v1.MetricPercentiles + (*ContainerPercentileSummary)(nil), // 15: api.v1.ContainerPercentileSummary + (*ContainerRequestLimits)(nil), // 16: api.v1.ContainerRequestLimits + (*GPUMigInstance)(nil), // 17: api.v1.GPUMigInstance + (*IndividualGPUMetric)(nil), // 18: api.v1.IndividualGPUMetric + (*ContainerIndividualGPUMetrics)(nil), // 19: api.v1.ContainerIndividualGPUMetrics + (*RuntimeProcess)(nil), // 20: api.v1.RuntimeProcess + (*ForecastResourceMetrics)(nil), // 21: api.v1.ForecastResourceMetrics + (*ResourceSummary)(nil), // 22: api.v1.ResourceSummary + (*LegacyPolicyRef)(nil), // 23: api.v1.LegacyPolicyRef + (*OptimizationStatus)(nil), // 24: api.v1.OptimizationStatus + (*WorkloadItem)(nil), // 25: api.v1.WorkloadItem + (*ContainerRuntimeInfo)(nil), // 26: api.v1.ContainerRuntimeInfo + (*NodeInfo)(nil), // 27: api.v1.NodeInfo + (*ResourceInfo)(nil), // 28: api.v1.ResourceInfo + (*Node)(nil), // 29: api.v1.Node + (*AttachedVolume)(nil), // 30: api.v1.AttachedVolume + (*NodeGroup)(nil), // 31: api.v1.NodeGroup + (*CostDataPoint)(nil), // 32: api.v1.CostDataPoint + (*ResourceDataPoint)(nil), // 33: api.v1.ResourceDataPoint + (*ContainerTimeSeries)(nil), // 34: api.v1.ContainerTimeSeries + (*SavingsData)(nil), // 35: api.v1.SavingsData + (*SavingsDataPoint)(nil), // 36: api.v1.SavingsDataPoint + (*SavingsTimeSeries)(nil), // 37: api.v1.SavingsTimeSeries + (*LabelSelectorRequirement)(nil), // 38: api.v1.LabelSelectorRequirement + (*Label)(nil), // 39: api.v1.Label + (*LabelSelector)(nil), // 40: api.v1.LabelSelector + (*RegexPattern)(nil), // 41: api.v1.RegexPattern + (*ResourceDetails)(nil), // 42: api.v1.ResourceDetails + (*PodDetails)(nil), // 43: api.v1.PodDetails + (*ContainerSummary)(nil), // 44: api.v1.ContainerSummary + (*DeploymentDetails)(nil), // 45: api.v1.DeploymentDetails + (*ContainerTemplate)(nil), // 46: api.v1.ContainerTemplate + (*DeploymentCondition)(nil), // 47: api.v1.DeploymentCondition + (*StatefulSetDetails)(nil), // 48: api.v1.StatefulSetDetails + (*VolumeClaimTemplate)(nil), // 49: api.v1.VolumeClaimTemplate + (*DaemonSetDetails)(nil), // 50: api.v1.DaemonSetDetails + (*ReplicaSetDetails)(nil), // 51: api.v1.ReplicaSetDetails + (*JobDetails)(nil), // 52: api.v1.JobDetails + (*JobCondition)(nil), // 53: api.v1.JobCondition + (*CronJobDetails)(nil), // 54: api.v1.CronJobDetails + (*ActiveJobReference)(nil), // 55: api.v1.ActiveJobReference + (*JobTemplate)(nil), // 56: api.v1.JobTemplate + (*ServiceDetails)(nil), // 57: api.v1.ServiceDetails + (*ServicePort)(nil), // 58: api.v1.ServicePort + (*LoadBalancerIngress)(nil), // 59: api.v1.LoadBalancerIngress + (*IngressDetails)(nil), // 60: api.v1.IngressDetails + (*IngressRule)(nil), // 61: api.v1.IngressRule + (*IngressPath)(nil), // 62: api.v1.IngressPath + (*IngressBackend)(nil), // 63: api.v1.IngressBackend + (*IngressTLS)(nil), // 64: api.v1.IngressTLS + (*PersistentVolumeClaimDetails)(nil), // 65: api.v1.PersistentVolumeClaimDetails + (*ResourceRequirements)(nil), // 66: api.v1.ResourceRequirements + (*VolumeNodeAffinity)(nil), // 67: api.v1.VolumeNodeAffinity + (*PVCCondition)(nil), // 68: api.v1.PVCCondition + (*PersistentVolumeDetails)(nil), // 69: api.v1.PersistentVolumeDetails + (*PVClaimReference)(nil), // 70: api.v1.PVClaimReference + (*PVVolumeSource)(nil), // 71: api.v1.PVVolumeSource + (*CSIVolumeSource)(nil), // 72: api.v1.CSIVolumeSource + (*HostPathVolumeSource)(nil), // 73: api.v1.HostPathVolumeSource + (*NFSVolumeSource)(nil), // 74: api.v1.NFSVolumeSource + (*StorageClassDetails)(nil), // 75: api.v1.StorageClassDetails + (*NamespaceDetails)(nil), // 76: api.v1.NamespaceDetails + (*NodeDetails)(nil), // 77: api.v1.NodeDetails + (*NodeTaint)(nil), // 78: api.v1.NodeTaint + (*NodeAddress)(nil), // 79: api.v1.NodeAddress + (*NodeCondition)(nil), // 80: api.v1.NodeCondition + (*NodeSystemInfo)(nil), // 81: api.v1.NodeSystemInfo + (*NodeImage)(nil), // 82: api.v1.NodeImage + (*TopologySelector)(nil), // 83: api.v1.TopologySelector + (*TopologySelectorTerm)(nil), // 84: api.v1.TopologySelectorTerm + (*TolerationInfo)(nil), // 85: api.v1.TolerationInfo + (*NodeSelector)(nil), // 86: api.v1.NodeSelector + (*NodeSelectorTerm)(nil), // 87: api.v1.NodeSelectorTerm + (*NodeSelectorRequirement)(nil), // 88: api.v1.NodeSelectorRequirement + (*HPADetails)(nil), // 89: api.v1.HPADetails + (*ScaleTargetRef)(nil), // 90: api.v1.ScaleTargetRef + (*HPAMetric)(nil), // 91: api.v1.HPAMetric + (*HPAResourceMetric)(nil), // 92: api.v1.HPAResourceMetric + (*HPAPodsMetric)(nil), // 93: api.v1.HPAPodsMetric + (*HPAObjectMetric)(nil), // 94: api.v1.HPAObjectMetric + (*HPAExternalMetric)(nil), // 95: api.v1.HPAExternalMetric + (*HPAMetricSelector)(nil), // 96: api.v1.HPAMetricSelector + (*HPAObjectReference)(nil), // 97: api.v1.HPAObjectReference + (*HPACurrentMetric)(nil), // 98: api.v1.HPACurrentMetric + (*HPACurrentResourceMetric)(nil), // 99: api.v1.HPACurrentResourceMetric + (*HPACurrentPodsMetric)(nil), // 100: api.v1.HPACurrentPodsMetric + (*HPACurrentObjectMetric)(nil), // 101: api.v1.HPACurrentObjectMetric + (*HPACurrentExternalMetric)(nil), // 102: api.v1.HPACurrentExternalMetric + (*HPACondition)(nil), // 103: api.v1.HPACondition + (*HPABehavior)(nil), // 104: api.v1.HPABehavior + (*HPAScalingRules)(nil), // 105: api.v1.HPAScalingRules + (*HPAScalingPolicy)(nil), // 106: api.v1.HPAScalingPolicy + (*VPADetails)(nil), // 107: api.v1.VPADetails + (*VPATargetRef)(nil), // 108: api.v1.VPATargetRef + (*VPAUpdatePolicy)(nil), // 109: api.v1.VPAUpdatePolicy + (*VPAResourcePolicy)(nil), // 110: api.v1.VPAResourcePolicy + (*VPAContainerResourcePolicy)(nil), // 111: api.v1.VPAContainerResourcePolicy + (*VPARecommendation)(nil), // 112: api.v1.VPARecommendation + (*VPAContainerRecommendation)(nil), // 113: api.v1.VPAContainerRecommendation + (*VPACondition)(nil), // 114: api.v1.VPACondition + (*LimitRangeDetails)(nil), // 115: api.v1.LimitRangeDetails + (*LimitRangeItem)(nil), // 116: api.v1.LimitRangeItem + (*ServiceAccountDetails)(nil), // 117: api.v1.ServiceAccountDetails + (*RoleDetails)(nil), // 118: api.v1.RoleDetails + (*RoleRule)(nil), // 119: api.v1.RoleRule + (*RoleBindingDetails)(nil), // 120: api.v1.RoleBindingDetails + (*RoleBindingSubject)(nil), // 121: api.v1.RoleBindingSubject + (*RoleReference)(nil), // 122: api.v1.RoleReference + (*KedaScaledObjectDetails)(nil), // 123: api.v1.KedaScaledObjectDetails + (*KedaScaledObjectTrigger)(nil), // 124: api.v1.KedaScaledObjectTrigger + (*KedaScaledObjectCondition)(nil), // 125: api.v1.KedaScaledObjectCondition + (*KarpenterResourceDetails)(nil), // 126: api.v1.KarpenterResourceDetails + (*KarpenterResourceCondition)(nil), // 127: api.v1.KarpenterResourceCondition + (*KarpenterCapacity)(nil), // 128: api.v1.KarpenterCapacity + (*KarpenterRequirement)(nil), // 129: api.v1.KarpenterRequirement + (*ResourceFieldFilter)(nil), // 130: api.v1.ResourceFieldFilter + (*ResourceFilterFieldOptions)(nil), // 131: api.v1.ResourceFilterFieldOptions + (*WorkloadFilters)(nil), // 132: api.v1.WorkloadFilters + (*WorkloadHealthFilter)(nil), // 133: api.v1.WorkloadHealthFilter + (*PodDisruptionBudgetDetails)(nil), // 134: api.v1.PodDisruptionBudgetDetails + (*PodDisruptionBudgetCondition)(nil), // 135: api.v1.PodDisruptionBudgetCondition + (*ResourceQuotaDetails)(nil), // 136: api.v1.ResourceQuotaDetails + (*ResourceQuotaCondition)(nil), // 137: api.v1.ResourceQuotaCondition + (*VolcanoJobDetails)(nil), // 138: api.v1.VolcanoJobDetails + (*VolcanoJobCondition)(nil), // 139: api.v1.VolcanoJobCondition + (*VolcanoTask)(nil), // 140: api.v1.VolcanoTask + (*SparkApplicationDetails)(nil), // 141: api.v1.SparkApplicationDetails + (*SparkDriverInfo)(nil), // 142: api.v1.SparkDriverInfo + (*SparkRestartPolicyInfo)(nil), // 143: api.v1.SparkRestartPolicyInfo + (*SparkDynamicAllocationInfo)(nil), // 144: api.v1.SparkDynamicAllocationInfo + (*ScheduledSparkApplicationDetails)(nil), // 145: api.v1.ScheduledSparkApplicationDetails + (*KyvernoPolicyDetails)(nil), // 146: api.v1.KyvernoPolicyDetails + (*PolicyReportResult)(nil), // 147: api.v1.PolicyReportResult + (*KyvernoPolicyReportDetails)(nil), // 148: api.v1.KyvernoPolicyReportDetails + (*GatekeeperConstraintTemplateDetails)(nil), // 149: api.v1.GatekeeperConstraintTemplateDetails + (*ConstraintViolation)(nil), // 150: api.v1.ConstraintViolation + (*GatekeeperConstraintDetails)(nil), // 151: api.v1.GatekeeperConstraintDetails + (*ArgoApplicationDetails)(nil), // 152: api.v1.ArgoApplicationDetails + (*ArgoApplicationSetDetails)(nil), // 153: api.v1.ArgoApplicationSetDetails + (*CustomResourceDefinitionVersion)(nil), // 154: api.v1.CustomResourceDefinitionVersion + (*CustomResourceDefinitionDetails)(nil), // 155: api.v1.CustomResourceDefinitionDetails + (*FluxGitRepositoryDetails)(nil), // 156: api.v1.FluxGitRepositoryDetails + (*FluxOCIRepositoryDetails)(nil), // 157: api.v1.FluxOCIRepositoryDetails + (*FluxHelmRepositoryDetails)(nil), // 158: api.v1.FluxHelmRepositoryDetails + (*FluxHelmChartDetails)(nil), // 159: api.v1.FluxHelmChartDetails + (*FluxBucketDetails)(nil), // 160: api.v1.FluxBucketDetails + (*FluxKustomizationDetails)(nil), // 161: api.v1.FluxKustomizationDetails + (*FluxHelmReleaseDetails)(nil), // 162: api.v1.FluxHelmReleaseDetails + (*FluxGenericDetails)(nil), // 163: api.v1.FluxGenericDetails + (*FlaggerObjectReference)(nil), // 164: api.v1.FlaggerObjectReference + (*FlaggerGatewayRef)(nil), // 165: api.v1.FlaggerGatewayRef + (*FlaggerCanaryMetric)(nil), // 166: api.v1.FlaggerCanaryMetric + (*FlaggerCanaryAlert)(nil), // 167: api.v1.FlaggerCanaryAlert + (*FlaggerCanaryWebhook)(nil), // 168: api.v1.FlaggerCanaryWebhook + (*FlaggerCanaryDetails)(nil), // 169: api.v1.FlaggerCanaryDetails + (*FlaggerMetricTemplateDetails)(nil), // 170: api.v1.FlaggerMetricTemplateDetails + (*FlaggerAlertProviderDetails)(nil), // 171: api.v1.FlaggerAlertProviderDetails + (*Event)(nil), // 172: api.v1.Event + (*EventDatapointInfo)(nil), // 173: api.v1.EventDatapointInfo + (*EventDatapoint)(nil), // 174: api.v1.EventDatapoint + (*DimensionCount)(nil), // 175: api.v1.DimensionCount + (*EventGroup)(nil), // 176: api.v1.EventGroup + nil, // 177: api.v1.WorkloadItem.LabelsEntry + nil, // 178: api.v1.WorkloadItem.AnnotationsEntry + nil, // 179: api.v1.NodeInfo.PriceSourceCountsEntry + nil, // 180: api.v1.Node.LabelsEntry + nil, // 181: api.v1.Node.AnnotationsEntry + nil, // 182: api.v1.LabelSelector.MatchLabelsEntry + nil, // 183: api.v1.ResourceDetails.ServiceSelectorEntry + nil, // 184: api.v1.ResourceDetails.ScParametersEntry + nil, // 185: api.v1.ServiceDetails.SelectorEntry + nil, // 186: api.v1.ResourceRequirements.RequestsEntry + nil, // 187: api.v1.ResourceRequirements.LimitsEntry + nil, // 188: api.v1.PersistentVolumeDetails.CapacityEntry + nil, // 189: api.v1.PVVolumeSource.VolumeAttributesEntry + nil, // 190: api.v1.CSIVolumeSource.VolumeAttributesEntry + nil, // 191: api.v1.StorageClassDetails.ParametersEntry + nil, // 192: api.v1.NamespaceDetails.ConditionsEntry + nil, // 193: api.v1.NodeDetails.CapacityEntry + nil, // 194: api.v1.NodeDetails.AllocatableEntry + nil, // 195: api.v1.TopologySelectorTerm.MatchLabelsEntry + nil, // 196: api.v1.HPAMetricSelector.SelectorEntry + nil, // 197: api.v1.VPAContainerResourcePolicy.MinAllowedEntry + nil, // 198: api.v1.VPAContainerResourcePolicy.MaxAllowedEntry + nil, // 199: api.v1.VPAContainerRecommendation.TargetEntry + nil, // 200: api.v1.VPAContainerRecommendation.LowerBoundEntry + nil, // 201: api.v1.VPAContainerRecommendation.UpperBoundEntry + nil, // 202: api.v1.VPAContainerRecommendation.UncappedTargetEntry + nil, // 203: api.v1.LimitRangeItem.DefaultLimitsEntry + nil, // 204: api.v1.LimitRangeItem.DefaultRequestEntry + nil, // 205: api.v1.LimitRangeItem.MaxEntry + nil, // 206: api.v1.LimitRangeItem.MinEntry + nil, // 207: api.v1.LimitRangeItem.MaxLimitRequestRatioEntry + nil, // 208: api.v1.KedaScaledObjectTrigger.MetadataEntry + nil, // 209: api.v1.KarpenterResourceDetails.LimitsEntry + nil, // 210: api.v1.KarpenterCapacity.OtherEntry + nil, // 211: api.v1.PodDisruptionBudgetDetails.SelectorLabelsEntry + nil, // 212: api.v1.ResourceQuotaDetails.HardLimitsEntry + nil, // 213: api.v1.ResourceQuotaDetails.UsedEntry + nil, // 214: api.v1.ResourceQuotaDetails.ScopeSelectorEntry + nil, // 215: api.v1.Event.AnnotationsEntry + nil, // 216: api.v1.Event.LabelsEntry + (*timestamppb.Timestamp)(nil), // 217: google.protobuf.Timestamp + (*money.Money)(nil), // 218: google.type.Money } var file_api_v1_common_proto_depIdxs = []int32{ 0, // 0: api.v1.AuditLogEntry.workload_type:type_name -> api.v1.K8sObjectKind - 191, // 1: api.v1.AuditLogEntry.created_at:type_name -> google.protobuf.Timestamp - 191, // 2: api.v1.AuditLogEntry.updated_at:type_name -> google.protobuf.Timestamp + 217, // 1: api.v1.AuditLogEntry.created_at:type_name -> google.protobuf.Timestamp + 217, // 2: api.v1.AuditLogEntry.updated_at:type_name -> google.protobuf.Timestamp 1, // 3: api.v1.Pagination.order_by:type_name -> api.v1.OrderByEnum - 192, // 4: api.v1.CostInfo.money_cpu_cost_per_hour:type_name -> google.type.Money - 192, // 5: api.v1.CostInfo.money_memory_cost_per_hour:type_name -> google.type.Money - 192, // 6: api.v1.CostInfo.money_total_cost_per_hour:type_name -> google.type.Money - 192, // 7: api.v1.CostInfo.money_total_cost_per_month:type_name -> google.type.Money - 192, // 8: api.v1.CostInfo.money_total_cost_per_year:type_name -> google.type.Money - 192, // 9: api.v1.CostInfo.money_optimized_cpu_cost_per_hour:type_name -> google.type.Money - 192, // 10: api.v1.CostInfo.money_optimized_memory_cost_per_hour:type_name -> google.type.Money - 192, // 11: api.v1.CostInfo.money_optimized_total_cost_per_hour:type_name -> google.type.Money - 192, // 12: api.v1.CostInfo.money_optimized_total_cost_per_month:type_name -> google.type.Money - 192, // 13: api.v1.CostInfo.money_optimized_total_cost_per_year:type_name -> google.type.Money - 192, // 14: api.v1.CostInfo.money_cpu_cost_for_time_period:type_name -> google.type.Money - 192, // 15: api.v1.CostInfo.money_memory_cost_for_time_period:type_name -> google.type.Money - 192, // 16: api.v1.CostInfo.money_total_cost_for_time_period:type_name -> google.type.Money - 192, // 17: api.v1.CostInfo.money_network_cost_for_time_period:type_name -> google.type.Money - 192, // 18: api.v1.CostInfo.money_optimized_cpu_cost_for_time_period:type_name -> google.type.Money - 192, // 19: api.v1.CostInfo.money_optimized_memory_cost_for_time_period:type_name -> google.type.Money - 192, // 20: api.v1.CostInfo.money_optimized_total_cost_for_time_period:type_name -> google.type.Money - 192, // 21: api.v1.CostInfo.money_optimized_gpu_cost_for_time_period:type_name -> google.type.Money - 192, // 22: api.v1.CostInfo.money_gpu_cost_for_time_period:type_name -> google.type.Money - 192, // 23: api.v1.CostInfo.money_gpu_cost_per_hour:type_name -> google.type.Money - 192, // 24: api.v1.CostInfo.money_optimized_gpu_cost_per_hour:type_name -> google.type.Money - 192, // 25: api.v1.CostInfo.money_cpu_cost_per_vcpu_per_hour:type_name -> google.type.Money - 192, // 26: api.v1.CostInfo.money_memory_cost_per_gib_per_hour:type_name -> google.type.Money - 9, // 27: api.v1.ContainerPercentileSummary.cpu_usage:type_name -> api.v1.MetricPercentiles - 9, // 28: api.v1.ContainerPercentileSummary.memory_usage:type_name -> api.v1.MetricPercentiles - 9, // 29: api.v1.ContainerPercentileSummary.cpu_request:type_name -> api.v1.MetricPercentiles - 9, // 30: api.v1.ContainerPercentileSummary.memory_request:type_name -> api.v1.MetricPercentiles - 9, // 31: api.v1.ContainerPercentileSummary.cpu_limit:type_name -> api.v1.MetricPercentiles - 9, // 32: api.v1.ContainerPercentileSummary.memory_limit:type_name -> api.v1.MetricPercentiles - 9, // 33: api.v1.ContainerPercentileSummary.gpu_usage:type_name -> api.v1.MetricPercentiles - 9, // 34: api.v1.ContainerPercentileSummary.gpu_vram_usage:type_name -> api.v1.MetricPercentiles - 9, // 35: api.v1.ContainerPercentileSummary.network_receive_bytes:type_name -> api.v1.MetricPercentiles - 9, // 36: api.v1.ContainerPercentileSummary.network_transmit_bytes:type_name -> api.v1.MetricPercentiles - 9, // 37: api.v1.ContainerPercentileSummary.fs_read_bytes:type_name -> api.v1.MetricPercentiles - 9, // 38: api.v1.ContainerPercentileSummary.fs_write_bytes:type_name -> api.v1.MetricPercentiles - 15, // 39: api.v1.ContainerRequestLimits.runtime_processes:type_name -> api.v1.RuntimeProcess - 13, // 40: api.v1.ContainerIndividualGPUMetrics.instances:type_name -> api.v1.IndividualGPUMetric - 191, // 41: api.v1.ContainerIndividualGPUMetrics.collected_at:type_name -> google.protobuf.Timestamp - 191, // 42: api.v1.ForecastResourceMetrics.timestamp:type_name -> google.protobuf.Timestamp - 18, // 43: api.v1.OptimizationStatus.legacy_policies:type_name -> api.v1.LegacyPolicyRef - 20, // 44: api.v1.WorkloadItem.children:type_name -> api.v1.WorkloadItem - 151, // 45: api.v1.WorkloadItem.labels:type_name -> api.v1.WorkloadItem.LabelsEntry - 152, // 46: api.v1.WorkloadItem.annotations:type_name -> api.v1.WorkloadItem.AnnotationsEntry - 8, // 47: api.v1.WorkloadItem.resource_metrics:type_name -> api.v1.ResourceMetrics - 7, // 48: api.v1.WorkloadItem.cost_info:type_name -> api.v1.CostInfo - 27, // 49: api.v1.WorkloadItem.cost_data_points:type_name -> api.v1.CostDataPoint - 28, // 50: api.v1.WorkloadItem.resource_data_points:type_name -> api.v1.ResourceDataPoint - 191, // 51: api.v1.WorkloadItem.created_at:type_name -> google.protobuf.Timestamp - 191, // 52: api.v1.WorkloadItem.deleted_at:type_name -> google.protobuf.Timestamp - 37, // 53: api.v1.WorkloadItem.resource_details:type_name -> api.v1.ResourceDetails - 19, // 54: api.v1.WorkloadItem.optimization_status:type_name -> api.v1.OptimizationStatus - 153, // 55: api.v1.NodeInfo.price_source_counts:type_name -> api.v1.NodeInfo.PriceSourceCountsEntry - 8, // 56: api.v1.Node.resource_metrics:type_name -> api.v1.ResourceMetrics - 7, // 57: api.v1.Node.cost_info:type_name -> api.v1.CostInfo - 21, // 58: api.v1.Node.container_runtime:type_name -> api.v1.ContainerRuntimeInfo - 37, // 59: api.v1.Node.resource_details:type_name -> api.v1.ResourceDetails - 20, // 60: api.v1.Node.children:type_name -> api.v1.WorkloadItem - 154, // 61: api.v1.Node.labels:type_name -> api.v1.Node.LabelsEntry - 155, // 62: api.v1.Node.annotations:type_name -> api.v1.Node.AnnotationsEntry - 192, // 63: api.v1.Node.money_price_per_hour:type_name -> google.type.Money - 192, // 64: api.v1.Node.money_eff_period_total_cost:type_name -> google.type.Money - 192, // 65: api.v1.Node.money_price_per_vcpu:type_name -> google.type.Money - 192, // 66: api.v1.Node.money_price_per_gib:type_name -> google.type.Money - 192, // 67: api.v1.Node.money_price_per_gpu:type_name -> google.type.Money - 192, // 68: api.v1.Node.money_cpu_price:type_name -> google.type.Money - 192, // 69: api.v1.Node.money_memory_price:type_name -> google.type.Money - 192, // 70: api.v1.Node.money_gpu_price:type_name -> google.type.Money - 192, // 71: api.v1.Node.money_on_demand_price_per_hour:type_name -> google.type.Money - 25, // 72: api.v1.Node.volumes_attached:type_name -> api.v1.AttachedVolume - 191, // 73: api.v1.Node.last_seen:type_name -> google.protobuf.Timestamp - 191, // 74: api.v1.Node.collected_at:type_name -> google.protobuf.Timestamp - 191, // 75: api.v1.Node.deletion_timestamp:type_name -> google.protobuf.Timestamp - 24, // 76: api.v1.NodeGroup.nodes:type_name -> api.v1.Node - 8, // 77: api.v1.NodeGroup.resource_metrics:type_name -> api.v1.ResourceMetrics - 7, // 78: api.v1.NodeGroup.cost_info:type_name -> api.v1.CostInfo - 22, // 79: api.v1.NodeGroup.node_info:type_name -> api.v1.NodeInfo - 23, // 80: api.v1.NodeGroup.resource_info:type_name -> api.v1.ResourceInfo - 27, // 81: api.v1.NodeGroup.cost_data_points:type_name -> api.v1.CostDataPoint - 28, // 82: api.v1.NodeGroup.resource_data_points:type_name -> api.v1.ResourceDataPoint - 7, // 83: api.v1.CostDataPoint.cost_info:type_name -> api.v1.CostInfo - 8, // 84: api.v1.ResourceDataPoint.resource_metrics:type_name -> api.v1.ResourceMetrics - 28, // 85: api.v1.ContainerTimeSeries.resource_data_points:type_name -> api.v1.ResourceDataPoint - 30, // 86: api.v1.SavingsDataPoint.savings_data:type_name -> api.v1.SavingsData - 31, // 87: api.v1.SavingsTimeSeries.savings_datapoints:type_name -> api.v1.SavingsDataPoint - 2, // 88: api.v1.LabelSelectorRequirement.operator:type_name -> api.v1.LabelSelectorOperator - 34, // 89: api.v1.LabelSelector.labels:type_name -> api.v1.Label - 156, // 90: api.v1.LabelSelector.match_labels:type_name -> api.v1.LabelSelector.MatchLabelsEntry - 33, // 91: api.v1.LabelSelector.match_expressions:type_name -> api.v1.LabelSelectorRequirement - 38, // 92: api.v1.ResourceDetails.pod_details:type_name -> api.v1.PodDetails - 40, // 93: api.v1.ResourceDetails.deployment_details:type_name -> api.v1.DeploymentDetails - 43, // 94: api.v1.ResourceDetails.stateful_set_details:type_name -> api.v1.StatefulSetDetails - 45, // 95: api.v1.ResourceDetails.daemon_set_details:type_name -> api.v1.DaemonSetDetails - 46, // 96: api.v1.ResourceDetails.replica_set_details:type_name -> api.v1.ReplicaSetDetails - 47, // 97: api.v1.ResourceDetails.job_details:type_name -> api.v1.JobDetails - 49, // 98: api.v1.ResourceDetails.cron_job_details:type_name -> api.v1.CronJobDetails - 52, // 99: api.v1.ResourceDetails.service_details:type_name -> api.v1.ServiceDetails - 55, // 100: api.v1.ResourceDetails.ingress_details:type_name -> api.v1.IngressDetails - 60, // 101: api.v1.ResourceDetails.pvc_details:type_name -> api.v1.PersistentVolumeClaimDetails - 64, // 102: api.v1.ResourceDetails.pv_details:type_name -> api.v1.PersistentVolumeDetails - 70, // 103: api.v1.ResourceDetails.sc_details:type_name -> api.v1.StorageClassDetails - 71, // 104: api.v1.ResourceDetails.ns_details:type_name -> api.v1.NamespaceDetails - 72, // 105: api.v1.ResourceDetails.node_details:type_name -> api.v1.NodeDetails - 84, // 106: api.v1.ResourceDetails.hpa_details:type_name -> api.v1.HPADetails - 102, // 107: api.v1.ResourceDetails.vpa_details:type_name -> api.v1.VPADetails - 110, // 108: api.v1.ResourceDetails.limit_range_details:type_name -> api.v1.LimitRangeDetails - 112, // 109: api.v1.ResourceDetails.service_account_details:type_name -> api.v1.ServiceAccountDetails - 113, // 110: api.v1.ResourceDetails.role_details:type_name -> api.v1.RoleDetails - 115, // 111: api.v1.ResourceDetails.role_binding_details:type_name -> api.v1.RoleBindingDetails - 118, // 112: api.v1.ResourceDetails.keda_scaled_object_details:type_name -> api.v1.KedaScaledObjectDetails - 121, // 113: api.v1.ResourceDetails.karpenter_resource_details:type_name -> api.v1.KarpenterResourceDetails - 128, // 114: api.v1.ResourceDetails.pod_disruption_budget_details:type_name -> api.v1.PodDisruptionBudgetDetails - 130, // 115: api.v1.ResourceDetails.resource_quota_details:type_name -> api.v1.ResourceQuotaDetails - 132, // 116: api.v1.ResourceDetails.volcano_job_details:type_name -> api.v1.VolcanoJobDetails - 135, // 117: api.v1.ResourceDetails.spark_application_details:type_name -> api.v1.SparkApplicationDetails - 139, // 118: api.v1.ResourceDetails.scheduled_spark_application_details:type_name -> api.v1.ScheduledSparkApplicationDetails - 140, // 119: api.v1.ResourceDetails.kyverno_policy_details:type_name -> api.v1.KyvernoPolicyDetails - 142, // 120: api.v1.ResourceDetails.kyverno_policy_report_details:type_name -> api.v1.KyvernoPolicyReportDetails - 143, // 121: api.v1.ResourceDetails.gatekeeper_constraint_template_details:type_name -> api.v1.GatekeeperConstraintTemplateDetails - 145, // 122: api.v1.ResourceDetails.gatekeeper_constraint_details:type_name -> api.v1.GatekeeperConstraintDetails - 53, // 123: api.v1.ResourceDetails.service_ports:type_name -> api.v1.ServicePort - 157, // 124: api.v1.ResourceDetails.service_selector:type_name -> api.v1.ResourceDetails.ServiceSelectorEntry - 158, // 125: api.v1.ResourceDetails.sc_parameters:type_name -> api.v1.ResourceDetails.ScParametersEntry - 73, // 126: api.v1.ResourceDetails.node_taints:type_name -> api.v1.NodeTaint - 39, // 127: api.v1.PodDetails.containers:type_name -> api.v1.ContainerSummary - 41, // 128: api.v1.DeploymentDetails.containers:type_name -> api.v1.ContainerTemplate - 42, // 129: api.v1.DeploymentDetails.conditions:type_name -> api.v1.DeploymentCondition - 41, // 130: api.v1.StatefulSetDetails.containers:type_name -> api.v1.ContainerTemplate - 44, // 131: api.v1.StatefulSetDetails.volume_claim_templates:type_name -> api.v1.VolumeClaimTemplate - 41, // 132: api.v1.DaemonSetDetails.containers:type_name -> api.v1.ContainerTemplate - 80, // 133: api.v1.DaemonSetDetails.tolerations:type_name -> api.v1.TolerationInfo - 81, // 134: api.v1.DaemonSetDetails.node_selector:type_name -> api.v1.NodeSelector - 41, // 135: api.v1.ReplicaSetDetails.containers:type_name -> api.v1.ContainerTemplate - 35, // 136: api.v1.ReplicaSetDetails.selector:type_name -> api.v1.LabelSelector - 41, // 137: api.v1.JobDetails.containers:type_name -> api.v1.ContainerTemplate - 48, // 138: api.v1.JobDetails.conditions:type_name -> api.v1.JobCondition - 35, // 139: api.v1.JobDetails.selector:type_name -> api.v1.LabelSelector - 41, // 140: api.v1.CronJobDetails.containers:type_name -> api.v1.ContainerTemplate - 50, // 141: api.v1.CronJobDetails.active_jobs:type_name -> api.v1.ActiveJobReference - 51, // 142: api.v1.CronJobDetails.job_template:type_name -> api.v1.JobTemplate - 41, // 143: api.v1.JobTemplate.containers:type_name -> api.v1.ContainerTemplate - 53, // 144: api.v1.ServiceDetails.ports:type_name -> api.v1.ServicePort - 159, // 145: api.v1.ServiceDetails.selector:type_name -> api.v1.ServiceDetails.SelectorEntry - 54, // 146: api.v1.ServiceDetails.load_balancer_ingress:type_name -> api.v1.LoadBalancerIngress - 56, // 147: api.v1.IngressDetails.rules:type_name -> api.v1.IngressRule - 59, // 148: api.v1.IngressDetails.tls:type_name -> api.v1.IngressTLS - 58, // 149: api.v1.IngressDetails.default_backend:type_name -> api.v1.IngressBackend - 54, // 150: api.v1.IngressDetails.load_balancer_ingress:type_name -> api.v1.LoadBalancerIngress - 57, // 151: api.v1.IngressRule.paths:type_name -> api.v1.IngressPath - 58, // 152: api.v1.IngressPath.backend:type_name -> api.v1.IngressBackend - 61, // 153: api.v1.PersistentVolumeClaimDetails.resource_requirements:type_name -> api.v1.ResourceRequirements - 35, // 154: api.v1.PersistentVolumeClaimDetails.selector:type_name -> api.v1.LabelSelector - 62, // 155: api.v1.PersistentVolumeClaimDetails.volume_node_affinity:type_name -> api.v1.VolumeNodeAffinity - 63, // 156: api.v1.PersistentVolumeClaimDetails.conditions:type_name -> api.v1.PVCCondition - 160, // 157: api.v1.ResourceRequirements.requests:type_name -> api.v1.ResourceRequirements.RequestsEntry - 161, // 158: api.v1.ResourceRequirements.limits:type_name -> api.v1.ResourceRequirements.LimitsEntry - 83, // 159: api.v1.VolumeNodeAffinity.required:type_name -> api.v1.NodeSelectorRequirement - 83, // 160: api.v1.VolumeNodeAffinity.preferred:type_name -> api.v1.NodeSelectorRequirement - 162, // 161: api.v1.PersistentVolumeDetails.capacity:type_name -> api.v1.PersistentVolumeDetails.CapacityEntry - 65, // 162: api.v1.PersistentVolumeDetails.claim_ref:type_name -> api.v1.PVClaimReference - 66, // 163: api.v1.PersistentVolumeDetails.volume_source:type_name -> api.v1.PVVolumeSource - 81, // 164: api.v1.PersistentVolumeDetails.node_affinity:type_name -> api.v1.NodeSelector - 67, // 165: api.v1.PVVolumeSource.csi:type_name -> api.v1.CSIVolumeSource - 68, // 166: api.v1.PVVolumeSource.host_path:type_name -> api.v1.HostPathVolumeSource - 69, // 167: api.v1.PVVolumeSource.nfs:type_name -> api.v1.NFSVolumeSource - 163, // 168: api.v1.PVVolumeSource.volume_attributes:type_name -> api.v1.PVVolumeSource.VolumeAttributesEntry - 164, // 169: api.v1.CSIVolumeSource.volume_attributes:type_name -> api.v1.CSIVolumeSource.VolumeAttributesEntry - 165, // 170: api.v1.StorageClassDetails.parameters:type_name -> api.v1.StorageClassDetails.ParametersEntry - 78, // 171: api.v1.StorageClassDetails.allowed_topologies:type_name -> api.v1.TopologySelector - 166, // 172: api.v1.NamespaceDetails.conditions:type_name -> api.v1.NamespaceDetails.ConditionsEntry - 74, // 173: api.v1.NodeDetails.addresses:type_name -> api.v1.NodeAddress - 75, // 174: api.v1.NodeDetails.conditions:type_name -> api.v1.NodeCondition - 76, // 175: api.v1.NodeDetails.system_info:type_name -> api.v1.NodeSystemInfo - 167, // 176: api.v1.NodeDetails.capacity:type_name -> api.v1.NodeDetails.CapacityEntry - 168, // 177: api.v1.NodeDetails.allocatable:type_name -> api.v1.NodeDetails.AllocatableEntry - 77, // 178: api.v1.NodeDetails.images:type_name -> api.v1.NodeImage - 79, // 179: api.v1.TopologySelector.match_label_expressions:type_name -> api.v1.TopologySelectorTerm - 169, // 180: api.v1.TopologySelectorTerm.match_labels:type_name -> api.v1.TopologySelectorTerm.MatchLabelsEntry - 82, // 181: api.v1.NodeSelector.terms:type_name -> api.v1.NodeSelectorTerm - 83, // 182: api.v1.NodeSelectorTerm.match_expressions:type_name -> api.v1.NodeSelectorRequirement - 83, // 183: api.v1.NodeSelectorTerm.match_fields:type_name -> api.v1.NodeSelectorRequirement - 85, // 184: api.v1.HPADetails.scale_target_ref:type_name -> api.v1.ScaleTargetRef - 86, // 185: api.v1.HPADetails.metrics:type_name -> api.v1.HPAMetric - 98, // 186: api.v1.HPADetails.conditions:type_name -> api.v1.HPACondition - 99, // 187: api.v1.HPADetails.behavior:type_name -> api.v1.HPABehavior - 93, // 188: api.v1.HPADetails.current_metrics:type_name -> api.v1.HPACurrentMetric - 87, // 189: api.v1.HPAMetric.resource:type_name -> api.v1.HPAResourceMetric - 88, // 190: api.v1.HPAMetric.pods:type_name -> api.v1.HPAPodsMetric - 89, // 191: api.v1.HPAMetric.object:type_name -> api.v1.HPAObjectMetric - 90, // 192: api.v1.HPAMetric.external:type_name -> api.v1.HPAExternalMetric - 91, // 193: api.v1.HPAPodsMetric.metric:type_name -> api.v1.HPAMetricSelector - 92, // 194: api.v1.HPAObjectMetric.described_object:type_name -> api.v1.HPAObjectReference - 91, // 195: api.v1.HPAObjectMetric.metric:type_name -> api.v1.HPAMetricSelector - 91, // 196: api.v1.HPAExternalMetric.metric:type_name -> api.v1.HPAMetricSelector - 170, // 197: api.v1.HPAMetricSelector.selector:type_name -> api.v1.HPAMetricSelector.SelectorEntry - 94, // 198: api.v1.HPACurrentMetric.resource:type_name -> api.v1.HPACurrentResourceMetric - 95, // 199: api.v1.HPACurrentMetric.pods:type_name -> api.v1.HPACurrentPodsMetric - 96, // 200: api.v1.HPACurrentMetric.object:type_name -> api.v1.HPACurrentObjectMetric - 97, // 201: api.v1.HPACurrentMetric.external:type_name -> api.v1.HPACurrentExternalMetric - 91, // 202: api.v1.HPACurrentPodsMetric.metric:type_name -> api.v1.HPAMetricSelector - 92, // 203: api.v1.HPACurrentObjectMetric.described_object:type_name -> api.v1.HPAObjectReference - 91, // 204: api.v1.HPACurrentObjectMetric.metric:type_name -> api.v1.HPAMetricSelector - 91, // 205: api.v1.HPACurrentExternalMetric.metric:type_name -> api.v1.HPAMetricSelector - 100, // 206: api.v1.HPABehavior.scale_up:type_name -> api.v1.HPAScalingRules - 100, // 207: api.v1.HPABehavior.scale_down:type_name -> api.v1.HPAScalingRules - 101, // 208: api.v1.HPAScalingRules.policies:type_name -> api.v1.HPAScalingPolicy - 103, // 209: api.v1.VPADetails.target_ref:type_name -> api.v1.VPATargetRef - 104, // 210: api.v1.VPADetails.update_policy:type_name -> api.v1.VPAUpdatePolicy - 105, // 211: api.v1.VPADetails.resource_policy:type_name -> api.v1.VPAResourcePolicy - 107, // 212: api.v1.VPADetails.recommendation:type_name -> api.v1.VPARecommendation - 109, // 213: api.v1.VPADetails.conditions:type_name -> api.v1.VPACondition - 106, // 214: api.v1.VPAResourcePolicy.container_policies:type_name -> api.v1.VPAContainerResourcePolicy - 171, // 215: api.v1.VPAContainerResourcePolicy.min_allowed:type_name -> api.v1.VPAContainerResourcePolicy.MinAllowedEntry - 172, // 216: api.v1.VPAContainerResourcePolicy.max_allowed:type_name -> api.v1.VPAContainerResourcePolicy.MaxAllowedEntry - 108, // 217: api.v1.VPARecommendation.container_recommendations:type_name -> api.v1.VPAContainerRecommendation - 173, // 218: api.v1.VPAContainerRecommendation.target:type_name -> api.v1.VPAContainerRecommendation.TargetEntry - 174, // 219: api.v1.VPAContainerRecommendation.lower_bound:type_name -> api.v1.VPAContainerRecommendation.LowerBoundEntry - 175, // 220: api.v1.VPAContainerRecommendation.upper_bound:type_name -> api.v1.VPAContainerRecommendation.UpperBoundEntry - 176, // 221: api.v1.VPAContainerRecommendation.uncapped_target:type_name -> api.v1.VPAContainerRecommendation.UncappedTargetEntry - 111, // 222: api.v1.LimitRangeDetails.limits:type_name -> api.v1.LimitRangeItem - 177, // 223: api.v1.LimitRangeItem.default_limits:type_name -> api.v1.LimitRangeItem.DefaultLimitsEntry - 178, // 224: api.v1.LimitRangeItem.default_request:type_name -> api.v1.LimitRangeItem.DefaultRequestEntry - 179, // 225: api.v1.LimitRangeItem.max:type_name -> api.v1.LimitRangeItem.MaxEntry - 180, // 226: api.v1.LimitRangeItem.min:type_name -> api.v1.LimitRangeItem.MinEntry - 181, // 227: api.v1.LimitRangeItem.max_limit_request_ratio:type_name -> api.v1.LimitRangeItem.MaxLimitRequestRatioEntry - 114, // 228: api.v1.RoleDetails.rules:type_name -> api.v1.RoleRule - 116, // 229: api.v1.RoleBindingDetails.subjects:type_name -> api.v1.RoleBindingSubject - 117, // 230: api.v1.RoleBindingDetails.role_ref:type_name -> api.v1.RoleReference - 119, // 231: api.v1.KedaScaledObjectDetails.triggers:type_name -> api.v1.KedaScaledObjectTrigger - 120, // 232: api.v1.KedaScaledObjectDetails.conditions:type_name -> api.v1.KedaScaledObjectCondition - 182, // 233: api.v1.KedaScaledObjectTrigger.metadata:type_name -> api.v1.KedaScaledObjectTrigger.MetadataEntry - 122, // 234: api.v1.KarpenterResourceDetails.conditions:type_name -> api.v1.KarpenterResourceCondition - 123, // 235: api.v1.KarpenterResourceDetails.capacity:type_name -> api.v1.KarpenterCapacity - 124, // 236: api.v1.KarpenterResourceDetails.requirements:type_name -> api.v1.KarpenterRequirement - 183, // 237: api.v1.KarpenterResourceDetails.limits:type_name -> api.v1.KarpenterResourceDetails.LimitsEntry - 184, // 238: api.v1.KarpenterCapacity.other:type_name -> api.v1.KarpenterCapacity.OtherEntry - 35, // 239: api.v1.WorkloadFilters.namespace_selector:type_name -> api.v1.LabelSelector - 35, // 240: api.v1.WorkloadFilters.workload_selector:type_name -> api.v1.LabelSelector - 0, // 241: api.v1.WorkloadFilters.kind_filter:type_name -> api.v1.K8sObjectKind - 36, // 242: api.v1.WorkloadFilters.name_pattern:type_name -> api.v1.RegexPattern - 35, // 243: api.v1.WorkloadFilters.annotation_selector:type_name -> api.v1.LabelSelector - 36, // 244: api.v1.WorkloadFilters.namespace_pattern:type_name -> api.v1.RegexPattern - 3, // 245: api.v1.WorkloadFilters.status:type_name -> api.v1.WorkloadStatusFilter - 185, // 246: api.v1.PodDisruptionBudgetDetails.selector_labels:type_name -> api.v1.PodDisruptionBudgetDetails.SelectorLabelsEntry - 129, // 247: api.v1.PodDisruptionBudgetDetails.conditions:type_name -> api.v1.PodDisruptionBudgetCondition - 186, // 248: api.v1.ResourceQuotaDetails.hard_limits:type_name -> api.v1.ResourceQuotaDetails.HardLimitsEntry - 187, // 249: api.v1.ResourceQuotaDetails.used:type_name -> api.v1.ResourceQuotaDetails.UsedEntry - 188, // 250: api.v1.ResourceQuotaDetails.scope_selector:type_name -> api.v1.ResourceQuotaDetails.ScopeSelectorEntry - 131, // 251: api.v1.ResourceQuotaDetails.conditions:type_name -> api.v1.ResourceQuotaCondition - 41, // 252: api.v1.VolcanoJobDetails.containers:type_name -> api.v1.ContainerTemplate - 133, // 253: api.v1.VolcanoJobDetails.conditions:type_name -> api.v1.VolcanoJobCondition - 134, // 254: api.v1.VolcanoJobDetails.tasks:type_name -> api.v1.VolcanoTask - 41, // 255: api.v1.VolcanoTask.containers:type_name -> api.v1.ContainerTemplate - 41, // 256: api.v1.SparkApplicationDetails.containers:type_name -> api.v1.ContainerTemplate - 136, // 257: api.v1.SparkApplicationDetails.spark_driver_info:type_name -> api.v1.SparkDriverInfo - 137, // 258: api.v1.SparkApplicationDetails.restart_policy:type_name -> api.v1.SparkRestartPolicyInfo - 138, // 259: api.v1.SparkApplicationDetails.dynamic_allocation:type_name -> api.v1.SparkDynamicAllocationInfo - 191, // 260: api.v1.ScheduledSparkApplicationDetails.last_run:type_name -> google.protobuf.Timestamp - 191, // 261: api.v1.ScheduledSparkApplicationDetails.next_run:type_name -> google.protobuf.Timestamp - 135, // 262: api.v1.ScheduledSparkApplicationDetails.template_details:type_name -> api.v1.SparkApplicationDetails - 141, // 263: api.v1.KyvernoPolicyReportDetails.results:type_name -> api.v1.PolicyReportResult - 191, // 264: api.v1.GatekeeperConstraintDetails.audit_timestamp:type_name -> google.protobuf.Timestamp - 144, // 265: api.v1.GatekeeperConstraintDetails.violations:type_name -> api.v1.ConstraintViolation - 189, // 266: api.v1.Event.annotations:type_name -> api.v1.Event.AnnotationsEntry - 190, // 267: api.v1.Event.labels:type_name -> api.v1.Event.LabelsEntry - 4, // 268: api.v1.Event.disruption_block_reason:type_name -> api.v1.DisruptionBlockReason - 191, // 269: api.v1.Event.created_at:type_name -> google.protobuf.Timestamp - 191, // 270: api.v1.Event.updated_at:type_name -> google.protobuf.Timestamp - 191, // 271: api.v1.Event.last_seen:type_name -> google.protobuf.Timestamp - 191, // 272: api.v1.Event.deleted_at:type_name -> google.protobuf.Timestamp - 147, // 273: api.v1.EventDatapoint.events:type_name -> api.v1.EventDatapointInfo - 191, // 274: api.v1.EventGroup.first_seen:type_name -> google.protobuf.Timestamp - 191, // 275: api.v1.EventGroup.last_seen:type_name -> google.protobuf.Timestamp - 276, // [276:276] is the sub-list for method output_type - 276, // [276:276] is the sub-list for method input_type - 276, // [276:276] is the sub-list for extension type_name - 276, // [276:276] is the sub-list for extension extendee - 0, // [0:276] is the sub-list for field type_name + 218, // 4: api.v1.CostInfo.money_cpu_cost_per_hour:type_name -> google.type.Money + 218, // 5: api.v1.CostInfo.money_memory_cost_per_hour:type_name -> google.type.Money + 218, // 6: api.v1.CostInfo.money_total_cost_per_hour:type_name -> google.type.Money + 218, // 7: api.v1.CostInfo.money_total_cost_per_month:type_name -> google.type.Money + 218, // 8: api.v1.CostInfo.money_total_cost_per_year:type_name -> google.type.Money + 218, // 9: api.v1.CostInfo.money_optimized_cpu_cost_per_hour:type_name -> google.type.Money + 218, // 10: api.v1.CostInfo.money_optimized_memory_cost_per_hour:type_name -> google.type.Money + 218, // 11: api.v1.CostInfo.money_optimized_total_cost_per_hour:type_name -> google.type.Money + 218, // 12: api.v1.CostInfo.money_optimized_total_cost_per_month:type_name -> google.type.Money + 218, // 13: api.v1.CostInfo.money_optimized_total_cost_per_year:type_name -> google.type.Money + 218, // 14: api.v1.CostInfo.money_cpu_cost_for_time_period:type_name -> google.type.Money + 218, // 15: api.v1.CostInfo.money_memory_cost_for_time_period:type_name -> google.type.Money + 218, // 16: api.v1.CostInfo.money_total_cost_for_time_period:type_name -> google.type.Money + 218, // 17: api.v1.CostInfo.money_network_cost_for_time_period:type_name -> google.type.Money + 218, // 18: api.v1.CostInfo.money_optimized_cpu_cost_for_time_period:type_name -> google.type.Money + 218, // 19: api.v1.CostInfo.money_optimized_memory_cost_for_time_period:type_name -> google.type.Money + 218, // 20: api.v1.CostInfo.money_optimized_total_cost_for_time_period:type_name -> google.type.Money + 218, // 21: api.v1.CostInfo.money_optimized_gpu_cost_for_time_period:type_name -> google.type.Money + 218, // 22: api.v1.CostInfo.money_gpu_cost_for_time_period:type_name -> google.type.Money + 218, // 23: api.v1.CostInfo.money_gpu_cost_per_hour:type_name -> google.type.Money + 218, // 24: api.v1.CostInfo.money_optimized_gpu_cost_per_hour:type_name -> google.type.Money + 218, // 25: api.v1.CostInfo.money_cpu_cost_per_vcpu_per_hour:type_name -> google.type.Money + 218, // 26: api.v1.CostInfo.money_memory_cost_per_gib_per_hour:type_name -> google.type.Money + 218, // 27: api.v1.CostInfo.money_month_to_date_cost:type_name -> google.type.Money + 218, // 28: api.v1.CostInfo.money_projected_month_cost:type_name -> google.type.Money + 218, // 29: api.v1.CostInfo.money_optimized_projected_month_cost:type_name -> google.type.Money + 14, // 30: api.v1.ContainerPercentileSummary.cpu_usage:type_name -> api.v1.MetricPercentiles + 14, // 31: api.v1.ContainerPercentileSummary.memory_usage:type_name -> api.v1.MetricPercentiles + 14, // 32: api.v1.ContainerPercentileSummary.cpu_request:type_name -> api.v1.MetricPercentiles + 14, // 33: api.v1.ContainerPercentileSummary.memory_request:type_name -> api.v1.MetricPercentiles + 14, // 34: api.v1.ContainerPercentileSummary.cpu_limit:type_name -> api.v1.MetricPercentiles + 14, // 35: api.v1.ContainerPercentileSummary.memory_limit:type_name -> api.v1.MetricPercentiles + 14, // 36: api.v1.ContainerPercentileSummary.gpu_usage:type_name -> api.v1.MetricPercentiles + 14, // 37: api.v1.ContainerPercentileSummary.gpu_vram_usage:type_name -> api.v1.MetricPercentiles + 14, // 38: api.v1.ContainerPercentileSummary.network_receive_bytes:type_name -> api.v1.MetricPercentiles + 14, // 39: api.v1.ContainerPercentileSummary.network_transmit_bytes:type_name -> api.v1.MetricPercentiles + 14, // 40: api.v1.ContainerPercentileSummary.fs_read_bytes:type_name -> api.v1.MetricPercentiles + 14, // 41: api.v1.ContainerPercentileSummary.fs_write_bytes:type_name -> api.v1.MetricPercentiles + 20, // 42: api.v1.ContainerRequestLimits.runtime_processes:type_name -> api.v1.RuntimeProcess + 18, // 43: api.v1.ContainerIndividualGPUMetrics.instances:type_name -> api.v1.IndividualGPUMetric + 217, // 44: api.v1.ContainerIndividualGPUMetrics.collected_at:type_name -> google.protobuf.Timestamp + 217, // 45: api.v1.ForecastResourceMetrics.timestamp:type_name -> google.protobuf.Timestamp + 23, // 46: api.v1.OptimizationStatus.legacy_policies:type_name -> api.v1.LegacyPolicyRef + 25, // 47: api.v1.WorkloadItem.children:type_name -> api.v1.WorkloadItem + 177, // 48: api.v1.WorkloadItem.labels:type_name -> api.v1.WorkloadItem.LabelsEntry + 178, // 49: api.v1.WorkloadItem.annotations:type_name -> api.v1.WorkloadItem.AnnotationsEntry + 13, // 50: api.v1.WorkloadItem.resource_metrics:type_name -> api.v1.ResourceMetrics + 12, // 51: api.v1.WorkloadItem.cost_info:type_name -> api.v1.CostInfo + 32, // 52: api.v1.WorkloadItem.cost_data_points:type_name -> api.v1.CostDataPoint + 33, // 53: api.v1.WorkloadItem.resource_data_points:type_name -> api.v1.ResourceDataPoint + 217, // 54: api.v1.WorkloadItem.created_at:type_name -> google.protobuf.Timestamp + 217, // 55: api.v1.WorkloadItem.deleted_at:type_name -> google.protobuf.Timestamp + 42, // 56: api.v1.WorkloadItem.resource_details:type_name -> api.v1.ResourceDetails + 24, // 57: api.v1.WorkloadItem.optimization_status:type_name -> api.v1.OptimizationStatus + 179, // 58: api.v1.NodeInfo.price_source_counts:type_name -> api.v1.NodeInfo.PriceSourceCountsEntry + 13, // 59: api.v1.Node.resource_metrics:type_name -> api.v1.ResourceMetrics + 12, // 60: api.v1.Node.cost_info:type_name -> api.v1.CostInfo + 26, // 61: api.v1.Node.container_runtime:type_name -> api.v1.ContainerRuntimeInfo + 42, // 62: api.v1.Node.resource_details:type_name -> api.v1.ResourceDetails + 25, // 63: api.v1.Node.children:type_name -> api.v1.WorkloadItem + 180, // 64: api.v1.Node.labels:type_name -> api.v1.Node.LabelsEntry + 181, // 65: api.v1.Node.annotations:type_name -> api.v1.Node.AnnotationsEntry + 218, // 66: api.v1.Node.money_price_per_hour:type_name -> google.type.Money + 218, // 67: api.v1.Node.money_eff_period_total_cost:type_name -> google.type.Money + 218, // 68: api.v1.Node.money_price_per_vcpu:type_name -> google.type.Money + 218, // 69: api.v1.Node.money_price_per_gib:type_name -> google.type.Money + 218, // 70: api.v1.Node.money_price_per_gpu:type_name -> google.type.Money + 218, // 71: api.v1.Node.money_cpu_price:type_name -> google.type.Money + 218, // 72: api.v1.Node.money_memory_price:type_name -> google.type.Money + 218, // 73: api.v1.Node.money_gpu_price:type_name -> google.type.Money + 218, // 74: api.v1.Node.money_on_demand_price_per_hour:type_name -> google.type.Money + 33, // 75: api.v1.Node.resource_data_points:type_name -> api.v1.ResourceDataPoint + 30, // 76: api.v1.Node.volumes_attached:type_name -> api.v1.AttachedVolume + 217, // 77: api.v1.Node.last_seen:type_name -> google.protobuf.Timestamp + 217, // 78: api.v1.Node.collected_at:type_name -> google.protobuf.Timestamp + 217, // 79: api.v1.Node.deletion_timestamp:type_name -> google.protobuf.Timestamp + 3, // 80: api.v1.Node.managed_by:type_name -> api.v1.NodeManager + 29, // 81: api.v1.NodeGroup.nodes:type_name -> api.v1.Node + 13, // 82: api.v1.NodeGroup.resource_metrics:type_name -> api.v1.ResourceMetrics + 12, // 83: api.v1.NodeGroup.cost_info:type_name -> api.v1.CostInfo + 27, // 84: api.v1.NodeGroup.node_info:type_name -> api.v1.NodeInfo + 28, // 85: api.v1.NodeGroup.resource_info:type_name -> api.v1.ResourceInfo + 32, // 86: api.v1.NodeGroup.cost_data_points:type_name -> api.v1.CostDataPoint + 33, // 87: api.v1.NodeGroup.resource_data_points:type_name -> api.v1.ResourceDataPoint + 2, // 88: api.v1.NodeGroup.provisioner:type_name -> api.v1.NodeGroupProvisioner + 12, // 89: api.v1.CostDataPoint.cost_info:type_name -> api.v1.CostInfo + 218, // 90: api.v1.CostDataPoint.money_on_demand_cost:type_name -> google.type.Money + 218, // 91: api.v1.CostDataPoint.money_spot_cost:type_name -> google.type.Money + 13, // 92: api.v1.ResourceDataPoint.resource_metrics:type_name -> api.v1.ResourceMetrics + 33, // 93: api.v1.ContainerTimeSeries.resource_data_points:type_name -> api.v1.ResourceDataPoint + 35, // 94: api.v1.SavingsDataPoint.savings_data:type_name -> api.v1.SavingsData + 36, // 95: api.v1.SavingsTimeSeries.savings_datapoints:type_name -> api.v1.SavingsDataPoint + 4, // 96: api.v1.LabelSelectorRequirement.operator:type_name -> api.v1.LabelSelectorOperator + 39, // 97: api.v1.LabelSelector.labels:type_name -> api.v1.Label + 182, // 98: api.v1.LabelSelector.match_labels:type_name -> api.v1.LabelSelector.MatchLabelsEntry + 38, // 99: api.v1.LabelSelector.match_expressions:type_name -> api.v1.LabelSelectorRequirement + 43, // 100: api.v1.ResourceDetails.pod_details:type_name -> api.v1.PodDetails + 45, // 101: api.v1.ResourceDetails.deployment_details:type_name -> api.v1.DeploymentDetails + 48, // 102: api.v1.ResourceDetails.stateful_set_details:type_name -> api.v1.StatefulSetDetails + 50, // 103: api.v1.ResourceDetails.daemon_set_details:type_name -> api.v1.DaemonSetDetails + 51, // 104: api.v1.ResourceDetails.replica_set_details:type_name -> api.v1.ReplicaSetDetails + 52, // 105: api.v1.ResourceDetails.job_details:type_name -> api.v1.JobDetails + 54, // 106: api.v1.ResourceDetails.cron_job_details:type_name -> api.v1.CronJobDetails + 57, // 107: api.v1.ResourceDetails.service_details:type_name -> api.v1.ServiceDetails + 60, // 108: api.v1.ResourceDetails.ingress_details:type_name -> api.v1.IngressDetails + 65, // 109: api.v1.ResourceDetails.pvc_details:type_name -> api.v1.PersistentVolumeClaimDetails + 69, // 110: api.v1.ResourceDetails.pv_details:type_name -> api.v1.PersistentVolumeDetails + 75, // 111: api.v1.ResourceDetails.sc_details:type_name -> api.v1.StorageClassDetails + 76, // 112: api.v1.ResourceDetails.ns_details:type_name -> api.v1.NamespaceDetails + 77, // 113: api.v1.ResourceDetails.node_details:type_name -> api.v1.NodeDetails + 89, // 114: api.v1.ResourceDetails.hpa_details:type_name -> api.v1.HPADetails + 107, // 115: api.v1.ResourceDetails.vpa_details:type_name -> api.v1.VPADetails + 115, // 116: api.v1.ResourceDetails.limit_range_details:type_name -> api.v1.LimitRangeDetails + 117, // 117: api.v1.ResourceDetails.service_account_details:type_name -> api.v1.ServiceAccountDetails + 118, // 118: api.v1.ResourceDetails.role_details:type_name -> api.v1.RoleDetails + 120, // 119: api.v1.ResourceDetails.role_binding_details:type_name -> api.v1.RoleBindingDetails + 123, // 120: api.v1.ResourceDetails.keda_scaled_object_details:type_name -> api.v1.KedaScaledObjectDetails + 126, // 121: api.v1.ResourceDetails.karpenter_resource_details:type_name -> api.v1.KarpenterResourceDetails + 134, // 122: api.v1.ResourceDetails.pod_disruption_budget_details:type_name -> api.v1.PodDisruptionBudgetDetails + 136, // 123: api.v1.ResourceDetails.resource_quota_details:type_name -> api.v1.ResourceQuotaDetails + 138, // 124: api.v1.ResourceDetails.volcano_job_details:type_name -> api.v1.VolcanoJobDetails + 141, // 125: api.v1.ResourceDetails.spark_application_details:type_name -> api.v1.SparkApplicationDetails + 145, // 126: api.v1.ResourceDetails.scheduled_spark_application_details:type_name -> api.v1.ScheduledSparkApplicationDetails + 146, // 127: api.v1.ResourceDetails.kyverno_policy_details:type_name -> api.v1.KyvernoPolicyDetails + 148, // 128: api.v1.ResourceDetails.kyverno_policy_report_details:type_name -> api.v1.KyvernoPolicyReportDetails + 149, // 129: api.v1.ResourceDetails.gatekeeper_constraint_template_details:type_name -> api.v1.GatekeeperConstraintTemplateDetails + 151, // 130: api.v1.ResourceDetails.gatekeeper_constraint_details:type_name -> api.v1.GatekeeperConstraintDetails + 152, // 131: api.v1.ResourceDetails.argo_application_details:type_name -> api.v1.ArgoApplicationDetails + 153, // 132: api.v1.ResourceDetails.argo_application_set_details:type_name -> api.v1.ArgoApplicationSetDetails + 155, // 133: api.v1.ResourceDetails.custom_resource_definition_details:type_name -> api.v1.CustomResourceDefinitionDetails + 156, // 134: api.v1.ResourceDetails.flux_git_repository_details:type_name -> api.v1.FluxGitRepositoryDetails + 157, // 135: api.v1.ResourceDetails.flux_oci_repository_details:type_name -> api.v1.FluxOCIRepositoryDetails + 158, // 136: api.v1.ResourceDetails.flux_helm_repository_details:type_name -> api.v1.FluxHelmRepositoryDetails + 159, // 137: api.v1.ResourceDetails.flux_helm_chart_details:type_name -> api.v1.FluxHelmChartDetails + 160, // 138: api.v1.ResourceDetails.flux_bucket_details:type_name -> api.v1.FluxBucketDetails + 161, // 139: api.v1.ResourceDetails.flux_kustomization_details:type_name -> api.v1.FluxKustomizationDetails + 162, // 140: api.v1.ResourceDetails.flux_helm_release_details:type_name -> api.v1.FluxHelmReleaseDetails + 163, // 141: api.v1.ResourceDetails.flux_external_artifact_details:type_name -> api.v1.FluxGenericDetails + 163, // 142: api.v1.ResourceDetails.flux_alert_details:type_name -> api.v1.FluxGenericDetails + 163, // 143: api.v1.ResourceDetails.flux_provider_details:type_name -> api.v1.FluxGenericDetails + 163, // 144: api.v1.ResourceDetails.flux_receiver_details:type_name -> api.v1.FluxGenericDetails + 163, // 145: api.v1.ResourceDetails.flux_instance_details:type_name -> api.v1.FluxGenericDetails + 163, // 146: api.v1.ResourceDetails.flux_report_details:type_name -> api.v1.FluxGenericDetails + 163, // 147: api.v1.ResourceDetails.flux_resource_set_input_provider_details:type_name -> api.v1.FluxGenericDetails + 163, // 148: api.v1.ResourceDetails.flux_resource_set_details:type_name -> api.v1.FluxGenericDetails + 163, // 149: api.v1.ResourceDetails.flux_image_repository_details:type_name -> api.v1.FluxGenericDetails + 163, // 150: api.v1.ResourceDetails.flux_image_policy_details:type_name -> api.v1.FluxGenericDetails + 163, // 151: api.v1.ResourceDetails.flux_image_update_automation_details:type_name -> api.v1.FluxGenericDetails + 169, // 152: api.v1.ResourceDetails.flagger_canary_details:type_name -> api.v1.FlaggerCanaryDetails + 170, // 153: api.v1.ResourceDetails.flagger_metric_template_details:type_name -> api.v1.FlaggerMetricTemplateDetails + 171, // 154: api.v1.ResourceDetails.flagger_alert_provider_details:type_name -> api.v1.FlaggerAlertProviderDetails + 58, // 155: api.v1.ResourceDetails.service_ports:type_name -> api.v1.ServicePort + 183, // 156: api.v1.ResourceDetails.service_selector:type_name -> api.v1.ResourceDetails.ServiceSelectorEntry + 184, // 157: api.v1.ResourceDetails.sc_parameters:type_name -> api.v1.ResourceDetails.ScParametersEntry + 78, // 158: api.v1.ResourceDetails.node_taints:type_name -> api.v1.NodeTaint + 44, // 159: api.v1.PodDetails.containers:type_name -> api.v1.ContainerSummary + 46, // 160: api.v1.DeploymentDetails.containers:type_name -> api.v1.ContainerTemplate + 47, // 161: api.v1.DeploymentDetails.conditions:type_name -> api.v1.DeploymentCondition + 46, // 162: api.v1.StatefulSetDetails.containers:type_name -> api.v1.ContainerTemplate + 49, // 163: api.v1.StatefulSetDetails.volume_claim_templates:type_name -> api.v1.VolumeClaimTemplate + 46, // 164: api.v1.DaemonSetDetails.containers:type_name -> api.v1.ContainerTemplate + 85, // 165: api.v1.DaemonSetDetails.tolerations:type_name -> api.v1.TolerationInfo + 86, // 166: api.v1.DaemonSetDetails.node_selector:type_name -> api.v1.NodeSelector + 46, // 167: api.v1.ReplicaSetDetails.containers:type_name -> api.v1.ContainerTemplate + 40, // 168: api.v1.ReplicaSetDetails.selector:type_name -> api.v1.LabelSelector + 46, // 169: api.v1.JobDetails.containers:type_name -> api.v1.ContainerTemplate + 53, // 170: api.v1.JobDetails.conditions:type_name -> api.v1.JobCondition + 40, // 171: api.v1.JobDetails.selector:type_name -> api.v1.LabelSelector + 46, // 172: api.v1.CronJobDetails.containers:type_name -> api.v1.ContainerTemplate + 55, // 173: api.v1.CronJobDetails.active_jobs:type_name -> api.v1.ActiveJobReference + 56, // 174: api.v1.CronJobDetails.job_template:type_name -> api.v1.JobTemplate + 46, // 175: api.v1.JobTemplate.containers:type_name -> api.v1.ContainerTemplate + 58, // 176: api.v1.ServiceDetails.ports:type_name -> api.v1.ServicePort + 185, // 177: api.v1.ServiceDetails.selector:type_name -> api.v1.ServiceDetails.SelectorEntry + 59, // 178: api.v1.ServiceDetails.load_balancer_ingress:type_name -> api.v1.LoadBalancerIngress + 61, // 179: api.v1.IngressDetails.rules:type_name -> api.v1.IngressRule + 64, // 180: api.v1.IngressDetails.tls:type_name -> api.v1.IngressTLS + 63, // 181: api.v1.IngressDetails.default_backend:type_name -> api.v1.IngressBackend + 59, // 182: api.v1.IngressDetails.load_balancer_ingress:type_name -> api.v1.LoadBalancerIngress + 62, // 183: api.v1.IngressRule.paths:type_name -> api.v1.IngressPath + 63, // 184: api.v1.IngressPath.backend:type_name -> api.v1.IngressBackend + 66, // 185: api.v1.PersistentVolumeClaimDetails.resource_requirements:type_name -> api.v1.ResourceRequirements + 40, // 186: api.v1.PersistentVolumeClaimDetails.selector:type_name -> api.v1.LabelSelector + 67, // 187: api.v1.PersistentVolumeClaimDetails.volume_node_affinity:type_name -> api.v1.VolumeNodeAffinity + 68, // 188: api.v1.PersistentVolumeClaimDetails.conditions:type_name -> api.v1.PVCCondition + 186, // 189: api.v1.ResourceRequirements.requests:type_name -> api.v1.ResourceRequirements.RequestsEntry + 187, // 190: api.v1.ResourceRequirements.limits:type_name -> api.v1.ResourceRequirements.LimitsEntry + 88, // 191: api.v1.VolumeNodeAffinity.required:type_name -> api.v1.NodeSelectorRequirement + 88, // 192: api.v1.VolumeNodeAffinity.preferred:type_name -> api.v1.NodeSelectorRequirement + 188, // 193: api.v1.PersistentVolumeDetails.capacity:type_name -> api.v1.PersistentVolumeDetails.CapacityEntry + 70, // 194: api.v1.PersistentVolumeDetails.claim_ref:type_name -> api.v1.PVClaimReference + 71, // 195: api.v1.PersistentVolumeDetails.volume_source:type_name -> api.v1.PVVolumeSource + 86, // 196: api.v1.PersistentVolumeDetails.node_affinity:type_name -> api.v1.NodeSelector + 72, // 197: api.v1.PVVolumeSource.csi:type_name -> api.v1.CSIVolumeSource + 73, // 198: api.v1.PVVolumeSource.host_path:type_name -> api.v1.HostPathVolumeSource + 74, // 199: api.v1.PVVolumeSource.nfs:type_name -> api.v1.NFSVolumeSource + 189, // 200: api.v1.PVVolumeSource.volume_attributes:type_name -> api.v1.PVVolumeSource.VolumeAttributesEntry + 190, // 201: api.v1.CSIVolumeSource.volume_attributes:type_name -> api.v1.CSIVolumeSource.VolumeAttributesEntry + 191, // 202: api.v1.StorageClassDetails.parameters:type_name -> api.v1.StorageClassDetails.ParametersEntry + 83, // 203: api.v1.StorageClassDetails.allowed_topologies:type_name -> api.v1.TopologySelector + 192, // 204: api.v1.NamespaceDetails.conditions:type_name -> api.v1.NamespaceDetails.ConditionsEntry + 79, // 205: api.v1.NodeDetails.addresses:type_name -> api.v1.NodeAddress + 80, // 206: api.v1.NodeDetails.conditions:type_name -> api.v1.NodeCondition + 81, // 207: api.v1.NodeDetails.system_info:type_name -> api.v1.NodeSystemInfo + 193, // 208: api.v1.NodeDetails.capacity:type_name -> api.v1.NodeDetails.CapacityEntry + 194, // 209: api.v1.NodeDetails.allocatable:type_name -> api.v1.NodeDetails.AllocatableEntry + 82, // 210: api.v1.NodeDetails.images:type_name -> api.v1.NodeImage + 84, // 211: api.v1.TopologySelector.match_label_expressions:type_name -> api.v1.TopologySelectorTerm + 195, // 212: api.v1.TopologySelectorTerm.match_labels:type_name -> api.v1.TopologySelectorTerm.MatchLabelsEntry + 87, // 213: api.v1.NodeSelector.terms:type_name -> api.v1.NodeSelectorTerm + 88, // 214: api.v1.NodeSelectorTerm.match_expressions:type_name -> api.v1.NodeSelectorRequirement + 88, // 215: api.v1.NodeSelectorTerm.match_fields:type_name -> api.v1.NodeSelectorRequirement + 90, // 216: api.v1.HPADetails.scale_target_ref:type_name -> api.v1.ScaleTargetRef + 91, // 217: api.v1.HPADetails.metrics:type_name -> api.v1.HPAMetric + 103, // 218: api.v1.HPADetails.conditions:type_name -> api.v1.HPACondition + 104, // 219: api.v1.HPADetails.behavior:type_name -> api.v1.HPABehavior + 98, // 220: api.v1.HPADetails.current_metrics:type_name -> api.v1.HPACurrentMetric + 92, // 221: api.v1.HPAMetric.resource:type_name -> api.v1.HPAResourceMetric + 93, // 222: api.v1.HPAMetric.pods:type_name -> api.v1.HPAPodsMetric + 94, // 223: api.v1.HPAMetric.object:type_name -> api.v1.HPAObjectMetric + 95, // 224: api.v1.HPAMetric.external:type_name -> api.v1.HPAExternalMetric + 96, // 225: api.v1.HPAPodsMetric.metric:type_name -> api.v1.HPAMetricSelector + 97, // 226: api.v1.HPAObjectMetric.described_object:type_name -> api.v1.HPAObjectReference + 96, // 227: api.v1.HPAObjectMetric.metric:type_name -> api.v1.HPAMetricSelector + 96, // 228: api.v1.HPAExternalMetric.metric:type_name -> api.v1.HPAMetricSelector + 196, // 229: api.v1.HPAMetricSelector.selector:type_name -> api.v1.HPAMetricSelector.SelectorEntry + 99, // 230: api.v1.HPACurrentMetric.resource:type_name -> api.v1.HPACurrentResourceMetric + 100, // 231: api.v1.HPACurrentMetric.pods:type_name -> api.v1.HPACurrentPodsMetric + 101, // 232: api.v1.HPACurrentMetric.object:type_name -> api.v1.HPACurrentObjectMetric + 102, // 233: api.v1.HPACurrentMetric.external:type_name -> api.v1.HPACurrentExternalMetric + 96, // 234: api.v1.HPACurrentPodsMetric.metric:type_name -> api.v1.HPAMetricSelector + 97, // 235: api.v1.HPACurrentObjectMetric.described_object:type_name -> api.v1.HPAObjectReference + 96, // 236: api.v1.HPACurrentObjectMetric.metric:type_name -> api.v1.HPAMetricSelector + 96, // 237: api.v1.HPACurrentExternalMetric.metric:type_name -> api.v1.HPAMetricSelector + 105, // 238: api.v1.HPABehavior.scale_up:type_name -> api.v1.HPAScalingRules + 105, // 239: api.v1.HPABehavior.scale_down:type_name -> api.v1.HPAScalingRules + 106, // 240: api.v1.HPAScalingRules.policies:type_name -> api.v1.HPAScalingPolicy + 108, // 241: api.v1.VPADetails.target_ref:type_name -> api.v1.VPATargetRef + 109, // 242: api.v1.VPADetails.update_policy:type_name -> api.v1.VPAUpdatePolicy + 110, // 243: api.v1.VPADetails.resource_policy:type_name -> api.v1.VPAResourcePolicy + 112, // 244: api.v1.VPADetails.recommendation:type_name -> api.v1.VPARecommendation + 114, // 245: api.v1.VPADetails.conditions:type_name -> api.v1.VPACondition + 111, // 246: api.v1.VPAResourcePolicy.container_policies:type_name -> api.v1.VPAContainerResourcePolicy + 197, // 247: api.v1.VPAContainerResourcePolicy.min_allowed:type_name -> api.v1.VPAContainerResourcePolicy.MinAllowedEntry + 198, // 248: api.v1.VPAContainerResourcePolicy.max_allowed:type_name -> api.v1.VPAContainerResourcePolicy.MaxAllowedEntry + 113, // 249: api.v1.VPARecommendation.container_recommendations:type_name -> api.v1.VPAContainerRecommendation + 199, // 250: api.v1.VPAContainerRecommendation.target:type_name -> api.v1.VPAContainerRecommendation.TargetEntry + 200, // 251: api.v1.VPAContainerRecommendation.lower_bound:type_name -> api.v1.VPAContainerRecommendation.LowerBoundEntry + 201, // 252: api.v1.VPAContainerRecommendation.upper_bound:type_name -> api.v1.VPAContainerRecommendation.UpperBoundEntry + 202, // 253: api.v1.VPAContainerRecommendation.uncapped_target:type_name -> api.v1.VPAContainerRecommendation.UncappedTargetEntry + 116, // 254: api.v1.LimitRangeDetails.limits:type_name -> api.v1.LimitRangeItem + 203, // 255: api.v1.LimitRangeItem.default_limits:type_name -> api.v1.LimitRangeItem.DefaultLimitsEntry + 204, // 256: api.v1.LimitRangeItem.default_request:type_name -> api.v1.LimitRangeItem.DefaultRequestEntry + 205, // 257: api.v1.LimitRangeItem.max:type_name -> api.v1.LimitRangeItem.MaxEntry + 206, // 258: api.v1.LimitRangeItem.min:type_name -> api.v1.LimitRangeItem.MinEntry + 207, // 259: api.v1.LimitRangeItem.max_limit_request_ratio:type_name -> api.v1.LimitRangeItem.MaxLimitRequestRatioEntry + 119, // 260: api.v1.RoleDetails.rules:type_name -> api.v1.RoleRule + 121, // 261: api.v1.RoleBindingDetails.subjects:type_name -> api.v1.RoleBindingSubject + 122, // 262: api.v1.RoleBindingDetails.role_ref:type_name -> api.v1.RoleReference + 124, // 263: api.v1.KedaScaledObjectDetails.triggers:type_name -> api.v1.KedaScaledObjectTrigger + 125, // 264: api.v1.KedaScaledObjectDetails.conditions:type_name -> api.v1.KedaScaledObjectCondition + 208, // 265: api.v1.KedaScaledObjectTrigger.metadata:type_name -> api.v1.KedaScaledObjectTrigger.MetadataEntry + 127, // 266: api.v1.KarpenterResourceDetails.conditions:type_name -> api.v1.KarpenterResourceCondition + 128, // 267: api.v1.KarpenterResourceDetails.capacity:type_name -> api.v1.KarpenterCapacity + 129, // 268: api.v1.KarpenterResourceDetails.requirements:type_name -> api.v1.KarpenterRequirement + 209, // 269: api.v1.KarpenterResourceDetails.limits:type_name -> api.v1.KarpenterResourceDetails.LimitsEntry + 210, // 270: api.v1.KarpenterCapacity.other:type_name -> api.v1.KarpenterCapacity.OtherEntry + 40, // 271: api.v1.WorkloadFilters.namespace_selector:type_name -> api.v1.LabelSelector + 40, // 272: api.v1.WorkloadFilters.workload_selector:type_name -> api.v1.LabelSelector + 0, // 273: api.v1.WorkloadFilters.kind_filter:type_name -> api.v1.K8sObjectKind + 41, // 274: api.v1.WorkloadFilters.name_pattern:type_name -> api.v1.RegexPattern + 40, // 275: api.v1.WorkloadFilters.annotation_selector:type_name -> api.v1.LabelSelector + 41, // 276: api.v1.WorkloadFilters.namespace_pattern:type_name -> api.v1.RegexPattern + 133, // 277: api.v1.WorkloadFilters.health:type_name -> api.v1.WorkloadHealthFilter + 5, // 278: api.v1.WorkloadFilters.status:type_name -> api.v1.WorkloadStatusFilter + 6, // 279: api.v1.WorkloadHealthFilter.status:type_name -> api.v1.WorkloadHealthStatusFilter + 7, // 280: api.v1.WorkloadHealthFilter.issues:type_name -> api.v1.WorkloadHealthIssue + 8, // 281: api.v1.WorkloadHealthFilter.issue_match:type_name -> api.v1.HealthIssueMatchMode + 211, // 282: api.v1.PodDisruptionBudgetDetails.selector_labels:type_name -> api.v1.PodDisruptionBudgetDetails.SelectorLabelsEntry + 135, // 283: api.v1.PodDisruptionBudgetDetails.conditions:type_name -> api.v1.PodDisruptionBudgetCondition + 212, // 284: api.v1.ResourceQuotaDetails.hard_limits:type_name -> api.v1.ResourceQuotaDetails.HardLimitsEntry + 213, // 285: api.v1.ResourceQuotaDetails.used:type_name -> api.v1.ResourceQuotaDetails.UsedEntry + 214, // 286: api.v1.ResourceQuotaDetails.scope_selector:type_name -> api.v1.ResourceQuotaDetails.ScopeSelectorEntry + 137, // 287: api.v1.ResourceQuotaDetails.conditions:type_name -> api.v1.ResourceQuotaCondition + 46, // 288: api.v1.VolcanoJobDetails.containers:type_name -> api.v1.ContainerTemplate + 139, // 289: api.v1.VolcanoJobDetails.conditions:type_name -> api.v1.VolcanoJobCondition + 140, // 290: api.v1.VolcanoJobDetails.tasks:type_name -> api.v1.VolcanoTask + 46, // 291: api.v1.VolcanoTask.containers:type_name -> api.v1.ContainerTemplate + 46, // 292: api.v1.SparkApplicationDetails.containers:type_name -> api.v1.ContainerTemplate + 142, // 293: api.v1.SparkApplicationDetails.spark_driver_info:type_name -> api.v1.SparkDriverInfo + 143, // 294: api.v1.SparkApplicationDetails.restart_policy:type_name -> api.v1.SparkRestartPolicyInfo + 144, // 295: api.v1.SparkApplicationDetails.dynamic_allocation:type_name -> api.v1.SparkDynamicAllocationInfo + 217, // 296: api.v1.ScheduledSparkApplicationDetails.last_run:type_name -> google.protobuf.Timestamp + 217, // 297: api.v1.ScheduledSparkApplicationDetails.next_run:type_name -> google.protobuf.Timestamp + 141, // 298: api.v1.ScheduledSparkApplicationDetails.template_details:type_name -> api.v1.SparkApplicationDetails + 147, // 299: api.v1.KyvernoPolicyReportDetails.results:type_name -> api.v1.PolicyReportResult + 217, // 300: api.v1.GatekeeperConstraintDetails.audit_timestamp:type_name -> google.protobuf.Timestamp + 150, // 301: api.v1.GatekeeperConstraintDetails.violations:type_name -> api.v1.ConstraintViolation + 217, // 302: api.v1.ArgoApplicationDetails.reconciled_at:type_name -> google.protobuf.Timestamp + 217, // 303: api.v1.ArgoApplicationDetails.last_sync_at:type_name -> google.protobuf.Timestamp + 154, // 304: api.v1.CustomResourceDefinitionDetails.versions:type_name -> api.v1.CustomResourceDefinitionVersion + 217, // 305: api.v1.FluxGitRepositoryDetails.artifact_last_update_time:type_name -> google.protobuf.Timestamp + 217, // 306: api.v1.FluxOCIRepositoryDetails.artifact_last_update_time:type_name -> google.protobuf.Timestamp + 217, // 307: api.v1.FluxHelmRepositoryDetails.artifact_last_update_time:type_name -> google.protobuf.Timestamp + 217, // 308: api.v1.FluxHelmChartDetails.artifact_last_update_time:type_name -> google.protobuf.Timestamp + 217, // 309: api.v1.FluxBucketDetails.artifact_last_update_time:type_name -> google.protobuf.Timestamp + 164, // 310: api.v1.FlaggerCanaryMetric.template_ref:type_name -> api.v1.FlaggerObjectReference + 164, // 311: api.v1.FlaggerCanaryAlert.provider_ref:type_name -> api.v1.FlaggerObjectReference + 217, // 312: api.v1.FlaggerCanaryDetails.last_transition_time:type_name -> google.protobuf.Timestamp + 164, // 313: api.v1.FlaggerCanaryDetails.target_ref:type_name -> api.v1.FlaggerObjectReference + 164, // 314: api.v1.FlaggerCanaryDetails.autoscaler_ref:type_name -> api.v1.FlaggerObjectReference + 165, // 315: api.v1.FlaggerCanaryDetails.gateway_refs:type_name -> api.v1.FlaggerGatewayRef + 166, // 316: api.v1.FlaggerCanaryDetails.metrics:type_name -> api.v1.FlaggerCanaryMetric + 167, // 317: api.v1.FlaggerCanaryDetails.alerts:type_name -> api.v1.FlaggerCanaryAlert + 168, // 318: api.v1.FlaggerCanaryDetails.webhooks:type_name -> api.v1.FlaggerCanaryWebhook + 215, // 319: api.v1.Event.annotations:type_name -> api.v1.Event.AnnotationsEntry + 216, // 320: api.v1.Event.labels:type_name -> api.v1.Event.LabelsEntry + 9, // 321: api.v1.Event.disruption_block_reason:type_name -> api.v1.DisruptionBlockReason + 217, // 322: api.v1.Event.created_at:type_name -> google.protobuf.Timestamp + 217, // 323: api.v1.Event.updated_at:type_name -> google.protobuf.Timestamp + 217, // 324: api.v1.Event.last_seen:type_name -> google.protobuf.Timestamp + 217, // 325: api.v1.Event.deleted_at:type_name -> google.protobuf.Timestamp + 173, // 326: api.v1.EventDatapoint.events:type_name -> api.v1.EventDatapointInfo + 217, // 327: api.v1.EventGroup.first_seen:type_name -> google.protobuf.Timestamp + 217, // 328: api.v1.EventGroup.last_seen:type_name -> google.protobuf.Timestamp + 329, // [329:329] is the sub-list for method output_type + 329, // [329:329] is the sub-list for method input_type + 329, // [329:329] is the sub-list for extension type_name + 329, // [329:329] is the sub-list for extension extendee + 0, // [0:329] is the sub-list for field type_name } func init() { file_api_v1_common_proto_init() } @@ -21449,7 +27077,7 @@ func file_api_v1_common_proto_init() { } } file_api_v1_common_proto_msgTypes[123].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*PodDisruptionBudgetDetails); i { + switch v := v.(*WorkloadHealthFilter); i { case 0: return &v.state case 1: @@ -21461,7 +27089,7 @@ func file_api_v1_common_proto_init() { } } file_api_v1_common_proto_msgTypes[124].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*PodDisruptionBudgetCondition); i { + switch v := v.(*PodDisruptionBudgetDetails); i { case 0: return &v.state case 1: @@ -21473,7 +27101,7 @@ func file_api_v1_common_proto_init() { } } file_api_v1_common_proto_msgTypes[125].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ResourceQuotaDetails); i { + switch v := v.(*PodDisruptionBudgetCondition); i { case 0: return &v.state case 1: @@ -21485,7 +27113,7 @@ func file_api_v1_common_proto_init() { } } file_api_v1_common_proto_msgTypes[126].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ResourceQuotaCondition); i { + switch v := v.(*ResourceQuotaDetails); i { case 0: return &v.state case 1: @@ -21497,7 +27125,7 @@ func file_api_v1_common_proto_init() { } } file_api_v1_common_proto_msgTypes[127].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*VolcanoJobDetails); i { + switch v := v.(*ResourceQuotaCondition); i { case 0: return &v.state case 1: @@ -21509,7 +27137,7 @@ func file_api_v1_common_proto_init() { } } file_api_v1_common_proto_msgTypes[128].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*VolcanoJobCondition); i { + switch v := v.(*VolcanoJobDetails); i { case 0: return &v.state case 1: @@ -21521,7 +27149,7 @@ func file_api_v1_common_proto_init() { } } file_api_v1_common_proto_msgTypes[129].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*VolcanoTask); i { + switch v := v.(*VolcanoJobCondition); i { case 0: return &v.state case 1: @@ -21533,7 +27161,7 @@ func file_api_v1_common_proto_init() { } } file_api_v1_common_proto_msgTypes[130].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*SparkApplicationDetails); i { + switch v := v.(*VolcanoTask); i { case 0: return &v.state case 1: @@ -21545,7 +27173,7 @@ func file_api_v1_common_proto_init() { } } file_api_v1_common_proto_msgTypes[131].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*SparkDriverInfo); i { + switch v := v.(*SparkApplicationDetails); i { case 0: return &v.state case 1: @@ -21557,7 +27185,7 @@ func file_api_v1_common_proto_init() { } } file_api_v1_common_proto_msgTypes[132].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*SparkRestartPolicyInfo); i { + switch v := v.(*SparkDriverInfo); i { case 0: return &v.state case 1: @@ -21569,7 +27197,7 @@ func file_api_v1_common_proto_init() { } } file_api_v1_common_proto_msgTypes[133].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*SparkDynamicAllocationInfo); i { + switch v := v.(*SparkRestartPolicyInfo); i { case 0: return &v.state case 1: @@ -21581,7 +27209,7 @@ func file_api_v1_common_proto_init() { } } file_api_v1_common_proto_msgTypes[134].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ScheduledSparkApplicationDetails); i { + switch v := v.(*SparkDynamicAllocationInfo); i { case 0: return &v.state case 1: @@ -21593,7 +27221,7 @@ func file_api_v1_common_proto_init() { } } file_api_v1_common_proto_msgTypes[135].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*KyvernoPolicyDetails); i { + switch v := v.(*ScheduledSparkApplicationDetails); i { case 0: return &v.state case 1: @@ -21605,7 +27233,7 @@ func file_api_v1_common_proto_init() { } } file_api_v1_common_proto_msgTypes[136].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*PolicyReportResult); i { + switch v := v.(*KyvernoPolicyDetails); i { case 0: return &v.state case 1: @@ -21617,7 +27245,7 @@ func file_api_v1_common_proto_init() { } } file_api_v1_common_proto_msgTypes[137].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*KyvernoPolicyReportDetails); i { + switch v := v.(*PolicyReportResult); i { case 0: return &v.state case 1: @@ -21629,7 +27257,7 @@ func file_api_v1_common_proto_init() { } } file_api_v1_common_proto_msgTypes[138].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GatekeeperConstraintTemplateDetails); i { + switch v := v.(*KyvernoPolicyReportDetails); i { case 0: return &v.state case 1: @@ -21641,7 +27269,7 @@ func file_api_v1_common_proto_init() { } } file_api_v1_common_proto_msgTypes[139].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ConstraintViolation); i { + switch v := v.(*GatekeeperConstraintTemplateDetails); i { case 0: return &v.state case 1: @@ -21653,7 +27281,7 @@ func file_api_v1_common_proto_init() { } } file_api_v1_common_proto_msgTypes[140].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GatekeeperConstraintDetails); i { + switch v := v.(*ConstraintViolation); i { case 0: return &v.state case 1: @@ -21665,7 +27293,7 @@ func file_api_v1_common_proto_init() { } } file_api_v1_common_proto_msgTypes[141].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Event); i { + switch v := v.(*GatekeeperConstraintDetails); i { case 0: return &v.state case 1: @@ -21677,7 +27305,7 @@ func file_api_v1_common_proto_init() { } } file_api_v1_common_proto_msgTypes[142].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*EventDatapointInfo); i { + switch v := v.(*ArgoApplicationDetails); i { case 0: return &v.state case 1: @@ -21689,7 +27317,7 @@ func file_api_v1_common_proto_init() { } } file_api_v1_common_proto_msgTypes[143].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*EventDatapoint); i { + switch v := v.(*ArgoApplicationSetDetails); i { case 0: return &v.state case 1: @@ -21701,7 +27329,7 @@ func file_api_v1_common_proto_init() { } } file_api_v1_common_proto_msgTypes[144].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*DimensionCount); i { + switch v := v.(*CustomResourceDefinitionVersion); i { case 0: return &v.state case 1: @@ -21713,6 +27341,258 @@ func file_api_v1_common_proto_init() { } } file_api_v1_common_proto_msgTypes[145].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*CustomResourceDefinitionDetails); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_api_v1_common_proto_msgTypes[146].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*FluxGitRepositoryDetails); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_api_v1_common_proto_msgTypes[147].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*FluxOCIRepositoryDetails); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_api_v1_common_proto_msgTypes[148].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*FluxHelmRepositoryDetails); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_api_v1_common_proto_msgTypes[149].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*FluxHelmChartDetails); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_api_v1_common_proto_msgTypes[150].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*FluxBucketDetails); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_api_v1_common_proto_msgTypes[151].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*FluxKustomizationDetails); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_api_v1_common_proto_msgTypes[152].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*FluxHelmReleaseDetails); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_api_v1_common_proto_msgTypes[153].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*FluxGenericDetails); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_api_v1_common_proto_msgTypes[154].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*FlaggerObjectReference); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_api_v1_common_proto_msgTypes[155].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*FlaggerGatewayRef); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_api_v1_common_proto_msgTypes[156].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*FlaggerCanaryMetric); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_api_v1_common_proto_msgTypes[157].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*FlaggerCanaryAlert); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_api_v1_common_proto_msgTypes[158].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*FlaggerCanaryWebhook); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_api_v1_common_proto_msgTypes[159].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*FlaggerCanaryDetails); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_api_v1_common_proto_msgTypes[160].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*FlaggerMetricTemplateDetails); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_api_v1_common_proto_msgTypes[161].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*FlaggerAlertProviderDetails); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_api_v1_common_proto_msgTypes[162].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Event); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_api_v1_common_proto_msgTypes[163].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*EventDatapointInfo); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_api_v1_common_proto_msgTypes[164].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*EventDatapoint); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_api_v1_common_proto_msgTypes[165].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*DimensionCount); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_api_v1_common_proto_msgTypes[166].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*EventGroup); i { case 0: return &v.state @@ -21762,16 +27642,42 @@ func file_api_v1_common_proto_init() { (*ResourceDetails_KyvernoPolicyReportDetails)(nil), (*ResourceDetails_GatekeeperConstraintTemplateDetails)(nil), (*ResourceDetails_GatekeeperConstraintDetails)(nil), + (*ResourceDetails_ArgoApplicationDetails)(nil), + (*ResourceDetails_ArgoApplicationSetDetails)(nil), + (*ResourceDetails_CustomResourceDefinitionDetails)(nil), + (*ResourceDetails_FluxGitRepositoryDetails)(nil), + (*ResourceDetails_FluxOciRepositoryDetails)(nil), + (*ResourceDetails_FluxHelmRepositoryDetails)(nil), + (*ResourceDetails_FluxHelmChartDetails)(nil), + (*ResourceDetails_FluxBucketDetails)(nil), + (*ResourceDetails_FluxKustomizationDetails)(nil), + (*ResourceDetails_FluxHelmReleaseDetails)(nil), + (*ResourceDetails_FluxExternalArtifactDetails)(nil), + (*ResourceDetails_FluxAlertDetails)(nil), + (*ResourceDetails_FluxProviderDetails)(nil), + (*ResourceDetails_FluxReceiverDetails)(nil), + (*ResourceDetails_FluxInstanceDetails)(nil), + (*ResourceDetails_FluxReportDetails)(nil), + (*ResourceDetails_FluxResourceSetInputProviderDetails)(nil), + (*ResourceDetails_FluxResourceSetDetails)(nil), + (*ResourceDetails_FluxImageRepositoryDetails)(nil), + (*ResourceDetails_FluxImagePolicyDetails)(nil), + (*ResourceDetails_FluxImageUpdateAutomationDetails)(nil), + (*ResourceDetails_FlaggerCanaryDetails)(nil), + (*ResourceDetails_FlaggerMetricTemplateDetails)(nil), + (*ResourceDetails_FlaggerAlertProviderDetails)(nil), } file_api_v1_common_proto_msgTypes[122].OneofWrappers = []interface{}{} - file_api_v1_common_proto_msgTypes[141].OneofWrappers = []interface{}{} + file_api_v1_common_proto_msgTypes[156].OneofWrappers = []interface{}{} + file_api_v1_common_proto_msgTypes[159].OneofWrappers = []interface{}{} + file_api_v1_common_proto_msgTypes[162].OneofWrappers = []interface{}{} type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_api_v1_common_proto_rawDesc, - NumEnums: 5, - NumMessages: 186, + NumEnums: 10, + NumMessages: 207, NumExtensions: 0, NumServices: 0, }, diff --git a/gen/api/v1/instance.pb.go b/gen/api/v1/instance.pb.go index d40b3669..3364e23a 100644 --- a/gen/api/v1/instance.pb.go +++ b/gen/api/v1/instance.pb.go @@ -2088,16 +2088,16 @@ var file_api_v1_instance_proto_rawDesc = []byte{ 0x6c, 0x6f, 0x75, 0x64, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x6c, 0x6f, 0x75, 0x64, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x73, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x86, 0x01, 0x0a, 0x0a, 0x63, 0x6f, 0x6d, 0x2e, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x8b, 0x01, 0x0a, 0x0a, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x42, 0x0d, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, - 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x30, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, + 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x35, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x64, 0x65, 0x76, 0x7a, 0x65, 0x72, 0x6f, 0x2d, 0x69, 0x6e, 0x63, 0x2f, - 0x7a, 0x78, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x72, 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x61, 0x70, 0x69, - 0x2f, 0x76, 0x31, 0x3b, 0x61, 0x70, 0x69, 0x76, 0x31, 0xa2, 0x02, 0x03, 0x41, 0x58, 0x58, 0xaa, - 0x02, 0x06, 0x41, 0x70, 0x69, 0x2e, 0x56, 0x31, 0xca, 0x02, 0x06, 0x41, 0x70, 0x69, 0x5c, 0x56, - 0x31, 0xe2, 0x02, 0x12, 0x41, 0x70, 0x69, 0x5c, 0x56, 0x31, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, - 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x07, 0x41, 0x70, 0x69, 0x3a, 0x3a, 0x56, 0x31, - 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2f, 0x64, 0x61, 0x6b, 0x72, 0x2f, 0x67, 0x65, + 0x6e, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x3b, 0x61, 0x70, 0x69, 0x76, 0x31, 0xa2, 0x02, + 0x03, 0x41, 0x58, 0x58, 0xaa, 0x02, 0x06, 0x41, 0x70, 0x69, 0x2e, 0x56, 0x31, 0xca, 0x02, 0x06, + 0x41, 0x70, 0x69, 0x5c, 0x56, 0x31, 0xe2, 0x02, 0x12, 0x41, 0x70, 0x69, 0x5c, 0x56, 0x31, 0x5c, + 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x07, 0x41, 0x70, + 0x69, 0x3a, 0x3a, 0x56, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/gen/api/v1/k8s.pb.go b/gen/api/v1/k8s.pb.go index fc0c8358..08d09db7 100644 --- a/gen/api/v1/k8s.pb.go +++ b/gen/api/v1/k8s.pb.go @@ -22,6 +22,228 @@ const ( _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) +// GetClusterNodeInventory: the node inventory of one cluster as it is now, +// per pool or one page of nodes, from the Postgres inventory only. +type NodeInventoryScope int32 + +const ( + NodeInventoryScope_NODE_INVENTORY_SCOPE_UNSPECIFIED NodeInventoryScope = 0 + NodeInventoryScope_NODE_INVENTORY_SCOPE_POOLS NodeInventoryScope = 1 // One row per node pool, no nodes on the wire. + NodeInventoryScope_NODE_INVENTORY_SCOPE_NODES NodeInventoryScope = 2 // One page of nodes. +) + +// Enum value maps for NodeInventoryScope. +var ( + NodeInventoryScope_name = map[int32]string{ + 0: "NODE_INVENTORY_SCOPE_UNSPECIFIED", + 1: "NODE_INVENTORY_SCOPE_POOLS", + 2: "NODE_INVENTORY_SCOPE_NODES", + } + NodeInventoryScope_value = map[string]int32{ + "NODE_INVENTORY_SCOPE_UNSPECIFIED": 0, + "NODE_INVENTORY_SCOPE_POOLS": 1, + "NODE_INVENTORY_SCOPE_NODES": 2, + } +) + +func (x NodeInventoryScope) Enum() *NodeInventoryScope { + p := new(NodeInventoryScope) + *p = x + return p +} + +func (x NodeInventoryScope) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (NodeInventoryScope) Descriptor() protoreflect.EnumDescriptor { + return file_api_v1_k8s_proto_enumTypes[0].Descriptor() +} + +func (NodeInventoryScope) Type() protoreflect.EnumType { + return &file_api_v1_k8s_proto_enumTypes[0] +} + +func (x NodeInventoryScope) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use NodeInventoryScope.Descriptor instead. +func (NodeInventoryScope) EnumDescriptor() ([]byte, []int) { + return file_api_v1_k8s_proto_rawDescGZIP(), []int{0} +} + +type NodeState int32 + +const ( + NodeState_NODE_STATE_UNSPECIFIED NodeState = 0 + NodeState_NODE_STATE_READY NodeState = 1 + NodeState_NODE_STATE_CORDONED NodeState = 2 + NodeState_NODE_STATE_DRAINING NodeState = 3 + NodeState_NODE_STATE_NOT_READY NodeState = 4 + NodeState_NODE_STATE_DELETING NodeState = 5 +) + +// Enum value maps for NodeState. +var ( + NodeState_name = map[int32]string{ + 0: "NODE_STATE_UNSPECIFIED", + 1: "NODE_STATE_READY", + 2: "NODE_STATE_CORDONED", + 3: "NODE_STATE_DRAINING", + 4: "NODE_STATE_NOT_READY", + 5: "NODE_STATE_DELETING", + } + NodeState_value = map[string]int32{ + "NODE_STATE_UNSPECIFIED": 0, + "NODE_STATE_READY": 1, + "NODE_STATE_CORDONED": 2, + "NODE_STATE_DRAINING": 3, + "NODE_STATE_NOT_READY": 4, + "NODE_STATE_DELETING": 5, + } +) + +func (x NodeState) Enum() *NodeState { + p := new(NodeState) + *p = x + return p +} + +func (x NodeState) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (NodeState) Descriptor() protoreflect.EnumDescriptor { + return file_api_v1_k8s_proto_enumTypes[1].Descriptor() +} + +func (NodeState) Type() protoreflect.EnumType { + return &file_api_v1_k8s_proto_enumTypes[1] +} + +func (x NodeState) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use NodeState.Descriptor instead. +func (NodeState) EnumDescriptor() ([]byte, []int) { + return file_api_v1_k8s_proto_rawDescGZIP(), []int{1} +} + +type NodeClaimNotDeletableReason int32 + +const ( + NodeClaimNotDeletableReason_NODE_CLAIM_NOT_DELETABLE_REASON_UNSPECIFIED NodeClaimNotDeletableReason = 0 // deletable + NodeClaimNotDeletableReason_NODE_CLAIM_NOT_DELETABLE_REASON_NODE_NOT_LIVE NodeClaimNotDeletableReason = 1 // no node metrics in the liveness window, or node soft-deleted + NodeClaimNotDeletableReason_NODE_CLAIM_NOT_DELETABLE_REASON_TERMINATING NodeClaimNotDeletableReason = 2 // NodeClaim already carries a deletionTimestamp + NodeClaimNotDeletableReason_NODE_CLAIM_NOT_DELETABLE_REASON_IN_FLIGHT NodeClaimNotDeletableReason = 3 // an unresolved request exists (latest_status carries it) + NodeClaimNotDeletableReason_NODE_CLAIM_NOT_DELETABLE_REASON_OPERATOR_VERSION NodeClaimNotDeletableReason = 4 // dakr-op absent, not pinging, or older than min_operator_version +) + +// Enum value maps for NodeClaimNotDeletableReason. +var ( + NodeClaimNotDeletableReason_name = map[int32]string{ + 0: "NODE_CLAIM_NOT_DELETABLE_REASON_UNSPECIFIED", + 1: "NODE_CLAIM_NOT_DELETABLE_REASON_NODE_NOT_LIVE", + 2: "NODE_CLAIM_NOT_DELETABLE_REASON_TERMINATING", + 3: "NODE_CLAIM_NOT_DELETABLE_REASON_IN_FLIGHT", + 4: "NODE_CLAIM_NOT_DELETABLE_REASON_OPERATOR_VERSION", + } + NodeClaimNotDeletableReason_value = map[string]int32{ + "NODE_CLAIM_NOT_DELETABLE_REASON_UNSPECIFIED": 0, + "NODE_CLAIM_NOT_DELETABLE_REASON_NODE_NOT_LIVE": 1, + "NODE_CLAIM_NOT_DELETABLE_REASON_TERMINATING": 2, + "NODE_CLAIM_NOT_DELETABLE_REASON_IN_FLIGHT": 3, + "NODE_CLAIM_NOT_DELETABLE_REASON_OPERATOR_VERSION": 4, + } +) + +func (x NodeClaimNotDeletableReason) Enum() *NodeClaimNotDeletableReason { + p := new(NodeClaimNotDeletableReason) + *p = x + return p +} + +func (x NodeClaimNotDeletableReason) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (NodeClaimNotDeletableReason) Descriptor() protoreflect.EnumDescriptor { + return file_api_v1_k8s_proto_enumTypes[2].Descriptor() +} + +func (NodeClaimNotDeletableReason) Type() protoreflect.EnumType { + return &file_api_v1_k8s_proto_enumTypes[2] +} + +func (x NodeClaimNotDeletableReason) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use NodeClaimNotDeletableReason.Descriptor instead. +func (NodeClaimNotDeletableReason) EnumDescriptor() ([]byte, []int) { + return file_api_v1_k8s_proto_rawDescGZIP(), []int{2} +} + +type NodeClaimDeletionState int32 + +const ( + NodeClaimDeletionState_NODE_CLAIM_DELETION_STATE_UNSPECIFIED NodeClaimDeletionState = 0 + NodeClaimDeletionState_NODE_CLAIM_DELETION_STATE_PENDING NodeClaimDeletionState = 1 + NodeClaimDeletionState_NODE_CLAIM_DELETION_STATE_SENT NodeClaimDeletionState = 2 + NodeClaimDeletionState_NODE_CLAIM_DELETION_STATE_ACCEPTED NodeClaimDeletionState = 3 + NodeClaimDeletionState_NODE_CLAIM_DELETION_STATE_DELETED NodeClaimDeletionState = 4 + NodeClaimDeletionState_NODE_CLAIM_DELETION_STATE_FAILED NodeClaimDeletionState = 5 +) + +// Enum value maps for NodeClaimDeletionState. +var ( + NodeClaimDeletionState_name = map[int32]string{ + 0: "NODE_CLAIM_DELETION_STATE_UNSPECIFIED", + 1: "NODE_CLAIM_DELETION_STATE_PENDING", + 2: "NODE_CLAIM_DELETION_STATE_SENT", + 3: "NODE_CLAIM_DELETION_STATE_ACCEPTED", + 4: "NODE_CLAIM_DELETION_STATE_DELETED", + 5: "NODE_CLAIM_DELETION_STATE_FAILED", + } + NodeClaimDeletionState_value = map[string]int32{ + "NODE_CLAIM_DELETION_STATE_UNSPECIFIED": 0, + "NODE_CLAIM_DELETION_STATE_PENDING": 1, + "NODE_CLAIM_DELETION_STATE_SENT": 2, + "NODE_CLAIM_DELETION_STATE_ACCEPTED": 3, + "NODE_CLAIM_DELETION_STATE_DELETED": 4, + "NODE_CLAIM_DELETION_STATE_FAILED": 5, + } +) + +func (x NodeClaimDeletionState) Enum() *NodeClaimDeletionState { + p := new(NodeClaimDeletionState) + *p = x + return p +} + +func (x NodeClaimDeletionState) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (NodeClaimDeletionState) Descriptor() protoreflect.EnumDescriptor { + return file_api_v1_k8s_proto_enumTypes[3].Descriptor() +} + +func (NodeClaimDeletionState) Type() protoreflect.EnumType { + return &file_api_v1_k8s_proto_enumTypes[3] +} + +func (x NodeClaimDeletionState) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use NodeClaimDeletionState.Descriptor instead. +func (NodeClaimDeletionState) EnumDescriptor() ([]byte, []int) { + return file_api_v1_k8s_proto_rawDescGZIP(), []int{3} +} + type GalaxyPOVNodeGroupBy int32 const ( @@ -85,11 +307,11 @@ func (x GalaxyPOVNodeGroupBy) String() string { } func (GalaxyPOVNodeGroupBy) Descriptor() protoreflect.EnumDescriptor { - return file_api_v1_k8s_proto_enumTypes[0].Descriptor() + return file_api_v1_k8s_proto_enumTypes[4].Descriptor() } func (GalaxyPOVNodeGroupBy) Type() protoreflect.EnumType { - return &file_api_v1_k8s_proto_enumTypes[0] + return &file_api_v1_k8s_proto_enumTypes[4] } func (x GalaxyPOVNodeGroupBy) Number() protoreflect.EnumNumber { @@ -98,7 +320,7 @@ func (x GalaxyPOVNodeGroupBy) Number() protoreflect.EnumNumber { // Deprecated: Use GalaxyPOVNodeGroupBy.Descriptor instead. func (GalaxyPOVNodeGroupBy) EnumDescriptor() ([]byte, []int) { - return file_api_v1_k8s_proto_rawDescGZIP(), []int{0} + return file_api_v1_k8s_proto_rawDescGZIP(), []int{4} } type GalaxyPOVWorkloadGroupBy int32 @@ -170,11 +392,11 @@ func (x GalaxyPOVWorkloadGroupBy) String() string { } func (GalaxyPOVWorkloadGroupBy) Descriptor() protoreflect.EnumDescriptor { - return file_api_v1_k8s_proto_enumTypes[1].Descriptor() + return file_api_v1_k8s_proto_enumTypes[5].Descriptor() } func (GalaxyPOVWorkloadGroupBy) Type() protoreflect.EnumType { - return &file_api_v1_k8s_proto_enumTypes[1] + return &file_api_v1_k8s_proto_enumTypes[5] } func (x GalaxyPOVWorkloadGroupBy) Number() protoreflect.EnumNumber { @@ -183,7 +405,7 @@ func (x GalaxyPOVWorkloadGroupBy) Number() protoreflect.EnumNumber { // Deprecated: Use GalaxyPOVWorkloadGroupBy.Descriptor instead. func (GalaxyPOVWorkloadGroupBy) EnumDescriptor() ([]byte, []int) { - return file_api_v1_k8s_proto_rawDescGZIP(), []int{1} + return file_api_v1_k8s_proto_rawDescGZIP(), []int{5} } // ClusterType represents different Kubernetes distribution types @@ -223,11 +445,11 @@ func (x ClusterType) String() string { } func (ClusterType) Descriptor() protoreflect.EnumDescriptor { - return file_api_v1_k8s_proto_enumTypes[2].Descriptor() + return file_api_v1_k8s_proto_enumTypes[6].Descriptor() } func (ClusterType) Type() protoreflect.EnumType { - return &file_api_v1_k8s_proto_enumTypes[2] + return &file_api_v1_k8s_proto_enumTypes[6] } func (x ClusterType) Number() protoreflect.EnumNumber { @@ -236,7 +458,7 @@ func (x ClusterType) Number() protoreflect.EnumNumber { // Deprecated: Use ClusterType.Descriptor instead. func (ClusterType) EnumDescriptor() ([]byte, []int) { - return file_api_v1_k8s_proto_rawDescGZIP(), []int{2} + return file_api_v1_k8s_proto_rawDescGZIP(), []int{6} } // GetNodeRequest is used to fetch a node in a specific cluster. @@ -1239,21 +1461,23 @@ func (x *GetWorkloadsSummaryResponse) GetOptimizationPotentialGpu() *WorkloadMet return nil } -// ListWorkloadsPaginatedResponse is one page of workloads plus the cursor for -// the next page and the total matching count (before pagination). -type ListWorkloadsPaginatedResponse struct { +// GetWorkloadRequestTrendsRequest takes the same scope as +// GetWorkloadsSummaryRequest so the overview card charts and its numbers +// describe the same workloads. +type GetWorkloadRequestTrendsRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - WorkloadItems []*WorkloadItem `protobuf:"bytes,1,rep,name=workload_items,json=workloadItems,proto3" json:"workload_items,omitempty"` // This page's workloads. - NextPageToken string `protobuf:"bytes,2,opt,name=next_page_token,json=nextPageToken,proto3" json:"next_page_token,omitempty"` // Opaque cursor for the next page; empty = no more data. - TotalCount int64 `protobuf:"varint,3,opt,name=total_count,json=totalCount,proto3" json:"total_count,omitempty"` // Total workloads matching the filters, before pagination. - PageSize int32 `protobuf:"varint,4,opt,name=page_size,json=pageSize,proto3" json:"page_size,omitempty"` // Effective page size applied after clamping; reflects the requested/clamped size, not len(workload_items). + TeamId string `protobuf:"bytes,1,opt,name=team_id,json=teamId,proto3" json:"team_id,omitempty"` + ClusterId string `protobuf:"bytes,2,opt,name=cluster_id,json=clusterId,proto3" json:"cluster_id,omitempty"` + StartTime *timestamppb.Timestamp `protobuf:"bytes,3,opt,name=start_time,json=startTime,proto3,oneof" json:"start_time,omitempty"` + EndTime *timestamppb.Timestamp `protobuf:"bytes,4,opt,name=end_time,json=endTime,proto3,oneof" json:"end_time,omitempty"` + Filters *WorkloadFilters `protobuf:"bytes,5,opt,name=filters,proto3,oneof" json:"filters,omitempty"` // Same WorkloadFilters as GetWorkloadsSummaryRequest. } -func (x *ListWorkloadsPaginatedResponse) Reset() { - *x = ListWorkloadsPaginatedResponse{} +func (x *GetWorkloadRequestTrendsRequest) Reset() { + *x = GetWorkloadRequestTrendsRequest{} if protoimpl.UnsafeEnabled { mi := &file_api_v1_k8s_proto_msgTypes[10] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -1261,13 +1485,13 @@ func (x *ListWorkloadsPaginatedResponse) Reset() { } } -func (x *ListWorkloadsPaginatedResponse) String() string { +func (x *GetWorkloadRequestTrendsRequest) String() string { return protoimpl.X.MessageStringOf(x) } -func (*ListWorkloadsPaginatedResponse) ProtoMessage() {} +func (*GetWorkloadRequestTrendsRequest) ProtoMessage() {} -func (x *ListWorkloadsPaginatedResponse) ProtoReflect() protoreflect.Message { +func (x *GetWorkloadRequestTrendsRequest) ProtoReflect() protoreflect.Message { mi := &file_api_v1_k8s_proto_msgTypes[10] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -1279,55 +1503,59 @@ func (x *ListWorkloadsPaginatedResponse) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use ListWorkloadsPaginatedResponse.ProtoReflect.Descriptor instead. -func (*ListWorkloadsPaginatedResponse) Descriptor() ([]byte, []int) { +// Deprecated: Use GetWorkloadRequestTrendsRequest.ProtoReflect.Descriptor instead. +func (*GetWorkloadRequestTrendsRequest) Descriptor() ([]byte, []int) { return file_api_v1_k8s_proto_rawDescGZIP(), []int{10} } -func (x *ListWorkloadsPaginatedResponse) GetWorkloadItems() []*WorkloadItem { +func (x *GetWorkloadRequestTrendsRequest) GetTeamId() string { if x != nil { - return x.WorkloadItems + return x.TeamId } - return nil + return "" } -func (x *ListWorkloadsPaginatedResponse) GetNextPageToken() string { +func (x *GetWorkloadRequestTrendsRequest) GetClusterId() string { if x != nil { - return x.NextPageToken + return x.ClusterId } return "" } -func (x *ListWorkloadsPaginatedResponse) GetTotalCount() int64 { +func (x *GetWorkloadRequestTrendsRequest) GetStartTime() *timestamppb.Timestamp { if x != nil { - return x.TotalCount + return x.StartTime } - return 0 + return nil } -func (x *ListWorkloadsPaginatedResponse) GetPageSize() int32 { +func (x *GetWorkloadRequestTrendsRequest) GetEndTime() *timestamppb.Timestamp { if x != nil { - return x.PageSize + return x.EndTime } - return 0 + return nil } -// GetResourcesRequest is uset to fetch resources from db. -type GetResourcesRequest struct { +func (x *GetWorkloadRequestTrendsRequest) GetFilters() *WorkloadFilters { + if x != nil { + return x.Filters + } + return nil +} + +// RequestTrendPoint is one bucket of a request series. timestamp is the bucket +// start in unix seconds; value is the average total request across the bucket. +type RequestTrendPoint struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - TeamId string `protobuf:"bytes,1,opt,name=team_id,json=teamId,proto3" json:"team_id,omitempty"` // Unique identifier for the team. - ClusterId *string `protobuf:"bytes,2,opt,name=cluster_id,json=clusterId,proto3,oneof" json:"cluster_id,omitempty"` // Unique identifier for the cluster. - Kind K8SObjectKind `protobuf:"varint,3,opt,name=kind,proto3,enum=api.v1.K8SObjectKind" json:"kind,omitempty"` // Type of object to get. - Search *string `protobuf:"bytes,4,opt,name=search,proto3,oneof" json:"search,omitempty"` // Search term to filter resources by name - Pagination *Pagination `protobuf:"bytes,11,opt,name=pagination,proto3" json:"pagination,omitempty"` // Pagination parameters - Filters []*ResourceFieldFilter `protobuf:"bytes,12,rep,name=filters,proto3" json:"filters,omitempty"` // Optional field-level filters; multiple filters AND together + Timestamp int64 `protobuf:"varint,1,opt,name=timestamp,proto3" json:"timestamp,omitempty"` + Value float64 `protobuf:"fixed64,2,opt,name=value,proto3" json:"value,omitempty"` } -func (x *GetResourcesRequest) Reset() { - *x = GetResourcesRequest{} +func (x *RequestTrendPoint) Reset() { + *x = RequestTrendPoint{} if protoimpl.UnsafeEnabled { mi := &file_api_v1_k8s_proto_msgTypes[11] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -1335,13 +1563,13 @@ func (x *GetResourcesRequest) Reset() { } } -func (x *GetResourcesRequest) String() string { +func (x *RequestTrendPoint) String() string { return protoimpl.X.MessageStringOf(x) } -func (*GetResourcesRequest) ProtoMessage() {} +func (*RequestTrendPoint) ProtoMessage() {} -func (x *GetResourcesRequest) ProtoReflect() protoreflect.Message { +func (x *RequestTrendPoint) ProtoReflect() protoreflect.Message { mi := &file_api_v1_k8s_proto_msgTypes[11] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -1353,86 +1581,131 @@ func (x *GetResourcesRequest) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use GetResourcesRequest.ProtoReflect.Descriptor instead. -func (*GetResourcesRequest) Descriptor() ([]byte, []int) { +// Deprecated: Use RequestTrendPoint.ProtoReflect.Descriptor instead. +func (*RequestTrendPoint) Descriptor() ([]byte, []int) { return file_api_v1_k8s_proto_rawDescGZIP(), []int{11} } -func (x *GetResourcesRequest) GetTeamId() string { +func (x *RequestTrendPoint) GetTimestamp() int64 { if x != nil { - return x.TeamId + return x.Timestamp } - return "" + return 0 } -func (x *GetResourcesRequest) GetClusterId() string { - if x != nil && x.ClusterId != nil { - return *x.ClusterId +func (x *RequestTrendPoint) GetValue() float64 { + if x != nil { + return x.Value } - return "" + return 0 } -func (x *GetResourcesRequest) GetKind() K8SObjectKind { - if x != nil { - return x.Kind +// RequestTrendSeries carries the bucketed points and the mean of the non-empty +// buckets, computed server-side so every reader quotes the same average. +type RequestTrendSeries struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Points []*RequestTrendPoint `protobuf:"bytes,1,rep,name=points,proto3" json:"points,omitempty"` + Average float64 `protobuf:"fixed64,2,opt,name=average,proto3" json:"average,omitempty"` +} + +func (x *RequestTrendSeries) Reset() { + *x = RequestTrendSeries{} + if protoimpl.UnsafeEnabled { + mi := &file_api_v1_k8s_proto_msgTypes[12] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } - return K8SObjectKind_K8S_OBJECT_KIND_UNSPECIFIED } -func (x *GetResourcesRequest) GetSearch() string { - if x != nil && x.Search != nil { - return *x.Search +func (x *RequestTrendSeries) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*RequestTrendSeries) ProtoMessage() {} + +func (x *RequestTrendSeries) ProtoReflect() protoreflect.Message { + mi := &file_api_v1_k8s_proto_msgTypes[12] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms } - return "" + return mi.MessageOf(x) } -func (x *GetResourcesRequest) GetPagination() *Pagination { +// Deprecated: Use RequestTrendSeries.ProtoReflect.Descriptor instead. +func (*RequestTrendSeries) Descriptor() ([]byte, []int) { + return file_api_v1_k8s_proto_rawDescGZIP(), []int{12} +} + +func (x *RequestTrendSeries) GetPoints() []*RequestTrendPoint { if x != nil { - return x.Pagination + return x.Points } return nil } -func (x *GetResourcesRequest) GetFilters() []*ResourceFieldFilter { +func (x *RequestTrendSeries) GetAverage() float64 { if x != nil { - return x.Filters + return x.Average } - return nil + return 0 } -// GetPodsRequest is used to fetch Pod resources from db. -type GetPodsRequest struct { +// GetWorkloadRequestTrendsResponse carries CPU values in millicores and memory +// values in bytes. Managed original falls back to the current request for a +// workload with no baseline rows, and counts an unset (NULL) baseline column +// as zero. +type GetWorkloadRequestTrendsResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - TeamId string `protobuf:"bytes,1,opt,name=team_id,json=teamId,proto3" json:"team_id,omitempty"` // Unique identifier for the team. - ClusterId *string `protobuf:"bytes,2,opt,name=cluster_id,json=clusterId,proto3,oneof" json:"cluster_id,omitempty"` // Unique identifier for the cluster. - Search *string `protobuf:"bytes,4,opt,name=search,proto3,oneof" json:"search,omitempty"` // Search term to filter pods by name - Pagination *Pagination `protobuf:"bytes,11,opt,name=pagination,proto3" json:"pagination,omitempty"` // Pagination parameters - NodeGroupNames []string `protobuf:"bytes,25,rep,name=node_group_names,json=nodeGroupNames,proto3" json:"node_group_names,omitempty"` // Optional filter for node groups - NodeIds []string `protobuf:"bytes,26,rep,name=node_ids,json=nodeIds,proto3" json:"node_ids,omitempty"` // Optional filter for specific node IDs - StartTime *timestamppb.Timestamp `protobuf:"bytes,27,opt,name=start_time,json=startTime,proto3" json:"start_time,omitempty"` // Start time for metrics query - EndTime *timestamppb.Timestamp `protobuf:"bytes,28,opt,name=end_time,json=endTime,proto3" json:"end_time,omitempty"` // End time for metrics query -} - -func (x *GetPodsRequest) Reset() { - *x = GetPodsRequest{} + UnmanagedCpuRequested *RequestTrendSeries `protobuf:"bytes,1,opt,name=unmanaged_cpu_requested,json=unmanagedCpuRequested,proto3" json:"unmanaged_cpu_requested,omitempty"` + UnmanagedMemoryRequested *RequestTrendSeries `protobuf:"bytes,2,opt,name=unmanaged_memory_requested,json=unmanagedMemoryRequested,proto3" json:"unmanaged_memory_requested,omitempty"` + ManagedCpuOriginal *RequestTrendSeries `protobuf:"bytes,3,opt,name=managed_cpu_original,json=managedCpuOriginal,proto3" json:"managed_cpu_original,omitempty"` + ManagedCpuOptimized *RequestTrendSeries `protobuf:"bytes,4,opt,name=managed_cpu_optimized,json=managedCpuOptimized,proto3" json:"managed_cpu_optimized,omitempty"` + ManagedMemoryOriginal *RequestTrendSeries `protobuf:"bytes,5,opt,name=managed_memory_original,json=managedMemoryOriginal,proto3" json:"managed_memory_original,omitempty"` + ManagedMemoryOptimized *RequestTrendSeries `protobuf:"bytes,6,opt,name=managed_memory_optimized,json=managedMemoryOptimized,proto3" json:"managed_memory_optimized,omitempty"` + ManagedWorkloadCount int32 `protobuf:"varint,7,opt,name=managed_workload_count,json=managedWorkloadCount,proto3" json:"managed_workload_count,omitempty"` + UnmanagedWorkloadCount int32 `protobuf:"varint,8,opt,name=unmanaged_workload_count,json=unmanagedWorkloadCount,proto3" json:"unmanaged_workload_count,omitempty"` + BucketSeconds int64 `protobuf:"varint,9,opt,name=bucket_seconds,json=bucketSeconds,proto3" json:"bucket_seconds,omitempty"` // Width of each bucket. + // Average total usage across each bucket, over the same containers and pods + // as the requested series. + UnmanagedCpuUsed *RequestTrendSeries `protobuf:"bytes,10,opt,name=unmanaged_cpu_used,json=unmanagedCpuUsed,proto3" json:"unmanaged_cpu_used,omitempty"` + UnmanagedMemoryUsed *RequestTrendSeries `protobuf:"bytes,11,opt,name=unmanaged_memory_used,json=unmanagedMemoryUsed,proto3" json:"unmanaged_memory_used,omitempty"` + ManagedCpuUsed *RequestTrendSeries `protobuf:"bytes,12,opt,name=managed_cpu_used,json=managedCpuUsed,proto3" json:"managed_cpu_used,omitempty"` + ManagedMemoryUsed *RequestTrendSeries `protobuf:"bytes,13,opt,name=managed_memory_used,json=managedMemoryUsed,proto3" json:"managed_memory_used,omitempty"` + // Average total request managed workloads would have if every container ran + // at its latest recommendation generated by then, whatever its status, times + // running pods. A container with no recommendation yet, or one whose + // recommendation leaves that resource alone, counts at its current request. + ManagedCpuRecommended *RequestTrendSeries `protobuf:"bytes,14,opt,name=managed_cpu_recommended,json=managedCpuRecommended,proto3" json:"managed_cpu_recommended,omitempty"` + ManagedMemoryRecommended *RequestTrendSeries `protobuf:"bytes,15,opt,name=managed_memory_recommended,json=managedMemoryRecommended,proto3" json:"managed_memory_recommended,omitempty"` +} + +func (x *GetWorkloadRequestTrendsResponse) Reset() { + *x = GetWorkloadRequestTrendsResponse{} if protoimpl.UnsafeEnabled { - mi := &file_api_v1_k8s_proto_msgTypes[12] + mi := &file_api_v1_k8s_proto_msgTypes[13] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } -func (x *GetPodsRequest) String() string { +func (x *GetWorkloadRequestTrendsResponse) String() string { return protoimpl.X.MessageStringOf(x) } -func (*GetPodsRequest) ProtoMessage() {} +func (*GetWorkloadRequestTrendsResponse) ProtoMessage() {} -func (x *GetPodsRequest) ProtoReflect() protoreflect.Message { - mi := &file_api_v1_k8s_proto_msgTypes[12] +func (x *GetWorkloadRequestTrendsResponse) ProtoReflect() protoreflect.Message { + mi := &file_api_v1_k8s_proto_msgTypes[13] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1443,142 +1716,131 @@ func (x *GetPodsRequest) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use GetPodsRequest.ProtoReflect.Descriptor instead. -func (*GetPodsRequest) Descriptor() ([]byte, []int) { - return file_api_v1_k8s_proto_rawDescGZIP(), []int{12} +// Deprecated: Use GetWorkloadRequestTrendsResponse.ProtoReflect.Descriptor instead. +func (*GetWorkloadRequestTrendsResponse) Descriptor() ([]byte, []int) { + return file_api_v1_k8s_proto_rawDescGZIP(), []int{13} } -func (x *GetPodsRequest) GetTeamId() string { +func (x *GetWorkloadRequestTrendsResponse) GetUnmanagedCpuRequested() *RequestTrendSeries { if x != nil { - return x.TeamId + return x.UnmanagedCpuRequested } - return "" + return nil } -func (x *GetPodsRequest) GetClusterId() string { - if x != nil && x.ClusterId != nil { - return *x.ClusterId +func (x *GetWorkloadRequestTrendsResponse) GetUnmanagedMemoryRequested() *RequestTrendSeries { + if x != nil { + return x.UnmanagedMemoryRequested } - return "" + return nil } -func (x *GetPodsRequest) GetSearch() string { - if x != nil && x.Search != nil { - return *x.Search +func (x *GetWorkloadRequestTrendsResponse) GetManagedCpuOriginal() *RequestTrendSeries { + if x != nil { + return x.ManagedCpuOriginal } - return "" + return nil } -func (x *GetPodsRequest) GetPagination() *Pagination { +func (x *GetWorkloadRequestTrendsResponse) GetManagedCpuOptimized() *RequestTrendSeries { if x != nil { - return x.Pagination + return x.ManagedCpuOptimized } return nil } -func (x *GetPodsRequest) GetNodeGroupNames() []string { +func (x *GetWorkloadRequestTrendsResponse) GetManagedMemoryOriginal() *RequestTrendSeries { if x != nil { - return x.NodeGroupNames + return x.ManagedMemoryOriginal } return nil } -func (x *GetPodsRequest) GetNodeIds() []string { +func (x *GetWorkloadRequestTrendsResponse) GetManagedMemoryOptimized() *RequestTrendSeries { if x != nil { - return x.NodeIds + return x.ManagedMemoryOptimized } return nil } -func (x *GetPodsRequest) GetStartTime() *timestamppb.Timestamp { +func (x *GetWorkloadRequestTrendsResponse) GetManagedWorkloadCount() int32 { if x != nil { - return x.StartTime + return x.ManagedWorkloadCount } - return nil + return 0 } -func (x *GetPodsRequest) GetEndTime() *timestamppb.Timestamp { +func (x *GetWorkloadRequestTrendsResponse) GetUnmanagedWorkloadCount() int32 { if x != nil { - return x.EndTime + return x.UnmanagedWorkloadCount } - return nil + return 0 } -// GetPodsResponse is the response for getting pod resources. -type GetPodsResponse struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - WorkloadItems []*WorkloadItem `protobuf:"bytes,1,rep,name=workload_items,json=workloadItems,proto3" json:"workload_items,omitempty"` // List of pods. - Pagination *Pagination `protobuf:"bytes,2,opt,name=pagination,proto3" json:"pagination,omitempty"` // Pagination information +func (x *GetWorkloadRequestTrendsResponse) GetBucketSeconds() int64 { + if x != nil { + return x.BucketSeconds + } + return 0 } -func (x *GetPodsResponse) Reset() { - *x = GetPodsResponse{} - if protoimpl.UnsafeEnabled { - mi := &file_api_v1_k8s_proto_msgTypes[13] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) +func (x *GetWorkloadRequestTrendsResponse) GetUnmanagedCpuUsed() *RequestTrendSeries { + if x != nil { + return x.UnmanagedCpuUsed } + return nil } -func (x *GetPodsResponse) String() string { - return protoimpl.X.MessageStringOf(x) +func (x *GetWorkloadRequestTrendsResponse) GetUnmanagedMemoryUsed() *RequestTrendSeries { + if x != nil { + return x.UnmanagedMemoryUsed + } + return nil } -func (*GetPodsResponse) ProtoMessage() {} - -func (x *GetPodsResponse) ProtoReflect() protoreflect.Message { - mi := &file_api_v1_k8s_proto_msgTypes[13] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms +func (x *GetWorkloadRequestTrendsResponse) GetManagedCpuUsed() *RequestTrendSeries { + if x != nil { + return x.ManagedCpuUsed } - return mi.MessageOf(x) + return nil } -// Deprecated: Use GetPodsResponse.ProtoReflect.Descriptor instead. -func (*GetPodsResponse) Descriptor() ([]byte, []int) { - return file_api_v1_k8s_proto_rawDescGZIP(), []int{13} +func (x *GetWorkloadRequestTrendsResponse) GetManagedMemoryUsed() *RequestTrendSeries { + if x != nil { + return x.ManagedMemoryUsed + } + return nil } -func (x *GetPodsResponse) GetWorkloadItems() []*WorkloadItem { +func (x *GetWorkloadRequestTrendsResponse) GetManagedCpuRecommended() *RequestTrendSeries { if x != nil { - return x.WorkloadItems + return x.ManagedCpuRecommended } return nil } -func (x *GetPodsResponse) GetPagination() *Pagination { +func (x *GetWorkloadRequestTrendsResponse) GetManagedMemoryRecommended() *RequestTrendSeries { if x != nil { - return x.Pagination + return x.ManagedMemoryRecommended } return nil } -// GetWorkloadRequest is used to fetch details of a specific workload in a cluster. -type GetWorkloadRequest struct { +// ListWorkloadsPaginatedResponse is one page of workloads plus the cursor for +// the next page and the total matching count (before pagination). +type ListWorkloadsPaginatedResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - TeamId string `protobuf:"bytes,1,opt,name=team_id,json=teamId,proto3" json:"team_id,omitempty"` // Unique identifier for the team. - ClusterId string `protobuf:"bytes,2,opt,name=cluster_id,json=clusterId,proto3" json:"cluster_id,omitempty"` // Unique identifier for the cluster. - Kind string `protobuf:"bytes,3,opt,name=kind,proto3" json:"kind,omitempty"` // Kind of the workload (e.g., Deployment, StatefulSet). - Uid string `protobuf:"bytes,4,opt,name=uid,proto3" json:"uid,omitempty"` // Unique identifier for the workload. - IncludeDeleted bool `protobuf:"varint,11,opt,name=include_deleted,json=includeDeleted,proto3" json:"include_deleted,omitempty"` - WithDetails bool `protobuf:"varint,12,opt,name=with_details,json=withDetails,proto3" json:"with_details,omitempty"` // Optional flag to include additional resource details - WithMetadata bool `protobuf:"varint,13,opt,name=with_metadata,json=withMetadata,proto3" json:"with_metadata,omitempty"` // Optional flag to include metadata - StartTime *timestamppb.Timestamp `protobuf:"bytes,21,opt,name=start_time,json=startTime,proto3,oneof" json:"start_time,omitempty"` - EndTime *timestamppb.Timestamp `protobuf:"bytes,22,opt,name=end_time,json=endTime,proto3,oneof" json:"end_time,omitempty"` + WorkloadItems []*WorkloadItem `protobuf:"bytes,1,rep,name=workload_items,json=workloadItems,proto3" json:"workload_items,omitempty"` // This page's workloads. + NextPageToken string `protobuf:"bytes,2,opt,name=next_page_token,json=nextPageToken,proto3" json:"next_page_token,omitempty"` // Opaque cursor for the next page; empty = no more data. + TotalCount int64 `protobuf:"varint,3,opt,name=total_count,json=totalCount,proto3" json:"total_count,omitempty"` // Total workloads matching the filters, before pagination. + PageSize int32 `protobuf:"varint,4,opt,name=page_size,json=pageSize,proto3" json:"page_size,omitempty"` // Effective page size applied after clamping; reflects the requested/clamped size, not len(workload_items). } -func (x *GetWorkloadRequest) Reset() { - *x = GetWorkloadRequest{} +func (x *ListWorkloadsPaginatedResponse) Reset() { + *x = ListWorkloadsPaginatedResponse{} if protoimpl.UnsafeEnabled { mi := &file_api_v1_k8s_proto_msgTypes[14] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -1586,13 +1848,13 @@ func (x *GetWorkloadRequest) Reset() { } } -func (x *GetWorkloadRequest) String() string { +func (x *ListWorkloadsPaginatedResponse) String() string { return protoimpl.X.MessageStringOf(x) } -func (*GetWorkloadRequest) ProtoMessage() {} +func (*ListWorkloadsPaginatedResponse) ProtoMessage() {} -func (x *GetWorkloadRequest) ProtoReflect() protoreflect.Message { +func (x *ListWorkloadsPaginatedResponse) ProtoReflect() protoreflect.Message { mi := &file_api_v1_k8s_proto_msgTypes[14] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -1604,89 +1866,55 @@ func (x *GetWorkloadRequest) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use GetWorkloadRequest.ProtoReflect.Descriptor instead. -func (*GetWorkloadRequest) Descriptor() ([]byte, []int) { +// Deprecated: Use ListWorkloadsPaginatedResponse.ProtoReflect.Descriptor instead. +func (*ListWorkloadsPaginatedResponse) Descriptor() ([]byte, []int) { return file_api_v1_k8s_proto_rawDescGZIP(), []int{14} } -func (x *GetWorkloadRequest) GetTeamId() string { - if x != nil { - return x.TeamId - } - return "" -} - -func (x *GetWorkloadRequest) GetClusterId() string { - if x != nil { - return x.ClusterId - } - return "" -} - -func (x *GetWorkloadRequest) GetKind() string { +func (x *ListWorkloadsPaginatedResponse) GetWorkloadItems() []*WorkloadItem { if x != nil { - return x.Kind + return x.WorkloadItems } - return "" + return nil } -func (x *GetWorkloadRequest) GetUid() string { +func (x *ListWorkloadsPaginatedResponse) GetNextPageToken() string { if x != nil { - return x.Uid + return x.NextPageToken } return "" } -func (x *GetWorkloadRequest) GetIncludeDeleted() bool { - if x != nil { - return x.IncludeDeleted - } - return false -} - -func (x *GetWorkloadRequest) GetWithDetails() bool { - if x != nil { - return x.WithDetails - } - return false -} - -func (x *GetWorkloadRequest) GetWithMetadata() bool { - if x != nil { - return x.WithMetadata - } - return false -} - -func (x *GetWorkloadRequest) GetStartTime() *timestamppb.Timestamp { +func (x *ListWorkloadsPaginatedResponse) GetTotalCount() int64 { if x != nil { - return x.StartTime + return x.TotalCount } - return nil + return 0 } -func (x *GetWorkloadRequest) GetEndTime() *timestamppb.Timestamp { +func (x *ListWorkloadsPaginatedResponse) GetPageSize() int32 { if x != nil { - return x.EndTime + return x.PageSize } - return nil + return 0 } -// GetClustersRequest is used to fetch clusters for a specific team. -type GetClustersRequest struct { +// GetResourcesRequest is uset to fetch resources from db. +type GetResourcesRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - TeamId string `protobuf:"bytes,1,opt,name=team_id,json=teamId,proto3" json:"team_id,omitempty"` // Unique identifier for the team. - StartTime *timestamppb.Timestamp `protobuf:"bytes,11,opt,name=start_time,json=startTime,proto3,oneof" json:"start_time,omitempty"` - EndTime *timestamppb.Timestamp `protobuf:"bytes,12,opt,name=end_time,json=endTime,proto3,oneof" json:"end_time,omitempty"` - BucketSize *int32 `protobuf:"varint,13,opt,name=bucket_size,json=bucketSize,proto3,oneof" json:"bucket_size,omitempty"` - NodeGroupNames []string `protobuf:"bytes,14,rep,name=node_group_names,json=nodeGroupNames,proto3" json:"node_group_names,omitempty"` // Optional filter for node groups + TeamId string `protobuf:"bytes,1,opt,name=team_id,json=teamId,proto3" json:"team_id,omitempty"` // Unique identifier for the team. + ClusterId *string `protobuf:"bytes,2,opt,name=cluster_id,json=clusterId,proto3,oneof" json:"cluster_id,omitempty"` // Unique identifier for the cluster. + Kind K8SObjectKind `protobuf:"varint,3,opt,name=kind,proto3,enum=api.v1.K8SObjectKind" json:"kind,omitempty"` // Type of object to get. + Search *string `protobuf:"bytes,4,opt,name=search,proto3,oneof" json:"search,omitempty"` // Search term to filter resources by name + Pagination *Pagination `protobuf:"bytes,11,opt,name=pagination,proto3" json:"pagination,omitempty"` // Pagination parameters + Filters []*ResourceFieldFilter `protobuf:"bytes,12,rep,name=filters,proto3" json:"filters,omitempty"` // Optional field-level filters; multiple filters AND together } -func (x *GetClustersRequest) Reset() { - *x = GetClustersRequest{} +func (x *GetResourcesRequest) Reset() { + *x = GetResourcesRequest{} if protoimpl.UnsafeEnabled { mi := &file_api_v1_k8s_proto_msgTypes[15] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -1694,13 +1922,13 @@ func (x *GetClustersRequest) Reset() { } } -func (x *GetClustersRequest) String() string { +func (x *GetResourcesRequest) String() string { return protoimpl.X.MessageStringOf(x) } -func (*GetClustersRequest) ProtoMessage() {} +func (*GetResourcesRequest) ProtoMessage() {} -func (x *GetClustersRequest) ProtoReflect() protoreflect.Message { +func (x *GetResourcesRequest) ProtoReflect() protoreflect.Message { mi := &file_api_v1_k8s_proto_msgTypes[15] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -1712,62 +1940,71 @@ func (x *GetClustersRequest) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use GetClustersRequest.ProtoReflect.Descriptor instead. -func (*GetClustersRequest) Descriptor() ([]byte, []int) { +// Deprecated: Use GetResourcesRequest.ProtoReflect.Descriptor instead. +func (*GetResourcesRequest) Descriptor() ([]byte, []int) { return file_api_v1_k8s_proto_rawDescGZIP(), []int{15} } -func (x *GetClustersRequest) GetTeamId() string { +func (x *GetResourcesRequest) GetTeamId() string { if x != nil { return x.TeamId } return "" } -func (x *GetClustersRequest) GetStartTime() *timestamppb.Timestamp { - if x != nil { - return x.StartTime +func (x *GetResourcesRequest) GetClusterId() string { + if x != nil && x.ClusterId != nil { + return *x.ClusterId } - return nil + return "" } -func (x *GetClustersRequest) GetEndTime() *timestamppb.Timestamp { +func (x *GetResourcesRequest) GetKind() K8SObjectKind { if x != nil { - return x.EndTime + return x.Kind } - return nil + return K8SObjectKind_K8S_OBJECT_KIND_UNSPECIFIED } -func (x *GetClustersRequest) GetBucketSize() int32 { - if x != nil && x.BucketSize != nil { - return *x.BucketSize +func (x *GetResourcesRequest) GetSearch() string { + if x != nil && x.Search != nil { + return *x.Search } - return 0 + return "" } -func (x *GetClustersRequest) GetNodeGroupNames() []string { +func (x *GetResourcesRequest) GetPagination() *Pagination { if x != nil { - return x.NodeGroupNames + return x.Pagination } return nil } -// ListClustersRequest is used to fetch clusters for a specific team with pagination. -// Deprecated: Use GetClustersRequest instead. -// -// Deprecated: Marked as deprecated in api/v1/k8s.proto. -type ListClustersRequest struct { +func (x *GetResourcesRequest) GetFilters() []*ResourceFieldFilter { + if x != nil { + return x.Filters + } + return nil +} + +// GetPodsRequest is used to fetch Pod resources from db. +type GetPodsRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - TeamId string `protobuf:"bytes,1,opt,name=team_id,json=teamId,proto3" json:"team_id,omitempty"` // Unique identifier for the team. - Search *string `protobuf:"bytes,2,opt,name=search,proto3,oneof" json:"search,omitempty"` // Search term to filter clusters by name or ID - Pagination *Pagination `protobuf:"bytes,11,opt,name=pagination,proto3" json:"pagination,omitempty"` // Pagination parameters + TeamId string `protobuf:"bytes,1,opt,name=team_id,json=teamId,proto3" json:"team_id,omitempty"` // Unique identifier for the team. + ClusterId *string `protobuf:"bytes,2,opt,name=cluster_id,json=clusterId,proto3,oneof" json:"cluster_id,omitempty"` // Unique identifier for the cluster. + Search *string `protobuf:"bytes,4,opt,name=search,proto3,oneof" json:"search,omitempty"` // Search term to filter pods by name + Pagination *Pagination `protobuf:"bytes,11,opt,name=pagination,proto3" json:"pagination,omitempty"` // Pagination parameters + NodeGroupNames []string `protobuf:"bytes,25,rep,name=node_group_names,json=nodeGroupNames,proto3" json:"node_group_names,omitempty"` // Optional filter for node groups + NodeIds []string `protobuf:"bytes,26,rep,name=node_ids,json=nodeIds,proto3" json:"node_ids,omitempty"` // Optional filter for specific node IDs + StartTime *timestamppb.Timestamp `protobuf:"bytes,27,opt,name=start_time,json=startTime,proto3" json:"start_time,omitempty"` // Start time for metrics query + EndTime *timestamppb.Timestamp `protobuf:"bytes,28,opt,name=end_time,json=endTime,proto3" json:"end_time,omitempty"` // End time for metrics query } -func (x *ListClustersRequest) Reset() { - *x = ListClustersRequest{} +func (x *GetPodsRequest) Reset() { + *x = GetPodsRequest{} if protoimpl.UnsafeEnabled { mi := &file_api_v1_k8s_proto_msgTypes[16] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -1775,13 +2012,13 @@ func (x *ListClustersRequest) Reset() { } } -func (x *ListClustersRequest) String() string { +func (x *GetPodsRequest) String() string { return protoimpl.X.MessageStringOf(x) } -func (*ListClustersRequest) ProtoMessage() {} +func (*GetPodsRequest) ProtoMessage() {} -func (x *ListClustersRequest) ProtoReflect() protoreflect.Message { +func (x *GetPodsRequest) ProtoReflect() protoreflect.Message { mi := &file_api_v1_k8s_proto_msgTypes[16] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -1793,48 +2030,79 @@ func (x *ListClustersRequest) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use ListClustersRequest.ProtoReflect.Descriptor instead. -func (*ListClustersRequest) Descriptor() ([]byte, []int) { +// Deprecated: Use GetPodsRequest.ProtoReflect.Descriptor instead. +func (*GetPodsRequest) Descriptor() ([]byte, []int) { return file_api_v1_k8s_proto_rawDescGZIP(), []int{16} } -func (x *ListClustersRequest) GetTeamId() string { +func (x *GetPodsRequest) GetTeamId() string { if x != nil { return x.TeamId } return "" } -func (x *ListClustersRequest) GetSearch() string { +func (x *GetPodsRequest) GetClusterId() string { + if x != nil && x.ClusterId != nil { + return *x.ClusterId + } + return "" +} + +func (x *GetPodsRequest) GetSearch() string { if x != nil && x.Search != nil { return *x.Search } return "" } -func (x *ListClustersRequest) GetPagination() *Pagination { +func (x *GetPodsRequest) GetPagination() *Pagination { if x != nil { return x.Pagination } return nil } -// GetClusterRequest is used to fetch details of a specific cluster for a team. -type GetClusterRequest struct { +func (x *GetPodsRequest) GetNodeGroupNames() []string { + if x != nil { + return x.NodeGroupNames + } + return nil +} + +func (x *GetPodsRequest) GetNodeIds() []string { + if x != nil { + return x.NodeIds + } + return nil +} + +func (x *GetPodsRequest) GetStartTime() *timestamppb.Timestamp { + if x != nil { + return x.StartTime + } + return nil +} + +func (x *GetPodsRequest) GetEndTime() *timestamppb.Timestamp { + if x != nil { + return x.EndTime + } + return nil +} + +// GetPodsResponse is the response for getting pod resources. +type GetPodsResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - TeamId string `protobuf:"bytes,1,opt,name=team_id,json=teamId,proto3" json:"team_id,omitempty"` // Unique identifier for the team. - ClusterId string `protobuf:"bytes,2,opt,name=cluster_id,json=clusterId,proto3" json:"cluster_id,omitempty"` // Unique identifier for the cluster. - StartTime *timestamppb.Timestamp `protobuf:"bytes,11,opt,name=start_time,json=startTime,proto3,oneof" json:"start_time,omitempty"` - EndTime *timestamppb.Timestamp `protobuf:"bytes,12,opt,name=end_time,json=endTime,proto3,oneof" json:"end_time,omitempty"` - BucketSize *int32 `protobuf:"varint,13,opt,name=bucket_size,json=bucketSize,proto3,oneof" json:"bucket_size,omitempty"` - NodeGroupNames []string `protobuf:"bytes,14,rep,name=node_group_names,json=nodeGroupNames,proto3" json:"node_group_names,omitempty"` // Optional filter for node groups + WorkloadItems []*WorkloadItem `protobuf:"bytes,1,rep,name=workload_items,json=workloadItems,proto3" json:"workload_items,omitempty"` // List of pods. + Pagination *Pagination `protobuf:"bytes,2,opt,name=pagination,proto3" json:"pagination,omitempty"` // Pagination information } -func (x *GetClusterRequest) Reset() { - *x = GetClusterRequest{} +func (x *GetPodsResponse) Reset() { + *x = GetPodsResponse{} if protoimpl.UnsafeEnabled { mi := &file_api_v1_k8s_proto_msgTypes[17] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -1842,13 +2110,13 @@ func (x *GetClusterRequest) Reset() { } } -func (x *GetClusterRequest) String() string { +func (x *GetPodsResponse) String() string { return protoimpl.X.MessageStringOf(x) } -func (*GetClusterRequest) ProtoMessage() {} +func (*GetPodsResponse) ProtoMessage() {} -func (x *GetClusterRequest) ProtoReflect() protoreflect.Message { +func (x *GetPodsResponse) ProtoReflect() protoreflect.Message { mi := &file_api_v1_k8s_proto_msgTypes[17] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -1860,65 +2128,44 @@ func (x *GetClusterRequest) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use GetClusterRequest.ProtoReflect.Descriptor instead. -func (*GetClusterRequest) Descriptor() ([]byte, []int) { +// Deprecated: Use GetPodsResponse.ProtoReflect.Descriptor instead. +func (*GetPodsResponse) Descriptor() ([]byte, []int) { return file_api_v1_k8s_proto_rawDescGZIP(), []int{17} } -func (x *GetClusterRequest) GetTeamId() string { - if x != nil { - return x.TeamId - } - return "" -} - -func (x *GetClusterRequest) GetClusterId() string { - if x != nil { - return x.ClusterId - } - return "" -} - -func (x *GetClusterRequest) GetStartTime() *timestamppb.Timestamp { - if x != nil { - return x.StartTime - } - return nil -} - -func (x *GetClusterRequest) GetEndTime() *timestamppb.Timestamp { +func (x *GetPodsResponse) GetWorkloadItems() []*WorkloadItem { if x != nil { - return x.EndTime + return x.WorkloadItems } return nil } -func (x *GetClusterRequest) GetBucketSize() int32 { - if x != nil && x.BucketSize != nil { - return *x.BucketSize - } - return 0 -} - -func (x *GetClusterRequest) GetNodeGroupNames() []string { +func (x *GetPodsResponse) GetPagination() *Pagination { if x != nil { - return x.NodeGroupNames + return x.Pagination } return nil } -// DeleteClusterRequest is used to delete a cluster and all its resources. -type DeleteClusterRequest struct { +// GetWorkloadRequest is used to fetch details of a specific workload in a cluster. +type GetWorkloadRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - TeamId string `protobuf:"bytes,1,opt,name=team_id,json=teamId,proto3" json:"team_id,omitempty"` // Unique identifier for the team. - ClusterId string `protobuf:"bytes,2,opt,name=cluster_id,json=clusterId,proto3" json:"cluster_id,omitempty"` // Unique identifier for the cluster. + TeamId string `protobuf:"bytes,1,opt,name=team_id,json=teamId,proto3" json:"team_id,omitempty"` // Unique identifier for the team. + ClusterId string `protobuf:"bytes,2,opt,name=cluster_id,json=clusterId,proto3" json:"cluster_id,omitempty"` // Unique identifier for the cluster. + Kind string `protobuf:"bytes,3,opt,name=kind,proto3" json:"kind,omitempty"` // Kind of the workload (e.g., Deployment, StatefulSet). + Uid string `protobuf:"bytes,4,opt,name=uid,proto3" json:"uid,omitempty"` // Unique identifier for the workload. + IncludeDeleted bool `protobuf:"varint,11,opt,name=include_deleted,json=includeDeleted,proto3" json:"include_deleted,omitempty"` + WithDetails bool `protobuf:"varint,12,opt,name=with_details,json=withDetails,proto3" json:"with_details,omitempty"` // Optional flag to include additional resource details + WithMetadata bool `protobuf:"varint,13,opt,name=with_metadata,json=withMetadata,proto3" json:"with_metadata,omitempty"` // Optional flag to include metadata + StartTime *timestamppb.Timestamp `protobuf:"bytes,21,opt,name=start_time,json=startTime,proto3,oneof" json:"start_time,omitempty"` + EndTime *timestamppb.Timestamp `protobuf:"bytes,22,opt,name=end_time,json=endTime,proto3,oneof" json:"end_time,omitempty"` } -func (x *DeleteClusterRequest) Reset() { - *x = DeleteClusterRequest{} +func (x *GetWorkloadRequest) Reset() { + *x = GetWorkloadRequest{} if protoimpl.UnsafeEnabled { mi := &file_api_v1_k8s_proto_msgTypes[18] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -1926,13 +2173,13 @@ func (x *DeleteClusterRequest) Reset() { } } -func (x *DeleteClusterRequest) String() string { +func (x *GetWorkloadRequest) String() string { return protoimpl.X.MessageStringOf(x) } -func (*DeleteClusterRequest) ProtoMessage() {} +func (*GetWorkloadRequest) ProtoMessage() {} -func (x *DeleteClusterRequest) ProtoReflect() protoreflect.Message { +func (x *GetWorkloadRequest) ProtoReflect() protoreflect.Message { mi := &file_api_v1_k8s_proto_msgTypes[18] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -1944,96 +2191,104 @@ func (x *DeleteClusterRequest) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use DeleteClusterRequest.ProtoReflect.Descriptor instead. -func (*DeleteClusterRequest) Descriptor() ([]byte, []int) { +// Deprecated: Use GetWorkloadRequest.ProtoReflect.Descriptor instead. +func (*GetWorkloadRequest) Descriptor() ([]byte, []int) { return file_api_v1_k8s_proto_rawDescGZIP(), []int{18} } -func (x *DeleteClusterRequest) GetTeamId() string { +func (x *GetWorkloadRequest) GetTeamId() string { if x != nil { return x.TeamId } return "" } -func (x *DeleteClusterRequest) GetClusterId() string { +func (x *GetWorkloadRequest) GetClusterId() string { if x != nil { return x.ClusterId } return "" } -// DeleteClusterResponse is the response for a delete cluster request. -type DeleteClusterResponse struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields +func (x *GetWorkloadRequest) GetKind() string { + if x != nil { + return x.Kind + } + return "" } -func (x *DeleteClusterResponse) Reset() { - *x = DeleteClusterResponse{} - if protoimpl.UnsafeEnabled { - mi := &file_api_v1_k8s_proto_msgTypes[19] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) +func (x *GetWorkloadRequest) GetUid() string { + if x != nil { + return x.Uid } + return "" } -func (x *DeleteClusterResponse) String() string { - return protoimpl.X.MessageStringOf(x) +func (x *GetWorkloadRequest) GetIncludeDeleted() bool { + if x != nil { + return x.IncludeDeleted + } + return false } -func (*DeleteClusterResponse) ProtoMessage() {} +func (x *GetWorkloadRequest) GetWithDetails() bool { + if x != nil { + return x.WithDetails + } + return false +} -func (x *DeleteClusterResponse) ProtoReflect() protoreflect.Message { - mi := &file_api_v1_k8s_proto_msgTypes[19] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms +func (x *GetWorkloadRequest) GetWithMetadata() bool { + if x != nil { + return x.WithMetadata } - return mi.MessageOf(x) + return false } -// Deprecated: Use DeleteClusterResponse.ProtoReflect.Descriptor instead. -func (*DeleteClusterResponse) Descriptor() ([]byte, []int) { - return file_api_v1_k8s_proto_rawDescGZIP(), []int{19} +func (x *GetWorkloadRequest) GetStartTime() *timestamppb.Timestamp { + if x != nil { + return x.StartTime + } + return nil } -// UpdateClusterRequest is used to update the configuration of an existing cluster. -// only certain fields can be updated, such as name -type UpdateClusterRequest struct { +func (x *GetWorkloadRequest) GetEndTime() *timestamppb.Timestamp { + if x != nil { + return x.EndTime + } + return nil +} + +// GetClustersRequest is used to fetch clusters for a specific team. +type GetClustersRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - TeamId string `protobuf:"bytes,1,opt,name=team_id,json=teamId,proto3" json:"team_id,omitempty"` // Unique identifier for the team. - ClusterId string `protobuf:"bytes,2,opt,name=cluster_id,json=clusterId,proto3" json:"cluster_id,omitempty"` // Unique identifier for the cluster. - ClusterName string `protobuf:"bytes,3,opt,name=cluster_name,json=clusterName,proto3" json:"cluster_name,omitempty"` // Updated cluster details. - CpuCostPerHour float64 `protobuf:"fixed64,4,opt,name=cpu_cost_per_hour,json=cpuCostPerHour,proto3" json:"cpu_cost_per_hour,omitempty"` // Estimated CPU cost per hour for one vcpu. - MemoryCostPerHour float64 `protobuf:"fixed64,5,opt,name=memory_cost_per_hour,json=memoryCostPerHour,proto3" json:"memory_cost_per_hour,omitempty"` // Estimated memory cost per hour for one GiB of memory. - GpuCostPerHour float64 `protobuf:"fixed64,6,opt,name=gpu_cost_per_hour,json=gpuCostPerHour,proto3" json:"gpu_cost_per_hour,omitempty"` // Estimate GPU cost per hour for one GPU. + TeamId string `protobuf:"bytes,1,opt,name=team_id,json=teamId,proto3" json:"team_id,omitempty"` // Unique identifier for the team. + StartTime *timestamppb.Timestamp `protobuf:"bytes,11,opt,name=start_time,json=startTime,proto3,oneof" json:"start_time,omitempty"` + EndTime *timestamppb.Timestamp `protobuf:"bytes,12,opt,name=end_time,json=endTime,proto3,oneof" json:"end_time,omitempty"` + BucketSize *int32 `protobuf:"varint,13,opt,name=bucket_size,json=bucketSize,proto3,oneof" json:"bucket_size,omitempty"` + NodeGroupNames []string `protobuf:"bytes,14,rep,name=node_group_names,json=nodeGroupNames,proto3" json:"node_group_names,omitempty"` // Optional filter for node groups } -func (x *UpdateClusterRequest) Reset() { - *x = UpdateClusterRequest{} +func (x *GetClustersRequest) Reset() { + *x = GetClustersRequest{} if protoimpl.UnsafeEnabled { - mi := &file_api_v1_k8s_proto_msgTypes[20] + mi := &file_api_v1_k8s_proto_msgTypes[19] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } -func (x *UpdateClusterRequest) String() string { +func (x *GetClustersRequest) String() string { return protoimpl.X.MessageStringOf(x) } -func (*UpdateClusterRequest) ProtoMessage() {} +func (*GetClustersRequest) ProtoMessage() {} -func (x *UpdateClusterRequest) ProtoReflect() protoreflect.Message { - mi := &file_api_v1_k8s_proto_msgTypes[20] +func (x *GetClustersRequest) ProtoReflect() protoreflect.Message { + mi := &file_api_v1_k8s_proto_msgTypes[19] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2044,79 +2299,77 @@ func (x *UpdateClusterRequest) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use UpdateClusterRequest.ProtoReflect.Descriptor instead. -func (*UpdateClusterRequest) Descriptor() ([]byte, []int) { - return file_api_v1_k8s_proto_rawDescGZIP(), []int{20} +// Deprecated: Use GetClustersRequest.ProtoReflect.Descriptor instead. +func (*GetClustersRequest) Descriptor() ([]byte, []int) { + return file_api_v1_k8s_proto_rawDescGZIP(), []int{19} } -func (x *UpdateClusterRequest) GetTeamId() string { +func (x *GetClustersRequest) GetTeamId() string { if x != nil { return x.TeamId } return "" } -func (x *UpdateClusterRequest) GetClusterId() string { +func (x *GetClustersRequest) GetStartTime() *timestamppb.Timestamp { if x != nil { - return x.ClusterId + return x.StartTime } - return "" + return nil } -func (x *UpdateClusterRequest) GetClusterName() string { +func (x *GetClustersRequest) GetEndTime() *timestamppb.Timestamp { if x != nil { - return x.ClusterName + return x.EndTime } - return "" + return nil } -func (x *UpdateClusterRequest) GetCpuCostPerHour() float64 { - if x != nil { - return x.CpuCostPerHour +func (x *GetClustersRequest) GetBucketSize() int32 { + if x != nil && x.BucketSize != nil { + return *x.BucketSize } return 0 } -func (x *UpdateClusterRequest) GetMemoryCostPerHour() float64 { - if x != nil { - return x.MemoryCostPerHour - } - return 0 -} - -func (x *UpdateClusterRequest) GetGpuCostPerHour() float64 { +func (x *GetClustersRequest) GetNodeGroupNames() []string { if x != nil { - return x.GpuCostPerHour + return x.NodeGroupNames } - return 0 + return nil } -// UpdateClusterResponse is the response for an update cluster request. -type UpdateClusterResponse struct { +// ListClustersRequest is used to fetch clusters for a specific team with pagination. +// Deprecated: Use GetClustersRequest instead. +// +// Deprecated: Marked as deprecated in api/v1/k8s.proto. +type ListClustersRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Cluster *Cluster `protobuf:"bytes,1,opt,name=cluster,proto3" json:"cluster,omitempty"` // Updated cluster details. + TeamId string `protobuf:"bytes,1,opt,name=team_id,json=teamId,proto3" json:"team_id,omitempty"` // Unique identifier for the team. + Search *string `protobuf:"bytes,2,opt,name=search,proto3,oneof" json:"search,omitempty"` // Search term to filter clusters by name or ID + Pagination *Pagination `protobuf:"bytes,11,opt,name=pagination,proto3" json:"pagination,omitempty"` // Pagination parameters } -func (x *UpdateClusterResponse) Reset() { - *x = UpdateClusterResponse{} +func (x *ListClustersRequest) Reset() { + *x = ListClustersRequest{} if protoimpl.UnsafeEnabled { - mi := &file_api_v1_k8s_proto_msgTypes[21] + mi := &file_api_v1_k8s_proto_msgTypes[20] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } -func (x *UpdateClusterResponse) String() string { +func (x *ListClustersRequest) String() string { return protoimpl.X.MessageStringOf(x) } -func (*UpdateClusterResponse) ProtoMessage() {} +func (*ListClustersRequest) ProtoMessage() {} -func (x *UpdateClusterResponse) ProtoReflect() protoreflect.Message { - mi := &file_api_v1_k8s_proto_msgTypes[21] +func (x *ListClustersRequest) ProtoReflect() protoreflect.Message { + mi := &file_api_v1_k8s_proto_msgTypes[20] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2127,45 +2380,63 @@ func (x *UpdateClusterResponse) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use UpdateClusterResponse.ProtoReflect.Descriptor instead. -func (*UpdateClusterResponse) Descriptor() ([]byte, []int) { - return file_api_v1_k8s_proto_rawDescGZIP(), []int{21} +// Deprecated: Use ListClustersRequest.ProtoReflect.Descriptor instead. +func (*ListClustersRequest) Descriptor() ([]byte, []int) { + return file_api_v1_k8s_proto_rawDescGZIP(), []int{20} } -func (x *UpdateClusterResponse) GetCluster() *Cluster { +func (x *ListClustersRequest) GetTeamId() string { if x != nil { - return x.Cluster + return x.TeamId + } + return "" +} + +func (x *ListClustersRequest) GetSearch() string { + if x != nil && x.Search != nil { + return *x.Search + } + return "" +} + +func (x *ListClustersRequest) GetPagination() *Pagination { + if x != nil { + return x.Pagination } return nil } -// GetResourcesResponse is the response for getting k8s resources. -type GetResourcesResponse struct { +// GetClusterRequest is used to fetch details of a specific cluster for a team. +type GetClusterRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - WorkloadItems []*WorkloadItem `protobuf:"bytes,1,rep,name=workload_items,json=workloadItems,proto3" json:"workload_items,omitempty"` // Return list of resources requested - Pagination *Pagination `protobuf:"bytes,2,opt,name=pagination,proto3" json:"pagination,omitempty"` // Pagination information + TeamId string `protobuf:"bytes,1,opt,name=team_id,json=teamId,proto3" json:"team_id,omitempty"` // Unique identifier for the team. + ClusterId string `protobuf:"bytes,2,opt,name=cluster_id,json=clusterId,proto3" json:"cluster_id,omitempty"` // Unique identifier for the cluster. + StartTime *timestamppb.Timestamp `protobuf:"bytes,11,opt,name=start_time,json=startTime,proto3,oneof" json:"start_time,omitempty"` + EndTime *timestamppb.Timestamp `protobuf:"bytes,12,opt,name=end_time,json=endTime,proto3,oneof" json:"end_time,omitempty"` + BucketSize *int32 `protobuf:"varint,13,opt,name=bucket_size,json=bucketSize,proto3,oneof" json:"bucket_size,omitempty"` + NodeGroupNames []string `protobuf:"bytes,14,rep,name=node_group_names,json=nodeGroupNames,proto3" json:"node_group_names,omitempty"` // Optional filter for node groups } -func (x *GetResourcesResponse) Reset() { - *x = GetResourcesResponse{} +func (x *GetClusterRequest) Reset() { + *x = GetClusterRequest{} if protoimpl.UnsafeEnabled { - mi := &file_api_v1_k8s_proto_msgTypes[22] + mi := &file_api_v1_k8s_proto_msgTypes[21] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } -func (x *GetResourcesResponse) String() string { +func (x *GetClusterRequest) String() string { return protoimpl.X.MessageStringOf(x) } -func (*GetResourcesResponse) ProtoMessage() {} +func (*GetClusterRequest) ProtoMessage() {} -func (x *GetResourcesResponse) ProtoReflect() protoreflect.Message { - mi := &file_api_v1_k8s_proto_msgTypes[22] +func (x *GetClusterRequest) ProtoReflect() protoreflect.Message { + mi := &file_api_v1_k8s_proto_msgTypes[21] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2176,53 +2447,80 @@ func (x *GetResourcesResponse) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use GetResourcesResponse.ProtoReflect.Descriptor instead. -func (*GetResourcesResponse) Descriptor() ([]byte, []int) { - return file_api_v1_k8s_proto_rawDescGZIP(), []int{22} +// Deprecated: Use GetClusterRequest.ProtoReflect.Descriptor instead. +func (*GetClusterRequest) Descriptor() ([]byte, []int) { + return file_api_v1_k8s_proto_rawDescGZIP(), []int{21} } -func (x *GetResourcesResponse) GetWorkloadItems() []*WorkloadItem { +func (x *GetClusterRequest) GetTeamId() string { if x != nil { - return x.WorkloadItems + return x.TeamId + } + return "" +} + +func (x *GetClusterRequest) GetClusterId() string { + if x != nil { + return x.ClusterId + } + return "" +} + +func (x *GetClusterRequest) GetStartTime() *timestamppb.Timestamp { + if x != nil { + return x.StartTime } return nil } -func (x *GetResourcesResponse) GetPagination() *Pagination { +func (x *GetClusterRequest) GetEndTime() *timestamppb.Timestamp { if x != nil { - return x.Pagination + return x.EndTime } return nil } -// GetResourceFilterOptionsRequest asks for the distinct filterable field values for a resource kind. -type GetResourceFilterOptionsRequest struct { +func (x *GetClusterRequest) GetBucketSize() int32 { + if x != nil && x.BucketSize != nil { + return *x.BucketSize + } + return 0 +} + +func (x *GetClusterRequest) GetNodeGroupNames() []string { + if x != nil { + return x.NodeGroupNames + } + return nil +} + +// DeleteClusterRequest is used to delete a cluster and all its resources. +type DeleteClusterRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - TeamId string `protobuf:"bytes,1,opt,name=team_id,json=teamId,proto3" json:"team_id,omitempty"` - ClusterId *string `protobuf:"bytes,2,opt,name=cluster_id,json=clusterId,proto3,oneof" json:"cluster_id,omitempty"` - Kind K8SObjectKind `protobuf:"varint,3,opt,name=kind,proto3,enum=api.v1.K8SObjectKind" json:"kind,omitempty"` + TeamId string `protobuf:"bytes,1,opt,name=team_id,json=teamId,proto3" json:"team_id,omitempty"` // Unique identifier for the team. + ClusterId string `protobuf:"bytes,2,opt,name=cluster_id,json=clusterId,proto3" json:"cluster_id,omitempty"` // Unique identifier for the cluster. } -func (x *GetResourceFilterOptionsRequest) Reset() { - *x = GetResourceFilterOptionsRequest{} +func (x *DeleteClusterRequest) Reset() { + *x = DeleteClusterRequest{} if protoimpl.UnsafeEnabled { - mi := &file_api_v1_k8s_proto_msgTypes[23] + mi := &file_api_v1_k8s_proto_msgTypes[22] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } -func (x *GetResourceFilterOptionsRequest) String() string { +func (x *DeleteClusterRequest) String() string { return protoimpl.X.MessageStringOf(x) } -func (*GetResourceFilterOptionsRequest) ProtoMessage() {} +func (*DeleteClusterRequest) ProtoMessage() {} -func (x *GetResourceFilterOptionsRequest) ProtoReflect() protoreflect.Message { - mi := &file_api_v1_k8s_proto_msgTypes[23] +func (x *DeleteClusterRequest) ProtoReflect() protoreflect.Message { + mi := &file_api_v1_k8s_proto_msgTypes[22] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2233,58 +2531,49 @@ func (x *GetResourceFilterOptionsRequest) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use GetResourceFilterOptionsRequest.ProtoReflect.Descriptor instead. -func (*GetResourceFilterOptionsRequest) Descriptor() ([]byte, []int) { - return file_api_v1_k8s_proto_rawDescGZIP(), []int{23} +// Deprecated: Use DeleteClusterRequest.ProtoReflect.Descriptor instead. +func (*DeleteClusterRequest) Descriptor() ([]byte, []int) { + return file_api_v1_k8s_proto_rawDescGZIP(), []int{22} } -func (x *GetResourceFilterOptionsRequest) GetTeamId() string { +func (x *DeleteClusterRequest) GetTeamId() string { if x != nil { return x.TeamId } return "" } -func (x *GetResourceFilterOptionsRequest) GetClusterId() string { - if x != nil && x.ClusterId != nil { - return *x.ClusterId - } - return "" -} - -func (x *GetResourceFilterOptionsRequest) GetKind() K8SObjectKind { +func (x *DeleteClusterRequest) GetClusterId() string { if x != nil { - return x.Kind + return x.ClusterId } - return K8SObjectKind_K8S_OBJECT_KIND_UNSPECIFIED + return "" } -// GetResourceFilterOptionsResponse returns the available filter options per field. -type GetResourceFilterOptionsResponse struct { +// DeleteClusterResponse is the response for a delete cluster request. +type DeleteClusterResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - - Options []*ResourceFilterFieldOptions `protobuf:"bytes,1,rep,name=options,proto3" json:"options,omitempty"` } -func (x *GetResourceFilterOptionsResponse) Reset() { - *x = GetResourceFilterOptionsResponse{} +func (x *DeleteClusterResponse) Reset() { + *x = DeleteClusterResponse{} if protoimpl.UnsafeEnabled { - mi := &file_api_v1_k8s_proto_msgTypes[24] + mi := &file_api_v1_k8s_proto_msgTypes[23] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } -func (x *GetResourceFilterOptionsResponse) String() string { +func (x *DeleteClusterResponse) String() string { return protoimpl.X.MessageStringOf(x) } -func (*GetResourceFilterOptionsResponse) ProtoMessage() {} +func (*DeleteClusterResponse) ProtoMessage() {} -func (x *GetResourceFilterOptionsResponse) ProtoReflect() protoreflect.Message { - mi := &file_api_v1_k8s_proto_msgTypes[24] +func (x *DeleteClusterResponse) ProtoReflect() protoreflect.Message { + mi := &file_api_v1_k8s_proto_msgTypes[23] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2295,45 +2584,43 @@ func (x *GetResourceFilterOptionsResponse) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use GetResourceFilterOptionsResponse.ProtoReflect.Descriptor instead. -func (*GetResourceFilterOptionsResponse) Descriptor() ([]byte, []int) { - return file_api_v1_k8s_proto_rawDescGZIP(), []int{24} -} - -func (x *GetResourceFilterOptionsResponse) GetOptions() []*ResourceFilterFieldOptions { - if x != nil { - return x.Options - } - return nil +// Deprecated: Use DeleteClusterResponse.ProtoReflect.Descriptor instead. +func (*DeleteClusterResponse) Descriptor() ([]byte, []int) { + return file_api_v1_k8s_proto_rawDescGZIP(), []int{23} } -// CreateClusterRequest is used to create a new cluster. -type CreateClusterRequest struct { +// UpdateClusterRequest is used to update the configuration of an existing cluster. +// only certain fields can be updated, such as name +type UpdateClusterRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - TeamId string `protobuf:"bytes,1,opt,name=team_id,json=teamId,proto3" json:"team_id,omitempty"` // Unique identifier for the team. - ClusterName string `protobuf:"bytes,2,opt,name=cluster_name,json=clusterName,proto3" json:"cluster_name,omitempty"` // Name of the new cluster. + TeamId string `protobuf:"bytes,1,opt,name=team_id,json=teamId,proto3" json:"team_id,omitempty"` // Unique identifier for the team. + ClusterId string `protobuf:"bytes,2,opt,name=cluster_id,json=clusterId,proto3" json:"cluster_id,omitempty"` // Unique identifier for the cluster. + ClusterName string `protobuf:"bytes,3,opt,name=cluster_name,json=clusterName,proto3" json:"cluster_name,omitempty"` // Updated cluster details. + CpuCostPerHour float64 `protobuf:"fixed64,4,opt,name=cpu_cost_per_hour,json=cpuCostPerHour,proto3" json:"cpu_cost_per_hour,omitempty"` // Estimated CPU cost per hour for one vcpu. + MemoryCostPerHour float64 `protobuf:"fixed64,5,opt,name=memory_cost_per_hour,json=memoryCostPerHour,proto3" json:"memory_cost_per_hour,omitempty"` // Estimated memory cost per hour for one GiB of memory. + GpuCostPerHour float64 `protobuf:"fixed64,6,opt,name=gpu_cost_per_hour,json=gpuCostPerHour,proto3" json:"gpu_cost_per_hour,omitempty"` // Estimate GPU cost per hour for one GPU. } -func (x *CreateClusterRequest) Reset() { - *x = CreateClusterRequest{} +func (x *UpdateClusterRequest) Reset() { + *x = UpdateClusterRequest{} if protoimpl.UnsafeEnabled { - mi := &file_api_v1_k8s_proto_msgTypes[25] + mi := &file_api_v1_k8s_proto_msgTypes[24] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } -func (x *CreateClusterRequest) String() string { +func (x *UpdateClusterRequest) String() string { return protoimpl.X.MessageStringOf(x) } -func (*CreateClusterRequest) ProtoMessage() {} +func (*UpdateClusterRequest) ProtoMessage() {} -func (x *CreateClusterRequest) ProtoReflect() protoreflect.Message { - mi := &file_api_v1_k8s_proto_msgTypes[25] +func (x *UpdateClusterRequest) ProtoReflect() protoreflect.Message { + mi := &file_api_v1_k8s_proto_msgTypes[24] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2344,37 +2631,113 @@ func (x *CreateClusterRequest) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use CreateClusterRequest.ProtoReflect.Descriptor instead. -func (*CreateClusterRequest) Descriptor() ([]byte, []int) { - return file_api_v1_k8s_proto_rawDescGZIP(), []int{25} +// Deprecated: Use UpdateClusterRequest.ProtoReflect.Descriptor instead. +func (*UpdateClusterRequest) Descriptor() ([]byte, []int) { + return file_api_v1_k8s_proto_rawDescGZIP(), []int{24} } -func (x *CreateClusterRequest) GetTeamId() string { +func (x *UpdateClusterRequest) GetTeamId() string { if x != nil { return x.TeamId } return "" } -func (x *CreateClusterRequest) GetClusterName() string { +func (x *UpdateClusterRequest) GetClusterId() string { + if x != nil { + return x.ClusterId + } + return "" +} + +func (x *UpdateClusterRequest) GetClusterName() string { if x != nil { return x.ClusterName } return "" } -// CreateClusterResponse is the response for a create cluster request. -type CreateClusterResponse struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields +func (x *UpdateClusterRequest) GetCpuCostPerHour() float64 { + if x != nil { + return x.CpuCostPerHour + } + return 0 +} - Cluster *Cluster `protobuf:"bytes,1,opt,name=cluster,proto3" json:"cluster,omitempty"` // Details of the created cluster. - Token string `protobuf:"bytes,2,opt,name=token,proto3" json:"token,omitempty"` // Token for the new cluster. +func (x *UpdateClusterRequest) GetMemoryCostPerHour() float64 { + if x != nil { + return x.MemoryCostPerHour + } + return 0 } -func (x *CreateClusterResponse) Reset() { - *x = CreateClusterResponse{} +func (x *UpdateClusterRequest) GetGpuCostPerHour() float64 { + if x != nil { + return x.GpuCostPerHour + } + return 0 +} + +// UpdateClusterResponse is the response for an update cluster request. +type UpdateClusterResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Cluster *Cluster `protobuf:"bytes,1,opt,name=cluster,proto3" json:"cluster,omitempty"` // Updated cluster details. +} + +func (x *UpdateClusterResponse) Reset() { + *x = UpdateClusterResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_api_v1_k8s_proto_msgTypes[25] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *UpdateClusterResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*UpdateClusterResponse) ProtoMessage() {} + +func (x *UpdateClusterResponse) ProtoReflect() protoreflect.Message { + mi := &file_api_v1_k8s_proto_msgTypes[25] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use UpdateClusterResponse.ProtoReflect.Descriptor instead. +func (*UpdateClusterResponse) Descriptor() ([]byte, []int) { + return file_api_v1_k8s_proto_rawDescGZIP(), []int{25} +} + +func (x *UpdateClusterResponse) GetCluster() *Cluster { + if x != nil { + return x.Cluster + } + return nil +} + +// GetResourcesResponse is the response for getting k8s resources. +type GetResourcesResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + WorkloadItems []*WorkloadItem `protobuf:"bytes,1,rep,name=workload_items,json=workloadItems,proto3" json:"workload_items,omitempty"` // Return list of resources requested + Pagination *Pagination `protobuf:"bytes,2,opt,name=pagination,proto3" json:"pagination,omitempty"` // Pagination information +} + +func (x *GetResourcesResponse) Reset() { + *x = GetResourcesResponse{} if protoimpl.UnsafeEnabled { mi := &file_api_v1_k8s_proto_msgTypes[26] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -2382,13 +2745,13 @@ func (x *CreateClusterResponse) Reset() { } } -func (x *CreateClusterResponse) String() string { +func (x *GetResourcesResponse) String() string { return protoimpl.X.MessageStringOf(x) } -func (*CreateClusterResponse) ProtoMessage() {} +func (*GetResourcesResponse) ProtoMessage() {} -func (x *CreateClusterResponse) ProtoReflect() protoreflect.Message { +func (x *GetResourcesResponse) ProtoReflect() protoreflect.Message { mi := &file_api_v1_k8s_proto_msgTypes[26] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -2400,37 +2763,38 @@ func (x *CreateClusterResponse) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use CreateClusterResponse.ProtoReflect.Descriptor instead. -func (*CreateClusterResponse) Descriptor() ([]byte, []int) { +// Deprecated: Use GetResourcesResponse.ProtoReflect.Descriptor instead. +func (*GetResourcesResponse) Descriptor() ([]byte, []int) { return file_api_v1_k8s_proto_rawDescGZIP(), []int{26} } -func (x *CreateClusterResponse) GetCluster() *Cluster { +func (x *GetResourcesResponse) GetWorkloadItems() []*WorkloadItem { if x != nil { - return x.Cluster + return x.WorkloadItems } return nil } -func (x *CreateClusterResponse) GetToken() string { +func (x *GetResourcesResponse) GetPagination() *Pagination { if x != nil { - return x.Token + return x.Pagination } - return "" + return nil } -// ResetClusterTokenRequest is used to reset the token for a cluster. -type ResetClusterTokenRequest struct { +// GetResourceFilterOptionsRequest asks for the distinct filterable field values for a resource kind. +type GetResourceFilterOptionsRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - TeamId string `protobuf:"bytes,1,opt,name=team_id,json=teamId,proto3" json:"team_id,omitempty"` // Unique identifier for the team. - ClusterId string `protobuf:"bytes,2,opt,name=cluster_id,json=clusterId,proto3" json:"cluster_id,omitempty"` // Unique identifier for the cluster. + TeamId string `protobuf:"bytes,1,opt,name=team_id,json=teamId,proto3" json:"team_id,omitempty"` + ClusterId *string `protobuf:"bytes,2,opt,name=cluster_id,json=clusterId,proto3,oneof" json:"cluster_id,omitempty"` + Kind K8SObjectKind `protobuf:"varint,3,opt,name=kind,proto3,enum=api.v1.K8SObjectKind" json:"kind,omitempty"` } -func (x *ResetClusterTokenRequest) Reset() { - *x = ResetClusterTokenRequest{} +func (x *GetResourceFilterOptionsRequest) Reset() { + *x = GetResourceFilterOptionsRequest{} if protoimpl.UnsafeEnabled { mi := &file_api_v1_k8s_proto_msgTypes[27] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -2438,13 +2802,13 @@ func (x *ResetClusterTokenRequest) Reset() { } } -func (x *ResetClusterTokenRequest) String() string { +func (x *GetResourceFilterOptionsRequest) String() string { return protoimpl.X.MessageStringOf(x) } -func (*ResetClusterTokenRequest) ProtoMessage() {} +func (*GetResourceFilterOptionsRequest) ProtoMessage() {} -func (x *ResetClusterTokenRequest) ProtoReflect() protoreflect.Message { +func (x *GetResourceFilterOptionsRequest) ProtoReflect() protoreflect.Message { mi := &file_api_v1_k8s_proto_msgTypes[27] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -2456,36 +2820,43 @@ func (x *ResetClusterTokenRequest) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use ResetClusterTokenRequest.ProtoReflect.Descriptor instead. -func (*ResetClusterTokenRequest) Descriptor() ([]byte, []int) { +// Deprecated: Use GetResourceFilterOptionsRequest.ProtoReflect.Descriptor instead. +func (*GetResourceFilterOptionsRequest) Descriptor() ([]byte, []int) { return file_api_v1_k8s_proto_rawDescGZIP(), []int{27} } -func (x *ResetClusterTokenRequest) GetTeamId() string { +func (x *GetResourceFilterOptionsRequest) GetTeamId() string { if x != nil { return x.TeamId } return "" } -func (x *ResetClusterTokenRequest) GetClusterId() string { - if x != nil { - return x.ClusterId +func (x *GetResourceFilterOptionsRequest) GetClusterId() string { + if x != nil && x.ClusterId != nil { + return *x.ClusterId } return "" } -// ResetClusterTokenResponse is the response for a reset cluster token request. -type ResetClusterTokenResponse struct { +func (x *GetResourceFilterOptionsRequest) GetKind() K8SObjectKind { + if x != nil { + return x.Kind + } + return K8SObjectKind_K8S_OBJECT_KIND_UNSPECIFIED +} + +// GetResourceFilterOptionsResponse returns the available filter options per field. +type GetResourceFilterOptionsResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Token string `protobuf:"bytes,1,opt,name=token,proto3" json:"token,omitempty"` // Token for the new cluster. + Options []*ResourceFilterFieldOptions `protobuf:"bytes,1,rep,name=options,proto3" json:"options,omitempty"` } -func (x *ResetClusterTokenResponse) Reset() { - *x = ResetClusterTokenResponse{} +func (x *GetResourceFilterOptionsResponse) Reset() { + *x = GetResourceFilterOptionsResponse{} if protoimpl.UnsafeEnabled { mi := &file_api_v1_k8s_proto_msgTypes[28] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -2493,13 +2864,13 @@ func (x *ResetClusterTokenResponse) Reset() { } } -func (x *ResetClusterTokenResponse) String() string { +func (x *GetResourceFilterOptionsResponse) String() string { return protoimpl.X.MessageStringOf(x) } -func (*ResetClusterTokenResponse) ProtoMessage() {} +func (*GetResourceFilterOptionsResponse) ProtoMessage() {} -func (x *ResetClusterTokenResponse) ProtoReflect() protoreflect.Message { +func (x *GetResourceFilterOptionsResponse) ProtoReflect() protoreflect.Message { mi := &file_api_v1_k8s_proto_msgTypes[28] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -2511,34 +2882,30 @@ func (x *ResetClusterTokenResponse) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use ResetClusterTokenResponse.ProtoReflect.Descriptor instead. -func (*ResetClusterTokenResponse) Descriptor() ([]byte, []int) { +// Deprecated: Use GetResourceFilterOptionsResponse.ProtoReflect.Descriptor instead. +func (*GetResourceFilterOptionsResponse) Descriptor() ([]byte, []int) { return file_api_v1_k8s_proto_rawDescGZIP(), []int{28} } -func (x *ResetClusterTokenResponse) GetToken() string { +func (x *GetResourceFilterOptionsResponse) GetOptions() []*ResourceFilterFieldOptions { if x != nil { - return x.Token + return x.Options } - return "" + return nil } -// GetNodeGroupsRequest is used to fetch node groups for a specific cluster. -type GetNodeGroupsRequest struct { +// CreateClusterRequest is used to create a new cluster. +type CreateClusterRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - TeamId string `protobuf:"bytes,1,opt,name=team_id,json=teamId,proto3" json:"team_id,omitempty"` // Unique identifier for the team. - ClusterId string `protobuf:"bytes,2,opt,name=cluster_id,json=clusterId,proto3" json:"cluster_id,omitempty"` // Unique identifier for the cluster. - StartTime *timestamppb.Timestamp `protobuf:"bytes,11,opt,name=start_time,json=startTime,proto3,oneof" json:"start_time,omitempty"` - EndTime *timestamppb.Timestamp `protobuf:"bytes,12,opt,name=end_time,json=endTime,proto3,oneof" json:"end_time,omitempty"` - BucketSize *int32 `protobuf:"varint,13,opt,name=bucket_size,json=bucketSize,proto3,oneof" json:"bucket_size,omitempty"` - ShowDeleted bool `protobuf:"varint,21,opt,name=show_deleted,json=showDeleted,proto3" json:"show_deleted,omitempty"` + TeamId string `protobuf:"bytes,1,opt,name=team_id,json=teamId,proto3" json:"team_id,omitempty"` // Unique identifier for the team. + ClusterName string `protobuf:"bytes,2,opt,name=cluster_name,json=clusterName,proto3" json:"cluster_name,omitempty"` // Name of the new cluster. } -func (x *GetNodeGroupsRequest) Reset() { - *x = GetNodeGroupsRequest{} +func (x *CreateClusterRequest) Reset() { + *x = CreateClusterRequest{} if protoimpl.UnsafeEnabled { mi := &file_api_v1_k8s_proto_msgTypes[29] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -2546,13 +2913,13 @@ func (x *GetNodeGroupsRequest) Reset() { } } -func (x *GetNodeGroupsRequest) String() string { +func (x *CreateClusterRequest) String() string { return protoimpl.X.MessageStringOf(x) } -func (*GetNodeGroupsRequest) ProtoMessage() {} +func (*CreateClusterRequest) ProtoMessage() {} -func (x *GetNodeGroupsRequest) ProtoReflect() protoreflect.Message { +func (x *CreateClusterRequest) ProtoReflect() protoreflect.Message { mi := &file_api_v1_k8s_proto_msgTypes[29] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -2564,64 +2931,37 @@ func (x *GetNodeGroupsRequest) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use GetNodeGroupsRequest.ProtoReflect.Descriptor instead. -func (*GetNodeGroupsRequest) Descriptor() ([]byte, []int) { +// Deprecated: Use CreateClusterRequest.ProtoReflect.Descriptor instead. +func (*CreateClusterRequest) Descriptor() ([]byte, []int) { return file_api_v1_k8s_proto_rawDescGZIP(), []int{29} } -func (x *GetNodeGroupsRequest) GetTeamId() string { +func (x *CreateClusterRequest) GetTeamId() string { if x != nil { return x.TeamId } return "" } -func (x *GetNodeGroupsRequest) GetClusterId() string { +func (x *CreateClusterRequest) GetClusterName() string { if x != nil { - return x.ClusterId + return x.ClusterName } return "" } -func (x *GetNodeGroupsRequest) GetStartTime() *timestamppb.Timestamp { - if x != nil { - return x.StartTime - } - return nil -} - -func (x *GetNodeGroupsRequest) GetEndTime() *timestamppb.Timestamp { - if x != nil { - return x.EndTime - } - return nil -} - -func (x *GetNodeGroupsRequest) GetBucketSize() int32 { - if x != nil && x.BucketSize != nil { - return *x.BucketSize - } - return 0 -} - -func (x *GetNodeGroupsRequest) GetShowDeleted() bool { - if x != nil { - return x.ShowDeleted - } - return false -} - -// GetAllNodeGroupsRequest is used to fetch all node groups for a specific team. -type GetAllNodeGroupsRequest struct { +// CreateClusterResponse is the response for a create cluster request. +type CreateClusterResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - TeamId string `protobuf:"bytes,1,opt,name=team_id,json=teamId,proto3" json:"team_id,omitempty"` // Unique identifier for the team. + Cluster *Cluster `protobuf:"bytes,1,opt,name=cluster,proto3" json:"cluster,omitempty"` // Details of the created cluster. + Token string `protobuf:"bytes,2,opt,name=token,proto3" json:"token,omitempty"` // Token for the new cluster. } -func (x *GetAllNodeGroupsRequest) Reset() { - *x = GetAllNodeGroupsRequest{} +func (x *CreateClusterResponse) Reset() { + *x = CreateClusterResponse{} if protoimpl.UnsafeEnabled { mi := &file_api_v1_k8s_proto_msgTypes[30] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -2629,13 +2969,13 @@ func (x *GetAllNodeGroupsRequest) Reset() { } } -func (x *GetAllNodeGroupsRequest) String() string { +func (x *CreateClusterResponse) String() string { return protoimpl.X.MessageStringOf(x) } -func (*GetAllNodeGroupsRequest) ProtoMessage() {} +func (*CreateClusterResponse) ProtoMessage() {} -func (x *GetAllNodeGroupsRequest) ProtoReflect() protoreflect.Message { +func (x *CreateClusterResponse) ProtoReflect() protoreflect.Message { mi := &file_api_v1_k8s_proto_msgTypes[30] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -2647,29 +2987,37 @@ func (x *GetAllNodeGroupsRequest) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use GetAllNodeGroupsRequest.ProtoReflect.Descriptor instead. -func (*GetAllNodeGroupsRequest) Descriptor() ([]byte, []int) { +// Deprecated: Use CreateClusterResponse.ProtoReflect.Descriptor instead. +func (*CreateClusterResponse) Descriptor() ([]byte, []int) { return file_api_v1_k8s_proto_rawDescGZIP(), []int{30} } -func (x *GetAllNodeGroupsRequest) GetTeamId() string { +func (x *CreateClusterResponse) GetCluster() *Cluster { if x != nil { - return x.TeamId + return x.Cluster } - return "" + return nil } -// GetNodeGroupsResponse contains the list of node groups for a specific cluster. -type GetNodeGroupsResponse struct { - state protoimpl.MessageState +func (x *CreateClusterResponse) GetToken() string { + if x != nil { + return x.Token + } + return "" +} + +// ResetClusterTokenRequest is used to reset the token for a cluster. +type ResetClusterTokenRequest struct { + state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - NodeGroups []*NodeGroup `protobuf:"bytes,1,rep,name=node_groups,json=nodeGroups,proto3" json:"node_groups,omitempty"` // List of node groups. + TeamId string `protobuf:"bytes,1,opt,name=team_id,json=teamId,proto3" json:"team_id,omitempty"` // Unique identifier for the team. + ClusterId string `protobuf:"bytes,2,opt,name=cluster_id,json=clusterId,proto3" json:"cluster_id,omitempty"` // Unique identifier for the cluster. } -func (x *GetNodeGroupsResponse) Reset() { - *x = GetNodeGroupsResponse{} +func (x *ResetClusterTokenRequest) Reset() { + *x = ResetClusterTokenRequest{} if protoimpl.UnsafeEnabled { mi := &file_api_v1_k8s_proto_msgTypes[31] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -2677,13 +3025,13 @@ func (x *GetNodeGroupsResponse) Reset() { } } -func (x *GetNodeGroupsResponse) String() string { +func (x *ResetClusterTokenRequest) String() string { return protoimpl.X.MessageStringOf(x) } -func (*GetNodeGroupsResponse) ProtoMessage() {} +func (*ResetClusterTokenRequest) ProtoMessage() {} -func (x *GetNodeGroupsResponse) ProtoReflect() protoreflect.Message { +func (x *ResetClusterTokenRequest) ProtoReflect() protoreflect.Message { mi := &file_api_v1_k8s_proto_msgTypes[31] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -2695,29 +3043,36 @@ func (x *GetNodeGroupsResponse) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use GetNodeGroupsResponse.ProtoReflect.Descriptor instead. -func (*GetNodeGroupsResponse) Descriptor() ([]byte, []int) { +// Deprecated: Use ResetClusterTokenRequest.ProtoReflect.Descriptor instead. +func (*ResetClusterTokenRequest) Descriptor() ([]byte, []int) { return file_api_v1_k8s_proto_rawDescGZIP(), []int{31} } -func (x *GetNodeGroupsResponse) GetNodeGroups() []*NodeGroup { +func (x *ResetClusterTokenRequest) GetTeamId() string { if x != nil { - return x.NodeGroups + return x.TeamId } - return nil + return "" } -// GetAllNodeGroupsResponse contains the list of all node groups for a specific team. -type GetAllNodeGroupsResponse struct { +func (x *ResetClusterTokenRequest) GetClusterId() string { + if x != nil { + return x.ClusterId + } + return "" +} + +// ResetClusterTokenResponse is the response for a reset cluster token request. +type ResetClusterTokenResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - NodeGroupMap map[string]*NodeGroup `protobuf:"bytes,1,rep,name=node_group_map,json=nodeGroupMap,proto3" json:"node_group_map,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` // Map of clusterId+nodeGroupName to NodeGroup. + Token string `protobuf:"bytes,1,opt,name=token,proto3" json:"token,omitempty"` // Token for the new cluster. } -func (x *GetAllNodeGroupsResponse) Reset() { - *x = GetAllNodeGroupsResponse{} +func (x *ResetClusterTokenResponse) Reset() { + *x = ResetClusterTokenResponse{} if protoimpl.UnsafeEnabled { mi := &file_api_v1_k8s_proto_msgTypes[32] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -2725,13 +3080,13 @@ func (x *GetAllNodeGroupsResponse) Reset() { } } -func (x *GetAllNodeGroupsResponse) String() string { +func (x *ResetClusterTokenResponse) String() string { return protoimpl.X.MessageStringOf(x) } -func (*GetAllNodeGroupsResponse) ProtoMessage() {} +func (*ResetClusterTokenResponse) ProtoMessage() {} -func (x *GetAllNodeGroupsResponse) ProtoReflect() protoreflect.Message { +func (x *ResetClusterTokenResponse) ProtoReflect() protoreflect.Message { mi := &file_api_v1_k8s_proto_msgTypes[32] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -2743,28 +3098,34 @@ func (x *GetAllNodeGroupsResponse) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use GetAllNodeGroupsResponse.ProtoReflect.Descriptor instead. -func (*GetAllNodeGroupsResponse) Descriptor() ([]byte, []int) { +// Deprecated: Use ResetClusterTokenResponse.ProtoReflect.Descriptor instead. +func (*ResetClusterTokenResponse) Descriptor() ([]byte, []int) { return file_api_v1_k8s_proto_rawDescGZIP(), []int{32} } -func (x *GetAllNodeGroupsResponse) GetNodeGroupMap() map[string]*NodeGroup { +func (x *ResetClusterTokenResponse) GetToken() string { if x != nil { - return x.NodeGroupMap + return x.Token } - return nil + return "" } -type NodeGroupSet struct { +// GetNodeGroupsRequest is used to fetch node groups for a specific cluster. +type GetNodeGroupsRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - NodeGroups []*NodeGroup `protobuf:"bytes,1,rep,name=node_groups,json=nodeGroups,proto3" json:"node_groups,omitempty"` // List of node groups. + TeamId string `protobuf:"bytes,1,opt,name=team_id,json=teamId,proto3" json:"team_id,omitempty"` // Unique identifier for the team. + ClusterId string `protobuf:"bytes,2,opt,name=cluster_id,json=clusterId,proto3" json:"cluster_id,omitempty"` // Unique identifier for the cluster. + StartTime *timestamppb.Timestamp `protobuf:"bytes,11,opt,name=start_time,json=startTime,proto3,oneof" json:"start_time,omitempty"` + EndTime *timestamppb.Timestamp `protobuf:"bytes,12,opt,name=end_time,json=endTime,proto3,oneof" json:"end_time,omitempty"` + BucketSize *int32 `protobuf:"varint,13,opt,name=bucket_size,json=bucketSize,proto3,oneof" json:"bucket_size,omitempty"` + ShowDeleted bool `protobuf:"varint,21,opt,name=show_deleted,json=showDeleted,proto3" json:"show_deleted,omitempty"` } -func (x *NodeGroupSet) Reset() { - *x = NodeGroupSet{} +func (x *GetNodeGroupsRequest) Reset() { + *x = GetNodeGroupsRequest{} if protoimpl.UnsafeEnabled { mi := &file_api_v1_k8s_proto_msgTypes[33] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -2772,13 +3133,13 @@ func (x *NodeGroupSet) Reset() { } } -func (x *NodeGroupSet) String() string { +func (x *GetNodeGroupsRequest) String() string { return protoimpl.X.MessageStringOf(x) } -func (*NodeGroupSet) ProtoMessage() {} +func (*GetNodeGroupsRequest) ProtoMessage() {} -func (x *NodeGroupSet) ProtoReflect() protoreflect.Message { +func (x *GetNodeGroupsRequest) ProtoReflect() protoreflect.Message { mi := &file_api_v1_k8s_proto_msgTypes[33] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -2790,33 +3151,64 @@ func (x *NodeGroupSet) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use NodeGroupSet.ProtoReflect.Descriptor instead. -func (*NodeGroupSet) Descriptor() ([]byte, []int) { +// Deprecated: Use GetNodeGroupsRequest.ProtoReflect.Descriptor instead. +func (*GetNodeGroupsRequest) Descriptor() ([]byte, []int) { return file_api_v1_k8s_proto_rawDescGZIP(), []int{33} } -func (x *NodeGroupSet) GetNodeGroups() []*NodeGroup { +func (x *GetNodeGroupsRequest) GetTeamId() string { if x != nil { - return x.NodeGroups + return x.TeamId + } + return "" +} + +func (x *GetNodeGroupsRequest) GetClusterId() string { + if x != nil { + return x.ClusterId + } + return "" +} + +func (x *GetNodeGroupsRequest) GetStartTime() *timestamppb.Timestamp { + if x != nil { + return x.StartTime } return nil } -// GetNodeGroupsUtilizationRequest is used to fetch timeseries utilization data for node groups for a specific cluster. -type GetNodeGroupsUtilizationRequest struct { +func (x *GetNodeGroupsRequest) GetEndTime() *timestamppb.Timestamp { + if x != nil { + return x.EndTime + } + return nil +} + +func (x *GetNodeGroupsRequest) GetBucketSize() int32 { + if x != nil && x.BucketSize != nil { + return *x.BucketSize + } + return 0 +} + +func (x *GetNodeGroupsRequest) GetShowDeleted() bool { + if x != nil { + return x.ShowDeleted + } + return false +} + +// GetAllNodeGroupsRequest is used to fetch all node groups for a specific team. +type GetAllNodeGroupsRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - TeamId string `protobuf:"bytes,1,opt,name=team_id,json=teamId,proto3" json:"team_id,omitempty"` // Unique identifier for the team. - ClusterId string `protobuf:"bytes,2,opt,name=cluster_id,json=clusterId,proto3" json:"cluster_id,omitempty"` // Unique identifier for the cluster. - StartTime *timestamppb.Timestamp `protobuf:"bytes,11,opt,name=start_time,json=startTime,proto3,oneof" json:"start_time,omitempty"` - EndTime *timestamppb.Timestamp `protobuf:"bytes,12,opt,name=end_time,json=endTime,proto3,oneof" json:"end_time,omitempty"` - BucketSize *int32 `protobuf:"varint,13,opt,name=bucket_size,json=bucketSize,proto3,oneof" json:"bucket_size,omitempty"` + TeamId string `protobuf:"bytes,1,opt,name=team_id,json=teamId,proto3" json:"team_id,omitempty"` // Unique identifier for the team. } -func (x *GetNodeGroupsUtilizationRequest) Reset() { - *x = GetNodeGroupsUtilizationRequest{} +func (x *GetAllNodeGroupsRequest) Reset() { + *x = GetAllNodeGroupsRequest{} if protoimpl.UnsafeEnabled { mi := &file_api_v1_k8s_proto_msgTypes[34] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -2824,13 +3216,13 @@ func (x *GetNodeGroupsUtilizationRequest) Reset() { } } -func (x *GetNodeGroupsUtilizationRequest) String() string { +func (x *GetAllNodeGroupsRequest) String() string { return protoimpl.X.MessageStringOf(x) } -func (*GetNodeGroupsUtilizationRequest) ProtoMessage() {} +func (*GetAllNodeGroupsRequest) ProtoMessage() {} -func (x *GetNodeGroupsUtilizationRequest) ProtoReflect() protoreflect.Message { +func (x *GetAllNodeGroupsRequest) ProtoReflect() protoreflect.Message { mi := &file_api_v1_k8s_proto_msgTypes[34] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -2842,60 +3234,29 @@ func (x *GetNodeGroupsUtilizationRequest) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use GetNodeGroupsUtilizationRequest.ProtoReflect.Descriptor instead. -func (*GetNodeGroupsUtilizationRequest) Descriptor() ([]byte, []int) { +// Deprecated: Use GetAllNodeGroupsRequest.ProtoReflect.Descriptor instead. +func (*GetAllNodeGroupsRequest) Descriptor() ([]byte, []int) { return file_api_v1_k8s_proto_rawDescGZIP(), []int{34} } -func (x *GetNodeGroupsUtilizationRequest) GetTeamId() string { +func (x *GetAllNodeGroupsRequest) GetTeamId() string { if x != nil { return x.TeamId } return "" } -func (x *GetNodeGroupsUtilizationRequest) GetClusterId() string { - if x != nil { - return x.ClusterId - } - return "" -} - -func (x *GetNodeGroupsUtilizationRequest) GetStartTime() *timestamppb.Timestamp { - if x != nil { - return x.StartTime - } - return nil -} - -func (x *GetNodeGroupsUtilizationRequest) GetEndTime() *timestamppb.Timestamp { - if x != nil { - return x.EndTime - } - return nil -} - -func (x *GetNodeGroupsUtilizationRequest) GetBucketSize() int32 { - if x != nil && x.BucketSize != nil { - return *x.BucketSize - } - return 0 -} - // GetNodeGroupsResponse contains the list of node groups for a specific cluster. -type GetNodeGroupsUtilizationResponse struct { +type GetNodeGroupsResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - NodeGroupToUtilMetrics map[string]*GetNodeGroupsUtilizationResponse_ListNodeGroupMetrics `protobuf:"bytes,1,rep,name=node_group_to_util_metrics,json=nodeGroupToUtilMetrics,proto3" json:"node_group_to_util_metrics,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` - InstanceTypeToUtilMetrics map[string]*GetNodeGroupsUtilizationResponse_ListNodeGroupMetrics `protobuf:"bytes,6,rep,name=instance_type_to_util_metrics,json=instanceTypeToUtilMetrics,proto3" json:"instance_type_to_util_metrics,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` - ReservationTypeToUtilMetrics map[string]*GetNodeGroupsUtilizationResponse_ListNodeGroupMetrics `protobuf:"bytes,11,rep,name=reservation_type_to_util_metrics,json=reservationTypeToUtilMetrics,proto3" json:"reservation_type_to_util_metrics,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` - LogicalAzToUtilMetrics map[string]*GetNodeGroupsUtilizationResponse_ListNodeGroupMetrics `protobuf:"bytes,16,rep,name=logical_az_to_util_metrics,json=logicalAzToUtilMetrics,proto3" json:"logical_az_to_util_metrics,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + NodeGroups []*NodeGroup `protobuf:"bytes,1,rep,name=node_groups,json=nodeGroups,proto3" json:"node_groups,omitempty"` // List of node groups. } -func (x *GetNodeGroupsUtilizationResponse) Reset() { - *x = GetNodeGroupsUtilizationResponse{} +func (x *GetNodeGroupsResponse) Reset() { + *x = GetNodeGroupsResponse{} if protoimpl.UnsafeEnabled { mi := &file_api_v1_k8s_proto_msgTypes[35] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -2903,13 +3264,13 @@ func (x *GetNodeGroupsUtilizationResponse) Reset() { } } -func (x *GetNodeGroupsUtilizationResponse) String() string { +func (x *GetNodeGroupsResponse) String() string { return protoimpl.X.MessageStringOf(x) } -func (*GetNodeGroupsUtilizationResponse) ProtoMessage() {} +func (*GetNodeGroupsResponse) ProtoMessage() {} -func (x *GetNodeGroupsUtilizationResponse) ProtoReflect() protoreflect.Message { +func (x *GetNodeGroupsResponse) ProtoReflect() protoreflect.Message { mi := &file_api_v1_k8s_proto_msgTypes[35] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -2921,66 +3282,29 @@ func (x *GetNodeGroupsUtilizationResponse) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use GetNodeGroupsUtilizationResponse.ProtoReflect.Descriptor instead. -func (*GetNodeGroupsUtilizationResponse) Descriptor() ([]byte, []int) { +// Deprecated: Use GetNodeGroupsResponse.ProtoReflect.Descriptor instead. +func (*GetNodeGroupsResponse) Descriptor() ([]byte, []int) { return file_api_v1_k8s_proto_rawDescGZIP(), []int{35} } -func (x *GetNodeGroupsUtilizationResponse) GetNodeGroupToUtilMetrics() map[string]*GetNodeGroupsUtilizationResponse_ListNodeGroupMetrics { - if x != nil { - return x.NodeGroupToUtilMetrics - } - return nil -} - -func (x *GetNodeGroupsUtilizationResponse) GetInstanceTypeToUtilMetrics() map[string]*GetNodeGroupsUtilizationResponse_ListNodeGroupMetrics { - if x != nil { - return x.InstanceTypeToUtilMetrics - } - return nil -} - -func (x *GetNodeGroupsUtilizationResponse) GetReservationTypeToUtilMetrics() map[string]*GetNodeGroupsUtilizationResponse_ListNodeGroupMetrics { - if x != nil { - return x.ReservationTypeToUtilMetrics - } - return nil -} - -func (x *GetNodeGroupsUtilizationResponse) GetLogicalAzToUtilMetrics() map[string]*GetNodeGroupsUtilizationResponse_ListNodeGroupMetrics { +func (x *GetNodeGroupsResponse) GetNodeGroups() []*NodeGroup { if x != nil { - return x.LogicalAzToUtilMetrics + return x.NodeGroups } return nil } -type NodeGroupMetric struct { +// GetAllNodeGroupsResponse contains the list of all node groups for a specific team. +type GetAllNodeGroupsResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - NodeCount int32 `protobuf:"varint,1,opt,name=node_count,json=nodeCount,proto3" json:"node_count,omitempty"` - MaxConcurrentNodeCount int32 `protobuf:"varint,2,opt,name=max_concurrent_node_count,json=maxConcurrentNodeCount,proto3" json:"max_concurrent_node_count,omitempty"` - CapacityCpu int64 `protobuf:"varint,11,opt,name=capacity_cpu,json=capacityCpu,proto3" json:"capacity_cpu,omitempty"` // millicores - CapacityMemory int64 `protobuf:"varint,12,opt,name=capacity_memory,json=capacityMemory,proto3" json:"capacity_memory,omitempty"` // bytes - CapacityGpuVram int64 `protobuf:"varint,13,opt,name=capacity_gpu_vram,json=capacityGpuVram,proto3" json:"capacity_gpu_vram,omitempty"` // bytes - CapacityGpuDevices int64 `protobuf:"varint,14,opt,name=capacity_gpu_devices,json=capacityGpuDevices,proto3" json:"capacity_gpu_devices,omitempty"` // number - AggrCpuReq int64 `protobuf:"varint,21,opt,name=aggr_cpu_req,json=aggrCpuReq,proto3" json:"aggr_cpu_req,omitempty"` // millicores - AggrMemoryReq int64 `protobuf:"varint,22,opt,name=aggr_memory_req,json=aggrMemoryReq,proto3" json:"aggr_memory_req,omitempty"` // bytes - AggrGpuReq int64 `protobuf:"varint,23,opt,name=aggr_gpu_req,json=aggrGpuReq,proto3" json:"aggr_gpu_req,omitempty"` // number - UtilCpuAvgUsage float32 `protobuf:"fixed32,31,opt,name=util_cpu_avg_usage,json=utilCpuAvgUsage,proto3" json:"util_cpu_avg_usage,omitempty"` // millicores - UtilMemoryAvgUsage float32 `protobuf:"fixed32,32,opt,name=util_memory_avg_usage,json=utilMemoryAvgUsage,proto3" json:"util_memory_avg_usage,omitempty"` // bytes - UtilGpuVramAvgUsage float32 `protobuf:"fixed32,33,opt,name=util_gpu_vram_avg_usage,json=utilGpuVramAvgUsage,proto3" json:"util_gpu_vram_avg_usage,omitempty"` // bytes - UtilGpuDevice float32 `protobuf:"fixed32,34,opt,name=util_gpu_device,json=utilGpuDevice,proto3" json:"util_gpu_device,omitempty"` // pct - HourlyCost *money.Money `protobuf:"bytes,50,opt,name=hourly_cost,json=hourlyCost,proto3" json:"hourly_cost,omitempty"` // average hourly cost within the time bucket - HourlyCpuCost *money.Money `protobuf:"bytes,51,opt,name=hourly_cpu_cost,json=hourlyCpuCost,proto3" json:"hourly_cpu_cost,omitempty"` // cost for cpu - HourlyMemoryCost *money.Money `protobuf:"bytes,52,opt,name=hourly_memory_cost,json=hourlyMemoryCost,proto3" json:"hourly_memory_cost,omitempty"` // cost for memory - HourlyGpuCost *money.Money `protobuf:"bytes,53,opt,name=hourly_gpu_cost,json=hourlyGpuCost,proto3" json:"hourly_gpu_cost,omitempty"` // cost for gpu - BucketTime *timestamppb.Timestamp `protobuf:"bytes,41,opt,name=bucket_time,json=bucketTime,proto3" json:"bucket_time,omitempty"` + NodeGroupMap map[string]*NodeGroup `protobuf:"bytes,1,rep,name=node_group_map,json=nodeGroupMap,proto3" json:"node_group_map,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` // Map of clusterId+nodeGroupName to NodeGroup. } -func (x *NodeGroupMetric) Reset() { - *x = NodeGroupMetric{} +func (x *GetAllNodeGroupsResponse) Reset() { + *x = GetAllNodeGroupsResponse{} if protoimpl.UnsafeEnabled { mi := &file_api_v1_k8s_proto_msgTypes[36] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -2988,13 +3312,13 @@ func (x *NodeGroupMetric) Reset() { } } -func (x *NodeGroupMetric) String() string { +func (x *GetAllNodeGroupsResponse) String() string { return protoimpl.X.MessageStringOf(x) } -func (*NodeGroupMetric) ProtoMessage() {} +func (*GetAllNodeGroupsResponse) ProtoMessage() {} -func (x *NodeGroupMetric) ProtoReflect() protoreflect.Message { +func (x *GetAllNodeGroupsResponse) ProtoReflect() protoreflect.Message { mi := &file_api_v1_k8s_proto_msgTypes[36] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -3006,163 +3330,167 @@ func (x *NodeGroupMetric) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use NodeGroupMetric.ProtoReflect.Descriptor instead. -func (*NodeGroupMetric) Descriptor() ([]byte, []int) { +// Deprecated: Use GetAllNodeGroupsResponse.ProtoReflect.Descriptor instead. +func (*GetAllNodeGroupsResponse) Descriptor() ([]byte, []int) { return file_api_v1_k8s_proto_rawDescGZIP(), []int{36} } -func (x *NodeGroupMetric) GetNodeCount() int32 { +func (x *GetAllNodeGroupsResponse) GetNodeGroupMap() map[string]*NodeGroup { if x != nil { - return x.NodeCount + return x.NodeGroupMap } - return 0 + return nil } -func (x *NodeGroupMetric) GetMaxConcurrentNodeCount() int32 { - if x != nil { - return x.MaxConcurrentNodeCount - } - return 0 -} +type NodeGroupSet struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields -func (x *NodeGroupMetric) GetCapacityCpu() int64 { - if x != nil { - return x.CapacityCpu - } - return 0 + NodeGroups []*NodeGroup `protobuf:"bytes,1,rep,name=node_groups,json=nodeGroups,proto3" json:"node_groups,omitempty"` // List of node groups. } -func (x *NodeGroupMetric) GetCapacityMemory() int64 { - if x != nil { - return x.CapacityMemory +func (x *NodeGroupSet) Reset() { + *x = NodeGroupSet{} + if protoimpl.UnsafeEnabled { + mi := &file_api_v1_k8s_proto_msgTypes[37] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } - return 0 } -func (x *NodeGroupMetric) GetCapacityGpuVram() int64 { - if x != nil { - return x.CapacityGpuVram - } - return 0 +func (x *NodeGroupSet) String() string { + return protoimpl.X.MessageStringOf(x) } -func (x *NodeGroupMetric) GetCapacityGpuDevices() int64 { - if x != nil { - return x.CapacityGpuDevices - } - return 0 -} +func (*NodeGroupSet) ProtoMessage() {} -func (x *NodeGroupMetric) GetAggrCpuReq() int64 { - if x != nil { - return x.AggrCpuReq +func (x *NodeGroupSet) ProtoReflect() protoreflect.Message { + mi := &file_api_v1_k8s_proto_msgTypes[37] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms } - return 0 + return mi.MessageOf(x) } -func (x *NodeGroupMetric) GetAggrMemoryReq() int64 { - if x != nil { - return x.AggrMemoryReq - } - return 0 +// Deprecated: Use NodeGroupSet.ProtoReflect.Descriptor instead. +func (*NodeGroupSet) Descriptor() ([]byte, []int) { + return file_api_v1_k8s_proto_rawDescGZIP(), []int{37} } -func (x *NodeGroupMetric) GetAggrGpuReq() int64 { +func (x *NodeGroupSet) GetNodeGroups() []*NodeGroup { if x != nil { - return x.AggrGpuReq + return x.NodeGroups } - return 0 + return nil } -func (x *NodeGroupMetric) GetUtilCpuAvgUsage() float32 { - if x != nil { - return x.UtilCpuAvgUsage - } - return 0 +// GetClusterNodeFilterIndexRequest is used to fetch the filterable node +// attributes for a specific cluster. +type GetClusterNodeFilterIndexRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + TeamId string `protobuf:"bytes,1,opt,name=team_id,json=teamId,proto3" json:"team_id,omitempty"` // Unique identifier for the team. + ClusterId string `protobuf:"bytes,2,opt,name=cluster_id,json=clusterId,proto3" json:"cluster_id,omitempty"` // Unique identifier for the cluster. + StartTime *timestamppb.Timestamp `protobuf:"bytes,11,opt,name=start_time,json=startTime,proto3,oneof" json:"start_time,omitempty"` + EndTime *timestamppb.Timestamp `protobuf:"bytes,12,opt,name=end_time,json=endTime,proto3,oneof" json:"end_time,omitempty"` } -func (x *NodeGroupMetric) GetUtilMemoryAvgUsage() float32 { - if x != nil { - return x.UtilMemoryAvgUsage +func (x *GetClusterNodeFilterIndexRequest) Reset() { + *x = GetClusterNodeFilterIndexRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_api_v1_k8s_proto_msgTypes[38] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } - return 0 } -func (x *NodeGroupMetric) GetUtilGpuVramAvgUsage() float32 { - if x != nil { - return x.UtilGpuVramAvgUsage - } - return 0 +func (x *GetClusterNodeFilterIndexRequest) String() string { + return protoimpl.X.MessageStringOf(x) } -func (x *NodeGroupMetric) GetUtilGpuDevice() float32 { - if x != nil { - return x.UtilGpuDevice +func (*GetClusterNodeFilterIndexRequest) ProtoMessage() {} + +func (x *GetClusterNodeFilterIndexRequest) ProtoReflect() protoreflect.Message { + mi := &file_api_v1_k8s_proto_msgTypes[38] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms } - return 0 + return mi.MessageOf(x) } -func (x *NodeGroupMetric) GetHourlyCost() *money.Money { - if x != nil { - return x.HourlyCost - } - return nil +// Deprecated: Use GetClusterNodeFilterIndexRequest.ProtoReflect.Descriptor instead. +func (*GetClusterNodeFilterIndexRequest) Descriptor() ([]byte, []int) { + return file_api_v1_k8s_proto_rawDescGZIP(), []int{38} } -func (x *NodeGroupMetric) GetHourlyCpuCost() *money.Money { +func (x *GetClusterNodeFilterIndexRequest) GetTeamId() string { if x != nil { - return x.HourlyCpuCost + return x.TeamId } - return nil + return "" } -func (x *NodeGroupMetric) GetHourlyMemoryCost() *money.Money { +func (x *GetClusterNodeFilterIndexRequest) GetClusterId() string { if x != nil { - return x.HourlyMemoryCost + return x.ClusterId } - return nil + return "" } -func (x *NodeGroupMetric) GetHourlyGpuCost() *money.Money { +func (x *GetClusterNodeFilterIndexRequest) GetStartTime() *timestamppb.Timestamp { if x != nil { - return x.HourlyGpuCost + return x.StartTime } return nil } -func (x *NodeGroupMetric) GetBucketTime() *timestamppb.Timestamp { +func (x *GetClusterNodeFilterIndexRequest) GetEndTime() *timestamppb.Timestamp { if x != nil { - return x.BucketTime + return x.EndTime } return nil } -// GetNodeGroupResponse contains node group for a specific cluster. -type GetNodeGroupResponse struct { +// NodeLabelSet is one distinct node label map, referenced by index from +// NodeLabelRef. Nodes in a cluster share label maps almost perfectly (the map +// is a function of instance type, zone, node pool and image), so distinct maps +// are sent once instead of once per node. +type NodeLabelSet struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - NodeGroup *NodeGroup `protobuf:"bytes,1,opt,name=node_group,json=nodeGroup,proto3" json:"node_group,omitempty"` // Node Group + Labels map[string]string `protobuf:"bytes,1,rep,name=labels,proto3" json:"labels,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` // Kubernetes labels on the node. } -func (x *GetNodeGroupResponse) Reset() { - *x = GetNodeGroupResponse{} +func (x *NodeLabelSet) Reset() { + *x = NodeLabelSet{} if protoimpl.UnsafeEnabled { - mi := &file_api_v1_k8s_proto_msgTypes[37] + mi := &file_api_v1_k8s_proto_msgTypes[39] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } -func (x *GetNodeGroupResponse) String() string { +func (x *NodeLabelSet) String() string { return protoimpl.X.MessageStringOf(x) } -func (*GetNodeGroupResponse) ProtoMessage() {} +func (*NodeLabelSet) ProtoMessage() {} -func (x *GetNodeGroupResponse) ProtoReflect() protoreflect.Message { - mi := &file_api_v1_k8s_proto_msgTypes[37] +func (x *NodeLabelSet) ProtoReflect() protoreflect.Message { + mi := &file_api_v1_k8s_proto_msgTypes[39] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3173,44 +3501,45 @@ func (x *GetNodeGroupResponse) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use GetNodeGroupResponse.ProtoReflect.Descriptor instead. -func (*GetNodeGroupResponse) Descriptor() ([]byte, []int) { - return file_api_v1_k8s_proto_rawDescGZIP(), []int{37} +// Deprecated: Use NodeLabelSet.ProtoReflect.Descriptor instead. +func (*NodeLabelSet) Descriptor() ([]byte, []int) { + return file_api_v1_k8s_proto_rawDescGZIP(), []int{39} } -func (x *GetNodeGroupResponse) GetNodeGroup() *NodeGroup { +func (x *NodeLabelSet) GetLabels() map[string]string { if x != nil { - return x.NodeGroup + return x.Labels } return nil } -// GetClusterResponse contains details of a specific cluster for a team. -type GetClusterResponse struct { +// NodeLabelRef attributes one node to its label set. +type NodeLabelRef struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Cluster *Cluster `protobuf:"bytes,1,opt,name=cluster,proto3" json:"cluster,omitempty"` // Details of the cluster. + NodeId string `protobuf:"bytes,1,opt,name=node_id,json=nodeId,proto3" json:"node_id,omitempty"` // k8s_nodes.id, matching Node.id. A node name is not an identity. + LabelSetIndex uint32 `protobuf:"varint,2,opt,name=label_set_index,json=labelSetIndex,proto3" json:"label_set_index,omitempty"` // Index into GetClusterNodeFilterIndexResponse.label_sets. } -func (x *GetClusterResponse) Reset() { - *x = GetClusterResponse{} +func (x *NodeLabelRef) Reset() { + *x = NodeLabelRef{} if protoimpl.UnsafeEnabled { - mi := &file_api_v1_k8s_proto_msgTypes[38] + mi := &file_api_v1_k8s_proto_msgTypes[40] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } -func (x *GetClusterResponse) String() string { +func (x *NodeLabelRef) String() string { return protoimpl.X.MessageStringOf(x) } -func (*GetClusterResponse) ProtoMessage() {} +func (*NodeLabelRef) ProtoMessage() {} -func (x *GetClusterResponse) ProtoReflect() protoreflect.Message { - mi := &file_api_v1_k8s_proto_msgTypes[38] +func (x *NodeLabelRef) ProtoReflect() protoreflect.Message { + mi := &file_api_v1_k8s_proto_msgTypes[40] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3221,49 +3550,54 @@ func (x *GetClusterResponse) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use GetClusterResponse.ProtoReflect.Descriptor instead. -func (*GetClusterResponse) Descriptor() ([]byte, []int) { - return file_api_v1_k8s_proto_rawDescGZIP(), []int{38} +// Deprecated: Use NodeLabelRef.ProtoReflect.Descriptor instead. +func (*NodeLabelRef) Descriptor() ([]byte, []int) { + return file_api_v1_k8s_proto_rawDescGZIP(), []int{40} } -func (x *GetClusterResponse) GetCluster() *Cluster { +func (x *NodeLabelRef) GetNodeId() string { if x != nil { - return x.Cluster + return x.NodeId } - return nil + return "" } -// GetClustersResponse contains the list of clusters and aggregated metrics for a team. -type GetClustersResponse struct { +func (x *NodeLabelRef) GetLabelSetIndex() uint32 { + if x != nil { + return x.LabelSetIndex + } + return 0 +} + +// NodePoolFields carries scalar fields flattened off a Karpenter NodePool spec, +// keyed by dotted path. Present only for node groups backed by a Karpenter +// NodePool: a NodePool is one source of a node group, not a synonym for one. +type NodePoolFields struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Clusters []*Cluster `protobuf:"bytes,1,rep,name=clusters,proto3" json:"clusters,omitempty"` // List of clusters. - ResourceMetrics *ResourceMetrics `protobuf:"bytes,2,opt,name=resource_metrics,json=resourceMetrics,proto3" json:"resource_metrics,omitempty"` // Aggregated resource metrics across clusters. - CostInfo *CostInfo `protobuf:"bytes,3,opt,name=cost_info,json=costInfo,proto3" json:"cost_info,omitempty"` // Aggregated cost information across clusters. - NodeInfo *NodeInfo `protobuf:"bytes,4,opt,name=node_info,json=nodeInfo,proto3" json:"node_info,omitempty"` // Aggregated node information across clusters. - CostDataPoints []*CostDataPoint `protobuf:"bytes,5,rep,name=cost_data_points,json=costDataPoints,proto3" json:"cost_data_points,omitempty"` // Time-series cost data points. - ResourceDataPoints []*ResourceDataPoint `protobuf:"bytes,6,rep,name=resource_data_points,json=resourceDataPoints,proto3" json:"resource_data_points,omitempty"` // Time-series resource utilization data points. + NodeGroupName string `protobuf:"bytes,1,opt,name=node_group_name,json=nodeGroupName,proto3" json:"node_group_name,omitempty"` // Node group the NodePool backs, by name. + Fields map[string]string `protobuf:"bytes,2,rep,name=fields,proto3" json:"fields,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` // Dotted spec path to its scalar value as JSON text. } -func (x *GetClustersResponse) Reset() { - *x = GetClustersResponse{} +func (x *NodePoolFields) Reset() { + *x = NodePoolFields{} if protoimpl.UnsafeEnabled { - mi := &file_api_v1_k8s_proto_msgTypes[39] + mi := &file_api_v1_k8s_proto_msgTypes[41] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } -func (x *GetClustersResponse) String() string { +func (x *NodePoolFields) String() string { return protoimpl.X.MessageStringOf(x) } -func (*GetClustersResponse) ProtoMessage() {} +func (*NodePoolFields) ProtoMessage() {} -func (x *GetClustersResponse) ProtoReflect() protoreflect.Message { - mi := &file_api_v1_k8s_proto_msgTypes[39] +func (x *NodePoolFields) ProtoReflect() protoreflect.Message { + mi := &file_api_v1_k8s_proto_msgTypes[41] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3274,83 +3608,54 @@ func (x *GetClustersResponse) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use GetClustersResponse.ProtoReflect.Descriptor instead. -func (*GetClustersResponse) Descriptor() ([]byte, []int) { - return file_api_v1_k8s_proto_rawDescGZIP(), []int{39} -} - -func (x *GetClustersResponse) GetClusters() []*Cluster { - if x != nil { - return x.Clusters - } - return nil -} - -func (x *GetClustersResponse) GetResourceMetrics() *ResourceMetrics { - if x != nil { - return x.ResourceMetrics - } - return nil -} - -func (x *GetClustersResponse) GetCostInfo() *CostInfo { - if x != nil { - return x.CostInfo - } - return nil -} - -func (x *GetClustersResponse) GetNodeInfo() *NodeInfo { - if x != nil { - return x.NodeInfo - } - return nil +// Deprecated: Use NodePoolFields.ProtoReflect.Descriptor instead. +func (*NodePoolFields) Descriptor() ([]byte, []int) { + return file_api_v1_k8s_proto_rawDescGZIP(), []int{41} } -func (x *GetClustersResponse) GetCostDataPoints() []*CostDataPoint { +func (x *NodePoolFields) GetNodeGroupName() string { if x != nil { - return x.CostDataPoints + return x.NodeGroupName } - return nil + return "" } -func (x *GetClustersResponse) GetResourceDataPoints() []*ResourceDataPoint { +func (x *NodePoolFields) GetFields() map[string]string { if x != nil { - return x.ResourceDataPoints + return x.Fields } return nil } -// ListClustersResponse contains the paginated list of clusters for a team. -// Deprecated: Use GetClustersResponse instead. -// -// Deprecated: Marked as deprecated in api/v1/k8s.proto. -type ListClustersResponse struct { +// GetClusterNodeFilterIndexResponse contains the filterable attributes of a +// cluster's nodes and of the Karpenter NodePools backing their node groups. +type GetClusterNodeFilterIndexResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Clusters []*Cluster `protobuf:"bytes,1,rep,name=clusters,proto3" json:"clusters,omitempty"` // List of clusters. - Pagination *Pagination `protobuf:"bytes,2,opt,name=pagination,proto3" json:"pagination,omitempty"` // Pagination information. + LabelSets []*NodeLabelSet `protobuf:"bytes,1,rep,name=label_sets,json=labelSets,proto3" json:"label_sets,omitempty"` // Distinct label maps. + Nodes []*NodeLabelRef `protobuf:"bytes,2,rep,name=nodes,proto3" json:"nodes,omitempty"` // Node to label-set attribution. + NodePools []*NodePoolFields `protobuf:"bytes,3,rep,name=node_pools,json=nodePools,proto3" json:"node_pools,omitempty"` // Per node group NodePool spec fields. } -func (x *ListClustersResponse) Reset() { - *x = ListClustersResponse{} +func (x *GetClusterNodeFilterIndexResponse) Reset() { + *x = GetClusterNodeFilterIndexResponse{} if protoimpl.UnsafeEnabled { - mi := &file_api_v1_k8s_proto_msgTypes[40] + mi := &file_api_v1_k8s_proto_msgTypes[42] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } -func (x *ListClustersResponse) String() string { +func (x *GetClusterNodeFilterIndexResponse) String() string { return protoimpl.X.MessageStringOf(x) } -func (*ListClustersResponse) ProtoMessage() {} +func (*GetClusterNodeFilterIndexResponse) ProtoMessage() {} -func (x *ListClustersResponse) ProtoReflect() protoreflect.Message { - mi := &file_api_v1_k8s_proto_msgTypes[40] +func (x *GetClusterNodeFilterIndexResponse) ProtoReflect() protoreflect.Message { + mi := &file_api_v1_k8s_proto_msgTypes[42] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3361,55 +3666,65 @@ func (x *ListClustersResponse) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use ListClustersResponse.ProtoReflect.Descriptor instead. -func (*ListClustersResponse) Descriptor() ([]byte, []int) { - return file_api_v1_k8s_proto_rawDescGZIP(), []int{40} +// Deprecated: Use GetClusterNodeFilterIndexResponse.ProtoReflect.Descriptor instead. +func (*GetClusterNodeFilterIndexResponse) Descriptor() ([]byte, []int) { + return file_api_v1_k8s_proto_rawDescGZIP(), []int{42} } -func (x *ListClustersResponse) GetClusters() []*Cluster { +func (x *GetClusterNodeFilterIndexResponse) GetLabelSets() []*NodeLabelSet { if x != nil { - return x.Clusters + return x.LabelSets } return nil } -func (x *ListClustersResponse) GetPagination() *Pagination { +func (x *GetClusterNodeFilterIndexResponse) GetNodes() []*NodeLabelRef { if x != nil { - return x.Pagination + return x.Nodes } return nil } -// GetNodeResponse contains details and metrics for a specific node in a cluster. -type GetNodeResponse struct { +func (x *GetClusterNodeFilterIndexResponse) GetNodePools() []*NodePoolFields { + if x != nil { + return x.NodePools + } + return nil +} + +// GetNodeGroupsUtilizationRequest is used to fetch timeseries utilization data for node groups for a specific cluster. +type GetNodeGroupsUtilizationRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Node *Node `protobuf:"bytes,1,opt,name=node,proto3" json:"node,omitempty"` // Details of the node. - ResourceMetrics *ResourceMetrics `protobuf:"bytes,2,opt,name=resource_metrics,json=resourceMetrics,proto3" json:"resource_metrics,omitempty"` // Resource metrics for the node. - CostInfo *CostInfo `protobuf:"bytes,3,opt,name=cost_info,json=costInfo,proto3" json:"cost_info,omitempty"` // Cost details for the node. - CostDataPoints []*CostDataPoint `protobuf:"bytes,4,rep,name=cost_data_points,json=costDataPoints,proto3" json:"cost_data_points,omitempty"` // Time-series cost data points for the node. - ResourceDataPoints []*ResourceDataPoint `protobuf:"bytes,5,rep,name=resource_data_points,json=resourceDataPoints,proto3" json:"resource_data_points,omitempty"` // Time-series resource metrics for the node. + TeamId string `protobuf:"bytes,1,opt,name=team_id,json=teamId,proto3" json:"team_id,omitempty"` // Unique identifier for the team. + ClusterId string `protobuf:"bytes,2,opt,name=cluster_id,json=clusterId,proto3" json:"cluster_id,omitempty"` // Unique identifier for the cluster. + StartTime *timestamppb.Timestamp `protobuf:"bytes,11,opt,name=start_time,json=startTime,proto3,oneof" json:"start_time,omitempty"` + EndTime *timestamppb.Timestamp `protobuf:"bytes,12,opt,name=end_time,json=endTime,proto3,oneof" json:"end_time,omitempty"` + // Requested bucket width in seconds. Honoured within server bounds and + // clamped otherwise; absent or 0 means the server picks a width for the + // window. The width actually used is echoed as bucket_size_seconds. + BucketSize *int32 `protobuf:"varint,13,opt,name=bucket_size,json=bucketSize,proto3,oneof" json:"bucket_size,omitempty"` } -func (x *GetNodeResponse) Reset() { - *x = GetNodeResponse{} +func (x *GetNodeGroupsUtilizationRequest) Reset() { + *x = GetNodeGroupsUtilizationRequest{} if protoimpl.UnsafeEnabled { - mi := &file_api_v1_k8s_proto_msgTypes[41] + mi := &file_api_v1_k8s_proto_msgTypes[43] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } -func (x *GetNodeResponse) String() string { +func (x *GetNodeGroupsUtilizationRequest) String() string { return protoimpl.X.MessageStringOf(x) } -func (*GetNodeResponse) ProtoMessage() {} +func (*GetNodeGroupsUtilizationRequest) ProtoMessage() {} -func (x *GetNodeResponse) ProtoReflect() protoreflect.Message { - mi := &file_api_v1_k8s_proto_msgTypes[41] +func (x *GetNodeGroupsUtilizationRequest) ProtoReflect() protoreflect.Message { + mi := &file_api_v1_k8s_proto_msgTypes[43] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3420,75 +3735,77 @@ func (x *GetNodeResponse) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use GetNodeResponse.ProtoReflect.Descriptor instead. -func (*GetNodeResponse) Descriptor() ([]byte, []int) { - return file_api_v1_k8s_proto_rawDescGZIP(), []int{41} +// Deprecated: Use GetNodeGroupsUtilizationRequest.ProtoReflect.Descriptor instead. +func (*GetNodeGroupsUtilizationRequest) Descriptor() ([]byte, []int) { + return file_api_v1_k8s_proto_rawDescGZIP(), []int{43} } -func (x *GetNodeResponse) GetNode() *Node { +func (x *GetNodeGroupsUtilizationRequest) GetTeamId() string { if x != nil { - return x.Node + return x.TeamId } - return nil + return "" } -func (x *GetNodeResponse) GetResourceMetrics() *ResourceMetrics { +func (x *GetNodeGroupsUtilizationRequest) GetClusterId() string { if x != nil { - return x.ResourceMetrics + return x.ClusterId } - return nil + return "" } -func (x *GetNodeResponse) GetCostInfo() *CostInfo { +func (x *GetNodeGroupsUtilizationRequest) GetStartTime() *timestamppb.Timestamp { if x != nil { - return x.CostInfo + return x.StartTime } return nil } -func (x *GetNodeResponse) GetCostDataPoints() []*CostDataPoint { +func (x *GetNodeGroupsUtilizationRequest) GetEndTime() *timestamppb.Timestamp { if x != nil { - return x.CostDataPoints + return x.EndTime } return nil } -func (x *GetNodeResponse) GetResourceDataPoints() []*ResourceDataPoint { - if x != nil { - return x.ResourceDataPoints +func (x *GetNodeGroupsUtilizationRequest) GetBucketSize() int32 { + if x != nil && x.BucketSize != nil { + return *x.BucketSize } - return nil + return 0 } -// GetWorkloadsResponse contains a list of workloads and aggregated metrics for a cluster. -type GetWorkloadsResponse struct { +// GetNodeGroupsResponse contains the list of node groups for a specific cluster. +type GetNodeGroupsUtilizationResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - WorkloadItems []*WorkloadItem `protobuf:"bytes,1,rep,name=workload_items,json=workloadItems,proto3" json:"workload_items,omitempty"` // List of workload hierarchy items. - CostInfo *CostInfo `protobuf:"bytes,2,opt,name=cost_info,json=costInfo,proto3" json:"cost_info,omitempty"` // Aggregated cost information for workloads. - ResourceMetrics *ResourceMetrics `protobuf:"bytes,3,opt,name=resource_metrics,json=resourceMetrics,proto3" json:"resource_metrics,omitempty"` // Aggregated resource metrics for workloads. - ResourceSummary *ResourceSummary `protobuf:"bytes,4,opt,name=resource_summary,json=resourceSummary,proto3" json:"resource_summary,omitempty"` // Summary of resource counts and pod statuses. + NodeGroupToUtilMetrics map[string]*GetNodeGroupsUtilizationResponse_ListNodeGroupMetrics `protobuf:"bytes,1,rep,name=node_group_to_util_metrics,json=nodeGroupToUtilMetrics,proto3" json:"node_group_to_util_metrics,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + InstanceTypeToUtilMetrics map[string]*GetNodeGroupsUtilizationResponse_ListNodeGroupMetrics `protobuf:"bytes,6,rep,name=instance_type_to_util_metrics,json=instanceTypeToUtilMetrics,proto3" json:"instance_type_to_util_metrics,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + ReservationTypeToUtilMetrics map[string]*GetNodeGroupsUtilizationResponse_ListNodeGroupMetrics `protobuf:"bytes,11,rep,name=reservation_type_to_util_metrics,json=reservationTypeToUtilMetrics,proto3" json:"reservation_type_to_util_metrics,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + LogicalAzToUtilMetrics map[string]*GetNodeGroupsUtilizationResponse_ListNodeGroupMetrics `protobuf:"bytes,16,rep,name=logical_az_to_util_metrics,json=logicalAzToUtilMetrics,proto3" json:"logical_az_to_util_metrics,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + // The bucket width every series above was computed with, in seconds. + BucketSizeSeconds int32 `protobuf:"varint,21,opt,name=bucket_size_seconds,json=bucketSizeSeconds,proto3" json:"bucket_size_seconds,omitempty"` } -func (x *GetWorkloadsResponse) Reset() { - *x = GetWorkloadsResponse{} +func (x *GetNodeGroupsUtilizationResponse) Reset() { + *x = GetNodeGroupsUtilizationResponse{} if protoimpl.UnsafeEnabled { - mi := &file_api_v1_k8s_proto_msgTypes[42] + mi := &file_api_v1_k8s_proto_msgTypes[44] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } -func (x *GetWorkloadsResponse) String() string { +func (x *GetNodeGroupsUtilizationResponse) String() string { return protoimpl.X.MessageStringOf(x) } -func (*GetWorkloadsResponse) ProtoMessage() {} +func (*GetNodeGroupsUtilizationResponse) ProtoMessage() {} -func (x *GetWorkloadsResponse) ProtoReflect() protoreflect.Message { - mi := &file_api_v1_k8s_proto_msgTypes[42] +func (x *GetNodeGroupsUtilizationResponse) ProtoReflect() protoreflect.Message { + mi := &file_api_v1_k8s_proto_msgTypes[44] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3499,70 +3816,100 @@ func (x *GetWorkloadsResponse) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use GetWorkloadsResponse.ProtoReflect.Descriptor instead. -func (*GetWorkloadsResponse) Descriptor() ([]byte, []int) { - return file_api_v1_k8s_proto_rawDescGZIP(), []int{42} +// Deprecated: Use GetNodeGroupsUtilizationResponse.ProtoReflect.Descriptor instead. +func (*GetNodeGroupsUtilizationResponse) Descriptor() ([]byte, []int) { + return file_api_v1_k8s_proto_rawDescGZIP(), []int{44} } -func (x *GetWorkloadsResponse) GetWorkloadItems() []*WorkloadItem { +func (x *GetNodeGroupsUtilizationResponse) GetNodeGroupToUtilMetrics() map[string]*GetNodeGroupsUtilizationResponse_ListNodeGroupMetrics { if x != nil { - return x.WorkloadItems + return x.NodeGroupToUtilMetrics } return nil } -func (x *GetWorkloadsResponse) GetCostInfo() *CostInfo { +func (x *GetNodeGroupsUtilizationResponse) GetInstanceTypeToUtilMetrics() map[string]*GetNodeGroupsUtilizationResponse_ListNodeGroupMetrics { if x != nil { - return x.CostInfo + return x.InstanceTypeToUtilMetrics } return nil } -func (x *GetWorkloadsResponse) GetResourceMetrics() *ResourceMetrics { +func (x *GetNodeGroupsUtilizationResponse) GetReservationTypeToUtilMetrics() map[string]*GetNodeGroupsUtilizationResponse_ListNodeGroupMetrics { if x != nil { - return x.ResourceMetrics + return x.ReservationTypeToUtilMetrics } return nil } -func (x *GetWorkloadsResponse) GetResourceSummary() *ResourceSummary { +func (x *GetNodeGroupsUtilizationResponse) GetLogicalAzToUtilMetrics() map[string]*GetNodeGroupsUtilizationResponse_ListNodeGroupMetrics { if x != nil { - return x.ResourceSummary + return x.LogicalAzToUtilMetrics } return nil } -// GetWorkloadResponse contains details and metrics for a specific workload in a cluster. -type GetWorkloadResponse struct { +func (x *GetNodeGroupsUtilizationResponse) GetBucketSizeSeconds() int32 { + if x != nil { + return x.BucketSizeSeconds + } + return 0 +} + +type GetClusterNodeInventoryRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - WorkloadItem *WorkloadItem `protobuf:"bytes,1,opt,name=workload_item,json=workloadItem,proto3" json:"workload_item,omitempty"` // Details of the workload hierarchy. - CostInfo *CostInfo `protobuf:"bytes,2,opt,name=cost_info,json=costInfo,proto3" json:"cost_info,omitempty"` // Aggregated cost information for the workload. - ResourceMetrics *ResourceMetrics `protobuf:"bytes,3,opt,name=resource_metrics,json=resourceMetrics,proto3" json:"resource_metrics,omitempty"` // Aggregated resource metrics for the workload. - ResourceSummary *ResourceSummary `protobuf:"bytes,4,opt,name=resource_summary,json=resourceSummary,proto3" json:"resource_summary,omitempty"` // Summary of resource counts and pod statuses for the workload. - CostDataPoints []*CostDataPoint `protobuf:"bytes,5,rep,name=cost_data_points,json=costDataPoints,proto3" json:"cost_data_points,omitempty"` // Time-series cost data points for the workload. - ResourceDataPoints []*ResourceDataPoint `protobuf:"bytes,6,rep,name=resource_data_points,json=resourceDataPoints,proto3" json:"resource_data_points,omitempty"` // Time-series resource metrics for the workload. -} - -func (x *GetWorkloadResponse) Reset() { - *x = GetWorkloadResponse{} + TeamId string `protobuf:"bytes,1,opt,name=team_id,json=teamId,proto3" json:"team_id,omitempty"` + ClusterId string `protobuf:"bytes,2,opt,name=cluster_id,json=clusterId,proto3" json:"cluster_id,omitempty"` + Scope NodeInventoryScope `protobuf:"varint,3,opt,name=scope,proto3,enum=api.v1.NodeInventoryScope" json:"scope,omitempty"` + NodeGroupName string `protobuf:"bytes,4,opt,name=node_group_name,json=nodeGroupName,proto3" json:"node_group_name,omitempty"` // NODES scope: empty = all pools (flat view). + PageSize int32 `protobuf:"varint,5,opt,name=page_size,json=pageSize,proto3" json:"page_size,omitempty"` // NODES scope; server-clamped, default 200. + PageToken string `protobuf:"bytes,6,opt,name=page_token,json=pageToken,proto3" json:"page_token,omitempty"` // Keyset cursor over (node_group_name, name). + States []NodeState `protobuf:"varint,7,rep,packed,name=states,proto3,enum=api.v1.NodeState" json:"states,omitempty"` // Optional filter; a state listed more than once counts once. + // NODES scope: true selects the one pool whose name is empty, the nodes that carry no + // node group, which node_group_name cannot select because empty there means every pool. + // Setting it together with a non-empty node_group_name is rejected with INVALID_ARGUMENT, + // as is setting it in the POOLS scope. + UnnamedNodeGroup bool `protobuf:"varint,8,opt,name=unnamed_node_group,json=unnamedNodeGroup,proto3" json:"unnamed_node_group,omitempty"` + // NODES scope: keeps only the nodes whose name contains this text, ignoring case + // (lower(name) contains lower(text)); every character is literal, none is a wildcard. + // Empty means no search. Applied before paging, so total_node_count and next_page_token + // describe the searched set; totals are never narrowed by it. Rejected with + // INVALID_ARGUMENT in the POOLS scope. A page_token from a different search is not + // rejected but walks the new set from that position, so start a new search from page one. + // At most 253 characters (Unicode code points, not bytes), the longest a node name can + // be; a longer search is rejected with INVALID_ARGUMENT. + NodeNameContains string `protobuf:"bytes,9,opt,name=node_name_contains,json=nodeNameContains,proto3" json:"node_name_contains,omitempty"` + // NODES scope: keeps only the nodes whose instance type is one of these, exactly. + // Empty means every type. Applied before paging, like node_name_contains, so + // total_node_count and next_page_token describe the filtered set; totals are never + // narrowed by it. Rejected with INVALID_ARGUMENT in the POOLS scope. At most 1000 + // entries; more is rejected with INVALID_ARGUMENT. + InstanceTypes []string `protobuf:"bytes,10,rep,name=instance_types,json=instanceTypes,proto3" json:"instance_types,omitempty"` + // POOLS scope: also return each pool's first nodes_per_pool nodes, in the NODES scope's + // order, with a per-pool cursor for the rest. 0 returns pools only. Server-clamped. + NodesPerPool int32 `protobuf:"varint,11,opt,name=nodes_per_pool,json=nodesPerPool,proto3" json:"nodes_per_pool,omitempty"` +} + +func (x *GetClusterNodeInventoryRequest) Reset() { + *x = GetClusterNodeInventoryRequest{} if protoimpl.UnsafeEnabled { - mi := &file_api_v1_k8s_proto_msgTypes[43] + mi := &file_api_v1_k8s_proto_msgTypes[45] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } -func (x *GetWorkloadResponse) String() string { +func (x *GetClusterNodeInventoryRequest) String() string { return protoimpl.X.MessageStringOf(x) } -func (*GetWorkloadResponse) ProtoMessage() {} +func (*GetClusterNodeInventoryRequest) ProtoMessage() {} -func (x *GetWorkloadResponse) ProtoReflect() protoreflect.Message { - mi := &file_api_v1_k8s_proto_msgTypes[43] +func (x *GetClusterNodeInventoryRequest) ProtoReflect() protoreflect.Message { + mi := &file_api_v1_k8s_proto_msgTypes[45] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3573,84 +3920,118 @@ func (x *GetWorkloadResponse) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use GetWorkloadResponse.ProtoReflect.Descriptor instead. -func (*GetWorkloadResponse) Descriptor() ([]byte, []int) { - return file_api_v1_k8s_proto_rawDescGZIP(), []int{43} +// Deprecated: Use GetClusterNodeInventoryRequest.ProtoReflect.Descriptor instead. +func (*GetClusterNodeInventoryRequest) Descriptor() ([]byte, []int) { + return file_api_v1_k8s_proto_rawDescGZIP(), []int{45} } -func (x *GetWorkloadResponse) GetWorkloadItem() *WorkloadItem { +func (x *GetClusterNodeInventoryRequest) GetTeamId() string { if x != nil { - return x.WorkloadItem + return x.TeamId } - return nil + return "" } -func (x *GetWorkloadResponse) GetCostInfo() *CostInfo { +func (x *GetClusterNodeInventoryRequest) GetClusterId() string { if x != nil { - return x.CostInfo + return x.ClusterId } - return nil + return "" } -func (x *GetWorkloadResponse) GetResourceMetrics() *ResourceMetrics { +func (x *GetClusterNodeInventoryRequest) GetScope() NodeInventoryScope { if x != nil { - return x.ResourceMetrics + return x.Scope } - return nil + return NodeInventoryScope_NODE_INVENTORY_SCOPE_UNSPECIFIED } -func (x *GetWorkloadResponse) GetResourceSummary() *ResourceSummary { +func (x *GetClusterNodeInventoryRequest) GetNodeGroupName() string { if x != nil { - return x.ResourceSummary + return x.NodeGroupName } - return nil + return "" } -func (x *GetWorkloadResponse) GetCostDataPoints() []*CostDataPoint { +func (x *GetClusterNodeInventoryRequest) GetPageSize() int32 { if x != nil { - return x.CostDataPoints + return x.PageSize + } + return 0 +} + +func (x *GetClusterNodeInventoryRequest) GetPageToken() string { + if x != nil { + return x.PageToken + } + return "" +} + +func (x *GetClusterNodeInventoryRequest) GetStates() []NodeState { + if x != nil { + return x.States } return nil } -func (x *GetWorkloadResponse) GetResourceDataPoints() []*ResourceDataPoint { +func (x *GetClusterNodeInventoryRequest) GetUnnamedNodeGroup() bool { if x != nil { - return x.ResourceDataPoints + return x.UnnamedNodeGroup + } + return false +} + +func (x *GetClusterNodeInventoryRequest) GetNodeNameContains() string { + if x != nil { + return x.NodeNameContains + } + return "" +} + +func (x *GetClusterNodeInventoryRequest) GetInstanceTypes() []string { + if x != nil { + return x.InstanceTypes } return nil } -// GetWorkloadContainerPercentilesRequest requests per-container percentile metrics for a workload. -type GetWorkloadContainerPercentilesRequest struct { +func (x *GetClusterNodeInventoryRequest) GetNodesPerPool() int32 { + if x != nil { + return x.NodesPerPool + } + return 0 +} + +type GetClusterNodeInventoryResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - TeamId string `protobuf:"bytes,1,opt,name=team_id,json=teamId,proto3" json:"team_id,omitempty"` - ClusterId string `protobuf:"bytes,2,opt,name=cluster_id,json=clusterId,proto3" json:"cluster_id,omitempty"` - Kind string `protobuf:"bytes,3,opt,name=kind,proto3" json:"kind,omitempty"` - Uid string `protobuf:"bytes,4,opt,name=uid,proto3" json:"uid,omitempty"` - StartTime *timestamppb.Timestamp `protobuf:"bytes,11,opt,name=start_time,json=startTime,proto3,oneof" json:"start_time,omitempty"` - EndTime *timestamppb.Timestamp `protobuf:"bytes,12,opt,name=end_time,json=endTime,proto3,oneof" json:"end_time,omitempty"` + AsOf *timestamppb.Timestamp `protobuf:"bytes,1,opt,name=as_of,json=asOf,proto3" json:"as_of,omitempty"` + Totals *NodeInventoryRollup `protobuf:"bytes,2,opt,name=totals,proto3" json:"totals,omitempty"` // Always set: whole cluster, ignores paging. + Pools []*NodePoolInventory `protobuf:"bytes,3,rep,name=pools,proto3" json:"pools,omitempty"` // POOLS scope. + Nodes []*NodeInventoryRow `protobuf:"bytes,4,rep,name=nodes,proto3" json:"nodes,omitempty"` // NODES scope. + NextPageToken string `protobuf:"bytes,5,opt,name=next_page_token,json=nextPageToken,proto3" json:"next_page_token,omitempty"` + TotalNodeCount int32 `protobuf:"varint,6,opt,name=total_node_count,json=totalNodeCount,proto3" json:"total_node_count,omitempty"` // Matching the filter, uncapped. } -func (x *GetWorkloadContainerPercentilesRequest) Reset() { - *x = GetWorkloadContainerPercentilesRequest{} +func (x *GetClusterNodeInventoryResponse) Reset() { + *x = GetClusterNodeInventoryResponse{} if protoimpl.UnsafeEnabled { - mi := &file_api_v1_k8s_proto_msgTypes[44] + mi := &file_api_v1_k8s_proto_msgTypes[46] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } -func (x *GetWorkloadContainerPercentilesRequest) String() string { +func (x *GetClusterNodeInventoryResponse) String() string { return protoimpl.X.MessageStringOf(x) } -func (*GetWorkloadContainerPercentilesRequest) ProtoMessage() {} +func (*GetClusterNodeInventoryResponse) ProtoMessage() {} -func (x *GetWorkloadContainerPercentilesRequest) ProtoReflect() protoreflect.Message { - mi := &file_api_v1_k8s_proto_msgTypes[44] +func (x *GetClusterNodeInventoryResponse) ProtoReflect() protoreflect.Message { + mi := &file_api_v1_k8s_proto_msgTypes[46] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3661,79 +4042,106 @@ func (x *GetWorkloadContainerPercentilesRequest) ProtoReflect() protoreflect.Mes return mi.MessageOf(x) } -// Deprecated: Use GetWorkloadContainerPercentilesRequest.ProtoReflect.Descriptor instead. -func (*GetWorkloadContainerPercentilesRequest) Descriptor() ([]byte, []int) { - return file_api_v1_k8s_proto_rawDescGZIP(), []int{44} +// Deprecated: Use GetClusterNodeInventoryResponse.ProtoReflect.Descriptor instead. +func (*GetClusterNodeInventoryResponse) Descriptor() ([]byte, []int) { + return file_api_v1_k8s_proto_rawDescGZIP(), []int{46} } -func (x *GetWorkloadContainerPercentilesRequest) GetTeamId() string { +func (x *GetClusterNodeInventoryResponse) GetAsOf() *timestamppb.Timestamp { if x != nil { - return x.TeamId + return x.AsOf } - return "" + return nil } -func (x *GetWorkloadContainerPercentilesRequest) GetClusterId() string { +func (x *GetClusterNodeInventoryResponse) GetTotals() *NodeInventoryRollup { if x != nil { - return x.ClusterId + return x.Totals } - return "" + return nil } -func (x *GetWorkloadContainerPercentilesRequest) GetKind() string { +func (x *GetClusterNodeInventoryResponse) GetPools() []*NodePoolInventory { if x != nil { - return x.Kind + return x.Pools } - return "" + return nil } -func (x *GetWorkloadContainerPercentilesRequest) GetUid() string { +func (x *GetClusterNodeInventoryResponse) GetNodes() []*NodeInventoryRow { if x != nil { - return x.Uid + return x.Nodes } - return "" + return nil } -func (x *GetWorkloadContainerPercentilesRequest) GetStartTime() *timestamppb.Timestamp { +func (x *GetClusterNodeInventoryResponse) GetNextPageToken() string { if x != nil { - return x.StartTime + return x.NextPageToken } - return nil + return "" } -func (x *GetWorkloadContainerPercentilesRequest) GetEndTime() *timestamppb.Timestamp { +func (x *GetClusterNodeInventoryResponse) GetTotalNodeCount() int32 { if x != nil { - return x.EndTime + return x.TotalNodeCount } - return nil + return 0 } -// GetWorkloadContainerPercentilesResponse returns per-container percentile statistics. -type GetWorkloadContainerPercentilesResponse struct { +type NodeInventoryRollup struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Containers []*ContainerPercentileSummary `protobuf:"bytes,1,rep,name=containers,proto3" json:"containers,omitempty"` -} - -func (x *GetWorkloadContainerPercentilesResponse) Reset() { - *x = GetWorkloadContainerPercentilesResponse{} + NodeCount int32 `protobuf:"varint,1,opt,name=node_count,json=nodeCount,proto3" json:"node_count,omitempty"` + NodeInfo *NodeInfo `protobuf:"bytes,2,opt,name=node_info,json=nodeInfo,proto3" json:"node_info,omitempty"` // Existing message: counts by reservation type. + States *NodeStateCounts `protobuf:"bytes,3,opt,name=states,proto3" json:"states,omitempty"` + GpuNodeCount int32 `protobuf:"varint,4,opt,name=gpu_node_count,json=gpuNodeCount,proto3" json:"gpu_node_count,omitempty"` + MoneyCostPerHour float64 `protobuf:"fixed64,5,opt,name=money_cost_per_hour,json=moneyCostPerHour,proto3" json:"money_cost_per_hour,omitempty"` // Σ current node price. + CpuCapacityMillicores float64 `protobuf:"fixed64,6,opt,name=cpu_capacity_millicores,json=cpuCapacityMillicores,proto3" json:"cpu_capacity_millicores,omitempty"` + MemoryCapacityBytes float64 `protobuf:"fixed64,7,opt,name=memory_capacity_bytes,json=memoryCapacityBytes,proto3" json:"memory_capacity_bytes,omitempty"` + GpuCapacity float64 `protobuf:"fixed64,8,opt,name=gpu_capacity,json=gpuCapacity,proto3" json:"gpu_capacity,omitempty"` + CpuRequestedMillicores float64 `protobuf:"fixed64,9,opt,name=cpu_requested_millicores,json=cpuRequestedMillicores,proto3" json:"cpu_requested_millicores,omitempty"` // Live pods, non-DaemonSet, not Succeeded / Failed. + MemoryRequestedBytes float64 `protobuf:"fixed64,10,opt,name=memory_requested_bytes,json=memoryRequestedBytes,proto3" json:"memory_requested_bytes,omitempty"` + GpuRequested float64 `protobuf:"fixed64,11,opt,name=gpu_requested,json=gpuRequested,proto3" json:"gpu_requested,omitempty"` + // Number of node pools with at least one live node in this set: every such pool for the + // whole-cluster totals, 1 for a pool's own rollup. Nodes with no node group count as one + // pool, the unnamed one. Not the number of pools the cluster declares: a pool with no + // live node is not counted. + PoolCount int32 `protobuf:"varint,12,opt,name=pool_count,json=poolCount,proto3" json:"pool_count,omitempty"` + // On-demand nodes among fallback_eligible_node_count. Karpenter launches spot + // first in a NodePool that admits both, so these are its fallbacks plus the + // nodes whose pods required on-demand, and the on-demand nodes launched before + // their NodePool admitted spot. + FallbackNodeCount int32 `protobuf:"varint,13,opt,name=fallback_node_count,json=fallbackNodeCount,proto3" json:"fallback_node_count,omitempty"` + // The nodes fallback_node_count is counted among: nodes whose NodePool, named by + // their karpenter.sh/nodepool label, admits both spot and on-demand, whatever + // capacity type they launched with. Never below fallback_node_count. At 0 no + // fallback can be counted here and fallback_node_count measures nothing. Only a + // fallback inside one Karpenter NodePool is counted: nodes of every other node + // group count 0, and so does a fallback from one node group or NodePool to + // another (Cluster Autoscaler priorities, weighted NodePools). + FallbackEligibleNodeCount int32 `protobuf:"varint,14,opt,name=fallback_eligible_node_count,json=fallbackEligibleNodeCount,proto3" json:"fallback_eligible_node_count,omitempty"` +} + +func (x *NodeInventoryRollup) Reset() { + *x = NodeInventoryRollup{} if protoimpl.UnsafeEnabled { - mi := &file_api_v1_k8s_proto_msgTypes[45] + mi := &file_api_v1_k8s_proto_msgTypes[47] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } -func (x *GetWorkloadContainerPercentilesResponse) String() string { +func (x *NodeInventoryRollup) String() string { return protoimpl.X.MessageStringOf(x) } -func (*GetWorkloadContainerPercentilesResponse) ProtoMessage() {} +func (*NodeInventoryRollup) ProtoMessage() {} -func (x *GetWorkloadContainerPercentilesResponse) ProtoReflect() protoreflect.Message { - mi := &file_api_v1_k8s_proto_msgTypes[45] +func (x *NodeInventoryRollup) ProtoReflect() protoreflect.Message { + mi := &file_api_v1_k8s_proto_msgTypes[47] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3744,167 +4152,123 @@ func (x *GetWorkloadContainerPercentilesResponse) ProtoReflect() protoreflect.Me return mi.MessageOf(x) } -// Deprecated: Use GetWorkloadContainerPercentilesResponse.ProtoReflect.Descriptor instead. -func (*GetWorkloadContainerPercentilesResponse) Descriptor() ([]byte, []int) { - return file_api_v1_k8s_proto_rawDescGZIP(), []int{45} +// Deprecated: Use NodeInventoryRollup.ProtoReflect.Descriptor instead. +func (*NodeInventoryRollup) Descriptor() ([]byte, []int) { + return file_api_v1_k8s_proto_rawDescGZIP(), []int{47} } -func (x *GetWorkloadContainerPercentilesResponse) GetContainers() []*ContainerPercentileSummary { +func (x *NodeInventoryRollup) GetNodeCount() int32 { if x != nil { - return x.Containers + return x.NodeCount } - return nil -} - -// GetWorkloadContainerTimeSeriesRequest requests per-container time-series metrics for a workload. -type GetWorkloadContainerTimeSeriesRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - TeamId string `protobuf:"bytes,1,opt,name=team_id,json=teamId,proto3" json:"team_id,omitempty"` - ClusterId string `protobuf:"bytes,2,opt,name=cluster_id,json=clusterId,proto3" json:"cluster_id,omitempty"` - Kind string `protobuf:"bytes,3,opt,name=kind,proto3" json:"kind,omitempty"` - Uid string `protobuf:"bytes,4,opt,name=uid,proto3" json:"uid,omitempty"` - StartTime *timestamppb.Timestamp `protobuf:"bytes,11,opt,name=start_time,json=startTime,proto3,oneof" json:"start_time,omitempty"` - EndTime *timestamppb.Timestamp `protobuf:"bytes,12,opt,name=end_time,json=endTime,proto3,oneof" json:"end_time,omitempty"` + return 0 } -func (x *GetWorkloadContainerTimeSeriesRequest) Reset() { - *x = GetWorkloadContainerTimeSeriesRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_api_v1_k8s_proto_msgTypes[46] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) +func (x *NodeInventoryRollup) GetNodeInfo() *NodeInfo { + if x != nil { + return x.NodeInfo } + return nil } -func (x *GetWorkloadContainerTimeSeriesRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*GetWorkloadContainerTimeSeriesRequest) ProtoMessage() {} - -func (x *GetWorkloadContainerTimeSeriesRequest) ProtoReflect() protoreflect.Message { - mi := &file_api_v1_k8s_proto_msgTypes[46] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms +func (x *NodeInventoryRollup) GetStates() *NodeStateCounts { + if x != nil { + return x.States } - return mi.MessageOf(x) -} - -// Deprecated: Use GetWorkloadContainerTimeSeriesRequest.ProtoReflect.Descriptor instead. -func (*GetWorkloadContainerTimeSeriesRequest) Descriptor() ([]byte, []int) { - return file_api_v1_k8s_proto_rawDescGZIP(), []int{46} + return nil } -func (x *GetWorkloadContainerTimeSeriesRequest) GetTeamId() string { +func (x *NodeInventoryRollup) GetGpuNodeCount() int32 { if x != nil { - return x.TeamId + return x.GpuNodeCount } - return "" + return 0 } -func (x *GetWorkloadContainerTimeSeriesRequest) GetClusterId() string { +func (x *NodeInventoryRollup) GetMoneyCostPerHour() float64 { if x != nil { - return x.ClusterId + return x.MoneyCostPerHour } - return "" + return 0 } -func (x *GetWorkloadContainerTimeSeriesRequest) GetKind() string { +func (x *NodeInventoryRollup) GetCpuCapacityMillicores() float64 { if x != nil { - return x.Kind + return x.CpuCapacityMillicores } - return "" + return 0 } -func (x *GetWorkloadContainerTimeSeriesRequest) GetUid() string { +func (x *NodeInventoryRollup) GetMemoryCapacityBytes() float64 { if x != nil { - return x.Uid + return x.MemoryCapacityBytes } - return "" + return 0 } -func (x *GetWorkloadContainerTimeSeriesRequest) GetStartTime() *timestamppb.Timestamp { +func (x *NodeInventoryRollup) GetGpuCapacity() float64 { if x != nil { - return x.StartTime + return x.GpuCapacity } - return nil + return 0 } -func (x *GetWorkloadContainerTimeSeriesRequest) GetEndTime() *timestamppb.Timestamp { +func (x *NodeInventoryRollup) GetCpuRequestedMillicores() float64 { if x != nil { - return x.EndTime + return x.CpuRequestedMillicores } - return nil -} - -// GetWorkloadContainerTimeSeriesResponse returns per-container time-series data. -type GetWorkloadContainerTimeSeriesResponse struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Containers []*ContainerTimeSeries `protobuf:"bytes,1,rep,name=containers,proto3" json:"containers,omitempty"` + return 0 } -func (x *GetWorkloadContainerTimeSeriesResponse) Reset() { - *x = GetWorkloadContainerTimeSeriesResponse{} - if protoimpl.UnsafeEnabled { - mi := &file_api_v1_k8s_proto_msgTypes[47] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) +func (x *NodeInventoryRollup) GetMemoryRequestedBytes() float64 { + if x != nil { + return x.MemoryRequestedBytes } + return 0 } -func (x *GetWorkloadContainerTimeSeriesResponse) String() string { - return protoimpl.X.MessageStringOf(x) +func (x *NodeInventoryRollup) GetGpuRequested() float64 { + if x != nil { + return x.GpuRequested + } + return 0 } -func (*GetWorkloadContainerTimeSeriesResponse) ProtoMessage() {} - -func (x *GetWorkloadContainerTimeSeriesResponse) ProtoReflect() protoreflect.Message { - mi := &file_api_v1_k8s_proto_msgTypes[47] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms +func (x *NodeInventoryRollup) GetPoolCount() int32 { + if x != nil { + return x.PoolCount } - return mi.MessageOf(x) + return 0 } -// Deprecated: Use GetWorkloadContainerTimeSeriesResponse.ProtoReflect.Descriptor instead. -func (*GetWorkloadContainerTimeSeriesResponse) Descriptor() ([]byte, []int) { - return file_api_v1_k8s_proto_rawDescGZIP(), []int{47} +func (x *NodeInventoryRollup) GetFallbackNodeCount() int32 { + if x != nil { + return x.FallbackNodeCount + } + return 0 } -func (x *GetWorkloadContainerTimeSeriesResponse) GetContainers() []*ContainerTimeSeries { +func (x *NodeInventoryRollup) GetFallbackEligibleNodeCount() int32 { if x != nil { - return x.Containers + return x.FallbackEligibleNodeCount } - return nil + return 0 } -// GetLatestContainerRequestLimitsRequest requests the most recent request/limit values per container. -type GetLatestContainerRequestLimitsRequest struct { +type NodeStateCounts struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - TeamId string `protobuf:"bytes,1,opt,name=team_id,json=teamId,proto3" json:"team_id,omitempty"` - ClusterId string `protobuf:"bytes,2,opt,name=cluster_id,json=clusterId,proto3" json:"cluster_id,omitempty"` - Uid string `protobuf:"bytes,3,opt,name=uid,proto3" json:"uid,omitempty"` + Ready int32 `protobuf:"varint,1,opt,name=ready,proto3" json:"ready,omitempty"` + Cordoned int32 `protobuf:"varint,2,opt,name=cordoned,proto3" json:"cordoned,omitempty"` + Draining int32 `protobuf:"varint,3,opt,name=draining,proto3" json:"draining,omitempty"` + NotReady int32 `protobuf:"varint,4,opt,name=not_ready,json=notReady,proto3" json:"not_ready,omitempty"` + Deleting int32 `protobuf:"varint,5,opt,name=deleting,proto3" json:"deleting,omitempty"` } -func (x *GetLatestContainerRequestLimitsRequest) Reset() { - *x = GetLatestContainerRequestLimitsRequest{} +func (x *NodeStateCounts) Reset() { + *x = NodeStateCounts{} if protoimpl.UnsafeEnabled { mi := &file_api_v1_k8s_proto_msgTypes[48] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -3912,13 +4276,13 @@ func (x *GetLatestContainerRequestLimitsRequest) Reset() { } } -func (x *GetLatestContainerRequestLimitsRequest) String() string { +func (x *NodeStateCounts) String() string { return protoimpl.X.MessageStringOf(x) } -func (*GetLatestContainerRequestLimitsRequest) ProtoMessage() {} +func (*NodeStateCounts) ProtoMessage() {} -func (x *GetLatestContainerRequestLimitsRequest) ProtoReflect() protoreflect.Message { +func (x *NodeStateCounts) ProtoReflect() protoreflect.Message { mi := &file_api_v1_k8s_proto_msgTypes[48] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -3930,43 +4294,61 @@ func (x *GetLatestContainerRequestLimitsRequest) ProtoReflect() protoreflect.Mes return mi.MessageOf(x) } -// Deprecated: Use GetLatestContainerRequestLimitsRequest.ProtoReflect.Descriptor instead. -func (*GetLatestContainerRequestLimitsRequest) Descriptor() ([]byte, []int) { +// Deprecated: Use NodeStateCounts.ProtoReflect.Descriptor instead. +func (*NodeStateCounts) Descriptor() ([]byte, []int) { return file_api_v1_k8s_proto_rawDescGZIP(), []int{48} } -func (x *GetLatestContainerRequestLimitsRequest) GetTeamId() string { +func (x *NodeStateCounts) GetReady() int32 { if x != nil { - return x.TeamId + return x.Ready } - return "" + return 0 } -func (x *GetLatestContainerRequestLimitsRequest) GetClusterId() string { +func (x *NodeStateCounts) GetCordoned() int32 { if x != nil { - return x.ClusterId + return x.Cordoned } - return "" + return 0 } -func (x *GetLatestContainerRequestLimitsRequest) GetUid() string { +func (x *NodeStateCounts) GetDraining() int32 { if x != nil { - return x.Uid + return x.Draining } - return "" + return 0 } -// GetLatestContainerRequestLimitsResponse returns per-container request/limit values. -type GetLatestContainerRequestLimitsResponse struct { +func (x *NodeStateCounts) GetNotReady() int32 { + if x != nil { + return x.NotReady + } + return 0 +} + +func (x *NodeStateCounts) GetDeleting() int32 { + if x != nil { + return x.Deleting + } + return 0 +} + +type NodePoolInventory struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Containers []*ContainerRequestLimits `protobuf:"bytes,1,rep,name=containers,proto3" json:"containers,omitempty"` + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + Provisioner NodeGroupProvisioner `protobuf:"varint,2,opt,name=provisioner,proto3,enum=api.v1.NodeGroupProvisioner" json:"provisioner,omitempty"` // Existing enum. + Rollup *NodeInventoryRollup `protobuf:"bytes,3,opt,name=rollup,proto3" json:"rollup,omitempty"` + InstanceTypes []*InstanceTypeCount `protobuf:"bytes,4,rep,name=instance_types,json=instanceTypes,proto3" json:"instance_types,omitempty"` // Descending by count. + Nodes []*NodeInventoryRow `protobuf:"bytes,5,rep,name=nodes,proto3" json:"nodes,omitempty"` // Set only when nodes_per_pool > 0. + NextPageToken string `protobuf:"bytes,6,opt,name=next_page_token,json=nextPageToken,proto3" json:"next_page_token,omitempty"` // Cursor for that pool's NODES read; empty when the pool fits. } -func (x *GetLatestContainerRequestLimitsResponse) Reset() { - *x = GetLatestContainerRequestLimitsResponse{} +func (x *NodePoolInventory) Reset() { + *x = NodePoolInventory{} if protoimpl.UnsafeEnabled { mi := &file_api_v1_k8s_proto_msgTypes[49] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -3974,13 +4356,13 @@ func (x *GetLatestContainerRequestLimitsResponse) Reset() { } } -func (x *GetLatestContainerRequestLimitsResponse) String() string { +func (x *NodePoolInventory) String() string { return protoimpl.X.MessageStringOf(x) } -func (*GetLatestContainerRequestLimitsResponse) ProtoMessage() {} +func (*NodePoolInventory) ProtoMessage() {} -func (x *GetLatestContainerRequestLimitsResponse) ProtoReflect() protoreflect.Message { +func (x *NodePoolInventory) ProtoReflect() protoreflect.Message { mi := &file_api_v1_k8s_proto_msgTypes[49] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -3992,32 +4374,64 @@ func (x *GetLatestContainerRequestLimitsResponse) ProtoReflect() protoreflect.Me return mi.MessageOf(x) } -// Deprecated: Use GetLatestContainerRequestLimitsResponse.ProtoReflect.Descriptor instead. -func (*GetLatestContainerRequestLimitsResponse) Descriptor() ([]byte, []int) { +// Deprecated: Use NodePoolInventory.ProtoReflect.Descriptor instead. +func (*NodePoolInventory) Descriptor() ([]byte, []int) { return file_api_v1_k8s_proto_rawDescGZIP(), []int{49} } -func (x *GetLatestContainerRequestLimitsResponse) GetContainers() []*ContainerRequestLimits { +func (x *NodePoolInventory) GetName() string { if x != nil { - return x.Containers + return x.Name + } + return "" +} + +func (x *NodePoolInventory) GetProvisioner() NodeGroupProvisioner { + if x != nil { + return x.Provisioner + } + return NodeGroupProvisioner_NODE_GROUP_PROVISIONER_UNSPECIFIED +} + +func (x *NodePoolInventory) GetRollup() *NodeInventoryRollup { + if x != nil { + return x.Rollup } return nil } -// GetNodeGPUMigInstancesRequest requests the most recent MIG-instance telemetry for a node. -type GetNodeGPUMigInstancesRequest struct { +func (x *NodePoolInventory) GetInstanceTypes() []*InstanceTypeCount { + if x != nil { + return x.InstanceTypes + } + return nil +} + +func (x *NodePoolInventory) GetNodes() []*NodeInventoryRow { + if x != nil { + return x.Nodes + } + return nil +} + +func (x *NodePoolInventory) GetNextPageToken() string { + if x != nil { + return x.NextPageToken + } + return "" +} + +type InstanceTypeCount struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - TeamId string `protobuf:"bytes,1,opt,name=team_id,json=teamId,proto3" json:"team_id,omitempty"` - ClusterId string `protobuf:"bytes,2,opt,name=cluster_id,json=clusterId,proto3" json:"cluster_id,omitempty"` - NodeId *string `protobuf:"bytes,3,opt,name=node_id,json=nodeId,proto3,oneof" json:"node_id,omitempty"` // Unique identifier for the node. - NodeUid *string `protobuf:"bytes,4,opt,name=node_uid,json=nodeUid,proto3,oneof" json:"node_uid,omitempty"` // alternatively -> Unique identifier for the node. + InstanceType string `protobuf:"bytes,1,opt,name=instance_type,json=instanceType,proto3" json:"instance_type,omitempty"` + Count int32 `protobuf:"varint,2,opt,name=count,proto3" json:"count,omitempty"` } -func (x *GetNodeGPUMigInstancesRequest) Reset() { - *x = GetNodeGPUMigInstancesRequest{} +func (x *InstanceTypeCount) Reset() { + *x = InstanceTypeCount{} if protoimpl.UnsafeEnabled { mi := &file_api_v1_k8s_proto_msgTypes[50] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -4025,13 +4439,13 @@ func (x *GetNodeGPUMigInstancesRequest) Reset() { } } -func (x *GetNodeGPUMigInstancesRequest) String() string { +func (x *InstanceTypeCount) String() string { return protoimpl.X.MessageStringOf(x) } -func (*GetNodeGPUMigInstancesRequest) ProtoMessage() {} +func (*InstanceTypeCount) ProtoMessage() {} -func (x *GetNodeGPUMigInstancesRequest) ProtoReflect() protoreflect.Message { +func (x *InstanceTypeCount) ProtoReflect() protoreflect.Message { mi := &file_api_v1_k8s_proto_msgTypes[50] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -4043,51 +4457,53 @@ func (x *GetNodeGPUMigInstancesRequest) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use GetNodeGPUMigInstancesRequest.ProtoReflect.Descriptor instead. -func (*GetNodeGPUMigInstancesRequest) Descriptor() ([]byte, []int) { +// Deprecated: Use InstanceTypeCount.ProtoReflect.Descriptor instead. +func (*InstanceTypeCount) Descriptor() ([]byte, []int) { return file_api_v1_k8s_proto_rawDescGZIP(), []int{50} } -func (x *GetNodeGPUMigInstancesRequest) GetTeamId() string { +func (x *InstanceTypeCount) GetInstanceType() string { if x != nil { - return x.TeamId + return x.InstanceType } return "" } -func (x *GetNodeGPUMigInstancesRequest) GetClusterId() string { +func (x *InstanceTypeCount) GetCount() int32 { if x != nil { - return x.ClusterId - } - return "" -} - -func (x *GetNodeGPUMigInstancesRequest) GetNodeId() string { - if x != nil && x.NodeId != nil { - return *x.NodeId - } - return "" -} - -func (x *GetNodeGPUMigInstancesRequest) GetNodeUid() string { - if x != nil && x.NodeUid != nil { - return *x.NodeUid + return x.Count } - return "" + return 0 } -// GetNodeGPUMigInstancesResponse returns the node's most recent MIG-instance telemetry. -type GetNodeGPUMigInstancesResponse struct { +type NodeInventoryRow struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Instances []*GPUMigInstance `protobuf:"bytes,1,rep,name=instances,proto3" json:"instances,omitempty"` - CollectedAt *timestamppb.Timestamp `protobuf:"bytes,2,opt,name=collected_at,json=collectedAt,proto3,oneof" json:"collected_at,omitempty"` -} - -func (x *GetNodeGPUMigInstancesResponse) Reset() { - *x = GetNodeGPUMigInstancesResponse{} + NodeId string `protobuf:"bytes,1,opt,name=node_id,json=nodeId,proto3" json:"node_id,omitempty"` + Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` + NodeGroupName string `protobuf:"bytes,3,opt,name=node_group_name,json=nodeGroupName,proto3" json:"node_group_name,omitempty"` + InstanceType string `protobuf:"bytes,4,opt,name=instance_type,json=instanceType,proto3" json:"instance_type,omitempty"` + CapacityType string `protobuf:"bytes,5,opt,name=capacity_type,json=capacityType,proto3" json:"capacity_type,omitempty"` // on_demand | spot | reserved | unknown + Zone string `protobuf:"bytes,6,opt,name=zone,proto3" json:"zone,omitempty"` + State NodeState `protobuf:"varint,7,opt,name=state,proto3,enum=api.v1.NodeState" json:"state,omitempty"` + CreatedAt int64 `protobuf:"varint,8,opt,name=created_at,json=createdAt,proto3" json:"created_at,omitempty"` // Unix seconds. + MoneyCostPerHour float64 `protobuf:"fixed64,9,opt,name=money_cost_per_hour,json=moneyCostPerHour,proto3" json:"money_cost_per_hour,omitempty"` + CpuCapacityMillicores float64 `protobuf:"fixed64,10,opt,name=cpu_capacity_millicores,json=cpuCapacityMillicores,proto3" json:"cpu_capacity_millicores,omitempty"` + MemoryCapacityBytes float64 `protobuf:"fixed64,11,opt,name=memory_capacity_bytes,json=memoryCapacityBytes,proto3" json:"memory_capacity_bytes,omitempty"` + GpuCapacity float64 `protobuf:"fixed64,12,opt,name=gpu_capacity,json=gpuCapacity,proto3" json:"gpu_capacity,omitempty"` + CpuRequestedMillicores float64 `protobuf:"fixed64,13,opt,name=cpu_requested_millicores,json=cpuRequestedMillicores,proto3" json:"cpu_requested_millicores,omitempty"` + MemoryRequestedBytes float64 `protobuf:"fixed64,14,opt,name=memory_requested_bytes,json=memoryRequestedBytes,proto3" json:"memory_requested_bytes,omitempty"` + GpuRequested float64 `protobuf:"fixed64,15,opt,name=gpu_requested,json=gpuRequested,proto3" json:"gpu_requested,omitempty"` + // Live pods on the node that have not reached Succeeded or Failed, DaemonSet + // pods included: occupancy, unlike the requested figures above, which leave + // DaemonSet pods out. + PodCount int32 `protobuf:"varint,16,opt,name=pod_count,json=podCount,proto3" json:"pod_count,omitempty"` +} + +func (x *NodeInventoryRow) Reset() { + *x = NodeInventoryRow{} if protoimpl.UnsafeEnabled { mi := &file_api_v1_k8s_proto_msgTypes[51] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -4095,13 +4511,13 @@ func (x *GetNodeGPUMigInstancesResponse) Reset() { } } -func (x *GetNodeGPUMigInstancesResponse) String() string { +func (x *NodeInventoryRow) String() string { return protoimpl.X.MessageStringOf(x) } -func (*GetNodeGPUMigInstancesResponse) ProtoMessage() {} +func (*NodeInventoryRow) ProtoMessage() {} -func (x *GetNodeGPUMigInstancesResponse) ProtoReflect() protoreflect.Message { +func (x *NodeInventoryRow) ProtoReflect() protoreflect.Message { mi := &file_api_v1_k8s_proto_msgTypes[51] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -4113,165 +4529,165 @@ func (x *GetNodeGPUMigInstancesResponse) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use GetNodeGPUMigInstancesResponse.ProtoReflect.Descriptor instead. -func (*GetNodeGPUMigInstancesResponse) Descriptor() ([]byte, []int) { +// Deprecated: Use NodeInventoryRow.ProtoReflect.Descriptor instead. +func (*NodeInventoryRow) Descriptor() ([]byte, []int) { return file_api_v1_k8s_proto_rawDescGZIP(), []int{51} } -func (x *GetNodeGPUMigInstancesResponse) GetInstances() []*GPUMigInstance { +func (x *NodeInventoryRow) GetNodeId() string { if x != nil { - return x.Instances + return x.NodeId } - return nil + return "" } -func (x *GetNodeGPUMigInstancesResponse) GetCollectedAt() *timestamppb.Timestamp { +func (x *NodeInventoryRow) GetName() string { if x != nil { - return x.CollectedAt + return x.Name } - return nil + return "" } -// GetContainerIndividualGPUMetricsRequest requests the most recent per-GPU breakdown per container. -type GetContainerIndividualGPUMetricsRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - TeamId string `protobuf:"bytes,1,opt,name=team_id,json=teamId,proto3" json:"team_id,omitempty"` - ClusterId string `protobuf:"bytes,2,opt,name=cluster_id,json=clusterId,proto3" json:"cluster_id,omitempty"` - Uid string `protobuf:"bytes,3,opt,name=uid,proto3" json:"uid,omitempty"` +func (x *NodeInventoryRow) GetNodeGroupName() string { + if x != nil { + return x.NodeGroupName + } + return "" } -func (x *GetContainerIndividualGPUMetricsRequest) Reset() { - *x = GetContainerIndividualGPUMetricsRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_api_v1_k8s_proto_msgTypes[52] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) +func (x *NodeInventoryRow) GetInstanceType() string { + if x != nil { + return x.InstanceType } + return "" } -func (x *GetContainerIndividualGPUMetricsRequest) String() string { - return protoimpl.X.MessageStringOf(x) +func (x *NodeInventoryRow) GetCapacityType() string { + if x != nil { + return x.CapacityType + } + return "" } -func (*GetContainerIndividualGPUMetricsRequest) ProtoMessage() {} - -func (x *GetContainerIndividualGPUMetricsRequest) ProtoReflect() protoreflect.Message { - mi := &file_api_v1_k8s_proto_msgTypes[52] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms +func (x *NodeInventoryRow) GetZone() string { + if x != nil { + return x.Zone } - return mi.MessageOf(x) + return "" } -// Deprecated: Use GetContainerIndividualGPUMetricsRequest.ProtoReflect.Descriptor instead. -func (*GetContainerIndividualGPUMetricsRequest) Descriptor() ([]byte, []int) { - return file_api_v1_k8s_proto_rawDescGZIP(), []int{52} +func (x *NodeInventoryRow) GetState() NodeState { + if x != nil { + return x.State + } + return NodeState_NODE_STATE_UNSPECIFIED } -func (x *GetContainerIndividualGPUMetricsRequest) GetTeamId() string { +func (x *NodeInventoryRow) GetCreatedAt() int64 { if x != nil { - return x.TeamId + return x.CreatedAt } - return "" + return 0 } -func (x *GetContainerIndividualGPUMetricsRequest) GetClusterId() string { +func (x *NodeInventoryRow) GetMoneyCostPerHour() float64 { if x != nil { - return x.ClusterId + return x.MoneyCostPerHour } - return "" + return 0 } -func (x *GetContainerIndividualGPUMetricsRequest) GetUid() string { +func (x *NodeInventoryRow) GetCpuCapacityMillicores() float64 { if x != nil { - return x.Uid + return x.CpuCapacityMillicores } - return "" + return 0 } -// GetContainerIndividualGPUMetricsResponse returns per-container per-GPU breakdowns. -type GetContainerIndividualGPUMetricsResponse struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Containers []*ContainerIndividualGPUMetrics `protobuf:"bytes,1,rep,name=containers,proto3" json:"containers,omitempty"` +func (x *NodeInventoryRow) GetMemoryCapacityBytes() float64 { + if x != nil { + return x.MemoryCapacityBytes + } + return 0 } -func (x *GetContainerIndividualGPUMetricsResponse) Reset() { - *x = GetContainerIndividualGPUMetricsResponse{} - if protoimpl.UnsafeEnabled { - mi := &file_api_v1_k8s_proto_msgTypes[53] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) +func (x *NodeInventoryRow) GetGpuCapacity() float64 { + if x != nil { + return x.GpuCapacity } + return 0 } -func (x *GetContainerIndividualGPUMetricsResponse) String() string { - return protoimpl.X.MessageStringOf(x) +func (x *NodeInventoryRow) GetCpuRequestedMillicores() float64 { + if x != nil { + return x.CpuRequestedMillicores + } + return 0 } -func (*GetContainerIndividualGPUMetricsResponse) ProtoMessage() {} - -func (x *GetContainerIndividualGPUMetricsResponse) ProtoReflect() protoreflect.Message { - mi := &file_api_v1_k8s_proto_msgTypes[53] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms +func (x *NodeInventoryRow) GetMemoryRequestedBytes() float64 { + if x != nil { + return x.MemoryRequestedBytes } - return mi.MessageOf(x) + return 0 } -// Deprecated: Use GetContainerIndividualGPUMetricsResponse.ProtoReflect.Descriptor instead. -func (*GetContainerIndividualGPUMetricsResponse) Descriptor() ([]byte, []int) { - return file_api_v1_k8s_proto_rawDescGZIP(), []int{53} +func (x *NodeInventoryRow) GetGpuRequested() float64 { + if x != nil { + return x.GpuRequested + } + return 0 } -func (x *GetContainerIndividualGPUMetricsResponse) GetContainers() []*ContainerIndividualGPUMetrics { +func (x *NodeInventoryRow) GetPodCount() int32 { if x != nil { - return x.Containers + return x.PodCount } - return nil + return 0 } -// LanguageVersions holds every distinct version detected for one language -// across a cluster's workloads. -type LanguageVersions struct { +type NodeGroupMetric struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Language string `protobuf:"bytes,1,opt,name=language,proto3" json:"language,omitempty"` - Versions []string `protobuf:"bytes,2,rep,name=versions,proto3" json:"versions,omitempty"` + NodeCount int32 `protobuf:"varint,1,opt,name=node_count,json=nodeCount,proto3" json:"node_count,omitempty"` + MaxConcurrentNodeCount int32 `protobuf:"varint,2,opt,name=max_concurrent_node_count,json=maxConcurrentNodeCount,proto3" json:"max_concurrent_node_count,omitempty"` + CapacityCpu int64 `protobuf:"varint,11,opt,name=capacity_cpu,json=capacityCpu,proto3" json:"capacity_cpu,omitempty"` // millicores + CapacityMemory int64 `protobuf:"varint,12,opt,name=capacity_memory,json=capacityMemory,proto3" json:"capacity_memory,omitempty"` // bytes + CapacityGpuVram int64 `protobuf:"varint,13,opt,name=capacity_gpu_vram,json=capacityGpuVram,proto3" json:"capacity_gpu_vram,omitempty"` // bytes + CapacityGpuDevices int64 `protobuf:"varint,14,opt,name=capacity_gpu_devices,json=capacityGpuDevices,proto3" json:"capacity_gpu_devices,omitempty"` // number + AggrCpuReq int64 `protobuf:"varint,21,opt,name=aggr_cpu_req,json=aggrCpuReq,proto3" json:"aggr_cpu_req,omitempty"` // millicores + AggrMemoryReq int64 `protobuf:"varint,22,opt,name=aggr_memory_req,json=aggrMemoryReq,proto3" json:"aggr_memory_req,omitempty"` // bytes + AggrGpuReq int64 `protobuf:"varint,23,opt,name=aggr_gpu_req,json=aggrGpuReq,proto3" json:"aggr_gpu_req,omitempty"` // number + UtilCpuAvgUsage float32 `protobuf:"fixed32,31,opt,name=util_cpu_avg_usage,json=utilCpuAvgUsage,proto3" json:"util_cpu_avg_usage,omitempty"` // millicores + UtilMemoryAvgUsage float32 `protobuf:"fixed32,32,opt,name=util_memory_avg_usage,json=utilMemoryAvgUsage,proto3" json:"util_memory_avg_usage,omitempty"` // bytes + UtilGpuVramAvgUsage float32 `protobuf:"fixed32,33,opt,name=util_gpu_vram_avg_usage,json=utilGpuVramAvgUsage,proto3" json:"util_gpu_vram_avg_usage,omitempty"` // bytes + UtilGpuDevice float32 `protobuf:"fixed32,34,opt,name=util_gpu_device,json=utilGpuDevice,proto3" json:"util_gpu_device,omitempty"` // pct + HourlyCost *money.Money `protobuf:"bytes,50,opt,name=hourly_cost,json=hourlyCost,proto3" json:"hourly_cost,omitempty"` // average hourly cost within the time bucket + HourlyCpuCost *money.Money `protobuf:"bytes,51,opt,name=hourly_cpu_cost,json=hourlyCpuCost,proto3" json:"hourly_cpu_cost,omitempty"` // cost for cpu + HourlyMemoryCost *money.Money `protobuf:"bytes,52,opt,name=hourly_memory_cost,json=hourlyMemoryCost,proto3" json:"hourly_memory_cost,omitempty"` // cost for memory + HourlyGpuCost *money.Money `protobuf:"bytes,53,opt,name=hourly_gpu_cost,json=hourlyGpuCost,proto3" json:"hourly_gpu_cost,omitempty"` // cost for gpu + BucketTime *timestamppb.Timestamp `protobuf:"bytes,41,opt,name=bucket_time,json=bucketTime,proto3" json:"bucket_time,omitempty"` } -func (x *LanguageVersions) Reset() { - *x = LanguageVersions{} +func (x *NodeGroupMetric) Reset() { + *x = NodeGroupMetric{} if protoimpl.UnsafeEnabled { - mi := &file_api_v1_k8s_proto_msgTypes[54] + mi := &file_api_v1_k8s_proto_msgTypes[52] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } -func (x *LanguageVersions) String() string { +func (x *NodeGroupMetric) String() string { return protoimpl.X.MessageStringOf(x) } -func (*LanguageVersions) ProtoMessage() {} +func (*NodeGroupMetric) ProtoMessage() {} -func (x *LanguageVersions) ProtoReflect() protoreflect.Message { - mi := &file_api_v1_k8s_proto_msgTypes[54] +func (x *NodeGroupMetric) ProtoReflect() protoreflect.Message { + mi := &file_api_v1_k8s_proto_msgTypes[52] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4282,161 +4698,163 @@ func (x *LanguageVersions) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use LanguageVersions.ProtoReflect.Descriptor instead. -func (*LanguageVersions) Descriptor() ([]byte, []int) { - return file_api_v1_k8s_proto_rawDescGZIP(), []int{54} +// Deprecated: Use NodeGroupMetric.ProtoReflect.Descriptor instead. +func (*NodeGroupMetric) Descriptor() ([]byte, []int) { + return file_api_v1_k8s_proto_rawDescGZIP(), []int{52} } -func (x *LanguageVersions) GetLanguage() string { +func (x *NodeGroupMetric) GetNodeCount() int32 { if x != nil { - return x.Language + return x.NodeCount } - return "" + return 0 } -func (x *LanguageVersions) GetVersions() []string { +func (x *NodeGroupMetric) GetMaxConcurrentNodeCount() int32 { if x != nil { - return x.Versions + return x.MaxConcurrentNodeCount } - return nil + return 0 } -// GetWorkloadLanguagesRequest requests the distinct languages/versions -// detected across a cluster's workloads (used to populate filter dropdowns). -type GetWorkloadLanguagesRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - TeamId string `protobuf:"bytes,1,opt,name=team_id,json=teamId,proto3" json:"team_id,omitempty"` - ClusterId string `protobuf:"bytes,2,opt,name=cluster_id,json=clusterId,proto3" json:"cluster_id,omitempty"` +func (x *NodeGroupMetric) GetCapacityCpu() int64 { + if x != nil { + return x.CapacityCpu + } + return 0 } -func (x *GetWorkloadLanguagesRequest) Reset() { - *x = GetWorkloadLanguagesRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_api_v1_k8s_proto_msgTypes[55] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) +func (x *NodeGroupMetric) GetCapacityMemory() int64 { + if x != nil { + return x.CapacityMemory } + return 0 } -func (x *GetWorkloadLanguagesRequest) String() string { - return protoimpl.X.MessageStringOf(x) +func (x *NodeGroupMetric) GetCapacityGpuVram() int64 { + if x != nil { + return x.CapacityGpuVram + } + return 0 } -func (*GetWorkloadLanguagesRequest) ProtoMessage() {} - -func (x *GetWorkloadLanguagesRequest) ProtoReflect() protoreflect.Message { - mi := &file_api_v1_k8s_proto_msgTypes[55] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms +func (x *NodeGroupMetric) GetCapacityGpuDevices() int64 { + if x != nil { + return x.CapacityGpuDevices } - return mi.MessageOf(x) + return 0 } -// Deprecated: Use GetWorkloadLanguagesRequest.ProtoReflect.Descriptor instead. -func (*GetWorkloadLanguagesRequest) Descriptor() ([]byte, []int) { - return file_api_v1_k8s_proto_rawDescGZIP(), []int{55} +func (x *NodeGroupMetric) GetAggrCpuReq() int64 { + if x != nil { + return x.AggrCpuReq + } + return 0 } -func (x *GetWorkloadLanguagesRequest) GetTeamId() string { +func (x *NodeGroupMetric) GetAggrMemoryReq() int64 { if x != nil { - return x.TeamId + return x.AggrMemoryReq } - return "" + return 0 } -func (x *GetWorkloadLanguagesRequest) GetClusterId() string { +func (x *NodeGroupMetric) GetAggrGpuReq() int64 { if x != nil { - return x.ClusterId + return x.AggrGpuReq } - return "" + return 0 } -// GetWorkloadLanguagesResponse returns every distinct language and its -// detected versions for the requested cluster. -type GetWorkloadLanguagesResponse struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields +func (x *NodeGroupMetric) GetUtilCpuAvgUsage() float32 { + if x != nil { + return x.UtilCpuAvgUsage + } + return 0 +} - Languages []*LanguageVersions `protobuf:"bytes,1,rep,name=languages,proto3" json:"languages,omitempty"` +func (x *NodeGroupMetric) GetUtilMemoryAvgUsage() float32 { + if x != nil { + return x.UtilMemoryAvgUsage + } + return 0 } -func (x *GetWorkloadLanguagesResponse) Reset() { - *x = GetWorkloadLanguagesResponse{} - if protoimpl.UnsafeEnabled { - mi := &file_api_v1_k8s_proto_msgTypes[56] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) +func (x *NodeGroupMetric) GetUtilGpuVramAvgUsage() float32 { + if x != nil { + return x.UtilGpuVramAvgUsage } + return 0 } -func (x *GetWorkloadLanguagesResponse) String() string { - return protoimpl.X.MessageStringOf(x) +func (x *NodeGroupMetric) GetUtilGpuDevice() float32 { + if x != nil { + return x.UtilGpuDevice + } + return 0 } -func (*GetWorkloadLanguagesResponse) ProtoMessage() {} +func (x *NodeGroupMetric) GetHourlyCost() *money.Money { + if x != nil { + return x.HourlyCost + } + return nil +} -func (x *GetWorkloadLanguagesResponse) ProtoReflect() protoreflect.Message { - mi := &file_api_v1_k8s_proto_msgTypes[56] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms +func (x *NodeGroupMetric) GetHourlyCpuCost() *money.Money { + if x != nil { + return x.HourlyCpuCost } - return mi.MessageOf(x) + return nil } -// Deprecated: Use GetWorkloadLanguagesResponse.ProtoReflect.Descriptor instead. -func (*GetWorkloadLanguagesResponse) Descriptor() ([]byte, []int) { - return file_api_v1_k8s_proto_rawDescGZIP(), []int{56} +func (x *NodeGroupMetric) GetHourlyMemoryCost() *money.Money { + if x != nil { + return x.HourlyMemoryCost + } + return nil } -func (x *GetWorkloadLanguagesResponse) GetLanguages() []*LanguageVersions { +func (x *NodeGroupMetric) GetHourlyGpuCost() *money.Money { if x != nil { - return x.Languages + return x.HourlyGpuCost } return nil } -// Request to get forecasted workloads -type GetForecastWorkloadsRequest struct { +func (x *NodeGroupMetric) GetBucketTime() *timestamppb.Timestamp { + if x != nil { + return x.BucketTime + } + return nil +} + +// GetNodeGroupResponse contains node group for a specific cluster. +type GetNodeGroupResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - TeamId string `protobuf:"bytes,1,opt,name=team_id,json=teamId,proto3" json:"team_id,omitempty"` - ClusterId string `protobuf:"bytes,2,opt,name=cluster_id,json=clusterId,proto3" json:"cluster_id,omitempty"` - ShowDeleted bool `protobuf:"varint,12,opt,name=show_deleted,json=showDeleted,proto3" json:"show_deleted,omitempty"` - StartTime *timestamppb.Timestamp `protobuf:"bytes,21,opt,name=start_time,json=startTime,proto3,oneof" json:"start_time,omitempty"` - EndTime *timestamppb.Timestamp `protobuf:"bytes,22,opt,name=end_time,json=endTime,proto3,oneof" json:"end_time,omitempty"` + NodeGroup *NodeGroup `protobuf:"bytes,1,opt,name=node_group,json=nodeGroup,proto3" json:"node_group,omitempty"` // Node Group } -func (x *GetForecastWorkloadsRequest) Reset() { - *x = GetForecastWorkloadsRequest{} +func (x *GetNodeGroupResponse) Reset() { + *x = GetNodeGroupResponse{} if protoimpl.UnsafeEnabled { - mi := &file_api_v1_k8s_proto_msgTypes[57] + mi := &file_api_v1_k8s_proto_msgTypes[53] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } -func (x *GetForecastWorkloadsRequest) String() string { +func (x *GetNodeGroupResponse) String() string { return protoimpl.X.MessageStringOf(x) } -func (*GetForecastWorkloadsRequest) ProtoMessage() {} +func (*GetNodeGroupResponse) ProtoMessage() {} -func (x *GetForecastWorkloadsRequest) ProtoReflect() protoreflect.Message { - mi := &file_api_v1_k8s_proto_msgTypes[57] +func (x *GetNodeGroupResponse) ProtoReflect() protoreflect.Message { + mi := &file_api_v1_k8s_proto_msgTypes[53] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4447,75 +4865,97 @@ func (x *GetForecastWorkloadsRequest) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use GetForecastWorkloadsRequest.ProtoReflect.Descriptor instead. -func (*GetForecastWorkloadsRequest) Descriptor() ([]byte, []int) { - return file_api_v1_k8s_proto_rawDescGZIP(), []int{57} +// Deprecated: Use GetNodeGroupResponse.ProtoReflect.Descriptor instead. +func (*GetNodeGroupResponse) Descriptor() ([]byte, []int) { + return file_api_v1_k8s_proto_rawDescGZIP(), []int{53} } -func (x *GetForecastWorkloadsRequest) GetTeamId() string { +func (x *GetNodeGroupResponse) GetNodeGroup() *NodeGroup { if x != nil { - return x.TeamId + return x.NodeGroup } - return "" + return nil } -func (x *GetForecastWorkloadsRequest) GetClusterId() string { - if x != nil { - return x.ClusterId - } - return "" +// GetClusterResponse contains details of a specific cluster for a team. +type GetClusterResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Cluster *Cluster `protobuf:"bytes,1,opt,name=cluster,proto3" json:"cluster,omitempty"` // Details of the cluster. } -func (x *GetForecastWorkloadsRequest) GetShowDeleted() bool { - if x != nil { - return x.ShowDeleted +func (x *GetClusterResponse) Reset() { + *x = GetClusterResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_api_v1_k8s_proto_msgTypes[54] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } - return false } -func (x *GetForecastWorkloadsRequest) GetStartTime() *timestamppb.Timestamp { - if x != nil { - return x.StartTime +func (x *GetClusterResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetClusterResponse) ProtoMessage() {} + +func (x *GetClusterResponse) ProtoReflect() protoreflect.Message { + mi := &file_api_v1_k8s_proto_msgTypes[54] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms } - return nil + return mi.MessageOf(x) } -func (x *GetForecastWorkloadsRequest) GetEndTime() *timestamppb.Timestamp { +// Deprecated: Use GetClusterResponse.ProtoReflect.Descriptor instead. +func (*GetClusterResponse) Descriptor() ([]byte, []int) { + return file_api_v1_k8s_proto_rawDescGZIP(), []int{54} +} + +func (x *GetClusterResponse) GetCluster() *Cluster { if x != nil { - return x.EndTime + return x.Cluster } return nil } -// Response to forecast workloads request -type GetForecastWorkloadsResponse struct { +// GetClustersResponse contains the list of clusters and aggregated metrics for a team. +type GetClustersResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - WorkloadItems []*WorkloadItem `protobuf:"bytes,1,rep,name=workload_items,json=workloadItems,proto3" json:"workload_items,omitempty"` // List of workload hierarchy items. - CostInfo *CostInfo `protobuf:"bytes,2,opt,name=cost_info,json=costInfo,proto3" json:"cost_info,omitempty"` // Aggregated cost information for workloads. - ResourceMetrics []*ForecastResourceMetrics `protobuf:"bytes,3,rep,name=resource_metrics,json=resourceMetrics,proto3" json:"resource_metrics,omitempty"` // List of forecasted resource metrics for the workload. - ResourceSummary *ResourceSummary `protobuf:"bytes,4,opt,name=resource_summary,json=resourceSummary,proto3" json:"resource_summary,omitempty"` // Summary of resource counts and pod statuses. + Clusters []*Cluster `protobuf:"bytes,1,rep,name=clusters,proto3" json:"clusters,omitempty"` // List of clusters. + ResourceMetrics *ResourceMetrics `protobuf:"bytes,2,opt,name=resource_metrics,json=resourceMetrics,proto3" json:"resource_metrics,omitempty"` // Aggregated resource metrics across clusters. + CostInfo *CostInfo `protobuf:"bytes,3,opt,name=cost_info,json=costInfo,proto3" json:"cost_info,omitempty"` // Aggregated cost information across clusters. + NodeInfo *NodeInfo `protobuf:"bytes,4,opt,name=node_info,json=nodeInfo,proto3" json:"node_info,omitempty"` // Aggregated node information across clusters. + CostDataPoints []*CostDataPoint `protobuf:"bytes,5,rep,name=cost_data_points,json=costDataPoints,proto3" json:"cost_data_points,omitempty"` // Time-series cost data points. + ResourceDataPoints []*ResourceDataPoint `protobuf:"bytes,6,rep,name=resource_data_points,json=resourceDataPoints,proto3" json:"resource_data_points,omitempty"` // Time-series resource utilization data points. } -func (x *GetForecastWorkloadsResponse) Reset() { - *x = GetForecastWorkloadsResponse{} +func (x *GetClustersResponse) Reset() { + *x = GetClustersResponse{} if protoimpl.UnsafeEnabled { - mi := &file_api_v1_k8s_proto_msgTypes[58] + mi := &file_api_v1_k8s_proto_msgTypes[55] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } -func (x *GetForecastWorkloadsResponse) String() string { +func (x *GetClustersResponse) String() string { return protoimpl.X.MessageStringOf(x) } -func (*GetForecastWorkloadsResponse) ProtoMessage() {} +func (*GetClustersResponse) ProtoMessage() {} -func (x *GetForecastWorkloadsResponse) ProtoReflect() protoreflect.Message { - mi := &file_api_v1_k8s_proto_msgTypes[58] +func (x *GetClustersResponse) ProtoReflect() protoreflect.Message { + mi := &file_api_v1_k8s_proto_msgTypes[55] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4526,71 +4966,83 @@ func (x *GetForecastWorkloadsResponse) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use GetForecastWorkloadsResponse.ProtoReflect.Descriptor instead. -func (*GetForecastWorkloadsResponse) Descriptor() ([]byte, []int) { - return file_api_v1_k8s_proto_rawDescGZIP(), []int{58} +// Deprecated: Use GetClustersResponse.ProtoReflect.Descriptor instead. +func (*GetClustersResponse) Descriptor() ([]byte, []int) { + return file_api_v1_k8s_proto_rawDescGZIP(), []int{55} } -func (x *GetForecastWorkloadsResponse) GetWorkloadItems() []*WorkloadItem { +func (x *GetClustersResponse) GetClusters() []*Cluster { if x != nil { - return x.WorkloadItems + return x.Clusters } return nil } -func (x *GetForecastWorkloadsResponse) GetCostInfo() *CostInfo { +func (x *GetClustersResponse) GetResourceMetrics() *ResourceMetrics { + if x != nil { + return x.ResourceMetrics + } + return nil +} + +func (x *GetClustersResponse) GetCostInfo() *CostInfo { if x != nil { return x.CostInfo } return nil } -func (x *GetForecastWorkloadsResponse) GetResourceMetrics() []*ForecastResourceMetrics { +func (x *GetClustersResponse) GetNodeInfo() *NodeInfo { if x != nil { - return x.ResourceMetrics + return x.NodeInfo } return nil } -func (x *GetForecastWorkloadsResponse) GetResourceSummary() *ResourceSummary { +func (x *GetClustersResponse) GetCostDataPoints() []*CostDataPoint { if x != nil { - return x.ResourceSummary + return x.CostDataPoints } return nil } -// Request to get forecast for a specific workload -type GetForecastWorkloadRequest struct { +func (x *GetClustersResponse) GetResourceDataPoints() []*ResourceDataPoint { + if x != nil { + return x.ResourceDataPoints + } + return nil +} + +// ListClustersResponse contains the paginated list of clusters for a team. +// Deprecated: Use GetClustersResponse instead. +// +// Deprecated: Marked as deprecated in api/v1/k8s.proto. +type ListClustersResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - TeamId string `protobuf:"bytes,1,opt,name=team_id,json=teamId,proto3" json:"team_id,omitempty"` // Unique identifier for the team. - ClusterId string `protobuf:"bytes,2,opt,name=cluster_id,json=clusterId,proto3" json:"cluster_id,omitempty"` // Unique identifier for the cluster. - Kind string `protobuf:"bytes,3,opt,name=kind,proto3" json:"kind,omitempty"` // Kind of the workload (e.g., Deployment, StatefulSet). - Uid string `protobuf:"bytes,4,opt,name=uid,proto3" json:"uid,omitempty"` // Unique identifier for the workload. - IncludeDeleted bool `protobuf:"varint,11,opt,name=include_deleted,json=includeDeleted,proto3" json:"include_deleted,omitempty"` - StartTime *timestamppb.Timestamp `protobuf:"bytes,21,opt,name=start_time,json=startTime,proto3,oneof" json:"start_time,omitempty"` - EndTime *timestamppb.Timestamp `protobuf:"bytes,22,opt,name=end_time,json=endTime,proto3,oneof" json:"end_time,omitempty"` + Clusters []*Cluster `protobuf:"bytes,1,rep,name=clusters,proto3" json:"clusters,omitempty"` // List of clusters. + Pagination *Pagination `protobuf:"bytes,2,opt,name=pagination,proto3" json:"pagination,omitempty"` // Pagination information. } -func (x *GetForecastWorkloadRequest) Reset() { - *x = GetForecastWorkloadRequest{} +func (x *ListClustersResponse) Reset() { + *x = ListClustersResponse{} if protoimpl.UnsafeEnabled { - mi := &file_api_v1_k8s_proto_msgTypes[59] + mi := &file_api_v1_k8s_proto_msgTypes[56] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } -func (x *GetForecastWorkloadRequest) String() string { +func (x *ListClustersResponse) String() string { return protoimpl.X.MessageStringOf(x) } -func (*GetForecastWorkloadRequest) ProtoMessage() {} +func (*ListClustersResponse) ProtoMessage() {} -func (x *GetForecastWorkloadRequest) ProtoReflect() protoreflect.Message { - mi := &file_api_v1_k8s_proto_msgTypes[59] +func (x *ListClustersResponse) ProtoReflect() protoreflect.Message { + mi := &file_api_v1_k8s_proto_msgTypes[56] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4601,91 +5053,58 @@ func (x *GetForecastWorkloadRequest) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use GetForecastWorkloadRequest.ProtoReflect.Descriptor instead. -func (*GetForecastWorkloadRequest) Descriptor() ([]byte, []int) { - return file_api_v1_k8s_proto_rawDescGZIP(), []int{59} +// Deprecated: Use ListClustersResponse.ProtoReflect.Descriptor instead. +func (*ListClustersResponse) Descriptor() ([]byte, []int) { + return file_api_v1_k8s_proto_rawDescGZIP(), []int{56} } -func (x *GetForecastWorkloadRequest) GetTeamId() string { +func (x *ListClustersResponse) GetClusters() []*Cluster { if x != nil { - return x.TeamId + return x.Clusters } - return "" + return nil } -func (x *GetForecastWorkloadRequest) GetClusterId() string { +func (x *ListClustersResponse) GetPagination() *Pagination { if x != nil { - return x.ClusterId + return x.Pagination } - return "" + return nil } -func (x *GetForecastWorkloadRequest) GetKind() string { - if x != nil { - return x.Kind - } - return "" -} +// GetNodeResponse contains details and metrics for a specific node in a cluster. +type GetNodeResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields -func (x *GetForecastWorkloadRequest) GetUid() string { - if x != nil { - return x.Uid - } - return "" -} - -func (x *GetForecastWorkloadRequest) GetIncludeDeleted() bool { - if x != nil { - return x.IncludeDeleted - } - return false -} - -func (x *GetForecastWorkloadRequest) GetStartTime() *timestamppb.Timestamp { - if x != nil { - return x.StartTime - } - return nil -} - -func (x *GetForecastWorkloadRequest) GetEndTime() *timestamppb.Timestamp { - if x != nil { - return x.EndTime - } - return nil -} - -// Response containing forecasted metrics for a specific workload -type GetForecastWorkloadResponse struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - WorkloadItem *WorkloadItem `protobuf:"bytes,1,opt,name=workload_item,json=workloadItem,proto3" json:"workload_item,omitempty"` // Details of the workload hierarchy. - CostInfo *CostInfo `protobuf:"bytes,2,opt,name=cost_info,json=costInfo,proto3" json:"cost_info,omitempty"` // Aggregated cost information for the workload. - ResourceMetrics []*ForecastResourceMetrics `protobuf:"bytes,3,rep,name=resource_metrics,json=resourceMetrics,proto3" json:"resource_metrics,omitempty"` // List of forecasted resource metrics for the workload. - ResourceSummary *ResourceSummary `protobuf:"bytes,4,opt,name=resource_summary,json=resourceSummary,proto3" json:"resource_summary,omitempty"` // Summary of resource counts and pod statuses for the workload. - CostDataPoints []*CostDataPoint `protobuf:"bytes,5,rep,name=cost_data_points,json=costDataPoints,proto3" json:"cost_data_points,omitempty"` // Time-series cost data points for the workload. - ResourceDataPoints []*ResourceDataPoint `protobuf:"bytes,6,rep,name=resource_data_points,json=resourceDataPoints,proto3" json:"resource_data_points,omitempty"` // Time-series resource metrics for the workload. + Node *Node `protobuf:"bytes,1,opt,name=node,proto3" json:"node,omitempty"` // Details of the node. + ResourceMetrics *ResourceMetrics `protobuf:"bytes,2,opt,name=resource_metrics,json=resourceMetrics,proto3" json:"resource_metrics,omitempty"` // Resource metrics for the node. + CostInfo *CostInfo `protobuf:"bytes,3,opt,name=cost_info,json=costInfo,proto3" json:"cost_info,omitempty"` // Cost details for the node. + CostDataPoints []*CostDataPoint `protobuf:"bytes,4,rep,name=cost_data_points,json=costDataPoints,proto3" json:"cost_data_points,omitempty"` // Time-series cost data points for the node. + ResourceDataPoints []*ResourceDataPoint `protobuf:"bytes,5,rep,name=resource_data_points,json=resourceDataPoints,proto3" json:"resource_data_points,omitempty"` // Time-series resource metrics for the node. + // Set only under with_details, and only when the Node ownerReference names a + // NodeClaim. Unset for every other node. + NodeClaim *NodeClaimRef `protobuf:"bytes,6,opt,name=node_claim,json=nodeClaim,proto3" json:"node_claim,omitempty"` } -func (x *GetForecastWorkloadResponse) Reset() { - *x = GetForecastWorkloadResponse{} +func (x *GetNodeResponse) Reset() { + *x = GetNodeResponse{} if protoimpl.UnsafeEnabled { - mi := &file_api_v1_k8s_proto_msgTypes[60] + mi := &file_api_v1_k8s_proto_msgTypes[57] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } -func (x *GetForecastWorkloadResponse) String() string { +func (x *GetNodeResponse) String() string { return protoimpl.X.MessageStringOf(x) } -func (*GetForecastWorkloadResponse) ProtoMessage() {} +func (*GetNodeResponse) ProtoMessage() {} -func (x *GetForecastWorkloadResponse) ProtoReflect() protoreflect.Message { - mi := &file_api_v1_k8s_proto_msgTypes[60] +func (x *GetNodeResponse) ProtoReflect() protoreflect.Message { + mi := &file_api_v1_k8s_proto_msgTypes[57] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4696,79 +5115,87 @@ func (x *GetForecastWorkloadResponse) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use GetForecastWorkloadResponse.ProtoReflect.Descriptor instead. -func (*GetForecastWorkloadResponse) Descriptor() ([]byte, []int) { - return file_api_v1_k8s_proto_rawDescGZIP(), []int{60} +// Deprecated: Use GetNodeResponse.ProtoReflect.Descriptor instead. +func (*GetNodeResponse) Descriptor() ([]byte, []int) { + return file_api_v1_k8s_proto_rawDescGZIP(), []int{57} } -func (x *GetForecastWorkloadResponse) GetWorkloadItem() *WorkloadItem { +func (x *GetNodeResponse) GetNode() *Node { if x != nil { - return x.WorkloadItem + return x.Node } return nil } -func (x *GetForecastWorkloadResponse) GetCostInfo() *CostInfo { +func (x *GetNodeResponse) GetResourceMetrics() *ResourceMetrics { if x != nil { - return x.CostInfo + return x.ResourceMetrics } return nil } -func (x *GetForecastWorkloadResponse) GetResourceMetrics() []*ForecastResourceMetrics { +func (x *GetNodeResponse) GetCostInfo() *CostInfo { if x != nil { - return x.ResourceMetrics + return x.CostInfo } return nil } -func (x *GetForecastWorkloadResponse) GetResourceSummary() *ResourceSummary { +func (x *GetNodeResponse) GetCostDataPoints() []*CostDataPoint { if x != nil { - return x.ResourceSummary + return x.CostDataPoints } return nil } -func (x *GetForecastWorkloadResponse) GetCostDataPoints() []*CostDataPoint { +func (x *GetNodeResponse) GetResourceDataPoints() []*ResourceDataPoint { if x != nil { - return x.CostDataPoints + return x.ResourceDataPoints } return nil } -func (x *GetForecastWorkloadResponse) GetResourceDataPoints() []*ResourceDataPoint { +func (x *GetNodeResponse) GetNodeClaim() *NodeClaimRef { if x != nil { - return x.ResourceDataPoints + return x.NodeClaim } return nil } -// GetClusterMetadataResponse -type GetClusterMetadataResponse struct { +// NodeClaimRef describes the Karpenter NodeClaim that owns a node and whether a +// delete may be requested for it right now. +type NodeClaimRef struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Clusters []*Cluster `protobuf:"bytes,1,rep,name=clusters,proto3" json:"clusters,omitempty"` // List of clusters. + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` // Node.ownerReferences[0].name + Uid string `protobuf:"bytes,2,opt,name=uid,proto3" json:"uid,omitempty"` // Node.ownerReferences[0].uid + Deletable bool `protobuf:"varint,3,opt,name=deletable,proto3" json:"deletable,omitempty"` // computed server-side; the only gate for the delete button + NotDeletableReason NodeClaimNotDeletableReason `protobuf:"varint,4,opt,name=not_deletable_reason,json=notDeletableReason,proto3,enum=api.v1.NodeClaimNotDeletableReason" json:"not_deletable_reason,omitempty"` + MinOperatorVersion string `protobuf:"bytes,5,opt,name=min_operator_version,json=minOperatorVersion,proto3" json:"min_operator_version,omitempty"` // always set; the dakr-op version this feature needs + // Newest request for this node if it is unresolved, or if it resolved within + // the last hour; otherwise unset. + LatestStatus *NodeClaimDeletionStatus `protobuf:"bytes,6,opt,name=latest_status,json=latestStatus,proto3" json:"latest_status,omitempty"` } -func (x *GetClusterMetadataResponse) Reset() { - *x = GetClusterMetadataResponse{} +func (x *NodeClaimRef) Reset() { + *x = NodeClaimRef{} if protoimpl.UnsafeEnabled { - mi := &file_api_v1_k8s_proto_msgTypes[61] + mi := &file_api_v1_k8s_proto_msgTypes[58] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } -func (x *GetClusterMetadataResponse) String() string { +func (x *NodeClaimRef) String() string { return protoimpl.X.MessageStringOf(x) } -func (*GetClusterMetadataResponse) ProtoMessage() {} +func (*NodeClaimRef) ProtoMessage() {} -func (x *GetClusterMetadataResponse) ProtoReflect() protoreflect.Message { - mi := &file_api_v1_k8s_proto_msgTypes[61] +func (x *NodeClaimRef) ProtoReflect() protoreflect.Message { + mi := &file_api_v1_k8s_proto_msgTypes[58] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4779,101 +5206,85 @@ func (x *GetClusterMetadataResponse) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use GetClusterMetadataResponse.ProtoReflect.Descriptor instead. -func (*GetClusterMetadataResponse) Descriptor() ([]byte, []int) { - return file_api_v1_k8s_proto_rawDescGZIP(), []int{61} +// Deprecated: Use NodeClaimRef.ProtoReflect.Descriptor instead. +func (*NodeClaimRef) Descriptor() ([]byte, []int) { + return file_api_v1_k8s_proto_rawDescGZIP(), []int{58} } -func (x *GetClusterMetadataResponse) GetClusters() []*Cluster { +func (x *NodeClaimRef) GetName() string { if x != nil { - return x.Clusters + return x.Name } - return nil -} - -type ClusterElement struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - ClusterId string `protobuf:"bytes,1,opt,name=cluster_id,json=clusterId,proto3" json:"cluster_id,omitempty"` - Element string `protobuf:"bytes,5,opt,name=element,proto3" json:"element,omitempty"` + return "" } -func (x *ClusterElement) Reset() { - *x = ClusterElement{} - if protoimpl.UnsafeEnabled { - mi := &file_api_v1_k8s_proto_msgTypes[62] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) +func (x *NodeClaimRef) GetUid() string { + if x != nil { + return x.Uid } + return "" } -func (x *ClusterElement) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ClusterElement) ProtoMessage() {} - -func (x *ClusterElement) ProtoReflect() protoreflect.Message { - mi := &file_api_v1_k8s_proto_msgTypes[62] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms +func (x *NodeClaimRef) GetDeletable() bool { + if x != nil { + return x.Deletable } - return mi.MessageOf(x) + return false } -// Deprecated: Use ClusterElement.ProtoReflect.Descriptor instead. -func (*ClusterElement) Descriptor() ([]byte, []int) { - return file_api_v1_k8s_proto_rawDescGZIP(), []int{62} +func (x *NodeClaimRef) GetNotDeletableReason() NodeClaimNotDeletableReason { + if x != nil { + return x.NotDeletableReason + } + return NodeClaimNotDeletableReason_NODE_CLAIM_NOT_DELETABLE_REASON_UNSPECIFIED } -func (x *ClusterElement) GetClusterId() string { +func (x *NodeClaimRef) GetMinOperatorVersion() string { if x != nil { - return x.ClusterId + return x.MinOperatorVersion } return "" } -func (x *ClusterElement) GetElement() string { +func (x *NodeClaimRef) GetLatestStatus() *NodeClaimDeletionStatus { if x != nil { - return x.Element + return x.LatestStatus } - return "" + return nil } -type GetAllNamespacesRequest struct { +type RequestNodeClaimDeletionRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - TeamId string `protobuf:"bytes,1,opt,name=team_id,json=teamId,proto3" json:"team_id,omitempty"` - ClusterIds []string `protobuf:"bytes,5,rep,name=cluster_ids,json=clusterIds,proto3" json:"cluster_ids,omitempty"` - StartTime int64 `protobuf:"varint,11,opt,name=start_time,json=startTime,proto3" json:"start_time,omitempty"` - StopTime int64 `protobuf:"varint,12,opt,name=stop_time,json=stopTime,proto3" json:"stop_time,omitempty"` + TeamId string `protobuf:"bytes,1,opt,name=team_id,json=teamId,proto3" json:"team_id,omitempty"` + ClusterId string `protobuf:"bytes,2,opt,name=cluster_id,json=clusterId,proto3" json:"cluster_id,omitempty"` + // Mirrors GetNodeRequest: either identifies the node. + NodeId *string `protobuf:"bytes,3,opt,name=node_id,json=nodeId,proto3,oneof" json:"node_id,omitempty"` + NodeUid *string `protobuf:"bytes,4,opt,name=node_uid,json=nodeUid,proto3,oneof" json:"node_uid,omitempty"` + // The NodeClaim UID the client is looking at. dakr compares it with the Node + // current owner uid and refuses with FailedPrecondition on mismatch. + NodeClaimUid string `protobuf:"bytes,5,opt,name=node_claim_uid,json=nodeClaimUid,proto3" json:"node_claim_uid,omitempty"` } -func (x *GetAllNamespacesRequest) Reset() { - *x = GetAllNamespacesRequest{} +func (x *RequestNodeClaimDeletionRequest) Reset() { + *x = RequestNodeClaimDeletionRequest{} if protoimpl.UnsafeEnabled { - mi := &file_api_v1_k8s_proto_msgTypes[63] + mi := &file_api_v1_k8s_proto_msgTypes[59] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } -func (x *GetAllNamespacesRequest) String() string { +func (x *RequestNodeClaimDeletionRequest) String() string { return protoimpl.X.MessageStringOf(x) } -func (*GetAllNamespacesRequest) ProtoMessage() {} +func (*RequestNodeClaimDeletionRequest) ProtoMessage() {} -func (x *GetAllNamespacesRequest) ProtoReflect() protoreflect.Message { - mi := &file_api_v1_k8s_proto_msgTypes[63] +func (x *RequestNodeClaimDeletionRequest) ProtoReflect() protoreflect.Message { + mi := &file_api_v1_k8s_proto_msgTypes[59] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4884,64 +5295,71 @@ func (x *GetAllNamespacesRequest) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use GetAllNamespacesRequest.ProtoReflect.Descriptor instead. -func (*GetAllNamespacesRequest) Descriptor() ([]byte, []int) { - return file_api_v1_k8s_proto_rawDescGZIP(), []int{63} +// Deprecated: Use RequestNodeClaimDeletionRequest.ProtoReflect.Descriptor instead. +func (*RequestNodeClaimDeletionRequest) Descriptor() ([]byte, []int) { + return file_api_v1_k8s_proto_rawDescGZIP(), []int{59} } -func (x *GetAllNamespacesRequest) GetTeamId() string { +func (x *RequestNodeClaimDeletionRequest) GetTeamId() string { if x != nil { return x.TeamId } return "" } -func (x *GetAllNamespacesRequest) GetClusterIds() []string { +func (x *RequestNodeClaimDeletionRequest) GetClusterId() string { if x != nil { - return x.ClusterIds + return x.ClusterId } - return nil + return "" } -func (x *GetAllNamespacesRequest) GetStartTime() int64 { - if x != nil { - return x.StartTime +func (x *RequestNodeClaimDeletionRequest) GetNodeId() string { + if x != nil && x.NodeId != nil { + return *x.NodeId } - return 0 + return "" } -func (x *GetAllNamespacesRequest) GetStopTime() int64 { +func (x *RequestNodeClaimDeletionRequest) GetNodeUid() string { + if x != nil && x.NodeUid != nil { + return *x.NodeUid + } + return "" +} + +func (x *RequestNodeClaimDeletionRequest) GetNodeClaimUid() string { if x != nil { - return x.StopTime + return x.NodeClaimUid } - return 0 + return "" } -type GetAllNamespacesResponse struct { +type RequestNodeClaimDeletionResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - ClusterIdWithNamespaces []*ClusterElement `protobuf:"bytes,1,rep,name=cluster_id_with_namespaces,json=clusterIdWithNamespaces,proto3" json:"cluster_id_with_namespaces,omitempty"` + Status *NodeClaimDeletionStatus `protobuf:"bytes,1,opt,name=status,proto3" json:"status,omitempty"` } -func (x *GetAllNamespacesResponse) Reset() { - *x = GetAllNamespacesResponse{} +func (x *RequestNodeClaimDeletionResponse) Reset() { + *x = RequestNodeClaimDeletionResponse{} if protoimpl.UnsafeEnabled { - mi := &file_api_v1_k8s_proto_msgTypes[64] + mi := &file_api_v1_k8s_proto_msgTypes[60] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } -func (x *GetAllNamespacesResponse) String() string { +func (x *RequestNodeClaimDeletionResponse) String() string { return protoimpl.X.MessageStringOf(x) } -func (*GetAllNamespacesResponse) ProtoMessage() {} +func (*RequestNodeClaimDeletionResponse) ProtoMessage() {} -func (x *GetAllNamespacesResponse) ProtoReflect() protoreflect.Message { - mi := &file_api_v1_k8s_proto_msgTypes[64] +func (x *RequestNodeClaimDeletionResponse) ProtoReflect() protoreflect.Message { + mi := &file_api_v1_k8s_proto_msgTypes[60] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4952,46 +5370,46 @@ func (x *GetAllNamespacesResponse) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use GetAllNamespacesResponse.ProtoReflect.Descriptor instead. -func (*GetAllNamespacesResponse) Descriptor() ([]byte, []int) { - return file_api_v1_k8s_proto_rawDescGZIP(), []int{64} +// Deprecated: Use RequestNodeClaimDeletionResponse.ProtoReflect.Descriptor instead. +func (*RequestNodeClaimDeletionResponse) Descriptor() ([]byte, []int) { + return file_api_v1_k8s_proto_rawDescGZIP(), []int{60} } -func (x *GetAllNamespacesResponse) GetClusterIdWithNamespaces() []*ClusterElement { +func (x *RequestNodeClaimDeletionResponse) GetStatus() *NodeClaimDeletionStatus { if x != nil { - return x.ClusterIdWithNamespaces + return x.Status } return nil } -// SearchNamespacesByClusterRequest searches namespaces by name within a single cluster. -type SearchNamespacesByClusterRequest struct { +type GetNodeClaimDeletionStatusRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - TeamId string `protobuf:"bytes,1,opt,name=team_id,json=teamId,proto3" json:"team_id,omitempty"` - ClusterId string `protobuf:"bytes,2,opt,name=cluster_id,json=clusterId,proto3" json:"cluster_id,omitempty"` - Search string `protobuf:"bytes,3,opt,name=search,proto3" json:"search,omitempty"` + TeamId string `protobuf:"bytes,1,opt,name=team_id,json=teamId,proto3" json:"team_id,omitempty"` + ClusterId string `protobuf:"bytes,2,opt,name=cluster_id,json=clusterId,proto3" json:"cluster_id,omitempty"` + NodeId *string `protobuf:"bytes,3,opt,name=node_id,json=nodeId,proto3,oneof" json:"node_id,omitempty"` + NodeUid *string `protobuf:"bytes,4,opt,name=node_uid,json=nodeUid,proto3,oneof" json:"node_uid,omitempty"` } -func (x *SearchNamespacesByClusterRequest) Reset() { - *x = SearchNamespacesByClusterRequest{} +func (x *GetNodeClaimDeletionStatusRequest) Reset() { + *x = GetNodeClaimDeletionStatusRequest{} if protoimpl.UnsafeEnabled { - mi := &file_api_v1_k8s_proto_msgTypes[65] + mi := &file_api_v1_k8s_proto_msgTypes[61] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } -func (x *SearchNamespacesByClusterRequest) String() string { +func (x *GetNodeClaimDeletionStatusRequest) String() string { return protoimpl.X.MessageStringOf(x) } -func (*SearchNamespacesByClusterRequest) ProtoMessage() {} +func (*GetNodeClaimDeletionStatusRequest) ProtoMessage() {} -func (x *SearchNamespacesByClusterRequest) ProtoReflect() protoreflect.Message { - mi := &file_api_v1_k8s_proto_msgTypes[65] +func (x *GetNodeClaimDeletionStatusRequest) ProtoReflect() protoreflect.Message { + mi := &file_api_v1_k8s_proto_msgTypes[61] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5002,58 +5420,64 @@ func (x *SearchNamespacesByClusterRequest) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use SearchNamespacesByClusterRequest.ProtoReflect.Descriptor instead. -func (*SearchNamespacesByClusterRequest) Descriptor() ([]byte, []int) { - return file_api_v1_k8s_proto_rawDescGZIP(), []int{65} +// Deprecated: Use GetNodeClaimDeletionStatusRequest.ProtoReflect.Descriptor instead. +func (*GetNodeClaimDeletionStatusRequest) Descriptor() ([]byte, []int) { + return file_api_v1_k8s_proto_rawDescGZIP(), []int{61} } -func (x *SearchNamespacesByClusterRequest) GetTeamId() string { +func (x *GetNodeClaimDeletionStatusRequest) GetTeamId() string { if x != nil { return x.TeamId } return "" } -func (x *SearchNamespacesByClusterRequest) GetClusterId() string { +func (x *GetNodeClaimDeletionStatusRequest) GetClusterId() string { if x != nil { return x.ClusterId } return "" } -func (x *SearchNamespacesByClusterRequest) GetSearch() string { - if x != nil { - return x.Search +func (x *GetNodeClaimDeletionStatusRequest) GetNodeId() string { + if x != nil && x.NodeId != nil { + return *x.NodeId } return "" } -// SearchNamespacesByClusterResponse returns the matching namespace names. -type SearchNamespacesByClusterResponse struct { +func (x *GetNodeClaimDeletionStatusRequest) GetNodeUid() string { + if x != nil && x.NodeUid != nil { + return *x.NodeUid + } + return "" +} + +type GetNodeClaimDeletionStatusResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Namespaces []string `protobuf:"bytes,1,rep,name=namespaces,proto3" json:"namespaces,omitempty"` + Status *NodeClaimDeletionStatus `protobuf:"bytes,1,opt,name=status,proto3" json:"status,omitempty"` // unset when no request was ever made for this node } -func (x *SearchNamespacesByClusterResponse) Reset() { - *x = SearchNamespacesByClusterResponse{} +func (x *GetNodeClaimDeletionStatusResponse) Reset() { + *x = GetNodeClaimDeletionStatusResponse{} if protoimpl.UnsafeEnabled { - mi := &file_api_v1_k8s_proto_msgTypes[66] + mi := &file_api_v1_k8s_proto_msgTypes[62] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } -func (x *SearchNamespacesByClusterResponse) String() string { +func (x *GetNodeClaimDeletionStatusResponse) String() string { return protoimpl.X.MessageStringOf(x) } -func (*SearchNamespacesByClusterResponse) ProtoMessage() {} +func (*GetNodeClaimDeletionStatusResponse) ProtoMessage() {} -func (x *SearchNamespacesByClusterResponse) ProtoReflect() protoreflect.Message { - mi := &file_api_v1_k8s_proto_msgTypes[66] +func (x *GetNodeClaimDeletionStatusResponse) ProtoReflect() protoreflect.Message { + mi := &file_api_v1_k8s_proto_msgTypes[62] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5064,45 +5488,48 @@ func (x *SearchNamespacesByClusterResponse) ProtoReflect() protoreflect.Message return mi.MessageOf(x) } -// Deprecated: Use SearchNamespacesByClusterResponse.ProtoReflect.Descriptor instead. -func (*SearchNamespacesByClusterResponse) Descriptor() ([]byte, []int) { - return file_api_v1_k8s_proto_rawDescGZIP(), []int{66} +// Deprecated: Use GetNodeClaimDeletionStatusResponse.ProtoReflect.Descriptor instead. +func (*GetNodeClaimDeletionStatusResponse) Descriptor() ([]byte, []int) { + return file_api_v1_k8s_proto_rawDescGZIP(), []int{62} } -func (x *SearchNamespacesByClusterResponse) GetNamespaces() []string { +func (x *GetNodeClaimDeletionStatusResponse) GetStatus() *NodeClaimDeletionStatus { if x != nil { - return x.Namespaces + return x.Status } return nil } -// ListNamespacesByClusterRequest lists namespaces within a single cluster. -type ListNamespacesByClusterRequest struct { +// GetNodeClaimRefsRequest asks for the delete affordance of a set of nodes. +// node_ids is the page the caller is showing, not the whole cluster: the server +// refuses more than it is willing to answer for in one round trip. +type GetNodeClaimRefsRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - TeamId string `protobuf:"bytes,1,opt,name=team_id,json=teamId,proto3" json:"team_id,omitempty"` - ClusterId string `protobuf:"bytes,2,opt,name=cluster_id,json=clusterId,proto3" json:"cluster_id,omitempty"` + TeamId string `protobuf:"bytes,1,opt,name=team_id,json=teamId,proto3" json:"team_id,omitempty"` + ClusterId string `protobuf:"bytes,2,opt,name=cluster_id,json=clusterId,proto3" json:"cluster_id,omitempty"` + NodeIds []string `protobuf:"bytes,3,rep,name=node_ids,json=nodeIds,proto3" json:"node_ids,omitempty"` } -func (x *ListNamespacesByClusterRequest) Reset() { - *x = ListNamespacesByClusterRequest{} +func (x *GetNodeClaimRefsRequest) Reset() { + *x = GetNodeClaimRefsRequest{} if protoimpl.UnsafeEnabled { - mi := &file_api_v1_k8s_proto_msgTypes[67] + mi := &file_api_v1_k8s_proto_msgTypes[63] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } -func (x *ListNamespacesByClusterRequest) String() string { +func (x *GetNodeClaimRefsRequest) String() string { return protoimpl.X.MessageStringOf(x) } -func (*ListNamespacesByClusterRequest) ProtoMessage() {} +func (*GetNodeClaimRefsRequest) ProtoMessage() {} -func (x *ListNamespacesByClusterRequest) ProtoReflect() protoreflect.Message { - mi := &file_api_v1_k8s_proto_msgTypes[67] +func (x *GetNodeClaimRefsRequest) ProtoReflect() protoreflect.Message { + mi := &file_api_v1_k8s_proto_msgTypes[63] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5113,51 +5540,61 @@ func (x *ListNamespacesByClusterRequest) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use ListNamespacesByClusterRequest.ProtoReflect.Descriptor instead. -func (*ListNamespacesByClusterRequest) Descriptor() ([]byte, []int) { - return file_api_v1_k8s_proto_rawDescGZIP(), []int{67} +// Deprecated: Use GetNodeClaimRefsRequest.ProtoReflect.Descriptor instead. +func (*GetNodeClaimRefsRequest) Descriptor() ([]byte, []int) { + return file_api_v1_k8s_proto_rawDescGZIP(), []int{63} } -func (x *ListNamespacesByClusterRequest) GetTeamId() string { +func (x *GetNodeClaimRefsRequest) GetTeamId() string { if x != nil { return x.TeamId } return "" } -func (x *ListNamespacesByClusterRequest) GetClusterId() string { +func (x *GetNodeClaimRefsRequest) GetClusterId() string { if x != nil { return x.ClusterId } return "" } -// ListNamespacesByClusterResponse returns namespace id and name pairs. -type ListNamespacesByClusterResponse struct { +func (x *GetNodeClaimRefsRequest) GetNodeIds() []string { + if x != nil { + return x.NodeIds + } + return nil +} + +// NodeClaimRefEntry pairs a node with its NodeClaim affordance. Nodes that are +// not owned by a NodeClaim carry no entry at all, matching GetNodeResponse +// leaving node_claim unset for them. +type NodeClaimRefEntry struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Namespaces []*NamespaceItem `protobuf:"bytes,1,rep,name=namespaces,proto3" json:"namespaces,omitempty"` + NodeId string `protobuf:"bytes,1,opt,name=node_id,json=nodeId,proto3" json:"node_id,omitempty"` + Ref *NodeClaimRef `protobuf:"bytes,2,opt,name=ref,proto3" json:"ref,omitempty"` } -func (x *ListNamespacesByClusterResponse) Reset() { - *x = ListNamespacesByClusterResponse{} +func (x *NodeClaimRefEntry) Reset() { + *x = NodeClaimRefEntry{} if protoimpl.UnsafeEnabled { - mi := &file_api_v1_k8s_proto_msgTypes[68] + mi := &file_api_v1_k8s_proto_msgTypes[64] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } -func (x *ListNamespacesByClusterResponse) String() string { +func (x *NodeClaimRefEntry) String() string { return protoimpl.X.MessageStringOf(x) } -func (*ListNamespacesByClusterResponse) ProtoMessage() {} +func (*NodeClaimRefEntry) ProtoMessage() {} -func (x *ListNamespacesByClusterResponse) ProtoReflect() protoreflect.Message { - mi := &file_api_v1_k8s_proto_msgTypes[68] +func (x *NodeClaimRefEntry) ProtoReflect() protoreflect.Message { + mi := &file_api_v1_k8s_proto_msgTypes[64] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5168,45 +5605,50 @@ func (x *ListNamespacesByClusterResponse) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use ListNamespacesByClusterResponse.ProtoReflect.Descriptor instead. -func (*ListNamespacesByClusterResponse) Descriptor() ([]byte, []int) { - return file_api_v1_k8s_proto_rawDescGZIP(), []int{68} +// Deprecated: Use NodeClaimRefEntry.ProtoReflect.Descriptor instead. +func (*NodeClaimRefEntry) Descriptor() ([]byte, []int) { + return file_api_v1_k8s_proto_rawDescGZIP(), []int{64} } -func (x *ListNamespacesByClusterResponse) GetNamespaces() []*NamespaceItem { +func (x *NodeClaimRefEntry) GetNodeId() string { if x != nil { - return x.Namespaces + return x.NodeId + } + return "" +} + +func (x *NodeClaimRefEntry) GetRef() *NodeClaimRef { + if x != nil { + return x.Ref } return nil } -// NamespaceItem represents a namespace with its id and name. -type NamespaceItem struct { +type GetNodeClaimRefsResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` - Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` + Entries []*NodeClaimRefEntry `protobuf:"bytes,1,rep,name=entries,proto3" json:"entries,omitempty"` } -func (x *NamespaceItem) Reset() { - *x = NamespaceItem{} +func (x *GetNodeClaimRefsResponse) Reset() { + *x = GetNodeClaimRefsResponse{} if protoimpl.UnsafeEnabled { - mi := &file_api_v1_k8s_proto_msgTypes[69] + mi := &file_api_v1_k8s_proto_msgTypes[65] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } -func (x *NamespaceItem) String() string { +func (x *GetNodeClaimRefsResponse) String() string { return protoimpl.X.MessageStringOf(x) } -func (*NamespaceItem) ProtoMessage() {} +func (*GetNodeClaimRefsResponse) ProtoMessage() {} -func (x *NamespaceItem) ProtoReflect() protoreflect.Message { - mi := &file_api_v1_k8s_proto_msgTypes[69] +func (x *GetNodeClaimRefsResponse) ProtoReflect() protoreflect.Message { + mi := &file_api_v1_k8s_proto_msgTypes[65] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5217,58 +5659,103 @@ func (x *NamespaceItem) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use NamespaceItem.ProtoReflect.Descriptor instead. -func (*NamespaceItem) Descriptor() ([]byte, []int) { - return file_api_v1_k8s_proto_rawDescGZIP(), []int{69} +// Deprecated: Use GetNodeClaimRefsResponse.ProtoReflect.Descriptor instead. +func (*GetNodeClaimRefsResponse) Descriptor() ([]byte, []int) { + return file_api_v1_k8s_proto_rawDescGZIP(), []int{65} } -func (x *NamespaceItem) GetId() string { +func (x *GetNodeClaimRefsResponse) GetEntries() []*NodeClaimRefEntry { if x != nil { - return x.Id + return x.Entries + } + return nil +} + +// NodeClaimDeletionItem is one node the caller wants deleted, carrying the +// NodeClaim UID it was looking at so a claim replaced since the read is +// refused rather than deleted. +type NodeClaimDeletionItem struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + NodeId string `protobuf:"bytes,1,opt,name=node_id,json=nodeId,proto3" json:"node_id,omitempty"` + NodeClaimUid string `protobuf:"bytes,2,opt,name=node_claim_uid,json=nodeClaimUid,proto3" json:"node_claim_uid,omitempty"` +} + +func (x *NodeClaimDeletionItem) Reset() { + *x = NodeClaimDeletionItem{} + if protoimpl.UnsafeEnabled { + mi := &file_api_v1_k8s_proto_msgTypes[66] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *NodeClaimDeletionItem) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*NodeClaimDeletionItem) ProtoMessage() {} + +func (x *NodeClaimDeletionItem) ProtoReflect() protoreflect.Message { + mi := &file_api_v1_k8s_proto_msgTypes[66] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use NodeClaimDeletionItem.ProtoReflect.Descriptor instead. +func (*NodeClaimDeletionItem) Descriptor() ([]byte, []int) { + return file_api_v1_k8s_proto_rawDescGZIP(), []int{66} +} + +func (x *NodeClaimDeletionItem) GetNodeId() string { + if x != nil { + return x.NodeId } return "" } -func (x *NamespaceItem) GetName() string { +func (x *NodeClaimDeletionItem) GetNodeClaimUid() string { if x != nil { - return x.Name + return x.NodeClaimUid } return "" } -type GetAllWorkloadNamesRequest struct { +type RequestNodeClaimDeletionsRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - TeamId string `protobuf:"bytes,1,opt,name=team_id,json=teamId,proto3" json:"team_id,omitempty"` - ClusterIds []string `protobuf:"bytes,5,rep,name=cluster_ids,json=clusterIds,proto3" json:"cluster_ids,omitempty"` - Namespaces []string `protobuf:"bytes,6,rep,name=namespaces,proto3" json:"namespaces,omitempty"` - Kinds []K8SObjectKind `protobuf:"varint,7,rep,packed,name=kinds,proto3,enum=api.v1.K8SObjectKind" json:"kinds,omitempty"` - WorkloadLabels []string `protobuf:"bytes,8,rep,name=workload_labels,json=workloadLabels,proto3" json:"workload_labels,omitempty"` // each element is "key=val" - NodeGroupNames []string `protobuf:"bytes,9,rep,name=node_group_names,json=nodeGroupNames,proto3" json:"node_group_names,omitempty"` - StartTime int64 `protobuf:"varint,11,opt,name=start_time,json=startTime,proto3" json:"start_time,omitempty"` - StopTime int64 `protobuf:"varint,12,opt,name=stop_time,json=stopTime,proto3" json:"stop_time,omitempty"` - IncludeDeleted bool `protobuf:"varint,21,opt,name=include_deleted,json=includeDeleted,proto3" json:"include_deleted,omitempty"` + TeamId string `protobuf:"bytes,1,opt,name=team_id,json=teamId,proto3" json:"team_id,omitempty"` + ClusterId string `protobuf:"bytes,2,opt,name=cluster_id,json=clusterId,proto3" json:"cluster_id,omitempty"` + Items []*NodeClaimDeletionItem `protobuf:"bytes,3,rep,name=items,proto3" json:"items,omitempty"` } -func (x *GetAllWorkloadNamesRequest) Reset() { - *x = GetAllWorkloadNamesRequest{} +func (x *RequestNodeClaimDeletionsRequest) Reset() { + *x = RequestNodeClaimDeletionsRequest{} if protoimpl.UnsafeEnabled { - mi := &file_api_v1_k8s_proto_msgTypes[70] + mi := &file_api_v1_k8s_proto_msgTypes[67] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } -func (x *GetAllWorkloadNamesRequest) String() string { +func (x *RequestNodeClaimDeletionsRequest) String() string { return protoimpl.X.MessageStringOf(x) } -func (*GetAllWorkloadNamesRequest) ProtoMessage() {} +func (*RequestNodeClaimDeletionsRequest) ProtoMessage() {} -func (x *GetAllWorkloadNamesRequest) ProtoReflect() protoreflect.Message { - mi := &file_api_v1_k8s_proto_msgTypes[70] +func (x *RequestNodeClaimDeletionsRequest) ProtoReflect() protoreflect.Message { + mi := &file_api_v1_k8s_proto_msgTypes[67] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5279,99 +5766,124 @@ func (x *GetAllWorkloadNamesRequest) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use GetAllWorkloadNamesRequest.ProtoReflect.Descriptor instead. -func (*GetAllWorkloadNamesRequest) Descriptor() ([]byte, []int) { - return file_api_v1_k8s_proto_rawDescGZIP(), []int{70} +// Deprecated: Use RequestNodeClaimDeletionsRequest.ProtoReflect.Descriptor instead. +func (*RequestNodeClaimDeletionsRequest) Descriptor() ([]byte, []int) { + return file_api_v1_k8s_proto_rawDescGZIP(), []int{67} } -func (x *GetAllWorkloadNamesRequest) GetTeamId() string { +func (x *RequestNodeClaimDeletionsRequest) GetTeamId() string { if x != nil { return x.TeamId } return "" } -func (x *GetAllWorkloadNamesRequest) GetClusterIds() []string { +func (x *RequestNodeClaimDeletionsRequest) GetClusterId() string { if x != nil { - return x.ClusterIds + return x.ClusterId } - return nil + return "" } -func (x *GetAllWorkloadNamesRequest) GetNamespaces() []string { +func (x *RequestNodeClaimDeletionsRequest) GetItems() []*NodeClaimDeletionItem { if x != nil { - return x.Namespaces + return x.Items } return nil } -func (x *GetAllWorkloadNamesRequest) GetKinds() []K8SObjectKind { - if x != nil { - return x.Kinds - } - return nil +// NodeClaimDeletionResult is the outcome for one item. Exactly one of status +// and error is meaningful: a queued item carries its new status, a refused one +// carries why. An in-flight refusal carries both, the error saying so and the +// status being the request already running. +type NodeClaimDeletionResult struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + NodeId string `protobuf:"bytes,1,opt,name=node_id,json=nodeId,proto3" json:"node_id,omitempty"` + Status *NodeClaimDeletionStatus `protobuf:"bytes,2,opt,name=status,proto3" json:"status,omitempty"` + Error string `protobuf:"bytes,3,opt,name=error,proto3" json:"error,omitempty"` } -func (x *GetAllWorkloadNamesRequest) GetWorkloadLabels() []string { - if x != nil { - return x.WorkloadLabels +func (x *NodeClaimDeletionResult) Reset() { + *x = NodeClaimDeletionResult{} + if protoimpl.UnsafeEnabled { + mi := &file_api_v1_k8s_proto_msgTypes[68] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } - return nil } -func (x *GetAllWorkloadNamesRequest) GetNodeGroupNames() []string { - if x != nil { - return x.NodeGroupNames +func (x *NodeClaimDeletionResult) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*NodeClaimDeletionResult) ProtoMessage() {} + +func (x *NodeClaimDeletionResult) ProtoReflect() protoreflect.Message { + mi := &file_api_v1_k8s_proto_msgTypes[68] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms } - return nil + return mi.MessageOf(x) } -func (x *GetAllWorkloadNamesRequest) GetStartTime() int64 { +// Deprecated: Use NodeClaimDeletionResult.ProtoReflect.Descriptor instead. +func (*NodeClaimDeletionResult) Descriptor() ([]byte, []int) { + return file_api_v1_k8s_proto_rawDescGZIP(), []int{68} +} + +func (x *NodeClaimDeletionResult) GetNodeId() string { if x != nil { - return x.StartTime + return x.NodeId } - return 0 + return "" } -func (x *GetAllWorkloadNamesRequest) GetStopTime() int64 { +func (x *NodeClaimDeletionResult) GetStatus() *NodeClaimDeletionStatus { if x != nil { - return x.StopTime + return x.Status } - return 0 + return nil } -func (x *GetAllWorkloadNamesRequest) GetIncludeDeleted() bool { +func (x *NodeClaimDeletionResult) GetError() string { if x != nil { - return x.IncludeDeleted + return x.Error } - return false + return "" } -type GetAllWorkloadNamesResponse struct { +type RequestNodeClaimDeletionsResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - ClusterIdWithWorkloadName []*ClusterElement `protobuf:"bytes,1,rep,name=cluster_id_with_workload_name,json=clusterIdWithWorkloadName,proto3" json:"cluster_id_with_workload_name,omitempty"` + Results []*NodeClaimDeletionResult `protobuf:"bytes,1,rep,name=results,proto3" json:"results,omitempty"` } -func (x *GetAllWorkloadNamesResponse) Reset() { - *x = GetAllWorkloadNamesResponse{} +func (x *RequestNodeClaimDeletionsResponse) Reset() { + *x = RequestNodeClaimDeletionsResponse{} if protoimpl.UnsafeEnabled { - mi := &file_api_v1_k8s_proto_msgTypes[71] + mi := &file_api_v1_k8s_proto_msgTypes[69] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } -func (x *GetAllWorkloadNamesResponse) String() string { +func (x *RequestNodeClaimDeletionsResponse) String() string { return protoimpl.X.MessageStringOf(x) } -func (*GetAllWorkloadNamesResponse) ProtoMessage() {} +func (*RequestNodeClaimDeletionsResponse) ProtoMessage() {} -func (x *GetAllWorkloadNamesResponse) ProtoReflect() protoreflect.Message { - mi := &file_api_v1_k8s_proto_msgTypes[71] +func (x *RequestNodeClaimDeletionsResponse) ProtoReflect() protoreflect.Message { + mi := &file_api_v1_k8s_proto_msgTypes[69] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5382,50 +5894,55 @@ func (x *GetAllWorkloadNamesResponse) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use GetAllWorkloadNamesResponse.ProtoReflect.Descriptor instead. -func (*GetAllWorkloadNamesResponse) Descriptor() ([]byte, []int) { - return file_api_v1_k8s_proto_rawDescGZIP(), []int{71} +// Deprecated: Use RequestNodeClaimDeletionsResponse.ProtoReflect.Descriptor instead. +func (*RequestNodeClaimDeletionsResponse) Descriptor() ([]byte, []int) { + return file_api_v1_k8s_proto_rawDescGZIP(), []int{69} } -func (x *GetAllWorkloadNamesResponse) GetClusterIdWithWorkloadName() []*ClusterElement { +func (x *RequestNodeClaimDeletionsResponse) GetResults() []*NodeClaimDeletionResult { if x != nil { - return x.ClusterIdWithWorkloadName + return x.Results } return nil } -type GetAllWorkloadLabelsRequest struct { +type NodeClaimDeletionStatus struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - TeamId string `protobuf:"bytes,1,opt,name=team_id,json=teamId,proto3" json:"team_id,omitempty"` - ClusterIds []string `protobuf:"bytes,5,rep,name=cluster_ids,json=clusterIds,proto3" json:"cluster_ids,omitempty"` - Namespaces []string `protobuf:"bytes,6,rep,name=namespaces,proto3" json:"namespaces,omitempty"` - Kinds []K8SObjectKind `protobuf:"varint,7,rep,packed,name=kinds,proto3,enum=api.v1.K8SObjectKind" json:"kinds,omitempty"` - WorkloadNames []string `protobuf:"bytes,8,rep,name=workload_names,json=workloadNames,proto3" json:"workload_names,omitempty"` - NodeGroupNames []string `protobuf:"bytes,9,rep,name=node_group_names,json=nodeGroupNames,proto3" json:"node_group_names,omitempty"` - StartTime int64 `protobuf:"varint,11,opt,name=start_time,json=startTime,proto3" json:"start_time,omitempty"` - StopTime int64 `protobuf:"varint,12,opt,name=stop_time,json=stopTime,proto3" json:"stop_time,omitempty"` -} - -func (x *GetAllWorkloadLabelsRequest) Reset() { - *x = GetAllWorkloadLabelsRequest{} + RequestId string `protobuf:"bytes,1,opt,name=request_id,json=requestId,proto3" json:"request_id,omitempty"` + NodeClaimName string `protobuf:"bytes,2,opt,name=node_claim_name,json=nodeClaimName,proto3" json:"node_claim_name,omitempty"` + NodeClaimUid string `protobuf:"bytes,3,opt,name=node_claim_uid,json=nodeClaimUid,proto3" json:"node_claim_uid,omitempty"` + State NodeClaimDeletionState `protobuf:"varint,4,opt,name=state,proto3,enum=api.v1.NodeClaimDeletionState" json:"state,omitempty"` + Error string `protobuf:"bytes,5,opt,name=error,proto3" json:"error,omitempty"` + RequestedByEmail string `protobuf:"bytes,6,opt,name=requested_by_email,json=requestedByEmail,proto3" json:"requested_by_email,omitempty"` + RequestedAt *timestamppb.Timestamp `protobuf:"bytes,7,opt,name=requested_at,json=requestedAt,proto3" json:"requested_at,omitempty"` + FirstSentAt *timestamppb.Timestamp `protobuf:"bytes,8,opt,name=first_sent_at,json=firstSentAt,proto3" json:"first_sent_at,omitempty"` + AcceptedAt *timestamppb.Timestamp `protobuf:"bytes,9,opt,name=accepted_at,json=acceptedAt,proto3" json:"accepted_at,omitempty"` + ResolvedAt *timestamppb.Timestamp `protobuf:"bytes,10,opt,name=resolved_at,json=resolvedAt,proto3" json:"resolved_at,omitempty"` + // Unresolved in pending/sent with no ack 5 minutes after first_sent_at (or + // after requested_at when nothing has been sent). + StaleUnacked bool `protobuf:"varint,11,opt,name=stale_unacked,json=staleUnacked,proto3" json:"stale_unacked,omitempty"` +} + +func (x *NodeClaimDeletionStatus) Reset() { + *x = NodeClaimDeletionStatus{} if protoimpl.UnsafeEnabled { - mi := &file_api_v1_k8s_proto_msgTypes[72] + mi := &file_api_v1_k8s_proto_msgTypes[70] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } -func (x *GetAllWorkloadLabelsRequest) String() string { +func (x *NodeClaimDeletionStatus) String() string { return protoimpl.X.MessageStringOf(x) } -func (*GetAllWorkloadLabelsRequest) ProtoMessage() {} +func (*NodeClaimDeletionStatus) ProtoMessage() {} -func (x *GetAllWorkloadLabelsRequest) ProtoReflect() protoreflect.Message { - mi := &file_api_v1_k8s_proto_msgTypes[72] +func (x *NodeClaimDeletionStatus) ProtoReflect() protoreflect.Message { + mi := &file_api_v1_k8s_proto_msgTypes[70] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5436,92 +5953,117 @@ func (x *GetAllWorkloadLabelsRequest) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use GetAllWorkloadLabelsRequest.ProtoReflect.Descriptor instead. -func (*GetAllWorkloadLabelsRequest) Descriptor() ([]byte, []int) { - return file_api_v1_k8s_proto_rawDescGZIP(), []int{72} +// Deprecated: Use NodeClaimDeletionStatus.ProtoReflect.Descriptor instead. +func (*NodeClaimDeletionStatus) Descriptor() ([]byte, []int) { + return file_api_v1_k8s_proto_rawDescGZIP(), []int{70} } -func (x *GetAllWorkloadLabelsRequest) GetTeamId() string { +func (x *NodeClaimDeletionStatus) GetRequestId() string { if x != nil { - return x.TeamId + return x.RequestId } return "" } -func (x *GetAllWorkloadLabelsRequest) GetClusterIds() []string { +func (x *NodeClaimDeletionStatus) GetNodeClaimName() string { if x != nil { - return x.ClusterIds + return x.NodeClaimName } - return nil + return "" } -func (x *GetAllWorkloadLabelsRequest) GetNamespaces() []string { +func (x *NodeClaimDeletionStatus) GetNodeClaimUid() string { if x != nil { - return x.Namespaces + return x.NodeClaimUid } - return nil + return "" } -func (x *GetAllWorkloadLabelsRequest) GetKinds() []K8SObjectKind { +func (x *NodeClaimDeletionStatus) GetState() NodeClaimDeletionState { if x != nil { - return x.Kinds + return x.State } - return nil + return NodeClaimDeletionState_NODE_CLAIM_DELETION_STATE_UNSPECIFIED } -func (x *GetAllWorkloadLabelsRequest) GetWorkloadNames() []string { +func (x *NodeClaimDeletionStatus) GetError() string { if x != nil { - return x.WorkloadNames + return x.Error } - return nil + return "" } -func (x *GetAllWorkloadLabelsRequest) GetNodeGroupNames() []string { +func (x *NodeClaimDeletionStatus) GetRequestedByEmail() string { if x != nil { - return x.NodeGroupNames + return x.RequestedByEmail + } + return "" +} + +func (x *NodeClaimDeletionStatus) GetRequestedAt() *timestamppb.Timestamp { + if x != nil { + return x.RequestedAt } return nil } -func (x *GetAllWorkloadLabelsRequest) GetStartTime() int64 { +func (x *NodeClaimDeletionStatus) GetFirstSentAt() *timestamppb.Timestamp { if x != nil { - return x.StartTime + return x.FirstSentAt } - return 0 + return nil } -func (x *GetAllWorkloadLabelsRequest) GetStopTime() int64 { +func (x *NodeClaimDeletionStatus) GetAcceptedAt() *timestamppb.Timestamp { if x != nil { - return x.StopTime + return x.AcceptedAt } - return 0 + return nil } -type GetAllWorkloadLabelsResponse struct { +func (x *NodeClaimDeletionStatus) GetResolvedAt() *timestamppb.Timestamp { + if x != nil { + return x.ResolvedAt + } + return nil +} + +func (x *NodeClaimDeletionStatus) GetStaleUnacked() bool { + if x != nil { + return x.StaleUnacked + } + return false +} + +// GetWorkloadsResponse contains a list of workloads and aggregated metrics for a cluster. +type GetWorkloadsResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - ClusterIdWithLabels []*ClusterElement `protobuf:"bytes,1,rep,name=cluster_id_with_labels,json=clusterIdWithLabels,proto3" json:"cluster_id_with_labels,omitempty"` + WorkloadItems []*WorkloadItem `protobuf:"bytes,1,rep,name=workload_items,json=workloadItems,proto3" json:"workload_items,omitempty"` // List of workload hierarchy items. + CostInfo *CostInfo `protobuf:"bytes,2,opt,name=cost_info,json=costInfo,proto3" json:"cost_info,omitempty"` // Aggregated cost information for workloads. + ResourceMetrics *ResourceMetrics `protobuf:"bytes,3,opt,name=resource_metrics,json=resourceMetrics,proto3" json:"resource_metrics,omitempty"` // Aggregated resource metrics for workloads. + ResourceSummary *ResourceSummary `protobuf:"bytes,4,opt,name=resource_summary,json=resourceSummary,proto3" json:"resource_summary,omitempty"` // Summary of resource counts and pod statuses. } -func (x *GetAllWorkloadLabelsResponse) Reset() { - *x = GetAllWorkloadLabelsResponse{} +func (x *GetWorkloadsResponse) Reset() { + *x = GetWorkloadsResponse{} if protoimpl.UnsafeEnabled { - mi := &file_api_v1_k8s_proto_msgTypes[73] + mi := &file_api_v1_k8s_proto_msgTypes[71] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } -func (x *GetAllWorkloadLabelsResponse) String() string { +func (x *GetWorkloadsResponse) String() string { return protoimpl.X.MessageStringOf(x) } -func (*GetAllWorkloadLabelsResponse) ProtoMessage() {} +func (*GetWorkloadsResponse) ProtoMessage() {} -func (x *GetAllWorkloadLabelsResponse) ProtoReflect() protoreflect.Message { - mi := &file_api_v1_k8s_proto_msgTypes[73] +func (x *GetWorkloadsResponse) ProtoReflect() protoreflect.Message { + mi := &file_api_v1_k8s_proto_msgTypes[71] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5532,47 +6074,70 @@ func (x *GetAllWorkloadLabelsResponse) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use GetAllWorkloadLabelsResponse.ProtoReflect.Descriptor instead. -func (*GetAllWorkloadLabelsResponse) Descriptor() ([]byte, []int) { - return file_api_v1_k8s_proto_rawDescGZIP(), []int{73} +// Deprecated: Use GetWorkloadsResponse.ProtoReflect.Descriptor instead. +func (*GetWorkloadsResponse) Descriptor() ([]byte, []int) { + return file_api_v1_k8s_proto_rawDescGZIP(), []int{71} } -func (x *GetAllWorkloadLabelsResponse) GetClusterIdWithLabels() []*ClusterElement { +func (x *GetWorkloadsResponse) GetWorkloadItems() []*WorkloadItem { if x != nil { - return x.ClusterIdWithLabels + return x.WorkloadItems } return nil } -type GetAllNodeGroupNamesRequest struct { +func (x *GetWorkloadsResponse) GetCostInfo() *CostInfo { + if x != nil { + return x.CostInfo + } + return nil +} + +func (x *GetWorkloadsResponse) GetResourceMetrics() *ResourceMetrics { + if x != nil { + return x.ResourceMetrics + } + return nil +} + +func (x *GetWorkloadsResponse) GetResourceSummary() *ResourceSummary { + if x != nil { + return x.ResourceSummary + } + return nil +} + +// GetWorkloadResponse contains details and metrics for a specific workload in a cluster. +type GetWorkloadResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - TeamId string `protobuf:"bytes,1,opt,name=team_id,json=teamId,proto3" json:"team_id,omitempty"` - ClusterIds []string `protobuf:"bytes,5,rep,name=cluster_ids,json=clusterIds,proto3" json:"cluster_ids,omitempty"` - StartTime int64 `protobuf:"varint,11,opt,name=start_time,json=startTime,proto3" json:"start_time,omitempty"` - StopTime int64 `protobuf:"varint,12,opt,name=stop_time,json=stopTime,proto3" json:"stop_time,omitempty"` - IncludeDeleted bool `protobuf:"varint,21,opt,name=include_deleted,json=includeDeleted,proto3" json:"include_deleted,omitempty"` + WorkloadItem *WorkloadItem `protobuf:"bytes,1,opt,name=workload_item,json=workloadItem,proto3" json:"workload_item,omitempty"` // Details of the workload hierarchy. + CostInfo *CostInfo `protobuf:"bytes,2,opt,name=cost_info,json=costInfo,proto3" json:"cost_info,omitempty"` // Aggregated cost information for the workload. + ResourceMetrics *ResourceMetrics `protobuf:"bytes,3,opt,name=resource_metrics,json=resourceMetrics,proto3" json:"resource_metrics,omitempty"` // Aggregated resource metrics for the workload. + ResourceSummary *ResourceSummary `protobuf:"bytes,4,opt,name=resource_summary,json=resourceSummary,proto3" json:"resource_summary,omitempty"` // Summary of resource counts and pod statuses for the workload. + CostDataPoints []*CostDataPoint `protobuf:"bytes,5,rep,name=cost_data_points,json=costDataPoints,proto3" json:"cost_data_points,omitempty"` // Time-series cost data points for the workload. + ResourceDataPoints []*ResourceDataPoint `protobuf:"bytes,6,rep,name=resource_data_points,json=resourceDataPoints,proto3" json:"resource_data_points,omitempty"` // Time-series resource metrics for the workload. } -func (x *GetAllNodeGroupNamesRequest) Reset() { - *x = GetAllNodeGroupNamesRequest{} +func (x *GetWorkloadResponse) Reset() { + *x = GetWorkloadResponse{} if protoimpl.UnsafeEnabled { - mi := &file_api_v1_k8s_proto_msgTypes[74] + mi := &file_api_v1_k8s_proto_msgTypes[72] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } -func (x *GetAllNodeGroupNamesRequest) String() string { +func (x *GetWorkloadResponse) String() string { return protoimpl.X.MessageStringOf(x) } -func (*GetAllNodeGroupNamesRequest) ProtoMessage() {} +func (*GetWorkloadResponse) ProtoMessage() {} -func (x *GetAllNodeGroupNamesRequest) ProtoReflect() protoreflect.Message { - mi := &file_api_v1_k8s_proto_msgTypes[74] +func (x *GetWorkloadResponse) ProtoReflect() protoreflect.Message { + mi := &file_api_v1_k8s_proto_msgTypes[72] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5583,71 +6148,84 @@ func (x *GetAllNodeGroupNamesRequest) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use GetAllNodeGroupNamesRequest.ProtoReflect.Descriptor instead. -func (*GetAllNodeGroupNamesRequest) Descriptor() ([]byte, []int) { - return file_api_v1_k8s_proto_rawDescGZIP(), []int{74} +// Deprecated: Use GetWorkloadResponse.ProtoReflect.Descriptor instead. +func (*GetWorkloadResponse) Descriptor() ([]byte, []int) { + return file_api_v1_k8s_proto_rawDescGZIP(), []int{72} } -func (x *GetAllNodeGroupNamesRequest) GetTeamId() string { +func (x *GetWorkloadResponse) GetWorkloadItem() *WorkloadItem { if x != nil { - return x.TeamId + return x.WorkloadItem } - return "" + return nil } -func (x *GetAllNodeGroupNamesRequest) GetClusterIds() []string { +func (x *GetWorkloadResponse) GetCostInfo() *CostInfo { if x != nil { - return x.ClusterIds + return x.CostInfo } return nil } -func (x *GetAllNodeGroupNamesRequest) GetStartTime() int64 { +func (x *GetWorkloadResponse) GetResourceMetrics() *ResourceMetrics { if x != nil { - return x.StartTime + return x.ResourceMetrics } - return 0 + return nil } -func (x *GetAllNodeGroupNamesRequest) GetStopTime() int64 { +func (x *GetWorkloadResponse) GetResourceSummary() *ResourceSummary { if x != nil { - return x.StopTime + return x.ResourceSummary } - return 0 + return nil } -func (x *GetAllNodeGroupNamesRequest) GetIncludeDeleted() bool { +func (x *GetWorkloadResponse) GetCostDataPoints() []*CostDataPoint { if x != nil { - return x.IncludeDeleted + return x.CostDataPoints } - return false + return nil } -type GetAllNodeGroupNamesResponse struct { +func (x *GetWorkloadResponse) GetResourceDataPoints() []*ResourceDataPoint { + if x != nil { + return x.ResourceDataPoints + } + return nil +} + +// GetWorkloadContainerPercentilesRequest requests per-container percentile metrics for a workload. +type GetWorkloadContainerPercentilesRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - ClusterIdWithNodeGroupNames []*ClusterElement `protobuf:"bytes,1,rep,name=cluster_id_with_node_group_names,json=clusterIdWithNodeGroupNames,proto3" json:"cluster_id_with_node_group_names,omitempty"` + TeamId string `protobuf:"bytes,1,opt,name=team_id,json=teamId,proto3" json:"team_id,omitempty"` + ClusterId string `protobuf:"bytes,2,opt,name=cluster_id,json=clusterId,proto3" json:"cluster_id,omitempty"` + Kind string `protobuf:"bytes,3,opt,name=kind,proto3" json:"kind,omitempty"` + Uid string `protobuf:"bytes,4,opt,name=uid,proto3" json:"uid,omitempty"` + StartTime *timestamppb.Timestamp `protobuf:"bytes,11,opt,name=start_time,json=startTime,proto3,oneof" json:"start_time,omitempty"` + EndTime *timestamppb.Timestamp `protobuf:"bytes,12,opt,name=end_time,json=endTime,proto3,oneof" json:"end_time,omitempty"` } -func (x *GetAllNodeGroupNamesResponse) Reset() { - *x = GetAllNodeGroupNamesResponse{} +func (x *GetWorkloadContainerPercentilesRequest) Reset() { + *x = GetWorkloadContainerPercentilesRequest{} if protoimpl.UnsafeEnabled { - mi := &file_api_v1_k8s_proto_msgTypes[75] + mi := &file_api_v1_k8s_proto_msgTypes[73] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } -func (x *GetAllNodeGroupNamesResponse) String() string { +func (x *GetWorkloadContainerPercentilesRequest) String() string { return protoimpl.X.MessageStringOf(x) } -func (*GetAllNodeGroupNamesResponse) ProtoMessage() {} +func (*GetWorkloadContainerPercentilesRequest) ProtoMessage() {} -func (x *GetAllNodeGroupNamesResponse) ProtoReflect() protoreflect.Message { - mi := &file_api_v1_k8s_proto_msgTypes[75] +func (x *GetWorkloadContainerPercentilesRequest) ProtoReflect() protoreflect.Message { + mi := &file_api_v1_k8s_proto_msgTypes[73] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5658,80 +6236,79 @@ func (x *GetAllNodeGroupNamesResponse) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use GetAllNodeGroupNamesResponse.ProtoReflect.Descriptor instead. -func (*GetAllNodeGroupNamesResponse) Descriptor() ([]byte, []int) { - return file_api_v1_k8s_proto_rawDescGZIP(), []int{75} +// Deprecated: Use GetWorkloadContainerPercentilesRequest.ProtoReflect.Descriptor instead. +func (*GetWorkloadContainerPercentilesRequest) Descriptor() ([]byte, []int) { + return file_api_v1_k8s_proto_rawDescGZIP(), []int{73} } -func (x *GetAllNodeGroupNamesResponse) GetClusterIdWithNodeGroupNames() []*ClusterElement { +func (x *GetWorkloadContainerPercentilesRequest) GetTeamId() string { if x != nil { - return x.ClusterIdWithNodeGroupNames + return x.TeamId } - return nil + return "" } -// Cluster represents a Kubernetes cluster with metadata, metrics, and cost information. -type Cluster struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` // Unique identifier for the cluster. - Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` // Name of the cluster. - TeamId string `protobuf:"bytes,3,opt,name=team_id,json=teamId,proto3" json:"team_id,omitempty"` // Identifier of the team owning the cluster. - CloudProviderId string `protobuf:"bytes,4,opt,name=cloud_provider_id,json=cloudProviderId,proto3" json:"cloud_provider_id,omitempty"` // Identifier of the cloud provider. - RegionId string `protobuf:"bytes,5,opt,name=region_id,json=regionId,proto3" json:"region_id,omitempty"` // Identifier of the region. - CloudProvider string `protobuf:"bytes,6,opt,name=cloud_provider,json=cloudProvider,proto3" json:"cloud_provider,omitempty"` // Name of the cloud provider. - Region string `protobuf:"bytes,7,opt,name=region,proto3" json:"region,omitempty"` // Name of the region. - CreatedAt int64 `protobuf:"varint,8,opt,name=created_at,json=createdAt,proto3" json:"created_at,omitempty"` // Creation time as a Unix timestamp. - UpdatedAt int64 `protobuf:"varint,9,opt,name=updated_at,json=updatedAt,proto3" json:"updated_at,omitempty"` // Last update time as a Unix timestamp. - ResourceMetrics *ResourceMetrics `protobuf:"bytes,10,opt,name=resource_metrics,json=resourceMetrics,proto3" json:"resource_metrics,omitempty"` // Aggregated resource metrics for the cluster. - CostInfo *CostInfo `protobuf:"bytes,11,opt,name=cost_info,json=costInfo,proto3" json:"cost_info,omitempty"` // Aggregated cost information for the cluster. - NodeInfo *NodeInfo `protobuf:"bytes,12,opt,name=node_info,json=nodeInfo,proto3" json:"node_info,omitempty"` // Aggregated node information for the cluster. - CostDataPoints []*CostDataPoint `protobuf:"bytes,13,rep,name=cost_data_points,json=costDataPoints,proto3" json:"cost_data_points,omitempty"` // Time-series cost data points for the cluster. - ResourceDataPoints []*ResourceDataPoint `protobuf:"bytes,14,rep,name=resource_data_points,json=resourceDataPoints,proto3" json:"resource_data_points,omitempty"` // Time-series resource metrics for the cluster. - MostExpensiveNode *Node `protobuf:"bytes,15,opt,name=most_expensive_node,json=mostExpensiveNode,proto3" json:"most_expensive_node,omitempty"` // Node with highest cost. - LeastExpensiveNode *Node `protobuf:"bytes,16,opt,name=least_expensive_node,json=leastExpensiveNode,proto3" json:"least_expensive_node,omitempty"` // Node with lowest cost. - MostUnderutilizedNode *Node `protobuf:"bytes,17,opt,name=most_underutilized_node,json=mostUnderutilizedNode,proto3" json:"most_underutilized_node,omitempty"` // Node with lowest resource utilization. - MostUnderutilizedContainer *Container `protobuf:"bytes,18,opt,name=most_underutilized_container,json=mostUnderutilizedContainer,proto3" json:"most_underutilized_container,omitempty"` // Container with lowest resource utilization. - CpuCostPerHour float64 `protobuf:"fixed64,19,opt,name=cpu_cost_per_hour,json=cpuCostPerHour,proto3" json:"cpu_cost_per_hour,omitempty"` // Price per vcpu per hour. - MemoryCostPerHour float64 `protobuf:"fixed64,20,opt,name=memory_cost_per_hour,json=memoryCostPerHour,proto3" json:"memory_cost_per_hour,omitempty"` // Pricer per GiB per hour. - HasBeenUpdated bool `protobuf:"varint,21,opt,name=has_been_updated,json=hasBeenUpdated,proto3" json:"has_been_updated,omitempty"` // If cluster has been updated by users. - CustomName string `protobuf:"bytes,22,opt,name=custom_name,json=customName,proto3" json:"custom_name,omitempty"` // Custom name for the cluster. - DisplayName string `protobuf:"bytes,23,opt,name=display_name,json=displayName,proto3" json:"display_name,omitempty"` // Display name for the cluster. - IsPriceAvailable bool `protobuf:"varint,24,opt,name=is_price_available,json=isPriceAvailable,proto3" json:"is_price_available,omitempty"` // Check if we can calculate pricing for cluster automatically - GpuCostPerHour float64 `protobuf:"fixed64,25,opt,name=gpu_cost_per_hour,json=gpuCostPerHour,proto3" json:"gpu_cost_per_hour,omitempty"` // Pricer per GPU per hour. - IsDisconnected *bool `protobuf:"varint,40,opt,name=is_disconnected,json=isDisconnected,proto3,oneof" json:"is_disconnected,omitempty"` - ZxpInfo *OperatorInfo `protobuf:"bytes,50,opt,name=zxp_info,json=zxpInfo,proto3" json:"zxp_info,omitempty"` - ZxpHelmInfo *OperatorInfo `protobuf:"bytes,51,opt,name=zxp_helm_info,json=zxpHelmInfo,proto3" json:"zxp_helm_info,omitempty"` - DakrOpInfo *OperatorInfo `protobuf:"bytes,60,opt,name=dakr_op_info,json=dakrOpInfo,proto3" json:"dakr_op_info,omitempty"` - NodeOpInfo *OperatorInfo `protobuf:"bytes,61,opt,name=node_op_info,json=nodeOpInfo,proto3" json:"node_op_info,omitempty"` - SecurityOpInfo *OperatorInfo `protobuf:"bytes,62,opt,name=security_op_info,json=securityOpInfo,proto3" json:"security_op_info,omitempty"` - NetworkOpInfo *OperatorInfo `protobuf:"bytes,63,opt,name=network_op_info,json=networkOpInfo,proto3" json:"network_op_info,omitempty"` - ImpactScore float64 `protobuf:"fixed64,70,opt,name=impact_score,json=impactScore,proto3" json:"impact_score,omitempty"` // Optimization impact score for cluster - Underutilization float64 `protobuf:"fixed64,81,opt,name=underutilization,proto3" json:"underutilization,omitempty"` // Percentage of how much is cluster underutilized that is using combined numbers of cpu, memory and GPU - Tags []string `protobuf:"bytes,91,rep,name=tags,proto3" json:"tags,omitempty"` // Tags for the cluster - HasArgoWorkloads bool `protobuf:"varint,101,opt,name=has_argo_workloads,json=hasArgoWorkloads,proto3" json:"has_argo_workloads,omitempty"` // Whether any workload on this cluster is managed by Argo CD - KubernetesVersion string `protobuf:"bytes,110,opt,name=kubernetes_version,json=kubernetesVersion,proto3" json:"kubernetes_version,omitempty"` // Kubernetes version of the cluster (e.g. "v1.28.3") +func (x *GetWorkloadContainerPercentilesRequest) GetClusterId() string { + if x != nil { + return x.ClusterId + } + return "" } -func (x *Cluster) Reset() { - *x = Cluster{} - if protoimpl.UnsafeEnabled { - mi := &file_api_v1_k8s_proto_msgTypes[76] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) +func (x *GetWorkloadContainerPercentilesRequest) GetKind() string { + if x != nil { + return x.Kind } + return "" } -func (x *Cluster) String() string { - return protoimpl.X.MessageStringOf(x) +func (x *GetWorkloadContainerPercentilesRequest) GetUid() string { + if x != nil { + return x.Uid + } + return "" } -func (*Cluster) ProtoMessage() {} +func (x *GetWorkloadContainerPercentilesRequest) GetStartTime() *timestamppb.Timestamp { + if x != nil { + return x.StartTime + } + return nil +} -func (x *Cluster) ProtoReflect() protoreflect.Message { - mi := &file_api_v1_k8s_proto_msgTypes[76] +func (x *GetWorkloadContainerPercentilesRequest) GetEndTime() *timestamppb.Timestamp { + if x != nil { + return x.EndTime + } + return nil +} + +// GetWorkloadContainerPercentilesResponse returns per-container percentile statistics. +type GetWorkloadContainerPercentilesResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Containers []*ContainerPercentileSummary `protobuf:"bytes,1,rep,name=containers,proto3" json:"containers,omitempty"` +} + +func (x *GetWorkloadContainerPercentilesResponse) Reset() { + *x = GetWorkloadContainerPercentilesResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_api_v1_k8s_proto_msgTypes[74] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetWorkloadContainerPercentilesResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetWorkloadContainerPercentilesResponse) ProtoMessage() {} + +func (x *GetWorkloadContainerPercentilesResponse) ProtoReflect() protoreflect.Message { + mi := &file_api_v1_k8s_proto_msgTypes[74] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5742,297 +6319,365 @@ func (x *Cluster) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use Cluster.ProtoReflect.Descriptor instead. -func (*Cluster) Descriptor() ([]byte, []int) { - return file_api_v1_k8s_proto_rawDescGZIP(), []int{76} +// Deprecated: Use GetWorkloadContainerPercentilesResponse.ProtoReflect.Descriptor instead. +func (*GetWorkloadContainerPercentilesResponse) Descriptor() ([]byte, []int) { + return file_api_v1_k8s_proto_rawDescGZIP(), []int{74} } -func (x *Cluster) GetId() string { +func (x *GetWorkloadContainerPercentilesResponse) GetContainers() []*ContainerPercentileSummary { if x != nil { - return x.Id + return x.Containers } - return "" + return nil } -func (x *Cluster) GetName() string { - if x != nil { - return x.Name - } - return "" +// GetWorkloadContainerTimeSeriesRequest requests per-container time-series metrics for a workload. +type GetWorkloadContainerTimeSeriesRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + TeamId string `protobuf:"bytes,1,opt,name=team_id,json=teamId,proto3" json:"team_id,omitempty"` + ClusterId string `protobuf:"bytes,2,opt,name=cluster_id,json=clusterId,proto3" json:"cluster_id,omitempty"` + Kind string `protobuf:"bytes,3,opt,name=kind,proto3" json:"kind,omitempty"` + Uid string `protobuf:"bytes,4,opt,name=uid,proto3" json:"uid,omitempty"` + StartTime *timestamppb.Timestamp `protobuf:"bytes,11,opt,name=start_time,json=startTime,proto3,oneof" json:"start_time,omitempty"` + EndTime *timestamppb.Timestamp `protobuf:"bytes,12,opt,name=end_time,json=endTime,proto3,oneof" json:"end_time,omitempty"` } -func (x *Cluster) GetTeamId() string { - if x != nil { - return x.TeamId +func (x *GetWorkloadContainerTimeSeriesRequest) Reset() { + *x = GetWorkloadContainerTimeSeriesRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_api_v1_k8s_proto_msgTypes[75] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } - return "" } -func (x *Cluster) GetCloudProviderId() string { - if x != nil { - return x.CloudProviderId - } - return "" +func (x *GetWorkloadContainerTimeSeriesRequest) String() string { + return protoimpl.X.MessageStringOf(x) } -func (x *Cluster) GetRegionId() string { - if x != nil { - return x.RegionId +func (*GetWorkloadContainerTimeSeriesRequest) ProtoMessage() {} + +func (x *GetWorkloadContainerTimeSeriesRequest) ProtoReflect() protoreflect.Message { + mi := &file_api_v1_k8s_proto_msgTypes[75] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms } - return "" + return mi.MessageOf(x) } -func (x *Cluster) GetCloudProvider() string { +// Deprecated: Use GetWorkloadContainerTimeSeriesRequest.ProtoReflect.Descriptor instead. +func (*GetWorkloadContainerTimeSeriesRequest) Descriptor() ([]byte, []int) { + return file_api_v1_k8s_proto_rawDescGZIP(), []int{75} +} + +func (x *GetWorkloadContainerTimeSeriesRequest) GetTeamId() string { if x != nil { - return x.CloudProvider + return x.TeamId } return "" } -func (x *Cluster) GetRegion() string { +func (x *GetWorkloadContainerTimeSeriesRequest) GetClusterId() string { if x != nil { - return x.Region + return x.ClusterId } return "" } -func (x *Cluster) GetCreatedAt() int64 { +func (x *GetWorkloadContainerTimeSeriesRequest) GetKind() string { if x != nil { - return x.CreatedAt + return x.Kind } - return 0 + return "" } -func (x *Cluster) GetUpdatedAt() int64 { +func (x *GetWorkloadContainerTimeSeriesRequest) GetUid() string { if x != nil { - return x.UpdatedAt + return x.Uid } - return 0 + return "" } -func (x *Cluster) GetResourceMetrics() *ResourceMetrics { +func (x *GetWorkloadContainerTimeSeriesRequest) GetStartTime() *timestamppb.Timestamp { if x != nil { - return x.ResourceMetrics + return x.StartTime } return nil } -func (x *Cluster) GetCostInfo() *CostInfo { +func (x *GetWorkloadContainerTimeSeriesRequest) GetEndTime() *timestamppb.Timestamp { if x != nil { - return x.CostInfo + return x.EndTime } return nil } -func (x *Cluster) GetNodeInfo() *NodeInfo { - if x != nil { - return x.NodeInfo - } - return nil +// GetWorkloadContainerTimeSeriesResponse returns per-container time-series data. +type GetWorkloadContainerTimeSeriesResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Containers []*ContainerTimeSeries `protobuf:"bytes,1,rep,name=containers,proto3" json:"containers,omitempty"` } -func (x *Cluster) GetCostDataPoints() []*CostDataPoint { - if x != nil { - return x.CostDataPoints +func (x *GetWorkloadContainerTimeSeriesResponse) Reset() { + *x = GetWorkloadContainerTimeSeriesResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_api_v1_k8s_proto_msgTypes[76] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } - return nil } -func (x *Cluster) GetResourceDataPoints() []*ResourceDataPoint { - if x != nil { - return x.ResourceDataPoints - } - return nil +func (x *GetWorkloadContainerTimeSeriesResponse) String() string { + return protoimpl.X.MessageStringOf(x) } -func (x *Cluster) GetMostExpensiveNode() *Node { - if x != nil { - return x.MostExpensiveNode +func (*GetWorkloadContainerTimeSeriesResponse) ProtoMessage() {} + +func (x *GetWorkloadContainerTimeSeriesResponse) ProtoReflect() protoreflect.Message { + mi := &file_api_v1_k8s_proto_msgTypes[76] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms } - return nil + return mi.MessageOf(x) } -func (x *Cluster) GetLeastExpensiveNode() *Node { - if x != nil { - return x.LeastExpensiveNode - } - return nil +// Deprecated: Use GetWorkloadContainerTimeSeriesResponse.ProtoReflect.Descriptor instead. +func (*GetWorkloadContainerTimeSeriesResponse) Descriptor() ([]byte, []int) { + return file_api_v1_k8s_proto_rawDescGZIP(), []int{76} } -func (x *Cluster) GetMostUnderutilizedNode() *Node { +func (x *GetWorkloadContainerTimeSeriesResponse) GetContainers() []*ContainerTimeSeries { if x != nil { - return x.MostUnderutilizedNode + return x.Containers } return nil } -func (x *Cluster) GetMostUnderutilizedContainer() *Container { - if x != nil { - return x.MostUnderutilizedContainer - } - return nil +// GetLatestContainerRequestLimitsRequest requests the most recent request/limit values per container. +type GetLatestContainerRequestLimitsRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + TeamId string `protobuf:"bytes,1,opt,name=team_id,json=teamId,proto3" json:"team_id,omitempty"` + ClusterId string `protobuf:"bytes,2,opt,name=cluster_id,json=clusterId,proto3" json:"cluster_id,omitempty"` + Uid string `protobuf:"bytes,3,opt,name=uid,proto3" json:"uid,omitempty"` } -func (x *Cluster) GetCpuCostPerHour() float64 { - if x != nil { - return x.CpuCostPerHour +func (x *GetLatestContainerRequestLimitsRequest) Reset() { + *x = GetLatestContainerRequestLimitsRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_api_v1_k8s_proto_msgTypes[77] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } - return 0 } -func (x *Cluster) GetMemoryCostPerHour() float64 { - if x != nil { - return x.MemoryCostPerHour - } - return 0 +func (x *GetLatestContainerRequestLimitsRequest) String() string { + return protoimpl.X.MessageStringOf(x) } -func (x *Cluster) GetHasBeenUpdated() bool { - if x != nil { - return x.HasBeenUpdated +func (*GetLatestContainerRequestLimitsRequest) ProtoMessage() {} + +func (x *GetLatestContainerRequestLimitsRequest) ProtoReflect() protoreflect.Message { + mi := &file_api_v1_k8s_proto_msgTypes[77] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms } - return false + return mi.MessageOf(x) } -func (x *Cluster) GetCustomName() string { - if x != nil { - return x.CustomName - } - return "" +// Deprecated: Use GetLatestContainerRequestLimitsRequest.ProtoReflect.Descriptor instead. +func (*GetLatestContainerRequestLimitsRequest) Descriptor() ([]byte, []int) { + return file_api_v1_k8s_proto_rawDescGZIP(), []int{77} } -func (x *Cluster) GetDisplayName() string { +func (x *GetLatestContainerRequestLimitsRequest) GetTeamId() string { if x != nil { - return x.DisplayName + return x.TeamId } return "" } -func (x *Cluster) GetIsPriceAvailable() bool { +func (x *GetLatestContainerRequestLimitsRequest) GetClusterId() string { if x != nil { - return x.IsPriceAvailable + return x.ClusterId } - return false + return "" } -func (x *Cluster) GetGpuCostPerHour() float64 { +func (x *GetLatestContainerRequestLimitsRequest) GetUid() string { if x != nil { - return x.GpuCostPerHour + return x.Uid } - return 0 + return "" } -func (x *Cluster) GetIsDisconnected() bool { - if x != nil && x.IsDisconnected != nil { - return *x.IsDisconnected - } - return false +// GetLatestContainerRequestLimitsResponse returns per-container request/limit values. +type GetLatestContainerRequestLimitsResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Containers []*ContainerRequestLimits `protobuf:"bytes,1,rep,name=containers,proto3" json:"containers,omitempty"` } -func (x *Cluster) GetZxpInfo() *OperatorInfo { - if x != nil { - return x.ZxpInfo +func (x *GetLatestContainerRequestLimitsResponse) Reset() { + *x = GetLatestContainerRequestLimitsResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_api_v1_k8s_proto_msgTypes[78] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } - return nil } -func (x *Cluster) GetZxpHelmInfo() *OperatorInfo { - if x != nil { - return x.ZxpHelmInfo - } - return nil +func (x *GetLatestContainerRequestLimitsResponse) String() string { + return protoimpl.X.MessageStringOf(x) } -func (x *Cluster) GetDakrOpInfo() *OperatorInfo { - if x != nil { - return x.DakrOpInfo +func (*GetLatestContainerRequestLimitsResponse) ProtoMessage() {} + +func (x *GetLatestContainerRequestLimitsResponse) ProtoReflect() protoreflect.Message { + mi := &file_api_v1_k8s_proto_msgTypes[78] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms } - return nil + return mi.MessageOf(x) } -func (x *Cluster) GetNodeOpInfo() *OperatorInfo { - if x != nil { - return x.NodeOpInfo - } - return nil +// Deprecated: Use GetLatestContainerRequestLimitsResponse.ProtoReflect.Descriptor instead. +func (*GetLatestContainerRequestLimitsResponse) Descriptor() ([]byte, []int) { + return file_api_v1_k8s_proto_rawDescGZIP(), []int{78} } -func (x *Cluster) GetSecurityOpInfo() *OperatorInfo { +func (x *GetLatestContainerRequestLimitsResponse) GetContainers() []*ContainerRequestLimits { if x != nil { - return x.SecurityOpInfo + return x.Containers } return nil } -func (x *Cluster) GetNetworkOpInfo() *OperatorInfo { - if x != nil { - return x.NetworkOpInfo +// GetNodeGPUMigInstancesRequest requests the most recent MIG-instance telemetry for a node. +type GetNodeGPUMigInstancesRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + TeamId string `protobuf:"bytes,1,opt,name=team_id,json=teamId,proto3" json:"team_id,omitempty"` + ClusterId string `protobuf:"bytes,2,opt,name=cluster_id,json=clusterId,proto3" json:"cluster_id,omitempty"` + NodeId *string `protobuf:"bytes,3,opt,name=node_id,json=nodeId,proto3,oneof" json:"node_id,omitempty"` // Unique identifier for the node. + NodeUid *string `protobuf:"bytes,4,opt,name=node_uid,json=nodeUid,proto3,oneof" json:"node_uid,omitempty"` // alternatively -> Unique identifier for the node. +} + +func (x *GetNodeGPUMigInstancesRequest) Reset() { + *x = GetNodeGPUMigInstancesRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_api_v1_k8s_proto_msgTypes[79] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } - return nil } -func (x *Cluster) GetImpactScore() float64 { - if x != nil { - return x.ImpactScore +func (x *GetNodeGPUMigInstancesRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetNodeGPUMigInstancesRequest) ProtoMessage() {} + +func (x *GetNodeGPUMigInstancesRequest) ProtoReflect() protoreflect.Message { + mi := &file_api_v1_k8s_proto_msgTypes[79] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms } - return 0 + return mi.MessageOf(x) } -func (x *Cluster) GetUnderutilization() float64 { +// Deprecated: Use GetNodeGPUMigInstancesRequest.ProtoReflect.Descriptor instead. +func (*GetNodeGPUMigInstancesRequest) Descriptor() ([]byte, []int) { + return file_api_v1_k8s_proto_rawDescGZIP(), []int{79} +} + +func (x *GetNodeGPUMigInstancesRequest) GetTeamId() string { if x != nil { - return x.Underutilization + return x.TeamId } - return 0 + return "" } -func (x *Cluster) GetTags() []string { +func (x *GetNodeGPUMigInstancesRequest) GetClusterId() string { if x != nil { - return x.Tags + return x.ClusterId } - return nil + return "" } -func (x *Cluster) GetHasArgoWorkloads() bool { - if x != nil { - return x.HasArgoWorkloads +func (x *GetNodeGPUMigInstancesRequest) GetNodeId() string { + if x != nil && x.NodeId != nil { + return *x.NodeId } - return false + return "" } -func (x *Cluster) GetKubernetesVersion() string { - if x != nil { - return x.KubernetesVersion +func (x *GetNodeGPUMigInstancesRequest) GetNodeUid() string { + if x != nil && x.NodeUid != nil { + return *x.NodeUid } return "" } -type OperatorInfo struct { +// GetNodeGPUMigInstancesResponse returns the node's most recent MIG-instance telemetry. +type GetNodeGPUMigInstancesResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Version string `protobuf:"bytes,1,opt,name=version,proto3" json:"version,omitempty"` - Commit string `protobuf:"bytes,11,opt,name=commit,proto3" json:"commit,omitempty"` - Date string `protobuf:"bytes,21,opt,name=date,proto3" json:"date,omitempty"` + Instances []*GPUMigInstance `protobuf:"bytes,1,rep,name=instances,proto3" json:"instances,omitempty"` + CollectedAt *timestamppb.Timestamp `protobuf:"bytes,2,opt,name=collected_at,json=collectedAt,proto3,oneof" json:"collected_at,omitempty"` } -func (x *OperatorInfo) Reset() { - *x = OperatorInfo{} +func (x *GetNodeGPUMigInstancesResponse) Reset() { + *x = GetNodeGPUMigInstancesResponse{} if protoimpl.UnsafeEnabled { - mi := &file_api_v1_k8s_proto_msgTypes[77] + mi := &file_api_v1_k8s_proto_msgTypes[80] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } -func (x *OperatorInfo) String() string { +func (x *GetNodeGPUMigInstancesResponse) String() string { return protoimpl.X.MessageStringOf(x) } -func (*OperatorInfo) ProtoMessage() {} +func (*GetNodeGPUMigInstancesResponse) ProtoMessage() {} -func (x *OperatorInfo) ProtoReflect() protoreflect.Message { - mi := &file_api_v1_k8s_proto_msgTypes[77] +func (x *GetNodeGPUMigInstancesResponse) ProtoReflect() protoreflect.Message { + mi := &file_api_v1_k8s_proto_msgTypes[80] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6043,60 +6688,57 @@ func (x *OperatorInfo) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use OperatorInfo.ProtoReflect.Descriptor instead. -func (*OperatorInfo) Descriptor() ([]byte, []int) { - return file_api_v1_k8s_proto_rawDescGZIP(), []int{77} -} - -func (x *OperatorInfo) GetVersion() string { - if x != nil { - return x.Version - } - return "" +// Deprecated: Use GetNodeGPUMigInstancesResponse.ProtoReflect.Descriptor instead. +func (*GetNodeGPUMigInstancesResponse) Descriptor() ([]byte, []int) { + return file_api_v1_k8s_proto_rawDescGZIP(), []int{80} } -func (x *OperatorInfo) GetCommit() string { +func (x *GetNodeGPUMigInstancesResponse) GetInstances() []*GPUMigInstance { if x != nil { - return x.Commit + return x.Instances } - return "" + return nil } -func (x *OperatorInfo) GetDate() string { +func (x *GetNodeGPUMigInstancesResponse) GetCollectedAt() *timestamppb.Timestamp { if x != nil { - return x.Date + return x.CollectedAt } - return "" + return nil } -// Container represents a Kubernetes container with identification and resource metrics. -type Container struct { +// GetNodeGPUMigInstanceTimeseriesRequest requests per-MIG-instance +// utilization as a time series for a node over a time range. +type GetNodeGPUMigInstanceTimeseriesRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` // Unique identifier for the container. - Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` // Name of the container. - ResourceMetrics *ResourceMetrics `protobuf:"bytes,3,opt,name=resource_metrics,json=resourceMetrics,proto3" json:"resource_metrics,omitempty"` // Resource utilization metrics for the container. + TeamId string `protobuf:"bytes,1,opt,name=team_id,json=teamId,proto3" json:"team_id,omitempty"` + ClusterId string `protobuf:"bytes,2,opt,name=cluster_id,json=clusterId,proto3" json:"cluster_id,omitempty"` + NodeId *string `protobuf:"bytes,3,opt,name=node_id,json=nodeId,proto3,oneof" json:"node_id,omitempty"` + NodeUid *string `protobuf:"bytes,4,opt,name=node_uid,json=nodeUid,proto3,oneof" json:"node_uid,omitempty"` + StartTime *timestamppb.Timestamp `protobuf:"bytes,5,opt,name=start_time,json=startTime,proto3" json:"start_time,omitempty"` + EndTime *timestamppb.Timestamp `protobuf:"bytes,6,opt,name=end_time,json=endTime,proto3" json:"end_time,omitempty"` } -func (x *Container) Reset() { - *x = Container{} +func (x *GetNodeGPUMigInstanceTimeseriesRequest) Reset() { + *x = GetNodeGPUMigInstanceTimeseriesRequest{} if protoimpl.UnsafeEnabled { - mi := &file_api_v1_k8s_proto_msgTypes[78] + mi := &file_api_v1_k8s_proto_msgTypes[81] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } -func (x *Container) String() string { +func (x *GetNodeGPUMigInstanceTimeseriesRequest) String() string { return protoimpl.X.MessageStringOf(x) } -func (*Container) ProtoMessage() {} +func (*GetNodeGPUMigInstanceTimeseriesRequest) ProtoMessage() {} -func (x *Container) ProtoReflect() protoreflect.Message { - mi := &file_api_v1_k8s_proto_msgTypes[78] +func (x *GetNodeGPUMigInstanceTimeseriesRequest) ProtoReflect() protoreflect.Message { + mi := &file_api_v1_k8s_proto_msgTypes[81] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6107,55 +6749,85 @@ func (x *Container) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use Container.ProtoReflect.Descriptor instead. -func (*Container) Descriptor() ([]byte, []int) { - return file_api_v1_k8s_proto_rawDescGZIP(), []int{78} +// Deprecated: Use GetNodeGPUMigInstanceTimeseriesRequest.ProtoReflect.Descriptor instead. +func (*GetNodeGPUMigInstanceTimeseriesRequest) Descriptor() ([]byte, []int) { + return file_api_v1_k8s_proto_rawDescGZIP(), []int{81} } -func (x *Container) GetId() string { +func (x *GetNodeGPUMigInstanceTimeseriesRequest) GetTeamId() string { if x != nil { - return x.Id + return x.TeamId } return "" } -func (x *Container) GetName() string { +func (x *GetNodeGPUMigInstanceTimeseriesRequest) GetClusterId() string { if x != nil { - return x.Name + return x.ClusterId } return "" } -func (x *Container) GetResourceMetrics() *ResourceMetrics { +func (x *GetNodeGPUMigInstanceTimeseriesRequest) GetNodeId() string { + if x != nil && x.NodeId != nil { + return *x.NodeId + } + return "" +} + +func (x *GetNodeGPUMigInstanceTimeseriesRequest) GetNodeUid() string { + if x != nil && x.NodeUid != nil { + return *x.NodeUid + } + return "" +} + +func (x *GetNodeGPUMigInstanceTimeseriesRequest) GetStartTime() *timestamppb.Timestamp { if x != nil { - return x.ResourceMetrics + return x.StartTime } return nil } -type GetLatestOperatorVersionRequest struct { +func (x *GetNodeGPUMigInstanceTimeseriesRequest) GetEndTime() *timestamppb.Timestamp { + if x != nil { + return x.EndTime + } + return nil +} + +// GPUMigInstanceDataPoint is one timestamped sample of a single MIG +// instance's utilization. +type GPUMigInstanceDataPoint struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields + + Timestamp *timestamppb.Timestamp `protobuf:"bytes,1,opt,name=timestamp,proto3" json:"timestamp,omitempty"` + TensorActive float64 `protobuf:"fixed64,2,opt,name=tensor_active,json=tensorActive,proto3" json:"tensor_active,omitempty"` + DramActive float64 `protobuf:"fixed64,3,opt,name=dram_active,json=dramActive,proto3" json:"dram_active,omitempty"` + GraphicsEngineActive float64 `protobuf:"fixed64,4,opt,name=graphics_engine_active,json=graphicsEngineActive,proto3" json:"graphics_engine_active,omitempty"` + FramebufferUsed float64 `protobuf:"fixed64,5,opt,name=framebuffer_used,json=framebufferUsed,proto3" json:"framebuffer_used,omitempty"` + FramebufferTotal float64 `protobuf:"fixed64,6,opt,name=framebuffer_total,json=framebufferTotal,proto3" json:"framebuffer_total,omitempty"` } -func (x *GetLatestOperatorVersionRequest) Reset() { - *x = GetLatestOperatorVersionRequest{} +func (x *GPUMigInstanceDataPoint) Reset() { + *x = GPUMigInstanceDataPoint{} if protoimpl.UnsafeEnabled { - mi := &file_api_v1_k8s_proto_msgTypes[79] + mi := &file_api_v1_k8s_proto_msgTypes[82] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } -func (x *GetLatestOperatorVersionRequest) String() string { +func (x *GPUMigInstanceDataPoint) String() string { return protoimpl.X.MessageStringOf(x) } -func (*GetLatestOperatorVersionRequest) ProtoMessage() {} +func (*GPUMigInstanceDataPoint) ProtoMessage() {} -func (x *GetLatestOperatorVersionRequest) ProtoReflect() protoreflect.Message { - mi := &file_api_v1_k8s_proto_msgTypes[79] +func (x *GPUMigInstanceDataPoint) ProtoReflect() protoreflect.Message { + mi := &file_api_v1_k8s_proto_msgTypes[82] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6166,200 +6838,161 @@ func (x *GetLatestOperatorVersionRequest) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use GetLatestOperatorVersionRequest.ProtoReflect.Descriptor instead. -func (*GetLatestOperatorVersionRequest) Descriptor() ([]byte, []int) { - return file_api_v1_k8s_proto_rawDescGZIP(), []int{79} +// Deprecated: Use GPUMigInstanceDataPoint.ProtoReflect.Descriptor instead. +func (*GPUMigInstanceDataPoint) Descriptor() ([]byte, []int) { + return file_api_v1_k8s_proto_rawDescGZIP(), []int{82} } -type GetLatestOperatorVersionResponse struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields +func (x *GPUMigInstanceDataPoint) GetTimestamp() *timestamppb.Timestamp { + if x != nil { + return x.Timestamp + } + return nil +} - ZxpInfo *OperatorInfo `protobuf:"bytes,1,opt,name=zxp_info,json=zxpInfo,proto3" json:"zxp_info,omitempty"` - ZxpHelmInfo *OperatorInfo `protobuf:"bytes,2,opt,name=zxp_helm_info,json=zxpHelmInfo,proto3" json:"zxp_helm_info,omitempty"` - ZxpNetmonInfo *OperatorInfo `protobuf:"bytes,3,opt,name=zxp_netmon_info,json=zxpNetmonInfo,proto3" json:"zxp_netmon_info,omitempty"` - ZxpNetmonHelmInfo *OperatorInfo `protobuf:"bytes,4,opt,name=zxp_netmon_helm_info,json=zxpNetmonHelmInfo,proto3" json:"zxp_netmon_helm_info,omitempty"` - DakrOpInfo *OperatorInfo `protobuf:"bytes,11,opt,name=dakr_op_info,json=dakrOpInfo,proto3" json:"dakr_op_info,omitempty"` - DakrOpHelmInfo *OperatorInfo `protobuf:"bytes,12,opt,name=dakr_op_helm_info,json=dakrOpHelmInfo,proto3" json:"dakr_op_helm_info,omitempty"` - NodeOpInfoAws *OperatorInfo `protobuf:"bytes,21,opt,name=node_op_info_aws,json=nodeOpInfoAws,proto3" json:"node_op_info_aws,omitempty"` - NodeOpInfoAzure *OperatorInfo `protobuf:"bytes,22,opt,name=node_op_info_azure,json=nodeOpInfoAzure,proto3" json:"node_op_info_azure,omitempty"` - NodeOpInfoGcp *OperatorInfo `protobuf:"bytes,23,opt,name=node_op_info_gcp,json=nodeOpInfoGcp,proto3" json:"node_op_info_gcp,omitempty"` - NodeOpInfoOci *OperatorInfo `protobuf:"bytes,24,opt,name=node_op_info_oci,json=nodeOpInfoOci,proto3" json:"node_op_info_oci,omitempty"` - SecurityOpInfo *OperatorInfo `protobuf:"bytes,31,opt,name=security_op_info,json=securityOpInfo,proto3" json:"security_op_info,omitempty"` - SecurityOpHelmInfo *OperatorInfo `protobuf:"bytes,32,opt,name=security_op_helm_info,json=securityOpHelmInfo,proto3" json:"security_op_helm_info,omitempty"` - TrezrInfo *OperatorInfo `protobuf:"bytes,41,opt,name=trezr_info,json=trezrInfo,proto3" json:"trezr_info,omitempty"` - TrezrHelmInfo *OperatorInfo `protobuf:"bytes,42,opt,name=trezr_helm_info,json=trezrHelmInfo,proto3" json:"trezr_helm_info,omitempty"` - // checkpoint-restore-operator helm chart (snapshot policies). Placeholder - // version until the DevZero-authored chart is published. - SnapshotHelmInfo *OperatorInfo `protobuf:"bytes,51,opt,name=snapshot_helm_info,json=snapshotHelmInfo,proto3" json:"snapshot_helm_info,omitempty"` -} - -func (x *GetLatestOperatorVersionResponse) Reset() { - *x = GetLatestOperatorVersionResponse{} - if protoimpl.UnsafeEnabled { - mi := &file_api_v1_k8s_proto_msgTypes[80] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *GetLatestOperatorVersionResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*GetLatestOperatorVersionResponse) ProtoMessage() {} - -func (x *GetLatestOperatorVersionResponse) ProtoReflect() protoreflect.Message { - mi := &file_api_v1_k8s_proto_msgTypes[80] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use GetLatestOperatorVersionResponse.ProtoReflect.Descriptor instead. -func (*GetLatestOperatorVersionResponse) Descriptor() ([]byte, []int) { - return file_api_v1_k8s_proto_rawDescGZIP(), []int{80} -} - -func (x *GetLatestOperatorVersionResponse) GetZxpInfo() *OperatorInfo { +func (x *GPUMigInstanceDataPoint) GetTensorActive() float64 { if x != nil { - return x.ZxpInfo + return x.TensorActive } - return nil + return 0 } -func (x *GetLatestOperatorVersionResponse) GetZxpHelmInfo() *OperatorInfo { +func (x *GPUMigInstanceDataPoint) GetDramActive() float64 { if x != nil { - return x.ZxpHelmInfo + return x.DramActive } - return nil + return 0 } -func (x *GetLatestOperatorVersionResponse) GetZxpNetmonInfo() *OperatorInfo { +func (x *GPUMigInstanceDataPoint) GetGraphicsEngineActive() float64 { if x != nil { - return x.ZxpNetmonInfo + return x.GraphicsEngineActive } - return nil + return 0 } -func (x *GetLatestOperatorVersionResponse) GetZxpNetmonHelmInfo() *OperatorInfo { +func (x *GPUMigInstanceDataPoint) GetFramebufferUsed() float64 { if x != nil { - return x.ZxpNetmonHelmInfo + return x.FramebufferUsed } - return nil + return 0 } -func (x *GetLatestOperatorVersionResponse) GetDakrOpInfo() *OperatorInfo { +func (x *GPUMigInstanceDataPoint) GetFramebufferTotal() float64 { if x != nil { - return x.DakrOpInfo + return x.FramebufferTotal } - return nil + return 0 } -func (x *GetLatestOperatorVersionResponse) GetDakrOpHelmInfo() *OperatorInfo { - if x != nil { - return x.DakrOpHelmInfo - } - return nil +// GPUMigInstanceTimeseries is one MIG instance's identity plus its +// utilization samples over the requested time range. +type GPUMigInstanceTimeseries struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + MigInstanceId string `protobuf:"bytes,1,opt,name=mig_instance_id,json=migInstanceId,proto3" json:"mig_instance_id,omitempty"` + MigProfile string `protobuf:"bytes,2,opt,name=mig_profile,json=migProfile,proto3" json:"mig_profile,omitempty"` + DeviceUuid string `protobuf:"bytes,3,opt,name=device_uuid,json=deviceUuid,proto3" json:"device_uuid,omitempty"` + ModelName string `protobuf:"bytes,4,opt,name=model_name,json=modelName,proto3" json:"model_name,omitempty"` + Points []*GPUMigInstanceDataPoint `protobuf:"bytes,5,rep,name=points,proto3" json:"points,omitempty"` } -func (x *GetLatestOperatorVersionResponse) GetNodeOpInfoAws() *OperatorInfo { - if x != nil { - return x.NodeOpInfoAws +func (x *GPUMigInstanceTimeseries) Reset() { + *x = GPUMigInstanceTimeseries{} + if protoimpl.UnsafeEnabled { + mi := &file_api_v1_k8s_proto_msgTypes[83] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } - return nil } -func (x *GetLatestOperatorVersionResponse) GetNodeOpInfoAzure() *OperatorInfo { - if x != nil { - return x.NodeOpInfoAzure - } - return nil +func (x *GPUMigInstanceTimeseries) String() string { + return protoimpl.X.MessageStringOf(x) } -func (x *GetLatestOperatorVersionResponse) GetNodeOpInfoGcp() *OperatorInfo { - if x != nil { - return x.NodeOpInfoGcp +func (*GPUMigInstanceTimeseries) ProtoMessage() {} + +func (x *GPUMigInstanceTimeseries) ProtoReflect() protoreflect.Message { + mi := &file_api_v1_k8s_proto_msgTypes[83] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms } - return nil + return mi.MessageOf(x) } -func (x *GetLatestOperatorVersionResponse) GetNodeOpInfoOci() *OperatorInfo { - if x != nil { - return x.NodeOpInfoOci - } - return nil +// Deprecated: Use GPUMigInstanceTimeseries.ProtoReflect.Descriptor instead. +func (*GPUMigInstanceTimeseries) Descriptor() ([]byte, []int) { + return file_api_v1_k8s_proto_rawDescGZIP(), []int{83} } -func (x *GetLatestOperatorVersionResponse) GetSecurityOpInfo() *OperatorInfo { +func (x *GPUMigInstanceTimeseries) GetMigInstanceId() string { if x != nil { - return x.SecurityOpInfo + return x.MigInstanceId } - return nil + return "" } -func (x *GetLatestOperatorVersionResponse) GetSecurityOpHelmInfo() *OperatorInfo { +func (x *GPUMigInstanceTimeseries) GetMigProfile() string { if x != nil { - return x.SecurityOpHelmInfo + return x.MigProfile } - return nil + return "" } -func (x *GetLatestOperatorVersionResponse) GetTrezrInfo() *OperatorInfo { +func (x *GPUMigInstanceTimeseries) GetDeviceUuid() string { if x != nil { - return x.TrezrInfo + return x.DeviceUuid } - return nil + return "" } -func (x *GetLatestOperatorVersionResponse) GetTrezrHelmInfo() *OperatorInfo { +func (x *GPUMigInstanceTimeseries) GetModelName() string { if x != nil { - return x.TrezrHelmInfo + return x.ModelName } - return nil + return "" } -func (x *GetLatestOperatorVersionResponse) GetSnapshotHelmInfo() *OperatorInfo { +func (x *GPUMigInstanceTimeseries) GetPoints() []*GPUMigInstanceDataPoint { if x != nil { - return x.SnapshotHelmInfo + return x.Points } return nil } -type GalaxyGetWorkloadPerspectiveRequest struct { +// GetNodeGPUMigInstanceTimeseriesResponse returns one series per MIG +// instance present on the node during the requested range. +type GetNodeGPUMigInstanceTimeseriesResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - TeamId string `protobuf:"bytes,1,opt,name=team_id,json=teamId,proto3" json:"team_id,omitempty"` - ClusterIds []string `protobuf:"bytes,2,rep,name=cluster_ids,json=clusterIds,proto3" json:"cluster_ids,omitempty"` // (optional) filter by cluster(s) - GroupBy GalaxyPOVWorkloadGroupBy `protobuf:"varint,3,opt,name=group_by,json=groupBy,proto3,enum=api.v1.GalaxyPOVWorkloadGroupBy" json:"group_by,omitempty"` // grouping type (e.g., namespace, cost, etc) - DatapointOpts *PerspectiveDatapointOpts `protobuf:"bytes,4,opt,name=datapoint_opts,json=datapointOpts,proto3" json:"datapoint_opts,omitempty"` // time range / resolution + Instances []*GPUMigInstanceTimeseries `protobuf:"bytes,1,rep,name=instances,proto3" json:"instances,omitempty"` } -func (x *GalaxyGetWorkloadPerspectiveRequest) Reset() { - *x = GalaxyGetWorkloadPerspectiveRequest{} +func (x *GetNodeGPUMigInstanceTimeseriesResponse) Reset() { + *x = GetNodeGPUMigInstanceTimeseriesResponse{} if protoimpl.UnsafeEnabled { - mi := &file_api_v1_k8s_proto_msgTypes[81] + mi := &file_api_v1_k8s_proto_msgTypes[84] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } -func (x *GalaxyGetWorkloadPerspectiveRequest) String() string { +func (x *GetNodeGPUMigInstanceTimeseriesResponse) String() string { return protoimpl.X.MessageStringOf(x) } -func (*GalaxyGetWorkloadPerspectiveRequest) ProtoMessage() {} +func (*GetNodeGPUMigInstanceTimeseriesResponse) ProtoMessage() {} -func (x *GalaxyGetWorkloadPerspectiveRequest) ProtoReflect() protoreflect.Message { - mi := &file_api_v1_k8s_proto_msgTypes[81] +func (x *GetNodeGPUMigInstanceTimeseriesResponse) ProtoReflect() protoreflect.Message { + mi := &file_api_v1_k8s_proto_msgTypes[84] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6370,66 +7003,46 @@ func (x *GalaxyGetWorkloadPerspectiveRequest) ProtoReflect() protoreflect.Messag return mi.MessageOf(x) } -// Deprecated: Use GalaxyGetWorkloadPerspectiveRequest.ProtoReflect.Descriptor instead. -func (*GalaxyGetWorkloadPerspectiveRequest) Descriptor() ([]byte, []int) { - return file_api_v1_k8s_proto_rawDescGZIP(), []int{81} -} - -func (x *GalaxyGetWorkloadPerspectiveRequest) GetTeamId() string { - if x != nil { - return x.TeamId - } - return "" -} - -func (x *GalaxyGetWorkloadPerspectiveRequest) GetClusterIds() []string { - if x != nil { - return x.ClusterIds - } - return nil -} - -func (x *GalaxyGetWorkloadPerspectiveRequest) GetGroupBy() GalaxyPOVWorkloadGroupBy { - if x != nil { - return x.GroupBy - } - return GalaxyPOVWorkloadGroupBy_GALAXY_POV_WORKLOAD_GROUP_BY_UNSPECIFIED +// Deprecated: Use GetNodeGPUMigInstanceTimeseriesResponse.ProtoReflect.Descriptor instead. +func (*GetNodeGPUMigInstanceTimeseriesResponse) Descriptor() ([]byte, []int) { + return file_api_v1_k8s_proto_rawDescGZIP(), []int{84} } -func (x *GalaxyGetWorkloadPerspectiveRequest) GetDatapointOpts() *PerspectiveDatapointOpts { +func (x *GetNodeGPUMigInstanceTimeseriesResponse) GetInstances() []*GPUMigInstanceTimeseries { if x != nil { - return x.DatapointOpts + return x.Instances } return nil } -type GalaxyGetWorkloadPerspectiveResponse struct { +// GetContainerIndividualGPUMetricsRequest requests the most recent per-GPU breakdown per container. +type GetContainerIndividualGPUMetricsRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Groupings []*GalaxyWorkloadGroup `protobuf:"bytes,1,rep,name=groupings,proto3" json:"groupings,omitempty"` - Clusters map[string]*Cluster `protobuf:"bytes,2,rep,name=clusters,proto3" json:"clusters,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` // cluster_id -> cluster details for all clusters referenced in groupings - Workloads map[string]*WorkloadItem `protobuf:"bytes,3,rep,name=workloads,proto3" json:"workloads,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` // workload_uid -> workload details for all workloads referenced in groupings + TeamId string `protobuf:"bytes,1,opt,name=team_id,json=teamId,proto3" json:"team_id,omitempty"` + ClusterId string `protobuf:"bytes,2,opt,name=cluster_id,json=clusterId,proto3" json:"cluster_id,omitempty"` + Uid string `protobuf:"bytes,3,opt,name=uid,proto3" json:"uid,omitempty"` } -func (x *GalaxyGetWorkloadPerspectiveResponse) Reset() { - *x = GalaxyGetWorkloadPerspectiveResponse{} +func (x *GetContainerIndividualGPUMetricsRequest) Reset() { + *x = GetContainerIndividualGPUMetricsRequest{} if protoimpl.UnsafeEnabled { - mi := &file_api_v1_k8s_proto_msgTypes[82] + mi := &file_api_v1_k8s_proto_msgTypes[85] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } -func (x *GalaxyGetWorkloadPerspectiveResponse) String() string { +func (x *GetContainerIndividualGPUMetricsRequest) String() string { return protoimpl.X.MessageStringOf(x) } -func (*GalaxyGetWorkloadPerspectiveResponse) ProtoMessage() {} +func (*GetContainerIndividualGPUMetricsRequest) ProtoMessage() {} -func (x *GalaxyGetWorkloadPerspectiveResponse) ProtoReflect() protoreflect.Message { - mi := &file_api_v1_k8s_proto_msgTypes[82] +func (x *GetContainerIndividualGPUMetricsRequest) ProtoReflect() protoreflect.Message { + mi := &file_api_v1_k8s_proto_msgTypes[85] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6440,64 +7053,58 @@ func (x *GalaxyGetWorkloadPerspectiveResponse) ProtoReflect() protoreflect.Messa return mi.MessageOf(x) } -// Deprecated: Use GalaxyGetWorkloadPerspectiveResponse.ProtoReflect.Descriptor instead. -func (*GalaxyGetWorkloadPerspectiveResponse) Descriptor() ([]byte, []int) { - return file_api_v1_k8s_proto_rawDescGZIP(), []int{82} +// Deprecated: Use GetContainerIndividualGPUMetricsRequest.ProtoReflect.Descriptor instead. +func (*GetContainerIndividualGPUMetricsRequest) Descriptor() ([]byte, []int) { + return file_api_v1_k8s_proto_rawDescGZIP(), []int{85} } -func (x *GalaxyGetWorkloadPerspectiveResponse) GetGroupings() []*GalaxyWorkloadGroup { +func (x *GetContainerIndividualGPUMetricsRequest) GetTeamId() string { if x != nil { - return x.Groupings + return x.TeamId } - return nil + return "" } -func (x *GalaxyGetWorkloadPerspectiveResponse) GetClusters() map[string]*Cluster { +func (x *GetContainerIndividualGPUMetricsRequest) GetClusterId() string { if x != nil { - return x.Clusters + return x.ClusterId } - return nil + return "" } -func (x *GalaxyGetWorkloadPerspectiveResponse) GetWorkloads() map[string]*WorkloadItem { +func (x *GetContainerIndividualGPUMetricsRequest) GetUid() string { if x != nil { - return x.Workloads + return x.Uid } - return nil + return "" } -type GalaxyWorkloadGroup struct { +// GetContainerIndividualGPUMetricsResponse returns per-container per-GPU breakdowns. +type GetContainerIndividualGPUMetricsResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - GroupKey string `protobuf:"bytes,1,opt,name=group_key,json=groupKey,proto3" json:"group_key,omitempty"` // e.g., "namespace-a", "Deployment", "high-priority" - // TODO (debo) might want to change workload_uids to just workload (same as GetWorkloads etc) - WorkloadUids []string `protobuf:"bytes,2,rep,name=workload_uids,json=workloadUids,proto3" json:"workload_uids,omitempty"` // workload UIDs in this group - // TODO (debo) might want to change cluster_ids to just cluster (same as GetClusters etc) - ClusterIds []string `protobuf:"bytes,3,rep,name=cluster_ids,json=clusterIds,proto3" json:"cluster_ids,omitempty"` // clusters these workloads belong to - CostInfo *CostInfo `protobuf:"bytes,4,opt,name=cost_info,json=costInfo,proto3" json:"cost_info,omitempty"` - ResourceMetrics *ResourceMetrics `protobuf:"bytes,5,opt,name=resource_metrics,json=resourceMetrics,proto3" json:"resource_metrics,omitempty"` - ResourceSummary *ResourceSummary `protobuf:"bytes,6,opt,name=resource_summary,json=resourceSummary,proto3" json:"resource_summary,omitempty"` // (optional) aggregated resource kind/status info + Containers []*ContainerIndividualGPUMetrics `protobuf:"bytes,1,rep,name=containers,proto3" json:"containers,omitempty"` } -func (x *GalaxyWorkloadGroup) Reset() { - *x = GalaxyWorkloadGroup{} +func (x *GetContainerIndividualGPUMetricsResponse) Reset() { + *x = GetContainerIndividualGPUMetricsResponse{} if protoimpl.UnsafeEnabled { - mi := &file_api_v1_k8s_proto_msgTypes[83] + mi := &file_api_v1_k8s_proto_msgTypes[86] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } -func (x *GalaxyWorkloadGroup) String() string { +func (x *GetContainerIndividualGPUMetricsResponse) String() string { return protoimpl.X.MessageStringOf(x) } -func (*GalaxyWorkloadGroup) ProtoMessage() {} +func (*GetContainerIndividualGPUMetricsResponse) ProtoMessage() {} -func (x *GalaxyWorkloadGroup) ProtoReflect() protoreflect.Message { - mi := &file_api_v1_k8s_proto_msgTypes[83] +func (x *GetContainerIndividualGPUMetricsResponse) ProtoReflect() protoreflect.Message { + mi := &file_api_v1_k8s_proto_msgTypes[86] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6508,80 +7115,46 @@ func (x *GalaxyWorkloadGroup) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use GalaxyWorkloadGroup.ProtoReflect.Descriptor instead. -func (*GalaxyWorkloadGroup) Descriptor() ([]byte, []int) { - return file_api_v1_k8s_proto_rawDescGZIP(), []int{83} -} - -func (x *GalaxyWorkloadGroup) GetGroupKey() string { - if x != nil { - return x.GroupKey - } - return "" +// Deprecated: Use GetContainerIndividualGPUMetricsResponse.ProtoReflect.Descriptor instead. +func (*GetContainerIndividualGPUMetricsResponse) Descriptor() ([]byte, []int) { + return file_api_v1_k8s_proto_rawDescGZIP(), []int{86} } -func (x *GalaxyWorkloadGroup) GetWorkloadUids() []string { +func (x *GetContainerIndividualGPUMetricsResponse) GetContainers() []*ContainerIndividualGPUMetrics { if x != nil { - return x.WorkloadUids + return x.Containers } return nil } -func (x *GalaxyWorkloadGroup) GetClusterIds() []string { - if x != nil { - return x.ClusterIds - } - return nil -} - -func (x *GalaxyWorkloadGroup) GetCostInfo() *CostInfo { - if x != nil { - return x.CostInfo - } - return nil -} - -func (x *GalaxyWorkloadGroup) GetResourceMetrics() *ResourceMetrics { - if x != nil { - return x.ResourceMetrics - } - return nil -} - -func (x *GalaxyWorkloadGroup) GetResourceSummary() *ResourceSummary { - if x != nil { - return x.ResourceSummary - } - return nil -} - -type PerspectiveDatapointOpts struct { +// LanguageVersions holds every distinct version detected for one language +// across a cluster's workloads. +type LanguageVersions struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - TimeStart int64 `protobuf:"varint,1,opt,name=time_start,json=timeStart,proto3" json:"time_start,omitempty"` - TimeStop int64 `protobuf:"varint,2,opt,name=time_stop,json=timeStop,proto3" json:"time_stop,omitempty"` - NumDatapointsInTimePeriod int64 `protobuf:"varint,3,opt,name=num_datapoints_in_time_period,json=numDatapointsInTimePeriod,proto3" json:"num_datapoints_in_time_period,omitempty"` + Language string `protobuf:"bytes,1,opt,name=language,proto3" json:"language,omitempty"` + Versions []string `protobuf:"bytes,2,rep,name=versions,proto3" json:"versions,omitempty"` } -func (x *PerspectiveDatapointOpts) Reset() { - *x = PerspectiveDatapointOpts{} +func (x *LanguageVersions) Reset() { + *x = LanguageVersions{} if protoimpl.UnsafeEnabled { - mi := &file_api_v1_k8s_proto_msgTypes[84] + mi := &file_api_v1_k8s_proto_msgTypes[87] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } -func (x *PerspectiveDatapointOpts) String() string { +func (x *LanguageVersions) String() string { return protoimpl.X.MessageStringOf(x) } -func (*PerspectiveDatapointOpts) ProtoMessage() {} +func (*LanguageVersions) ProtoMessage() {} -func (x *PerspectiveDatapointOpts) ProtoReflect() protoreflect.Message { - mi := &file_api_v1_k8s_proto_msgTypes[84] +func (x *LanguageVersions) ProtoReflect() protoreflect.Message { + mi := &file_api_v1_k8s_proto_msgTypes[87] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6592,73 +7165,53 @@ func (x *PerspectiveDatapointOpts) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use PerspectiveDatapointOpts.ProtoReflect.Descriptor instead. -func (*PerspectiveDatapointOpts) Descriptor() ([]byte, []int) { - return file_api_v1_k8s_proto_rawDescGZIP(), []int{84} -} - -func (x *PerspectiveDatapointOpts) GetTimeStart() int64 { - if x != nil { - return x.TimeStart - } - return 0 +// Deprecated: Use LanguageVersions.ProtoReflect.Descriptor instead. +func (*LanguageVersions) Descriptor() ([]byte, []int) { + return file_api_v1_k8s_proto_rawDescGZIP(), []int{87} } -func (x *PerspectiveDatapointOpts) GetTimeStop() int64 { +func (x *LanguageVersions) GetLanguage() string { if x != nil { - return x.TimeStop + return x.Language } - return 0 + return "" } -func (x *PerspectiveDatapointOpts) GetNumDatapointsInTimePeriod() int64 { +func (x *LanguageVersions) GetVersions() []string { if x != nil { - return x.NumDatapointsInTimePeriod + return x.Versions } - return 0 + return nil } -// Request for listing audit logs -type ListAuditLogsRequest struct { +// GetWorkloadLanguagesRequest requests the distinct languages/versions +// detected across a cluster's workloads (used to populate filter dropdowns). +type GetWorkloadLanguagesRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - TeamId string `protobuf:"bytes,1,opt,name=team_id,json=teamId,proto3" json:"team_id,omitempty"` - ClusterId []string `protobuf:"bytes,2,rep,name=cluster_id,json=clusterId,proto3" json:"cluster_id,omitempty"` - NodeId []string `protobuf:"bytes,3,rep,name=node_id,json=nodeId,proto3" json:"node_id,omitempty"` - WorkloadId []string `protobuf:"bytes,4,rep,name=workload_id,json=workloadId,proto3" json:"workload_id,omitempty"` - WorkloadType []K8SObjectKind `protobuf:"varint,5,rep,packed,name=workload_type,json=workloadType,proto3,enum=api.v1.K8SObjectKind" json:"workload_type,omitempty"` - RecommendationPolicyId []string `protobuf:"bytes,6,rep,name=recommendation_policy_id,json=recommendationPolicyId,proto3" json:"recommendation_policy_id,omitempty"` - RecommendationId []string `protobuf:"bytes,7,rep,name=recommendation_id,json=recommendationId,proto3" json:"recommendation_id,omitempty"` - OriginatingUserId []string `protobuf:"bytes,8,rep,name=originating_user_id,json=originatingUserId,proto3" json:"originating_user_id,omitempty"` - // Deprecated: Marked as deprecated in api/v1/k8s.proto. - EmailContains *string `protobuf:"bytes,9,opt,name=email_contains,json=emailContains,proto3,oneof" json:"email_contains,omitempty"` - Event []string `protobuf:"bytes,10,rep,name=event,proto3" json:"event,omitempty"` - StartTime *timestamppb.Timestamp `protobuf:"bytes,11,opt,name=start_time,json=startTime,proto3" json:"start_time,omitempty"` - EndTime *timestamppb.Timestamp `protobuf:"bytes,12,opt,name=end_time,json=endTime,proto3" json:"end_time,omitempty"` - EmailMatches []string `protobuf:"bytes,13,rep,name=email_matches,json=emailMatches,proto3" json:"email_matches,omitempty"` // Exact match on originating_user_email (equality with IN clause) - Pagination *Pagination `protobuf:"bytes,21,opt,name=pagination,proto3" json:"pagination,omitempty"` - AuditLogIds []string `protobuf:"bytes,31,rep,name=audit_log_ids,json=auditLogIds,proto3" json:"audit_log_ids,omitempty"` + TeamId string `protobuf:"bytes,1,opt,name=team_id,json=teamId,proto3" json:"team_id,omitempty"` + ClusterId string `protobuf:"bytes,2,opt,name=cluster_id,json=clusterId,proto3" json:"cluster_id,omitempty"` } -func (x *ListAuditLogsRequest) Reset() { - *x = ListAuditLogsRequest{} +func (x *GetWorkloadLanguagesRequest) Reset() { + *x = GetWorkloadLanguagesRequest{} if protoimpl.UnsafeEnabled { - mi := &file_api_v1_k8s_proto_msgTypes[85] + mi := &file_api_v1_k8s_proto_msgTypes[88] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } -func (x *ListAuditLogsRequest) String() string { +func (x *GetWorkloadLanguagesRequest) String() string { return protoimpl.X.MessageStringOf(x) } -func (*ListAuditLogsRequest) ProtoMessage() {} +func (*GetWorkloadLanguagesRequest) ProtoMessage() {} -func (x *ListAuditLogsRequest) ProtoReflect() protoreflect.Message { - mi := &file_api_v1_k8s_proto_msgTypes[85] +func (x *GetWorkloadLanguagesRequest) ProtoReflect() protoreflect.Message { + mi := &file_api_v1_k8s_proto_msgTypes[88] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6669,144 +7222,183 @@ func (x *ListAuditLogsRequest) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use ListAuditLogsRequest.ProtoReflect.Descriptor instead. -func (*ListAuditLogsRequest) Descriptor() ([]byte, []int) { - return file_api_v1_k8s_proto_rawDescGZIP(), []int{85} +// Deprecated: Use GetWorkloadLanguagesRequest.ProtoReflect.Descriptor instead. +func (*GetWorkloadLanguagesRequest) Descriptor() ([]byte, []int) { + return file_api_v1_k8s_proto_rawDescGZIP(), []int{88} } -func (x *ListAuditLogsRequest) GetTeamId() string { +func (x *GetWorkloadLanguagesRequest) GetTeamId() string { if x != nil { return x.TeamId } return "" } -func (x *ListAuditLogsRequest) GetClusterId() []string { +func (x *GetWorkloadLanguagesRequest) GetClusterId() string { if x != nil { return x.ClusterId } - return nil + return "" } -func (x *ListAuditLogsRequest) GetNodeId() []string { - if x != nil { - return x.NodeId - } - return nil +// GetWorkloadLanguagesResponse returns every distinct language and its +// detected versions for the requested cluster. +type GetWorkloadLanguagesResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Languages []*LanguageVersions `protobuf:"bytes,1,rep,name=languages,proto3" json:"languages,omitempty"` } -func (x *ListAuditLogsRequest) GetWorkloadId() []string { - if x != nil { - return x.WorkloadId +func (x *GetWorkloadLanguagesResponse) Reset() { + *x = GetWorkloadLanguagesResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_api_v1_k8s_proto_msgTypes[89] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } - return nil } -func (x *ListAuditLogsRequest) GetWorkloadType() []K8SObjectKind { - if x != nil { - return x.WorkloadType - } - return nil +func (x *GetWorkloadLanguagesResponse) String() string { + return protoimpl.X.MessageStringOf(x) } -func (x *ListAuditLogsRequest) GetRecommendationPolicyId() []string { - if x != nil { - return x.RecommendationPolicyId +func (*GetWorkloadLanguagesResponse) ProtoMessage() {} + +func (x *GetWorkloadLanguagesResponse) ProtoReflect() protoreflect.Message { + mi := &file_api_v1_k8s_proto_msgTypes[89] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms } - return nil + return mi.MessageOf(x) } -func (x *ListAuditLogsRequest) GetRecommendationId() []string { - if x != nil { - return x.RecommendationId - } - return nil +// Deprecated: Use GetWorkloadLanguagesResponse.ProtoReflect.Descriptor instead. +func (*GetWorkloadLanguagesResponse) Descriptor() ([]byte, []int) { + return file_api_v1_k8s_proto_rawDescGZIP(), []int{89} } -func (x *ListAuditLogsRequest) GetOriginatingUserId() []string { +func (x *GetWorkloadLanguagesResponse) GetLanguages() []*LanguageVersions { if x != nil { - return x.OriginatingUserId + return x.Languages } return nil } -// Deprecated: Marked as deprecated in api/v1/k8s.proto. -func (x *ListAuditLogsRequest) GetEmailContains() string { - if x != nil && x.EmailContains != nil { - return *x.EmailContains +// Request to get forecasted workloads +type GetForecastWorkloadsRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + TeamId string `protobuf:"bytes,1,opt,name=team_id,json=teamId,proto3" json:"team_id,omitempty"` + ClusterId string `protobuf:"bytes,2,opt,name=cluster_id,json=clusterId,proto3" json:"cluster_id,omitempty"` + ShowDeleted bool `protobuf:"varint,12,opt,name=show_deleted,json=showDeleted,proto3" json:"show_deleted,omitempty"` + StartTime *timestamppb.Timestamp `protobuf:"bytes,21,opt,name=start_time,json=startTime,proto3,oneof" json:"start_time,omitempty"` + EndTime *timestamppb.Timestamp `protobuf:"bytes,22,opt,name=end_time,json=endTime,proto3,oneof" json:"end_time,omitempty"` +} + +func (x *GetForecastWorkloadsRequest) Reset() { + *x = GetForecastWorkloadsRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_api_v1_k8s_proto_msgTypes[90] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } - return "" } -func (x *ListAuditLogsRequest) GetEvent() []string { - if x != nil { - return x.Event +func (x *GetForecastWorkloadsRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetForecastWorkloadsRequest) ProtoMessage() {} + +func (x *GetForecastWorkloadsRequest) ProtoReflect() protoreflect.Message { + mi := &file_api_v1_k8s_proto_msgTypes[90] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms } - return nil + return mi.MessageOf(x) } -func (x *ListAuditLogsRequest) GetStartTime() *timestamppb.Timestamp { +// Deprecated: Use GetForecastWorkloadsRequest.ProtoReflect.Descriptor instead. +func (*GetForecastWorkloadsRequest) Descriptor() ([]byte, []int) { + return file_api_v1_k8s_proto_rawDescGZIP(), []int{90} +} + +func (x *GetForecastWorkloadsRequest) GetTeamId() string { if x != nil { - return x.StartTime + return x.TeamId } - return nil + return "" } -func (x *ListAuditLogsRequest) GetEndTime() *timestamppb.Timestamp { +func (x *GetForecastWorkloadsRequest) GetClusterId() string { if x != nil { - return x.EndTime + return x.ClusterId } - return nil + return "" } -func (x *ListAuditLogsRequest) GetEmailMatches() []string { +func (x *GetForecastWorkloadsRequest) GetShowDeleted() bool { if x != nil { - return x.EmailMatches + return x.ShowDeleted } - return nil + return false } -func (x *ListAuditLogsRequest) GetPagination() *Pagination { +func (x *GetForecastWorkloadsRequest) GetStartTime() *timestamppb.Timestamp { if x != nil { - return x.Pagination + return x.StartTime } return nil } -func (x *ListAuditLogsRequest) GetAuditLogIds() []string { +func (x *GetForecastWorkloadsRequest) GetEndTime() *timestamppb.Timestamp { if x != nil { - return x.AuditLogIds + return x.EndTime } return nil } -// Response for listing audit logs -type ListAuditLogsResponse struct { +// Response to forecast workloads request +type GetForecastWorkloadsResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Logs []*AuditLogEntry `protobuf:"bytes,1,rep,name=logs,proto3" json:"logs,omitempty"` - Pagination *Pagination `protobuf:"bytes,21,opt,name=pagination,proto3" json:"pagination,omitempty"` + WorkloadItems []*WorkloadItem `protobuf:"bytes,1,rep,name=workload_items,json=workloadItems,proto3" json:"workload_items,omitempty"` // List of workload hierarchy items. + CostInfo *CostInfo `protobuf:"bytes,2,opt,name=cost_info,json=costInfo,proto3" json:"cost_info,omitempty"` // Aggregated cost information for workloads. + ResourceMetrics []*ForecastResourceMetrics `protobuf:"bytes,3,rep,name=resource_metrics,json=resourceMetrics,proto3" json:"resource_metrics,omitempty"` // List of forecasted resource metrics for the workload. + ResourceSummary *ResourceSummary `protobuf:"bytes,4,opt,name=resource_summary,json=resourceSummary,proto3" json:"resource_summary,omitempty"` // Summary of resource counts and pod statuses. } -func (x *ListAuditLogsResponse) Reset() { - *x = ListAuditLogsResponse{} +func (x *GetForecastWorkloadsResponse) Reset() { + *x = GetForecastWorkloadsResponse{} if protoimpl.UnsafeEnabled { - mi := &file_api_v1_k8s_proto_msgTypes[86] + mi := &file_api_v1_k8s_proto_msgTypes[91] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } -func (x *ListAuditLogsResponse) String() string { +func (x *GetForecastWorkloadsResponse) String() string { return protoimpl.X.MessageStringOf(x) } -func (*ListAuditLogsResponse) ProtoMessage() {} +func (*GetForecastWorkloadsResponse) ProtoMessage() {} -func (x *ListAuditLogsResponse) ProtoReflect() protoreflect.Message { - mi := &file_api_v1_k8s_proto_msgTypes[86] +func (x *GetForecastWorkloadsResponse) ProtoReflect() protoreflect.Message { + mi := &file_api_v1_k8s_proto_msgTypes[91] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6817,64 +7409,71 @@ func (x *ListAuditLogsResponse) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use ListAuditLogsResponse.ProtoReflect.Descriptor instead. -func (*ListAuditLogsResponse) Descriptor() ([]byte, []int) { - return file_api_v1_k8s_proto_rawDescGZIP(), []int{86} +// Deprecated: Use GetForecastWorkloadsResponse.ProtoReflect.Descriptor instead. +func (*GetForecastWorkloadsResponse) Descriptor() ([]byte, []int) { + return file_api_v1_k8s_proto_rawDescGZIP(), []int{91} } -func (x *ListAuditLogsResponse) GetLogs() []*AuditLogEntry { +func (x *GetForecastWorkloadsResponse) GetWorkloadItems() []*WorkloadItem { if x != nil { - return x.Logs + return x.WorkloadItems } return nil } -func (x *ListAuditLogsResponse) GetPagination() *Pagination { +func (x *GetForecastWorkloadsResponse) GetCostInfo() *CostInfo { if x != nil { - return x.Pagination + return x.CostInfo } return nil } -// Request for fetching all audit log originators. -type ListAuditLogOriginatorsRequest struct { +func (x *GetForecastWorkloadsResponse) GetResourceMetrics() []*ForecastResourceMetrics { + if x != nil { + return x.ResourceMetrics + } + return nil +} + +func (x *GetForecastWorkloadsResponse) GetResourceSummary() *ResourceSummary { + if x != nil { + return x.ResourceSummary + } + return nil +} + +// Request to get forecast for a specific workload +type GetForecastWorkloadRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - TeamId string `protobuf:"bytes,1,opt,name=team_id,json=teamId,proto3" json:"team_id,omitempty"` - ClusterId []string `protobuf:"bytes,2,rep,name=cluster_id,json=clusterId,proto3" json:"cluster_id,omitempty"` - NodeId []string `protobuf:"bytes,3,rep,name=node_id,json=nodeId,proto3" json:"node_id,omitempty"` - WorkloadId []string `protobuf:"bytes,4,rep,name=workload_id,json=workloadId,proto3" json:"workload_id,omitempty"` - WorkloadType []K8SObjectKind `protobuf:"varint,5,rep,packed,name=workload_type,json=workloadType,proto3,enum=api.v1.K8SObjectKind" json:"workload_type,omitempty"` - RecommendationPolicyId []string `protobuf:"bytes,6,rep,name=recommendation_policy_id,json=recommendationPolicyId,proto3" json:"recommendation_policy_id,omitempty"` - RecommendationId []string `protobuf:"bytes,7,rep,name=recommendation_id,json=recommendationId,proto3" json:"recommendation_id,omitempty"` - OriginatingUserId []string `protobuf:"bytes,8,rep,name=originating_user_id,json=originatingUserId,proto3" json:"originating_user_id,omitempty"` - // Deprecated: Marked as deprecated in api/v1/k8s.proto. - EmailContains *string `protobuf:"bytes,9,opt,name=email_contains,json=emailContains,proto3,oneof" json:"email_contains,omitempty"` - Event []string `protobuf:"bytes,10,rep,name=event,proto3" json:"event,omitempty"` - StartTime *timestamppb.Timestamp `protobuf:"bytes,11,opt,name=start_time,json=startTime,proto3" json:"start_time,omitempty"` - EndTime *timestamppb.Timestamp `protobuf:"bytes,12,opt,name=end_time,json=endTime,proto3" json:"end_time,omitempty"` - EmailMatches []string `protobuf:"bytes,13,rep,name=email_matches,json=emailMatches,proto3" json:"email_matches,omitempty"` // Exact match on originating_user_email (equality with IN clause) + TeamId string `protobuf:"bytes,1,opt,name=team_id,json=teamId,proto3" json:"team_id,omitempty"` // Unique identifier for the team. + ClusterId string `protobuf:"bytes,2,opt,name=cluster_id,json=clusterId,proto3" json:"cluster_id,omitempty"` // Unique identifier for the cluster. + Kind string `protobuf:"bytes,3,opt,name=kind,proto3" json:"kind,omitempty"` // Kind of the workload (e.g., Deployment, StatefulSet). + Uid string `protobuf:"bytes,4,opt,name=uid,proto3" json:"uid,omitempty"` // Unique identifier for the workload. + IncludeDeleted bool `protobuf:"varint,11,opt,name=include_deleted,json=includeDeleted,proto3" json:"include_deleted,omitempty"` + StartTime *timestamppb.Timestamp `protobuf:"bytes,21,opt,name=start_time,json=startTime,proto3,oneof" json:"start_time,omitempty"` + EndTime *timestamppb.Timestamp `protobuf:"bytes,22,opt,name=end_time,json=endTime,proto3,oneof" json:"end_time,omitempty"` } -func (x *ListAuditLogOriginatorsRequest) Reset() { - *x = ListAuditLogOriginatorsRequest{} +func (x *GetForecastWorkloadRequest) Reset() { + *x = GetForecastWorkloadRequest{} if protoimpl.UnsafeEnabled { - mi := &file_api_v1_k8s_proto_msgTypes[87] + mi := &file_api_v1_k8s_proto_msgTypes[92] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } -func (x *ListAuditLogOriginatorsRequest) String() string { +func (x *GetForecastWorkloadRequest) String() string { return protoimpl.X.MessageStringOf(x) } -func (*ListAuditLogOriginatorsRequest) ProtoMessage() {} +func (*GetForecastWorkloadRequest) ProtoMessage() {} -func (x *ListAuditLogOriginatorsRequest) ProtoReflect() protoreflect.Message { - mi := &file_api_v1_k8s_proto_msgTypes[87] +func (x *GetForecastWorkloadRequest) ProtoReflect() protoreflect.Message { + mi := &file_api_v1_k8s_proto_msgTypes[92] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6885,129 +7484,174 @@ func (x *ListAuditLogOriginatorsRequest) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use ListAuditLogOriginatorsRequest.ProtoReflect.Descriptor instead. -func (*ListAuditLogOriginatorsRequest) Descriptor() ([]byte, []int) { - return file_api_v1_k8s_proto_rawDescGZIP(), []int{87} +// Deprecated: Use GetForecastWorkloadRequest.ProtoReflect.Descriptor instead. +func (*GetForecastWorkloadRequest) Descriptor() ([]byte, []int) { + return file_api_v1_k8s_proto_rawDescGZIP(), []int{92} } -func (x *ListAuditLogOriginatorsRequest) GetTeamId() string { +func (x *GetForecastWorkloadRequest) GetTeamId() string { if x != nil { return x.TeamId } return "" } -func (x *ListAuditLogOriginatorsRequest) GetClusterId() []string { +func (x *GetForecastWorkloadRequest) GetClusterId() string { if x != nil { return x.ClusterId } - return nil + return "" } -func (x *ListAuditLogOriginatorsRequest) GetNodeId() []string { +func (x *GetForecastWorkloadRequest) GetKind() string { if x != nil { - return x.NodeId + return x.Kind } - return nil + return "" } -func (x *ListAuditLogOriginatorsRequest) GetWorkloadId() []string { +func (x *GetForecastWorkloadRequest) GetUid() string { if x != nil { - return x.WorkloadId + return x.Uid } - return nil + return "" } -func (x *ListAuditLogOriginatorsRequest) GetWorkloadType() []K8SObjectKind { +func (x *GetForecastWorkloadRequest) GetIncludeDeleted() bool { if x != nil { - return x.WorkloadType + return x.IncludeDeleted } - return nil + return false } -func (x *ListAuditLogOriginatorsRequest) GetRecommendationPolicyId() []string { +func (x *GetForecastWorkloadRequest) GetStartTime() *timestamppb.Timestamp { if x != nil { - return x.RecommendationPolicyId + return x.StartTime } return nil } -func (x *ListAuditLogOriginatorsRequest) GetRecommendationId() []string { +func (x *GetForecastWorkloadRequest) GetEndTime() *timestamppb.Timestamp { if x != nil { - return x.RecommendationId + return x.EndTime } return nil } -func (x *ListAuditLogOriginatorsRequest) GetOriginatingUserId() []string { +// Response containing forecasted metrics for a specific workload +type GetForecastWorkloadResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + WorkloadItem *WorkloadItem `protobuf:"bytes,1,opt,name=workload_item,json=workloadItem,proto3" json:"workload_item,omitempty"` // Details of the workload hierarchy. + CostInfo *CostInfo `protobuf:"bytes,2,opt,name=cost_info,json=costInfo,proto3" json:"cost_info,omitempty"` // Aggregated cost information for the workload. + ResourceMetrics []*ForecastResourceMetrics `protobuf:"bytes,3,rep,name=resource_metrics,json=resourceMetrics,proto3" json:"resource_metrics,omitempty"` // List of forecasted resource metrics for the workload. + ResourceSummary *ResourceSummary `protobuf:"bytes,4,opt,name=resource_summary,json=resourceSummary,proto3" json:"resource_summary,omitempty"` // Summary of resource counts and pod statuses for the workload. + CostDataPoints []*CostDataPoint `protobuf:"bytes,5,rep,name=cost_data_points,json=costDataPoints,proto3" json:"cost_data_points,omitempty"` // Time-series cost data points for the workload. + ResourceDataPoints []*ResourceDataPoint `protobuf:"bytes,6,rep,name=resource_data_points,json=resourceDataPoints,proto3" json:"resource_data_points,omitempty"` // Time-series resource metrics for the workload. +} + +func (x *GetForecastWorkloadResponse) Reset() { + *x = GetForecastWorkloadResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_api_v1_k8s_proto_msgTypes[93] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetForecastWorkloadResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetForecastWorkloadResponse) ProtoMessage() {} + +func (x *GetForecastWorkloadResponse) ProtoReflect() protoreflect.Message { + mi := &file_api_v1_k8s_proto_msgTypes[93] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetForecastWorkloadResponse.ProtoReflect.Descriptor instead. +func (*GetForecastWorkloadResponse) Descriptor() ([]byte, []int) { + return file_api_v1_k8s_proto_rawDescGZIP(), []int{93} +} + +func (x *GetForecastWorkloadResponse) GetWorkloadItem() *WorkloadItem { if x != nil { - return x.OriginatingUserId + return x.WorkloadItem } return nil } -// Deprecated: Marked as deprecated in api/v1/k8s.proto. -func (x *ListAuditLogOriginatorsRequest) GetEmailContains() string { - if x != nil && x.EmailContains != nil { - return *x.EmailContains +func (x *GetForecastWorkloadResponse) GetCostInfo() *CostInfo { + if x != nil { + return x.CostInfo } - return "" + return nil } -func (x *ListAuditLogOriginatorsRequest) GetEvent() []string { +func (x *GetForecastWorkloadResponse) GetResourceMetrics() []*ForecastResourceMetrics { if x != nil { - return x.Event + return x.ResourceMetrics } return nil } -func (x *ListAuditLogOriginatorsRequest) GetStartTime() *timestamppb.Timestamp { +func (x *GetForecastWorkloadResponse) GetResourceSummary() *ResourceSummary { if x != nil { - return x.StartTime + return x.ResourceSummary } return nil } -func (x *ListAuditLogOriginatorsRequest) GetEndTime() *timestamppb.Timestamp { +func (x *GetForecastWorkloadResponse) GetCostDataPoints() []*CostDataPoint { if x != nil { - return x.EndTime + return x.CostDataPoints } return nil } -func (x *ListAuditLogOriginatorsRequest) GetEmailMatches() []string { +func (x *GetForecastWorkloadResponse) GetResourceDataPoints() []*ResourceDataPoint { if x != nil { - return x.EmailMatches + return x.ResourceDataPoints } return nil } -// Response for fetching all audit log originators. -type ListAuditLogOriginatorsResponse struct { +// GetClusterMetadataResponse +type GetClusterMetadataResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - EmailAddresses []string `protobuf:"bytes,1,rep,name=email_addresses,json=emailAddresses,proto3" json:"email_addresses,omitempty"` + Clusters []*Cluster `protobuf:"bytes,1,rep,name=clusters,proto3" json:"clusters,omitempty"` // List of clusters. } -func (x *ListAuditLogOriginatorsResponse) Reset() { - *x = ListAuditLogOriginatorsResponse{} +func (x *GetClusterMetadataResponse) Reset() { + *x = GetClusterMetadataResponse{} if protoimpl.UnsafeEnabled { - mi := &file_api_v1_k8s_proto_msgTypes[88] + mi := &file_api_v1_k8s_proto_msgTypes[94] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } -func (x *ListAuditLogOriginatorsResponse) String() string { +func (x *GetClusterMetadataResponse) String() string { return protoimpl.X.MessageStringOf(x) } -func (*ListAuditLogOriginatorsResponse) ProtoMessage() {} +func (*GetClusterMetadataResponse) ProtoMessage() {} -func (x *ListAuditLogOriginatorsResponse) ProtoReflect() protoreflect.Message { - mi := &file_api_v1_k8s_proto_msgTypes[88] +func (x *GetClusterMetadataResponse) ProtoReflect() protoreflect.Message { + mi := &file_api_v1_k8s_proto_msgTypes[94] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -7018,48 +7662,44 @@ func (x *ListAuditLogOriginatorsResponse) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use ListAuditLogOriginatorsResponse.ProtoReflect.Descriptor instead. -func (*ListAuditLogOriginatorsResponse) Descriptor() ([]byte, []int) { - return file_api_v1_k8s_proto_rawDescGZIP(), []int{88} +// Deprecated: Use GetClusterMetadataResponse.ProtoReflect.Descriptor instead. +func (*GetClusterMetadataResponse) Descriptor() ([]byte, []int) { + return file_api_v1_k8s_proto_rawDescGZIP(), []int{94} } -func (x *ListAuditLogOriginatorsResponse) GetEmailAddresses() []string { +func (x *GetClusterMetadataResponse) GetClusters() []*Cluster { if x != nil { - return x.EmailAddresses + return x.Clusters } return nil } -// SendWorkloadEmailRequest is used to send an email w/ csv data. -type SendWorkloadEmailRequest struct { +type ClusterElement struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - TeamId string `protobuf:"bytes,1,opt,name=team_id,json=teamId,proto3" json:"team_id,omitempty"` - ClusterId string `protobuf:"bytes,2,opt,name=cluster_id,json=clusterId,proto3" json:"cluster_id,omitempty"` - StartTime int64 `protobuf:"varint,11,opt,name=start_time,json=startTime,proto3" json:"start_time,omitempty"` - StopTime int64 `protobuf:"varint,12,opt,name=stop_time,json=stopTime,proto3" json:"stop_time,omitempty"` - To []string `protobuf:"bytes,21,rep,name=to,proto3" json:"to,omitempty"` + ClusterId string `protobuf:"bytes,1,opt,name=cluster_id,json=clusterId,proto3" json:"cluster_id,omitempty"` + Element string `protobuf:"bytes,5,opt,name=element,proto3" json:"element,omitempty"` } -func (x *SendWorkloadEmailRequest) Reset() { - *x = SendWorkloadEmailRequest{} +func (x *ClusterElement) Reset() { + *x = ClusterElement{} if protoimpl.UnsafeEnabled { - mi := &file_api_v1_k8s_proto_msgTypes[89] + mi := &file_api_v1_k8s_proto_msgTypes[95] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } -func (x *SendWorkloadEmailRequest) String() string { +func (x *ClusterElement) String() string { return protoimpl.X.MessageStringOf(x) } -func (*SendWorkloadEmailRequest) ProtoMessage() {} +func (*ClusterElement) ProtoMessage() {} -func (x *SendWorkloadEmailRequest) ProtoReflect() protoreflect.Message { - mi := &file_api_v1_k8s_proto_msgTypes[89] +func (x *ClusterElement) ProtoReflect() protoreflect.Message { + mi := &file_api_v1_k8s_proto_msgTypes[95] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -7070,72 +7710,53 @@ func (x *SendWorkloadEmailRequest) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use SendWorkloadEmailRequest.ProtoReflect.Descriptor instead. -func (*SendWorkloadEmailRequest) Descriptor() ([]byte, []int) { - return file_api_v1_k8s_proto_rawDescGZIP(), []int{89} -} - -func (x *SendWorkloadEmailRequest) GetTeamId() string { - if x != nil { - return x.TeamId - } - return "" +// Deprecated: Use ClusterElement.ProtoReflect.Descriptor instead. +func (*ClusterElement) Descriptor() ([]byte, []int) { + return file_api_v1_k8s_proto_rawDescGZIP(), []int{95} } -func (x *SendWorkloadEmailRequest) GetClusterId() string { +func (x *ClusterElement) GetClusterId() string { if x != nil { return x.ClusterId } return "" } -func (x *SendWorkloadEmailRequest) GetStartTime() int64 { - if x != nil { - return x.StartTime - } - return 0 -} - -func (x *SendWorkloadEmailRequest) GetStopTime() int64 { - if x != nil { - return x.StopTime - } - return 0 -} - -func (x *SendWorkloadEmailRequest) GetTo() []string { +func (x *ClusterElement) GetElement() string { if x != nil { - return x.To + return x.Element } - return nil + return "" } -// SendWorkloadEmailResponse... -type SendWorkloadEmailResponse struct { +type GetAllNamespacesRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Message string `protobuf:"bytes,1,opt,name=message,proto3" json:"message,omitempty"` + TeamId string `protobuf:"bytes,1,opt,name=team_id,json=teamId,proto3" json:"team_id,omitempty"` + ClusterIds []string `protobuf:"bytes,5,rep,name=cluster_ids,json=clusterIds,proto3" json:"cluster_ids,omitempty"` + StartTime int64 `protobuf:"varint,11,opt,name=start_time,json=startTime,proto3" json:"start_time,omitempty"` + StopTime int64 `protobuf:"varint,12,opt,name=stop_time,json=stopTime,proto3" json:"stop_time,omitempty"` } -func (x *SendWorkloadEmailResponse) Reset() { - *x = SendWorkloadEmailResponse{} +func (x *GetAllNamespacesRequest) Reset() { + *x = GetAllNamespacesRequest{} if protoimpl.UnsafeEnabled { - mi := &file_api_v1_k8s_proto_msgTypes[90] + mi := &file_api_v1_k8s_proto_msgTypes[96] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } -func (x *SendWorkloadEmailResponse) String() string { +func (x *GetAllNamespacesRequest) String() string { return protoimpl.X.MessageStringOf(x) } -func (*SendWorkloadEmailResponse) ProtoMessage() {} +func (*GetAllNamespacesRequest) ProtoMessage() {} -func (x *SendWorkloadEmailResponse) ProtoReflect() protoreflect.Message { - mi := &file_api_v1_k8s_proto_msgTypes[90] +func (x *GetAllNamespacesRequest) ProtoReflect() protoreflect.Message { + mi := &file_api_v1_k8s_proto_msgTypes[96] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -7146,44 +7767,64 @@ func (x *SendWorkloadEmailResponse) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use SendWorkloadEmailResponse.ProtoReflect.Descriptor instead. -func (*SendWorkloadEmailResponse) Descriptor() ([]byte, []int) { - return file_api_v1_k8s_proto_rawDescGZIP(), []int{90} +// Deprecated: Use GetAllNamespacesRequest.ProtoReflect.Descriptor instead. +func (*GetAllNamespacesRequest) Descriptor() ([]byte, []int) { + return file_api_v1_k8s_proto_rawDescGZIP(), []int{96} } -func (x *SendWorkloadEmailResponse) GetMessage() string { +func (x *GetAllNamespacesRequest) GetTeamId() string { if x != nil { - return x.Message + return x.TeamId } return "" } -// SendWeeklySummaryEmailRequest is used to send a weekly summary email. -type SendWeeklySummaryEmailRequest struct { +func (x *GetAllNamespacesRequest) GetClusterIds() []string { + if x != nil { + return x.ClusterIds + } + return nil +} + +func (x *GetAllNamespacesRequest) GetStartTime() int64 { + if x != nil { + return x.StartTime + } + return 0 +} + +func (x *GetAllNamespacesRequest) GetStopTime() int64 { + if x != nil { + return x.StopTime + } + return 0 +} + +type GetAllNamespacesResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Request *SendWeeklySummaryEmailRequestData `protobuf:"bytes,1,opt,name=request,proto3" json:"request,omitempty"` // Request data wrapper. + ClusterIdWithNamespaces []*ClusterElement `protobuf:"bytes,1,rep,name=cluster_id_with_namespaces,json=clusterIdWithNamespaces,proto3" json:"cluster_id_with_namespaces,omitempty"` } -func (x *SendWeeklySummaryEmailRequest) Reset() { - *x = SendWeeklySummaryEmailRequest{} +func (x *GetAllNamespacesResponse) Reset() { + *x = GetAllNamespacesResponse{} if protoimpl.UnsafeEnabled { - mi := &file_api_v1_k8s_proto_msgTypes[91] + mi := &file_api_v1_k8s_proto_msgTypes[97] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } -func (x *SendWeeklySummaryEmailRequest) String() string { +func (x *GetAllNamespacesResponse) String() string { return protoimpl.X.MessageStringOf(x) } -func (*SendWeeklySummaryEmailRequest) ProtoMessage() {} +func (*GetAllNamespacesResponse) ProtoMessage() {} -func (x *SendWeeklySummaryEmailRequest) ProtoReflect() protoreflect.Message { - mi := &file_api_v1_k8s_proto_msgTypes[91] +func (x *GetAllNamespacesResponse) ProtoReflect() protoreflect.Message { + mi := &file_api_v1_k8s_proto_msgTypes[97] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -7194,45 +7835,46 @@ func (x *SendWeeklySummaryEmailRequest) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use SendWeeklySummaryEmailRequest.ProtoReflect.Descriptor instead. -func (*SendWeeklySummaryEmailRequest) Descriptor() ([]byte, []int) { - return file_api_v1_k8s_proto_rawDescGZIP(), []int{91} +// Deprecated: Use GetAllNamespacesResponse.ProtoReflect.Descriptor instead. +func (*GetAllNamespacesResponse) Descriptor() ([]byte, []int) { + return file_api_v1_k8s_proto_rawDescGZIP(), []int{97} } -func (x *SendWeeklySummaryEmailRequest) GetRequest() *SendWeeklySummaryEmailRequestData { +func (x *GetAllNamespacesResponse) GetClusterIdWithNamespaces() []*ClusterElement { if x != nil { - return x.Request + return x.ClusterIdWithNamespaces } return nil } -// SendWeeklySummaryEmailRequestData contains the actual request data. -type SendWeeklySummaryEmailRequestData struct { +// SearchNamespacesByClusterRequest searches namespaces by name within a single cluster. +type SearchNamespacesByClusterRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - TeamId string `protobuf:"bytes,1,opt,name=team_id,json=teamId,proto3" json:"team_id,omitempty"` // Unique identifier for the team. - Recipients []string `protobuf:"bytes,2,rep,name=recipients,proto3" json:"recipients,omitempty"` // List of email recipients. If empty, will use team members. + TeamId string `protobuf:"bytes,1,opt,name=team_id,json=teamId,proto3" json:"team_id,omitempty"` + ClusterId string `protobuf:"bytes,2,opt,name=cluster_id,json=clusterId,proto3" json:"cluster_id,omitempty"` + Search string `protobuf:"bytes,3,opt,name=search,proto3" json:"search,omitempty"` } -func (x *SendWeeklySummaryEmailRequestData) Reset() { - *x = SendWeeklySummaryEmailRequestData{} +func (x *SearchNamespacesByClusterRequest) Reset() { + *x = SearchNamespacesByClusterRequest{} if protoimpl.UnsafeEnabled { - mi := &file_api_v1_k8s_proto_msgTypes[92] + mi := &file_api_v1_k8s_proto_msgTypes[98] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } -func (x *SendWeeklySummaryEmailRequestData) String() string { +func (x *SearchNamespacesByClusterRequest) String() string { return protoimpl.X.MessageStringOf(x) } -func (*SendWeeklySummaryEmailRequestData) ProtoMessage() {} +func (*SearchNamespacesByClusterRequest) ProtoMessage() {} -func (x *SendWeeklySummaryEmailRequestData) ProtoReflect() protoreflect.Message { - mi := &file_api_v1_k8s_proto_msgTypes[92] +func (x *SearchNamespacesByClusterRequest) ProtoReflect() protoreflect.Message { + mi := &file_api_v1_k8s_proto_msgTypes[98] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -7243,51 +7885,58 @@ func (x *SendWeeklySummaryEmailRequestData) ProtoReflect() protoreflect.Message return mi.MessageOf(x) } -// Deprecated: Use SendWeeklySummaryEmailRequestData.ProtoReflect.Descriptor instead. -func (*SendWeeklySummaryEmailRequestData) Descriptor() ([]byte, []int) { - return file_api_v1_k8s_proto_rawDescGZIP(), []int{92} +// Deprecated: Use SearchNamespacesByClusterRequest.ProtoReflect.Descriptor instead. +func (*SearchNamespacesByClusterRequest) Descriptor() ([]byte, []int) { + return file_api_v1_k8s_proto_rawDescGZIP(), []int{98} } -func (x *SendWeeklySummaryEmailRequestData) GetTeamId() string { +func (x *SearchNamespacesByClusterRequest) GetTeamId() string { if x != nil { return x.TeamId } return "" } -func (x *SendWeeklySummaryEmailRequestData) GetRecipients() []string { +func (x *SearchNamespacesByClusterRequest) GetClusterId() string { if x != nil { - return x.Recipients + return x.ClusterId } - return nil + return "" } -// SendWeeklySummaryEmailResponse contains the result of sending a weekly summary email. -type SendWeeklySummaryEmailResponse struct { +func (x *SearchNamespacesByClusterRequest) GetSearch() string { + if x != nil { + return x.Search + } + return "" +} + +// SearchNamespacesByClusterResponse returns the matching namespace names. +type SearchNamespacesByClusterResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Message string `protobuf:"bytes,1,opt,name=message,proto3" json:"message,omitempty"` // Status message about the email sending operation. + Namespaces []string `protobuf:"bytes,1,rep,name=namespaces,proto3" json:"namespaces,omitempty"` } -func (x *SendWeeklySummaryEmailResponse) Reset() { - *x = SendWeeklySummaryEmailResponse{} +func (x *SearchNamespacesByClusterResponse) Reset() { + *x = SearchNamespacesByClusterResponse{} if protoimpl.UnsafeEnabled { - mi := &file_api_v1_k8s_proto_msgTypes[93] + mi := &file_api_v1_k8s_proto_msgTypes[99] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } -func (x *SendWeeklySummaryEmailResponse) String() string { +func (x *SearchNamespacesByClusterResponse) String() string { return protoimpl.X.MessageStringOf(x) } -func (*SendWeeklySummaryEmailResponse) ProtoMessage() {} +func (*SearchNamespacesByClusterResponse) ProtoMessage() {} -func (x *SendWeeklySummaryEmailResponse) ProtoReflect() protoreflect.Message { - mi := &file_api_v1_k8s_proto_msgTypes[93] +func (x *SearchNamespacesByClusterResponse) ProtoReflect() protoreflect.Message { + mi := &file_api_v1_k8s_proto_msgTypes[99] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -7298,50 +7947,45 @@ func (x *SendWeeklySummaryEmailResponse) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use SendWeeklySummaryEmailResponse.ProtoReflect.Descriptor instead. -func (*SendWeeklySummaryEmailResponse) Descriptor() ([]byte, []int) { - return file_api_v1_k8s_proto_rawDescGZIP(), []int{93} +// Deprecated: Use SearchNamespacesByClusterResponse.ProtoReflect.Descriptor instead. +func (*SearchNamespacesByClusterResponse) Descriptor() ([]byte, []int) { + return file_api_v1_k8s_proto_rawDescGZIP(), []int{99} } -func (x *SendWeeklySummaryEmailResponse) GetMessage() string { +func (x *SearchNamespacesByClusterResponse) GetNamespaces() []string { if x != nil { - return x.Message + return x.Namespaces } - return "" + return nil } -// GetClustersNodeInfoRequest is used to fetch aggregated node information across clusters. -type GetClustersNodeInfoRequest struct { +// ListNamespacesByClusterRequest lists namespaces within a single cluster. +type ListNamespacesByClusterRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - TeamId string `protobuf:"bytes,1,opt,name=team_id,json=teamId,proto3" json:"team_id,omitempty"` // Unique identifier for the team. - ClusterIds []string `protobuf:"bytes,2,rep,name=cluster_ids,json=clusterIds,proto3" json:"cluster_ids,omitempty"` // Optional list of cluster IDs to filter by. If empty, returns info for all clusters. - StartTime *timestamppb.Timestamp `protobuf:"bytes,11,opt,name=start_time,json=startTime,proto3,oneof" json:"start_time,omitempty"` - EndTime *timestamppb.Timestamp `protobuf:"bytes,12,opt,name=end_time,json=endTime,proto3,oneof" json:"end_time,omitempty"` - ExcludeMostExpNode bool `protobuf:"varint,41,opt,name=exclude_most_exp_node,json=excludeMostExpNode,proto3" json:"exclude_most_exp_node,omitempty"` - ExcludeLeastExpNode bool `protobuf:"varint,42,opt,name=exclude_least_exp_node,json=excludeLeastExpNode,proto3" json:"exclude_least_exp_node,omitempty"` - ExcludeMostUnderutilNode bool `protobuf:"varint,43,opt,name=exclude_most_underutil_node,json=excludeMostUnderutilNode,proto3" json:"exclude_most_underutil_node,omitempty"` + TeamId string `protobuf:"bytes,1,opt,name=team_id,json=teamId,proto3" json:"team_id,omitempty"` + ClusterId string `protobuf:"bytes,2,opt,name=cluster_id,json=clusterId,proto3" json:"cluster_id,omitempty"` } -func (x *GetClustersNodeInfoRequest) Reset() { - *x = GetClustersNodeInfoRequest{} +func (x *ListNamespacesByClusterRequest) Reset() { + *x = ListNamespacesByClusterRequest{} if protoimpl.UnsafeEnabled { - mi := &file_api_v1_k8s_proto_msgTypes[94] + mi := &file_api_v1_k8s_proto_msgTypes[100] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } -func (x *GetClustersNodeInfoRequest) String() string { +func (x *ListNamespacesByClusterRequest) String() string { return protoimpl.X.MessageStringOf(x) } -func (*GetClustersNodeInfoRequest) ProtoMessage() {} +func (*ListNamespacesByClusterRequest) ProtoMessage() {} -func (x *GetClustersNodeInfoRequest) ProtoReflect() protoreflect.Message { - mi := &file_api_v1_k8s_proto_msgTypes[94] +func (x *ListNamespacesByClusterRequest) ProtoReflect() protoreflect.Message { + mi := &file_api_v1_k8s_proto_msgTypes[100] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -7352,89 +7996,100 @@ func (x *GetClustersNodeInfoRequest) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use GetClustersNodeInfoRequest.ProtoReflect.Descriptor instead. -func (*GetClustersNodeInfoRequest) Descriptor() ([]byte, []int) { - return file_api_v1_k8s_proto_rawDescGZIP(), []int{94} +// Deprecated: Use ListNamespacesByClusterRequest.ProtoReflect.Descriptor instead. +func (*ListNamespacesByClusterRequest) Descriptor() ([]byte, []int) { + return file_api_v1_k8s_proto_rawDescGZIP(), []int{100} } -func (x *GetClustersNodeInfoRequest) GetTeamId() string { +func (x *ListNamespacesByClusterRequest) GetTeamId() string { if x != nil { return x.TeamId } return "" } -func (x *GetClustersNodeInfoRequest) GetClusterIds() []string { +func (x *ListNamespacesByClusterRequest) GetClusterId() string { if x != nil { - return x.ClusterIds + return x.ClusterId } - return nil + return "" } -func (x *GetClustersNodeInfoRequest) GetStartTime() *timestamppb.Timestamp { - if x != nil { - return x.StartTime - } - return nil +// ListNamespacesByClusterResponse returns namespace id and name pairs. +type ListNamespacesByClusterResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Namespaces []*NamespaceItem `protobuf:"bytes,1,rep,name=namespaces,proto3" json:"namespaces,omitempty"` } -func (x *GetClustersNodeInfoRequest) GetEndTime() *timestamppb.Timestamp { - if x != nil { - return x.EndTime +func (x *ListNamespacesByClusterResponse) Reset() { + *x = ListNamespacesByClusterResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_api_v1_k8s_proto_msgTypes[101] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } - return nil } -func (x *GetClustersNodeInfoRequest) GetExcludeMostExpNode() bool { - if x != nil { - return x.ExcludeMostExpNode - } - return false +func (x *ListNamespacesByClusterResponse) String() string { + return protoimpl.X.MessageStringOf(x) } -func (x *GetClustersNodeInfoRequest) GetExcludeLeastExpNode() bool { - if x != nil { - return x.ExcludeLeastExpNode +func (*ListNamespacesByClusterResponse) ProtoMessage() {} + +func (x *ListNamespacesByClusterResponse) ProtoReflect() protoreflect.Message { + mi := &file_api_v1_k8s_proto_msgTypes[101] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms } - return false + return mi.MessageOf(x) } -func (x *GetClustersNodeInfoRequest) GetExcludeMostUnderutilNode() bool { +// Deprecated: Use ListNamespacesByClusterResponse.ProtoReflect.Descriptor instead. +func (*ListNamespacesByClusterResponse) Descriptor() ([]byte, []int) { + return file_api_v1_k8s_proto_rawDescGZIP(), []int{101} +} + +func (x *ListNamespacesByClusterResponse) GetNamespaces() []*NamespaceItem { if x != nil { - return x.ExcludeMostUnderutilNode + return x.Namespaces } - return false + return nil } -// GetClustersNodeInfoResponse contains aggregated node information across clusters. -type GetClustersNodeInfoResponse struct { +// NamespaceItem represents a namespace with its id and name. +type NamespaceItem struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - NodeInfo *NodeInfo `protobuf:"bytes,1,opt,name=node_info,json=nodeInfo,proto3" json:"node_info,omitempty"` // Aggregated node information across all clusters. - MostExpensiveNode *Node `protobuf:"bytes,2,opt,name=most_expensive_node,json=mostExpensiveNode,proto3" json:"most_expensive_node,omitempty"` // The most expensive node across all clusters. - LeastExpensiveNode *Node `protobuf:"bytes,3,opt,name=least_expensive_node,json=leastExpensiveNode,proto3" json:"least_expensive_node,omitempty"` // The least expensive node across all clusters. - MostUnderutilizedNode *Node `protobuf:"bytes,4,opt,name=most_underutilized_node,json=mostUnderutilizedNode,proto3" json:"most_underutilized_node,omitempty"` // The most underutilized node across all clusters. + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` } -func (x *GetClustersNodeInfoResponse) Reset() { - *x = GetClustersNodeInfoResponse{} +func (x *NamespaceItem) Reset() { + *x = NamespaceItem{} if protoimpl.UnsafeEnabled { - mi := &file_api_v1_k8s_proto_msgTypes[95] + mi := &file_api_v1_k8s_proto_msgTypes[102] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } -func (x *GetClustersNodeInfoResponse) String() string { +func (x *NamespaceItem) String() string { return protoimpl.X.MessageStringOf(x) } -func (*GetClustersNodeInfoResponse) ProtoMessage() {} +func (*NamespaceItem) ProtoMessage() {} -func (x *GetClustersNodeInfoResponse) ProtoReflect() protoreflect.Message { - mi := &file_api_v1_k8s_proto_msgTypes[95] +func (x *NamespaceItem) ProtoReflect() protoreflect.Message { + mi := &file_api_v1_k8s_proto_msgTypes[102] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -7445,67 +8100,58 @@ func (x *GetClustersNodeInfoResponse) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use GetClustersNodeInfoResponse.ProtoReflect.Descriptor instead. -func (*GetClustersNodeInfoResponse) Descriptor() ([]byte, []int) { - return file_api_v1_k8s_proto_rawDescGZIP(), []int{95} -} - -func (x *GetClustersNodeInfoResponse) GetNodeInfo() *NodeInfo { - if x != nil { - return x.NodeInfo - } - return nil -} - -func (x *GetClustersNodeInfoResponse) GetMostExpensiveNode() *Node { - if x != nil { - return x.MostExpensiveNode - } - return nil +// Deprecated: Use NamespaceItem.ProtoReflect.Descriptor instead. +func (*NamespaceItem) Descriptor() ([]byte, []int) { + return file_api_v1_k8s_proto_rawDescGZIP(), []int{102} } -func (x *GetClustersNodeInfoResponse) GetLeastExpensiveNode() *Node { +func (x *NamespaceItem) GetId() string { if x != nil { - return x.LeastExpensiveNode + return x.Id } - return nil + return "" } -func (x *GetClustersNodeInfoResponse) GetMostUnderutilizedNode() *Node { +func (x *NamespaceItem) GetName() string { if x != nil { - return x.MostUnderutilizedNode + return x.Name } - return nil + return "" } -type SearchK8SResourcesRequest struct { +type GetAllWorkloadNamesRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - TeamId string `protobuf:"bytes,1,opt,name=team_id,json=teamId,proto3" json:"team_id,omitempty"` // Unique identifier for the team. - ClusterId string `protobuf:"bytes,2,opt,name=cluster_id,json=clusterId,proto3" json:"cluster_id,omitempty"` // Unique identifier for the cluster. - SearchQuery string `protobuf:"bytes,3,opt,name=search_query,json=searchQuery,proto3" json:"search_query,omitempty"` // Search term to match against name, uid, and namespace. - IncludeDeleted bool `protobuf:"varint,4,opt,name=include_deleted,json=includeDeleted,proto3" json:"include_deleted,omitempty"` // Include soft-deleted resources in results. + TeamId string `protobuf:"bytes,1,opt,name=team_id,json=teamId,proto3" json:"team_id,omitempty"` + ClusterIds []string `protobuf:"bytes,5,rep,name=cluster_ids,json=clusterIds,proto3" json:"cluster_ids,omitempty"` + Namespaces []string `protobuf:"bytes,6,rep,name=namespaces,proto3" json:"namespaces,omitempty"` + Kinds []K8SObjectKind `protobuf:"varint,7,rep,packed,name=kinds,proto3,enum=api.v1.K8SObjectKind" json:"kinds,omitempty"` + WorkloadLabels []string `protobuf:"bytes,8,rep,name=workload_labels,json=workloadLabels,proto3" json:"workload_labels,omitempty"` // each element is "key=val" + NodeGroupNames []string `protobuf:"bytes,9,rep,name=node_group_names,json=nodeGroupNames,proto3" json:"node_group_names,omitempty"` + StartTime int64 `protobuf:"varint,11,opt,name=start_time,json=startTime,proto3" json:"start_time,omitempty"` + StopTime int64 `protobuf:"varint,12,opt,name=stop_time,json=stopTime,proto3" json:"stop_time,omitempty"` + IncludeDeleted bool `protobuf:"varint,21,opt,name=include_deleted,json=includeDeleted,proto3" json:"include_deleted,omitempty"` } -func (x *SearchK8SResourcesRequest) Reset() { - *x = SearchK8SResourcesRequest{} +func (x *GetAllWorkloadNamesRequest) Reset() { + *x = GetAllWorkloadNamesRequest{} if protoimpl.UnsafeEnabled { - mi := &file_api_v1_k8s_proto_msgTypes[96] + mi := &file_api_v1_k8s_proto_msgTypes[103] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } -func (x *SearchK8SResourcesRequest) String() string { +func (x *GetAllWorkloadNamesRequest) String() string { return protoimpl.X.MessageStringOf(x) } -func (*SearchK8SResourcesRequest) ProtoMessage() {} +func (*GetAllWorkloadNamesRequest) ProtoMessage() {} -func (x *SearchK8SResourcesRequest) ProtoReflect() protoreflect.Message { - mi := &file_api_v1_k8s_proto_msgTypes[96] +func (x *GetAllWorkloadNamesRequest) ProtoReflect() protoreflect.Message { + mi := &file_api_v1_k8s_proto_msgTypes[103] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -7516,64 +8162,99 @@ func (x *SearchK8SResourcesRequest) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use SearchK8SResourcesRequest.ProtoReflect.Descriptor instead. -func (*SearchK8SResourcesRequest) Descriptor() ([]byte, []int) { - return file_api_v1_k8s_proto_rawDescGZIP(), []int{96} +// Deprecated: Use GetAllWorkloadNamesRequest.ProtoReflect.Descriptor instead. +func (*GetAllWorkloadNamesRequest) Descriptor() ([]byte, []int) { + return file_api_v1_k8s_proto_rawDescGZIP(), []int{103} } -func (x *SearchK8SResourcesRequest) GetTeamId() string { +func (x *GetAllWorkloadNamesRequest) GetTeamId() string { if x != nil { return x.TeamId } return "" } -func (x *SearchK8SResourcesRequest) GetClusterId() string { +func (x *GetAllWorkloadNamesRequest) GetClusterIds() []string { if x != nil { - return x.ClusterId + return x.ClusterIds } - return "" + return nil } -func (x *SearchK8SResourcesRequest) GetSearchQuery() string { +func (x *GetAllWorkloadNamesRequest) GetNamespaces() []string { if x != nil { - return x.SearchQuery + return x.Namespaces } - return "" + return nil } -func (x *SearchK8SResourcesRequest) GetIncludeDeleted() bool { +func (x *GetAllWorkloadNamesRequest) GetKinds() []K8SObjectKind { + if x != nil { + return x.Kinds + } + return nil +} + +func (x *GetAllWorkloadNamesRequest) GetWorkloadLabels() []string { + if x != nil { + return x.WorkloadLabels + } + return nil +} + +func (x *GetAllWorkloadNamesRequest) GetNodeGroupNames() []string { + if x != nil { + return x.NodeGroupNames + } + return nil +} + +func (x *GetAllWorkloadNamesRequest) GetStartTime() int64 { + if x != nil { + return x.StartTime + } + return 0 +} + +func (x *GetAllWorkloadNamesRequest) GetStopTime() int64 { + if x != nil { + return x.StopTime + } + return 0 +} + +func (x *GetAllWorkloadNamesRequest) GetIncludeDeleted() bool { if x != nil { return x.IncludeDeleted } return false } -type SearchK8SResourcesResponse struct { +type GetAllWorkloadNamesResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Results []*K8SSearchResult `protobuf:"bytes,1,rep,name=results,proto3" json:"results,omitempty"` // List of matching resources. + ClusterIdWithWorkloadName []*ClusterElement `protobuf:"bytes,1,rep,name=cluster_id_with_workload_name,json=clusterIdWithWorkloadName,proto3" json:"cluster_id_with_workload_name,omitempty"` } -func (x *SearchK8SResourcesResponse) Reset() { - *x = SearchK8SResourcesResponse{} +func (x *GetAllWorkloadNamesResponse) Reset() { + *x = GetAllWorkloadNamesResponse{} if protoimpl.UnsafeEnabled { - mi := &file_api_v1_k8s_proto_msgTypes[97] + mi := &file_api_v1_k8s_proto_msgTypes[104] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } -func (x *SearchK8SResourcesResponse) String() string { +func (x *GetAllWorkloadNamesResponse) String() string { return protoimpl.X.MessageStringOf(x) } -func (*SearchK8SResourcesResponse) ProtoMessage() {} +func (*GetAllWorkloadNamesResponse) ProtoMessage() {} -func (x *SearchK8SResourcesResponse) ProtoReflect() protoreflect.Message { - mi := &file_api_v1_k8s_proto_msgTypes[97] +func (x *GetAllWorkloadNamesResponse) ProtoReflect() protoreflect.Message { + mi := &file_api_v1_k8s_proto_msgTypes[104] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -7584,47 +8265,50 @@ func (x *SearchK8SResourcesResponse) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use SearchK8SResourcesResponse.ProtoReflect.Descriptor instead. -func (*SearchK8SResourcesResponse) Descriptor() ([]byte, []int) { - return file_api_v1_k8s_proto_rawDescGZIP(), []int{97} +// Deprecated: Use GetAllWorkloadNamesResponse.ProtoReflect.Descriptor instead. +func (*GetAllWorkloadNamesResponse) Descriptor() ([]byte, []int) { + return file_api_v1_k8s_proto_rawDescGZIP(), []int{104} } -func (x *SearchK8SResourcesResponse) GetResults() []*K8SSearchResult { +func (x *GetAllWorkloadNamesResponse) GetClusterIdWithWorkloadName() []*ClusterElement { if x != nil { - return x.Results + return x.ClusterIdWithWorkloadName } return nil } -type K8SSearchResult struct { +type GetAllWorkloadLabelsRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Uid string `protobuf:"bytes,1,opt,name=uid,proto3" json:"uid,omitempty"` // Unique identifier of the resource. - Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` // Name of the resource. - Kind string `protobuf:"bytes,3,opt,name=kind,proto3" json:"kind,omitempty"` // Kind of the resource (e.g., Deployment, StatefulSet). - MatchedField string `protobuf:"bytes,4,opt,name=matched_field,json=matchedField,proto3" json:"matched_field,omitempty"` // Field that matched the search query (name, uid, or namespace). - Namespace string `protobuf:"bytes,5,opt,name=namespace,proto3" json:"namespace,omitempty"` // Namespace of the resource. + TeamId string `protobuf:"bytes,1,opt,name=team_id,json=teamId,proto3" json:"team_id,omitempty"` + ClusterIds []string `protobuf:"bytes,5,rep,name=cluster_ids,json=clusterIds,proto3" json:"cluster_ids,omitempty"` + Namespaces []string `protobuf:"bytes,6,rep,name=namespaces,proto3" json:"namespaces,omitempty"` + Kinds []K8SObjectKind `protobuf:"varint,7,rep,packed,name=kinds,proto3,enum=api.v1.K8SObjectKind" json:"kinds,omitempty"` + WorkloadNames []string `protobuf:"bytes,8,rep,name=workload_names,json=workloadNames,proto3" json:"workload_names,omitempty"` + NodeGroupNames []string `protobuf:"bytes,9,rep,name=node_group_names,json=nodeGroupNames,proto3" json:"node_group_names,omitempty"` + StartTime int64 `protobuf:"varint,11,opt,name=start_time,json=startTime,proto3" json:"start_time,omitempty"` + StopTime int64 `protobuf:"varint,12,opt,name=stop_time,json=stopTime,proto3" json:"stop_time,omitempty"` } -func (x *K8SSearchResult) Reset() { - *x = K8SSearchResult{} +func (x *GetAllWorkloadLabelsRequest) Reset() { + *x = GetAllWorkloadLabelsRequest{} if protoimpl.UnsafeEnabled { - mi := &file_api_v1_k8s_proto_msgTypes[98] + mi := &file_api_v1_k8s_proto_msgTypes[105] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } -func (x *K8SSearchResult) String() string { +func (x *GetAllWorkloadLabelsRequest) String() string { return protoimpl.X.MessageStringOf(x) } -func (*K8SSearchResult) ProtoMessage() {} +func (*GetAllWorkloadLabelsRequest) ProtoMessage() {} -func (x *K8SSearchResult) ProtoReflect() protoreflect.Message { - mi := &file_api_v1_k8s_proto_msgTypes[98] +func (x *GetAllWorkloadLabelsRequest) ProtoReflect() protoreflect.Message { + mi := &file_api_v1_k8s_proto_msgTypes[105] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -7635,73 +8319,92 @@ func (x *K8SSearchResult) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use K8SSearchResult.ProtoReflect.Descriptor instead. -func (*K8SSearchResult) Descriptor() ([]byte, []int) { - return file_api_v1_k8s_proto_rawDescGZIP(), []int{98} +// Deprecated: Use GetAllWorkloadLabelsRequest.ProtoReflect.Descriptor instead. +func (*GetAllWorkloadLabelsRequest) Descriptor() ([]byte, []int) { + return file_api_v1_k8s_proto_rawDescGZIP(), []int{105} } -func (x *K8SSearchResult) GetUid() string { +func (x *GetAllWorkloadLabelsRequest) GetTeamId() string { if x != nil { - return x.Uid + return x.TeamId } return "" } -func (x *K8SSearchResult) GetName() string { +func (x *GetAllWorkloadLabelsRequest) GetClusterIds() []string { if x != nil { - return x.Name + return x.ClusterIds } - return "" + return nil } -func (x *K8SSearchResult) GetKind() string { +func (x *GetAllWorkloadLabelsRequest) GetNamespaces() []string { if x != nil { - return x.Kind + return x.Namespaces } - return "" + return nil } -func (x *K8SSearchResult) GetMatchedField() string { +func (x *GetAllWorkloadLabelsRequest) GetKinds() []K8SObjectKind { if x != nil { - return x.MatchedField + return x.Kinds } - return "" + return nil } -func (x *K8SSearchResult) GetNamespace() string { +func (x *GetAllWorkloadLabelsRequest) GetWorkloadNames() []string { if x != nil { - return x.Namespace + return x.WorkloadNames } - return "" + return nil } -type SearchK8SWorkloadsRequest struct { +func (x *GetAllWorkloadLabelsRequest) GetNodeGroupNames() []string { + if x != nil { + return x.NodeGroupNames + } + return nil +} + +func (x *GetAllWorkloadLabelsRequest) GetStartTime() int64 { + if x != nil { + return x.StartTime + } + return 0 +} + +func (x *GetAllWorkloadLabelsRequest) GetStopTime() int64 { + if x != nil { + return x.StopTime + } + return 0 +} + +type GetAllWorkloadLabelsResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - TeamId string `protobuf:"bytes,1,opt,name=team_id,json=teamId,proto3" json:"team_id,omitempty"` // Unique identifier for the team. - ClusterIds []string `protobuf:"bytes,2,rep,name=cluster_ids,json=clusterIds,proto3" json:"cluster_ids,omitempty"` // Unique identifier for the cluster. - SearchQuery string `protobuf:"bytes,3,opt,name=search_query,json=searchQuery,proto3" json:"search_query,omitempty"` // Search term to match against name, uid, and namespace. + ClusterIdWithLabels []*ClusterElement `protobuf:"bytes,1,rep,name=cluster_id_with_labels,json=clusterIdWithLabels,proto3" json:"cluster_id_with_labels,omitempty"` } -func (x *SearchK8SWorkloadsRequest) Reset() { - *x = SearchK8SWorkloadsRequest{} +func (x *GetAllWorkloadLabelsResponse) Reset() { + *x = GetAllWorkloadLabelsResponse{} if protoimpl.UnsafeEnabled { - mi := &file_api_v1_k8s_proto_msgTypes[99] + mi := &file_api_v1_k8s_proto_msgTypes[106] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } -func (x *SearchK8SWorkloadsRequest) String() string { +func (x *GetAllWorkloadLabelsResponse) String() string { return protoimpl.X.MessageStringOf(x) } -func (*SearchK8SWorkloadsRequest) ProtoMessage() {} +func (*GetAllWorkloadLabelsResponse) ProtoMessage() {} -func (x *SearchK8SWorkloadsRequest) ProtoReflect() protoreflect.Message { - mi := &file_api_v1_k8s_proto_msgTypes[99] +func (x *GetAllWorkloadLabelsResponse) ProtoReflect() protoreflect.Message { + mi := &file_api_v1_k8s_proto_msgTypes[106] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -7712,57 +8415,47 @@ func (x *SearchK8SWorkloadsRequest) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use SearchK8SWorkloadsRequest.ProtoReflect.Descriptor instead. -func (*SearchK8SWorkloadsRequest) Descriptor() ([]byte, []int) { - return file_api_v1_k8s_proto_rawDescGZIP(), []int{99} -} - -func (x *SearchK8SWorkloadsRequest) GetTeamId() string { - if x != nil { - return x.TeamId - } - return "" +// Deprecated: Use GetAllWorkloadLabelsResponse.ProtoReflect.Descriptor instead. +func (*GetAllWorkloadLabelsResponse) Descriptor() ([]byte, []int) { + return file_api_v1_k8s_proto_rawDescGZIP(), []int{106} } -func (x *SearchK8SWorkloadsRequest) GetClusterIds() []string { +func (x *GetAllWorkloadLabelsResponse) GetClusterIdWithLabels() []*ClusterElement { if x != nil { - return x.ClusterIds + return x.ClusterIdWithLabels } return nil } -func (x *SearchK8SWorkloadsRequest) GetSearchQuery() string { - if x != nil { - return x.SearchQuery - } - return "" -} - -type SearchK8SWorkloadsResponse struct { +type GetAllNodeGroupNamesRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Results []*K8SWorkloadSearchResult `protobuf:"bytes,1,rep,name=results,proto3" json:"results,omitempty"` // List of matching workloads. + TeamId string `protobuf:"bytes,1,opt,name=team_id,json=teamId,proto3" json:"team_id,omitempty"` + ClusterIds []string `protobuf:"bytes,5,rep,name=cluster_ids,json=clusterIds,proto3" json:"cluster_ids,omitempty"` + StartTime int64 `protobuf:"varint,11,opt,name=start_time,json=startTime,proto3" json:"start_time,omitempty"` + StopTime int64 `protobuf:"varint,12,opt,name=stop_time,json=stopTime,proto3" json:"stop_time,omitempty"` + IncludeDeleted bool `protobuf:"varint,21,opt,name=include_deleted,json=includeDeleted,proto3" json:"include_deleted,omitempty"` } -func (x *SearchK8SWorkloadsResponse) Reset() { - *x = SearchK8SWorkloadsResponse{} +func (x *GetAllNodeGroupNamesRequest) Reset() { + *x = GetAllNodeGroupNamesRequest{} if protoimpl.UnsafeEnabled { - mi := &file_api_v1_k8s_proto_msgTypes[100] + mi := &file_api_v1_k8s_proto_msgTypes[107] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } -func (x *SearchK8SWorkloadsResponse) String() string { +func (x *GetAllNodeGroupNamesRequest) String() string { return protoimpl.X.MessageStringOf(x) } -func (*SearchK8SWorkloadsResponse) ProtoMessage() {} +func (*GetAllNodeGroupNamesRequest) ProtoMessage() {} -func (x *SearchK8SWorkloadsResponse) ProtoReflect() protoreflect.Message { - mi := &file_api_v1_k8s_proto_msgTypes[100] +func (x *GetAllNodeGroupNamesRequest) ProtoReflect() protoreflect.Message { + mi := &file_api_v1_k8s_proto_msgTypes[107] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -7773,44 +8466,71 @@ func (x *SearchK8SWorkloadsResponse) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use SearchK8SWorkloadsResponse.ProtoReflect.Descriptor instead. -func (*SearchK8SWorkloadsResponse) Descriptor() ([]byte, []int) { - return file_api_v1_k8s_proto_rawDescGZIP(), []int{100} +// Deprecated: Use GetAllNodeGroupNamesRequest.ProtoReflect.Descriptor instead. +func (*GetAllNodeGroupNamesRequest) Descriptor() ([]byte, []int) { + return file_api_v1_k8s_proto_rawDescGZIP(), []int{107} } -func (x *SearchK8SWorkloadsResponse) GetResults() []*K8SWorkloadSearchResult { +func (x *GetAllNodeGroupNamesRequest) GetTeamId() string { if x != nil { - return x.Results + return x.TeamId + } + return "" +} + +func (x *GetAllNodeGroupNamesRequest) GetClusterIds() []string { + if x != nil { + return x.ClusterIds } return nil } -type K8SWorkloadSearchResult struct { +func (x *GetAllNodeGroupNamesRequest) GetStartTime() int64 { + if x != nil { + return x.StartTime + } + return 0 +} + +func (x *GetAllNodeGroupNamesRequest) GetStopTime() int64 { + if x != nil { + return x.StopTime + } + return 0 +} + +func (x *GetAllNodeGroupNamesRequest) GetIncludeDeleted() bool { + if x != nil { + return x.IncludeDeleted + } + return false +} + +type GetAllNodeGroupNamesResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Kind string `protobuf:"bytes,1,opt,name=kind,proto3" json:"kind,omitempty"` // Kind of the workload (e.g., Deployment, StatefulSet). - Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` // Name of the workload. + ClusterIdWithNodeGroupNames []*ClusterElement `protobuf:"bytes,1,rep,name=cluster_id_with_node_group_names,json=clusterIdWithNodeGroupNames,proto3" json:"cluster_id_with_node_group_names,omitempty"` } -func (x *K8SWorkloadSearchResult) Reset() { - *x = K8SWorkloadSearchResult{} +func (x *GetAllNodeGroupNamesResponse) Reset() { + *x = GetAllNodeGroupNamesResponse{} if protoimpl.UnsafeEnabled { - mi := &file_api_v1_k8s_proto_msgTypes[101] + mi := &file_api_v1_k8s_proto_msgTypes[108] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } -func (x *K8SWorkloadSearchResult) String() string { +func (x *GetAllNodeGroupNamesResponse) String() string { return protoimpl.X.MessageStringOf(x) } -func (*K8SWorkloadSearchResult) ProtoMessage() {} +func (*GetAllNodeGroupNamesResponse) ProtoMessage() {} -func (x *K8SWorkloadSearchResult) ProtoReflect() protoreflect.Message { - mi := &file_api_v1_k8s_proto_msgTypes[101] +func (x *GetAllNodeGroupNamesResponse) ProtoReflect() protoreflect.Message { + mi := &file_api_v1_k8s_proto_msgTypes[108] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -7821,53 +8541,80 @@ func (x *K8SWorkloadSearchResult) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use K8SWorkloadSearchResult.ProtoReflect.Descriptor instead. -func (*K8SWorkloadSearchResult) Descriptor() ([]byte, []int) { - return file_api_v1_k8s_proto_rawDescGZIP(), []int{101} -} - -func (x *K8SWorkloadSearchResult) GetKind() string { - if x != nil { - return x.Kind - } - return "" +// Deprecated: Use GetAllNodeGroupNamesResponse.ProtoReflect.Descriptor instead. +func (*GetAllNodeGroupNamesResponse) Descriptor() ([]byte, []int) { + return file_api_v1_k8s_proto_rawDescGZIP(), []int{108} } -func (x *K8SWorkloadSearchResult) GetName() string { +func (x *GetAllNodeGroupNamesResponse) GetClusterIdWithNodeGroupNames() []*ClusterElement { if x != nil { - return x.Name + return x.ClusterIdWithNodeGroupNames } - return "" + return nil } -type MetadataForWorkloadsRequest struct { +// Cluster represents a Kubernetes cluster with metadata, metrics, and cost information. +type Cluster struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - TeamId string `protobuf:"bytes,1,opt,name=team_id,json=teamId,proto3" json:"team_id,omitempty"` - ClusterId string `protobuf:"bytes,2,opt,name=cluster_id,json=clusterId,proto3" json:"cluster_id,omitempty"` - WorkloadUidToKind map[string]K8SObjectKind `protobuf:"bytes,6,rep,name=workload_uid_to_kind,json=workloadUidToKind,proto3" json:"workload_uid_to_kind,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"varint,2,opt,name=value,proto3,enum=api.v1.K8SObjectKind"` - IncludeDeleted bool `protobuf:"varint,11,opt,name=include_deleted,json=includeDeleted,proto3" json:"include_deleted,omitempty"` + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` // Unique identifier for the cluster. + Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` // Name of the cluster. + TeamId string `protobuf:"bytes,3,opt,name=team_id,json=teamId,proto3" json:"team_id,omitempty"` // Identifier of the team owning the cluster. + CloudProviderId string `protobuf:"bytes,4,opt,name=cloud_provider_id,json=cloudProviderId,proto3" json:"cloud_provider_id,omitempty"` // Identifier of the cloud provider. + RegionId string `protobuf:"bytes,5,opt,name=region_id,json=regionId,proto3" json:"region_id,omitempty"` // Identifier of the region. + CloudProvider string `protobuf:"bytes,6,opt,name=cloud_provider,json=cloudProvider,proto3" json:"cloud_provider,omitempty"` // Name of the cloud provider. + Region string `protobuf:"bytes,7,opt,name=region,proto3" json:"region,omitempty"` // Name of the region. + CreatedAt int64 `protobuf:"varint,8,opt,name=created_at,json=createdAt,proto3" json:"created_at,omitempty"` // Creation time as a Unix timestamp. + UpdatedAt int64 `protobuf:"varint,9,opt,name=updated_at,json=updatedAt,proto3" json:"updated_at,omitempty"` // Last update time as a Unix timestamp. + ResourceMetrics *ResourceMetrics `protobuf:"bytes,10,opt,name=resource_metrics,json=resourceMetrics,proto3" json:"resource_metrics,omitempty"` // Aggregated resource metrics for the cluster. + CostInfo *CostInfo `protobuf:"bytes,11,opt,name=cost_info,json=costInfo,proto3" json:"cost_info,omitempty"` // Aggregated cost information for the cluster. + NodeInfo *NodeInfo `protobuf:"bytes,12,opt,name=node_info,json=nodeInfo,proto3" json:"node_info,omitempty"` // Aggregated node information for the cluster. + CostDataPoints []*CostDataPoint `protobuf:"bytes,13,rep,name=cost_data_points,json=costDataPoints,proto3" json:"cost_data_points,omitempty"` // Time-series cost data points for the cluster. + ResourceDataPoints []*ResourceDataPoint `protobuf:"bytes,14,rep,name=resource_data_points,json=resourceDataPoints,proto3" json:"resource_data_points,omitempty"` // Time-series resource metrics for the cluster. + MostExpensiveNode *Node `protobuf:"bytes,15,opt,name=most_expensive_node,json=mostExpensiveNode,proto3" json:"most_expensive_node,omitempty"` // Node with highest cost. + LeastExpensiveNode *Node `protobuf:"bytes,16,opt,name=least_expensive_node,json=leastExpensiveNode,proto3" json:"least_expensive_node,omitempty"` // Node with lowest cost. + MostUnderutilizedNode *Node `protobuf:"bytes,17,opt,name=most_underutilized_node,json=mostUnderutilizedNode,proto3" json:"most_underutilized_node,omitempty"` // Node with lowest resource utilization. + MostUnderutilizedContainer *Container `protobuf:"bytes,18,opt,name=most_underutilized_container,json=mostUnderutilizedContainer,proto3" json:"most_underutilized_container,omitempty"` // Container with lowest resource utilization. + CpuCostPerHour float64 `protobuf:"fixed64,19,opt,name=cpu_cost_per_hour,json=cpuCostPerHour,proto3" json:"cpu_cost_per_hour,omitempty"` // Price per vcpu per hour. + MemoryCostPerHour float64 `protobuf:"fixed64,20,opt,name=memory_cost_per_hour,json=memoryCostPerHour,proto3" json:"memory_cost_per_hour,omitempty"` // Pricer per GiB per hour. + HasBeenUpdated bool `protobuf:"varint,21,opt,name=has_been_updated,json=hasBeenUpdated,proto3" json:"has_been_updated,omitempty"` // If cluster has been updated by users. + CustomName string `protobuf:"bytes,22,opt,name=custom_name,json=customName,proto3" json:"custom_name,omitempty"` // Custom name for the cluster. + DisplayName string `protobuf:"bytes,23,opt,name=display_name,json=displayName,proto3" json:"display_name,omitempty"` // Display name for the cluster. + IsPriceAvailable bool `protobuf:"varint,24,opt,name=is_price_available,json=isPriceAvailable,proto3" json:"is_price_available,omitempty"` // Check if we can calculate pricing for cluster automatically + GpuCostPerHour float64 `protobuf:"fixed64,25,opt,name=gpu_cost_per_hour,json=gpuCostPerHour,proto3" json:"gpu_cost_per_hour,omitempty"` // Pricer per GPU per hour. + IsDisconnected *bool `protobuf:"varint,40,opt,name=is_disconnected,json=isDisconnected,proto3,oneof" json:"is_disconnected,omitempty"` + ZxpInfo *OperatorInfo `protobuf:"bytes,50,opt,name=zxp_info,json=zxpInfo,proto3" json:"zxp_info,omitempty"` + ZxpHelmInfo *OperatorInfo `protobuf:"bytes,51,opt,name=zxp_helm_info,json=zxpHelmInfo,proto3" json:"zxp_helm_info,omitempty"` + DakrOpInfo *OperatorInfo `protobuf:"bytes,60,opt,name=dakr_op_info,json=dakrOpInfo,proto3" json:"dakr_op_info,omitempty"` + NodeOpInfo *OperatorInfo `protobuf:"bytes,61,opt,name=node_op_info,json=nodeOpInfo,proto3" json:"node_op_info,omitempty"` + SecurityOpInfo *OperatorInfo `protobuf:"bytes,62,opt,name=security_op_info,json=securityOpInfo,proto3" json:"security_op_info,omitempty"` + NetworkOpInfo *OperatorInfo `protobuf:"bytes,63,opt,name=network_op_info,json=networkOpInfo,proto3" json:"network_op_info,omitempty"` + ImpactScore float64 `protobuf:"fixed64,70,opt,name=impact_score,json=impactScore,proto3" json:"impact_score,omitempty"` // Optimization impact score for cluster + Underutilization float64 `protobuf:"fixed64,81,opt,name=underutilization,proto3" json:"underutilization,omitempty"` // Percentage of how much is cluster underutilized that is using combined numbers of cpu, memory and GPU + Tags []string `protobuf:"bytes,91,rep,name=tags,proto3" json:"tags,omitempty"` // Tags for the cluster + HasArgoWorkloads bool `protobuf:"varint,101,opt,name=has_argo_workloads,json=hasArgoWorkloads,proto3" json:"has_argo_workloads,omitempty"` // Whether any workload on this cluster is managed by Argo CD + KubernetesVersion string `protobuf:"bytes,110,opt,name=kubernetes_version,json=kubernetesVersion,proto3" json:"kubernetes_version,omitempty"` // Kubernetes version of the cluster (e.g. "v1.28.3") } -func (x *MetadataForWorkloadsRequest) Reset() { - *x = MetadataForWorkloadsRequest{} +func (x *Cluster) Reset() { + *x = Cluster{} if protoimpl.UnsafeEnabled { - mi := &file_api_v1_k8s_proto_msgTypes[102] + mi := &file_api_v1_k8s_proto_msgTypes[109] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } -func (x *MetadataForWorkloadsRequest) String() string { +func (x *Cluster) String() string { return protoimpl.X.MessageStringOf(x) } -func (*MetadataForWorkloadsRequest) ProtoMessage() {} +func (*Cluster) ProtoMessage() {} -func (x *MetadataForWorkloadsRequest) ProtoReflect() protoreflect.Message { - mi := &file_api_v1_k8s_proto_msgTypes[102] +func (x *Cluster) ProtoReflect() protoreflect.Message { + mi := &file_api_v1_k8s_proto_msgTypes[109] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -7878,331 +8625,297 @@ func (x *MetadataForWorkloadsRequest) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use MetadataForWorkloadsRequest.ProtoReflect.Descriptor instead. -func (*MetadataForWorkloadsRequest) Descriptor() ([]byte, []int) { - return file_api_v1_k8s_proto_rawDescGZIP(), []int{102} +// Deprecated: Use Cluster.ProtoReflect.Descriptor instead. +func (*Cluster) Descriptor() ([]byte, []int) { + return file_api_v1_k8s_proto_rawDescGZIP(), []int{109} } -func (x *MetadataForWorkloadsRequest) GetTeamId() string { +func (x *Cluster) GetId() string { if x != nil { - return x.TeamId + return x.Id } return "" } -func (x *MetadataForWorkloadsRequest) GetClusterId() string { +func (x *Cluster) GetName() string { if x != nil { - return x.ClusterId + return x.Name } return "" } -func (x *MetadataForWorkloadsRequest) GetWorkloadUidToKind() map[string]K8SObjectKind { +func (x *Cluster) GetTeamId() string { if x != nil { - return x.WorkloadUidToKind + return x.TeamId } - return nil + return "" } -func (x *MetadataForWorkloadsRequest) GetIncludeDeleted() bool { +func (x *Cluster) GetCloudProviderId() string { if x != nil { - return x.IncludeDeleted + return x.CloudProviderId } - return false + return "" } -type MetadataForWorkloadsResponse struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - WorkloadUidToMetadata map[string]*WorkloadMetadata `protobuf:"bytes,1,rep,name=workload_uid_to_metadata,json=workloadUidToMetadata,proto3" json:"workload_uid_to_metadata,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` +func (x *Cluster) GetRegionId() string { + if x != nil { + return x.RegionId + } + return "" } -func (x *MetadataForWorkloadsResponse) Reset() { - *x = MetadataForWorkloadsResponse{} - if protoimpl.UnsafeEnabled { - mi := &file_api_v1_k8s_proto_msgTypes[103] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) +func (x *Cluster) GetCloudProvider() string { + if x != nil { + return x.CloudProvider } + return "" } -func (x *MetadataForWorkloadsResponse) String() string { - return protoimpl.X.MessageStringOf(x) +func (x *Cluster) GetRegion() string { + if x != nil { + return x.Region + } + return "" } -func (*MetadataForWorkloadsResponse) ProtoMessage() {} - -func (x *MetadataForWorkloadsResponse) ProtoReflect() protoreflect.Message { - mi := &file_api_v1_k8s_proto_msgTypes[103] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms +func (x *Cluster) GetCreatedAt() int64 { + if x != nil { + return x.CreatedAt } - return mi.MessageOf(x) + return 0 } -// Deprecated: Use MetadataForWorkloadsResponse.ProtoReflect.Descriptor instead. -func (*MetadataForWorkloadsResponse) Descriptor() ([]byte, []int) { - return file_api_v1_k8s_proto_rawDescGZIP(), []int{103} +func (x *Cluster) GetUpdatedAt() int64 { + if x != nil { + return x.UpdatedAt + } + return 0 } -func (x *MetadataForWorkloadsResponse) GetWorkloadUidToMetadata() map[string]*WorkloadMetadata { +func (x *Cluster) GetResourceMetrics() *ResourceMetrics { if x != nil { - return x.WorkloadUidToMetadata + return x.ResourceMetrics } return nil } -type AddClusterTagsRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - TeamId string `protobuf:"bytes,1,opt,name=team_id,json=teamId,proto3" json:"team_id,omitempty"` - ClusterId string `protobuf:"bytes,2,opt,name=cluster_id,json=clusterId,proto3" json:"cluster_id,omitempty"` - Tags []string `protobuf:"bytes,3,rep,name=tags,proto3" json:"tags,omitempty"` -} - -func (x *AddClusterTagsRequest) Reset() { - *x = AddClusterTagsRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_api_v1_k8s_proto_msgTypes[104] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) +func (x *Cluster) GetCostInfo() *CostInfo { + if x != nil { + return x.CostInfo } + return nil } -func (x *AddClusterTagsRequest) String() string { - return protoimpl.X.MessageStringOf(x) +func (x *Cluster) GetNodeInfo() *NodeInfo { + if x != nil { + return x.NodeInfo + } + return nil } -func (*AddClusterTagsRequest) ProtoMessage() {} - -func (x *AddClusterTagsRequest) ProtoReflect() protoreflect.Message { - mi := &file_api_v1_k8s_proto_msgTypes[104] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms +func (x *Cluster) GetCostDataPoints() []*CostDataPoint { + if x != nil { + return x.CostDataPoints } - return mi.MessageOf(x) + return nil } -// Deprecated: Use AddClusterTagsRequest.ProtoReflect.Descriptor instead. -func (*AddClusterTagsRequest) Descriptor() ([]byte, []int) { - return file_api_v1_k8s_proto_rawDescGZIP(), []int{104} +func (x *Cluster) GetResourceDataPoints() []*ResourceDataPoint { + if x != nil { + return x.ResourceDataPoints + } + return nil } -func (x *AddClusterTagsRequest) GetTeamId() string { +func (x *Cluster) GetMostExpensiveNode() *Node { if x != nil { - return x.TeamId + return x.MostExpensiveNode } - return "" + return nil } -func (x *AddClusterTagsRequest) GetClusterId() string { +func (x *Cluster) GetLeastExpensiveNode() *Node { if x != nil { - return x.ClusterId + return x.LeastExpensiveNode } - return "" + return nil } -func (x *AddClusterTagsRequest) GetTags() []string { +func (x *Cluster) GetMostUnderutilizedNode() *Node { if x != nil { - return x.Tags + return x.MostUnderutilizedNode } return nil } -type AddClusterTagsResponse struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Cluster *Cluster `protobuf:"bytes,1,opt,name=cluster,proto3" json:"cluster,omitempty"` +func (x *Cluster) GetMostUnderutilizedContainer() *Container { + if x != nil { + return x.MostUnderutilizedContainer + } + return nil } -func (x *AddClusterTagsResponse) Reset() { - *x = AddClusterTagsResponse{} - if protoimpl.UnsafeEnabled { - mi := &file_api_v1_k8s_proto_msgTypes[105] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) +func (x *Cluster) GetCpuCostPerHour() float64 { + if x != nil { + return x.CpuCostPerHour } + return 0 } -func (x *AddClusterTagsResponse) String() string { - return protoimpl.X.MessageStringOf(x) +func (x *Cluster) GetMemoryCostPerHour() float64 { + if x != nil { + return x.MemoryCostPerHour + } + return 0 } -func (*AddClusterTagsResponse) ProtoMessage() {} - -func (x *AddClusterTagsResponse) ProtoReflect() protoreflect.Message { - mi := &file_api_v1_k8s_proto_msgTypes[105] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms +func (x *Cluster) GetHasBeenUpdated() bool { + if x != nil { + return x.HasBeenUpdated } - return mi.MessageOf(x) + return false } -// Deprecated: Use AddClusterTagsResponse.ProtoReflect.Descriptor instead. -func (*AddClusterTagsResponse) Descriptor() ([]byte, []int) { - return file_api_v1_k8s_proto_rawDescGZIP(), []int{105} +func (x *Cluster) GetCustomName() string { + if x != nil { + return x.CustomName + } + return "" } -func (x *AddClusterTagsResponse) GetCluster() *Cluster { +func (x *Cluster) GetDisplayName() string { if x != nil { - return x.Cluster + return x.DisplayName } - return nil + return "" } -type RemoveClusterTagsRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - TeamId string `protobuf:"bytes,1,opt,name=team_id,json=teamId,proto3" json:"team_id,omitempty"` - ClusterId string `protobuf:"bytes,2,opt,name=cluster_id,json=clusterId,proto3" json:"cluster_id,omitempty"` - Tags []string `protobuf:"bytes,3,rep,name=tags,proto3" json:"tags,omitempty"` +func (x *Cluster) GetIsPriceAvailable() bool { + if x != nil { + return x.IsPriceAvailable + } + return false } -func (x *RemoveClusterTagsRequest) Reset() { - *x = RemoveClusterTagsRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_api_v1_k8s_proto_msgTypes[106] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) +func (x *Cluster) GetGpuCostPerHour() float64 { + if x != nil { + return x.GpuCostPerHour } + return 0 } -func (x *RemoveClusterTagsRequest) String() string { - return protoimpl.X.MessageStringOf(x) +func (x *Cluster) GetIsDisconnected() bool { + if x != nil && x.IsDisconnected != nil { + return *x.IsDisconnected + } + return false } -func (*RemoveClusterTagsRequest) ProtoMessage() {} - -func (x *RemoveClusterTagsRequest) ProtoReflect() protoreflect.Message { - mi := &file_api_v1_k8s_proto_msgTypes[106] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms +func (x *Cluster) GetZxpInfo() *OperatorInfo { + if x != nil { + return x.ZxpInfo } - return mi.MessageOf(x) + return nil } -// Deprecated: Use RemoveClusterTagsRequest.ProtoReflect.Descriptor instead. -func (*RemoveClusterTagsRequest) Descriptor() ([]byte, []int) { - return file_api_v1_k8s_proto_rawDescGZIP(), []int{106} +func (x *Cluster) GetZxpHelmInfo() *OperatorInfo { + if x != nil { + return x.ZxpHelmInfo + } + return nil } -func (x *RemoveClusterTagsRequest) GetTeamId() string { +func (x *Cluster) GetDakrOpInfo() *OperatorInfo { if x != nil { - return x.TeamId + return x.DakrOpInfo } - return "" + return nil } -func (x *RemoveClusterTagsRequest) GetClusterId() string { +func (x *Cluster) GetNodeOpInfo() *OperatorInfo { if x != nil { - return x.ClusterId + return x.NodeOpInfo } - return "" + return nil } -func (x *RemoveClusterTagsRequest) GetTags() []string { +func (x *Cluster) GetSecurityOpInfo() *OperatorInfo { if x != nil { - return x.Tags + return x.SecurityOpInfo } return nil } -type RemoveClusterTagsResponse struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Cluster *Cluster `protobuf:"bytes,1,opt,name=cluster,proto3" json:"cluster,omitempty"` +func (x *Cluster) GetNetworkOpInfo() *OperatorInfo { + if x != nil { + return x.NetworkOpInfo + } + return nil } -func (x *RemoveClusterTagsResponse) Reset() { - *x = RemoveClusterTagsResponse{} - if protoimpl.UnsafeEnabled { - mi := &file_api_v1_k8s_proto_msgTypes[107] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) +func (x *Cluster) GetImpactScore() float64 { + if x != nil { + return x.ImpactScore } + return 0 } -func (x *RemoveClusterTagsResponse) String() string { - return protoimpl.X.MessageStringOf(x) +func (x *Cluster) GetUnderutilization() float64 { + if x != nil { + return x.Underutilization + } + return 0 } -func (*RemoveClusterTagsResponse) ProtoMessage() {} - -func (x *RemoveClusterTagsResponse) ProtoReflect() protoreflect.Message { - mi := &file_api_v1_k8s_proto_msgTypes[107] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms +func (x *Cluster) GetTags() []string { + if x != nil { + return x.Tags } - return mi.MessageOf(x) + return nil } -// Deprecated: Use RemoveClusterTagsResponse.ProtoReflect.Descriptor instead. -func (*RemoveClusterTagsResponse) Descriptor() ([]byte, []int) { - return file_api_v1_k8s_proto_rawDescGZIP(), []int{107} +func (x *Cluster) GetHasArgoWorkloads() bool { + if x != nil { + return x.HasArgoWorkloads + } + return false } -func (x *RemoveClusterTagsResponse) GetCluster() *Cluster { +func (x *Cluster) GetKubernetesVersion() string { if x != nil { - return x.Cluster + return x.KubernetesVersion } - return nil + return "" } -type ListTagsRequest struct { +type OperatorInfo struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - TeamId string `protobuf:"bytes,1,opt,name=team_id,json=teamId,proto3" json:"team_id,omitempty"` + Version string `protobuf:"bytes,1,opt,name=version,proto3" json:"version,omitempty"` + Commit string `protobuf:"bytes,11,opt,name=commit,proto3" json:"commit,omitempty"` + Date string `protobuf:"bytes,21,opt,name=date,proto3" json:"date,omitempty"` } -func (x *ListTagsRequest) Reset() { - *x = ListTagsRequest{} +func (x *OperatorInfo) Reset() { + *x = OperatorInfo{} if protoimpl.UnsafeEnabled { - mi := &file_api_v1_k8s_proto_msgTypes[108] + mi := &file_api_v1_k8s_proto_msgTypes[110] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } -func (x *ListTagsRequest) String() string { +func (x *OperatorInfo) String() string { return protoimpl.X.MessageStringOf(x) } -func (*ListTagsRequest) ProtoMessage() {} +func (*OperatorInfo) ProtoMessage() {} -func (x *ListTagsRequest) ProtoReflect() protoreflect.Message { - mi := &file_api_v1_k8s_proto_msgTypes[108] +func (x *OperatorInfo) ProtoReflect() protoreflect.Message { + mi := &file_api_v1_k8s_proto_msgTypes[110] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -8213,44 +8926,60 @@ func (x *ListTagsRequest) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use ListTagsRequest.ProtoReflect.Descriptor instead. -func (*ListTagsRequest) Descriptor() ([]byte, []int) { - return file_api_v1_k8s_proto_rawDescGZIP(), []int{108} +// Deprecated: Use OperatorInfo.ProtoReflect.Descriptor instead. +func (*OperatorInfo) Descriptor() ([]byte, []int) { + return file_api_v1_k8s_proto_rawDescGZIP(), []int{110} } -func (x *ListTagsRequest) GetTeamId() string { +func (x *OperatorInfo) GetVersion() string { if x != nil { - return x.TeamId + return x.Version } return "" } -type TagSummary struct { +func (x *OperatorInfo) GetCommit() string { + if x != nil { + return x.Commit + } + return "" +} + +func (x *OperatorInfo) GetDate() string { + if x != nil { + return x.Date + } + return "" +} + +// Container represents a Kubernetes container with identification and resource metrics. +type Container struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Tag string `protobuf:"bytes,1,opt,name=tag,proto3" json:"tag,omitempty"` - ClusterIds []string `protobuf:"bytes,2,rep,name=cluster_ids,json=clusterIds,proto3" json:"cluster_ids,omitempty"` + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` // Unique identifier for the container. + Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` // Name of the container. + ResourceMetrics *ResourceMetrics `protobuf:"bytes,3,opt,name=resource_metrics,json=resourceMetrics,proto3" json:"resource_metrics,omitempty"` // Resource utilization metrics for the container. } -func (x *TagSummary) Reset() { - *x = TagSummary{} +func (x *Container) Reset() { + *x = Container{} if protoimpl.UnsafeEnabled { - mi := &file_api_v1_k8s_proto_msgTypes[109] + mi := &file_api_v1_k8s_proto_msgTypes[111] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } -func (x *TagSummary) String() string { +func (x *Container) String() string { return protoimpl.X.MessageStringOf(x) } -func (*TagSummary) ProtoMessage() {} +func (*Container) ProtoMessage() {} -func (x *TagSummary) ProtoReflect() protoreflect.Message { - mi := &file_api_v1_k8s_proto_msgTypes[109] +func (x *Container) ProtoReflect() protoreflect.Message { + mi := &file_api_v1_k8s_proto_msgTypes[111] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -8261,50 +8990,55 @@ func (x *TagSummary) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use TagSummary.ProtoReflect.Descriptor instead. -func (*TagSummary) Descriptor() ([]byte, []int) { - return file_api_v1_k8s_proto_rawDescGZIP(), []int{109} +// Deprecated: Use Container.ProtoReflect.Descriptor instead. +func (*Container) Descriptor() ([]byte, []int) { + return file_api_v1_k8s_proto_rawDescGZIP(), []int{111} } -func (x *TagSummary) GetTag() string { +func (x *Container) GetId() string { if x != nil { - return x.Tag + return x.Id } return "" } -func (x *TagSummary) GetClusterIds() []string { +func (x *Container) GetName() string { if x != nil { - return x.ClusterIds + return x.Name + } + return "" +} + +func (x *Container) GetResourceMetrics() *ResourceMetrics { + if x != nil { + return x.ResourceMetrics } return nil } -type ListTagsResponse struct { +type GetLatestOperatorVersionRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - - Tags []*TagSummary `protobuf:"bytes,1,rep,name=tags,proto3" json:"tags,omitempty"` } -func (x *ListTagsResponse) Reset() { - *x = ListTagsResponse{} +func (x *GetLatestOperatorVersionRequest) Reset() { + *x = GetLatestOperatorVersionRequest{} if protoimpl.UnsafeEnabled { - mi := &file_api_v1_k8s_proto_msgTypes[110] + mi := &file_api_v1_k8s_proto_msgTypes[112] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } -func (x *ListTagsResponse) String() string { +func (x *GetLatestOperatorVersionRequest) String() string { return protoimpl.X.MessageStringOf(x) } -func (*ListTagsResponse) ProtoMessage() {} +func (*GetLatestOperatorVersionRequest) ProtoMessage() {} -func (x *ListTagsResponse) ProtoReflect() protoreflect.Message { - mi := &file_api_v1_k8s_proto_msgTypes[110] +func (x *GetLatestOperatorVersionRequest) ProtoReflect() protoreflect.Message { + mi := &file_api_v1_k8s_proto_msgTypes[112] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -8315,50 +9049,52 @@ func (x *ListTagsResponse) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use ListTagsResponse.ProtoReflect.Descriptor instead. -func (*ListTagsResponse) Descriptor() ([]byte, []int) { - return file_api_v1_k8s_proto_rawDescGZIP(), []int{110} -} - -func (x *ListTagsResponse) GetTags() []*TagSummary { - if x != nil { - return x.Tags - } - return nil +// Deprecated: Use GetLatestOperatorVersionRequest.ProtoReflect.Descriptor instead. +func (*GetLatestOperatorVersionRequest) Descriptor() ([]byte, []int) { + return file_api_v1_k8s_proto_rawDescGZIP(), []int{112} } -type WorkloadMetadata struct { +type GetLatestOperatorVersionResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - PodUidToNodeMetadata map[string]*NodeMetadata `protobuf:"bytes,1,rep,name=pod_uid_to_node_metadata,json=podUidToNodeMetadata,proto3" json:"pod_uid_to_node_metadata,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` - PodUidToPodMetadata map[string]*PodMetadata `protobuf:"bytes,2,rep,name=pod_uid_to_pod_metadata,json=podUidToPodMetadata,proto3" json:"pod_uid_to_pod_metadata,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` - Label string `protobuf:"bytes,6,opt,name=label,proto3" json:"label,omitempty"` - Annotation string `protobuf:"bytes,7,opt,name=annotation,proto3" json:"annotation,omitempty"` - Spec string `protobuf:"bytes,8,opt,name=spec,proto3" json:"spec,omitempty"` - Status string `protobuf:"bytes,9,opt,name=status,proto3" json:"status,omitempty"` - Metadata string `protobuf:"bytes,10,opt,name=metadata,proto3" json:"metadata,omitempty"` - Kind K8SObjectKind `protobuf:"varint,11,opt,name=kind,proto3,enum=api.v1.K8SObjectKind" json:"kind,omitempty"` + ZxpInfo *OperatorInfo `protobuf:"bytes,1,opt,name=zxp_info,json=zxpInfo,proto3" json:"zxp_info,omitempty"` + ZxpHelmInfo *OperatorInfo `protobuf:"bytes,2,opt,name=zxp_helm_info,json=zxpHelmInfo,proto3" json:"zxp_helm_info,omitempty"` + ZxpNetmonInfo *OperatorInfo `protobuf:"bytes,3,opt,name=zxp_netmon_info,json=zxpNetmonInfo,proto3" json:"zxp_netmon_info,omitempty"` + ZxpNetmonHelmInfo *OperatorInfo `protobuf:"bytes,4,opt,name=zxp_netmon_helm_info,json=zxpNetmonHelmInfo,proto3" json:"zxp_netmon_helm_info,omitempty"` + DakrOpInfo *OperatorInfo `protobuf:"bytes,11,opt,name=dakr_op_info,json=dakrOpInfo,proto3" json:"dakr_op_info,omitempty"` + DakrOpHelmInfo *OperatorInfo `protobuf:"bytes,12,opt,name=dakr_op_helm_info,json=dakrOpHelmInfo,proto3" json:"dakr_op_helm_info,omitempty"` + NodeOpInfoAws *OperatorInfo `protobuf:"bytes,21,opt,name=node_op_info_aws,json=nodeOpInfoAws,proto3" json:"node_op_info_aws,omitempty"` + NodeOpInfoAzure *OperatorInfo `protobuf:"bytes,22,opt,name=node_op_info_azure,json=nodeOpInfoAzure,proto3" json:"node_op_info_azure,omitempty"` + NodeOpInfoGcp *OperatorInfo `protobuf:"bytes,23,opt,name=node_op_info_gcp,json=nodeOpInfoGcp,proto3" json:"node_op_info_gcp,omitempty"` + NodeOpInfoOci *OperatorInfo `protobuf:"bytes,24,opt,name=node_op_info_oci,json=nodeOpInfoOci,proto3" json:"node_op_info_oci,omitempty"` + SecurityOpInfo *OperatorInfo `protobuf:"bytes,31,opt,name=security_op_info,json=securityOpInfo,proto3" json:"security_op_info,omitempty"` + SecurityOpHelmInfo *OperatorInfo `protobuf:"bytes,32,opt,name=security_op_helm_info,json=securityOpHelmInfo,proto3" json:"security_op_helm_info,omitempty"` + TrezrInfo *OperatorInfo `protobuf:"bytes,41,opt,name=trezr_info,json=trezrInfo,proto3" json:"trezr_info,omitempty"` + TrezrHelmInfo *OperatorInfo `protobuf:"bytes,42,opt,name=trezr_helm_info,json=trezrHelmInfo,proto3" json:"trezr_helm_info,omitempty"` + // checkpoint-restore-operator helm chart (snapshot policies). Placeholder + // version until the DevZero-authored chart is published. + SnapshotHelmInfo *OperatorInfo `protobuf:"bytes,51,opt,name=snapshot_helm_info,json=snapshotHelmInfo,proto3" json:"snapshot_helm_info,omitempty"` } -func (x *WorkloadMetadata) Reset() { - *x = WorkloadMetadata{} +func (x *GetLatestOperatorVersionResponse) Reset() { + *x = GetLatestOperatorVersionResponse{} if protoimpl.UnsafeEnabled { - mi := &file_api_v1_k8s_proto_msgTypes[111] + mi := &file_api_v1_k8s_proto_msgTypes[113] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } -func (x *WorkloadMetadata) String() string { +func (x *GetLatestOperatorVersionResponse) String() string { return protoimpl.X.MessageStringOf(x) } -func (*WorkloadMetadata) ProtoMessage() {} +func (*GetLatestOperatorVersionResponse) ProtoMessage() {} -func (x *WorkloadMetadata) ProtoReflect() protoreflect.Message { - mi := &file_api_v1_k8s_proto_msgTypes[111] +func (x *GetLatestOperatorVersionResponse) ProtoReflect() protoreflect.Message { + mi := &file_api_v1_k8s_proto_msgTypes[113] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -8369,187 +9105,144 @@ func (x *WorkloadMetadata) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use WorkloadMetadata.ProtoReflect.Descriptor instead. -func (*WorkloadMetadata) Descriptor() ([]byte, []int) { - return file_api_v1_k8s_proto_rawDescGZIP(), []int{111} +// Deprecated: Use GetLatestOperatorVersionResponse.ProtoReflect.Descriptor instead. +func (*GetLatestOperatorVersionResponse) Descriptor() ([]byte, []int) { + return file_api_v1_k8s_proto_rawDescGZIP(), []int{113} } -func (x *WorkloadMetadata) GetPodUidToNodeMetadata() map[string]*NodeMetadata { +func (x *GetLatestOperatorVersionResponse) GetZxpInfo() *OperatorInfo { if x != nil { - return x.PodUidToNodeMetadata + return x.ZxpInfo } return nil } -func (x *WorkloadMetadata) GetPodUidToPodMetadata() map[string]*PodMetadata { +func (x *GetLatestOperatorVersionResponse) GetZxpHelmInfo() *OperatorInfo { if x != nil { - return x.PodUidToPodMetadata + return x.ZxpHelmInfo } return nil } -func (x *WorkloadMetadata) GetLabel() string { +func (x *GetLatestOperatorVersionResponse) GetZxpNetmonInfo() *OperatorInfo { if x != nil { - return x.Label + return x.ZxpNetmonInfo } - return "" + return nil } -func (x *WorkloadMetadata) GetAnnotation() string { +func (x *GetLatestOperatorVersionResponse) GetZxpNetmonHelmInfo() *OperatorInfo { if x != nil { - return x.Annotation + return x.ZxpNetmonHelmInfo } - return "" + return nil } -func (x *WorkloadMetadata) GetSpec() string { +func (x *GetLatestOperatorVersionResponse) GetDakrOpInfo() *OperatorInfo { if x != nil { - return x.Spec + return x.DakrOpInfo } - return "" + return nil } -func (x *WorkloadMetadata) GetStatus() string { +func (x *GetLatestOperatorVersionResponse) GetDakrOpHelmInfo() *OperatorInfo { if x != nil { - return x.Status + return x.DakrOpHelmInfo } - return "" + return nil } -func (x *WorkloadMetadata) GetMetadata() string { +func (x *GetLatestOperatorVersionResponse) GetNodeOpInfoAws() *OperatorInfo { if x != nil { - return x.Metadata + return x.NodeOpInfoAws } - return "" + return nil } -func (x *WorkloadMetadata) GetKind() K8SObjectKind { +func (x *GetLatestOperatorVersionResponse) GetNodeOpInfoAzure() *OperatorInfo { if x != nil { - return x.Kind + return x.NodeOpInfoAzure } - return K8SObjectKind_K8S_OBJECT_KIND_UNSPECIFIED + return nil } -type PodMetadata struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` - Label string `protobuf:"bytes,6,opt,name=label,proto3" json:"label,omitempty"` - Annotation string `protobuf:"bytes,7,opt,name=annotation,proto3" json:"annotation,omitempty"` - Spec string `protobuf:"bytes,8,opt,name=spec,proto3" json:"spec,omitempty"` - Status string `protobuf:"bytes,9,opt,name=status,proto3" json:"status,omitempty"` - Metadata string `protobuf:"bytes,10,opt,name=metadata,proto3" json:"metadata,omitempty"` -} - -func (x *PodMetadata) Reset() { - *x = PodMetadata{} - if protoimpl.UnsafeEnabled { - mi := &file_api_v1_k8s_proto_msgTypes[112] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *PodMetadata) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*PodMetadata) ProtoMessage() {} - -func (x *PodMetadata) ProtoReflect() protoreflect.Message { - mi := &file_api_v1_k8s_proto_msgTypes[112] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms +func (x *GetLatestOperatorVersionResponse) GetNodeOpInfoGcp() *OperatorInfo { + if x != nil { + return x.NodeOpInfoGcp } - return mi.MessageOf(x) -} - -// Deprecated: Use PodMetadata.ProtoReflect.Descriptor instead. -func (*PodMetadata) Descriptor() ([]byte, []int) { - return file_api_v1_k8s_proto_rawDescGZIP(), []int{112} + return nil } -func (x *PodMetadata) GetName() string { +func (x *GetLatestOperatorVersionResponse) GetNodeOpInfoOci() *OperatorInfo { if x != nil { - return x.Name + return x.NodeOpInfoOci } - return "" + return nil } -func (x *PodMetadata) GetLabel() string { +func (x *GetLatestOperatorVersionResponse) GetSecurityOpInfo() *OperatorInfo { if x != nil { - return x.Label + return x.SecurityOpInfo } - return "" + return nil } -func (x *PodMetadata) GetAnnotation() string { +func (x *GetLatestOperatorVersionResponse) GetSecurityOpHelmInfo() *OperatorInfo { if x != nil { - return x.Annotation + return x.SecurityOpHelmInfo } - return "" + return nil } -func (x *PodMetadata) GetSpec() string { +func (x *GetLatestOperatorVersionResponse) GetTrezrInfo() *OperatorInfo { if x != nil { - return x.Spec + return x.TrezrInfo } - return "" + return nil } -func (x *PodMetadata) GetStatus() string { +func (x *GetLatestOperatorVersionResponse) GetTrezrHelmInfo() *OperatorInfo { if x != nil { - return x.Status + return x.TrezrHelmInfo } - return "" + return nil } -func (x *PodMetadata) GetMetadata() string { +func (x *GetLatestOperatorVersionResponse) GetSnapshotHelmInfo() *OperatorInfo { if x != nil { - return x.Metadata + return x.SnapshotHelmInfo } - return "" + return nil } -type NodeMetadata struct { +type GalaxyGetWorkloadPerspectiveRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - NodeName string `protobuf:"bytes,1,opt,name=node_name,json=nodeName,proto3" json:"node_name,omitempty"` - NodeGroupName string `protobuf:"bytes,2,opt,name=node_group_name,json=nodeGroupName,proto3" json:"node_group_name,omitempty"` - NodeGroupType string `protobuf:"bytes,3,opt,name=node_group_type,json=nodeGroupType,proto3" json:"node_group_type,omitempty"` - CloudInstTypeName string `protobuf:"bytes,4,opt,name=cloud_inst_type_name,json=cloudInstTypeName,proto3" json:"cloud_inst_type_name,omitempty"` - Label string `protobuf:"bytes,6,opt,name=label,proto3" json:"label,omitempty"` - Annotation string `protobuf:"bytes,7,opt,name=annotation,proto3" json:"annotation,omitempty"` - Spec string `protobuf:"bytes,8,opt,name=spec,proto3" json:"spec,omitempty"` - Status string `protobuf:"bytes,9,opt,name=status,proto3" json:"status,omitempty"` - Metadata string `protobuf:"bytes,10,opt,name=metadata,proto3" json:"metadata,omitempty"` + TeamId string `protobuf:"bytes,1,opt,name=team_id,json=teamId,proto3" json:"team_id,omitempty"` + ClusterIds []string `protobuf:"bytes,2,rep,name=cluster_ids,json=clusterIds,proto3" json:"cluster_ids,omitempty"` // (optional) filter by cluster(s) + GroupBy GalaxyPOVWorkloadGroupBy `protobuf:"varint,3,opt,name=group_by,json=groupBy,proto3,enum=api.v1.GalaxyPOVWorkloadGroupBy" json:"group_by,omitempty"` // grouping type (e.g., namespace, cost, etc) + DatapointOpts *PerspectiveDatapointOpts `protobuf:"bytes,4,opt,name=datapoint_opts,json=datapointOpts,proto3" json:"datapoint_opts,omitempty"` // time range / resolution } -func (x *NodeMetadata) Reset() { - *x = NodeMetadata{} +func (x *GalaxyGetWorkloadPerspectiveRequest) Reset() { + *x = GalaxyGetWorkloadPerspectiveRequest{} if protoimpl.UnsafeEnabled { - mi := &file_api_v1_k8s_proto_msgTypes[113] + mi := &file_api_v1_k8s_proto_msgTypes[114] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } -func (x *NodeMetadata) String() string { +func (x *GalaxyGetWorkloadPerspectiveRequest) String() string { return protoimpl.X.MessageStringOf(x) } -func (*NodeMetadata) ProtoMessage() {} +func (*GalaxyGetWorkloadPerspectiveRequest) ProtoMessage() {} -func (x *NodeMetadata) ProtoReflect() protoreflect.Message { - mi := &file_api_v1_k8s_proto_msgTypes[113] +func (x *GalaxyGetWorkloadPerspectiveRequest) ProtoReflect() protoreflect.Message { + mi := &file_api_v1_k8s_proto_msgTypes[114] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -8560,104 +9253,134 @@ func (x *NodeMetadata) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use NodeMetadata.ProtoReflect.Descriptor instead. -func (*NodeMetadata) Descriptor() ([]byte, []int) { - return file_api_v1_k8s_proto_rawDescGZIP(), []int{113} +// Deprecated: Use GalaxyGetWorkloadPerspectiveRequest.ProtoReflect.Descriptor instead. +func (*GalaxyGetWorkloadPerspectiveRequest) Descriptor() ([]byte, []int) { + return file_api_v1_k8s_proto_rawDescGZIP(), []int{114} } -func (x *NodeMetadata) GetNodeName() string { +func (x *GalaxyGetWorkloadPerspectiveRequest) GetTeamId() string { if x != nil { - return x.NodeName + return x.TeamId } return "" } -func (x *NodeMetadata) GetNodeGroupName() string { +func (x *GalaxyGetWorkloadPerspectiveRequest) GetClusterIds() []string { if x != nil { - return x.NodeGroupName + return x.ClusterIds } - return "" + return nil } -func (x *NodeMetadata) GetNodeGroupType() string { +func (x *GalaxyGetWorkloadPerspectiveRequest) GetGroupBy() GalaxyPOVWorkloadGroupBy { if x != nil { - return x.NodeGroupType + return x.GroupBy } - return "" + return GalaxyPOVWorkloadGroupBy_GALAXY_POV_WORKLOAD_GROUP_BY_UNSPECIFIED } -func (x *NodeMetadata) GetCloudInstTypeName() string { +func (x *GalaxyGetWorkloadPerspectiveRequest) GetDatapointOpts() *PerspectiveDatapointOpts { if x != nil { - return x.CloudInstTypeName + return x.DatapointOpts } - return "" + return nil } -func (x *NodeMetadata) GetLabel() string { - if x != nil { - return x.Label +type GalaxyGetWorkloadPerspectiveResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Groupings []*GalaxyWorkloadGroup `protobuf:"bytes,1,rep,name=groupings,proto3" json:"groupings,omitempty"` + Clusters map[string]*Cluster `protobuf:"bytes,2,rep,name=clusters,proto3" json:"clusters,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` // cluster_id -> cluster details for all clusters referenced in groupings + Workloads map[string]*WorkloadItem `protobuf:"bytes,3,rep,name=workloads,proto3" json:"workloads,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` // workload_uid -> workload details for all workloads referenced in groupings +} + +func (x *GalaxyGetWorkloadPerspectiveResponse) Reset() { + *x = GalaxyGetWorkloadPerspectiveResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_api_v1_k8s_proto_msgTypes[115] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } - return "" } -func (x *NodeMetadata) GetAnnotation() string { - if x != nil { - return x.Annotation +func (x *GalaxyGetWorkloadPerspectiveResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GalaxyGetWorkloadPerspectiveResponse) ProtoMessage() {} + +func (x *GalaxyGetWorkloadPerspectiveResponse) ProtoReflect() protoreflect.Message { + mi := &file_api_v1_k8s_proto_msgTypes[115] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms } - return "" + return mi.MessageOf(x) } -func (x *NodeMetadata) GetSpec() string { +// Deprecated: Use GalaxyGetWorkloadPerspectiveResponse.ProtoReflect.Descriptor instead. +func (*GalaxyGetWorkloadPerspectiveResponse) Descriptor() ([]byte, []int) { + return file_api_v1_k8s_proto_rawDescGZIP(), []int{115} +} + +func (x *GalaxyGetWorkloadPerspectiveResponse) GetGroupings() []*GalaxyWorkloadGroup { if x != nil { - return x.Spec + return x.Groupings } - return "" + return nil } -func (x *NodeMetadata) GetStatus() string { +func (x *GalaxyGetWorkloadPerspectiveResponse) GetClusters() map[string]*Cluster { if x != nil { - return x.Status + return x.Clusters } - return "" + return nil } -func (x *NodeMetadata) GetMetadata() string { +func (x *GalaxyGetWorkloadPerspectiveResponse) GetWorkloads() map[string]*WorkloadItem { if x != nil { - return x.Metadata + return x.Workloads } - return "" + return nil } -// GetRelatedResourcesRequest is used to fetch relations for a specific kind -type GetRelatedResourcesRequest struct { +type GalaxyWorkloadGroup struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - TeamId string `protobuf:"bytes,1,opt,name=team_id,json=teamId,proto3" json:"team_id,omitempty"` // Unique identifier for the team. - ClusterId string `protobuf:"bytes,2,opt,name=cluster_id,json=clusterId,proto3" json:"cluster_id,omitempty"` // Unique identifier for the cluster. - Kind K8SObjectKind `protobuf:"varint,3,opt,name=kind,proto3,enum=api.v1.K8SObjectKind" json:"kind,omitempty"` // Type of object to get. - Uid string `protobuf:"bytes,4,opt,name=uid,proto3" json:"uid,omitempty"` // Unique identifier for the workload. - KarpenterKind *string `protobuf:"bytes,5,opt,name=karpenterKind,proto3,oneof" json:"karpenterKind,omitempty"` // used for karpenter deployments + GroupKey string `protobuf:"bytes,1,opt,name=group_key,json=groupKey,proto3" json:"group_key,omitempty"` // e.g., "namespace-a", "Deployment", "high-priority" + // TODO (debo) might want to change workload_uids to just workload (same as GetWorkloads etc) + WorkloadUids []string `protobuf:"bytes,2,rep,name=workload_uids,json=workloadUids,proto3" json:"workload_uids,omitempty"` // workload UIDs in this group + // TODO (debo) might want to change cluster_ids to just cluster (same as GetClusters etc) + ClusterIds []string `protobuf:"bytes,3,rep,name=cluster_ids,json=clusterIds,proto3" json:"cluster_ids,omitempty"` // clusters these workloads belong to + CostInfo *CostInfo `protobuf:"bytes,4,opt,name=cost_info,json=costInfo,proto3" json:"cost_info,omitempty"` + ResourceMetrics *ResourceMetrics `protobuf:"bytes,5,opt,name=resource_metrics,json=resourceMetrics,proto3" json:"resource_metrics,omitempty"` + ResourceSummary *ResourceSummary `protobuf:"bytes,6,opt,name=resource_summary,json=resourceSummary,proto3" json:"resource_summary,omitempty"` // (optional) aggregated resource kind/status info } -func (x *GetRelatedResourcesRequest) Reset() { - *x = GetRelatedResourcesRequest{} +func (x *GalaxyWorkloadGroup) Reset() { + *x = GalaxyWorkloadGroup{} if protoimpl.UnsafeEnabled { - mi := &file_api_v1_k8s_proto_msgTypes[114] + mi := &file_api_v1_k8s_proto_msgTypes[116] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } -func (x *GetRelatedResourcesRequest) String() string { +func (x *GalaxyWorkloadGroup) String() string { return protoimpl.X.MessageStringOf(x) } -func (*GetRelatedResourcesRequest) ProtoMessage() {} +func (*GalaxyWorkloadGroup) ProtoMessage() {} -func (x *GetRelatedResourcesRequest) ProtoReflect() protoreflect.Message { - mi := &file_api_v1_k8s_proto_msgTypes[114] +func (x *GalaxyWorkloadGroup) ProtoReflect() protoreflect.Message { + mi := &file_api_v1_k8s_proto_msgTypes[116] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -8668,74 +9391,80 @@ func (x *GetRelatedResourcesRequest) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use GetRelatedResourcesRequest.ProtoReflect.Descriptor instead. -func (*GetRelatedResourcesRequest) Descriptor() ([]byte, []int) { - return file_api_v1_k8s_proto_rawDescGZIP(), []int{114} +// Deprecated: Use GalaxyWorkloadGroup.ProtoReflect.Descriptor instead. +func (*GalaxyWorkloadGroup) Descriptor() ([]byte, []int) { + return file_api_v1_k8s_proto_rawDescGZIP(), []int{116} } -func (x *GetRelatedResourcesRequest) GetTeamId() string { +func (x *GalaxyWorkloadGroup) GetGroupKey() string { if x != nil { - return x.TeamId + return x.GroupKey } return "" } -func (x *GetRelatedResourcesRequest) GetClusterId() string { +func (x *GalaxyWorkloadGroup) GetWorkloadUids() []string { if x != nil { - return x.ClusterId + return x.WorkloadUids } - return "" + return nil } -func (x *GetRelatedResourcesRequest) GetKind() K8SObjectKind { +func (x *GalaxyWorkloadGroup) GetClusterIds() []string { if x != nil { - return x.Kind + return x.ClusterIds } - return K8SObjectKind_K8S_OBJECT_KIND_UNSPECIFIED + return nil } -func (x *GetRelatedResourcesRequest) GetUid() string { +func (x *GalaxyWorkloadGroup) GetCostInfo() *CostInfo { if x != nil { - return x.Uid + return x.CostInfo } - return "" + return nil } -func (x *GetRelatedResourcesRequest) GetKarpenterKind() string { - if x != nil && x.KarpenterKind != nil { - return *x.KarpenterKind +func (x *GalaxyWorkloadGroup) GetResourceMetrics() *ResourceMetrics { + if x != nil { + return x.ResourceMetrics } - return "" + return nil } -type GetWorkloadPodHistoryResponse struct { +func (x *GalaxyWorkloadGroup) GetResourceSummary() *ResourceSummary { + if x != nil { + return x.ResourceSummary + } + return nil +} + +type PerspectiveDatapointOpts struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - WorkloadUid string `protobuf:"bytes,1,opt,name=workload_uid,json=workloadUid,proto3" json:"workload_uid,omitempty"` - ReplicaSets []*ReplicaSetHistory `protobuf:"bytes,2,rep,name=replica_sets,json=replicaSets,proto3" json:"replica_sets,omitempty"` // Populated if workload is Deployment or ArgoRollout - DirectPods []*PodHistory `protobuf:"bytes,3,rep,name=direct_pods,json=directPods,proto3" json:"direct_pods,omitempty"` // Populated if workload directly owns pods (e.g. StatefulSet, DaemonSet) - Jobs []*JobHistory `protobuf:"bytes,4,rep,name=jobs,proto3" json:"jobs,omitempty"` // Populated if workload is CronJob + TimeStart int64 `protobuf:"varint,1,opt,name=time_start,json=timeStart,proto3" json:"time_start,omitempty"` + TimeStop int64 `protobuf:"varint,2,opt,name=time_stop,json=timeStop,proto3" json:"time_stop,omitempty"` + NumDatapointsInTimePeriod int64 `protobuf:"varint,3,opt,name=num_datapoints_in_time_period,json=numDatapointsInTimePeriod,proto3" json:"num_datapoints_in_time_period,omitempty"` } -func (x *GetWorkloadPodHistoryResponse) Reset() { - *x = GetWorkloadPodHistoryResponse{} +func (x *PerspectiveDatapointOpts) Reset() { + *x = PerspectiveDatapointOpts{} if protoimpl.UnsafeEnabled { - mi := &file_api_v1_k8s_proto_msgTypes[115] + mi := &file_api_v1_k8s_proto_msgTypes[117] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } -func (x *GetWorkloadPodHistoryResponse) String() string { +func (x *PerspectiveDatapointOpts) String() string { return protoimpl.X.MessageStringOf(x) } -func (*GetWorkloadPodHistoryResponse) ProtoMessage() {} +func (*PerspectiveDatapointOpts) ProtoMessage() {} -func (x *GetWorkloadPodHistoryResponse) ProtoReflect() protoreflect.Message { - mi := &file_api_v1_k8s_proto_msgTypes[115] +func (x *PerspectiveDatapointOpts) ProtoReflect() protoreflect.Message { + mi := &file_api_v1_k8s_proto_msgTypes[117] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -8746,69 +9475,73 @@ func (x *GetWorkloadPodHistoryResponse) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use GetWorkloadPodHistoryResponse.ProtoReflect.Descriptor instead. -func (*GetWorkloadPodHistoryResponse) Descriptor() ([]byte, []int) { - return file_api_v1_k8s_proto_rawDescGZIP(), []int{115} -} - -func (x *GetWorkloadPodHistoryResponse) GetWorkloadUid() string { - if x != nil { - return x.WorkloadUid - } - return "" +// Deprecated: Use PerspectiveDatapointOpts.ProtoReflect.Descriptor instead. +func (*PerspectiveDatapointOpts) Descriptor() ([]byte, []int) { + return file_api_v1_k8s_proto_rawDescGZIP(), []int{117} } -func (x *GetWorkloadPodHistoryResponse) GetReplicaSets() []*ReplicaSetHistory { +func (x *PerspectiveDatapointOpts) GetTimeStart() int64 { if x != nil { - return x.ReplicaSets + return x.TimeStart } - return nil + return 0 } -func (x *GetWorkloadPodHistoryResponse) GetDirectPods() []*PodHistory { +func (x *PerspectiveDatapointOpts) GetTimeStop() int64 { if x != nil { - return x.DirectPods + return x.TimeStop } - return nil + return 0 } -func (x *GetWorkloadPodHistoryResponse) GetJobs() []*JobHistory { +func (x *PerspectiveDatapointOpts) GetNumDatapointsInTimePeriod() int64 { if x != nil { - return x.Jobs + return x.NumDatapointsInTimePeriod } - return nil + return 0 } -type JobHistory struct { +// Request for listing audit logs +type ListAuditLogsRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Uid string `protobuf:"bytes,1,opt,name=uid,proto3" json:"uid,omitempty"` - Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` - Status string `protobuf:"bytes,3,opt,name=status,proto3" json:"status,omitempty"` // active, succeeded, failed - CreatedAt *timestamppb.Timestamp `protobuf:"bytes,4,opt,name=created_at,json=createdAt,proto3" json:"created_at,omitempty"` - DeletedAt *timestamppb.Timestamp `protobuf:"bytes,5,opt,name=deleted_at,json=deletedAt,proto3" json:"deleted_at,omitempty"` - Pods []*PodHistory `protobuf:"bytes,6,rep,name=pods,proto3" json:"pods,omitempty"` + TeamId string `protobuf:"bytes,1,opt,name=team_id,json=teamId,proto3" json:"team_id,omitempty"` + ClusterId []string `protobuf:"bytes,2,rep,name=cluster_id,json=clusterId,proto3" json:"cluster_id,omitempty"` + NodeId []string `protobuf:"bytes,3,rep,name=node_id,json=nodeId,proto3" json:"node_id,omitempty"` + WorkloadId []string `protobuf:"bytes,4,rep,name=workload_id,json=workloadId,proto3" json:"workload_id,omitempty"` + WorkloadType []K8SObjectKind `protobuf:"varint,5,rep,packed,name=workload_type,json=workloadType,proto3,enum=api.v1.K8SObjectKind" json:"workload_type,omitempty"` + RecommendationPolicyId []string `protobuf:"bytes,6,rep,name=recommendation_policy_id,json=recommendationPolicyId,proto3" json:"recommendation_policy_id,omitempty"` + RecommendationId []string `protobuf:"bytes,7,rep,name=recommendation_id,json=recommendationId,proto3" json:"recommendation_id,omitempty"` + OriginatingUserId []string `protobuf:"bytes,8,rep,name=originating_user_id,json=originatingUserId,proto3" json:"originating_user_id,omitempty"` + // Deprecated: Marked as deprecated in api/v1/k8s.proto. + EmailContains *string `protobuf:"bytes,9,opt,name=email_contains,json=emailContains,proto3,oneof" json:"email_contains,omitempty"` + Event []string `protobuf:"bytes,10,rep,name=event,proto3" json:"event,omitempty"` + StartTime *timestamppb.Timestamp `protobuf:"bytes,11,opt,name=start_time,json=startTime,proto3" json:"start_time,omitempty"` + EndTime *timestamppb.Timestamp `protobuf:"bytes,12,opt,name=end_time,json=endTime,proto3" json:"end_time,omitempty"` + EmailMatches []string `protobuf:"bytes,13,rep,name=email_matches,json=emailMatches,proto3" json:"email_matches,omitempty"` // Exact match on originating_user_email (equality with IN clause) + Pagination *Pagination `protobuf:"bytes,21,opt,name=pagination,proto3" json:"pagination,omitempty"` + AuditLogIds []string `protobuf:"bytes,31,rep,name=audit_log_ids,json=auditLogIds,proto3" json:"audit_log_ids,omitempty"` } -func (x *JobHistory) Reset() { - *x = JobHistory{} +func (x *ListAuditLogsRequest) Reset() { + *x = ListAuditLogsRequest{} if protoimpl.UnsafeEnabled { - mi := &file_api_v1_k8s_proto_msgTypes[116] + mi := &file_api_v1_k8s_proto_msgTypes[118] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } -func (x *JobHistory) String() string { +func (x *ListAuditLogsRequest) String() string { return protoimpl.X.MessageStringOf(x) } -func (*JobHistory) ProtoMessage() {} +func (*ListAuditLogsRequest) ProtoMessage() {} -func (x *JobHistory) ProtoReflect() protoreflect.Message { - mi := &file_api_v1_k8s_proto_msgTypes[116] +func (x *ListAuditLogsRequest) ProtoReflect() protoreflect.Message { + mi := &file_api_v1_k8s_proto_msgTypes[118] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -8819,171 +9552,144 @@ func (x *JobHistory) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use JobHistory.ProtoReflect.Descriptor instead. -func (*JobHistory) Descriptor() ([]byte, []int) { - return file_api_v1_k8s_proto_rawDescGZIP(), []int{116} +// Deprecated: Use ListAuditLogsRequest.ProtoReflect.Descriptor instead. +func (*ListAuditLogsRequest) Descriptor() ([]byte, []int) { + return file_api_v1_k8s_proto_rawDescGZIP(), []int{118} } -func (x *JobHistory) GetUid() string { +func (x *ListAuditLogsRequest) GetTeamId() string { if x != nil { - return x.Uid + return x.TeamId } return "" } -func (x *JobHistory) GetName() string { +func (x *ListAuditLogsRequest) GetClusterId() []string { if x != nil { - return x.Name + return x.ClusterId } - return "" + return nil } -func (x *JobHistory) GetStatus() string { +func (x *ListAuditLogsRequest) GetNodeId() []string { if x != nil { - return x.Status + return x.NodeId } - return "" + return nil } -func (x *JobHistory) GetCreatedAt() *timestamppb.Timestamp { +func (x *ListAuditLogsRequest) GetWorkloadId() []string { if x != nil { - return x.CreatedAt + return x.WorkloadId } return nil } -func (x *JobHistory) GetDeletedAt() *timestamppb.Timestamp { +func (x *ListAuditLogsRequest) GetWorkloadType() []K8SObjectKind { if x != nil { - return x.DeletedAt + return x.WorkloadType } return nil } -func (x *JobHistory) GetPods() []*PodHistory { +func (x *ListAuditLogsRequest) GetRecommendationPolicyId() []string { if x != nil { - return x.Pods + return x.RecommendationPolicyId } return nil } -type ReplicaSetHistory struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Uid string `protobuf:"bytes,1,opt,name=uid,proto3" json:"uid,omitempty"` - Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` - CreatedAt *timestamppb.Timestamp `protobuf:"bytes,3,opt,name=created_at,json=createdAt,proto3" json:"created_at,omitempty"` - DeletedAt *timestamppb.Timestamp `protobuf:"bytes,4,opt,name=deleted_at,json=deletedAt,proto3" json:"deleted_at,omitempty"` - Revision string `protobuf:"bytes,5,opt,name=revision,proto3" json:"revision,omitempty"` // from adjustments/annotations if available - Pods []*PodHistory `protobuf:"bytes,6,rep,name=pods,proto3" json:"pods,omitempty"` -} - -func (x *ReplicaSetHistory) Reset() { - *x = ReplicaSetHistory{} - if protoimpl.UnsafeEnabled { - mi := &file_api_v1_k8s_proto_msgTypes[117] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) +func (x *ListAuditLogsRequest) GetRecommendationId() []string { + if x != nil { + return x.RecommendationId } + return nil } -func (x *ReplicaSetHistory) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ReplicaSetHistory) ProtoMessage() {} - -func (x *ReplicaSetHistory) ProtoReflect() protoreflect.Message { - mi := &file_api_v1_k8s_proto_msgTypes[117] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms +func (x *ListAuditLogsRequest) GetOriginatingUserId() []string { + if x != nil { + return x.OriginatingUserId } - return mi.MessageOf(x) + return nil } -// Deprecated: Use ReplicaSetHistory.ProtoReflect.Descriptor instead. -func (*ReplicaSetHistory) Descriptor() ([]byte, []int) { - return file_api_v1_k8s_proto_rawDescGZIP(), []int{117} +// Deprecated: Marked as deprecated in api/v1/k8s.proto. +func (x *ListAuditLogsRequest) GetEmailContains() string { + if x != nil && x.EmailContains != nil { + return *x.EmailContains + } + return "" } -func (x *ReplicaSetHistory) GetUid() string { +func (x *ListAuditLogsRequest) GetEvent() []string { if x != nil { - return x.Uid + return x.Event } - return "" + return nil } -func (x *ReplicaSetHistory) GetName() string { +func (x *ListAuditLogsRequest) GetStartTime() *timestamppb.Timestamp { if x != nil { - return x.Name + return x.StartTime } - return "" + return nil } -func (x *ReplicaSetHistory) GetCreatedAt() *timestamppb.Timestamp { +func (x *ListAuditLogsRequest) GetEndTime() *timestamppb.Timestamp { if x != nil { - return x.CreatedAt + return x.EndTime } return nil } -func (x *ReplicaSetHistory) GetDeletedAt() *timestamppb.Timestamp { +func (x *ListAuditLogsRequest) GetEmailMatches() []string { if x != nil { - return x.DeletedAt + return x.EmailMatches } return nil } -func (x *ReplicaSetHistory) GetRevision() string { +func (x *ListAuditLogsRequest) GetPagination() *Pagination { if x != nil { - return x.Revision + return x.Pagination } - return "" + return nil } -func (x *ReplicaSetHistory) GetPods() []*PodHistory { +func (x *ListAuditLogsRequest) GetAuditLogIds() []string { if x != nil { - return x.Pods + return x.AuditLogIds } return nil } -type PodHistory struct { +// Response for listing audit logs +type ListAuditLogsResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Uid string `protobuf:"bytes,1,opt,name=uid,proto3" json:"uid,omitempty"` - Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` - Namespace string `protobuf:"bytes,3,opt,name=namespace,proto3" json:"namespace,omitempty"` - Phase string `protobuf:"bytes,4,opt,name=phase,proto3" json:"phase,omitempty"` - NodeName string `protobuf:"bytes,5,opt,name=node_name,json=nodeName,proto3" json:"node_name,omitempty"` - CreatedAt *timestamppb.Timestamp `protobuf:"bytes,6,opt,name=created_at,json=createdAt,proto3" json:"created_at,omitempty"` - DeletedAt *timestamppb.Timestamp `protobuf:"bytes,7,opt,name=deleted_at,json=deletedAt,proto3" json:"deleted_at,omitempty"` + Logs []*AuditLogEntry `protobuf:"bytes,1,rep,name=logs,proto3" json:"logs,omitempty"` + Pagination *Pagination `protobuf:"bytes,21,opt,name=pagination,proto3" json:"pagination,omitempty"` } -func (x *PodHistory) Reset() { - *x = PodHistory{} +func (x *ListAuditLogsResponse) Reset() { + *x = ListAuditLogsResponse{} if protoimpl.UnsafeEnabled { - mi := &file_api_v1_k8s_proto_msgTypes[118] + mi := &file_api_v1_k8s_proto_msgTypes[119] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } -func (x *PodHistory) String() string { +func (x *ListAuditLogsResponse) String() string { return protoimpl.X.MessageStringOf(x) } -func (*PodHistory) ProtoMessage() {} +func (*ListAuditLogsResponse) ProtoMessage() {} -func (x *PodHistory) ProtoReflect() protoreflect.Message { - mi := &file_api_v1_k8s_proto_msgTypes[118] +func (x *ListAuditLogsResponse) ProtoReflect() protoreflect.Message { + mi := &file_api_v1_k8s_proto_msgTypes[119] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -8994,87 +9700,64 @@ func (x *PodHistory) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use PodHistory.ProtoReflect.Descriptor instead. -func (*PodHistory) Descriptor() ([]byte, []int) { - return file_api_v1_k8s_proto_rawDescGZIP(), []int{118} -} - -func (x *PodHistory) GetUid() string { - if x != nil { - return x.Uid - } - return "" -} - -func (x *PodHistory) GetName() string { - if x != nil { - return x.Name - } - return "" -} - -func (x *PodHistory) GetNamespace() string { - if x != nil { - return x.Namespace - } - return "" -} - -func (x *PodHistory) GetPhase() string { - if x != nil { - return x.Phase - } - return "" -} - -func (x *PodHistory) GetNodeName() string { - if x != nil { - return x.NodeName - } - return "" +// Deprecated: Use ListAuditLogsResponse.ProtoReflect.Descriptor instead. +func (*ListAuditLogsResponse) Descriptor() ([]byte, []int) { + return file_api_v1_k8s_proto_rawDescGZIP(), []int{119} } -func (x *PodHistory) GetCreatedAt() *timestamppb.Timestamp { +func (x *ListAuditLogsResponse) GetLogs() []*AuditLogEntry { if x != nil { - return x.CreatedAt + return x.Logs } return nil } -func (x *PodHistory) GetDeletedAt() *timestamppb.Timestamp { +func (x *ListAuditLogsResponse) GetPagination() *Pagination { if x != nil { - return x.DeletedAt + return x.Pagination } return nil } -type GetRelatedResourcesResponse struct { +// Request for fetching all audit log originators. +type ListAuditLogOriginatorsRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Relations []*K8SRelatedResource `protobuf:"bytes,1,rep,name=relations,proto3" json:"relations,omitempty"` // List of resource relations. - Nodes []*K8SResourceNode `protobuf:"bytes,2,rep,name=nodes,proto3" json:"nodes,omitempty"` // List of unique nodes (resources) in the graph. - Edges []*K8SResourceEdge `protobuf:"bytes,3,rep,name=edges,proto3" json:"edges,omitempty"` // List of edges for the graph. + TeamId string `protobuf:"bytes,1,opt,name=team_id,json=teamId,proto3" json:"team_id,omitempty"` + ClusterId []string `protobuf:"bytes,2,rep,name=cluster_id,json=clusterId,proto3" json:"cluster_id,omitempty"` + NodeId []string `protobuf:"bytes,3,rep,name=node_id,json=nodeId,proto3" json:"node_id,omitempty"` + WorkloadId []string `protobuf:"bytes,4,rep,name=workload_id,json=workloadId,proto3" json:"workload_id,omitempty"` + WorkloadType []K8SObjectKind `protobuf:"varint,5,rep,packed,name=workload_type,json=workloadType,proto3,enum=api.v1.K8SObjectKind" json:"workload_type,omitempty"` + RecommendationPolicyId []string `protobuf:"bytes,6,rep,name=recommendation_policy_id,json=recommendationPolicyId,proto3" json:"recommendation_policy_id,omitempty"` + RecommendationId []string `protobuf:"bytes,7,rep,name=recommendation_id,json=recommendationId,proto3" json:"recommendation_id,omitempty"` + OriginatingUserId []string `protobuf:"bytes,8,rep,name=originating_user_id,json=originatingUserId,proto3" json:"originating_user_id,omitempty"` + // Deprecated: Marked as deprecated in api/v1/k8s.proto. + EmailContains *string `protobuf:"bytes,9,opt,name=email_contains,json=emailContains,proto3,oneof" json:"email_contains,omitempty"` + Event []string `protobuf:"bytes,10,rep,name=event,proto3" json:"event,omitempty"` + StartTime *timestamppb.Timestamp `protobuf:"bytes,11,opt,name=start_time,json=startTime,proto3" json:"start_time,omitempty"` + EndTime *timestamppb.Timestamp `protobuf:"bytes,12,opt,name=end_time,json=endTime,proto3" json:"end_time,omitempty"` + EmailMatches []string `protobuf:"bytes,13,rep,name=email_matches,json=emailMatches,proto3" json:"email_matches,omitempty"` // Exact match on originating_user_email (equality with IN clause) } -func (x *GetRelatedResourcesResponse) Reset() { - *x = GetRelatedResourcesResponse{} +func (x *ListAuditLogOriginatorsRequest) Reset() { + *x = ListAuditLogOriginatorsRequest{} if protoimpl.UnsafeEnabled { - mi := &file_api_v1_k8s_proto_msgTypes[119] + mi := &file_api_v1_k8s_proto_msgTypes[120] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } -func (x *GetRelatedResourcesResponse) String() string { +func (x *ListAuditLogOriginatorsRequest) String() string { return protoimpl.X.MessageStringOf(x) } -func (*GetRelatedResourcesResponse) ProtoMessage() {} +func (*ListAuditLogOriginatorsRequest) ProtoMessage() {} -func (x *GetRelatedResourcesResponse) ProtoReflect() protoreflect.Message { - mi := &file_api_v1_k8s_proto_msgTypes[119] +func (x *ListAuditLogOriginatorsRequest) ProtoReflect() protoreflect.Message { + mi := &file_api_v1_k8s_proto_msgTypes[120] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -9085,141 +9768,114 @@ func (x *GetRelatedResourcesResponse) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use GetRelatedResourcesResponse.ProtoReflect.Descriptor instead. -func (*GetRelatedResourcesResponse) Descriptor() ([]byte, []int) { - return file_api_v1_k8s_proto_rawDescGZIP(), []int{119} +// Deprecated: Use ListAuditLogOriginatorsRequest.ProtoReflect.Descriptor instead. +func (*ListAuditLogOriginatorsRequest) Descriptor() ([]byte, []int) { + return file_api_v1_k8s_proto_rawDescGZIP(), []int{120} } -func (x *GetRelatedResourcesResponse) GetRelations() []*K8SRelatedResource { +func (x *ListAuditLogOriginatorsRequest) GetTeamId() string { if x != nil { - return x.Relations + return x.TeamId } - return nil + return "" } -func (x *GetRelatedResourcesResponse) GetNodes() []*K8SResourceNode { +func (x *ListAuditLogOriginatorsRequest) GetClusterId() []string { if x != nil { - return x.Nodes + return x.ClusterId } return nil } -func (x *GetRelatedResourcesResponse) GetEdges() []*K8SResourceEdge { +func (x *ListAuditLogOriginatorsRequest) GetNodeId() []string { if x != nil { - return x.Edges + return x.NodeId } return nil } -// K8sRelatedResource represents a relationship between two Kubernetes resources -type K8SRelatedResource struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - SourceKind string `protobuf:"bytes,1,opt,name=source_kind,json=sourceKind,proto3" json:"source_kind,omitempty"` // Kind of the source resource (e.g., Deployment, ReplicaSet). - SourceUid string `protobuf:"bytes,2,opt,name=source_uid,json=sourceUid,proto3" json:"source_uid,omitempty"` // UID of the source resource. - SourceName string `protobuf:"bytes,3,opt,name=source_name,json=sourceName,proto3" json:"source_name,omitempty"` // Name of the source resource. - TargetKind string `protobuf:"bytes,4,opt,name=target_kind,json=targetKind,proto3" json:"target_kind,omitempty"` // Kind of the target resource (e.g., ReplicaSet, Pod). - TargetUid string `protobuf:"bytes,5,opt,name=target_uid,json=targetUid,proto3" json:"target_uid,omitempty"` // UID of the target resource. - TargetName string `protobuf:"bytes,6,opt,name=target_name,json=targetName,proto3" json:"target_name,omitempty"` // Name of the target resource. - RelationshipType string `protobuf:"bytes,7,opt,name=relationship_type,json=relationshipType,proto3" json:"relationship_type,omitempty"` // Type of relationship (e.g., owns, controls, selects). -} - -func (x *K8SRelatedResource) Reset() { - *x = K8SRelatedResource{} - if protoimpl.UnsafeEnabled { - mi := &file_api_v1_k8s_proto_msgTypes[120] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) +func (x *ListAuditLogOriginatorsRequest) GetWorkloadId() []string { + if x != nil { + return x.WorkloadId } + return nil } -func (x *K8SRelatedResource) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*K8SRelatedResource) ProtoMessage() {} - -func (x *K8SRelatedResource) ProtoReflect() protoreflect.Message { - mi := &file_api_v1_k8s_proto_msgTypes[120] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms +func (x *ListAuditLogOriginatorsRequest) GetWorkloadType() []K8SObjectKind { + if x != nil { + return x.WorkloadType } - return mi.MessageOf(x) + return nil } -// Deprecated: Use K8SRelatedResource.ProtoReflect.Descriptor instead. -func (*K8SRelatedResource) Descriptor() ([]byte, []int) { - return file_api_v1_k8s_proto_rawDescGZIP(), []int{120} +func (x *ListAuditLogOriginatorsRequest) GetRecommendationPolicyId() []string { + if x != nil { + return x.RecommendationPolicyId + } + return nil } -func (x *K8SRelatedResource) GetSourceKind() string { +func (x *ListAuditLogOriginatorsRequest) GetRecommendationId() []string { if x != nil { - return x.SourceKind + return x.RecommendationId } - return "" + return nil } -func (x *K8SRelatedResource) GetSourceUid() string { +func (x *ListAuditLogOriginatorsRequest) GetOriginatingUserId() []string { if x != nil { - return x.SourceUid + return x.OriginatingUserId } - return "" + return nil } -func (x *K8SRelatedResource) GetSourceName() string { - if x != nil { - return x.SourceName +// Deprecated: Marked as deprecated in api/v1/k8s.proto. +func (x *ListAuditLogOriginatorsRequest) GetEmailContains() string { + if x != nil && x.EmailContains != nil { + return *x.EmailContains } return "" } -func (x *K8SRelatedResource) GetTargetKind() string { +func (x *ListAuditLogOriginatorsRequest) GetEvent() []string { if x != nil { - return x.TargetKind + return x.Event } - return "" + return nil } -func (x *K8SRelatedResource) GetTargetUid() string { +func (x *ListAuditLogOriginatorsRequest) GetStartTime() *timestamppb.Timestamp { if x != nil { - return x.TargetUid + return x.StartTime } - return "" + return nil } -func (x *K8SRelatedResource) GetTargetName() string { +func (x *ListAuditLogOriginatorsRequest) GetEndTime() *timestamppb.Timestamp { if x != nil { - return x.TargetName + return x.EndTime } - return "" + return nil } -func (x *K8SRelatedResource) GetRelationshipType() string { +func (x *ListAuditLogOriginatorsRequest) GetEmailMatches() []string { if x != nil { - return x.RelationshipType + return x.EmailMatches } - return "" + return nil } -// K8sResourceNode represents a node in the resource graph -type K8SResourceNode struct { +// Response for fetching all audit log originators. +type ListAuditLogOriginatorsResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` // Unique identifier (UID) of the resource. - Kind string `protobuf:"bytes,2,opt,name=kind,proto3" json:"kind,omitempty"` // Kind of the resource (e.g., Deployment, ReplicaSet, Pod). - Name string `protobuf:"bytes,3,opt,name=name,proto3" json:"name,omitempty"` // Name of the resource. + EmailAddresses []string `protobuf:"bytes,1,rep,name=email_addresses,json=emailAddresses,proto3" json:"email_addresses,omitempty"` } -func (x *K8SResourceNode) Reset() { - *x = K8SResourceNode{} +func (x *ListAuditLogOriginatorsResponse) Reset() { + *x = ListAuditLogOriginatorsResponse{} if protoimpl.UnsafeEnabled { mi := &file_api_v1_k8s_proto_msgTypes[121] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -9227,13 +9883,13 @@ func (x *K8SResourceNode) Reset() { } } -func (x *K8SResourceNode) String() string { +func (x *ListAuditLogOriginatorsResponse) String() string { return protoimpl.X.MessageStringOf(x) } -func (*K8SResourceNode) ProtoMessage() {} +func (*ListAuditLogOriginatorsResponse) ProtoMessage() {} -func (x *K8SResourceNode) ProtoReflect() protoreflect.Message { +func (x *ListAuditLogOriginatorsResponse) ProtoReflect() protoreflect.Message { mi := &file_api_v1_k8s_proto_msgTypes[121] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -9245,46 +9901,33 @@ func (x *K8SResourceNode) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use K8SResourceNode.ProtoReflect.Descriptor instead. -func (*K8SResourceNode) Descriptor() ([]byte, []int) { +// Deprecated: Use ListAuditLogOriginatorsResponse.ProtoReflect.Descriptor instead. +func (*ListAuditLogOriginatorsResponse) Descriptor() ([]byte, []int) { return file_api_v1_k8s_proto_rawDescGZIP(), []int{121} } -func (x *K8SResourceNode) GetId() string { - if x != nil { - return x.Id - } - return "" -} - -func (x *K8SResourceNode) GetKind() string { - if x != nil { - return x.Kind - } - return "" -} - -func (x *K8SResourceNode) GetName() string { +func (x *ListAuditLogOriginatorsResponse) GetEmailAddresses() []string { if x != nil { - return x.Name + return x.EmailAddresses } - return "" + return nil } -// K8sResourceEdge represents an edge in the resource graph -type K8SResourceEdge struct { +// SendWorkloadEmailRequest is used to send an email w/ csv data. +type SendWorkloadEmailRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` // Unique identifier for the edge. - Source string `protobuf:"bytes,2,opt,name=source,proto3" json:"source,omitempty"` // UID of the source resource. - Target string `protobuf:"bytes,3,opt,name=target,proto3" json:"target,omitempty"` // UID of the target resource. - Label string `protobuf:"bytes,4,opt,name=label,proto3" json:"label,omitempty"` // Label for the edge (relationship type). + TeamId string `protobuf:"bytes,1,opt,name=team_id,json=teamId,proto3" json:"team_id,omitempty"` + ClusterId string `protobuf:"bytes,2,opt,name=cluster_id,json=clusterId,proto3" json:"cluster_id,omitempty"` + StartTime int64 `protobuf:"varint,11,opt,name=start_time,json=startTime,proto3" json:"start_time,omitempty"` + StopTime int64 `protobuf:"varint,12,opt,name=stop_time,json=stopTime,proto3" json:"stop_time,omitempty"` + To []string `protobuf:"bytes,21,rep,name=to,proto3" json:"to,omitempty"` } -func (x *K8SResourceEdge) Reset() { - *x = K8SResourceEdge{} +func (x *SendWorkloadEmailRequest) Reset() { + *x = SendWorkloadEmailRequest{} if protoimpl.UnsafeEnabled { mi := &file_api_v1_k8s_proto_msgTypes[122] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -9292,13 +9935,13 @@ func (x *K8SResourceEdge) Reset() { } } -func (x *K8SResourceEdge) String() string { +func (x *SendWorkloadEmailRequest) String() string { return protoimpl.X.MessageStringOf(x) } -func (*K8SResourceEdge) ProtoMessage() {} +func (*SendWorkloadEmailRequest) ProtoMessage() {} -func (x *K8SResourceEdge) ProtoReflect() protoreflect.Message { +func (x *SendWorkloadEmailRequest) ProtoReflect() protoreflect.Message { mi := &file_api_v1_k8s_proto_msgTypes[122] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -9310,51 +9953,57 @@ func (x *K8SResourceEdge) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use K8SResourceEdge.ProtoReflect.Descriptor instead. -func (*K8SResourceEdge) Descriptor() ([]byte, []int) { +// Deprecated: Use SendWorkloadEmailRequest.ProtoReflect.Descriptor instead. +func (*SendWorkloadEmailRequest) Descriptor() ([]byte, []int) { return file_api_v1_k8s_proto_rawDescGZIP(), []int{122} } -func (x *K8SResourceEdge) GetId() string { +func (x *SendWorkloadEmailRequest) GetTeamId() string { if x != nil { - return x.Id + return x.TeamId } return "" } -func (x *K8SResourceEdge) GetSource() string { +func (x *SendWorkloadEmailRequest) GetClusterId() string { if x != nil { - return x.Source + return x.ClusterId } return "" } -func (x *K8SResourceEdge) GetTarget() string { +func (x *SendWorkloadEmailRequest) GetStartTime() int64 { if x != nil { - return x.Target + return x.StartTime } - return "" + return 0 } -func (x *K8SResourceEdge) GetLabel() string { +func (x *SendWorkloadEmailRequest) GetStopTime() int64 { if x != nil { - return x.Label + return x.StopTime } - return "" + return 0 } -// GetClusterTypeRequest is used to determine the cluster type based on kubelet version -type GetClusterTypeRequest struct { +func (x *SendWorkloadEmailRequest) GetTo() []string { + if x != nil { + return x.To + } + return nil +} + +// SendWorkloadEmailResponse... +type SendWorkloadEmailResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - TeamId string `protobuf:"bytes,1,opt,name=team_id,json=teamId,proto3" json:"team_id,omitempty"` // Unique identifier for the team. - ClusterId string `protobuf:"bytes,2,opt,name=cluster_id,json=clusterId,proto3" json:"cluster_id,omitempty"` // Unique identifier for the cluster. + Message string `protobuf:"bytes,1,opt,name=message,proto3" json:"message,omitempty"` } -func (x *GetClusterTypeRequest) Reset() { - *x = GetClusterTypeRequest{} +func (x *SendWorkloadEmailResponse) Reset() { + *x = SendWorkloadEmailResponse{} if protoimpl.UnsafeEnabled { mi := &file_api_v1_k8s_proto_msgTypes[123] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -9362,13 +10011,13 @@ func (x *GetClusterTypeRequest) Reset() { } } -func (x *GetClusterTypeRequest) String() string { +func (x *SendWorkloadEmailResponse) String() string { return protoimpl.X.MessageStringOf(x) } -func (*GetClusterTypeRequest) ProtoMessage() {} +func (*SendWorkloadEmailResponse) ProtoMessage() {} -func (x *GetClusterTypeRequest) ProtoReflect() protoreflect.Message { +func (x *SendWorkloadEmailResponse) ProtoReflect() protoreflect.Message { mi := &file_api_v1_k8s_proto_msgTypes[123] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -9380,37 +10029,29 @@ func (x *GetClusterTypeRequest) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use GetClusterTypeRequest.ProtoReflect.Descriptor instead. -func (*GetClusterTypeRequest) Descriptor() ([]byte, []int) { +// Deprecated: Use SendWorkloadEmailResponse.ProtoReflect.Descriptor instead. +func (*SendWorkloadEmailResponse) Descriptor() ([]byte, []int) { return file_api_v1_k8s_proto_rawDescGZIP(), []int{123} } -func (x *GetClusterTypeRequest) GetTeamId() string { - if x != nil { - return x.TeamId - } - return "" -} - -func (x *GetClusterTypeRequest) GetClusterId() string { +func (x *SendWorkloadEmailResponse) GetMessage() string { if x != nil { - return x.ClusterId + return x.Message } return "" } -// GetClusterTypeResponse returns the determined cluster type -type GetClusterTypeResponse struct { +// SendWeeklySummaryEmailRequest is used to send a weekly summary email. +type SendWeeklySummaryEmailRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - ClusterType ClusterType `protobuf:"varint,1,opt,name=cluster_type,json=clusterType,proto3,enum=api.v1.ClusterType" json:"cluster_type,omitempty"` // Detected cluster type - KubeletVersion string `protobuf:"bytes,2,opt,name=kubelet_version,json=kubeletVersion,proto3" json:"kubelet_version,omitempty"` // The raw kubelet version string + Request *SendWeeklySummaryEmailRequestData `protobuf:"bytes,1,opt,name=request,proto3" json:"request,omitempty"` // Request data wrapper. } -func (x *GetClusterTypeResponse) Reset() { - *x = GetClusterTypeResponse{} +func (x *SendWeeklySummaryEmailRequest) Reset() { + *x = SendWeeklySummaryEmailRequest{} if protoimpl.UnsafeEnabled { mi := &file_api_v1_k8s_proto_msgTypes[124] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -9418,13 +10059,13 @@ func (x *GetClusterTypeResponse) Reset() { } } -func (x *GetClusterTypeResponse) String() string { +func (x *SendWeeklySummaryEmailRequest) String() string { return protoimpl.X.MessageStringOf(x) } -func (*GetClusterTypeResponse) ProtoMessage() {} +func (*SendWeeklySummaryEmailRequest) ProtoMessage() {} -func (x *GetClusterTypeResponse) ProtoReflect() protoreflect.Message { +func (x *SendWeeklySummaryEmailRequest) ProtoReflect() protoreflect.Message { mi := &file_api_v1_k8s_proto_msgTypes[124] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -9436,36 +10077,30 @@ func (x *GetClusterTypeResponse) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use GetClusterTypeResponse.ProtoReflect.Descriptor instead. -func (*GetClusterTypeResponse) Descriptor() ([]byte, []int) { +// Deprecated: Use SendWeeklySummaryEmailRequest.ProtoReflect.Descriptor instead. +func (*SendWeeklySummaryEmailRequest) Descriptor() ([]byte, []int) { return file_api_v1_k8s_proto_rawDescGZIP(), []int{124} } -func (x *GetClusterTypeResponse) GetClusterType() ClusterType { - if x != nil { - return x.ClusterType - } - return ClusterType_CLUSTER_TYPE_UNSPECIFIED -} - -func (x *GetClusterTypeResponse) GetKubeletVersion() string { +func (x *SendWeeklySummaryEmailRequest) GetRequest() *SendWeeklySummaryEmailRequestData { if x != nil { - return x.KubeletVersion + return x.Request } - return "" + return nil } -type GetMigPartedConfigRequest struct { +// SendWeeklySummaryEmailRequestData contains the actual request data. +type SendWeeklySummaryEmailRequestData struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - TeamId string `protobuf:"bytes,1,opt,name=team_id,json=teamId,proto3" json:"team_id,omitempty"` - ClusterId string `protobuf:"bytes,2,opt,name=cluster_id,json=clusterId,proto3" json:"cluster_id,omitempty"` + TeamId string `protobuf:"bytes,1,opt,name=team_id,json=teamId,proto3" json:"team_id,omitempty"` // Unique identifier for the team. + Recipients []string `protobuf:"bytes,2,rep,name=recipients,proto3" json:"recipients,omitempty"` // List of email recipients. If empty, will use team members. } -func (x *GetMigPartedConfigRequest) Reset() { - *x = GetMigPartedConfigRequest{} +func (x *SendWeeklySummaryEmailRequestData) Reset() { + *x = SendWeeklySummaryEmailRequestData{} if protoimpl.UnsafeEnabled { mi := &file_api_v1_k8s_proto_msgTypes[125] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -9473,13 +10108,13 @@ func (x *GetMigPartedConfigRequest) Reset() { } } -func (x *GetMigPartedConfigRequest) String() string { +func (x *SendWeeklySummaryEmailRequestData) String() string { return protoimpl.X.MessageStringOf(x) } -func (*GetMigPartedConfigRequest) ProtoMessage() {} +func (*SendWeeklySummaryEmailRequestData) ProtoMessage() {} -func (x *GetMigPartedConfigRequest) ProtoReflect() protoreflect.Message { +func (x *SendWeeklySummaryEmailRequestData) ProtoReflect() protoreflect.Message { mi := &file_api_v1_k8s_proto_msgTypes[125] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -9491,47 +10126,36 @@ func (x *GetMigPartedConfigRequest) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use GetMigPartedConfigRequest.ProtoReflect.Descriptor instead. -func (*GetMigPartedConfigRequest) Descriptor() ([]byte, []int) { +// Deprecated: Use SendWeeklySummaryEmailRequestData.ProtoReflect.Descriptor instead. +func (*SendWeeklySummaryEmailRequestData) Descriptor() ([]byte, []int) { return file_api_v1_k8s_proto_rawDescGZIP(), []int{125} } -func (x *GetMigPartedConfigRequest) GetTeamId() string { +func (x *SendWeeklySummaryEmailRequestData) GetTeamId() string { if x != nil { return x.TeamId } return "" } -func (x *GetMigPartedConfigRequest) GetClusterId() string { +func (x *SendWeeklySummaryEmailRequestData) GetRecipients() []string { if x != nil { - return x.ClusterId + return x.Recipients } - return "" + return nil } -type GetMigPartedConfigResponse struct { +// SendWeeklySummaryEmailResponse contains the result of sending a weekly summary email. +type SendWeeklySummaryEmailResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - // Whether a mig-parted ConfigMap has been collected for this cluster. - Found bool `protobuf:"varint,1,opt,name=found,proto3" json:"found,omitempty"` - Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` - Namespace string `protobuf:"bytes,3,opt,name=namespace,proto3" json:"namespace,omitempty"` - // Config schema version (e.g. v1). - Version string `protobuf:"bytes,4,opt,name=version,proto3" json:"version,omitempty"` - // Parsed profile map from config.yaml as JSON: - // {profile-name: [{devices, mig-enabled, mig-devices}, ...]}. - MigConfigsJson string `protobuf:"bytes,5,opt,name=mig_configs_json,json=migConfigsJson,proto3" json:"mig_configs_json,omitempty"` - // Original config.yaml text; authoritative when parse_error is set. - Raw string `protobuf:"bytes,6,opt,name=raw,proto3" json:"raw,omitempty"` - ParseError string `protobuf:"bytes,7,opt,name=parse_error,json=parseError,proto3" json:"parse_error,omitempty"` - LastSeen *timestamppb.Timestamp `protobuf:"bytes,8,opt,name=last_seen,json=lastSeen,proto3" json:"last_seen,omitempty"` + Message string `protobuf:"bytes,1,opt,name=message,proto3" json:"message,omitempty"` // Status message about the email sending operation. } -func (x *GetMigPartedConfigResponse) Reset() { - *x = GetMigPartedConfigResponse{} +func (x *SendWeeklySummaryEmailResponse) Reset() { + *x = SendWeeklySummaryEmailResponse{} if protoimpl.UnsafeEnabled { mi := &file_api_v1_k8s_proto_msgTypes[126] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -9539,13 +10163,13 @@ func (x *GetMigPartedConfigResponse) Reset() { } } -func (x *GetMigPartedConfigResponse) String() string { +func (x *SendWeeklySummaryEmailResponse) String() string { return protoimpl.X.MessageStringOf(x) } -func (*GetMigPartedConfigResponse) ProtoMessage() {} +func (*SendWeeklySummaryEmailResponse) ProtoMessage() {} -func (x *GetMigPartedConfigResponse) ProtoReflect() protoreflect.Message { +func (x *SendWeeklySummaryEmailResponse) ProtoReflect() protoreflect.Message { mi := &file_api_v1_k8s_proto_msgTypes[126] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -9557,80 +10181,35 @@ func (x *GetMigPartedConfigResponse) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use GetMigPartedConfigResponse.ProtoReflect.Descriptor instead. -func (*GetMigPartedConfigResponse) Descriptor() ([]byte, []int) { +// Deprecated: Use SendWeeklySummaryEmailResponse.ProtoReflect.Descriptor instead. +func (*SendWeeklySummaryEmailResponse) Descriptor() ([]byte, []int) { return file_api_v1_k8s_proto_rawDescGZIP(), []int{126} } -func (x *GetMigPartedConfigResponse) GetFound() bool { - if x != nil { - return x.Found - } - return false -} - -func (x *GetMigPartedConfigResponse) GetName() string { - if x != nil { - return x.Name - } - return "" -} - -func (x *GetMigPartedConfigResponse) GetNamespace() string { - if x != nil { - return x.Namespace - } - return "" -} - -func (x *GetMigPartedConfigResponse) GetVersion() string { - if x != nil { - return x.Version - } - return "" -} - -func (x *GetMigPartedConfigResponse) GetMigConfigsJson() string { - if x != nil { - return x.MigConfigsJson - } - return "" -} - -func (x *GetMigPartedConfigResponse) GetRaw() string { - if x != nil { - return x.Raw - } - return "" -} - -func (x *GetMigPartedConfigResponse) GetParseError() string { +func (x *SendWeeklySummaryEmailResponse) GetMessage() string { if x != nil { - return x.ParseError + return x.Message } return "" } -func (x *GetMigPartedConfigResponse) GetLastSeen() *timestamppb.Timestamp { - if x != nil { - return x.LastSeen - } - return nil -} - -type GetWorkloadsStatsRequest struct { +// GetClustersNodeInfoRequest is used to fetch aggregated node information across clusters. +type GetClustersNodeInfoRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - TeamId string `protobuf:"bytes,1,opt,name=team_id,json=teamId,proto3" json:"team_id,omitempty"` // Unique identifier for the team. - ClusterId string `protobuf:"bytes,2,opt,name=cluster_id,json=clusterId,proto3" json:"cluster_id,omitempty"` // Unique identifier for the cluster. - StartTime *timestamppb.Timestamp `protobuf:"bytes,21,opt,name=start_time,json=startTime,proto3,oneof" json:"start_time,omitempty"` - EndTime *timestamppb.Timestamp `protobuf:"bytes,22,opt,name=end_time,json=endTime,proto3,oneof" json:"end_time,omitempty"` + TeamId string `protobuf:"bytes,1,opt,name=team_id,json=teamId,proto3" json:"team_id,omitempty"` // Unique identifier for the team. + ClusterIds []string `protobuf:"bytes,2,rep,name=cluster_ids,json=clusterIds,proto3" json:"cluster_ids,omitempty"` // Optional list of cluster IDs to filter by. If empty, returns info for all clusters. + StartTime *timestamppb.Timestamp `protobuf:"bytes,11,opt,name=start_time,json=startTime,proto3,oneof" json:"start_time,omitempty"` + EndTime *timestamppb.Timestamp `protobuf:"bytes,12,opt,name=end_time,json=endTime,proto3,oneof" json:"end_time,omitempty"` + ExcludeMostExpNode bool `protobuf:"varint,41,opt,name=exclude_most_exp_node,json=excludeMostExpNode,proto3" json:"exclude_most_exp_node,omitempty"` + ExcludeLeastExpNode bool `protobuf:"varint,42,opt,name=exclude_least_exp_node,json=excludeLeastExpNode,proto3" json:"exclude_least_exp_node,omitempty"` + ExcludeMostUnderutilNode bool `protobuf:"varint,43,opt,name=exclude_most_underutil_node,json=excludeMostUnderutilNode,proto3" json:"exclude_most_underutil_node,omitempty"` } -func (x *GetWorkloadsStatsRequest) Reset() { - *x = GetWorkloadsStatsRequest{} +func (x *GetClustersNodeInfoRequest) Reset() { + *x = GetClustersNodeInfoRequest{} if protoimpl.UnsafeEnabled { mi := &file_api_v1_k8s_proto_msgTypes[127] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -9638,13 +10217,13 @@ func (x *GetWorkloadsStatsRequest) Reset() { } } -func (x *GetWorkloadsStatsRequest) String() string { +func (x *GetClustersNodeInfoRequest) String() string { return protoimpl.X.MessageStringOf(x) } -func (*GetWorkloadsStatsRequest) ProtoMessage() {} +func (*GetClustersNodeInfoRequest) ProtoMessage() {} -func (x *GetWorkloadsStatsRequest) ProtoReflect() protoreflect.Message { +func (x *GetClustersNodeInfoRequest) ProtoReflect() protoreflect.Message { mi := &file_api_v1_k8s_proto_msgTypes[127] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -9656,53 +10235,74 @@ func (x *GetWorkloadsStatsRequest) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use GetWorkloadsStatsRequest.ProtoReflect.Descriptor instead. -func (*GetWorkloadsStatsRequest) Descriptor() ([]byte, []int) { +// Deprecated: Use GetClustersNodeInfoRequest.ProtoReflect.Descriptor instead. +func (*GetClustersNodeInfoRequest) Descriptor() ([]byte, []int) { return file_api_v1_k8s_proto_rawDescGZIP(), []int{127} } -func (x *GetWorkloadsStatsRequest) GetTeamId() string { +func (x *GetClustersNodeInfoRequest) GetTeamId() string { if x != nil { return x.TeamId } return "" } -func (x *GetWorkloadsStatsRequest) GetClusterId() string { +func (x *GetClustersNodeInfoRequest) GetClusterIds() []string { if x != nil { - return x.ClusterId + return x.ClusterIds } - return "" + return nil } -func (x *GetWorkloadsStatsRequest) GetStartTime() *timestamppb.Timestamp { +func (x *GetClustersNodeInfoRequest) GetStartTime() *timestamppb.Timestamp { if x != nil { return x.StartTime } return nil } -func (x *GetWorkloadsStatsRequest) GetEndTime() *timestamppb.Timestamp { +func (x *GetClustersNodeInfoRequest) GetEndTime() *timestamppb.Timestamp { if x != nil { return x.EndTime } return nil } -type GetWorkloadsStatsResponse struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields +func (x *GetClustersNodeInfoRequest) GetExcludeMostExpNode() bool { + if x != nil { + return x.ExcludeMostExpNode + } + return false +} - Total int32 `protobuf:"varint,1,opt,name=total,proto3" json:"total,omitempty"` - Optimized int32 `protobuf:"varint,2,opt,name=optimized,proto3" json:"optimized,omitempty"` - Unoptimized int32 `protobuf:"varint,3,opt,name=unoptimized,proto3" json:"unoptimized,omitempty"` - WithoutRequests int32 `protobuf:"varint,4,opt,name=without_requests,json=withoutRequests,proto3" json:"without_requests,omitempty"` - OptimizedPercentage int32 `protobuf:"varint,5,opt,name=optimized_percentage,json=optimizedPercentage,proto3" json:"optimized_percentage,omitempty"` +func (x *GetClustersNodeInfoRequest) GetExcludeLeastExpNode() bool { + if x != nil { + return x.ExcludeLeastExpNode + } + return false } -func (x *GetWorkloadsStatsResponse) Reset() { - *x = GetWorkloadsStatsResponse{} +func (x *GetClustersNodeInfoRequest) GetExcludeMostUnderutilNode() bool { + if x != nil { + return x.ExcludeMostUnderutilNode + } + return false +} + +// GetClustersNodeInfoResponse contains aggregated node information across clusters. +type GetClustersNodeInfoResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + NodeInfo *NodeInfo `protobuf:"bytes,1,opt,name=node_info,json=nodeInfo,proto3" json:"node_info,omitempty"` // Aggregated node information across all clusters. + MostExpensiveNode *Node `protobuf:"bytes,2,opt,name=most_expensive_node,json=mostExpensiveNode,proto3" json:"most_expensive_node,omitempty"` // The most expensive node across all clusters. + LeastExpensiveNode *Node `protobuf:"bytes,3,opt,name=least_expensive_node,json=leastExpensiveNode,proto3" json:"least_expensive_node,omitempty"` // The least expensive node across all clusters. + MostUnderutilizedNode *Node `protobuf:"bytes,4,opt,name=most_underutilized_node,json=mostUnderutilizedNode,proto3" json:"most_underutilized_node,omitempty"` // The most underutilized node across all clusters. +} + +func (x *GetClustersNodeInfoResponse) Reset() { + *x = GetClustersNodeInfoResponse{} if protoimpl.UnsafeEnabled { mi := &file_api_v1_k8s_proto_msgTypes[128] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -9710,13 +10310,13 @@ func (x *GetWorkloadsStatsResponse) Reset() { } } -func (x *GetWorkloadsStatsResponse) String() string { +func (x *GetClustersNodeInfoResponse) String() string { return protoimpl.X.MessageStringOf(x) } -func (*GetWorkloadsStatsResponse) ProtoMessage() {} +func (*GetClustersNodeInfoResponse) ProtoMessage() {} -func (x *GetWorkloadsStatsResponse) ProtoReflect() protoreflect.Message { +func (x *GetClustersNodeInfoResponse) ProtoReflect() protoreflect.Message { mi := &file_api_v1_k8s_proto_msgTypes[128] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -9728,59 +10328,52 @@ func (x *GetWorkloadsStatsResponse) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use GetWorkloadsStatsResponse.ProtoReflect.Descriptor instead. -func (*GetWorkloadsStatsResponse) Descriptor() ([]byte, []int) { +// Deprecated: Use GetClustersNodeInfoResponse.ProtoReflect.Descriptor instead. +func (*GetClustersNodeInfoResponse) Descriptor() ([]byte, []int) { return file_api_v1_k8s_proto_rawDescGZIP(), []int{128} } -func (x *GetWorkloadsStatsResponse) GetTotal() int32 { - if x != nil { - return x.Total - } - return 0 -} - -func (x *GetWorkloadsStatsResponse) GetOptimized() int32 { +func (x *GetClustersNodeInfoResponse) GetNodeInfo() *NodeInfo { if x != nil { - return x.Optimized + return x.NodeInfo } - return 0 + return nil } -func (x *GetWorkloadsStatsResponse) GetUnoptimized() int32 { +func (x *GetClustersNodeInfoResponse) GetMostExpensiveNode() *Node { if x != nil { - return x.Unoptimized + return x.MostExpensiveNode } - return 0 + return nil } -func (x *GetWorkloadsStatsResponse) GetWithoutRequests() int32 { +func (x *GetClustersNodeInfoResponse) GetLeastExpensiveNode() *Node { if x != nil { - return x.WithoutRequests + return x.LeastExpensiveNode } - return 0 + return nil } -func (x *GetWorkloadsStatsResponse) GetOptimizedPercentage() int32 { +func (x *GetClustersNodeInfoResponse) GetMostUnderutilizedNode() *Node { if x != nil { - return x.OptimizedPercentage + return x.MostUnderutilizedNode } - return 0 + return nil } -type DailyUtilizationRequest struct { +type SearchK8SResourcesRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - TeamId string `protobuf:"bytes,1,opt,name=team_id,json=teamId,proto3" json:"team_id,omitempty"` - ClusterIds []string `protobuf:"bytes,2,rep,name=cluster_ids,json=clusterIds,proto3" json:"cluster_ids,omitempty"` - StartTime *timestamppb.Timestamp `protobuf:"bytes,21,opt,name=start_time,json=startTime,proto3" json:"start_time,omitempty"` - EndTime *timestamppb.Timestamp `protobuf:"bytes,22,opt,name=end_time,json=endTime,proto3" json:"end_time,omitempty"` + TeamId string `protobuf:"bytes,1,opt,name=team_id,json=teamId,proto3" json:"team_id,omitempty"` // Unique identifier for the team. + ClusterId string `protobuf:"bytes,2,opt,name=cluster_id,json=clusterId,proto3" json:"cluster_id,omitempty"` // Unique identifier for the cluster. + SearchQuery string `protobuf:"bytes,3,opt,name=search_query,json=searchQuery,proto3" json:"search_query,omitempty"` // Search term to match against name, uid, and namespace. + IncludeDeleted bool `protobuf:"varint,4,opt,name=include_deleted,json=includeDeleted,proto3" json:"include_deleted,omitempty"` // Include soft-deleted resources in results. } -func (x *DailyUtilizationRequest) Reset() { - *x = DailyUtilizationRequest{} +func (x *SearchK8SResourcesRequest) Reset() { + *x = SearchK8SResourcesRequest{} if protoimpl.UnsafeEnabled { mi := &file_api_v1_k8s_proto_msgTypes[129] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -9788,13 +10381,13 @@ func (x *DailyUtilizationRequest) Reset() { } } -func (x *DailyUtilizationRequest) String() string { +func (x *SearchK8SResourcesRequest) String() string { return protoimpl.X.MessageStringOf(x) } -func (*DailyUtilizationRequest) ProtoMessage() {} +func (*SearchK8SResourcesRequest) ProtoMessage() {} -func (x *DailyUtilizationRequest) ProtoReflect() protoreflect.Message { +func (x *SearchK8SResourcesRequest) ProtoReflect() protoreflect.Message { mi := &file_api_v1_k8s_proto_msgTypes[129] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -9806,51 +10399,49 @@ func (x *DailyUtilizationRequest) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use DailyUtilizationRequest.ProtoReflect.Descriptor instead. -func (*DailyUtilizationRequest) Descriptor() ([]byte, []int) { +// Deprecated: Use SearchK8SResourcesRequest.ProtoReflect.Descriptor instead. +func (*SearchK8SResourcesRequest) Descriptor() ([]byte, []int) { return file_api_v1_k8s_proto_rawDescGZIP(), []int{129} } -func (x *DailyUtilizationRequest) GetTeamId() string { +func (x *SearchK8SResourcesRequest) GetTeamId() string { if x != nil { return x.TeamId } return "" } -func (x *DailyUtilizationRequest) GetClusterIds() []string { +func (x *SearchK8SResourcesRequest) GetClusterId() string { if x != nil { - return x.ClusterIds + return x.ClusterId } - return nil + return "" } -func (x *DailyUtilizationRequest) GetStartTime() *timestamppb.Timestamp { +func (x *SearchK8SResourcesRequest) GetSearchQuery() string { if x != nil { - return x.StartTime + return x.SearchQuery } - return nil + return "" } -func (x *DailyUtilizationRequest) GetEndTime() *timestamppb.Timestamp { +func (x *SearchK8SResourcesRequest) GetIncludeDeleted() bool { if x != nil { - return x.EndTime + return x.IncludeDeleted } - return nil + return false } -type DailyUtilizationResponse struct { +type SearchK8SResourcesResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - ClusterIdToDailyTotalCoreMinutes map[string]*DailyUtilizationResponse_Datapoints `protobuf:"bytes,1,rep,name=cluster_id_to_daily_total_core_minutes,json=clusterIdToDailyTotalCoreMinutes,proto3" json:"cluster_id_to_daily_total_core_minutes,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` - ClusterIdToMeta map[string]*Cluster `protobuf:"bytes,11,rep,name=cluster_id_to_meta,json=clusterIdToMeta,proto3" json:"cluster_id_to_meta,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` // for easier lookups - TotalCoreMinutesInPeriod int32 `protobuf:"varint,21,opt,name=total_core_minutes_in_period,json=totalCoreMinutesInPeriod,proto3" json:"total_core_minutes_in_period,omitempty"` + Results []*K8SSearchResult `protobuf:"bytes,1,rep,name=results,proto3" json:"results,omitempty"` // List of matching resources. } -func (x *DailyUtilizationResponse) Reset() { - *x = DailyUtilizationResponse{} +func (x *SearchK8SResourcesResponse) Reset() { + *x = SearchK8SResourcesResponse{} if protoimpl.UnsafeEnabled { mi := &file_api_v1_k8s_proto_msgTypes[130] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -9858,13 +10449,13 @@ func (x *DailyUtilizationResponse) Reset() { } } -func (x *DailyUtilizationResponse) String() string { +func (x *SearchK8SResourcesResponse) String() string { return protoimpl.X.MessageStringOf(x) } -func (*DailyUtilizationResponse) ProtoMessage() {} +func (*SearchK8SResourcesResponse) ProtoMessage() {} -func (x *DailyUtilizationResponse) ProtoReflect() protoreflect.Message { +func (x *SearchK8SResourcesResponse) ProtoReflect() protoreflect.Message { mi := &file_api_v1_k8s_proto_msgTypes[130] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -9876,45 +10467,32 @@ func (x *DailyUtilizationResponse) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use DailyUtilizationResponse.ProtoReflect.Descriptor instead. -func (*DailyUtilizationResponse) Descriptor() ([]byte, []int) { +// Deprecated: Use SearchK8SResourcesResponse.ProtoReflect.Descriptor instead. +func (*SearchK8SResourcesResponse) Descriptor() ([]byte, []int) { return file_api_v1_k8s_proto_rawDescGZIP(), []int{130} } -func (x *DailyUtilizationResponse) GetClusterIdToDailyTotalCoreMinutes() map[string]*DailyUtilizationResponse_Datapoints { - if x != nil { - return x.ClusterIdToDailyTotalCoreMinutes - } - return nil -} - -func (x *DailyUtilizationResponse) GetClusterIdToMeta() map[string]*Cluster { +func (x *SearchK8SResourcesResponse) GetResults() []*K8SSearchResult { if x != nil { - return x.ClusterIdToMeta + return x.Results } return nil } -func (x *DailyUtilizationResponse) GetTotalCoreMinutesInPeriod() int32 { - if x != nil { - return x.TotalCoreMinutesInPeriod - } - return 0 -} - -type DailyUtilizationInstanceTypeRequest struct { +type K8SSearchResult struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - TeamId string `protobuf:"bytes,1,opt,name=team_id,json=teamId,proto3" json:"team_id,omitempty"` - ClusterIds []string `protobuf:"bytes,2,rep,name=cluster_ids,json=clusterIds,proto3" json:"cluster_ids,omitempty"` - StartTime *timestamppb.Timestamp `protobuf:"bytes,21,opt,name=start_time,json=startTime,proto3" json:"start_time,omitempty"` - EndTime *timestamppb.Timestamp `protobuf:"bytes,22,opt,name=end_time,json=endTime,proto3" json:"end_time,omitempty"` + Uid string `protobuf:"bytes,1,opt,name=uid,proto3" json:"uid,omitempty"` // Unique identifier of the resource. + Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` // Name of the resource. + Kind string `protobuf:"bytes,3,opt,name=kind,proto3" json:"kind,omitempty"` // Kind of the resource (e.g., Deployment, StatefulSet). + MatchedField string `protobuf:"bytes,4,opt,name=matched_field,json=matchedField,proto3" json:"matched_field,omitempty"` // Field that matched the search query (name, uid, or namespace). + Namespace string `protobuf:"bytes,5,opt,name=namespace,proto3" json:"namespace,omitempty"` // Namespace of the resource. } -func (x *DailyUtilizationInstanceTypeRequest) Reset() { - *x = DailyUtilizationInstanceTypeRequest{} +func (x *K8SSearchResult) Reset() { + *x = K8SSearchResult{} if protoimpl.UnsafeEnabled { mi := &file_api_v1_k8s_proto_msgTypes[131] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -9922,13 +10500,13 @@ func (x *DailyUtilizationInstanceTypeRequest) Reset() { } } -func (x *DailyUtilizationInstanceTypeRequest) String() string { +func (x *K8SSearchResult) String() string { return protoimpl.X.MessageStringOf(x) } -func (*DailyUtilizationInstanceTypeRequest) ProtoMessage() {} +func (*K8SSearchResult) ProtoMessage() {} -func (x *DailyUtilizationInstanceTypeRequest) ProtoReflect() protoreflect.Message { +func (x *K8SSearchResult) ProtoReflect() protoreflect.Message { mi := &file_api_v1_k8s_proto_msgTypes[131] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -9940,50 +10518,58 @@ func (x *DailyUtilizationInstanceTypeRequest) ProtoReflect() protoreflect.Messag return mi.MessageOf(x) } -// Deprecated: Use DailyUtilizationInstanceTypeRequest.ProtoReflect.Descriptor instead. -func (*DailyUtilizationInstanceTypeRequest) Descriptor() ([]byte, []int) { +// Deprecated: Use K8SSearchResult.ProtoReflect.Descriptor instead. +func (*K8SSearchResult) Descriptor() ([]byte, []int) { return file_api_v1_k8s_proto_rawDescGZIP(), []int{131} } -func (x *DailyUtilizationInstanceTypeRequest) GetTeamId() string { +func (x *K8SSearchResult) GetUid() string { if x != nil { - return x.TeamId + return x.Uid } return "" } -func (x *DailyUtilizationInstanceTypeRequest) GetClusterIds() []string { +func (x *K8SSearchResult) GetName() string { if x != nil { - return x.ClusterIds + return x.Name } - return nil + return "" } -func (x *DailyUtilizationInstanceTypeRequest) GetStartTime() *timestamppb.Timestamp { +func (x *K8SSearchResult) GetKind() string { if x != nil { - return x.StartTime + return x.Kind } - return nil + return "" } -func (x *DailyUtilizationInstanceTypeRequest) GetEndTime() *timestamppb.Timestamp { +func (x *K8SSearchResult) GetMatchedField() string { if x != nil { - return x.EndTime + return x.MatchedField } - return nil + return "" } -type DailyUtilizationInstanceTypeResponse struct { +func (x *K8SSearchResult) GetNamespace() string { + if x != nil { + return x.Namespace + } + return "" +} + +type SearchK8SWorkloadsRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - ClusterIdToDatapoints map[string]*DailyUtilizationInstanceTypeResponse_Datapoints `protobuf:"bytes,1,rep,name=cluster_id_to_datapoints,json=clusterIdToDatapoints,proto3" json:"cluster_id_to_datapoints,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` - ClusterIdToMeta map[string]*Cluster `protobuf:"bytes,11,rep,name=cluster_id_to_meta,json=clusterIdToMeta,proto3" json:"cluster_id_to_meta,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + TeamId string `protobuf:"bytes,1,opt,name=team_id,json=teamId,proto3" json:"team_id,omitempty"` // Unique identifier for the team. + ClusterIds []string `protobuf:"bytes,2,rep,name=cluster_ids,json=clusterIds,proto3" json:"cluster_ids,omitempty"` // Unique identifier for the cluster. + SearchQuery string `protobuf:"bytes,3,opt,name=search_query,json=searchQuery,proto3" json:"search_query,omitempty"` // Search term to match against name, uid, and namespace. } -func (x *DailyUtilizationInstanceTypeResponse) Reset() { - *x = DailyUtilizationInstanceTypeResponse{} +func (x *SearchK8SWorkloadsRequest) Reset() { + *x = SearchK8SWorkloadsRequest{} if protoimpl.UnsafeEnabled { mi := &file_api_v1_k8s_proto_msgTypes[132] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -9991,13 +10577,13 @@ func (x *DailyUtilizationInstanceTypeResponse) Reset() { } } -func (x *DailyUtilizationInstanceTypeResponse) String() string { +func (x *SearchK8SWorkloadsRequest) String() string { return protoimpl.X.MessageStringOf(x) } -func (*DailyUtilizationInstanceTypeResponse) ProtoMessage() {} +func (*SearchK8SWorkloadsRequest) ProtoMessage() {} -func (x *DailyUtilizationInstanceTypeResponse) ProtoReflect() protoreflect.Message { +func (x *SearchK8SWorkloadsRequest) ProtoReflect() protoreflect.Message { mi := &file_api_v1_k8s_proto_msgTypes[132] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -10009,38 +10595,42 @@ func (x *DailyUtilizationInstanceTypeResponse) ProtoReflect() protoreflect.Messa return mi.MessageOf(x) } -// Deprecated: Use DailyUtilizationInstanceTypeResponse.ProtoReflect.Descriptor instead. -func (*DailyUtilizationInstanceTypeResponse) Descriptor() ([]byte, []int) { +// Deprecated: Use SearchK8SWorkloadsRequest.ProtoReflect.Descriptor instead. +func (*SearchK8SWorkloadsRequest) Descriptor() ([]byte, []int) { return file_api_v1_k8s_proto_rawDescGZIP(), []int{132} } -func (x *DailyUtilizationInstanceTypeResponse) GetClusterIdToDatapoints() map[string]*DailyUtilizationInstanceTypeResponse_Datapoints { +func (x *SearchK8SWorkloadsRequest) GetTeamId() string { if x != nil { - return x.ClusterIdToDatapoints + return x.TeamId } - return nil + return "" } -func (x *DailyUtilizationInstanceTypeResponse) GetClusterIdToMeta() map[string]*Cluster { +func (x *SearchK8SWorkloadsRequest) GetClusterIds() []string { if x != nil { - return x.ClusterIdToMeta + return x.ClusterIds } return nil } -type DailyUtilizationNodeTypeRequest struct { +func (x *SearchK8SWorkloadsRequest) GetSearchQuery() string { + if x != nil { + return x.SearchQuery + } + return "" +} + +type SearchK8SWorkloadsResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - TeamId string `protobuf:"bytes,1,opt,name=team_id,json=teamId,proto3" json:"team_id,omitempty"` - ClusterIds []string `protobuf:"bytes,2,rep,name=cluster_ids,json=clusterIds,proto3" json:"cluster_ids,omitempty"` - StartTime *timestamppb.Timestamp `protobuf:"bytes,21,opt,name=start_time,json=startTime,proto3" json:"start_time,omitempty"` - EndTime *timestamppb.Timestamp `protobuf:"bytes,22,opt,name=end_time,json=endTime,proto3" json:"end_time,omitempty"` + Results []*K8SWorkloadSearchResult `protobuf:"bytes,1,rep,name=results,proto3" json:"results,omitempty"` // List of matching workloads. } -func (x *DailyUtilizationNodeTypeRequest) Reset() { - *x = DailyUtilizationNodeTypeRequest{} +func (x *SearchK8SWorkloadsResponse) Reset() { + *x = SearchK8SWorkloadsResponse{} if protoimpl.UnsafeEnabled { mi := &file_api_v1_k8s_proto_msgTypes[133] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -10048,13 +10638,13 @@ func (x *DailyUtilizationNodeTypeRequest) Reset() { } } -func (x *DailyUtilizationNodeTypeRequest) String() string { +func (x *SearchK8SWorkloadsResponse) String() string { return protoimpl.X.MessageStringOf(x) } -func (*DailyUtilizationNodeTypeRequest) ProtoMessage() {} +func (*SearchK8SWorkloadsResponse) ProtoMessage() {} -func (x *DailyUtilizationNodeTypeRequest) ProtoReflect() protoreflect.Message { +func (x *SearchK8SWorkloadsResponse) ProtoReflect() protoreflect.Message { mi := &file_api_v1_k8s_proto_msgTypes[133] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -10066,50 +10656,29 @@ func (x *DailyUtilizationNodeTypeRequest) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use DailyUtilizationNodeTypeRequest.ProtoReflect.Descriptor instead. -func (*DailyUtilizationNodeTypeRequest) Descriptor() ([]byte, []int) { +// Deprecated: Use SearchK8SWorkloadsResponse.ProtoReflect.Descriptor instead. +func (*SearchK8SWorkloadsResponse) Descriptor() ([]byte, []int) { return file_api_v1_k8s_proto_rawDescGZIP(), []int{133} } -func (x *DailyUtilizationNodeTypeRequest) GetTeamId() string { - if x != nil { - return x.TeamId - } - return "" -} - -func (x *DailyUtilizationNodeTypeRequest) GetClusterIds() []string { - if x != nil { - return x.ClusterIds - } - return nil -} - -func (x *DailyUtilizationNodeTypeRequest) GetStartTime() *timestamppb.Timestamp { - if x != nil { - return x.StartTime - } - return nil -} - -func (x *DailyUtilizationNodeTypeRequest) GetEndTime() *timestamppb.Timestamp { +func (x *SearchK8SWorkloadsResponse) GetResults() []*K8SWorkloadSearchResult { if x != nil { - return x.EndTime + return x.Results } return nil } -type DailyUtilizationNodeTypeResponse struct { +type K8SWorkloadSearchResult struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - ClusterIdToDatapoints map[string]*DailyUtilizationNodeTypeResponse_Datapoints `protobuf:"bytes,1,rep,name=cluster_id_to_datapoints,json=clusterIdToDatapoints,proto3" json:"cluster_id_to_datapoints,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` - ClusterIdToMeta map[string]*Cluster `protobuf:"bytes,11,rep,name=cluster_id_to_meta,json=clusterIdToMeta,proto3" json:"cluster_id_to_meta,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + Kind string `protobuf:"bytes,1,opt,name=kind,proto3" json:"kind,omitempty"` // Kind of the workload (e.g., Deployment, StatefulSet). + Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` // Name of the workload. } -func (x *DailyUtilizationNodeTypeResponse) Reset() { - *x = DailyUtilizationNodeTypeResponse{} +func (x *K8SWorkloadSearchResult) Reset() { + *x = K8SWorkloadSearchResult{} if protoimpl.UnsafeEnabled { mi := &file_api_v1_k8s_proto_msgTypes[134] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -10117,13 +10686,13 @@ func (x *DailyUtilizationNodeTypeResponse) Reset() { } } -func (x *DailyUtilizationNodeTypeResponse) String() string { +func (x *K8SWorkloadSearchResult) String() string { return protoimpl.X.MessageStringOf(x) } -func (*DailyUtilizationNodeTypeResponse) ProtoMessage() {} +func (*K8SWorkloadSearchResult) ProtoMessage() {} -func (x *DailyUtilizationNodeTypeResponse) ProtoReflect() protoreflect.Message { +func (x *K8SWorkloadSearchResult) ProtoReflect() protoreflect.Message { mi := &file_api_v1_k8s_proto_msgTypes[134] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -10135,38 +10704,38 @@ func (x *DailyUtilizationNodeTypeResponse) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use DailyUtilizationNodeTypeResponse.ProtoReflect.Descriptor instead. -func (*DailyUtilizationNodeTypeResponse) Descriptor() ([]byte, []int) { +// Deprecated: Use K8SWorkloadSearchResult.ProtoReflect.Descriptor instead. +func (*K8SWorkloadSearchResult) Descriptor() ([]byte, []int) { return file_api_v1_k8s_proto_rawDescGZIP(), []int{134} } -func (x *DailyUtilizationNodeTypeResponse) GetClusterIdToDatapoints() map[string]*DailyUtilizationNodeTypeResponse_Datapoints { +func (x *K8SWorkloadSearchResult) GetKind() string { if x != nil { - return x.ClusterIdToDatapoints + return x.Kind } - return nil + return "" } -func (x *DailyUtilizationNodeTypeResponse) GetClusterIdToMeta() map[string]*Cluster { +func (x *K8SWorkloadSearchResult) GetName() string { if x != nil { - return x.ClusterIdToMeta + return x.Name } - return nil + return "" } -// LookupNodeInstanceRequest is used to debug and inspect instance lookup for a node -type LookupNodeInstanceRequest struct { +type MetadataForWorkloadsRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - TeamId string `protobuf:"bytes,1,opt,name=team_id,json=teamId,proto3" json:"team_id,omitempty"` // Unique identifier for the team. - ClusterId string `protobuf:"bytes,2,opt,name=cluster_id,json=clusterId,proto3" json:"cluster_id,omitempty"` // Unique identifier for the cluster. - NodeId string `protobuf:"bytes,3,opt,name=node_id,json=nodeId,proto3" json:"node_id,omitempty"` // Unique identifier for the node. + TeamId string `protobuf:"bytes,1,opt,name=team_id,json=teamId,proto3" json:"team_id,omitempty"` + ClusterId string `protobuf:"bytes,2,opt,name=cluster_id,json=clusterId,proto3" json:"cluster_id,omitempty"` + WorkloadUidToKind map[string]K8SObjectKind `protobuf:"bytes,6,rep,name=workload_uid_to_kind,json=workloadUidToKind,proto3" json:"workload_uid_to_kind,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"varint,2,opt,name=value,proto3,enum=api.v1.K8SObjectKind"` + IncludeDeleted bool `protobuf:"varint,11,opt,name=include_deleted,json=includeDeleted,proto3" json:"include_deleted,omitempty"` } -func (x *LookupNodeInstanceRequest) Reset() { - *x = LookupNodeInstanceRequest{} +func (x *MetadataForWorkloadsRequest) Reset() { + *x = MetadataForWorkloadsRequest{} if protoimpl.UnsafeEnabled { mi := &file_api_v1_k8s_proto_msgTypes[135] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -10174,13 +10743,13 @@ func (x *LookupNodeInstanceRequest) Reset() { } } -func (x *LookupNodeInstanceRequest) String() string { +func (x *MetadataForWorkloadsRequest) String() string { return protoimpl.X.MessageStringOf(x) } -func (*LookupNodeInstanceRequest) ProtoMessage() {} +func (*MetadataForWorkloadsRequest) ProtoMessage() {} -func (x *LookupNodeInstanceRequest) ProtoReflect() protoreflect.Message { +func (x *MetadataForWorkloadsRequest) ProtoReflect() protoreflect.Message { mi := &file_api_v1_k8s_proto_msgTypes[135] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -10192,47 +10761,49 @@ func (x *LookupNodeInstanceRequest) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use LookupNodeInstanceRequest.ProtoReflect.Descriptor instead. -func (*LookupNodeInstanceRequest) Descriptor() ([]byte, []int) { +// Deprecated: Use MetadataForWorkloadsRequest.ProtoReflect.Descriptor instead. +func (*MetadataForWorkloadsRequest) Descriptor() ([]byte, []int) { return file_api_v1_k8s_proto_rawDescGZIP(), []int{135} } -func (x *LookupNodeInstanceRequest) GetTeamId() string { +func (x *MetadataForWorkloadsRequest) GetTeamId() string { if x != nil { return x.TeamId } return "" } -func (x *LookupNodeInstanceRequest) GetClusterId() string { +func (x *MetadataForWorkloadsRequest) GetClusterId() string { if x != nil { return x.ClusterId } return "" } -func (x *LookupNodeInstanceRequest) GetNodeId() string { +func (x *MetadataForWorkloadsRequest) GetWorkloadUidToKind() map[string]K8SObjectKind { if x != nil { - return x.NodeId + return x.WorkloadUidToKind } - return "" + return nil } -// InstanceLookupParams contains the parameters used to lookup an instance from cache -type InstanceLookupParams struct { +func (x *MetadataForWorkloadsRequest) GetIncludeDeleted() bool { + if x != nil { + return x.IncludeDeleted + } + return false +} + +type MetadataForWorkloadsResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - CloudProviderId int64 `protobuf:"varint,1,opt,name=cloud_provider_id,json=cloudProviderId,proto3" json:"cloud_provider_id,omitempty"` // Cloud provider ID used in lookup - RegionId int64 `protobuf:"varint,2,opt,name=region_id,json=regionId,proto3" json:"region_id,omitempty"` // Region ID used in lookup - AvailabilityZone string `protobuf:"bytes,3,opt,name=availability_zone,json=availabilityZone,proto3" json:"availability_zone,omitempty"` // Availability zone name used in lookup - InstanceType string `protobuf:"bytes,4,opt,name=instance_type,json=instanceType,proto3" json:"instance_type,omitempty"` // Instance type name used in lookup - OperatingSystemId int64 `protobuf:"varint,5,opt,name=operating_system_id,json=operatingSystemId,proto3" json:"operating_system_id,omitempty"` // Operating system ID used in lookup + WorkloadUidToMetadata map[string]*WorkloadMetadata `protobuf:"bytes,1,rep,name=workload_uid_to_metadata,json=workloadUidToMetadata,proto3" json:"workload_uid_to_metadata,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` } -func (x *InstanceLookupParams) Reset() { - *x = InstanceLookupParams{} +func (x *MetadataForWorkloadsResponse) Reset() { + *x = MetadataForWorkloadsResponse{} if protoimpl.UnsafeEnabled { mi := &file_api_v1_k8s_proto_msgTypes[136] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -10240,13 +10811,13 @@ func (x *InstanceLookupParams) Reset() { } } -func (x *InstanceLookupParams) String() string { +func (x *MetadataForWorkloadsResponse) String() string { return protoimpl.X.MessageStringOf(x) } -func (*InstanceLookupParams) ProtoMessage() {} +func (*MetadataForWorkloadsResponse) ProtoMessage() {} -func (x *InstanceLookupParams) ProtoReflect() protoreflect.Message { +func (x *MetadataForWorkloadsResponse) ProtoReflect() protoreflect.Message { mi := &file_api_v1_k8s_proto_msgTypes[136] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -10258,61 +10829,30 @@ func (x *InstanceLookupParams) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use InstanceLookupParams.ProtoReflect.Descriptor instead. -func (*InstanceLookupParams) Descriptor() ([]byte, []int) { +// Deprecated: Use MetadataForWorkloadsResponse.ProtoReflect.Descriptor instead. +func (*MetadataForWorkloadsResponse) Descriptor() ([]byte, []int) { return file_api_v1_k8s_proto_rawDescGZIP(), []int{136} } -func (x *InstanceLookupParams) GetCloudProviderId() int64 { - if x != nil { - return x.CloudProviderId - } - return 0 -} - -func (x *InstanceLookupParams) GetRegionId() int64 { - if x != nil { - return x.RegionId - } - return 0 -} - -func (x *InstanceLookupParams) GetAvailabilityZone() string { - if x != nil { - return x.AvailabilityZone - } - return "" -} - -func (x *InstanceLookupParams) GetInstanceType() string { - if x != nil { - return x.InstanceType - } - return "" -} - -func (x *InstanceLookupParams) GetOperatingSystemId() int64 { +func (x *MetadataForWorkloadsResponse) GetWorkloadUidToMetadata() map[string]*WorkloadMetadata { if x != nil { - return x.OperatingSystemId + return x.WorkloadUidToMetadata } - return 0 + return nil } -// LookupNodeInstanceResponse contains the results of instance lookup for a node -type LookupNodeInstanceResponse struct { +type AddClusterTagsRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - DynamicInstance *Instance `protobuf:"bytes,1,opt,name=dynamic_instance,json=dynamicInstance,proto3" json:"dynamic_instance,omitempty"` // Instance retrieved dynamically from cache using node attributes - CachedInstance *Instance `protobuf:"bytes,2,opt,name=cached_instance,json=cachedInstance,proto3" json:"cached_instance,omitempty"` // Instance retrieved from node.InstanceID if available - LookupParams *InstanceLookupParams `protobuf:"bytes,3,opt,name=lookup_params,json=lookupParams,proto3" json:"lookup_params,omitempty"` // Parameters used to lookup the dynamic instance - DynamicInstanceFound bool `protobuf:"varint,4,opt,name=dynamic_instance_found,json=dynamicInstanceFound,proto3" json:"dynamic_instance_found,omitempty"` // Whether dynamic instance lookup succeeded - CachedInstanceFound bool `protobuf:"varint,5,opt,name=cached_instance_found,json=cachedInstanceFound,proto3" json:"cached_instance_found,omitempty"` // Whether cached instance lookup succeeded + TeamId string `protobuf:"bytes,1,opt,name=team_id,json=teamId,proto3" json:"team_id,omitempty"` + ClusterId string `protobuf:"bytes,2,opt,name=cluster_id,json=clusterId,proto3" json:"cluster_id,omitempty"` + Tags []string `protobuf:"bytes,3,rep,name=tags,proto3" json:"tags,omitempty"` } -func (x *LookupNodeInstanceResponse) Reset() { - *x = LookupNodeInstanceResponse{} +func (x *AddClusterTagsRequest) Reset() { + *x = AddClusterTagsRequest{} if protoimpl.UnsafeEnabled { mi := &file_api_v1_k8s_proto_msgTypes[137] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -10320,13 +10860,13 @@ func (x *LookupNodeInstanceResponse) Reset() { } } -func (x *LookupNodeInstanceResponse) String() string { +func (x *AddClusterTagsRequest) String() string { return protoimpl.X.MessageStringOf(x) } -func (*LookupNodeInstanceResponse) ProtoMessage() {} +func (*AddClusterTagsRequest) ProtoMessage() {} -func (x *LookupNodeInstanceResponse) ProtoReflect() protoreflect.Message { +func (x *AddClusterTagsRequest) ProtoReflect() protoreflect.Message { mi := &file_api_v1_k8s_proto_msgTypes[137] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -10338,71 +10878,57 @@ func (x *LookupNodeInstanceResponse) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use LookupNodeInstanceResponse.ProtoReflect.Descriptor instead. -func (*LookupNodeInstanceResponse) Descriptor() ([]byte, []int) { +// Deprecated: Use AddClusterTagsRequest.ProtoReflect.Descriptor instead. +func (*AddClusterTagsRequest) Descriptor() ([]byte, []int) { return file_api_v1_k8s_proto_rawDescGZIP(), []int{137} } -func (x *LookupNodeInstanceResponse) GetDynamicInstance() *Instance { +func (x *AddClusterTagsRequest) GetTeamId() string { if x != nil { - return x.DynamicInstance + return x.TeamId } - return nil + return "" } -func (x *LookupNodeInstanceResponse) GetCachedInstance() *Instance { +func (x *AddClusterTagsRequest) GetClusterId() string { if x != nil { - return x.CachedInstance + return x.ClusterId } - return nil + return "" } -func (x *LookupNodeInstanceResponse) GetLookupParams() *InstanceLookupParams { +func (x *AddClusterTagsRequest) GetTags() []string { if x != nil { - return x.LookupParams + return x.Tags } return nil } -func (x *LookupNodeInstanceResponse) GetDynamicInstanceFound() bool { - if x != nil { - return x.DynamicInstanceFound - } - return false -} - -func (x *LookupNodeInstanceResponse) GetCachedInstanceFound() bool { - if x != nil { - return x.CachedInstanceFound - } - return false -} - -type GetNodeGroupsUtilizationResponse_ListNodeGroupMetrics struct { +type AddClusterTagsResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - NodeGroupMetrics []*NodeGroupMetric `protobuf:"bytes,1,rep,name=node_group_metrics,json=nodeGroupMetrics,proto3" json:"node_group_metrics,omitempty"` + Cluster *Cluster `protobuf:"bytes,1,opt,name=cluster,proto3" json:"cluster,omitempty"` } -func (x *GetNodeGroupsUtilizationResponse_ListNodeGroupMetrics) Reset() { - *x = GetNodeGroupsUtilizationResponse_ListNodeGroupMetrics{} +func (x *AddClusterTagsResponse) Reset() { + *x = AddClusterTagsResponse{} if protoimpl.UnsafeEnabled { - mi := &file_api_v1_k8s_proto_msgTypes[139] + mi := &file_api_v1_k8s_proto_msgTypes[138] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } -func (x *GetNodeGroupsUtilizationResponse_ListNodeGroupMetrics) String() string { +func (x *AddClusterTagsResponse) String() string { return protoimpl.X.MessageStringOf(x) } -func (*GetNodeGroupsUtilizationResponse_ListNodeGroupMetrics) ProtoMessage() {} +func (*AddClusterTagsResponse) ProtoMessage() {} -func (x *GetNodeGroupsUtilizationResponse_ListNodeGroupMetrics) ProtoReflect() protoreflect.Message { - mi := &file_api_v1_k8s_proto_msgTypes[139] +func (x *AddClusterTagsResponse) ProtoReflect() protoreflect.Message { + mi := &file_api_v1_k8s_proto_msgTypes[138] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -10413,43 +10939,45 @@ func (x *GetNodeGroupsUtilizationResponse_ListNodeGroupMetrics) ProtoReflect() p return mi.MessageOf(x) } -// Deprecated: Use GetNodeGroupsUtilizationResponse_ListNodeGroupMetrics.ProtoReflect.Descriptor instead. -func (*GetNodeGroupsUtilizationResponse_ListNodeGroupMetrics) Descriptor() ([]byte, []int) { - return file_api_v1_k8s_proto_rawDescGZIP(), []int{35, 0} +// Deprecated: Use AddClusterTagsResponse.ProtoReflect.Descriptor instead. +func (*AddClusterTagsResponse) Descriptor() ([]byte, []int) { + return file_api_v1_k8s_proto_rawDescGZIP(), []int{138} } -func (x *GetNodeGroupsUtilizationResponse_ListNodeGroupMetrics) GetNodeGroupMetrics() []*NodeGroupMetric { +func (x *AddClusterTagsResponse) GetCluster() *Cluster { if x != nil { - return x.NodeGroupMetrics + return x.Cluster } return nil } -type DailyUtilizationResponse_Datapoints struct { +type RemoveClusterTagsRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Datapoints []*DailyUtilizationResponse_Datapoint `protobuf:"bytes,1,rep,name=datapoints,proto3" json:"datapoints,omitempty"` + TeamId string `protobuf:"bytes,1,opt,name=team_id,json=teamId,proto3" json:"team_id,omitempty"` + ClusterId string `protobuf:"bytes,2,opt,name=cluster_id,json=clusterId,proto3" json:"cluster_id,omitempty"` + Tags []string `protobuf:"bytes,3,rep,name=tags,proto3" json:"tags,omitempty"` } -func (x *DailyUtilizationResponse_Datapoints) Reset() { - *x = DailyUtilizationResponse_Datapoints{} +func (x *RemoveClusterTagsRequest) Reset() { + *x = RemoveClusterTagsRequest{} if protoimpl.UnsafeEnabled { - mi := &file_api_v1_k8s_proto_msgTypes[150] + mi := &file_api_v1_k8s_proto_msgTypes[139] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } -func (x *DailyUtilizationResponse_Datapoints) String() string { +func (x *RemoveClusterTagsRequest) String() string { return protoimpl.X.MessageStringOf(x) } -func (*DailyUtilizationResponse_Datapoints) ProtoMessage() {} +func (*RemoveClusterTagsRequest) ProtoMessage() {} -func (x *DailyUtilizationResponse_Datapoints) ProtoReflect() protoreflect.Message { - mi := &file_api_v1_k8s_proto_msgTypes[150] +func (x *RemoveClusterTagsRequest) ProtoReflect() protoreflect.Message { + mi := &file_api_v1_k8s_proto_msgTypes[139] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -10460,44 +10988,57 @@ func (x *DailyUtilizationResponse_Datapoints) ProtoReflect() protoreflect.Messag return mi.MessageOf(x) } -// Deprecated: Use DailyUtilizationResponse_Datapoints.ProtoReflect.Descriptor instead. -func (*DailyUtilizationResponse_Datapoints) Descriptor() ([]byte, []int) { - return file_api_v1_k8s_proto_rawDescGZIP(), []int{130, 0} +// Deprecated: Use RemoveClusterTagsRequest.ProtoReflect.Descriptor instead. +func (*RemoveClusterTagsRequest) Descriptor() ([]byte, []int) { + return file_api_v1_k8s_proto_rawDescGZIP(), []int{139} } -func (x *DailyUtilizationResponse_Datapoints) GetDatapoints() []*DailyUtilizationResponse_Datapoint { +func (x *RemoveClusterTagsRequest) GetTeamId() string { if x != nil { - return x.Datapoints + return x.TeamId + } + return "" +} + +func (x *RemoveClusterTagsRequest) GetClusterId() string { + if x != nil { + return x.ClusterId + } + return "" +} + +func (x *RemoveClusterTagsRequest) GetTags() []string { + if x != nil { + return x.Tags } return nil } -type DailyUtilizationResponse_Datapoint struct { +type RemoveClusterTagsResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Timestamp int64 `protobuf:"varint,1,opt,name=timestamp,proto3" json:"timestamp,omitempty"` // Unix timestamp (seconds since epoch). - Value int32 `protobuf:"varint,6,opt,name=value,proto3" json:"value,omitempty"` // some value + Cluster *Cluster `protobuf:"bytes,1,opt,name=cluster,proto3" json:"cluster,omitempty"` } -func (x *DailyUtilizationResponse_Datapoint) Reset() { - *x = DailyUtilizationResponse_Datapoint{} +func (x *RemoveClusterTagsResponse) Reset() { + *x = RemoveClusterTagsResponse{} if protoimpl.UnsafeEnabled { - mi := &file_api_v1_k8s_proto_msgTypes[151] + mi := &file_api_v1_k8s_proto_msgTypes[140] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } -func (x *DailyUtilizationResponse_Datapoint) String() string { +func (x *RemoveClusterTagsResponse) String() string { return protoimpl.X.MessageStringOf(x) } -func (*DailyUtilizationResponse_Datapoint) ProtoMessage() {} +func (*RemoveClusterTagsResponse) ProtoMessage() {} -func (x *DailyUtilizationResponse_Datapoint) ProtoReflect() protoreflect.Message { - mi := &file_api_v1_k8s_proto_msgTypes[151] +func (x *RemoveClusterTagsResponse) ProtoReflect() protoreflect.Message { + mi := &file_api_v1_k8s_proto_msgTypes[140] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -10508,50 +11049,43 @@ func (x *DailyUtilizationResponse_Datapoint) ProtoReflect() protoreflect.Message return mi.MessageOf(x) } -// Deprecated: Use DailyUtilizationResponse_Datapoint.ProtoReflect.Descriptor instead. -func (*DailyUtilizationResponse_Datapoint) Descriptor() ([]byte, []int) { - return file_api_v1_k8s_proto_rawDescGZIP(), []int{130, 1} -} - -func (x *DailyUtilizationResponse_Datapoint) GetTimestamp() int64 { - if x != nil { - return x.Timestamp - } - return 0 +// Deprecated: Use RemoveClusterTagsResponse.ProtoReflect.Descriptor instead. +func (*RemoveClusterTagsResponse) Descriptor() ([]byte, []int) { + return file_api_v1_k8s_proto_rawDescGZIP(), []int{140} } -func (x *DailyUtilizationResponse_Datapoint) GetValue() int32 { +func (x *RemoveClusterTagsResponse) GetCluster() *Cluster { if x != nil { - return x.Value + return x.Cluster } - return 0 + return nil } -type DailyUtilizationInstanceTypeResponse_Datapoints struct { +type ListTagsRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Datapoints []*DailyUtilizationInstanceTypeResponse_Datapoint `protobuf:"bytes,1,rep,name=datapoints,proto3" json:"datapoints,omitempty"` + TeamId string `protobuf:"bytes,1,opt,name=team_id,json=teamId,proto3" json:"team_id,omitempty"` } -func (x *DailyUtilizationInstanceTypeResponse_Datapoints) Reset() { - *x = DailyUtilizationInstanceTypeResponse_Datapoints{} +func (x *ListTagsRequest) Reset() { + *x = ListTagsRequest{} if protoimpl.UnsafeEnabled { - mi := &file_api_v1_k8s_proto_msgTypes[154] + mi := &file_api_v1_k8s_proto_msgTypes[141] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } -func (x *DailyUtilizationInstanceTypeResponse_Datapoints) String() string { +func (x *ListTagsRequest) String() string { return protoimpl.X.MessageStringOf(x) } -func (*DailyUtilizationInstanceTypeResponse_Datapoints) ProtoMessage() {} +func (*ListTagsRequest) ProtoMessage() {} -func (x *DailyUtilizationInstanceTypeResponse_Datapoints) ProtoReflect() protoreflect.Message { - mi := &file_api_v1_k8s_proto_msgTypes[154] +func (x *ListTagsRequest) ProtoReflect() protoreflect.Message { + mi := &file_api_v1_k8s_proto_msgTypes[141] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -10562,44 +11096,44 @@ func (x *DailyUtilizationInstanceTypeResponse_Datapoints) ProtoReflect() protore return mi.MessageOf(x) } -// Deprecated: Use DailyUtilizationInstanceTypeResponse_Datapoints.ProtoReflect.Descriptor instead. -func (*DailyUtilizationInstanceTypeResponse_Datapoints) Descriptor() ([]byte, []int) { - return file_api_v1_k8s_proto_rawDescGZIP(), []int{132, 0} +// Deprecated: Use ListTagsRequest.ProtoReflect.Descriptor instead. +func (*ListTagsRequest) Descriptor() ([]byte, []int) { + return file_api_v1_k8s_proto_rawDescGZIP(), []int{141} } -func (x *DailyUtilizationInstanceTypeResponse_Datapoints) GetDatapoints() []*DailyUtilizationInstanceTypeResponse_Datapoint { +func (x *ListTagsRequest) GetTeamId() string { if x != nil { - return x.Datapoints + return x.TeamId } - return nil + return "" } -type DailyUtilizationInstanceTypeResponse_Datapoint struct { +type TagSummary struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Timestamp int64 `protobuf:"varint,1,opt,name=timestamp,proto3" json:"timestamp,omitempty"` // Unix timestamp (seconds since epoch). - InstanceTypeToDailyTotalCoreMinutes map[string]int32 `protobuf:"bytes,11,rep,name=instance_type_to_daily_total_core_minutes,json=instanceTypeToDailyTotalCoreMinutes,proto3" json:"instance_type_to_daily_total_core_minutes,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"varint,2,opt,name=value,proto3"` + Tag string `protobuf:"bytes,1,opt,name=tag,proto3" json:"tag,omitempty"` + ClusterIds []string `protobuf:"bytes,2,rep,name=cluster_ids,json=clusterIds,proto3" json:"cluster_ids,omitempty"` } -func (x *DailyUtilizationInstanceTypeResponse_Datapoint) Reset() { - *x = DailyUtilizationInstanceTypeResponse_Datapoint{} +func (x *TagSummary) Reset() { + *x = TagSummary{} if protoimpl.UnsafeEnabled { - mi := &file_api_v1_k8s_proto_msgTypes[155] + mi := &file_api_v1_k8s_proto_msgTypes[142] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } -func (x *DailyUtilizationInstanceTypeResponse_Datapoint) String() string { +func (x *TagSummary) String() string { return protoimpl.X.MessageStringOf(x) } -func (*DailyUtilizationInstanceTypeResponse_Datapoint) ProtoMessage() {} +func (*TagSummary) ProtoMessage() {} -func (x *DailyUtilizationInstanceTypeResponse_Datapoint) ProtoReflect() protoreflect.Message { - mi := &file_api_v1_k8s_proto_msgTypes[155] +func (x *TagSummary) ProtoReflect() protoreflect.Message { + mi := &file_api_v1_k8s_proto_msgTypes[142] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -10610,50 +11144,50 @@ func (x *DailyUtilizationInstanceTypeResponse_Datapoint) ProtoReflect() protoref return mi.MessageOf(x) } -// Deprecated: Use DailyUtilizationInstanceTypeResponse_Datapoint.ProtoReflect.Descriptor instead. -func (*DailyUtilizationInstanceTypeResponse_Datapoint) Descriptor() ([]byte, []int) { - return file_api_v1_k8s_proto_rawDescGZIP(), []int{132, 1} +// Deprecated: Use TagSummary.ProtoReflect.Descriptor instead. +func (*TagSummary) Descriptor() ([]byte, []int) { + return file_api_v1_k8s_proto_rawDescGZIP(), []int{142} } -func (x *DailyUtilizationInstanceTypeResponse_Datapoint) GetTimestamp() int64 { +func (x *TagSummary) GetTag() string { if x != nil { - return x.Timestamp + return x.Tag } - return 0 + return "" } -func (x *DailyUtilizationInstanceTypeResponse_Datapoint) GetInstanceTypeToDailyTotalCoreMinutes() map[string]int32 { +func (x *TagSummary) GetClusterIds() []string { if x != nil { - return x.InstanceTypeToDailyTotalCoreMinutes + return x.ClusterIds } return nil } -type DailyUtilizationNodeTypeResponse_Datapoints struct { +type ListTagsResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Datapoints []*DailyUtilizationNodeTypeResponse_Datapoint `protobuf:"bytes,1,rep,name=datapoints,proto3" json:"datapoints,omitempty"` + Tags []*TagSummary `protobuf:"bytes,1,rep,name=tags,proto3" json:"tags,omitempty"` } -func (x *DailyUtilizationNodeTypeResponse_Datapoints) Reset() { - *x = DailyUtilizationNodeTypeResponse_Datapoints{} +func (x *ListTagsResponse) Reset() { + *x = ListTagsResponse{} if protoimpl.UnsafeEnabled { - mi := &file_api_v1_k8s_proto_msgTypes[159] + mi := &file_api_v1_k8s_proto_msgTypes[143] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } -func (x *DailyUtilizationNodeTypeResponse_Datapoints) String() string { +func (x *ListTagsResponse) String() string { return protoimpl.X.MessageStringOf(x) } -func (*DailyUtilizationNodeTypeResponse_Datapoints) ProtoMessage() {} +func (*ListTagsResponse) ProtoMessage() {} -func (x *DailyUtilizationNodeTypeResponse_Datapoints) ProtoReflect() protoreflect.Message { - mi := &file_api_v1_k8s_proto_msgTypes[159] +func (x *ListTagsResponse) ProtoReflect() protoreflect.Message { + mi := &file_api_v1_k8s_proto_msgTypes[143] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -10664,44 +11198,50 @@ func (x *DailyUtilizationNodeTypeResponse_Datapoints) ProtoReflect() protoreflec return mi.MessageOf(x) } -// Deprecated: Use DailyUtilizationNodeTypeResponse_Datapoints.ProtoReflect.Descriptor instead. -func (*DailyUtilizationNodeTypeResponse_Datapoints) Descriptor() ([]byte, []int) { - return file_api_v1_k8s_proto_rawDescGZIP(), []int{134, 0} +// Deprecated: Use ListTagsResponse.ProtoReflect.Descriptor instead. +func (*ListTagsResponse) Descriptor() ([]byte, []int) { + return file_api_v1_k8s_proto_rawDescGZIP(), []int{143} } -func (x *DailyUtilizationNodeTypeResponse_Datapoints) GetDatapoints() []*DailyUtilizationNodeTypeResponse_Datapoint { +func (x *ListTagsResponse) GetTags() []*TagSummary { if x != nil { - return x.Datapoints + return x.Tags } return nil } -type DailyUtilizationNodeTypeResponse_Datapoint struct { +type WorkloadMetadata struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Timestamp int64 `protobuf:"varint,1,opt,name=timestamp,proto3" json:"timestamp,omitempty"` // Unix timestamp (seconds since epoch). - NodeTypeToDailyTotalCoreMinutes map[string]int32 `protobuf:"bytes,11,rep,name=node_type_to_daily_total_core_minutes,json=nodeTypeToDailyTotalCoreMinutes,proto3" json:"node_type_to_daily_total_core_minutes,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"varint,2,opt,name=value,proto3"` + PodUidToNodeMetadata map[string]*NodeMetadata `protobuf:"bytes,1,rep,name=pod_uid_to_node_metadata,json=podUidToNodeMetadata,proto3" json:"pod_uid_to_node_metadata,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + PodUidToPodMetadata map[string]*PodMetadata `protobuf:"bytes,2,rep,name=pod_uid_to_pod_metadata,json=podUidToPodMetadata,proto3" json:"pod_uid_to_pod_metadata,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + Label string `protobuf:"bytes,6,opt,name=label,proto3" json:"label,omitempty"` + Annotation string `protobuf:"bytes,7,opt,name=annotation,proto3" json:"annotation,omitempty"` + Spec string `protobuf:"bytes,8,opt,name=spec,proto3" json:"spec,omitempty"` + Status string `protobuf:"bytes,9,opt,name=status,proto3" json:"status,omitempty"` + Metadata string `protobuf:"bytes,10,opt,name=metadata,proto3" json:"metadata,omitempty"` + Kind K8SObjectKind `protobuf:"varint,11,opt,name=kind,proto3,enum=api.v1.K8SObjectKind" json:"kind,omitempty"` } -func (x *DailyUtilizationNodeTypeResponse_Datapoint) Reset() { - *x = DailyUtilizationNodeTypeResponse_Datapoint{} +func (x *WorkloadMetadata) Reset() { + *x = WorkloadMetadata{} if protoimpl.UnsafeEnabled { - mi := &file_api_v1_k8s_proto_msgTypes[160] + mi := &file_api_v1_k8s_proto_msgTypes[144] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } -func (x *DailyUtilizationNodeTypeResponse_Datapoint) String() string { +func (x *WorkloadMetadata) String() string { return protoimpl.X.MessageStringOf(x) } -func (*DailyUtilizationNodeTypeResponse_Datapoint) ProtoMessage() {} +func (*WorkloadMetadata) ProtoMessage() {} -func (x *DailyUtilizationNodeTypeResponse_Datapoint) ProtoReflect() protoreflect.Message { - mi := &file_api_v1_k8s_proto_msgTypes[160] +func (x *WorkloadMetadata) ProtoReflect() protoreflect.Message { + mi := &file_api_v1_k8s_proto_msgTypes[144] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -10712,83 +11252,2426 @@ func (x *DailyUtilizationNodeTypeResponse_Datapoint) ProtoReflect() protoreflect return mi.MessageOf(x) } -// Deprecated: Use DailyUtilizationNodeTypeResponse_Datapoint.ProtoReflect.Descriptor instead. -func (*DailyUtilizationNodeTypeResponse_Datapoint) Descriptor() ([]byte, []int) { - return file_api_v1_k8s_proto_rawDescGZIP(), []int{134, 1} +// Deprecated: Use WorkloadMetadata.ProtoReflect.Descriptor instead. +func (*WorkloadMetadata) Descriptor() ([]byte, []int) { + return file_api_v1_k8s_proto_rawDescGZIP(), []int{144} } -func (x *DailyUtilizationNodeTypeResponse_Datapoint) GetTimestamp() int64 { +func (x *WorkloadMetadata) GetPodUidToNodeMetadata() map[string]*NodeMetadata { if x != nil { - return x.Timestamp + return x.PodUidToNodeMetadata } - return 0 + return nil } -func (x *DailyUtilizationNodeTypeResponse_Datapoint) GetNodeTypeToDailyTotalCoreMinutes() map[string]int32 { +func (x *WorkloadMetadata) GetPodUidToPodMetadata() map[string]*PodMetadata { if x != nil { - return x.NodeTypeToDailyTotalCoreMinutes + return x.PodUidToPodMetadata } return nil } -var File_api_v1_k8s_proto protoreflect.FileDescriptor +func (x *WorkloadMetadata) GetLabel() string { + if x != nil { + return x.Label + } + return "" +} -var file_api_v1_k8s_proto_rawDesc = []byte{ - 0x0a, 0x10, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x6b, 0x38, 0x73, 0x2e, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x12, 0x06, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x1a, 0x1f, 0x67, 0x6f, 0x6f, 0x67, - 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x74, 0x69, 0x6d, 0x65, - 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x13, 0x61, 0x70, 0x69, - 0x2f, 0x76, 0x31, 0x2f, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, - 0x1a, 0x15, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, - 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x17, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, - 0x74, 0x79, 0x70, 0x65, 0x2f, 0x6d, 0x6f, 0x6e, 0x65, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, - 0x22, 0xff, 0x02, 0x0a, 0x0e, 0x47, 0x65, 0x74, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x12, 0x17, 0x0a, 0x07, 0x74, 0x65, 0x61, 0x6d, 0x5f, 0x69, 0x64, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x74, 0x65, 0x61, 0x6d, 0x49, 0x64, 0x12, 0x1d, 0x0a, 0x0a, - 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x09, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, 0x64, 0x12, 0x1c, 0x0a, 0x07, 0x6e, - 0x6f, 0x64, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x06, - 0x6e, 0x6f, 0x64, 0x65, 0x49, 0x64, 0x88, 0x01, 0x01, 0x12, 0x1e, 0x0a, 0x08, 0x6e, 0x6f, 0x64, - 0x65, 0x5f, 0x75, 0x69, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x48, 0x01, 0x52, 0x07, 0x6e, - 0x6f, 0x64, 0x65, 0x55, 0x69, 0x64, 0x88, 0x01, 0x01, 0x12, 0x21, 0x0a, 0x0c, 0x77, 0x69, 0x74, - 0x68, 0x5f, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x08, 0x52, - 0x0b, 0x77, 0x69, 0x74, 0x68, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x23, 0x0a, 0x0d, - 0x77, 0x69, 0x74, 0x68, 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x0c, 0x20, - 0x01, 0x28, 0x08, 0x52, 0x0c, 0x77, 0x69, 0x74, 0x68, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, - 0x61, 0x12, 0x3e, 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, - 0x15, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, - 0x70, 0x48, 0x02, 0x52, 0x09, 0x73, 0x74, 0x61, 0x72, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x88, 0x01, - 0x01, 0x12, 0x3a, 0x0a, 0x08, 0x65, 0x6e, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x16, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x48, - 0x03, 0x52, 0x07, 0x65, 0x6e, 0x64, 0x54, 0x69, 0x6d, 0x65, 0x88, 0x01, 0x01, 0x42, 0x0a, 0x0a, - 0x08, 0x5f, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x69, 0x64, 0x42, 0x0b, 0x0a, 0x09, 0x5f, 0x6e, 0x6f, - 0x64, 0x65, 0x5f, 0x75, 0x69, 0x64, 0x42, 0x0d, 0x0a, 0x0b, 0x5f, 0x73, 0x74, 0x61, 0x72, 0x74, - 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x42, 0x0b, 0x0a, 0x09, 0x5f, 0x65, 0x6e, 0x64, 0x5f, 0x74, 0x69, - 0x6d, 0x65, 0x22, 0xb6, 0x02, 0x0a, 0x1c, 0x47, 0x65, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, - 0x61, 0x64, 0x50, 0x6f, 0x64, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x12, 0x17, 0x0a, 0x07, 0x74, 0x65, 0x61, 0x6d, 0x5f, 0x69, 0x64, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x74, 0x65, 0x61, 0x6d, 0x49, 0x64, 0x12, 0x1d, 0x0a, 0x0a, - 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x09, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, 0x64, 0x12, 0x23, 0x0a, 0x0d, 0x77, - 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x5f, 0x6b, 0x69, 0x6e, 0x64, 0x18, 0x03, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x0c, 0x77, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x4b, 0x69, 0x6e, 0x64, - 0x12, 0x21, 0x0a, 0x0c, 0x77, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x5f, 0x75, 0x69, 0x64, - 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x77, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, - 0x55, 0x69, 0x64, 0x12, 0x3e, 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x74, 0x69, 0x6d, - 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, - 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, - 0x61, 0x6d, 0x70, 0x48, 0x00, 0x52, 0x09, 0x73, 0x74, 0x61, 0x72, 0x74, 0x54, 0x69, 0x6d, 0x65, - 0x88, 0x01, 0x01, 0x12, 0x3a, 0x0a, 0x08, 0x65, 0x6e, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, - 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, - 0x70, 0x48, 0x01, 0x52, 0x07, 0x65, 0x6e, 0x64, 0x54, 0x69, 0x6d, 0x65, 0x88, 0x01, 0x01, 0x42, - 0x0d, 0x0a, 0x0b, 0x5f, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x42, 0x0b, - 0x0a, 0x09, 0x5f, 0x65, 0x6e, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x22, 0x34, 0x0a, 0x19, 0x47, - 0x65, 0x74, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, - 0x61, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x17, 0x0a, 0x07, 0x74, 0x65, 0x61, 0x6d, - 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x74, 0x65, 0x61, 0x6d, 0x49, +func (x *WorkloadMetadata) GetAnnotation() string { + if x != nil { + return x.Annotation + } + return "" +} + +func (x *WorkloadMetadata) GetSpec() string { + if x != nil { + return x.Spec + } + return "" +} + +func (x *WorkloadMetadata) GetStatus() string { + if x != nil { + return x.Status + } + return "" +} + +func (x *WorkloadMetadata) GetMetadata() string { + if x != nil { + return x.Metadata + } + return "" +} + +func (x *WorkloadMetadata) GetKind() K8SObjectKind { + if x != nil { + return x.Kind + } + return K8SObjectKind_K8S_OBJECT_KIND_UNSPECIFIED +} + +type PodMetadata struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + Label string `protobuf:"bytes,6,opt,name=label,proto3" json:"label,omitempty"` + Annotation string `protobuf:"bytes,7,opt,name=annotation,proto3" json:"annotation,omitempty"` + Spec string `protobuf:"bytes,8,opt,name=spec,proto3" json:"spec,omitempty"` + Status string `protobuf:"bytes,9,opt,name=status,proto3" json:"status,omitempty"` + Metadata string `protobuf:"bytes,10,opt,name=metadata,proto3" json:"metadata,omitempty"` +} + +func (x *PodMetadata) Reset() { + *x = PodMetadata{} + if protoimpl.UnsafeEnabled { + mi := &file_api_v1_k8s_proto_msgTypes[145] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *PodMetadata) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*PodMetadata) ProtoMessage() {} + +func (x *PodMetadata) ProtoReflect() protoreflect.Message { + mi := &file_api_v1_k8s_proto_msgTypes[145] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use PodMetadata.ProtoReflect.Descriptor instead. +func (*PodMetadata) Descriptor() ([]byte, []int) { + return file_api_v1_k8s_proto_rawDescGZIP(), []int{145} +} + +func (x *PodMetadata) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *PodMetadata) GetLabel() string { + if x != nil { + return x.Label + } + return "" +} + +func (x *PodMetadata) GetAnnotation() string { + if x != nil { + return x.Annotation + } + return "" +} + +func (x *PodMetadata) GetSpec() string { + if x != nil { + return x.Spec + } + return "" +} + +func (x *PodMetadata) GetStatus() string { + if x != nil { + return x.Status + } + return "" +} + +func (x *PodMetadata) GetMetadata() string { + if x != nil { + return x.Metadata + } + return "" +} + +type NodeMetadata struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + NodeName string `protobuf:"bytes,1,opt,name=node_name,json=nodeName,proto3" json:"node_name,omitempty"` + NodeGroupName string `protobuf:"bytes,2,opt,name=node_group_name,json=nodeGroupName,proto3" json:"node_group_name,omitempty"` + NodeGroupType string `protobuf:"bytes,3,opt,name=node_group_type,json=nodeGroupType,proto3" json:"node_group_type,omitempty"` + CloudInstTypeName string `protobuf:"bytes,4,opt,name=cloud_inst_type_name,json=cloudInstTypeName,proto3" json:"cloud_inst_type_name,omitempty"` + Label string `protobuf:"bytes,6,opt,name=label,proto3" json:"label,omitempty"` + Annotation string `protobuf:"bytes,7,opt,name=annotation,proto3" json:"annotation,omitempty"` + Spec string `protobuf:"bytes,8,opt,name=spec,proto3" json:"spec,omitempty"` + Status string `protobuf:"bytes,9,opt,name=status,proto3" json:"status,omitempty"` + Metadata string `protobuf:"bytes,10,opt,name=metadata,proto3" json:"metadata,omitempty"` +} + +func (x *NodeMetadata) Reset() { + *x = NodeMetadata{} + if protoimpl.UnsafeEnabled { + mi := &file_api_v1_k8s_proto_msgTypes[146] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *NodeMetadata) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*NodeMetadata) ProtoMessage() {} + +func (x *NodeMetadata) ProtoReflect() protoreflect.Message { + mi := &file_api_v1_k8s_proto_msgTypes[146] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use NodeMetadata.ProtoReflect.Descriptor instead. +func (*NodeMetadata) Descriptor() ([]byte, []int) { + return file_api_v1_k8s_proto_rawDescGZIP(), []int{146} +} + +func (x *NodeMetadata) GetNodeName() string { + if x != nil { + return x.NodeName + } + return "" +} + +func (x *NodeMetadata) GetNodeGroupName() string { + if x != nil { + return x.NodeGroupName + } + return "" +} + +func (x *NodeMetadata) GetNodeGroupType() string { + if x != nil { + return x.NodeGroupType + } + return "" +} + +func (x *NodeMetadata) GetCloudInstTypeName() string { + if x != nil { + return x.CloudInstTypeName + } + return "" +} + +func (x *NodeMetadata) GetLabel() string { + if x != nil { + return x.Label + } + return "" +} + +func (x *NodeMetadata) GetAnnotation() string { + if x != nil { + return x.Annotation + } + return "" +} + +func (x *NodeMetadata) GetSpec() string { + if x != nil { + return x.Spec + } + return "" +} + +func (x *NodeMetadata) GetStatus() string { + if x != nil { + return x.Status + } + return "" +} + +func (x *NodeMetadata) GetMetadata() string { + if x != nil { + return x.Metadata + } + return "" +} + +// GetRelatedResourcesRequest is used to fetch relations for a specific kind +type GetRelatedResourcesRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + TeamId string `protobuf:"bytes,1,opt,name=team_id,json=teamId,proto3" json:"team_id,omitempty"` // Unique identifier for the team. + ClusterId string `protobuf:"bytes,2,opt,name=cluster_id,json=clusterId,proto3" json:"cluster_id,omitempty"` // Unique identifier for the cluster. + Kind K8SObjectKind `protobuf:"varint,3,opt,name=kind,proto3,enum=api.v1.K8SObjectKind" json:"kind,omitempty"` // Type of object to get. + Uid string `protobuf:"bytes,4,opt,name=uid,proto3" json:"uid,omitempty"` // Unique identifier for the workload. + KarpenterKind *string `protobuf:"bytes,5,opt,name=karpenterKind,proto3,oneof" json:"karpenterKind,omitempty"` // used for karpenter deployments +} + +func (x *GetRelatedResourcesRequest) Reset() { + *x = GetRelatedResourcesRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_api_v1_k8s_proto_msgTypes[147] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetRelatedResourcesRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetRelatedResourcesRequest) ProtoMessage() {} + +func (x *GetRelatedResourcesRequest) ProtoReflect() protoreflect.Message { + mi := &file_api_v1_k8s_proto_msgTypes[147] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetRelatedResourcesRequest.ProtoReflect.Descriptor instead. +func (*GetRelatedResourcesRequest) Descriptor() ([]byte, []int) { + return file_api_v1_k8s_proto_rawDescGZIP(), []int{147} +} + +func (x *GetRelatedResourcesRequest) GetTeamId() string { + if x != nil { + return x.TeamId + } + return "" +} + +func (x *GetRelatedResourcesRequest) GetClusterId() string { + if x != nil { + return x.ClusterId + } + return "" +} + +func (x *GetRelatedResourcesRequest) GetKind() K8SObjectKind { + if x != nil { + return x.Kind + } + return K8SObjectKind_K8S_OBJECT_KIND_UNSPECIFIED +} + +func (x *GetRelatedResourcesRequest) GetUid() string { + if x != nil { + return x.Uid + } + return "" +} + +func (x *GetRelatedResourcesRequest) GetKarpenterKind() string { + if x != nil && x.KarpenterKind != nil { + return *x.KarpenterKind + } + return "" +} + +type GetWorkloadPodHistoryResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + WorkloadUid string `protobuf:"bytes,1,opt,name=workload_uid,json=workloadUid,proto3" json:"workload_uid,omitempty"` + ReplicaSets []*ReplicaSetHistory `protobuf:"bytes,2,rep,name=replica_sets,json=replicaSets,proto3" json:"replica_sets,omitempty"` // Populated if workload is Deployment or ArgoRollout + DirectPods []*PodHistory `protobuf:"bytes,3,rep,name=direct_pods,json=directPods,proto3" json:"direct_pods,omitempty"` // Populated if workload directly owns pods (e.g. StatefulSet, DaemonSet) + Jobs []*JobHistory `protobuf:"bytes,4,rep,name=jobs,proto3" json:"jobs,omitempty"` // Populated if workload is CronJob +} + +func (x *GetWorkloadPodHistoryResponse) Reset() { + *x = GetWorkloadPodHistoryResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_api_v1_k8s_proto_msgTypes[148] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetWorkloadPodHistoryResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetWorkloadPodHistoryResponse) ProtoMessage() {} + +func (x *GetWorkloadPodHistoryResponse) ProtoReflect() protoreflect.Message { + mi := &file_api_v1_k8s_proto_msgTypes[148] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetWorkloadPodHistoryResponse.ProtoReflect.Descriptor instead. +func (*GetWorkloadPodHistoryResponse) Descriptor() ([]byte, []int) { + return file_api_v1_k8s_proto_rawDescGZIP(), []int{148} +} + +func (x *GetWorkloadPodHistoryResponse) GetWorkloadUid() string { + if x != nil { + return x.WorkloadUid + } + return "" +} + +func (x *GetWorkloadPodHistoryResponse) GetReplicaSets() []*ReplicaSetHistory { + if x != nil { + return x.ReplicaSets + } + return nil +} + +func (x *GetWorkloadPodHistoryResponse) GetDirectPods() []*PodHistory { + if x != nil { + return x.DirectPods + } + return nil +} + +func (x *GetWorkloadPodHistoryResponse) GetJobs() []*JobHistory { + if x != nil { + return x.Jobs + } + return nil +} + +type JobHistory struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Uid string `protobuf:"bytes,1,opt,name=uid,proto3" json:"uid,omitempty"` + Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` + Status string `protobuf:"bytes,3,opt,name=status,proto3" json:"status,omitempty"` // active, succeeded, failed + CreatedAt *timestamppb.Timestamp `protobuf:"bytes,4,opt,name=created_at,json=createdAt,proto3" json:"created_at,omitempty"` + DeletedAt *timestamppb.Timestamp `protobuf:"bytes,5,opt,name=deleted_at,json=deletedAt,proto3" json:"deleted_at,omitempty"` + Pods []*PodHistory `protobuf:"bytes,6,rep,name=pods,proto3" json:"pods,omitempty"` +} + +func (x *JobHistory) Reset() { + *x = JobHistory{} + if protoimpl.UnsafeEnabled { + mi := &file_api_v1_k8s_proto_msgTypes[149] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *JobHistory) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*JobHistory) ProtoMessage() {} + +func (x *JobHistory) ProtoReflect() protoreflect.Message { + mi := &file_api_v1_k8s_proto_msgTypes[149] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use JobHistory.ProtoReflect.Descriptor instead. +func (*JobHistory) Descriptor() ([]byte, []int) { + return file_api_v1_k8s_proto_rawDescGZIP(), []int{149} +} + +func (x *JobHistory) GetUid() string { + if x != nil { + return x.Uid + } + return "" +} + +func (x *JobHistory) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *JobHistory) GetStatus() string { + if x != nil { + return x.Status + } + return "" +} + +func (x *JobHistory) GetCreatedAt() *timestamppb.Timestamp { + if x != nil { + return x.CreatedAt + } + return nil +} + +func (x *JobHistory) GetDeletedAt() *timestamppb.Timestamp { + if x != nil { + return x.DeletedAt + } + return nil +} + +func (x *JobHistory) GetPods() []*PodHistory { + if x != nil { + return x.Pods + } + return nil +} + +type ReplicaSetHistory struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Uid string `protobuf:"bytes,1,opt,name=uid,proto3" json:"uid,omitempty"` + Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` + CreatedAt *timestamppb.Timestamp `protobuf:"bytes,3,opt,name=created_at,json=createdAt,proto3" json:"created_at,omitempty"` + DeletedAt *timestamppb.Timestamp `protobuf:"bytes,4,opt,name=deleted_at,json=deletedAt,proto3" json:"deleted_at,omitempty"` + Revision string `protobuf:"bytes,5,opt,name=revision,proto3" json:"revision,omitempty"` // from adjustments/annotations if available + Pods []*PodHistory `protobuf:"bytes,6,rep,name=pods,proto3" json:"pods,omitempty"` +} + +func (x *ReplicaSetHistory) Reset() { + *x = ReplicaSetHistory{} + if protoimpl.UnsafeEnabled { + mi := &file_api_v1_k8s_proto_msgTypes[150] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ReplicaSetHistory) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ReplicaSetHistory) ProtoMessage() {} + +func (x *ReplicaSetHistory) ProtoReflect() protoreflect.Message { + mi := &file_api_v1_k8s_proto_msgTypes[150] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ReplicaSetHistory.ProtoReflect.Descriptor instead. +func (*ReplicaSetHistory) Descriptor() ([]byte, []int) { + return file_api_v1_k8s_proto_rawDescGZIP(), []int{150} +} + +func (x *ReplicaSetHistory) GetUid() string { + if x != nil { + return x.Uid + } + return "" +} + +func (x *ReplicaSetHistory) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *ReplicaSetHistory) GetCreatedAt() *timestamppb.Timestamp { + if x != nil { + return x.CreatedAt + } + return nil +} + +func (x *ReplicaSetHistory) GetDeletedAt() *timestamppb.Timestamp { + if x != nil { + return x.DeletedAt + } + return nil +} + +func (x *ReplicaSetHistory) GetRevision() string { + if x != nil { + return x.Revision + } + return "" +} + +func (x *ReplicaSetHistory) GetPods() []*PodHistory { + if x != nil { + return x.Pods + } + return nil +} + +type PodHistory struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Uid string `protobuf:"bytes,1,opt,name=uid,proto3" json:"uid,omitempty"` + Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` + Namespace string `protobuf:"bytes,3,opt,name=namespace,proto3" json:"namespace,omitempty"` + Phase string `protobuf:"bytes,4,opt,name=phase,proto3" json:"phase,omitempty"` + NodeName string `protobuf:"bytes,5,opt,name=node_name,json=nodeName,proto3" json:"node_name,omitempty"` + CreatedAt *timestamppb.Timestamp `protobuf:"bytes,6,opt,name=created_at,json=createdAt,proto3" json:"created_at,omitempty"` + DeletedAt *timestamppb.Timestamp `protobuf:"bytes,7,opt,name=deleted_at,json=deletedAt,proto3" json:"deleted_at,omitempty"` +} + +func (x *PodHistory) Reset() { + *x = PodHistory{} + if protoimpl.UnsafeEnabled { + mi := &file_api_v1_k8s_proto_msgTypes[151] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *PodHistory) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*PodHistory) ProtoMessage() {} + +func (x *PodHistory) ProtoReflect() protoreflect.Message { + mi := &file_api_v1_k8s_proto_msgTypes[151] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use PodHistory.ProtoReflect.Descriptor instead. +func (*PodHistory) Descriptor() ([]byte, []int) { + return file_api_v1_k8s_proto_rawDescGZIP(), []int{151} +} + +func (x *PodHistory) GetUid() string { + if x != nil { + return x.Uid + } + return "" +} + +func (x *PodHistory) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *PodHistory) GetNamespace() string { + if x != nil { + return x.Namespace + } + return "" +} + +func (x *PodHistory) GetPhase() string { + if x != nil { + return x.Phase + } + return "" +} + +func (x *PodHistory) GetNodeName() string { + if x != nil { + return x.NodeName + } + return "" +} + +func (x *PodHistory) GetCreatedAt() *timestamppb.Timestamp { + if x != nil { + return x.CreatedAt + } + return nil +} + +func (x *PodHistory) GetDeletedAt() *timestamppb.Timestamp { + if x != nil { + return x.DeletedAt + } + return nil +} + +type GetRelatedResourcesResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Relations []*K8SRelatedResource `protobuf:"bytes,1,rep,name=relations,proto3" json:"relations,omitempty"` // List of resource relations. + Nodes []*K8SResourceNode `protobuf:"bytes,2,rep,name=nodes,proto3" json:"nodes,omitempty"` // List of unique nodes (resources) in the graph. + Edges []*K8SResourceEdge `protobuf:"bytes,3,rep,name=edges,proto3" json:"edges,omitempty"` // List of edges for the graph. +} + +func (x *GetRelatedResourcesResponse) Reset() { + *x = GetRelatedResourcesResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_api_v1_k8s_proto_msgTypes[152] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetRelatedResourcesResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetRelatedResourcesResponse) ProtoMessage() {} + +func (x *GetRelatedResourcesResponse) ProtoReflect() protoreflect.Message { + mi := &file_api_v1_k8s_proto_msgTypes[152] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetRelatedResourcesResponse.ProtoReflect.Descriptor instead. +func (*GetRelatedResourcesResponse) Descriptor() ([]byte, []int) { + return file_api_v1_k8s_proto_rawDescGZIP(), []int{152} +} + +func (x *GetRelatedResourcesResponse) GetRelations() []*K8SRelatedResource { + if x != nil { + return x.Relations + } + return nil +} + +func (x *GetRelatedResourcesResponse) GetNodes() []*K8SResourceNode { + if x != nil { + return x.Nodes + } + return nil +} + +func (x *GetRelatedResourcesResponse) GetEdges() []*K8SResourceEdge { + if x != nil { + return x.Edges + } + return nil +} + +// K8sRelatedResource represents a relationship between two Kubernetes resources +type K8SRelatedResource struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + SourceKind string `protobuf:"bytes,1,opt,name=source_kind,json=sourceKind,proto3" json:"source_kind,omitempty"` // Kind of the source resource (e.g., Deployment, ReplicaSet). + SourceUid string `protobuf:"bytes,2,opt,name=source_uid,json=sourceUid,proto3" json:"source_uid,omitempty"` // UID of the source resource. + SourceName string `protobuf:"bytes,3,opt,name=source_name,json=sourceName,proto3" json:"source_name,omitempty"` // Name of the source resource. + TargetKind string `protobuf:"bytes,4,opt,name=target_kind,json=targetKind,proto3" json:"target_kind,omitempty"` // Kind of the target resource (e.g., ReplicaSet, Pod). + TargetUid string `protobuf:"bytes,5,opt,name=target_uid,json=targetUid,proto3" json:"target_uid,omitempty"` // UID of the target resource. + TargetName string `protobuf:"bytes,6,opt,name=target_name,json=targetName,proto3" json:"target_name,omitempty"` // Name of the target resource. + RelationshipType string `protobuf:"bytes,7,opt,name=relationship_type,json=relationshipType,proto3" json:"relationship_type,omitempty"` // Type of relationship (e.g., owns, controls, selects). +} + +func (x *K8SRelatedResource) Reset() { + *x = K8SRelatedResource{} + if protoimpl.UnsafeEnabled { + mi := &file_api_v1_k8s_proto_msgTypes[153] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *K8SRelatedResource) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*K8SRelatedResource) ProtoMessage() {} + +func (x *K8SRelatedResource) ProtoReflect() protoreflect.Message { + mi := &file_api_v1_k8s_proto_msgTypes[153] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use K8SRelatedResource.ProtoReflect.Descriptor instead. +func (*K8SRelatedResource) Descriptor() ([]byte, []int) { + return file_api_v1_k8s_proto_rawDescGZIP(), []int{153} +} + +func (x *K8SRelatedResource) GetSourceKind() string { + if x != nil { + return x.SourceKind + } + return "" +} + +func (x *K8SRelatedResource) GetSourceUid() string { + if x != nil { + return x.SourceUid + } + return "" +} + +func (x *K8SRelatedResource) GetSourceName() string { + if x != nil { + return x.SourceName + } + return "" +} + +func (x *K8SRelatedResource) GetTargetKind() string { + if x != nil { + return x.TargetKind + } + return "" +} + +func (x *K8SRelatedResource) GetTargetUid() string { + if x != nil { + return x.TargetUid + } + return "" +} + +func (x *K8SRelatedResource) GetTargetName() string { + if x != nil { + return x.TargetName + } + return "" +} + +func (x *K8SRelatedResource) GetRelationshipType() string { + if x != nil { + return x.RelationshipType + } + return "" +} + +// K8sResourceNode represents a node in the resource graph +type K8SResourceNode struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` // Unique identifier (UID) of the resource. + Kind string `protobuf:"bytes,2,opt,name=kind,proto3" json:"kind,omitempty"` // Kind of the resource (e.g., Deployment, ReplicaSet, Pod). + Name string `protobuf:"bytes,3,opt,name=name,proto3" json:"name,omitempty"` // Name of the resource. +} + +func (x *K8SResourceNode) Reset() { + *x = K8SResourceNode{} + if protoimpl.UnsafeEnabled { + mi := &file_api_v1_k8s_proto_msgTypes[154] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *K8SResourceNode) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*K8SResourceNode) ProtoMessage() {} + +func (x *K8SResourceNode) ProtoReflect() protoreflect.Message { + mi := &file_api_v1_k8s_proto_msgTypes[154] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use K8SResourceNode.ProtoReflect.Descriptor instead. +func (*K8SResourceNode) Descriptor() ([]byte, []int) { + return file_api_v1_k8s_proto_rawDescGZIP(), []int{154} +} + +func (x *K8SResourceNode) GetId() string { + if x != nil { + return x.Id + } + return "" +} + +func (x *K8SResourceNode) GetKind() string { + if x != nil { + return x.Kind + } + return "" +} + +func (x *K8SResourceNode) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +// K8sResourceEdge represents an edge in the resource graph +type K8SResourceEdge struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` // Unique identifier for the edge. + Source string `protobuf:"bytes,2,opt,name=source,proto3" json:"source,omitempty"` // UID of the source resource. + Target string `protobuf:"bytes,3,opt,name=target,proto3" json:"target,omitempty"` // UID of the target resource. + Label string `protobuf:"bytes,4,opt,name=label,proto3" json:"label,omitempty"` // Label for the edge (relationship type). +} + +func (x *K8SResourceEdge) Reset() { + *x = K8SResourceEdge{} + if protoimpl.UnsafeEnabled { + mi := &file_api_v1_k8s_proto_msgTypes[155] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *K8SResourceEdge) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*K8SResourceEdge) ProtoMessage() {} + +func (x *K8SResourceEdge) ProtoReflect() protoreflect.Message { + mi := &file_api_v1_k8s_proto_msgTypes[155] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use K8SResourceEdge.ProtoReflect.Descriptor instead. +func (*K8SResourceEdge) Descriptor() ([]byte, []int) { + return file_api_v1_k8s_proto_rawDescGZIP(), []int{155} +} + +func (x *K8SResourceEdge) GetId() string { + if x != nil { + return x.Id + } + return "" +} + +func (x *K8SResourceEdge) GetSource() string { + if x != nil { + return x.Source + } + return "" +} + +func (x *K8SResourceEdge) GetTarget() string { + if x != nil { + return x.Target + } + return "" +} + +func (x *K8SResourceEdge) GetLabel() string { + if x != nil { + return x.Label + } + return "" +} + +// GetClusterTypeRequest is used to determine the cluster type based on kubelet version +type GetClusterTypeRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + TeamId string `protobuf:"bytes,1,opt,name=team_id,json=teamId,proto3" json:"team_id,omitempty"` // Unique identifier for the team. + ClusterId string `protobuf:"bytes,2,opt,name=cluster_id,json=clusterId,proto3" json:"cluster_id,omitempty"` // Unique identifier for the cluster. +} + +func (x *GetClusterTypeRequest) Reset() { + *x = GetClusterTypeRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_api_v1_k8s_proto_msgTypes[156] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetClusterTypeRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetClusterTypeRequest) ProtoMessage() {} + +func (x *GetClusterTypeRequest) ProtoReflect() protoreflect.Message { + mi := &file_api_v1_k8s_proto_msgTypes[156] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetClusterTypeRequest.ProtoReflect.Descriptor instead. +func (*GetClusterTypeRequest) Descriptor() ([]byte, []int) { + return file_api_v1_k8s_proto_rawDescGZIP(), []int{156} +} + +func (x *GetClusterTypeRequest) GetTeamId() string { + if x != nil { + return x.TeamId + } + return "" +} + +func (x *GetClusterTypeRequest) GetClusterId() string { + if x != nil { + return x.ClusterId + } + return "" +} + +// GetClusterTypeResponse returns the determined cluster type +type GetClusterTypeResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + ClusterType ClusterType `protobuf:"varint,1,opt,name=cluster_type,json=clusterType,proto3,enum=api.v1.ClusterType" json:"cluster_type,omitempty"` // Detected cluster type + KubeletVersion string `protobuf:"bytes,2,opt,name=kubelet_version,json=kubeletVersion,proto3" json:"kubelet_version,omitempty"` // The raw kubelet version string +} + +func (x *GetClusterTypeResponse) Reset() { + *x = GetClusterTypeResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_api_v1_k8s_proto_msgTypes[157] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetClusterTypeResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetClusterTypeResponse) ProtoMessage() {} + +func (x *GetClusterTypeResponse) ProtoReflect() protoreflect.Message { + mi := &file_api_v1_k8s_proto_msgTypes[157] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetClusterTypeResponse.ProtoReflect.Descriptor instead. +func (*GetClusterTypeResponse) Descriptor() ([]byte, []int) { + return file_api_v1_k8s_proto_rawDescGZIP(), []int{157} +} + +func (x *GetClusterTypeResponse) GetClusterType() ClusterType { + if x != nil { + return x.ClusterType + } + return ClusterType_CLUSTER_TYPE_UNSPECIFIED +} + +func (x *GetClusterTypeResponse) GetKubeletVersion() string { + if x != nil { + return x.KubeletVersion + } + return "" +} + +type GetMigPartedConfigRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + TeamId string `protobuf:"bytes,1,opt,name=team_id,json=teamId,proto3" json:"team_id,omitempty"` + ClusterId string `protobuf:"bytes,2,opt,name=cluster_id,json=clusterId,proto3" json:"cluster_id,omitempty"` +} + +func (x *GetMigPartedConfigRequest) Reset() { + *x = GetMigPartedConfigRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_api_v1_k8s_proto_msgTypes[158] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetMigPartedConfigRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetMigPartedConfigRequest) ProtoMessage() {} + +func (x *GetMigPartedConfigRequest) ProtoReflect() protoreflect.Message { + mi := &file_api_v1_k8s_proto_msgTypes[158] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetMigPartedConfigRequest.ProtoReflect.Descriptor instead. +func (*GetMigPartedConfigRequest) Descriptor() ([]byte, []int) { + return file_api_v1_k8s_proto_rawDescGZIP(), []int{158} +} + +func (x *GetMigPartedConfigRequest) GetTeamId() string { + if x != nil { + return x.TeamId + } + return "" +} + +func (x *GetMigPartedConfigRequest) GetClusterId() string { + if x != nil { + return x.ClusterId + } + return "" +} + +type GetMigPartedConfigResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Whether a mig-parted ConfigMap has been collected for this cluster. + Found bool `protobuf:"varint,1,opt,name=found,proto3" json:"found,omitempty"` + Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` + Namespace string `protobuf:"bytes,3,opt,name=namespace,proto3" json:"namespace,omitempty"` + // Config schema version (e.g. v1). + Version string `protobuf:"bytes,4,opt,name=version,proto3" json:"version,omitempty"` + // Parsed profile map from config.yaml as JSON: + // {profile-name: [{devices, mig-enabled, mig-devices}, ...]}. + MigConfigsJson string `protobuf:"bytes,5,opt,name=mig_configs_json,json=migConfigsJson,proto3" json:"mig_configs_json,omitempty"` + // Original config.yaml text; authoritative when parse_error is set. + Raw string `protobuf:"bytes,6,opt,name=raw,proto3" json:"raw,omitempty"` + ParseError string `protobuf:"bytes,7,opt,name=parse_error,json=parseError,proto3" json:"parse_error,omitempty"` + LastSeen *timestamppb.Timestamp `protobuf:"bytes,8,opt,name=last_seen,json=lastSeen,proto3" json:"last_seen,omitempty"` +} + +func (x *GetMigPartedConfigResponse) Reset() { + *x = GetMigPartedConfigResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_api_v1_k8s_proto_msgTypes[159] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetMigPartedConfigResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetMigPartedConfigResponse) ProtoMessage() {} + +func (x *GetMigPartedConfigResponse) ProtoReflect() protoreflect.Message { + mi := &file_api_v1_k8s_proto_msgTypes[159] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetMigPartedConfigResponse.ProtoReflect.Descriptor instead. +func (*GetMigPartedConfigResponse) Descriptor() ([]byte, []int) { + return file_api_v1_k8s_proto_rawDescGZIP(), []int{159} +} + +func (x *GetMigPartedConfigResponse) GetFound() bool { + if x != nil { + return x.Found + } + return false +} + +func (x *GetMigPartedConfigResponse) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *GetMigPartedConfigResponse) GetNamespace() string { + if x != nil { + return x.Namespace + } + return "" +} + +func (x *GetMigPartedConfigResponse) GetVersion() string { + if x != nil { + return x.Version + } + return "" +} + +func (x *GetMigPartedConfigResponse) GetMigConfigsJson() string { + if x != nil { + return x.MigConfigsJson + } + return "" +} + +func (x *GetMigPartedConfigResponse) GetRaw() string { + if x != nil { + return x.Raw + } + return "" +} + +func (x *GetMigPartedConfigResponse) GetParseError() string { + if x != nil { + return x.ParseError + } + return "" +} + +func (x *GetMigPartedConfigResponse) GetLastSeen() *timestamppb.Timestamp { + if x != nil { + return x.LastSeen + } + return nil +} + +type GetWorkloadsStatsRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + TeamId string `protobuf:"bytes,1,opt,name=team_id,json=teamId,proto3" json:"team_id,omitempty"` // Unique identifier for the team. + ClusterId string `protobuf:"bytes,2,opt,name=cluster_id,json=clusterId,proto3" json:"cluster_id,omitempty"` // Unique identifier for the cluster. + StartTime *timestamppb.Timestamp `protobuf:"bytes,21,opt,name=start_time,json=startTime,proto3,oneof" json:"start_time,omitempty"` + EndTime *timestamppb.Timestamp `protobuf:"bytes,22,opt,name=end_time,json=endTime,proto3,oneof" json:"end_time,omitempty"` +} + +func (x *GetWorkloadsStatsRequest) Reset() { + *x = GetWorkloadsStatsRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_api_v1_k8s_proto_msgTypes[160] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetWorkloadsStatsRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetWorkloadsStatsRequest) ProtoMessage() {} + +func (x *GetWorkloadsStatsRequest) ProtoReflect() protoreflect.Message { + mi := &file_api_v1_k8s_proto_msgTypes[160] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetWorkloadsStatsRequest.ProtoReflect.Descriptor instead. +func (*GetWorkloadsStatsRequest) Descriptor() ([]byte, []int) { + return file_api_v1_k8s_proto_rawDescGZIP(), []int{160} +} + +func (x *GetWorkloadsStatsRequest) GetTeamId() string { + if x != nil { + return x.TeamId + } + return "" +} + +func (x *GetWorkloadsStatsRequest) GetClusterId() string { + if x != nil { + return x.ClusterId + } + return "" +} + +func (x *GetWorkloadsStatsRequest) GetStartTime() *timestamppb.Timestamp { + if x != nil { + return x.StartTime + } + return nil +} + +func (x *GetWorkloadsStatsRequest) GetEndTime() *timestamppb.Timestamp { + if x != nil { + return x.EndTime + } + return nil +} + +type GetWorkloadsStatsResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Total int32 `protobuf:"varint,1,opt,name=total,proto3" json:"total,omitempty"` + Optimized int32 `protobuf:"varint,2,opt,name=optimized,proto3" json:"optimized,omitempty"` + Unoptimized int32 `protobuf:"varint,3,opt,name=unoptimized,proto3" json:"unoptimized,omitempty"` + WithoutRequests int32 `protobuf:"varint,4,opt,name=without_requests,json=withoutRequests,proto3" json:"without_requests,omitempty"` + OptimizedPercentage int32 `protobuf:"varint,5,opt,name=optimized_percentage,json=optimizedPercentage,proto3" json:"optimized_percentage,omitempty"` +} + +func (x *GetWorkloadsStatsResponse) Reset() { + *x = GetWorkloadsStatsResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_api_v1_k8s_proto_msgTypes[161] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetWorkloadsStatsResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetWorkloadsStatsResponse) ProtoMessage() {} + +func (x *GetWorkloadsStatsResponse) ProtoReflect() protoreflect.Message { + mi := &file_api_v1_k8s_proto_msgTypes[161] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetWorkloadsStatsResponse.ProtoReflect.Descriptor instead. +func (*GetWorkloadsStatsResponse) Descriptor() ([]byte, []int) { + return file_api_v1_k8s_proto_rawDescGZIP(), []int{161} +} + +func (x *GetWorkloadsStatsResponse) GetTotal() int32 { + if x != nil { + return x.Total + } + return 0 +} + +func (x *GetWorkloadsStatsResponse) GetOptimized() int32 { + if x != nil { + return x.Optimized + } + return 0 +} + +func (x *GetWorkloadsStatsResponse) GetUnoptimized() int32 { + if x != nil { + return x.Unoptimized + } + return 0 +} + +func (x *GetWorkloadsStatsResponse) GetWithoutRequests() int32 { + if x != nil { + return x.WithoutRequests + } + return 0 +} + +func (x *GetWorkloadsStatsResponse) GetOptimizedPercentage() int32 { + if x != nil { + return x.OptimizedPercentage + } + return 0 +} + +type DailyUtilizationRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + TeamId string `protobuf:"bytes,1,opt,name=team_id,json=teamId,proto3" json:"team_id,omitempty"` + ClusterIds []string `protobuf:"bytes,2,rep,name=cluster_ids,json=clusterIds,proto3" json:"cluster_ids,omitempty"` + StartTime *timestamppb.Timestamp `protobuf:"bytes,21,opt,name=start_time,json=startTime,proto3" json:"start_time,omitempty"` + EndTime *timestamppb.Timestamp `protobuf:"bytes,22,opt,name=end_time,json=endTime,proto3" json:"end_time,omitempty"` +} + +func (x *DailyUtilizationRequest) Reset() { + *x = DailyUtilizationRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_api_v1_k8s_proto_msgTypes[162] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *DailyUtilizationRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*DailyUtilizationRequest) ProtoMessage() {} + +func (x *DailyUtilizationRequest) ProtoReflect() protoreflect.Message { + mi := &file_api_v1_k8s_proto_msgTypes[162] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use DailyUtilizationRequest.ProtoReflect.Descriptor instead. +func (*DailyUtilizationRequest) Descriptor() ([]byte, []int) { + return file_api_v1_k8s_proto_rawDescGZIP(), []int{162} +} + +func (x *DailyUtilizationRequest) GetTeamId() string { + if x != nil { + return x.TeamId + } + return "" +} + +func (x *DailyUtilizationRequest) GetClusterIds() []string { + if x != nil { + return x.ClusterIds + } + return nil +} + +func (x *DailyUtilizationRequest) GetStartTime() *timestamppb.Timestamp { + if x != nil { + return x.StartTime + } + return nil +} + +func (x *DailyUtilizationRequest) GetEndTime() *timestamppb.Timestamp { + if x != nil { + return x.EndTime + } + return nil +} + +type DailyUtilizationResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + ClusterIdToDailyTotalCoreMinutes map[string]*DailyUtilizationResponse_Datapoints `protobuf:"bytes,1,rep,name=cluster_id_to_daily_total_core_minutes,json=clusterIdToDailyTotalCoreMinutes,proto3" json:"cluster_id_to_daily_total_core_minutes,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + ClusterIdToMeta map[string]*Cluster `protobuf:"bytes,11,rep,name=cluster_id_to_meta,json=clusterIdToMeta,proto3" json:"cluster_id_to_meta,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` // for easier lookups + TotalCoreMinutesInPeriod int32 `protobuf:"varint,21,opt,name=total_core_minutes_in_period,json=totalCoreMinutesInPeriod,proto3" json:"total_core_minutes_in_period,omitempty"` +} + +func (x *DailyUtilizationResponse) Reset() { + *x = DailyUtilizationResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_api_v1_k8s_proto_msgTypes[163] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *DailyUtilizationResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*DailyUtilizationResponse) ProtoMessage() {} + +func (x *DailyUtilizationResponse) ProtoReflect() protoreflect.Message { + mi := &file_api_v1_k8s_proto_msgTypes[163] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use DailyUtilizationResponse.ProtoReflect.Descriptor instead. +func (*DailyUtilizationResponse) Descriptor() ([]byte, []int) { + return file_api_v1_k8s_proto_rawDescGZIP(), []int{163} +} + +func (x *DailyUtilizationResponse) GetClusterIdToDailyTotalCoreMinutes() map[string]*DailyUtilizationResponse_Datapoints { + if x != nil { + return x.ClusterIdToDailyTotalCoreMinutes + } + return nil +} + +func (x *DailyUtilizationResponse) GetClusterIdToMeta() map[string]*Cluster { + if x != nil { + return x.ClusterIdToMeta + } + return nil +} + +func (x *DailyUtilizationResponse) GetTotalCoreMinutesInPeriod() int32 { + if x != nil { + return x.TotalCoreMinutesInPeriod + } + return 0 +} + +type DailyUtilizationInstanceTypeRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + TeamId string `protobuf:"bytes,1,opt,name=team_id,json=teamId,proto3" json:"team_id,omitempty"` + ClusterIds []string `protobuf:"bytes,2,rep,name=cluster_ids,json=clusterIds,proto3" json:"cluster_ids,omitempty"` + StartTime *timestamppb.Timestamp `protobuf:"bytes,21,opt,name=start_time,json=startTime,proto3" json:"start_time,omitempty"` + EndTime *timestamppb.Timestamp `protobuf:"bytes,22,opt,name=end_time,json=endTime,proto3" json:"end_time,omitempty"` +} + +func (x *DailyUtilizationInstanceTypeRequest) Reset() { + *x = DailyUtilizationInstanceTypeRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_api_v1_k8s_proto_msgTypes[164] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *DailyUtilizationInstanceTypeRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*DailyUtilizationInstanceTypeRequest) ProtoMessage() {} + +func (x *DailyUtilizationInstanceTypeRequest) ProtoReflect() protoreflect.Message { + mi := &file_api_v1_k8s_proto_msgTypes[164] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use DailyUtilizationInstanceTypeRequest.ProtoReflect.Descriptor instead. +func (*DailyUtilizationInstanceTypeRequest) Descriptor() ([]byte, []int) { + return file_api_v1_k8s_proto_rawDescGZIP(), []int{164} +} + +func (x *DailyUtilizationInstanceTypeRequest) GetTeamId() string { + if x != nil { + return x.TeamId + } + return "" +} + +func (x *DailyUtilizationInstanceTypeRequest) GetClusterIds() []string { + if x != nil { + return x.ClusterIds + } + return nil +} + +func (x *DailyUtilizationInstanceTypeRequest) GetStartTime() *timestamppb.Timestamp { + if x != nil { + return x.StartTime + } + return nil +} + +func (x *DailyUtilizationInstanceTypeRequest) GetEndTime() *timestamppb.Timestamp { + if x != nil { + return x.EndTime + } + return nil +} + +type DailyUtilizationInstanceTypeResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + ClusterIdToDatapoints map[string]*DailyUtilizationInstanceTypeResponse_Datapoints `protobuf:"bytes,1,rep,name=cluster_id_to_datapoints,json=clusterIdToDatapoints,proto3" json:"cluster_id_to_datapoints,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + ClusterIdToMeta map[string]*Cluster `protobuf:"bytes,11,rep,name=cluster_id_to_meta,json=clusterIdToMeta,proto3" json:"cluster_id_to_meta,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` +} + +func (x *DailyUtilizationInstanceTypeResponse) Reset() { + *x = DailyUtilizationInstanceTypeResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_api_v1_k8s_proto_msgTypes[165] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *DailyUtilizationInstanceTypeResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*DailyUtilizationInstanceTypeResponse) ProtoMessage() {} + +func (x *DailyUtilizationInstanceTypeResponse) ProtoReflect() protoreflect.Message { + mi := &file_api_v1_k8s_proto_msgTypes[165] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use DailyUtilizationInstanceTypeResponse.ProtoReflect.Descriptor instead. +func (*DailyUtilizationInstanceTypeResponse) Descriptor() ([]byte, []int) { + return file_api_v1_k8s_proto_rawDescGZIP(), []int{165} +} + +func (x *DailyUtilizationInstanceTypeResponse) GetClusterIdToDatapoints() map[string]*DailyUtilizationInstanceTypeResponse_Datapoints { + if x != nil { + return x.ClusterIdToDatapoints + } + return nil +} + +func (x *DailyUtilizationInstanceTypeResponse) GetClusterIdToMeta() map[string]*Cluster { + if x != nil { + return x.ClusterIdToMeta + } + return nil +} + +type DailyUtilizationNodeTypeRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + TeamId string `protobuf:"bytes,1,opt,name=team_id,json=teamId,proto3" json:"team_id,omitempty"` + ClusterIds []string `protobuf:"bytes,2,rep,name=cluster_ids,json=clusterIds,proto3" json:"cluster_ids,omitempty"` + StartTime *timestamppb.Timestamp `protobuf:"bytes,21,opt,name=start_time,json=startTime,proto3" json:"start_time,omitempty"` + EndTime *timestamppb.Timestamp `protobuf:"bytes,22,opt,name=end_time,json=endTime,proto3" json:"end_time,omitempty"` +} + +func (x *DailyUtilizationNodeTypeRequest) Reset() { + *x = DailyUtilizationNodeTypeRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_api_v1_k8s_proto_msgTypes[166] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *DailyUtilizationNodeTypeRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*DailyUtilizationNodeTypeRequest) ProtoMessage() {} + +func (x *DailyUtilizationNodeTypeRequest) ProtoReflect() protoreflect.Message { + mi := &file_api_v1_k8s_proto_msgTypes[166] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use DailyUtilizationNodeTypeRequest.ProtoReflect.Descriptor instead. +func (*DailyUtilizationNodeTypeRequest) Descriptor() ([]byte, []int) { + return file_api_v1_k8s_proto_rawDescGZIP(), []int{166} +} + +func (x *DailyUtilizationNodeTypeRequest) GetTeamId() string { + if x != nil { + return x.TeamId + } + return "" +} + +func (x *DailyUtilizationNodeTypeRequest) GetClusterIds() []string { + if x != nil { + return x.ClusterIds + } + return nil +} + +func (x *DailyUtilizationNodeTypeRequest) GetStartTime() *timestamppb.Timestamp { + if x != nil { + return x.StartTime + } + return nil +} + +func (x *DailyUtilizationNodeTypeRequest) GetEndTime() *timestamppb.Timestamp { + if x != nil { + return x.EndTime + } + return nil +} + +type DailyUtilizationNodeTypeResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + ClusterIdToDatapoints map[string]*DailyUtilizationNodeTypeResponse_Datapoints `protobuf:"bytes,1,rep,name=cluster_id_to_datapoints,json=clusterIdToDatapoints,proto3" json:"cluster_id_to_datapoints,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + ClusterIdToMeta map[string]*Cluster `protobuf:"bytes,11,rep,name=cluster_id_to_meta,json=clusterIdToMeta,proto3" json:"cluster_id_to_meta,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` +} + +func (x *DailyUtilizationNodeTypeResponse) Reset() { + *x = DailyUtilizationNodeTypeResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_api_v1_k8s_proto_msgTypes[167] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *DailyUtilizationNodeTypeResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*DailyUtilizationNodeTypeResponse) ProtoMessage() {} + +func (x *DailyUtilizationNodeTypeResponse) ProtoReflect() protoreflect.Message { + mi := &file_api_v1_k8s_proto_msgTypes[167] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use DailyUtilizationNodeTypeResponse.ProtoReflect.Descriptor instead. +func (*DailyUtilizationNodeTypeResponse) Descriptor() ([]byte, []int) { + return file_api_v1_k8s_proto_rawDescGZIP(), []int{167} +} + +func (x *DailyUtilizationNodeTypeResponse) GetClusterIdToDatapoints() map[string]*DailyUtilizationNodeTypeResponse_Datapoints { + if x != nil { + return x.ClusterIdToDatapoints + } + return nil +} + +func (x *DailyUtilizationNodeTypeResponse) GetClusterIdToMeta() map[string]*Cluster { + if x != nil { + return x.ClusterIdToMeta + } + return nil +} + +// LookupNodeInstanceRequest is used to debug and inspect instance lookup for a node +type LookupNodeInstanceRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + TeamId string `protobuf:"bytes,1,opt,name=team_id,json=teamId,proto3" json:"team_id,omitempty"` // Unique identifier for the team. + ClusterId string `protobuf:"bytes,2,opt,name=cluster_id,json=clusterId,proto3" json:"cluster_id,omitempty"` // Unique identifier for the cluster. + NodeId string `protobuf:"bytes,3,opt,name=node_id,json=nodeId,proto3" json:"node_id,omitempty"` // Unique identifier for the node. +} + +func (x *LookupNodeInstanceRequest) Reset() { + *x = LookupNodeInstanceRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_api_v1_k8s_proto_msgTypes[168] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *LookupNodeInstanceRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*LookupNodeInstanceRequest) ProtoMessage() {} + +func (x *LookupNodeInstanceRequest) ProtoReflect() protoreflect.Message { + mi := &file_api_v1_k8s_proto_msgTypes[168] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use LookupNodeInstanceRequest.ProtoReflect.Descriptor instead. +func (*LookupNodeInstanceRequest) Descriptor() ([]byte, []int) { + return file_api_v1_k8s_proto_rawDescGZIP(), []int{168} +} + +func (x *LookupNodeInstanceRequest) GetTeamId() string { + if x != nil { + return x.TeamId + } + return "" +} + +func (x *LookupNodeInstanceRequest) GetClusterId() string { + if x != nil { + return x.ClusterId + } + return "" +} + +func (x *LookupNodeInstanceRequest) GetNodeId() string { + if x != nil { + return x.NodeId + } + return "" +} + +// InstanceLookupParams contains the parameters used to lookup an instance from cache +type InstanceLookupParams struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + CloudProviderId int64 `protobuf:"varint,1,opt,name=cloud_provider_id,json=cloudProviderId,proto3" json:"cloud_provider_id,omitempty"` // Cloud provider ID used in lookup + RegionId int64 `protobuf:"varint,2,opt,name=region_id,json=regionId,proto3" json:"region_id,omitempty"` // Region ID used in lookup + AvailabilityZone string `protobuf:"bytes,3,opt,name=availability_zone,json=availabilityZone,proto3" json:"availability_zone,omitempty"` // Availability zone name used in lookup + InstanceType string `protobuf:"bytes,4,opt,name=instance_type,json=instanceType,proto3" json:"instance_type,omitempty"` // Instance type name used in lookup + OperatingSystemId int64 `protobuf:"varint,5,opt,name=operating_system_id,json=operatingSystemId,proto3" json:"operating_system_id,omitempty"` // Operating system ID used in lookup +} + +func (x *InstanceLookupParams) Reset() { + *x = InstanceLookupParams{} + if protoimpl.UnsafeEnabled { + mi := &file_api_v1_k8s_proto_msgTypes[169] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *InstanceLookupParams) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*InstanceLookupParams) ProtoMessage() {} + +func (x *InstanceLookupParams) ProtoReflect() protoreflect.Message { + mi := &file_api_v1_k8s_proto_msgTypes[169] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use InstanceLookupParams.ProtoReflect.Descriptor instead. +func (*InstanceLookupParams) Descriptor() ([]byte, []int) { + return file_api_v1_k8s_proto_rawDescGZIP(), []int{169} +} + +func (x *InstanceLookupParams) GetCloudProviderId() int64 { + if x != nil { + return x.CloudProviderId + } + return 0 +} + +func (x *InstanceLookupParams) GetRegionId() int64 { + if x != nil { + return x.RegionId + } + return 0 +} + +func (x *InstanceLookupParams) GetAvailabilityZone() string { + if x != nil { + return x.AvailabilityZone + } + return "" +} + +func (x *InstanceLookupParams) GetInstanceType() string { + if x != nil { + return x.InstanceType + } + return "" +} + +func (x *InstanceLookupParams) GetOperatingSystemId() int64 { + if x != nil { + return x.OperatingSystemId + } + return 0 +} + +// LookupNodeInstanceResponse contains the results of instance lookup for a node +type LookupNodeInstanceResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + DynamicInstance *Instance `protobuf:"bytes,1,opt,name=dynamic_instance,json=dynamicInstance,proto3" json:"dynamic_instance,omitempty"` // Instance retrieved dynamically from cache using node attributes + CachedInstance *Instance `protobuf:"bytes,2,opt,name=cached_instance,json=cachedInstance,proto3" json:"cached_instance,omitempty"` // Instance retrieved from node.InstanceID if available + LookupParams *InstanceLookupParams `protobuf:"bytes,3,opt,name=lookup_params,json=lookupParams,proto3" json:"lookup_params,omitempty"` // Parameters used to lookup the dynamic instance + DynamicInstanceFound bool `protobuf:"varint,4,opt,name=dynamic_instance_found,json=dynamicInstanceFound,proto3" json:"dynamic_instance_found,omitempty"` // Whether dynamic instance lookup succeeded + CachedInstanceFound bool `protobuf:"varint,5,opt,name=cached_instance_found,json=cachedInstanceFound,proto3" json:"cached_instance_found,omitempty"` // Whether cached instance lookup succeeded +} + +func (x *LookupNodeInstanceResponse) Reset() { + *x = LookupNodeInstanceResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_api_v1_k8s_proto_msgTypes[170] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *LookupNodeInstanceResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*LookupNodeInstanceResponse) ProtoMessage() {} + +func (x *LookupNodeInstanceResponse) ProtoReflect() protoreflect.Message { + mi := &file_api_v1_k8s_proto_msgTypes[170] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use LookupNodeInstanceResponse.ProtoReflect.Descriptor instead. +func (*LookupNodeInstanceResponse) Descriptor() ([]byte, []int) { + return file_api_v1_k8s_proto_rawDescGZIP(), []int{170} +} + +func (x *LookupNodeInstanceResponse) GetDynamicInstance() *Instance { + if x != nil { + return x.DynamicInstance + } + return nil +} + +func (x *LookupNodeInstanceResponse) GetCachedInstance() *Instance { + if x != nil { + return x.CachedInstance + } + return nil +} + +func (x *LookupNodeInstanceResponse) GetLookupParams() *InstanceLookupParams { + if x != nil { + return x.LookupParams + } + return nil +} + +func (x *LookupNodeInstanceResponse) GetDynamicInstanceFound() bool { + if x != nil { + return x.DynamicInstanceFound + } + return false +} + +func (x *LookupNodeInstanceResponse) GetCachedInstanceFound() bool { + if x != nil { + return x.CachedInstanceFound + } + return false +} + +type GetNodeGroupsUtilizationResponse_ListNodeGroupMetrics struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + NodeGroupMetrics []*NodeGroupMetric `protobuf:"bytes,1,rep,name=node_group_metrics,json=nodeGroupMetrics,proto3" json:"node_group_metrics,omitempty"` +} + +func (x *GetNodeGroupsUtilizationResponse_ListNodeGroupMetrics) Reset() { + *x = GetNodeGroupsUtilizationResponse_ListNodeGroupMetrics{} + if protoimpl.UnsafeEnabled { + mi := &file_api_v1_k8s_proto_msgTypes[174] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetNodeGroupsUtilizationResponse_ListNodeGroupMetrics) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetNodeGroupsUtilizationResponse_ListNodeGroupMetrics) ProtoMessage() {} + +func (x *GetNodeGroupsUtilizationResponse_ListNodeGroupMetrics) ProtoReflect() protoreflect.Message { + mi := &file_api_v1_k8s_proto_msgTypes[174] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetNodeGroupsUtilizationResponse_ListNodeGroupMetrics.ProtoReflect.Descriptor instead. +func (*GetNodeGroupsUtilizationResponse_ListNodeGroupMetrics) Descriptor() ([]byte, []int) { + return file_api_v1_k8s_proto_rawDescGZIP(), []int{44, 0} +} + +func (x *GetNodeGroupsUtilizationResponse_ListNodeGroupMetrics) GetNodeGroupMetrics() []*NodeGroupMetric { + if x != nil { + return x.NodeGroupMetrics + } + return nil +} + +type DailyUtilizationResponse_Datapoints struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Datapoints []*DailyUtilizationResponse_Datapoint `protobuf:"bytes,1,rep,name=datapoints,proto3" json:"datapoints,omitempty"` +} + +func (x *DailyUtilizationResponse_Datapoints) Reset() { + *x = DailyUtilizationResponse_Datapoints{} + if protoimpl.UnsafeEnabled { + mi := &file_api_v1_k8s_proto_msgTypes[185] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *DailyUtilizationResponse_Datapoints) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*DailyUtilizationResponse_Datapoints) ProtoMessage() {} + +func (x *DailyUtilizationResponse_Datapoints) ProtoReflect() protoreflect.Message { + mi := &file_api_v1_k8s_proto_msgTypes[185] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use DailyUtilizationResponse_Datapoints.ProtoReflect.Descriptor instead. +func (*DailyUtilizationResponse_Datapoints) Descriptor() ([]byte, []int) { + return file_api_v1_k8s_proto_rawDescGZIP(), []int{163, 0} +} + +func (x *DailyUtilizationResponse_Datapoints) GetDatapoints() []*DailyUtilizationResponse_Datapoint { + if x != nil { + return x.Datapoints + } + return nil +} + +type DailyUtilizationResponse_Datapoint struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Timestamp int64 `protobuf:"varint,1,opt,name=timestamp,proto3" json:"timestamp,omitempty"` // Unix timestamp (seconds since epoch). + Value int32 `protobuf:"varint,6,opt,name=value,proto3" json:"value,omitempty"` // some value +} + +func (x *DailyUtilizationResponse_Datapoint) Reset() { + *x = DailyUtilizationResponse_Datapoint{} + if protoimpl.UnsafeEnabled { + mi := &file_api_v1_k8s_proto_msgTypes[186] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *DailyUtilizationResponse_Datapoint) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*DailyUtilizationResponse_Datapoint) ProtoMessage() {} + +func (x *DailyUtilizationResponse_Datapoint) ProtoReflect() protoreflect.Message { + mi := &file_api_v1_k8s_proto_msgTypes[186] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use DailyUtilizationResponse_Datapoint.ProtoReflect.Descriptor instead. +func (*DailyUtilizationResponse_Datapoint) Descriptor() ([]byte, []int) { + return file_api_v1_k8s_proto_rawDescGZIP(), []int{163, 1} +} + +func (x *DailyUtilizationResponse_Datapoint) GetTimestamp() int64 { + if x != nil { + return x.Timestamp + } + return 0 +} + +func (x *DailyUtilizationResponse_Datapoint) GetValue() int32 { + if x != nil { + return x.Value + } + return 0 +} + +type DailyUtilizationInstanceTypeResponse_Datapoints struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Datapoints []*DailyUtilizationInstanceTypeResponse_Datapoint `protobuf:"bytes,1,rep,name=datapoints,proto3" json:"datapoints,omitempty"` +} + +func (x *DailyUtilizationInstanceTypeResponse_Datapoints) Reset() { + *x = DailyUtilizationInstanceTypeResponse_Datapoints{} + if protoimpl.UnsafeEnabled { + mi := &file_api_v1_k8s_proto_msgTypes[189] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *DailyUtilizationInstanceTypeResponse_Datapoints) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*DailyUtilizationInstanceTypeResponse_Datapoints) ProtoMessage() {} + +func (x *DailyUtilizationInstanceTypeResponse_Datapoints) ProtoReflect() protoreflect.Message { + mi := &file_api_v1_k8s_proto_msgTypes[189] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use DailyUtilizationInstanceTypeResponse_Datapoints.ProtoReflect.Descriptor instead. +func (*DailyUtilizationInstanceTypeResponse_Datapoints) Descriptor() ([]byte, []int) { + return file_api_v1_k8s_proto_rawDescGZIP(), []int{165, 0} +} + +func (x *DailyUtilizationInstanceTypeResponse_Datapoints) GetDatapoints() []*DailyUtilizationInstanceTypeResponse_Datapoint { + if x != nil { + return x.Datapoints + } + return nil +} + +type DailyUtilizationInstanceTypeResponse_Datapoint struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Timestamp int64 `protobuf:"varint,1,opt,name=timestamp,proto3" json:"timestamp,omitempty"` // Unix timestamp (seconds since epoch). + InstanceTypeToDailyTotalCoreMinutes map[string]int32 `protobuf:"bytes,11,rep,name=instance_type_to_daily_total_core_minutes,json=instanceTypeToDailyTotalCoreMinutes,proto3" json:"instance_type_to_daily_total_core_minutes,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"varint,2,opt,name=value,proto3"` +} + +func (x *DailyUtilizationInstanceTypeResponse_Datapoint) Reset() { + *x = DailyUtilizationInstanceTypeResponse_Datapoint{} + if protoimpl.UnsafeEnabled { + mi := &file_api_v1_k8s_proto_msgTypes[190] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *DailyUtilizationInstanceTypeResponse_Datapoint) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*DailyUtilizationInstanceTypeResponse_Datapoint) ProtoMessage() {} + +func (x *DailyUtilizationInstanceTypeResponse_Datapoint) ProtoReflect() protoreflect.Message { + mi := &file_api_v1_k8s_proto_msgTypes[190] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use DailyUtilizationInstanceTypeResponse_Datapoint.ProtoReflect.Descriptor instead. +func (*DailyUtilizationInstanceTypeResponse_Datapoint) Descriptor() ([]byte, []int) { + return file_api_v1_k8s_proto_rawDescGZIP(), []int{165, 1} +} + +func (x *DailyUtilizationInstanceTypeResponse_Datapoint) GetTimestamp() int64 { + if x != nil { + return x.Timestamp + } + return 0 +} + +func (x *DailyUtilizationInstanceTypeResponse_Datapoint) GetInstanceTypeToDailyTotalCoreMinutes() map[string]int32 { + if x != nil { + return x.InstanceTypeToDailyTotalCoreMinutes + } + return nil +} + +type DailyUtilizationNodeTypeResponse_Datapoints struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Datapoints []*DailyUtilizationNodeTypeResponse_Datapoint `protobuf:"bytes,1,rep,name=datapoints,proto3" json:"datapoints,omitempty"` +} + +func (x *DailyUtilizationNodeTypeResponse_Datapoints) Reset() { + *x = DailyUtilizationNodeTypeResponse_Datapoints{} + if protoimpl.UnsafeEnabled { + mi := &file_api_v1_k8s_proto_msgTypes[194] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *DailyUtilizationNodeTypeResponse_Datapoints) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*DailyUtilizationNodeTypeResponse_Datapoints) ProtoMessage() {} + +func (x *DailyUtilizationNodeTypeResponse_Datapoints) ProtoReflect() protoreflect.Message { + mi := &file_api_v1_k8s_proto_msgTypes[194] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use DailyUtilizationNodeTypeResponse_Datapoints.ProtoReflect.Descriptor instead. +func (*DailyUtilizationNodeTypeResponse_Datapoints) Descriptor() ([]byte, []int) { + return file_api_v1_k8s_proto_rawDescGZIP(), []int{167, 0} +} + +func (x *DailyUtilizationNodeTypeResponse_Datapoints) GetDatapoints() []*DailyUtilizationNodeTypeResponse_Datapoint { + if x != nil { + return x.Datapoints + } + return nil +} + +type DailyUtilizationNodeTypeResponse_Datapoint struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Timestamp int64 `protobuf:"varint,1,opt,name=timestamp,proto3" json:"timestamp,omitempty"` // Unix timestamp (seconds since epoch). + NodeTypeToDailyTotalCoreMinutes map[string]int32 `protobuf:"bytes,11,rep,name=node_type_to_daily_total_core_minutes,json=nodeTypeToDailyTotalCoreMinutes,proto3" json:"node_type_to_daily_total_core_minutes,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"varint,2,opt,name=value,proto3"` +} + +func (x *DailyUtilizationNodeTypeResponse_Datapoint) Reset() { + *x = DailyUtilizationNodeTypeResponse_Datapoint{} + if protoimpl.UnsafeEnabled { + mi := &file_api_v1_k8s_proto_msgTypes[195] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *DailyUtilizationNodeTypeResponse_Datapoint) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*DailyUtilizationNodeTypeResponse_Datapoint) ProtoMessage() {} + +func (x *DailyUtilizationNodeTypeResponse_Datapoint) ProtoReflect() protoreflect.Message { + mi := &file_api_v1_k8s_proto_msgTypes[195] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use DailyUtilizationNodeTypeResponse_Datapoint.ProtoReflect.Descriptor instead. +func (*DailyUtilizationNodeTypeResponse_Datapoint) Descriptor() ([]byte, []int) { + return file_api_v1_k8s_proto_rawDescGZIP(), []int{167, 1} +} + +func (x *DailyUtilizationNodeTypeResponse_Datapoint) GetTimestamp() int64 { + if x != nil { + return x.Timestamp + } + return 0 +} + +func (x *DailyUtilizationNodeTypeResponse_Datapoint) GetNodeTypeToDailyTotalCoreMinutes() map[string]int32 { + if x != nil { + return x.NodeTypeToDailyTotalCoreMinutes + } + return nil +} + +var File_api_v1_k8s_proto protoreflect.FileDescriptor + +var file_api_v1_k8s_proto_rawDesc = []byte{ + 0x0a, 0x10, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x6b, 0x38, 0x73, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x12, 0x06, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x1a, 0x1f, 0x67, 0x6f, 0x6f, 0x67, + 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x74, 0x69, 0x6d, 0x65, + 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x13, 0x61, 0x70, 0x69, + 0x2f, 0x76, 0x31, 0x2f, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x1a, 0x15, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, + 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x17, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, + 0x74, 0x79, 0x70, 0x65, 0x2f, 0x6d, 0x6f, 0x6e, 0x65, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x22, 0xff, 0x02, 0x0a, 0x0e, 0x47, 0x65, 0x74, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x12, 0x17, 0x0a, 0x07, 0x74, 0x65, 0x61, 0x6d, 0x5f, 0x69, 0x64, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x74, 0x65, 0x61, 0x6d, 0x49, 0x64, 0x12, 0x1d, 0x0a, 0x0a, + 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x09, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, 0x64, 0x12, 0x1c, 0x0a, 0x07, 0x6e, + 0x6f, 0x64, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x06, + 0x6e, 0x6f, 0x64, 0x65, 0x49, 0x64, 0x88, 0x01, 0x01, 0x12, 0x1e, 0x0a, 0x08, 0x6e, 0x6f, 0x64, + 0x65, 0x5f, 0x75, 0x69, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x48, 0x01, 0x52, 0x07, 0x6e, + 0x6f, 0x64, 0x65, 0x55, 0x69, 0x64, 0x88, 0x01, 0x01, 0x12, 0x21, 0x0a, 0x0c, 0x77, 0x69, 0x74, + 0x68, 0x5f, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x08, 0x52, + 0x0b, 0x77, 0x69, 0x74, 0x68, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x23, 0x0a, 0x0d, + 0x77, 0x69, 0x74, 0x68, 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x0c, 0x20, + 0x01, 0x28, 0x08, 0x52, 0x0c, 0x77, 0x69, 0x74, 0x68, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, + 0x61, 0x12, 0x3e, 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, + 0x15, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, + 0x70, 0x48, 0x02, 0x52, 0x09, 0x73, 0x74, 0x61, 0x72, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x88, 0x01, + 0x01, 0x12, 0x3a, 0x0a, 0x08, 0x65, 0x6e, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x16, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x48, + 0x03, 0x52, 0x07, 0x65, 0x6e, 0x64, 0x54, 0x69, 0x6d, 0x65, 0x88, 0x01, 0x01, 0x42, 0x0a, 0x0a, + 0x08, 0x5f, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x69, 0x64, 0x42, 0x0b, 0x0a, 0x09, 0x5f, 0x6e, 0x6f, + 0x64, 0x65, 0x5f, 0x75, 0x69, 0x64, 0x42, 0x0d, 0x0a, 0x0b, 0x5f, 0x73, 0x74, 0x61, 0x72, 0x74, + 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x42, 0x0b, 0x0a, 0x09, 0x5f, 0x65, 0x6e, 0x64, 0x5f, 0x74, 0x69, + 0x6d, 0x65, 0x22, 0xb6, 0x02, 0x0a, 0x1c, 0x47, 0x65, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, + 0x61, 0x64, 0x50, 0x6f, 0x64, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x12, 0x17, 0x0a, 0x07, 0x74, 0x65, 0x61, 0x6d, 0x5f, 0x69, 0x64, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x74, 0x65, 0x61, 0x6d, 0x49, 0x64, 0x12, 0x1d, 0x0a, 0x0a, + 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x09, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, 0x64, 0x12, 0x23, 0x0a, 0x0d, 0x77, + 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x5f, 0x6b, 0x69, 0x6e, 0x64, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x0c, 0x77, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x4b, 0x69, 0x6e, 0x64, + 0x12, 0x21, 0x0a, 0x0c, 0x77, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x5f, 0x75, 0x69, 0x64, + 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x77, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, + 0x55, 0x69, 0x64, 0x12, 0x3e, 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x74, 0x69, 0x6d, + 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, + 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, + 0x61, 0x6d, 0x70, 0x48, 0x00, 0x52, 0x09, 0x73, 0x74, 0x61, 0x72, 0x74, 0x54, 0x69, 0x6d, 0x65, + 0x88, 0x01, 0x01, 0x12, 0x3a, 0x0a, 0x08, 0x65, 0x6e, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, + 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, + 0x70, 0x48, 0x01, 0x52, 0x07, 0x65, 0x6e, 0x64, 0x54, 0x69, 0x6d, 0x65, 0x88, 0x01, 0x01, 0x42, + 0x0d, 0x0a, 0x0b, 0x5f, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x42, 0x0b, + 0x0a, 0x09, 0x5f, 0x65, 0x6e, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x22, 0x34, 0x0a, 0x19, 0x47, + 0x65, 0x74, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, + 0x61, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x17, 0x0a, 0x07, 0x74, 0x65, 0x61, 0x6d, + 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x74, 0x65, 0x61, 0x6d, 0x49, 0x64, 0x22, 0xb0, 0x02, 0x0a, 0x13, 0x47, 0x65, 0x74, 0x4e, 0x6f, 0x64, 0x65, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x17, 0x0a, 0x07, 0x74, 0x65, 0x61, 0x6d, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x74, 0x65, 0x61, 0x6d, @@ -10974,7 +13857,110 @@ var file_api_v1_k8s_proto_rawDesc = []byte{ 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x42, 0x72, 0x65, 0x61, 0x6b, 0x64, 0x6f, 0x77, 0x6e, 0x52, 0x18, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x6f, 0x74, - 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x47, 0x70, 0x75, 0x22, 0xc3, 0x01, 0x0a, 0x1e, 0x4c, 0x69, + 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x47, 0x70, 0x75, 0x22, 0xb5, 0x02, 0x0a, 0x1f, 0x47, 0x65, + 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x54, 0x72, 0x65, 0x6e, 0x64, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x17, 0x0a, + 0x07, 0x74, 0x65, 0x61, 0x6d, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, + 0x74, 0x65, 0x61, 0x6d, 0x49, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, + 0x72, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x6c, 0x75, 0x73, + 0x74, 0x65, 0x72, 0x49, 0x64, 0x12, 0x3e, 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x74, + 0x69, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, + 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, + 0x73, 0x74, 0x61, 0x6d, 0x70, 0x48, 0x00, 0x52, 0x09, 0x73, 0x74, 0x61, 0x72, 0x74, 0x54, 0x69, + 0x6d, 0x65, 0x88, 0x01, 0x01, 0x12, 0x3a, 0x0a, 0x08, 0x65, 0x6e, 0x64, 0x5f, 0x74, 0x69, 0x6d, + 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, + 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, + 0x61, 0x6d, 0x70, 0x48, 0x01, 0x52, 0x07, 0x65, 0x6e, 0x64, 0x54, 0x69, 0x6d, 0x65, 0x88, 0x01, + 0x01, 0x12, 0x36, 0x0a, 0x07, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x73, 0x18, 0x05, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x57, 0x6f, 0x72, 0x6b, + 0x6c, 0x6f, 0x61, 0x64, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x73, 0x48, 0x02, 0x52, 0x07, 0x66, + 0x69, 0x6c, 0x74, 0x65, 0x72, 0x73, 0x88, 0x01, 0x01, 0x42, 0x0d, 0x0a, 0x0b, 0x5f, 0x73, 0x74, + 0x61, 0x72, 0x74, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x42, 0x0b, 0x0a, 0x09, 0x5f, 0x65, 0x6e, 0x64, + 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x42, 0x0a, 0x0a, 0x08, 0x5f, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, + 0x73, 0x22, 0x47, 0x0a, 0x11, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x54, 0x72, 0x65, 0x6e, + 0x64, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, + 0x61, 0x6d, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, + 0x74, 0x61, 0x6d, 0x70, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x01, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x61, 0x0a, 0x12, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x54, 0x72, 0x65, 0x6e, 0x64, 0x53, 0x65, 0x72, 0x69, 0x65, 0x73, + 0x12, 0x31, 0x0a, 0x06, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, + 0x32, 0x19, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x54, 0x72, 0x65, 0x6e, 0x64, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x52, 0x06, 0x70, 0x6f, 0x69, + 0x6e, 0x74, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x76, 0x65, 0x72, 0x61, 0x67, 0x65, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x01, 0x52, 0x07, 0x61, 0x76, 0x65, 0x72, 0x61, 0x67, 0x65, 0x22, 0x89, 0x09, + 0x0a, 0x20, 0x47, 0x65, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x54, 0x72, 0x65, 0x6e, 0x64, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x12, 0x52, 0x0a, 0x17, 0x75, 0x6e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x64, 0x5f, + 0x63, 0x70, 0x75, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x65, 0x64, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x54, 0x72, 0x65, 0x6e, 0x64, 0x53, 0x65, 0x72, 0x69, 0x65, 0x73, 0x52, + 0x15, 0x75, 0x6e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x64, 0x43, 0x70, 0x75, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x65, 0x64, 0x12, 0x58, 0x0a, 0x1a, 0x75, 0x6e, 0x6d, 0x61, 0x6e, 0x61, + 0x67, 0x65, 0x64, 0x5f, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x65, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x61, 0x70, 0x69, + 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x54, 0x72, 0x65, 0x6e, 0x64, + 0x53, 0x65, 0x72, 0x69, 0x65, 0x73, 0x52, 0x18, 0x75, 0x6e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, + 0x64, 0x4d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x65, 0x64, + 0x12, 0x4c, 0x0a, 0x14, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x64, 0x5f, 0x63, 0x70, 0x75, 0x5f, + 0x6f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x61, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, + 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x54, + 0x72, 0x65, 0x6e, 0x64, 0x53, 0x65, 0x72, 0x69, 0x65, 0x73, 0x52, 0x12, 0x6d, 0x61, 0x6e, 0x61, + 0x67, 0x65, 0x64, 0x43, 0x70, 0x75, 0x4f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x61, 0x6c, 0x12, 0x4e, + 0x0a, 0x15, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x64, 0x5f, 0x63, 0x70, 0x75, 0x5f, 0x6f, 0x70, + 0x74, 0x69, 0x6d, 0x69, 0x7a, 0x65, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, + 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x54, 0x72, + 0x65, 0x6e, 0x64, 0x53, 0x65, 0x72, 0x69, 0x65, 0x73, 0x52, 0x13, 0x6d, 0x61, 0x6e, 0x61, 0x67, + 0x65, 0x64, 0x43, 0x70, 0x75, 0x4f, 0x70, 0x74, 0x69, 0x6d, 0x69, 0x7a, 0x65, 0x64, 0x12, 0x52, + 0x0a, 0x17, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x64, 0x5f, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, + 0x5f, 0x6f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x61, 0x6c, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x1a, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x54, 0x72, 0x65, 0x6e, 0x64, 0x53, 0x65, 0x72, 0x69, 0x65, 0x73, 0x52, 0x15, 0x6d, 0x61, 0x6e, + 0x61, 0x67, 0x65, 0x64, 0x4d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x4f, 0x72, 0x69, 0x67, 0x69, 0x6e, + 0x61, 0x6c, 0x12, 0x54, 0x0a, 0x18, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x64, 0x5f, 0x6d, 0x65, + 0x6d, 0x6f, 0x72, 0x79, 0x5f, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x69, 0x7a, 0x65, 0x64, 0x18, 0x06, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x54, 0x72, 0x65, 0x6e, 0x64, 0x53, 0x65, 0x72, 0x69, 0x65, 0x73, + 0x52, 0x16, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x64, 0x4d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x4f, + 0x70, 0x74, 0x69, 0x6d, 0x69, 0x7a, 0x65, 0x64, 0x12, 0x34, 0x0a, 0x16, 0x6d, 0x61, 0x6e, 0x61, + 0x67, 0x65, 0x64, 0x5f, 0x77, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x5f, 0x63, 0x6f, 0x75, + 0x6e, 0x74, 0x18, 0x07, 0x20, 0x01, 0x28, 0x05, 0x52, 0x14, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, + 0x64, 0x57, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x38, + 0x0a, 0x18, 0x75, 0x6e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x64, 0x5f, 0x77, 0x6f, 0x72, 0x6b, + 0x6c, 0x6f, 0x61, 0x64, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x08, 0x20, 0x01, 0x28, 0x05, + 0x52, 0x16, 0x75, 0x6e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x64, 0x57, 0x6f, 0x72, 0x6b, 0x6c, + 0x6f, 0x61, 0x64, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x25, 0x0a, 0x0e, 0x62, 0x75, 0x63, 0x6b, + 0x65, 0x74, 0x5f, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x18, 0x09, 0x20, 0x01, 0x28, 0x03, + 0x52, 0x0d, 0x62, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x53, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x12, + 0x48, 0x0a, 0x12, 0x75, 0x6e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x64, 0x5f, 0x63, 0x70, 0x75, + 0x5f, 0x75, 0x73, 0x65, 0x64, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x61, 0x70, + 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x54, 0x72, 0x65, 0x6e, + 0x64, 0x53, 0x65, 0x72, 0x69, 0x65, 0x73, 0x52, 0x10, 0x75, 0x6e, 0x6d, 0x61, 0x6e, 0x61, 0x67, + 0x65, 0x64, 0x43, 0x70, 0x75, 0x55, 0x73, 0x65, 0x64, 0x12, 0x4e, 0x0a, 0x15, 0x75, 0x6e, 0x6d, + 0x61, 0x6e, 0x61, 0x67, 0x65, 0x64, 0x5f, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x5f, 0x75, 0x73, + 0x65, 0x64, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, + 0x31, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x54, 0x72, 0x65, 0x6e, 0x64, 0x53, 0x65, + 0x72, 0x69, 0x65, 0x73, 0x52, 0x13, 0x75, 0x6e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x64, 0x4d, + 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x55, 0x73, 0x65, 0x64, 0x12, 0x44, 0x0a, 0x10, 0x6d, 0x61, 0x6e, + 0x61, 0x67, 0x65, 0x64, 0x5f, 0x63, 0x70, 0x75, 0x5f, 0x75, 0x73, 0x65, 0x64, 0x18, 0x0c, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x54, 0x72, 0x65, 0x6e, 0x64, 0x53, 0x65, 0x72, 0x69, 0x65, 0x73, 0x52, + 0x0e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x64, 0x43, 0x70, 0x75, 0x55, 0x73, 0x65, 0x64, 0x12, + 0x4a, 0x0a, 0x13, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x64, 0x5f, 0x6d, 0x65, 0x6d, 0x6f, 0x72, + 0x79, 0x5f, 0x75, 0x73, 0x65, 0x64, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x61, + 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x54, 0x72, 0x65, + 0x6e, 0x64, 0x53, 0x65, 0x72, 0x69, 0x65, 0x73, 0x52, 0x11, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, + 0x64, 0x4d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x55, 0x73, 0x65, 0x64, 0x12, 0x52, 0x0a, 0x17, 0x6d, + 0x61, 0x6e, 0x61, 0x67, 0x65, 0x64, 0x5f, 0x63, 0x70, 0x75, 0x5f, 0x72, 0x65, 0x63, 0x6f, 0x6d, + 0x6d, 0x65, 0x6e, 0x64, 0x65, 0x64, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x61, + 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x54, 0x72, 0x65, + 0x6e, 0x64, 0x53, 0x65, 0x72, 0x69, 0x65, 0x73, 0x52, 0x15, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, + 0x64, 0x43, 0x70, 0x75, 0x52, 0x65, 0x63, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x64, 0x65, 0x64, 0x12, + 0x58, 0x0a, 0x1a, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x64, 0x5f, 0x6d, 0x65, 0x6d, 0x6f, 0x72, + 0x79, 0x5f, 0x72, 0x65, 0x63, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x64, 0x65, 0x64, 0x18, 0x0f, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x54, 0x72, 0x65, 0x6e, 0x64, 0x53, 0x65, 0x72, 0x69, 0x65, 0x73, 0x52, + 0x18, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x64, 0x4d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x52, 0x65, + 0x63, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x64, 0x65, 0x64, 0x22, 0xc3, 0x01, 0x0a, 0x1e, 0x4c, 0x69, 0x73, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x73, 0x50, 0x61, 0x67, 0x69, 0x6e, 0x61, 0x74, 0x65, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3b, 0x0a, 0x0e, 0x77, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x5f, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x18, 0x01, @@ -11223,96 +14209,133 @@ var file_api_v1_k8s_proto_rawDesc = []byte{ 0x72, 0x6f, 0x75, 0x70, 0x53, 0x65, 0x74, 0x12, 0x32, 0x0a, 0x0b, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x52, - 0x0a, 0x6e, 0x6f, 0x64, 0x65, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x22, 0xa7, 0x02, 0x0a, 0x1f, - 0x47, 0x65, 0x74, 0x4e, 0x6f, 0x64, 0x65, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x55, 0x74, 0x69, - 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, - 0x17, 0x0a, 0x07, 0x74, 0x65, 0x61, 0x6d, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x06, 0x74, 0x65, 0x61, 0x6d, 0x49, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x6c, 0x75, 0x73, - 0x74, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x6c, - 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, 0x64, 0x12, 0x3e, 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x72, 0x74, - 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, - 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, - 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x48, 0x00, 0x52, 0x09, 0x73, 0x74, 0x61, 0x72, 0x74, - 0x54, 0x69, 0x6d, 0x65, 0x88, 0x01, 0x01, 0x12, 0x3a, 0x0a, 0x08, 0x65, 0x6e, 0x64, 0x5f, 0x74, - 0x69, 0x6d, 0x65, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, - 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, - 0x73, 0x74, 0x61, 0x6d, 0x70, 0x48, 0x01, 0x52, 0x07, 0x65, 0x6e, 0x64, 0x54, 0x69, 0x6d, 0x65, - 0x88, 0x01, 0x01, 0x12, 0x24, 0x0a, 0x0b, 0x62, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x5f, 0x73, 0x69, - 0x7a, 0x65, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x05, 0x48, 0x02, 0x52, 0x0a, 0x62, 0x75, 0x63, 0x6b, - 0x65, 0x74, 0x53, 0x69, 0x7a, 0x65, 0x88, 0x01, 0x01, 0x42, 0x0d, 0x0a, 0x0b, 0x5f, 0x73, 0x74, - 0x61, 0x72, 0x74, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x42, 0x0b, 0x0a, 0x09, 0x5f, 0x65, 0x6e, 0x64, - 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x42, 0x0e, 0x0a, 0x0c, 0x5f, 0x62, 0x75, 0x63, 0x6b, 0x65, 0x74, - 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x22, 0xdd, 0x09, 0x0a, 0x20, 0x47, 0x65, 0x74, 0x4e, 0x6f, 0x64, + 0x0a, 0x6e, 0x6f, 0x64, 0x65, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x22, 0xf2, 0x01, 0x0a, 0x20, + 0x47, 0x65, 0x74, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x4e, 0x6f, 0x64, 0x65, 0x46, 0x69, + 0x6c, 0x74, 0x65, 0x72, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x12, 0x17, 0x0a, 0x07, 0x74, 0x65, 0x61, 0x6d, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x06, 0x74, 0x65, 0x61, 0x6d, 0x49, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x6c, 0x75, + 0x73, 0x74, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, + 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, 0x64, 0x12, 0x3e, 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x72, + 0x74, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, + 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, + 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x48, 0x00, 0x52, 0x09, 0x73, 0x74, 0x61, 0x72, + 0x74, 0x54, 0x69, 0x6d, 0x65, 0x88, 0x01, 0x01, 0x12, 0x3a, 0x0a, 0x08, 0x65, 0x6e, 0x64, 0x5f, + 0x74, 0x69, 0x6d, 0x65, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, + 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, + 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x48, 0x01, 0x52, 0x07, 0x65, 0x6e, 0x64, 0x54, 0x69, 0x6d, + 0x65, 0x88, 0x01, 0x01, 0x42, 0x0d, 0x0a, 0x0b, 0x5f, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x74, + 0x69, 0x6d, 0x65, 0x42, 0x0b, 0x0a, 0x09, 0x5f, 0x65, 0x6e, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, + 0x22, 0x83, 0x01, 0x0a, 0x0c, 0x4e, 0x6f, 0x64, 0x65, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x53, 0x65, + 0x74, 0x12, 0x38, 0x0a, 0x06, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, + 0x0b, 0x32, 0x20, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x4c, + 0x61, 0x62, 0x65, 0x6c, 0x53, 0x65, 0x74, 0x2e, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x45, 0x6e, + 0x74, 0x72, 0x79, 0x52, 0x06, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x1a, 0x39, 0x0a, 0x0b, 0x4c, + 0x61, 0x62, 0x65, 0x6c, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, + 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, + 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, + 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x4f, 0x0a, 0x0c, 0x4e, 0x6f, 0x64, 0x65, 0x4c, 0x61, + 0x62, 0x65, 0x6c, 0x52, 0x65, 0x66, 0x12, 0x17, 0x0a, 0x07, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x69, + 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x6e, 0x6f, 0x64, 0x65, 0x49, 0x64, 0x12, + 0x26, 0x0a, 0x0f, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x5f, 0x73, 0x65, 0x74, 0x5f, 0x69, 0x6e, 0x64, + 0x65, 0x78, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0d, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x53, + 0x65, 0x74, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x22, 0xaf, 0x01, 0x0a, 0x0e, 0x4e, 0x6f, 0x64, 0x65, + 0x50, 0x6f, 0x6f, 0x6c, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x12, 0x26, 0x0a, 0x0f, 0x6e, 0x6f, + 0x64, 0x65, 0x5f, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x0d, 0x6e, 0x6f, 0x64, 0x65, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4e, 0x61, + 0x6d, 0x65, 0x12, 0x3a, 0x0a, 0x06, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x18, 0x02, 0x20, 0x03, + 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4e, 0x6f, 0x64, 0x65, + 0x50, 0x6f, 0x6f, 0x6c, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x2e, 0x46, 0x69, 0x65, 0x6c, 0x64, + 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x06, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x1a, 0x39, + 0x0a, 0x0b, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, + 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, + 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, + 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0xbb, 0x01, 0x0a, 0x21, 0x47, 0x65, + 0x74, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x4e, 0x6f, 0x64, 0x65, 0x46, 0x69, 0x6c, 0x74, + 0x65, 0x72, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, + 0x33, 0x0a, 0x0a, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x5f, 0x73, 0x65, 0x74, 0x73, 0x18, 0x01, 0x20, + 0x03, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4e, 0x6f, 0x64, + 0x65, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x53, 0x65, 0x74, 0x52, 0x09, 0x6c, 0x61, 0x62, 0x65, 0x6c, + 0x53, 0x65, 0x74, 0x73, 0x12, 0x2a, 0x0a, 0x05, 0x6e, 0x6f, 0x64, 0x65, 0x73, 0x18, 0x02, 0x20, + 0x03, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4e, 0x6f, 0x64, + 0x65, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x52, 0x65, 0x66, 0x52, 0x05, 0x6e, 0x6f, 0x64, 0x65, 0x73, + 0x12, 0x35, 0x0a, 0x0a, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x70, 0x6f, 0x6f, 0x6c, 0x73, 0x18, 0x03, + 0x20, 0x03, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4e, 0x6f, + 0x64, 0x65, 0x50, 0x6f, 0x6f, 0x6c, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x52, 0x09, 0x6e, 0x6f, + 0x64, 0x65, 0x50, 0x6f, 0x6f, 0x6c, 0x73, 0x22, 0xa7, 0x02, 0x0a, 0x1f, 0x47, 0x65, 0x74, 0x4e, + 0x6f, 0x64, 0x65, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x55, 0x74, 0x69, 0x6c, 0x69, 0x7a, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x17, 0x0a, 0x07, 0x74, + 0x65, 0x61, 0x6d, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x74, 0x65, + 0x61, 0x6d, 0x49, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, + 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, + 0x72, 0x49, 0x64, 0x12, 0x3e, 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x74, 0x69, 0x6d, + 0x65, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, + 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, + 0x61, 0x6d, 0x70, 0x48, 0x00, 0x52, 0x09, 0x73, 0x74, 0x61, 0x72, 0x74, 0x54, 0x69, 0x6d, 0x65, + 0x88, 0x01, 0x01, 0x12, 0x3a, 0x0a, 0x08, 0x65, 0x6e, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, + 0x0c, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, + 0x70, 0x48, 0x01, 0x52, 0x07, 0x65, 0x6e, 0x64, 0x54, 0x69, 0x6d, 0x65, 0x88, 0x01, 0x01, 0x12, + 0x24, 0x0a, 0x0b, 0x62, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x0d, + 0x20, 0x01, 0x28, 0x05, 0x48, 0x02, 0x52, 0x0a, 0x62, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x53, 0x69, + 0x7a, 0x65, 0x88, 0x01, 0x01, 0x42, 0x0d, 0x0a, 0x0b, 0x5f, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, + 0x74, 0x69, 0x6d, 0x65, 0x42, 0x0b, 0x0a, 0x09, 0x5f, 0x65, 0x6e, 0x64, 0x5f, 0x74, 0x69, 0x6d, + 0x65, 0x42, 0x0e, 0x0a, 0x0c, 0x5f, 0x62, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x5f, 0x73, 0x69, 0x7a, + 0x65, 0x22, 0x8d, 0x0a, 0x0a, 0x20, 0x47, 0x65, 0x74, 0x4e, 0x6f, 0x64, 0x65, 0x47, 0x72, 0x6f, + 0x75, 0x70, 0x73, 0x55, 0x74, 0x69, 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x80, 0x01, 0x0a, 0x1a, 0x6e, 0x6f, 0x64, 0x65, 0x5f, + 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x74, 0x6f, 0x5f, 0x75, 0x74, 0x69, 0x6c, 0x5f, 0x6d, 0x65, + 0x74, 0x72, 0x69, 0x63, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x44, 0x2e, 0x61, 0x70, + 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x4e, 0x6f, 0x64, 0x65, 0x47, 0x72, 0x6f, 0x75, + 0x70, 0x73, 0x55, 0x74, 0x69, 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x54, + 0x6f, 0x55, 0x74, 0x69, 0x6c, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x45, 0x6e, 0x74, 0x72, + 0x79, 0x52, 0x16, 0x6e, 0x6f, 0x64, 0x65, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x54, 0x6f, 0x55, 0x74, + 0x69, 0x6c, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x12, 0x89, 0x01, 0x0a, 0x1d, 0x69, 0x6e, + 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x5f, 0x74, 0x6f, 0x5f, 0x75, + 0x74, 0x69, 0x6c, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, + 0x0b, 0x32, 0x47, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x4e, 0x6f, + 0x64, 0x65, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x55, 0x74, 0x69, 0x6c, 0x69, 0x7a, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x49, 0x6e, 0x73, 0x74, + 0x61, 0x6e, 0x63, 0x65, 0x54, 0x79, 0x70, 0x65, 0x54, 0x6f, 0x55, 0x74, 0x69, 0x6c, 0x4d, 0x65, + 0x74, 0x72, 0x69, 0x63, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x19, 0x69, 0x6e, 0x73, 0x74, + 0x61, 0x6e, 0x63, 0x65, 0x54, 0x79, 0x70, 0x65, 0x54, 0x6f, 0x55, 0x74, 0x69, 0x6c, 0x4d, 0x65, + 0x74, 0x72, 0x69, 0x63, 0x73, 0x12, 0x92, 0x01, 0x0a, 0x20, 0x72, 0x65, 0x73, 0x65, 0x72, 0x76, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x5f, 0x74, 0x6f, 0x5f, 0x75, 0x74, + 0x69, 0x6c, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x18, 0x0b, 0x20, 0x03, 0x28, 0x0b, + 0x32, 0x4a, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x4e, 0x6f, 0x64, 0x65, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x55, 0x74, 0x69, 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x80, 0x01, 0x0a, 0x1a, 0x6e, - 0x6f, 0x64, 0x65, 0x5f, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x74, 0x6f, 0x5f, 0x75, 0x74, 0x69, - 0x6c, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, + 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x52, 0x65, 0x73, 0x65, 0x72, + 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x79, 0x70, 0x65, 0x54, 0x6f, 0x55, 0x74, 0x69, 0x6c, + 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x1c, 0x72, 0x65, + 0x73, 0x65, 0x72, 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x79, 0x70, 0x65, 0x54, 0x6f, 0x55, + 0x74, 0x69, 0x6c, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x12, 0x80, 0x01, 0x0a, 0x1a, 0x6c, + 0x6f, 0x67, 0x69, 0x63, 0x61, 0x6c, 0x5f, 0x61, 0x7a, 0x5f, 0x74, 0x6f, 0x5f, 0x75, 0x74, 0x69, + 0x6c, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x18, 0x10, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x44, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x4e, 0x6f, 0x64, 0x65, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x55, 0x74, 0x69, 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x47, 0x72, - 0x6f, 0x75, 0x70, 0x54, 0x6f, 0x55, 0x74, 0x69, 0x6c, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, - 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x16, 0x6e, 0x6f, 0x64, 0x65, 0x47, 0x72, 0x6f, 0x75, 0x70, - 0x54, 0x6f, 0x55, 0x74, 0x69, 0x6c, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x12, 0x89, 0x01, - 0x0a, 0x1d, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x5f, - 0x74, 0x6f, 0x5f, 0x75, 0x74, 0x69, 0x6c, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x18, - 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x47, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, - 0x65, 0x74, 0x4e, 0x6f, 0x64, 0x65, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x55, 0x74, 0x69, 0x6c, - 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, - 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x54, 0x79, 0x70, 0x65, 0x54, 0x6f, 0x55, 0x74, - 0x69, 0x6c, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x19, - 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x54, 0x79, 0x70, 0x65, 0x54, 0x6f, 0x55, 0x74, - 0x69, 0x6c, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x12, 0x92, 0x01, 0x0a, 0x20, 0x72, 0x65, - 0x73, 0x65, 0x72, 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x5f, 0x74, - 0x6f, 0x5f, 0x75, 0x74, 0x69, 0x6c, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x18, 0x0b, - 0x20, 0x03, 0x28, 0x0b, 0x32, 0x4a, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, - 0x74, 0x4e, 0x6f, 0x64, 0x65, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x55, 0x74, 0x69, 0x6c, 0x69, - 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x52, - 0x65, 0x73, 0x65, 0x72, 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x79, 0x70, 0x65, 0x54, 0x6f, - 0x55, 0x74, 0x69, 0x6c, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, - 0x52, 0x1c, 0x72, 0x65, 0x73, 0x65, 0x72, 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x79, 0x70, - 0x65, 0x54, 0x6f, 0x55, 0x74, 0x69, 0x6c, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x12, 0x80, - 0x01, 0x0a, 0x1a, 0x6c, 0x6f, 0x67, 0x69, 0x63, 0x61, 0x6c, 0x5f, 0x61, 0x7a, 0x5f, 0x74, 0x6f, - 0x5f, 0x75, 0x74, 0x69, 0x6c, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x18, 0x10, 0x20, - 0x03, 0x28, 0x0b, 0x32, 0x44, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, - 0x4e, 0x6f, 0x64, 0x65, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x55, 0x74, 0x69, 0x6c, 0x69, 0x7a, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x4c, 0x6f, - 0x67, 0x69, 0x63, 0x61, 0x6c, 0x41, 0x7a, 0x54, 0x6f, 0x55, 0x74, 0x69, 0x6c, 0x4d, 0x65, 0x74, - 0x72, 0x69, 0x63, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x16, 0x6c, 0x6f, 0x67, 0x69, 0x63, - 0x61, 0x6c, 0x41, 0x7a, 0x54, 0x6f, 0x55, 0x74, 0x69, 0x6c, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, - 0x73, 0x1a, 0x5d, 0x0a, 0x14, 0x4c, 0x69, 0x73, 0x74, 0x4e, 0x6f, 0x64, 0x65, 0x47, 0x72, 0x6f, - 0x75, 0x70, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x12, 0x45, 0x0a, 0x12, 0x6e, 0x6f, 0x64, - 0x65, 0x5f, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x18, - 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4e, - 0x6f, 0x64, 0x65, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x52, 0x10, - 0x6e, 0x6f, 0x64, 0x65, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, - 0x1a, 0x88, 0x01, 0x0a, 0x1b, 0x4e, 0x6f, 0x64, 0x65, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x54, 0x6f, - 0x55, 0x74, 0x69, 0x6c, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, - 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, - 0x65, 0x79, 0x12, 0x53, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x3d, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x4e, 0x6f, - 0x64, 0x65, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x55, 0x74, 0x69, 0x6c, 0x69, 0x7a, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x4c, 0x69, 0x73, 0x74, - 0x4e, 0x6f, 0x64, 0x65, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, - 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x8b, 0x01, 0x0a, 0x1e, - 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x54, 0x79, 0x70, 0x65, 0x54, 0x6f, 0x55, 0x74, - 0x69, 0x6c, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, - 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, - 0x12, 0x53, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x3d, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x4e, 0x6f, 0x64, 0x65, - 0x47, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x55, 0x74, 0x69, 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x4e, 0x6f, - 0x64, 0x65, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x52, 0x05, - 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x8e, 0x01, 0x0a, 0x21, 0x52, 0x65, - 0x73, 0x65, 0x72, 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x79, 0x70, 0x65, 0x54, 0x6f, 0x55, - 0x74, 0x69, 0x6c, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, - 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, - 0x79, 0x12, 0x53, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x3d, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x4e, 0x6f, 0x64, - 0x65, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x55, 0x74, 0x69, 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x4e, - 0x6f, 0x64, 0x65, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x52, - 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x88, 0x01, 0x0a, 0x1b, 0x4c, - 0x6f, 0x67, 0x69, 0x63, 0x61, 0x6c, 0x41, 0x7a, 0x54, 0x6f, 0x55, 0x74, 0x69, 0x6c, 0x4d, 0x65, + 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x4c, 0x6f, 0x67, 0x69, 0x63, 0x61, + 0x6c, 0x41, 0x7a, 0x54, 0x6f, 0x55, 0x74, 0x69, 0x6c, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, + 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x16, 0x6c, 0x6f, 0x67, 0x69, 0x63, 0x61, 0x6c, 0x41, 0x7a, + 0x54, 0x6f, 0x55, 0x74, 0x69, 0x6c, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x12, 0x2e, 0x0a, + 0x13, 0x62, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x5f, 0x73, 0x65, 0x63, + 0x6f, 0x6e, 0x64, 0x73, 0x18, 0x15, 0x20, 0x01, 0x28, 0x05, 0x52, 0x11, 0x62, 0x75, 0x63, 0x6b, + 0x65, 0x74, 0x53, 0x69, 0x7a, 0x65, 0x53, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x1a, 0x5d, 0x0a, + 0x14, 0x4c, 0x69, 0x73, 0x74, 0x4e, 0x6f, 0x64, 0x65, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4d, 0x65, + 0x74, 0x72, 0x69, 0x63, 0x73, 0x12, 0x45, 0x0a, 0x12, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x67, 0x72, + 0x6f, 0x75, 0x70, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, + 0x0b, 0x32, 0x17, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x47, + 0x72, 0x6f, 0x75, 0x70, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x52, 0x10, 0x6e, 0x6f, 0x64, 0x65, + 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x1a, 0x88, 0x01, 0x0a, + 0x1b, 0x4e, 0x6f, 0x64, 0x65, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x54, 0x6f, 0x55, 0x74, 0x69, 0x6c, + 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, + 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x53, + 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x3d, 0x2e, + 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x4e, 0x6f, 0x64, 0x65, 0x47, 0x72, + 0x6f, 0x75, 0x70, 0x73, 0x55, 0x74, 0x69, 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x4e, 0x6f, 0x64, 0x65, + 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x52, 0x05, 0x76, 0x61, + 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x8b, 0x01, 0x0a, 0x1e, 0x49, 0x6e, 0x73, 0x74, + 0x61, 0x6e, 0x63, 0x65, 0x54, 0x79, 0x70, 0x65, 0x54, 0x6f, 0x55, 0x74, 0x69, 0x6c, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x53, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x3d, 0x2e, 0x61, 0x70, @@ -11320,1057 +14343,1411 @@ var file_api_v1_k8s_proto_rawDesc = []byte{ 0x70, 0x73, 0x55, 0x74, 0x69, 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x4e, 0x6f, 0x64, 0x65, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, - 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0xeb, 0x06, 0x0a, 0x0f, 0x4e, 0x6f, 0x64, 0x65, 0x47, 0x72, - 0x6f, 0x75, 0x70, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x12, 0x1d, 0x0a, 0x0a, 0x6e, 0x6f, 0x64, - 0x65, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x09, 0x6e, - 0x6f, 0x64, 0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x39, 0x0a, 0x19, 0x6d, 0x61, 0x78, 0x5f, - 0x63, 0x6f, 0x6e, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x6e, 0x6f, 0x64, 0x65, 0x5f, - 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x16, 0x6d, 0x61, 0x78, - 0x43, 0x6f, 0x6e, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x4e, 0x6f, 0x64, 0x65, 0x43, 0x6f, - 0x75, 0x6e, 0x74, 0x12, 0x21, 0x0a, 0x0c, 0x63, 0x61, 0x70, 0x61, 0x63, 0x69, 0x74, 0x79, 0x5f, - 0x63, 0x70, 0x75, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0b, 0x63, 0x61, 0x70, 0x61, 0x63, - 0x69, 0x74, 0x79, 0x43, 0x70, 0x75, 0x12, 0x27, 0x0a, 0x0f, 0x63, 0x61, 0x70, 0x61, 0x63, 0x69, - 0x74, 0x79, 0x5f, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x03, 0x52, - 0x0e, 0x63, 0x61, 0x70, 0x61, 0x63, 0x69, 0x74, 0x79, 0x4d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x12, - 0x2a, 0x0a, 0x11, 0x63, 0x61, 0x70, 0x61, 0x63, 0x69, 0x74, 0x79, 0x5f, 0x67, 0x70, 0x75, 0x5f, - 0x76, 0x72, 0x61, 0x6d, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0f, 0x63, 0x61, 0x70, 0x61, - 0x63, 0x69, 0x74, 0x79, 0x47, 0x70, 0x75, 0x56, 0x72, 0x61, 0x6d, 0x12, 0x30, 0x0a, 0x14, 0x63, - 0x61, 0x70, 0x61, 0x63, 0x69, 0x74, 0x79, 0x5f, 0x67, 0x70, 0x75, 0x5f, 0x64, 0x65, 0x76, 0x69, - 0x63, 0x65, 0x73, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x03, 0x52, 0x12, 0x63, 0x61, 0x70, 0x61, 0x63, - 0x69, 0x74, 0x79, 0x47, 0x70, 0x75, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x73, 0x12, 0x20, 0x0a, - 0x0c, 0x61, 0x67, 0x67, 0x72, 0x5f, 0x63, 0x70, 0x75, 0x5f, 0x72, 0x65, 0x71, 0x18, 0x15, 0x20, - 0x01, 0x28, 0x03, 0x52, 0x0a, 0x61, 0x67, 0x67, 0x72, 0x43, 0x70, 0x75, 0x52, 0x65, 0x71, 0x12, - 0x26, 0x0a, 0x0f, 0x61, 0x67, 0x67, 0x72, 0x5f, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x5f, 0x72, - 0x65, 0x71, 0x18, 0x16, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0d, 0x61, 0x67, 0x67, 0x72, 0x4d, 0x65, - 0x6d, 0x6f, 0x72, 0x79, 0x52, 0x65, 0x71, 0x12, 0x20, 0x0a, 0x0c, 0x61, 0x67, 0x67, 0x72, 0x5f, - 0x67, 0x70, 0x75, 0x5f, 0x72, 0x65, 0x71, 0x18, 0x17, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0a, 0x61, - 0x67, 0x67, 0x72, 0x47, 0x70, 0x75, 0x52, 0x65, 0x71, 0x12, 0x2b, 0x0a, 0x12, 0x75, 0x74, 0x69, - 0x6c, 0x5f, 0x63, 0x70, 0x75, 0x5f, 0x61, 0x76, 0x67, 0x5f, 0x75, 0x73, 0x61, 0x67, 0x65, 0x18, - 0x1f, 0x20, 0x01, 0x28, 0x02, 0x52, 0x0f, 0x75, 0x74, 0x69, 0x6c, 0x43, 0x70, 0x75, 0x41, 0x76, - 0x67, 0x55, 0x73, 0x61, 0x67, 0x65, 0x12, 0x31, 0x0a, 0x15, 0x75, 0x74, 0x69, 0x6c, 0x5f, 0x6d, - 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x5f, 0x61, 0x76, 0x67, 0x5f, 0x75, 0x73, 0x61, 0x67, 0x65, 0x18, - 0x20, 0x20, 0x01, 0x28, 0x02, 0x52, 0x12, 0x75, 0x74, 0x69, 0x6c, 0x4d, 0x65, 0x6d, 0x6f, 0x72, - 0x79, 0x41, 0x76, 0x67, 0x55, 0x73, 0x61, 0x67, 0x65, 0x12, 0x34, 0x0a, 0x17, 0x75, 0x74, 0x69, - 0x6c, 0x5f, 0x67, 0x70, 0x75, 0x5f, 0x76, 0x72, 0x61, 0x6d, 0x5f, 0x61, 0x76, 0x67, 0x5f, 0x75, - 0x73, 0x61, 0x67, 0x65, 0x18, 0x21, 0x20, 0x01, 0x28, 0x02, 0x52, 0x13, 0x75, 0x74, 0x69, 0x6c, - 0x47, 0x70, 0x75, 0x56, 0x72, 0x61, 0x6d, 0x41, 0x76, 0x67, 0x55, 0x73, 0x61, 0x67, 0x65, 0x12, - 0x26, 0x0a, 0x0f, 0x75, 0x74, 0x69, 0x6c, 0x5f, 0x67, 0x70, 0x75, 0x5f, 0x64, 0x65, 0x76, 0x69, - 0x63, 0x65, 0x18, 0x22, 0x20, 0x01, 0x28, 0x02, 0x52, 0x0d, 0x75, 0x74, 0x69, 0x6c, 0x47, 0x70, - 0x75, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x12, 0x33, 0x0a, 0x0b, 0x68, 0x6f, 0x75, 0x72, 0x6c, - 0x79, 0x5f, 0x63, 0x6f, 0x73, 0x74, 0x18, 0x32, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x67, - 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x2e, 0x4d, 0x6f, 0x6e, 0x65, 0x79, - 0x52, 0x0a, 0x68, 0x6f, 0x75, 0x72, 0x6c, 0x79, 0x43, 0x6f, 0x73, 0x74, 0x12, 0x3a, 0x0a, 0x0f, - 0x68, 0x6f, 0x75, 0x72, 0x6c, 0x79, 0x5f, 0x63, 0x70, 0x75, 0x5f, 0x63, 0x6f, 0x73, 0x74, 0x18, - 0x33, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x74, + 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x8e, 0x01, 0x0a, 0x21, 0x52, 0x65, 0x73, 0x65, 0x72, 0x76, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x79, 0x70, 0x65, 0x54, 0x6f, 0x55, 0x74, 0x69, 0x6c, 0x4d, + 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, + 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x53, 0x0a, + 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x3d, 0x2e, 0x61, + 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x4e, 0x6f, 0x64, 0x65, 0x47, 0x72, 0x6f, + 0x75, 0x70, 0x73, 0x55, 0x74, 0x69, 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x4e, 0x6f, 0x64, 0x65, 0x47, + 0x72, 0x6f, 0x75, 0x70, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x52, 0x05, 0x76, 0x61, 0x6c, + 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x88, 0x01, 0x0a, 0x1b, 0x4c, 0x6f, 0x67, 0x69, 0x63, + 0x61, 0x6c, 0x41, 0x7a, 0x54, 0x6f, 0x55, 0x74, 0x69, 0x6c, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, + 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x53, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, + 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x3d, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, + 0x2e, 0x47, 0x65, 0x74, 0x4e, 0x6f, 0x64, 0x65, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x55, 0x74, + 0x69, 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x4e, 0x6f, 0x64, 0x65, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4d, + 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, + 0x01, 0x22, 0xc2, 0x03, 0x0a, 0x1e, 0x47, 0x65, 0x74, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, + 0x4e, 0x6f, 0x64, 0x65, 0x49, 0x6e, 0x76, 0x65, 0x6e, 0x74, 0x6f, 0x72, 0x79, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x12, 0x17, 0x0a, 0x07, 0x74, 0x65, 0x61, 0x6d, 0x5f, 0x69, 0x64, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x74, 0x65, 0x61, 0x6d, 0x49, 0x64, 0x12, 0x1d, 0x0a, + 0x0a, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x09, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, 0x64, 0x12, 0x30, 0x0a, 0x05, + 0x73, 0x63, 0x6f, 0x70, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1a, 0x2e, 0x61, 0x70, + 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x49, 0x6e, 0x76, 0x65, 0x6e, 0x74, 0x6f, + 0x72, 0x79, 0x53, 0x63, 0x6f, 0x70, 0x65, 0x52, 0x05, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x12, 0x26, + 0x0a, 0x0f, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x6e, 0x61, 0x6d, + 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x6e, 0x6f, 0x64, 0x65, 0x47, 0x72, 0x6f, + 0x75, 0x70, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x70, 0x61, 0x67, 0x65, 0x5f, 0x73, + 0x69, 0x7a, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x05, 0x52, 0x08, 0x70, 0x61, 0x67, 0x65, 0x53, + 0x69, 0x7a, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x70, 0x61, 0x67, 0x65, 0x5f, 0x74, 0x6f, 0x6b, 0x65, + 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x70, 0x61, 0x67, 0x65, 0x54, 0x6f, 0x6b, + 0x65, 0x6e, 0x12, 0x29, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x65, 0x73, 0x18, 0x07, 0x20, 0x03, + 0x28, 0x0e, 0x32, 0x11, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4e, 0x6f, 0x64, 0x65, + 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x65, 0x73, 0x12, 0x2c, 0x0a, + 0x12, 0x75, 0x6e, 0x6e, 0x61, 0x6d, 0x65, 0x64, 0x5f, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x67, 0x72, + 0x6f, 0x75, 0x70, 0x18, 0x08, 0x20, 0x01, 0x28, 0x08, 0x52, 0x10, 0x75, 0x6e, 0x6e, 0x61, 0x6d, + 0x65, 0x64, 0x4e, 0x6f, 0x64, 0x65, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x12, 0x2c, 0x0a, 0x12, 0x6e, + 0x6f, 0x64, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, + 0x73, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x6e, 0x6f, 0x64, 0x65, 0x4e, 0x61, 0x6d, + 0x65, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x73, 0x12, 0x25, 0x0a, 0x0e, 0x69, 0x6e, 0x73, + 0x74, 0x61, 0x6e, 0x63, 0x65, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x73, 0x18, 0x0a, 0x20, 0x03, 0x28, + 0x09, 0x52, 0x0d, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x54, 0x79, 0x70, 0x65, 0x73, + 0x12, 0x24, 0x0a, 0x0e, 0x6e, 0x6f, 0x64, 0x65, 0x73, 0x5f, 0x70, 0x65, 0x72, 0x5f, 0x70, 0x6f, + 0x6f, 0x6c, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0c, 0x6e, 0x6f, 0x64, 0x65, 0x73, 0x50, + 0x65, 0x72, 0x50, 0x6f, 0x6f, 0x6c, 0x22, 0xba, 0x02, 0x0a, 0x1f, 0x47, 0x65, 0x74, 0x43, 0x6c, + 0x75, 0x73, 0x74, 0x65, 0x72, 0x4e, 0x6f, 0x64, 0x65, 0x49, 0x6e, 0x76, 0x65, 0x6e, 0x74, 0x6f, + 0x72, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2f, 0x0a, 0x05, 0x61, 0x73, + 0x5f, 0x6f, 0x66, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, + 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, + 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x04, 0x61, 0x73, 0x4f, 0x66, 0x12, 0x33, 0x0a, 0x06, 0x74, + 0x6f, 0x74, 0x61, 0x6c, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x61, 0x70, + 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x49, 0x6e, 0x76, 0x65, 0x6e, 0x74, 0x6f, + 0x72, 0x79, 0x52, 0x6f, 0x6c, 0x6c, 0x75, 0x70, 0x52, 0x06, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x73, + 0x12, 0x2f, 0x0a, 0x05, 0x70, 0x6f, 0x6f, 0x6c, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, + 0x19, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x50, 0x6f, 0x6f, + 0x6c, 0x49, 0x6e, 0x76, 0x65, 0x6e, 0x74, 0x6f, 0x72, 0x79, 0x52, 0x05, 0x70, 0x6f, 0x6f, 0x6c, + 0x73, 0x12, 0x2e, 0x0a, 0x05, 0x6e, 0x6f, 0x64, 0x65, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, + 0x32, 0x18, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x49, 0x6e, + 0x76, 0x65, 0x6e, 0x74, 0x6f, 0x72, 0x79, 0x52, 0x6f, 0x77, 0x52, 0x05, 0x6e, 0x6f, 0x64, 0x65, + 0x73, 0x12, 0x26, 0x0a, 0x0f, 0x6e, 0x65, 0x78, 0x74, 0x5f, 0x70, 0x61, 0x67, 0x65, 0x5f, 0x74, + 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x6e, 0x65, 0x78, 0x74, + 0x50, 0x61, 0x67, 0x65, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x28, 0x0a, 0x10, 0x74, 0x6f, 0x74, + 0x61, 0x6c, 0x5f, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x06, 0x20, + 0x01, 0x28, 0x05, 0x52, 0x0e, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x4e, 0x6f, 0x64, 0x65, 0x43, 0x6f, + 0x75, 0x6e, 0x74, 0x22, 0x9d, 0x05, 0x0a, 0x13, 0x4e, 0x6f, 0x64, 0x65, 0x49, 0x6e, 0x76, 0x65, + 0x6e, 0x74, 0x6f, 0x72, 0x79, 0x52, 0x6f, 0x6c, 0x6c, 0x75, 0x70, 0x12, 0x1d, 0x0a, 0x0a, 0x6e, + 0x6f, 0x64, 0x65, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, + 0x09, 0x6e, 0x6f, 0x64, 0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x2d, 0x0a, 0x09, 0x6e, 0x6f, + 0x64, 0x65, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, + 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x52, + 0x08, 0x6e, 0x6f, 0x64, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x2f, 0x0a, 0x06, 0x73, 0x74, 0x61, + 0x74, 0x65, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x61, 0x70, 0x69, 0x2e, + 0x76, 0x31, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x53, 0x74, 0x61, 0x74, 0x65, 0x43, 0x6f, 0x75, 0x6e, + 0x74, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x65, 0x73, 0x12, 0x24, 0x0a, 0x0e, 0x67, 0x70, + 0x75, 0x5f, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x04, 0x20, 0x01, + 0x28, 0x05, 0x52, 0x0c, 0x67, 0x70, 0x75, 0x4e, 0x6f, 0x64, 0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74, + 0x12, 0x2d, 0x0a, 0x13, 0x6d, 0x6f, 0x6e, 0x65, 0x79, 0x5f, 0x63, 0x6f, 0x73, 0x74, 0x5f, 0x70, + 0x65, 0x72, 0x5f, 0x68, 0x6f, 0x75, 0x72, 0x18, 0x05, 0x20, 0x01, 0x28, 0x01, 0x52, 0x10, 0x6d, + 0x6f, 0x6e, 0x65, 0x79, 0x43, 0x6f, 0x73, 0x74, 0x50, 0x65, 0x72, 0x48, 0x6f, 0x75, 0x72, 0x12, + 0x36, 0x0a, 0x17, 0x63, 0x70, 0x75, 0x5f, 0x63, 0x61, 0x70, 0x61, 0x63, 0x69, 0x74, 0x79, 0x5f, + 0x6d, 0x69, 0x6c, 0x6c, 0x69, 0x63, 0x6f, 0x72, 0x65, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x01, + 0x52, 0x15, 0x63, 0x70, 0x75, 0x43, 0x61, 0x70, 0x61, 0x63, 0x69, 0x74, 0x79, 0x4d, 0x69, 0x6c, + 0x6c, 0x69, 0x63, 0x6f, 0x72, 0x65, 0x73, 0x12, 0x32, 0x0a, 0x15, 0x6d, 0x65, 0x6d, 0x6f, 0x72, + 0x79, 0x5f, 0x63, 0x61, 0x70, 0x61, 0x63, 0x69, 0x74, 0x79, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, + 0x18, 0x07, 0x20, 0x01, 0x28, 0x01, 0x52, 0x13, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x43, 0x61, + 0x70, 0x61, 0x63, 0x69, 0x74, 0x79, 0x42, 0x79, 0x74, 0x65, 0x73, 0x12, 0x21, 0x0a, 0x0c, 0x67, + 0x70, 0x75, 0x5f, 0x63, 0x61, 0x70, 0x61, 0x63, 0x69, 0x74, 0x79, 0x18, 0x08, 0x20, 0x01, 0x28, + 0x01, 0x52, 0x0b, 0x67, 0x70, 0x75, 0x43, 0x61, 0x70, 0x61, 0x63, 0x69, 0x74, 0x79, 0x12, 0x38, + 0x0a, 0x18, 0x63, 0x70, 0x75, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x65, 0x64, 0x5f, + 0x6d, 0x69, 0x6c, 0x6c, 0x69, 0x63, 0x6f, 0x72, 0x65, 0x73, 0x18, 0x09, 0x20, 0x01, 0x28, 0x01, + 0x52, 0x16, 0x63, 0x70, 0x75, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x65, 0x64, 0x4d, 0x69, + 0x6c, 0x6c, 0x69, 0x63, 0x6f, 0x72, 0x65, 0x73, 0x12, 0x34, 0x0a, 0x16, 0x6d, 0x65, 0x6d, 0x6f, + 0x72, 0x79, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x65, 0x64, 0x5f, 0x62, 0x79, 0x74, + 0x65, 0x73, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x01, 0x52, 0x14, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x65, 0x64, 0x42, 0x79, 0x74, 0x65, 0x73, 0x12, 0x23, + 0x0a, 0x0d, 0x67, 0x70, 0x75, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x65, 0x64, 0x18, + 0x0b, 0x20, 0x01, 0x28, 0x01, 0x52, 0x0c, 0x67, 0x70, 0x75, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x65, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x70, 0x6f, 0x6f, 0x6c, 0x5f, 0x63, 0x6f, 0x75, 0x6e, + 0x74, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x05, 0x52, 0x09, 0x70, 0x6f, 0x6f, 0x6c, 0x43, 0x6f, 0x75, + 0x6e, 0x74, 0x12, 0x2e, 0x0a, 0x13, 0x66, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x5f, 0x6e, + 0x6f, 0x64, 0x65, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x05, 0x52, + 0x11, 0x66, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x4e, 0x6f, 0x64, 0x65, 0x43, 0x6f, 0x75, + 0x6e, 0x74, 0x12, 0x3f, 0x0a, 0x1c, 0x66, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x5f, 0x65, + 0x6c, 0x69, 0x67, 0x69, 0x62, 0x6c, 0x65, 0x5f, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x63, 0x6f, 0x75, + 0x6e, 0x74, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x05, 0x52, 0x19, 0x66, 0x61, 0x6c, 0x6c, 0x62, 0x61, + 0x63, 0x6b, 0x45, 0x6c, 0x69, 0x67, 0x69, 0x62, 0x6c, 0x65, 0x4e, 0x6f, 0x64, 0x65, 0x43, 0x6f, + 0x75, 0x6e, 0x74, 0x22, 0x98, 0x01, 0x0a, 0x0f, 0x4e, 0x6f, 0x64, 0x65, 0x53, 0x74, 0x61, 0x74, + 0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x72, 0x65, 0x61, 0x64, 0x79, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x72, 0x65, 0x61, 0x64, 0x79, 0x12, 0x1a, 0x0a, + 0x08, 0x63, 0x6f, 0x72, 0x64, 0x6f, 0x6e, 0x65, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, + 0x08, 0x63, 0x6f, 0x72, 0x64, 0x6f, 0x6e, 0x65, 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x64, 0x72, 0x61, + 0x69, 0x6e, 0x69, 0x6e, 0x67, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x08, 0x64, 0x72, 0x61, + 0x69, 0x6e, 0x69, 0x6e, 0x67, 0x12, 0x1b, 0x0a, 0x09, 0x6e, 0x6f, 0x74, 0x5f, 0x72, 0x65, 0x61, + 0x64, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x52, 0x08, 0x6e, 0x6f, 0x74, 0x52, 0x65, 0x61, + 0x64, 0x79, 0x12, 0x1a, 0x0a, 0x08, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x69, 0x6e, 0x67, 0x18, 0x05, + 0x20, 0x01, 0x28, 0x05, 0x52, 0x08, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x69, 0x6e, 0x67, 0x22, 0xb6, + 0x02, 0x0a, 0x11, 0x4e, 0x6f, 0x64, 0x65, 0x50, 0x6f, 0x6f, 0x6c, 0x49, 0x6e, 0x76, 0x65, 0x6e, + 0x74, 0x6f, 0x72, 0x79, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x3e, 0x0a, 0x0b, 0x70, 0x72, 0x6f, 0x76, + 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1c, 0x2e, + 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x47, 0x72, 0x6f, 0x75, 0x70, + 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x52, 0x0b, 0x70, 0x72, 0x6f, + 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x12, 0x33, 0x0a, 0x06, 0x72, 0x6f, 0x6c, 0x6c, + 0x75, 0x70, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, + 0x31, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x49, 0x6e, 0x76, 0x65, 0x6e, 0x74, 0x6f, 0x72, 0x79, 0x52, + 0x6f, 0x6c, 0x6c, 0x75, 0x70, 0x52, 0x06, 0x72, 0x6f, 0x6c, 0x6c, 0x75, 0x70, 0x12, 0x40, 0x0a, + 0x0e, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x73, 0x18, + 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x49, + 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x54, 0x79, 0x70, 0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74, + 0x52, 0x0d, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x54, 0x79, 0x70, 0x65, 0x73, 0x12, + 0x2e, 0x0a, 0x05, 0x6e, 0x6f, 0x64, 0x65, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x18, + 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x49, 0x6e, 0x76, 0x65, + 0x6e, 0x74, 0x6f, 0x72, 0x79, 0x52, 0x6f, 0x77, 0x52, 0x05, 0x6e, 0x6f, 0x64, 0x65, 0x73, 0x12, + 0x26, 0x0a, 0x0f, 0x6e, 0x65, 0x78, 0x74, 0x5f, 0x70, 0x61, 0x67, 0x65, 0x5f, 0x74, 0x6f, 0x6b, + 0x65, 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x6e, 0x65, 0x78, 0x74, 0x50, 0x61, + 0x67, 0x65, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x22, 0x4e, 0x0a, 0x11, 0x49, 0x6e, 0x73, 0x74, 0x61, + 0x6e, 0x63, 0x65, 0x54, 0x79, 0x70, 0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x23, 0x0a, 0x0d, + 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x0c, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x54, 0x79, 0x70, + 0x65, 0x12, 0x14, 0x0a, 0x05, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, + 0x52, 0x05, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x22, 0xfd, 0x04, 0x0a, 0x10, 0x4e, 0x6f, 0x64, 0x65, + 0x49, 0x6e, 0x76, 0x65, 0x6e, 0x74, 0x6f, 0x72, 0x79, 0x52, 0x6f, 0x77, 0x12, 0x17, 0x0a, 0x07, + 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x6e, + 0x6f, 0x64, 0x65, 0x49, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x26, 0x0a, 0x0f, 0x6e, 0x6f, 0x64, + 0x65, 0x5f, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x0d, 0x6e, 0x6f, 0x64, 0x65, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4e, 0x61, 0x6d, + 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x5f, 0x74, 0x79, + 0x70, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, + 0x63, 0x65, 0x54, 0x79, 0x70, 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x63, 0x61, 0x70, 0x61, 0x63, 0x69, + 0x74, 0x79, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x63, + 0x61, 0x70, 0x61, 0x63, 0x69, 0x74, 0x79, 0x54, 0x79, 0x70, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x7a, + 0x6f, 0x6e, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x7a, 0x6f, 0x6e, 0x65, 0x12, + 0x27, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x11, + 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x53, 0x74, 0x61, 0x74, + 0x65, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x72, 0x65, 0x61, + 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x08, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x63, 0x72, + 0x65, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x12, 0x2d, 0x0a, 0x13, 0x6d, 0x6f, 0x6e, 0x65, 0x79, + 0x5f, 0x63, 0x6f, 0x73, 0x74, 0x5f, 0x70, 0x65, 0x72, 0x5f, 0x68, 0x6f, 0x75, 0x72, 0x18, 0x09, + 0x20, 0x01, 0x28, 0x01, 0x52, 0x10, 0x6d, 0x6f, 0x6e, 0x65, 0x79, 0x43, 0x6f, 0x73, 0x74, 0x50, + 0x65, 0x72, 0x48, 0x6f, 0x75, 0x72, 0x12, 0x36, 0x0a, 0x17, 0x63, 0x70, 0x75, 0x5f, 0x63, 0x61, + 0x70, 0x61, 0x63, 0x69, 0x74, 0x79, 0x5f, 0x6d, 0x69, 0x6c, 0x6c, 0x69, 0x63, 0x6f, 0x72, 0x65, + 0x73, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x01, 0x52, 0x15, 0x63, 0x70, 0x75, 0x43, 0x61, 0x70, 0x61, + 0x63, 0x69, 0x74, 0x79, 0x4d, 0x69, 0x6c, 0x6c, 0x69, 0x63, 0x6f, 0x72, 0x65, 0x73, 0x12, 0x32, + 0x0a, 0x15, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x5f, 0x63, 0x61, 0x70, 0x61, 0x63, 0x69, 0x74, + 0x79, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x01, 0x52, 0x13, 0x6d, + 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x43, 0x61, 0x70, 0x61, 0x63, 0x69, 0x74, 0x79, 0x42, 0x79, 0x74, + 0x65, 0x73, 0x12, 0x21, 0x0a, 0x0c, 0x67, 0x70, 0x75, 0x5f, 0x63, 0x61, 0x70, 0x61, 0x63, 0x69, + 0x74, 0x79, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x01, 0x52, 0x0b, 0x67, 0x70, 0x75, 0x43, 0x61, 0x70, + 0x61, 0x63, 0x69, 0x74, 0x79, 0x12, 0x38, 0x0a, 0x18, 0x63, 0x70, 0x75, 0x5f, 0x72, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x65, 0x64, 0x5f, 0x6d, 0x69, 0x6c, 0x6c, 0x69, 0x63, 0x6f, 0x72, 0x65, + 0x73, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x01, 0x52, 0x16, 0x63, 0x70, 0x75, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x65, 0x64, 0x4d, 0x69, 0x6c, 0x6c, 0x69, 0x63, 0x6f, 0x72, 0x65, 0x73, 0x12, + 0x34, 0x0a, 0x16, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x65, 0x64, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x01, 0x52, + 0x14, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x65, 0x64, + 0x42, 0x79, 0x74, 0x65, 0x73, 0x12, 0x23, 0x0a, 0x0d, 0x67, 0x70, 0x75, 0x5f, 0x72, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x65, 0x64, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x01, 0x52, 0x0c, 0x67, 0x70, + 0x75, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x65, 0x64, 0x12, 0x1b, 0x0a, 0x09, 0x70, 0x6f, + 0x64, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x10, 0x20, 0x01, 0x28, 0x05, 0x52, 0x08, 0x70, + 0x6f, 0x64, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x22, 0xeb, 0x06, 0x0a, 0x0f, 0x4e, 0x6f, 0x64, 0x65, + 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x12, 0x1d, 0x0a, 0x0a, 0x6e, + 0x6f, 0x64, 0x65, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, + 0x09, 0x6e, 0x6f, 0x64, 0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x39, 0x0a, 0x19, 0x6d, 0x61, + 0x78, 0x5f, 0x63, 0x6f, 0x6e, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x6e, 0x6f, 0x64, + 0x65, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x16, 0x6d, + 0x61, 0x78, 0x43, 0x6f, 0x6e, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x4e, 0x6f, 0x64, 0x65, + 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x21, 0x0a, 0x0c, 0x63, 0x61, 0x70, 0x61, 0x63, 0x69, 0x74, + 0x79, 0x5f, 0x63, 0x70, 0x75, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0b, 0x63, 0x61, 0x70, + 0x61, 0x63, 0x69, 0x74, 0x79, 0x43, 0x70, 0x75, 0x12, 0x27, 0x0a, 0x0f, 0x63, 0x61, 0x70, 0x61, + 0x63, 0x69, 0x74, 0x79, 0x5f, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x18, 0x0c, 0x20, 0x01, 0x28, + 0x03, 0x52, 0x0e, 0x63, 0x61, 0x70, 0x61, 0x63, 0x69, 0x74, 0x79, 0x4d, 0x65, 0x6d, 0x6f, 0x72, + 0x79, 0x12, 0x2a, 0x0a, 0x11, 0x63, 0x61, 0x70, 0x61, 0x63, 0x69, 0x74, 0x79, 0x5f, 0x67, 0x70, + 0x75, 0x5f, 0x76, 0x72, 0x61, 0x6d, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0f, 0x63, 0x61, + 0x70, 0x61, 0x63, 0x69, 0x74, 0x79, 0x47, 0x70, 0x75, 0x56, 0x72, 0x61, 0x6d, 0x12, 0x30, 0x0a, + 0x14, 0x63, 0x61, 0x70, 0x61, 0x63, 0x69, 0x74, 0x79, 0x5f, 0x67, 0x70, 0x75, 0x5f, 0x64, 0x65, + 0x76, 0x69, 0x63, 0x65, 0x73, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x03, 0x52, 0x12, 0x63, 0x61, 0x70, + 0x61, 0x63, 0x69, 0x74, 0x79, 0x47, 0x70, 0x75, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x73, 0x12, + 0x20, 0x0a, 0x0c, 0x61, 0x67, 0x67, 0x72, 0x5f, 0x63, 0x70, 0x75, 0x5f, 0x72, 0x65, 0x71, 0x18, + 0x15, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0a, 0x61, 0x67, 0x67, 0x72, 0x43, 0x70, 0x75, 0x52, 0x65, + 0x71, 0x12, 0x26, 0x0a, 0x0f, 0x61, 0x67, 0x67, 0x72, 0x5f, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, + 0x5f, 0x72, 0x65, 0x71, 0x18, 0x16, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0d, 0x61, 0x67, 0x67, 0x72, + 0x4d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x52, 0x65, 0x71, 0x12, 0x20, 0x0a, 0x0c, 0x61, 0x67, 0x67, + 0x72, 0x5f, 0x67, 0x70, 0x75, 0x5f, 0x72, 0x65, 0x71, 0x18, 0x17, 0x20, 0x01, 0x28, 0x03, 0x52, + 0x0a, 0x61, 0x67, 0x67, 0x72, 0x47, 0x70, 0x75, 0x52, 0x65, 0x71, 0x12, 0x2b, 0x0a, 0x12, 0x75, + 0x74, 0x69, 0x6c, 0x5f, 0x63, 0x70, 0x75, 0x5f, 0x61, 0x76, 0x67, 0x5f, 0x75, 0x73, 0x61, 0x67, + 0x65, 0x18, 0x1f, 0x20, 0x01, 0x28, 0x02, 0x52, 0x0f, 0x75, 0x74, 0x69, 0x6c, 0x43, 0x70, 0x75, + 0x41, 0x76, 0x67, 0x55, 0x73, 0x61, 0x67, 0x65, 0x12, 0x31, 0x0a, 0x15, 0x75, 0x74, 0x69, 0x6c, + 0x5f, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x5f, 0x61, 0x76, 0x67, 0x5f, 0x75, 0x73, 0x61, 0x67, + 0x65, 0x18, 0x20, 0x20, 0x01, 0x28, 0x02, 0x52, 0x12, 0x75, 0x74, 0x69, 0x6c, 0x4d, 0x65, 0x6d, + 0x6f, 0x72, 0x79, 0x41, 0x76, 0x67, 0x55, 0x73, 0x61, 0x67, 0x65, 0x12, 0x34, 0x0a, 0x17, 0x75, + 0x74, 0x69, 0x6c, 0x5f, 0x67, 0x70, 0x75, 0x5f, 0x76, 0x72, 0x61, 0x6d, 0x5f, 0x61, 0x76, 0x67, + 0x5f, 0x75, 0x73, 0x61, 0x67, 0x65, 0x18, 0x21, 0x20, 0x01, 0x28, 0x02, 0x52, 0x13, 0x75, 0x74, + 0x69, 0x6c, 0x47, 0x70, 0x75, 0x56, 0x72, 0x61, 0x6d, 0x41, 0x76, 0x67, 0x55, 0x73, 0x61, 0x67, + 0x65, 0x12, 0x26, 0x0a, 0x0f, 0x75, 0x74, 0x69, 0x6c, 0x5f, 0x67, 0x70, 0x75, 0x5f, 0x64, 0x65, + 0x76, 0x69, 0x63, 0x65, 0x18, 0x22, 0x20, 0x01, 0x28, 0x02, 0x52, 0x0d, 0x75, 0x74, 0x69, 0x6c, + 0x47, 0x70, 0x75, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x12, 0x33, 0x0a, 0x0b, 0x68, 0x6f, 0x75, + 0x72, 0x6c, 0x79, 0x5f, 0x63, 0x6f, 0x73, 0x74, 0x18, 0x32, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, + 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x2e, 0x4d, 0x6f, 0x6e, + 0x65, 0x79, 0x52, 0x0a, 0x68, 0x6f, 0x75, 0x72, 0x6c, 0x79, 0x43, 0x6f, 0x73, 0x74, 0x12, 0x3a, + 0x0a, 0x0f, 0x68, 0x6f, 0x75, 0x72, 0x6c, 0x79, 0x5f, 0x63, 0x70, 0x75, 0x5f, 0x63, 0x6f, 0x73, + 0x74, 0x18, 0x33, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, + 0x2e, 0x74, 0x79, 0x70, 0x65, 0x2e, 0x4d, 0x6f, 0x6e, 0x65, 0x79, 0x52, 0x0d, 0x68, 0x6f, 0x75, + 0x72, 0x6c, 0x79, 0x43, 0x70, 0x75, 0x43, 0x6f, 0x73, 0x74, 0x12, 0x40, 0x0a, 0x12, 0x68, 0x6f, + 0x75, 0x72, 0x6c, 0x79, 0x5f, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x5f, 0x63, 0x6f, 0x73, 0x74, + 0x18, 0x34, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, + 0x74, 0x79, 0x70, 0x65, 0x2e, 0x4d, 0x6f, 0x6e, 0x65, 0x79, 0x52, 0x10, 0x68, 0x6f, 0x75, 0x72, + 0x6c, 0x79, 0x4d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x43, 0x6f, 0x73, 0x74, 0x12, 0x3a, 0x0a, 0x0f, + 0x68, 0x6f, 0x75, 0x72, 0x6c, 0x79, 0x5f, 0x67, 0x70, 0x75, 0x5f, 0x63, 0x6f, 0x73, 0x74, 0x18, + 0x35, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x2e, 0x4d, 0x6f, 0x6e, 0x65, 0x79, 0x52, 0x0d, 0x68, 0x6f, 0x75, 0x72, 0x6c, - 0x79, 0x43, 0x70, 0x75, 0x43, 0x6f, 0x73, 0x74, 0x12, 0x40, 0x0a, 0x12, 0x68, 0x6f, 0x75, 0x72, - 0x6c, 0x79, 0x5f, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x5f, 0x63, 0x6f, 0x73, 0x74, 0x18, 0x34, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x74, 0x79, - 0x70, 0x65, 0x2e, 0x4d, 0x6f, 0x6e, 0x65, 0x79, 0x52, 0x10, 0x68, 0x6f, 0x75, 0x72, 0x6c, 0x79, - 0x4d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x43, 0x6f, 0x73, 0x74, 0x12, 0x3a, 0x0a, 0x0f, 0x68, 0x6f, - 0x75, 0x72, 0x6c, 0x79, 0x5f, 0x67, 0x70, 0x75, 0x5f, 0x63, 0x6f, 0x73, 0x74, 0x18, 0x35, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x74, 0x79, 0x70, - 0x65, 0x2e, 0x4d, 0x6f, 0x6e, 0x65, 0x79, 0x52, 0x0d, 0x68, 0x6f, 0x75, 0x72, 0x6c, 0x79, 0x47, - 0x70, 0x75, 0x43, 0x6f, 0x73, 0x74, 0x12, 0x3b, 0x0a, 0x0b, 0x62, 0x75, 0x63, 0x6b, 0x65, 0x74, - 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x29, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, - 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, - 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0a, 0x62, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x54, - 0x69, 0x6d, 0x65, 0x22, 0x48, 0x0a, 0x14, 0x47, 0x65, 0x74, 0x4e, 0x6f, 0x64, 0x65, 0x47, 0x72, - 0x6f, 0x75, 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x30, 0x0a, 0x0a, 0x6e, - 0x6f, 0x64, 0x65, 0x5f, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x11, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x47, 0x72, 0x6f, - 0x75, 0x70, 0x52, 0x09, 0x6e, 0x6f, 0x64, 0x65, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x22, 0x3f, 0x0a, - 0x12, 0x47, 0x65, 0x74, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x12, 0x29, 0x0a, 0x07, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6c, - 0x75, 0x73, 0x74, 0x65, 0x72, 0x52, 0x07, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x22, 0xf2, - 0x02, 0x0a, 0x13, 0x47, 0x65, 0x74, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x73, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2b, 0x0a, 0x08, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, - 0x72, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, - 0x31, 0x2e, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x52, 0x08, 0x63, 0x6c, 0x75, 0x73, 0x74, - 0x65, 0x72, 0x73, 0x12, 0x42, 0x0a, 0x10, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, - 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, - 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x4d, - 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x52, 0x0f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, - 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x12, 0x2d, 0x0a, 0x09, 0x63, 0x6f, 0x73, 0x74, 0x5f, - 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x61, 0x70, 0x69, - 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x73, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x08, 0x63, 0x6f, - 0x73, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x2d, 0x0a, 0x09, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x69, - 0x6e, 0x66, 0x6f, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x61, 0x70, 0x69, 0x2e, - 0x76, 0x31, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x08, 0x6e, 0x6f, 0x64, - 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x3f, 0x0a, 0x10, 0x63, 0x6f, 0x73, 0x74, 0x5f, 0x64, 0x61, - 0x74, 0x61, 0x5f, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, - 0x15, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x73, 0x74, 0x44, 0x61, 0x74, - 0x61, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x52, 0x0e, 0x63, 0x6f, 0x73, 0x74, 0x44, 0x61, 0x74, 0x61, - 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x12, 0x4b, 0x0a, 0x14, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, - 0x63, 0x65, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x5f, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x18, 0x06, - 0x20, 0x03, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, - 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x44, 0x61, 0x74, 0x61, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x52, - 0x12, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x44, 0x61, 0x74, 0x61, 0x50, 0x6f, 0x69, - 0x6e, 0x74, 0x73, 0x22, 0x7b, 0x0a, 0x14, 0x4c, 0x69, 0x73, 0x74, 0x43, 0x6c, 0x75, 0x73, 0x74, - 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2b, 0x0a, 0x08, 0x63, - 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0f, 0x2e, - 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x52, 0x08, - 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x73, 0x12, 0x32, 0x0a, 0x0a, 0x70, 0x61, 0x67, 0x69, - 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x61, - 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x61, 0x67, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x52, 0x0a, 0x70, 0x61, 0x67, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x3a, 0x02, 0x18, 0x01, - 0x22, 0xb4, 0x02, 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x20, 0x0a, 0x04, 0x6e, 0x6f, 0x64, 0x65, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4e, 0x6f, 0x64, 0x65, - 0x52, 0x04, 0x6e, 0x6f, 0x64, 0x65, 0x12, 0x42, 0x0a, 0x10, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, - 0x63, 0x65, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x17, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, - 0x63, 0x65, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x52, 0x0f, 0x72, 0x65, 0x73, 0x6f, 0x75, - 0x72, 0x63, 0x65, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x12, 0x2d, 0x0a, 0x09, 0x63, 0x6f, - 0x73, 0x74, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, - 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x73, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, - 0x08, 0x63, 0x6f, 0x73, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x3f, 0x0a, 0x10, 0x63, 0x6f, 0x73, - 0x74, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x5f, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x18, 0x04, 0x20, - 0x03, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x73, - 0x74, 0x44, 0x61, 0x74, 0x61, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x52, 0x0e, 0x63, 0x6f, 0x73, 0x74, - 0x44, 0x61, 0x74, 0x61, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x12, 0x4b, 0x0a, 0x14, 0x72, 0x65, - 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x5f, 0x70, 0x6f, 0x69, 0x6e, - 0x74, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, - 0x31, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x44, 0x61, 0x74, 0x61, 0x50, 0x6f, - 0x69, 0x6e, 0x74, 0x52, 0x12, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x44, 0x61, 0x74, - 0x61, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x22, 0x8a, 0x02, 0x0a, 0x14, 0x47, 0x65, 0x74, 0x57, - 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x12, 0x3b, 0x0a, 0x0e, 0x77, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x5f, 0x69, 0x74, 0x65, - 0x6d, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, - 0x31, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x49, 0x74, 0x65, 0x6d, 0x52, 0x0d, - 0x77, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x49, 0x74, 0x65, 0x6d, 0x73, 0x12, 0x2d, 0x0a, - 0x09, 0x63, 0x6f, 0x73, 0x74, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x10, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x73, 0x74, 0x49, 0x6e, - 0x66, 0x6f, 0x52, 0x08, 0x63, 0x6f, 0x73, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x42, 0x0a, 0x10, - 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, - 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, - 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x52, - 0x0f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, - 0x12, 0x42, 0x0a, 0x10, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x73, 0x75, 0x6d, - 0x6d, 0x61, 0x72, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x61, 0x70, 0x69, - 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x75, 0x6d, 0x6d, - 0x61, 0x72, 0x79, 0x52, 0x0f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x75, 0x6d, - 0x6d, 0x61, 0x72, 0x79, 0x22, 0x95, 0x03, 0x0a, 0x13, 0x47, 0x65, 0x74, 0x57, 0x6f, 0x72, 0x6b, - 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x39, 0x0a, 0x0d, - 0x77, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x5f, 0x69, 0x74, 0x65, 0x6d, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x57, 0x6f, 0x72, - 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x49, 0x74, 0x65, 0x6d, 0x52, 0x0c, 0x77, 0x6f, 0x72, 0x6b, 0x6c, - 0x6f, 0x61, 0x64, 0x49, 0x74, 0x65, 0x6d, 0x12, 0x2d, 0x0a, 0x09, 0x63, 0x6f, 0x73, 0x74, 0x5f, - 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x61, 0x70, 0x69, - 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x73, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x08, 0x63, 0x6f, - 0x73, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x42, 0x0a, 0x10, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, - 0x63, 0x65, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x17, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, - 0x63, 0x65, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x52, 0x0f, 0x72, 0x65, 0x73, 0x6f, 0x75, - 0x72, 0x63, 0x65, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x12, 0x42, 0x0a, 0x10, 0x72, 0x65, - 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x73, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x18, 0x04, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, - 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x52, 0x0f, 0x72, - 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x12, 0x3f, - 0x0a, 0x10, 0x63, 0x6f, 0x73, 0x74, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x5f, 0x70, 0x6f, 0x69, 0x6e, - 0x74, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, - 0x31, 0x2e, 0x43, 0x6f, 0x73, 0x74, 0x44, 0x61, 0x74, 0x61, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x52, - 0x0e, 0x63, 0x6f, 0x73, 0x74, 0x44, 0x61, 0x74, 0x61, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x12, - 0x4b, 0x0a, 0x14, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x64, 0x61, 0x74, 0x61, - 0x5f, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x19, 0x2e, - 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x44, - 0x61, 0x74, 0x61, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x52, 0x12, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, - 0x63, 0x65, 0x44, 0x61, 0x74, 0x61, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x22, 0x9e, 0x02, 0x0a, - 0x26, 0x47, 0x65, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x43, 0x6f, 0x6e, 0x74, - 0x61, 0x69, 0x6e, 0x65, 0x72, 0x50, 0x65, 0x72, 0x63, 0x65, 0x6e, 0x74, 0x69, 0x6c, 0x65, 0x73, + 0x79, 0x47, 0x70, 0x75, 0x43, 0x6f, 0x73, 0x74, 0x12, 0x3b, 0x0a, 0x0b, 0x62, 0x75, 0x63, 0x6b, + 0x65, 0x74, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x29, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, + 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, + 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0a, 0x62, 0x75, 0x63, 0x6b, 0x65, + 0x74, 0x54, 0x69, 0x6d, 0x65, 0x22, 0x48, 0x0a, 0x14, 0x47, 0x65, 0x74, 0x4e, 0x6f, 0x64, 0x65, + 0x47, 0x72, 0x6f, 0x75, 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x30, 0x0a, + 0x0a, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x11, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x47, + 0x72, 0x6f, 0x75, 0x70, 0x52, 0x09, 0x6e, 0x6f, 0x64, 0x65, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x22, + 0x3f, 0x0a, 0x12, 0x47, 0x65, 0x74, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x29, 0x0a, 0x07, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, + 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x52, 0x07, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, + 0x22, 0xf2, 0x02, 0x0a, 0x13, 0x47, 0x65, 0x74, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x73, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2b, 0x0a, 0x08, 0x63, 0x6c, 0x75, 0x73, + 0x74, 0x65, 0x72, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x61, 0x70, 0x69, + 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x52, 0x08, 0x63, 0x6c, 0x75, + 0x73, 0x74, 0x65, 0x72, 0x73, 0x12, 0x42, 0x0a, 0x10, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, + 0x65, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x17, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, + 0x65, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x52, 0x0f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, + 0x63, 0x65, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x12, 0x2d, 0x0a, 0x09, 0x63, 0x6f, 0x73, + 0x74, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x61, + 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x73, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x08, + 0x63, 0x6f, 0x73, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x2d, 0x0a, 0x09, 0x6e, 0x6f, 0x64, 0x65, + 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x61, 0x70, + 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x08, 0x6e, + 0x6f, 0x64, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x3f, 0x0a, 0x10, 0x63, 0x6f, 0x73, 0x74, 0x5f, + 0x64, 0x61, 0x74, 0x61, 0x5f, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, + 0x0b, 0x32, 0x15, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x73, 0x74, 0x44, + 0x61, 0x74, 0x61, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x52, 0x0e, 0x63, 0x6f, 0x73, 0x74, 0x44, 0x61, + 0x74, 0x61, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x12, 0x4b, 0x0a, 0x14, 0x72, 0x65, 0x73, 0x6f, + 0x75, 0x72, 0x63, 0x65, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x5f, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, + 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, + 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x44, 0x61, 0x74, 0x61, 0x50, 0x6f, 0x69, 0x6e, + 0x74, 0x52, 0x12, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x44, 0x61, 0x74, 0x61, 0x50, + 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x22, 0x7b, 0x0a, 0x14, 0x4c, 0x69, 0x73, 0x74, 0x43, 0x6c, 0x75, + 0x73, 0x74, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2b, 0x0a, + 0x08, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, + 0x0f, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, + 0x52, 0x08, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x73, 0x12, 0x32, 0x0a, 0x0a, 0x70, 0x61, + 0x67, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, + 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x61, 0x67, 0x69, 0x6e, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x52, 0x0a, 0x70, 0x61, 0x67, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x3a, 0x02, + 0x18, 0x01, 0x22, 0xe9, 0x02, 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x20, 0x0a, 0x04, 0x6e, 0x6f, 0x64, 0x65, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4e, 0x6f, + 0x64, 0x65, 0x52, 0x04, 0x6e, 0x6f, 0x64, 0x65, 0x12, 0x42, 0x0a, 0x10, 0x72, 0x65, 0x73, 0x6f, + 0x75, 0x72, 0x63, 0x65, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x73, 0x6f, + 0x75, 0x72, 0x63, 0x65, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x52, 0x0f, 0x72, 0x65, 0x73, + 0x6f, 0x75, 0x72, 0x63, 0x65, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x12, 0x2d, 0x0a, 0x09, + 0x63, 0x6f, 0x73, 0x74, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x10, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x73, 0x74, 0x49, 0x6e, 0x66, + 0x6f, 0x52, 0x08, 0x63, 0x6f, 0x73, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x3f, 0x0a, 0x10, 0x63, + 0x6f, 0x73, 0x74, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x5f, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x18, + 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x43, + 0x6f, 0x73, 0x74, 0x44, 0x61, 0x74, 0x61, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x52, 0x0e, 0x63, 0x6f, + 0x73, 0x74, 0x44, 0x61, 0x74, 0x61, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x12, 0x4b, 0x0a, 0x14, + 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x5f, 0x70, 0x6f, + 0x69, 0x6e, 0x74, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x61, 0x70, 0x69, + 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x44, 0x61, 0x74, 0x61, + 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x52, 0x12, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x44, + 0x61, 0x74, 0x61, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x12, 0x33, 0x0a, 0x0a, 0x6e, 0x6f, 0x64, + 0x65, 0x5f, 0x63, 0x6c, 0x61, 0x69, 0x6d, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, + 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x43, 0x6c, 0x61, 0x69, 0x6d, + 0x52, 0x65, 0x66, 0x52, 0x09, 0x6e, 0x6f, 0x64, 0x65, 0x43, 0x6c, 0x61, 0x69, 0x6d, 0x22, 0xa1, + 0x02, 0x0a, 0x0c, 0x4e, 0x6f, 0x64, 0x65, 0x43, 0x6c, 0x61, 0x69, 0x6d, 0x52, 0x65, 0x66, 0x12, + 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, + 0x61, 0x6d, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x03, 0x75, 0x69, 0x64, 0x12, 0x1c, 0x0a, 0x09, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x61, 0x62, + 0x6c, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x61, + 0x62, 0x6c, 0x65, 0x12, 0x55, 0x0a, 0x14, 0x6e, 0x6f, 0x74, 0x5f, 0x64, 0x65, 0x6c, 0x65, 0x74, + 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, + 0x0e, 0x32, 0x23, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x43, + 0x6c, 0x61, 0x69, 0x6d, 0x4e, 0x6f, 0x74, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x61, 0x62, 0x6c, 0x65, + 0x52, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x52, 0x12, 0x6e, 0x6f, 0x74, 0x44, 0x65, 0x6c, 0x65, 0x74, + 0x61, 0x62, 0x6c, 0x65, 0x52, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x12, 0x30, 0x0a, 0x14, 0x6d, 0x69, + 0x6e, 0x5f, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, + 0x6f, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x12, 0x6d, 0x69, 0x6e, 0x4f, 0x70, 0x65, + 0x72, 0x61, 0x74, 0x6f, 0x72, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x44, 0x0a, 0x0d, + 0x6c, 0x61, 0x74, 0x65, 0x73, 0x74, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x06, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4e, 0x6f, 0x64, + 0x65, 0x43, 0x6c, 0x61, 0x69, 0x6d, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, + 0x61, 0x74, 0x75, 0x73, 0x52, 0x0c, 0x6c, 0x61, 0x74, 0x65, 0x73, 0x74, 0x53, 0x74, 0x61, 0x74, + 0x75, 0x73, 0x22, 0xd6, 0x01, 0x0a, 0x1f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x4e, 0x6f, + 0x64, 0x65, 0x43, 0x6c, 0x61, 0x69, 0x6d, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x69, 0x6f, 0x6e, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x17, 0x0a, 0x07, 0x74, 0x65, 0x61, 0x6d, 0x5f, 0x69, + 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x74, 0x65, 0x61, 0x6d, 0x49, 0x64, 0x12, + 0x1d, 0x0a, 0x0a, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, 0x64, 0x12, 0x1c, + 0x0a, 0x07, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x48, + 0x00, 0x52, 0x06, 0x6e, 0x6f, 0x64, 0x65, 0x49, 0x64, 0x88, 0x01, 0x01, 0x12, 0x1e, 0x0a, 0x08, + 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x75, 0x69, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x48, 0x01, + 0x52, 0x07, 0x6e, 0x6f, 0x64, 0x65, 0x55, 0x69, 0x64, 0x88, 0x01, 0x01, 0x12, 0x24, 0x0a, 0x0e, + 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x63, 0x6c, 0x61, 0x69, 0x6d, 0x5f, 0x75, 0x69, 0x64, 0x18, 0x05, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x6e, 0x6f, 0x64, 0x65, 0x43, 0x6c, 0x61, 0x69, 0x6d, 0x55, + 0x69, 0x64, 0x42, 0x0a, 0x0a, 0x08, 0x5f, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x69, 0x64, 0x42, 0x0b, + 0x0a, 0x09, 0x5f, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x75, 0x69, 0x64, 0x22, 0x5b, 0x0a, 0x20, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x4e, 0x6f, 0x64, 0x65, 0x43, 0x6c, 0x61, 0x69, 0x6d, 0x44, + 0x65, 0x6c, 0x65, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, + 0x37, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x1f, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x43, 0x6c, 0x61, + 0x69, 0x6d, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, + 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, 0xb2, 0x01, 0x0a, 0x21, 0x47, 0x65, 0x74, + 0x4e, 0x6f, 0x64, 0x65, 0x43, 0x6c, 0x61, 0x69, 0x6d, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x69, 0x6f, + 0x6e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x17, + 0x0a, 0x07, 0x74, 0x65, 0x61, 0x6d, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x06, 0x74, 0x65, 0x61, 0x6d, 0x49, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x6c, 0x75, 0x73, 0x74, + 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x6c, 0x75, + 0x73, 0x74, 0x65, 0x72, 0x49, 0x64, 0x12, 0x1c, 0x0a, 0x07, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x69, + 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x06, 0x6e, 0x6f, 0x64, 0x65, 0x49, + 0x64, 0x88, 0x01, 0x01, 0x12, 0x1e, 0x0a, 0x08, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x75, 0x69, 0x64, + 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x48, 0x01, 0x52, 0x07, 0x6e, 0x6f, 0x64, 0x65, 0x55, 0x69, + 0x64, 0x88, 0x01, 0x01, 0x42, 0x0a, 0x0a, 0x08, 0x5f, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x69, 0x64, + 0x42, 0x0b, 0x0a, 0x09, 0x5f, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x75, 0x69, 0x64, 0x22, 0x5d, 0x0a, + 0x22, 0x47, 0x65, 0x74, 0x4e, 0x6f, 0x64, 0x65, 0x43, 0x6c, 0x61, 0x69, 0x6d, 0x44, 0x65, 0x6c, + 0x65, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x12, 0x37, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4e, 0x6f, 0x64, + 0x65, 0x43, 0x6c, 0x61, 0x69, 0x6d, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, + 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, 0x6c, 0x0a, 0x17, + 0x47, 0x65, 0x74, 0x4e, 0x6f, 0x64, 0x65, 0x43, 0x6c, 0x61, 0x69, 0x6d, 0x52, 0x65, 0x66, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x17, 0x0a, 0x07, 0x74, 0x65, 0x61, 0x6d, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x74, 0x65, 0x61, 0x6d, 0x49, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, 0x64, 0x12, - 0x12, 0x0a, 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6b, - 0x69, 0x6e, 0x64, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x69, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x03, 0x75, 0x69, 0x64, 0x12, 0x3e, 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x74, - 0x69, 0x6d, 0x65, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, - 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, - 0x73, 0x74, 0x61, 0x6d, 0x70, 0x48, 0x00, 0x52, 0x09, 0x73, 0x74, 0x61, 0x72, 0x74, 0x54, 0x69, - 0x6d, 0x65, 0x88, 0x01, 0x01, 0x12, 0x3a, 0x0a, 0x08, 0x65, 0x6e, 0x64, 0x5f, 0x74, 0x69, 0x6d, - 0x65, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, - 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, - 0x61, 0x6d, 0x70, 0x48, 0x01, 0x52, 0x07, 0x65, 0x6e, 0x64, 0x54, 0x69, 0x6d, 0x65, 0x88, 0x01, - 0x01, 0x42, 0x0d, 0x0a, 0x0b, 0x5f, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x74, 0x69, 0x6d, 0x65, - 0x42, 0x0b, 0x0a, 0x09, 0x5f, 0x65, 0x6e, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x22, 0x6d, 0x0a, - 0x27, 0x47, 0x65, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x43, 0x6f, 0x6e, 0x74, - 0x61, 0x69, 0x6e, 0x65, 0x72, 0x50, 0x65, 0x72, 0x63, 0x65, 0x6e, 0x74, 0x69, 0x6c, 0x65, 0x73, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x42, 0x0a, 0x0a, 0x63, 0x6f, 0x6e, 0x74, - 0x61, 0x69, 0x6e, 0x65, 0x72, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x61, - 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x50, - 0x65, 0x72, 0x63, 0x65, 0x6e, 0x74, 0x69, 0x6c, 0x65, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, - 0x52, 0x0a, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x73, 0x22, 0x9d, 0x02, 0x0a, - 0x25, 0x47, 0x65, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x43, 0x6f, 0x6e, 0x74, - 0x61, 0x69, 0x6e, 0x65, 0x72, 0x54, 0x69, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x69, 0x65, 0x73, 0x52, + 0x19, 0x0a, 0x08, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, + 0x09, 0x52, 0x07, 0x6e, 0x6f, 0x64, 0x65, 0x49, 0x64, 0x73, 0x22, 0x54, 0x0a, 0x11, 0x4e, 0x6f, + 0x64, 0x65, 0x43, 0x6c, 0x61, 0x69, 0x6d, 0x52, 0x65, 0x66, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, + 0x17, 0x0a, 0x07, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x06, 0x6e, 0x6f, 0x64, 0x65, 0x49, 0x64, 0x12, 0x26, 0x0a, 0x03, 0x72, 0x65, 0x66, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4e, + 0x6f, 0x64, 0x65, 0x43, 0x6c, 0x61, 0x69, 0x6d, 0x52, 0x65, 0x66, 0x52, 0x03, 0x72, 0x65, 0x66, + 0x22, 0x4f, 0x0a, 0x18, 0x47, 0x65, 0x74, 0x4e, 0x6f, 0x64, 0x65, 0x43, 0x6c, 0x61, 0x69, 0x6d, + 0x52, 0x65, 0x66, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x33, 0x0a, 0x07, + 0x65, 0x6e, 0x74, 0x72, 0x69, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x19, 0x2e, + 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x43, 0x6c, 0x61, 0x69, 0x6d, + 0x52, 0x65, 0x66, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x07, 0x65, 0x6e, 0x74, 0x72, 0x69, 0x65, + 0x73, 0x22, 0x56, 0x0a, 0x15, 0x4e, 0x6f, 0x64, 0x65, 0x43, 0x6c, 0x61, 0x69, 0x6d, 0x44, 0x65, + 0x6c, 0x65, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x74, 0x65, 0x6d, 0x12, 0x17, 0x0a, 0x07, 0x6e, 0x6f, + 0x64, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x6e, 0x6f, 0x64, + 0x65, 0x49, 0x64, 0x12, 0x24, 0x0a, 0x0e, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x63, 0x6c, 0x61, 0x69, + 0x6d, 0x5f, 0x75, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x6e, 0x6f, 0x64, + 0x65, 0x43, 0x6c, 0x61, 0x69, 0x6d, 0x55, 0x69, 0x64, 0x22, 0x8f, 0x01, 0x0a, 0x20, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x4e, 0x6f, 0x64, 0x65, 0x43, 0x6c, 0x61, 0x69, 0x6d, 0x44, 0x65, + 0x6c, 0x65, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x17, + 0x0a, 0x07, 0x74, 0x65, 0x61, 0x6d, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x06, 0x74, 0x65, 0x61, 0x6d, 0x49, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x6c, 0x75, 0x73, 0x74, + 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x6c, 0x75, + 0x73, 0x74, 0x65, 0x72, 0x49, 0x64, 0x12, 0x33, 0x0a, 0x05, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x18, + 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4e, + 0x6f, 0x64, 0x65, 0x43, 0x6c, 0x61, 0x69, 0x6d, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x69, 0x6f, 0x6e, + 0x49, 0x74, 0x65, 0x6d, 0x52, 0x05, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x22, 0x81, 0x01, 0x0a, 0x17, + 0x4e, 0x6f, 0x64, 0x65, 0x43, 0x6c, 0x61, 0x69, 0x6d, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x69, 0x6f, + 0x6e, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x12, 0x17, 0x0a, 0x07, 0x6e, 0x6f, 0x64, 0x65, 0x5f, + 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x6e, 0x6f, 0x64, 0x65, 0x49, 0x64, + 0x12, 0x37, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x1f, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x43, 0x6c, + 0x61, 0x69, 0x6d, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x75, + 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x72, 0x72, + 0x6f, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x22, + 0x5e, 0x0a, 0x21, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x4e, 0x6f, 0x64, 0x65, 0x43, 0x6c, + 0x61, 0x69, 0x6d, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x39, 0x0a, 0x07, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x18, + 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4e, + 0x6f, 0x64, 0x65, 0x43, 0x6c, 0x61, 0x69, 0x6d, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x69, 0x6f, 0x6e, + 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x52, 0x07, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x22, + 0x9e, 0x04, 0x0a, 0x17, 0x4e, 0x6f, 0x64, 0x65, 0x43, 0x6c, 0x61, 0x69, 0x6d, 0x44, 0x65, 0x6c, + 0x65, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x1d, 0x0a, 0x0a, 0x72, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x09, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x49, 0x64, 0x12, 0x26, 0x0a, 0x0f, 0x6e, 0x6f, + 0x64, 0x65, 0x5f, 0x63, 0x6c, 0x61, 0x69, 0x6d, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x0d, 0x6e, 0x6f, 0x64, 0x65, 0x43, 0x6c, 0x61, 0x69, 0x6d, 0x4e, 0x61, + 0x6d, 0x65, 0x12, 0x24, 0x0a, 0x0e, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x63, 0x6c, 0x61, 0x69, 0x6d, + 0x5f, 0x75, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x6e, 0x6f, 0x64, 0x65, + 0x43, 0x6c, 0x61, 0x69, 0x6d, 0x55, 0x69, 0x64, 0x12, 0x34, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, + 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1e, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, + 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x43, 0x6c, 0x61, 0x69, 0x6d, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x69, + 0x6f, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x12, 0x14, + 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x65, + 0x72, 0x72, 0x6f, 0x72, 0x12, 0x2c, 0x0a, 0x12, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x65, + 0x64, 0x5f, 0x62, 0x79, 0x5f, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x10, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x65, 0x64, 0x42, 0x79, 0x45, 0x6d, 0x61, + 0x69, 0x6c, 0x12, 0x3d, 0x0a, 0x0c, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x65, 0x64, 0x5f, + 0x61, 0x74, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, + 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, + 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0b, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x65, 0x64, 0x41, + 0x74, 0x12, 0x3e, 0x0a, 0x0d, 0x66, 0x69, 0x72, 0x73, 0x74, 0x5f, 0x73, 0x65, 0x6e, 0x74, 0x5f, + 0x61, 0x74, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, + 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, + 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0b, 0x66, 0x69, 0x72, 0x73, 0x74, 0x53, 0x65, 0x6e, 0x74, 0x41, + 0x74, 0x12, 0x3b, 0x0a, 0x0b, 0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, + 0x18, 0x09, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, + 0x6d, 0x70, 0x52, 0x0a, 0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x65, 0x64, 0x41, 0x74, 0x12, 0x3b, + 0x0a, 0x0b, 0x72, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x0a, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, + 0x0a, 0x72, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x64, 0x41, 0x74, 0x12, 0x23, 0x0a, 0x0d, 0x73, + 0x74, 0x61, 0x6c, 0x65, 0x5f, 0x75, 0x6e, 0x61, 0x63, 0x6b, 0x65, 0x64, 0x18, 0x0b, 0x20, 0x01, + 0x28, 0x08, 0x52, 0x0c, 0x73, 0x74, 0x61, 0x6c, 0x65, 0x55, 0x6e, 0x61, 0x63, 0x6b, 0x65, 0x64, + 0x22, 0x8a, 0x02, 0x0a, 0x14, 0x47, 0x65, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, + 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3b, 0x0a, 0x0e, 0x77, 0x6f, 0x72, + 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x5f, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, + 0x0b, 0x32, 0x14, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x6c, + 0x6f, 0x61, 0x64, 0x49, 0x74, 0x65, 0x6d, 0x52, 0x0d, 0x77, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, + 0x64, 0x49, 0x74, 0x65, 0x6d, 0x73, 0x12, 0x2d, 0x0a, 0x09, 0x63, 0x6f, 0x73, 0x74, 0x5f, 0x69, + 0x6e, 0x66, 0x6f, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x61, 0x70, 0x69, 0x2e, + 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x73, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x08, 0x63, 0x6f, 0x73, + 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x42, 0x0a, 0x10, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, + 0x65, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x17, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, + 0x65, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x52, 0x0f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, + 0x63, 0x65, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x12, 0x42, 0x0a, 0x10, 0x72, 0x65, 0x73, + 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x73, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x18, 0x04, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x73, + 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x52, 0x0f, 0x72, 0x65, + 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x22, 0x95, 0x03, + 0x0a, 0x13, 0x47, 0x65, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x39, 0x0a, 0x0d, 0x77, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, + 0x64, 0x5f, 0x69, 0x74, 0x65, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x61, + 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x49, 0x74, + 0x65, 0x6d, 0x52, 0x0c, 0x77, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x49, 0x74, 0x65, 0x6d, + 0x12, 0x2d, 0x0a, 0x09, 0x63, 0x6f, 0x73, 0x74, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x73, + 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x08, 0x63, 0x6f, 0x73, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x12, + 0x42, 0x0a, 0x10, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x6d, 0x65, 0x74, 0x72, + 0x69, 0x63, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x61, 0x70, 0x69, 0x2e, + 0x76, 0x31, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x4d, 0x65, 0x74, 0x72, 0x69, + 0x63, 0x73, 0x52, 0x0f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x4d, 0x65, 0x74, 0x72, + 0x69, 0x63, 0x73, 0x12, 0x42, 0x0a, 0x10, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, + 0x73, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, + 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, + 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x52, 0x0f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, + 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x12, 0x3f, 0x0a, 0x10, 0x63, 0x6f, 0x73, 0x74, 0x5f, + 0x64, 0x61, 0x74, 0x61, 0x5f, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, + 0x0b, 0x32, 0x15, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x73, 0x74, 0x44, + 0x61, 0x74, 0x61, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x52, 0x0e, 0x63, 0x6f, 0x73, 0x74, 0x44, 0x61, + 0x74, 0x61, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x12, 0x4b, 0x0a, 0x14, 0x72, 0x65, 0x73, 0x6f, + 0x75, 0x72, 0x63, 0x65, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x5f, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, + 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, + 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x44, 0x61, 0x74, 0x61, 0x50, 0x6f, 0x69, 0x6e, + 0x74, 0x52, 0x12, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x44, 0x61, 0x74, 0x61, 0x50, + 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x22, 0x9e, 0x02, 0x0a, 0x26, 0x47, 0x65, 0x74, 0x57, 0x6f, 0x72, + 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x50, 0x65, + 0x72, 0x63, 0x65, 0x6e, 0x74, 0x69, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x12, 0x17, 0x0a, 0x07, 0x74, 0x65, 0x61, 0x6d, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x06, 0x74, 0x65, 0x61, 0x6d, 0x49, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x6c, 0x75, + 0x73, 0x74, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, + 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6b, 0x69, 0x6e, 0x64, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x12, 0x10, 0x0a, 0x03, + 0x75, 0x69, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x69, 0x64, 0x12, 0x3e, + 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x0b, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x48, 0x00, + 0x52, 0x09, 0x73, 0x74, 0x61, 0x72, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x88, 0x01, 0x01, 0x12, 0x3a, + 0x0a, 0x08, 0x65, 0x6e, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, + 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x48, 0x01, 0x52, 0x07, + 0x65, 0x6e, 0x64, 0x54, 0x69, 0x6d, 0x65, 0x88, 0x01, 0x01, 0x42, 0x0d, 0x0a, 0x0b, 0x5f, 0x73, + 0x74, 0x61, 0x72, 0x74, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x42, 0x0b, 0x0a, 0x09, 0x5f, 0x65, 0x6e, + 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x22, 0x6d, 0x0a, 0x27, 0x47, 0x65, 0x74, 0x57, 0x6f, 0x72, + 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x50, 0x65, + 0x72, 0x63, 0x65, 0x6e, 0x74, 0x69, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x12, 0x42, 0x0a, 0x0a, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x73, 0x18, + 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x43, + 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x50, 0x65, 0x72, 0x63, 0x65, 0x6e, 0x74, 0x69, + 0x6c, 0x65, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x52, 0x0a, 0x63, 0x6f, 0x6e, 0x74, 0x61, + 0x69, 0x6e, 0x65, 0x72, 0x73, 0x22, 0x9d, 0x02, 0x0a, 0x25, 0x47, 0x65, 0x74, 0x57, 0x6f, 0x72, + 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x54, 0x69, + 0x6d, 0x65, 0x53, 0x65, 0x72, 0x69, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, + 0x17, 0x0a, 0x07, 0x74, 0x65, 0x61, 0x6d, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x06, 0x74, 0x65, 0x61, 0x6d, 0x49, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x6c, 0x75, 0x73, + 0x74, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x6c, + 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x12, 0x10, 0x0a, 0x03, 0x75, + 0x69, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x69, 0x64, 0x12, 0x3e, 0x0a, + 0x0a, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x0b, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x48, 0x00, 0x52, + 0x09, 0x73, 0x74, 0x61, 0x72, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x88, 0x01, 0x01, 0x12, 0x3a, 0x0a, + 0x08, 0x65, 0x6e, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, + 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x48, 0x01, 0x52, 0x07, 0x65, + 0x6e, 0x64, 0x54, 0x69, 0x6d, 0x65, 0x88, 0x01, 0x01, 0x42, 0x0d, 0x0a, 0x0b, 0x5f, 0x73, 0x74, + 0x61, 0x72, 0x74, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x42, 0x0b, 0x0a, 0x09, 0x5f, 0x65, 0x6e, 0x64, + 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x22, 0x65, 0x0a, 0x26, 0x47, 0x65, 0x74, 0x57, 0x6f, 0x72, 0x6b, + 0x6c, 0x6f, 0x61, 0x64, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x54, 0x69, 0x6d, + 0x65, 0x53, 0x65, 0x72, 0x69, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, + 0x3b, 0x0a, 0x0a, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x73, 0x18, 0x01, 0x20, + 0x03, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6e, + 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x54, 0x69, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x69, 0x65, 0x73, + 0x52, 0x0a, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x73, 0x22, 0x72, 0x0a, 0x26, + 0x47, 0x65, 0x74, 0x4c, 0x61, 0x74, 0x65, 0x73, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, + 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x17, 0x0a, 0x07, 0x74, 0x65, 0x61, 0x6d, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x74, 0x65, 0x61, 0x6d, 0x49, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, 0x64, 0x12, 0x12, - 0x0a, 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6b, 0x69, - 0x6e, 0x64, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x69, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x03, 0x75, 0x69, 0x64, 0x12, 0x3e, 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x74, 0x69, - 0x6d, 0x65, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, + 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, 0x64, 0x12, 0x10, + 0x0a, 0x03, 0x75, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x69, 0x64, + 0x22, 0x69, 0x0a, 0x27, 0x47, 0x65, 0x74, 0x4c, 0x61, 0x74, 0x65, 0x73, 0x74, 0x43, 0x6f, 0x6e, + 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x4c, 0x69, 0x6d, + 0x69, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3e, 0x0a, 0x0a, 0x63, + 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, + 0x1e, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, + 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x73, 0x52, + 0x0a, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x73, 0x22, 0xae, 0x01, 0x0a, 0x1d, + 0x47, 0x65, 0x74, 0x4e, 0x6f, 0x64, 0x65, 0x47, 0x50, 0x55, 0x4d, 0x69, 0x67, 0x49, 0x6e, 0x73, + 0x74, 0x61, 0x6e, 0x63, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x17, 0x0a, + 0x07, 0x74, 0x65, 0x61, 0x6d, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, + 0x74, 0x65, 0x61, 0x6d, 0x49, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, + 0x72, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x6c, 0x75, 0x73, + 0x74, 0x65, 0x72, 0x49, 0x64, 0x12, 0x1c, 0x0a, 0x07, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x69, 0x64, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x06, 0x6e, 0x6f, 0x64, 0x65, 0x49, 0x64, + 0x88, 0x01, 0x01, 0x12, 0x1e, 0x0a, 0x08, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x75, 0x69, 0x64, 0x18, + 0x04, 0x20, 0x01, 0x28, 0x09, 0x48, 0x01, 0x52, 0x07, 0x6e, 0x6f, 0x64, 0x65, 0x55, 0x69, 0x64, + 0x88, 0x01, 0x01, 0x42, 0x0a, 0x0a, 0x08, 0x5f, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x69, 0x64, 0x42, + 0x0b, 0x0a, 0x09, 0x5f, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x75, 0x69, 0x64, 0x22, 0xab, 0x01, 0x0a, + 0x1e, 0x47, 0x65, 0x74, 0x4e, 0x6f, 0x64, 0x65, 0x47, 0x50, 0x55, 0x4d, 0x69, 0x67, 0x49, 0x6e, + 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, + 0x34, 0x0a, 0x09, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, + 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x50, 0x55, 0x4d, + 0x69, 0x67, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x52, 0x09, 0x69, 0x6e, 0x73, 0x74, + 0x61, 0x6e, 0x63, 0x65, 0x73, 0x12, 0x42, 0x0a, 0x0c, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, + 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, + 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, + 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x48, 0x00, 0x52, 0x0b, 0x63, 0x6f, 0x6c, 0x6c, 0x65, + 0x63, 0x74, 0x65, 0x64, 0x41, 0x74, 0x88, 0x01, 0x01, 0x42, 0x0f, 0x0a, 0x0d, 0x5f, 0x63, 0x6f, + 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x22, 0xa9, 0x02, 0x0a, 0x26, 0x47, + 0x65, 0x74, 0x4e, 0x6f, 0x64, 0x65, 0x47, 0x50, 0x55, 0x4d, 0x69, 0x67, 0x49, 0x6e, 0x73, 0x74, + 0x61, 0x6e, 0x63, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x65, 0x72, 0x69, 0x65, 0x73, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x17, 0x0a, 0x07, 0x74, 0x65, 0x61, 0x6d, 0x5f, 0x69, 0x64, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x74, 0x65, 0x61, 0x6d, 0x49, 0x64, 0x12, 0x1d, + 0x0a, 0x0a, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x09, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, 0x64, 0x12, 0x1c, 0x0a, + 0x07, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, + 0x52, 0x06, 0x6e, 0x6f, 0x64, 0x65, 0x49, 0x64, 0x88, 0x01, 0x01, 0x12, 0x1e, 0x0a, 0x08, 0x6e, + 0x6f, 0x64, 0x65, 0x5f, 0x75, 0x69, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x48, 0x01, 0x52, + 0x07, 0x6e, 0x6f, 0x64, 0x65, 0x55, 0x69, 0x64, 0x88, 0x01, 0x01, 0x12, 0x39, 0x0a, 0x0a, 0x73, + 0x74, 0x61, 0x72, 0x74, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, + 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x73, 0x74, 0x61, + 0x72, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x35, 0x0a, 0x08, 0x65, 0x6e, 0x64, 0x5f, 0x74, 0x69, + 0x6d, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, - 0x74, 0x61, 0x6d, 0x70, 0x48, 0x00, 0x52, 0x09, 0x73, 0x74, 0x61, 0x72, 0x74, 0x54, 0x69, 0x6d, - 0x65, 0x88, 0x01, 0x01, 0x12, 0x3a, 0x0a, 0x08, 0x65, 0x6e, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, - 0x18, 0x0c, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, - 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, - 0x6d, 0x70, 0x48, 0x01, 0x52, 0x07, 0x65, 0x6e, 0x64, 0x54, 0x69, 0x6d, 0x65, 0x88, 0x01, 0x01, - 0x42, 0x0d, 0x0a, 0x0b, 0x5f, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x42, - 0x0b, 0x0a, 0x09, 0x5f, 0x65, 0x6e, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x22, 0x65, 0x0a, 0x26, - 0x47, 0x65, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x43, 0x6f, 0x6e, 0x74, 0x61, - 0x69, 0x6e, 0x65, 0x72, 0x54, 0x69, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x69, 0x65, 0x73, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3b, 0x0a, 0x0a, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, - 0x6e, 0x65, 0x72, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x61, 0x70, 0x69, - 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x54, 0x69, 0x6d, - 0x65, 0x53, 0x65, 0x72, 0x69, 0x65, 0x73, 0x52, 0x0a, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, - 0x65, 0x72, 0x73, 0x22, 0x72, 0x0a, 0x26, 0x47, 0x65, 0x74, 0x4c, 0x61, 0x74, 0x65, 0x73, 0x74, - 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x17, 0x0a, + 0x74, 0x61, 0x6d, 0x70, 0x52, 0x07, 0x65, 0x6e, 0x64, 0x54, 0x69, 0x6d, 0x65, 0x42, 0x0a, 0x0a, + 0x08, 0x5f, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x69, 0x64, 0x42, 0x0b, 0x0a, 0x09, 0x5f, 0x6e, 0x6f, + 0x64, 0x65, 0x5f, 0x75, 0x69, 0x64, 0x22, 0xa7, 0x02, 0x0a, 0x17, 0x47, 0x50, 0x55, 0x4d, 0x69, + 0x67, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x44, 0x61, 0x74, 0x61, 0x50, 0x6f, 0x69, + 0x6e, 0x74, 0x12, 0x38, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, + 0x70, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x23, 0x0a, 0x0d, + 0x74, 0x65, 0x6e, 0x73, 0x6f, 0x72, 0x5f, 0x61, 0x63, 0x74, 0x69, 0x76, 0x65, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x01, 0x52, 0x0c, 0x74, 0x65, 0x6e, 0x73, 0x6f, 0x72, 0x41, 0x63, 0x74, 0x69, 0x76, + 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x64, 0x72, 0x61, 0x6d, 0x5f, 0x61, 0x63, 0x74, 0x69, 0x76, 0x65, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x01, 0x52, 0x0a, 0x64, 0x72, 0x61, 0x6d, 0x41, 0x63, 0x74, 0x69, + 0x76, 0x65, 0x12, 0x34, 0x0a, 0x16, 0x67, 0x72, 0x61, 0x70, 0x68, 0x69, 0x63, 0x73, 0x5f, 0x65, + 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x5f, 0x61, 0x63, 0x74, 0x69, 0x76, 0x65, 0x18, 0x04, 0x20, 0x01, + 0x28, 0x01, 0x52, 0x14, 0x67, 0x72, 0x61, 0x70, 0x68, 0x69, 0x63, 0x73, 0x45, 0x6e, 0x67, 0x69, + 0x6e, 0x65, 0x41, 0x63, 0x74, 0x69, 0x76, 0x65, 0x12, 0x29, 0x0a, 0x10, 0x66, 0x72, 0x61, 0x6d, + 0x65, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x5f, 0x75, 0x73, 0x65, 0x64, 0x18, 0x05, 0x20, 0x01, + 0x28, 0x01, 0x52, 0x0f, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x55, + 0x73, 0x65, 0x64, 0x12, 0x2b, 0x0a, 0x11, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x62, 0x75, 0x66, 0x66, + 0x65, 0x72, 0x5f, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x18, 0x06, 0x20, 0x01, 0x28, 0x01, 0x52, 0x10, + 0x66, 0x72, 0x61, 0x6d, 0x65, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x54, 0x6f, 0x74, 0x61, 0x6c, + 0x22, 0xdc, 0x01, 0x0a, 0x18, 0x47, 0x50, 0x55, 0x4d, 0x69, 0x67, 0x49, 0x6e, 0x73, 0x74, 0x61, + 0x6e, 0x63, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x65, 0x72, 0x69, 0x65, 0x73, 0x12, 0x26, 0x0a, + 0x0f, 0x6d, 0x69, 0x67, 0x5f, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x5f, 0x69, 0x64, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x6d, 0x69, 0x67, 0x49, 0x6e, 0x73, 0x74, 0x61, + 0x6e, 0x63, 0x65, 0x49, 0x64, 0x12, 0x1f, 0x0a, 0x0b, 0x6d, 0x69, 0x67, 0x5f, 0x70, 0x72, 0x6f, + 0x66, 0x69, 0x6c, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x6d, 0x69, 0x67, 0x50, + 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, + 0x5f, 0x75, 0x75, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x64, 0x65, 0x76, + 0x69, 0x63, 0x65, 0x55, 0x75, 0x69, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x6d, 0x6f, 0x64, 0x65, 0x6c, + 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6d, 0x6f, 0x64, + 0x65, 0x6c, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x37, 0x0a, 0x06, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, + 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, + 0x47, 0x50, 0x55, 0x4d, 0x69, 0x67, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x44, 0x61, + 0x74, 0x61, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x52, 0x06, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x22, + 0x69, 0x0a, 0x27, 0x47, 0x65, 0x74, 0x4e, 0x6f, 0x64, 0x65, 0x47, 0x50, 0x55, 0x4d, 0x69, 0x67, + 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x65, 0x72, 0x69, + 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3e, 0x0a, 0x09, 0x69, 0x6e, + 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x20, 0x2e, + 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x50, 0x55, 0x4d, 0x69, 0x67, 0x49, 0x6e, 0x73, + 0x74, 0x61, 0x6e, 0x63, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x65, 0x72, 0x69, 0x65, 0x73, 0x52, + 0x09, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x73, 0x22, 0x73, 0x0a, 0x27, 0x47, 0x65, + 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x49, 0x6e, 0x64, 0x69, 0x76, 0x69, + 0x64, 0x75, 0x61, 0x6c, 0x47, 0x50, 0x55, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x17, 0x0a, 0x07, 0x74, 0x65, 0x61, 0x6d, 0x5f, 0x69, 0x64, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x74, 0x65, 0x61, 0x6d, 0x49, 0x64, 0x12, 0x1d, + 0x0a, 0x0a, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x09, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, 0x64, 0x12, 0x10, 0x0a, + 0x03, 0x75, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x69, 0x64, 0x22, + 0x71, 0x0a, 0x28, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x49, + 0x6e, 0x64, 0x69, 0x76, 0x69, 0x64, 0x75, 0x61, 0x6c, 0x47, 0x50, 0x55, 0x4d, 0x65, 0x74, 0x72, + 0x69, 0x63, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x45, 0x0a, 0x0a, 0x63, + 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, + 0x25, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, + 0x65, 0x72, 0x49, 0x6e, 0x64, 0x69, 0x76, 0x69, 0x64, 0x75, 0x61, 0x6c, 0x47, 0x50, 0x55, 0x4d, + 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x52, 0x0a, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, + 0x72, 0x73, 0x22, 0x4a, 0x0a, 0x10, 0x4c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x56, 0x65, + 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, + 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, + 0x67, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x02, + 0x20, 0x03, 0x28, 0x09, 0x52, 0x08, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x55, + 0x0a, 0x1b, 0x47, 0x65, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x4c, 0x61, 0x6e, + 0x67, 0x75, 0x61, 0x67, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x17, 0x0a, 0x07, 0x74, 0x65, 0x61, 0x6d, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x74, 0x65, 0x61, 0x6d, 0x49, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x6c, 0x75, 0x73, - 0x74, 0x65, 0x72, 0x49, 0x64, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x03, 0x75, 0x69, 0x64, 0x22, 0x69, 0x0a, 0x27, 0x47, 0x65, 0x74, 0x4c, 0x61, - 0x74, 0x65, 0x73, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x12, 0x3e, 0x0a, 0x0a, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x73, - 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, - 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x73, 0x52, 0x0a, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, - 0x72, 0x73, 0x22, 0xae, 0x01, 0x0a, 0x1d, 0x47, 0x65, 0x74, 0x4e, 0x6f, 0x64, 0x65, 0x47, 0x50, - 0x55, 0x4d, 0x69, 0x67, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x73, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x12, 0x17, 0x0a, 0x07, 0x74, 0x65, 0x61, 0x6d, 0x5f, 0x69, 0x64, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x74, 0x65, 0x61, 0x6d, 0x49, 0x64, 0x12, 0x1d, 0x0a, - 0x0a, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x09, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, 0x64, 0x12, 0x1c, 0x0a, 0x07, - 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, - 0x06, 0x6e, 0x6f, 0x64, 0x65, 0x49, 0x64, 0x88, 0x01, 0x01, 0x12, 0x1e, 0x0a, 0x08, 0x6e, 0x6f, - 0x64, 0x65, 0x5f, 0x75, 0x69, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x48, 0x01, 0x52, 0x07, - 0x6e, 0x6f, 0x64, 0x65, 0x55, 0x69, 0x64, 0x88, 0x01, 0x01, 0x42, 0x0a, 0x0a, 0x08, 0x5f, 0x6e, - 0x6f, 0x64, 0x65, 0x5f, 0x69, 0x64, 0x42, 0x0b, 0x0a, 0x09, 0x5f, 0x6e, 0x6f, 0x64, 0x65, 0x5f, - 0x75, 0x69, 0x64, 0x22, 0xab, 0x01, 0x0a, 0x1e, 0x47, 0x65, 0x74, 0x4e, 0x6f, 0x64, 0x65, 0x47, - 0x50, 0x55, 0x4d, 0x69, 0x67, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x73, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x34, 0x0a, 0x09, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, - 0x63, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x61, 0x70, 0x69, 0x2e, - 0x76, 0x31, 0x2e, 0x47, 0x50, 0x55, 0x4d, 0x69, 0x67, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, - 0x65, 0x52, 0x09, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x73, 0x12, 0x42, 0x0a, 0x0c, - 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x48, 0x00, - 0x52, 0x0b, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x65, 0x64, 0x41, 0x74, 0x88, 0x01, 0x01, - 0x42, 0x0f, 0x0a, 0x0d, 0x5f, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x65, 0x64, 0x5f, 0x61, - 0x74, 0x22, 0x73, 0x0a, 0x27, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, - 0x72, 0x49, 0x6e, 0x64, 0x69, 0x76, 0x69, 0x64, 0x75, 0x61, 0x6c, 0x47, 0x50, 0x55, 0x4d, 0x65, - 0x74, 0x72, 0x69, 0x63, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x17, 0x0a, 0x07, - 0x74, 0x65, 0x61, 0x6d, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x74, - 0x65, 0x61, 0x6d, 0x49, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, - 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x6c, 0x75, 0x73, 0x74, - 0x65, 0x72, 0x49, 0x64, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x03, 0x75, 0x69, 0x64, 0x22, 0x71, 0x0a, 0x28, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, - 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x49, 0x6e, 0x64, 0x69, 0x76, 0x69, 0x64, 0x75, 0x61, 0x6c, - 0x47, 0x50, 0x55, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x12, 0x45, 0x0a, 0x0a, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x73, - 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, - 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x49, 0x6e, 0x64, 0x69, 0x76, 0x69, 0x64, - 0x75, 0x61, 0x6c, 0x47, 0x50, 0x55, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x52, 0x0a, 0x63, - 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x73, 0x22, 0x4a, 0x0a, 0x10, 0x4c, 0x61, 0x6e, - 0x67, 0x75, 0x61, 0x67, 0x65, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x1a, 0x0a, - 0x08, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x08, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x76, 0x65, 0x72, - 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x08, 0x76, 0x65, 0x72, - 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x55, 0x0a, 0x1b, 0x47, 0x65, 0x74, 0x57, 0x6f, 0x72, 0x6b, - 0x6c, 0x6f, 0x61, 0x64, 0x4c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x73, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x12, 0x17, 0x0a, 0x07, 0x74, 0x65, 0x61, 0x6d, 0x5f, 0x69, 0x64, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x74, 0x65, 0x61, 0x6d, 0x49, 0x64, 0x12, 0x1d, 0x0a, - 0x0a, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x09, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, 0x64, 0x22, 0x56, 0x0a, 0x1c, - 0x47, 0x65, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x4c, 0x61, 0x6e, 0x67, 0x75, - 0x61, 0x67, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x36, 0x0a, 0x09, - 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, - 0x18, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, - 0x65, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x09, 0x6c, 0x61, 0x6e, 0x67, 0x75, - 0x61, 0x67, 0x65, 0x73, 0x22, 0x90, 0x02, 0x0a, 0x1b, 0x47, 0x65, 0x74, 0x46, 0x6f, 0x72, 0x65, - 0x63, 0x61, 0x73, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x73, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x12, 0x17, 0x0a, 0x07, 0x74, 0x65, 0x61, 0x6d, 0x5f, 0x69, 0x64, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x74, 0x65, 0x61, 0x6d, 0x49, 0x64, 0x12, 0x1d, 0x0a, - 0x0a, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x09, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, 0x64, 0x12, 0x21, 0x0a, 0x0c, - 0x73, 0x68, 0x6f, 0x77, 0x5f, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x18, 0x0c, 0x20, 0x01, - 0x28, 0x08, 0x52, 0x0b, 0x73, 0x68, 0x6f, 0x77, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x12, - 0x3e, 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x15, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x48, - 0x00, 0x52, 0x09, 0x73, 0x74, 0x61, 0x72, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x88, 0x01, 0x01, 0x12, - 0x3a, 0x0a, 0x08, 0x65, 0x6e, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x16, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, - 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x48, 0x01, 0x52, - 0x07, 0x65, 0x6e, 0x64, 0x54, 0x69, 0x6d, 0x65, 0x88, 0x01, 0x01, 0x42, 0x0d, 0x0a, 0x0b, 0x5f, - 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x42, 0x0b, 0x0a, 0x09, 0x5f, 0x65, - 0x6e, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x22, 0x9a, 0x02, 0x0a, 0x1c, 0x47, 0x65, 0x74, 0x46, - 0x6f, 0x72, 0x65, 0x63, 0x61, 0x73, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x73, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3b, 0x0a, 0x0e, 0x77, 0x6f, 0x72, 0x6b, - 0x6c, 0x6f, 0x61, 0x64, 0x5f, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, - 0x32, 0x14, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, - 0x61, 0x64, 0x49, 0x74, 0x65, 0x6d, 0x52, 0x0d, 0x77, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, - 0x49, 0x74, 0x65, 0x6d, 0x73, 0x12, 0x2d, 0x0a, 0x09, 0x63, 0x6f, 0x73, 0x74, 0x5f, 0x69, 0x6e, - 0x66, 0x6f, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, - 0x31, 0x2e, 0x43, 0x6f, 0x73, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x08, 0x63, 0x6f, 0x73, 0x74, - 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x4a, 0x0a, 0x10, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, - 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1f, - 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x46, 0x6f, 0x72, 0x65, 0x63, 0x61, 0x73, 0x74, - 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x52, - 0x0f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, - 0x12, 0x42, 0x0a, 0x10, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x73, 0x75, 0x6d, - 0x6d, 0x61, 0x72, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x61, 0x70, 0x69, - 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x75, 0x6d, 0x6d, - 0x61, 0x72, 0x79, 0x52, 0x0f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x75, 0x6d, - 0x6d, 0x61, 0x72, 0x79, 0x22, 0xbb, 0x02, 0x0a, 0x1a, 0x47, 0x65, 0x74, 0x46, 0x6f, 0x72, 0x65, - 0x63, 0x61, 0x73, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x12, 0x17, 0x0a, 0x07, 0x74, 0x65, 0x61, 0x6d, 0x5f, 0x69, 0x64, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x74, 0x65, 0x61, 0x6d, 0x49, 0x64, 0x12, 0x1d, 0x0a, 0x0a, - 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x09, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6b, - 0x69, 0x6e, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x12, - 0x10, 0x0a, 0x03, 0x75, 0x69, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x69, - 0x64, 0x12, 0x27, 0x0a, 0x0f, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x5f, 0x64, 0x65, 0x6c, - 0x65, 0x74, 0x65, 0x64, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0e, 0x69, 0x6e, 0x63, 0x6c, - 0x75, 0x64, 0x65, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x12, 0x3e, 0x0a, 0x0a, 0x73, 0x74, - 0x61, 0x72, 0x74, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x15, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, - 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, - 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x48, 0x00, 0x52, 0x09, 0x73, 0x74, - 0x61, 0x72, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x88, 0x01, 0x01, 0x12, 0x3a, 0x0a, 0x08, 0x65, 0x6e, - 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x16, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, + 0x74, 0x65, 0x72, 0x49, 0x64, 0x22, 0x56, 0x0a, 0x1c, 0x47, 0x65, 0x74, 0x57, 0x6f, 0x72, 0x6b, + 0x6c, 0x6f, 0x61, 0x64, 0x4c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x73, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x36, 0x0a, 0x09, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, + 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, + 0x31, 0x2e, 0x4c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, + 0x6e, 0x73, 0x52, 0x09, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x73, 0x22, 0x90, 0x02, + 0x0a, 0x1b, 0x47, 0x65, 0x74, 0x46, 0x6f, 0x72, 0x65, 0x63, 0x61, 0x73, 0x74, 0x57, 0x6f, 0x72, + 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x17, 0x0a, + 0x07, 0x74, 0x65, 0x61, 0x6d, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, + 0x74, 0x65, 0x61, 0x6d, 0x49, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, + 0x72, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x6c, 0x75, 0x73, + 0x74, 0x65, 0x72, 0x49, 0x64, 0x12, 0x21, 0x0a, 0x0c, 0x73, 0x68, 0x6f, 0x77, 0x5f, 0x64, 0x65, + 0x6c, 0x65, 0x74, 0x65, 0x64, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, 0x73, 0x68, 0x6f, + 0x77, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x12, 0x3e, 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x72, + 0x74, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x15, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, - 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x48, 0x01, 0x52, 0x07, 0x65, 0x6e, 0x64, 0x54, - 0x69, 0x6d, 0x65, 0x88, 0x01, 0x01, 0x42, 0x0d, 0x0a, 0x0b, 0x5f, 0x73, 0x74, 0x61, 0x72, 0x74, - 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x42, 0x0b, 0x0a, 0x09, 0x5f, 0x65, 0x6e, 0x64, 0x5f, 0x74, 0x69, - 0x6d, 0x65, 0x22, 0xa5, 0x03, 0x0a, 0x1b, 0x47, 0x65, 0x74, 0x46, 0x6f, 0x72, 0x65, 0x63, 0x61, - 0x73, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x12, 0x39, 0x0a, 0x0d, 0x77, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x5f, 0x69, - 0x74, 0x65, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x61, 0x70, 0x69, 0x2e, + 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x48, 0x00, 0x52, 0x09, 0x73, 0x74, 0x61, 0x72, + 0x74, 0x54, 0x69, 0x6d, 0x65, 0x88, 0x01, 0x01, 0x12, 0x3a, 0x0a, 0x08, 0x65, 0x6e, 0x64, 0x5f, + 0x74, 0x69, 0x6d, 0x65, 0x18, 0x16, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, + 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, + 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x48, 0x01, 0x52, 0x07, 0x65, 0x6e, 0x64, 0x54, 0x69, 0x6d, + 0x65, 0x88, 0x01, 0x01, 0x42, 0x0d, 0x0a, 0x0b, 0x5f, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x74, + 0x69, 0x6d, 0x65, 0x42, 0x0b, 0x0a, 0x09, 0x5f, 0x65, 0x6e, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, + 0x22, 0x9a, 0x02, 0x0a, 0x1c, 0x47, 0x65, 0x74, 0x46, 0x6f, 0x72, 0x65, 0x63, 0x61, 0x73, 0x74, + 0x57, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x12, 0x3b, 0x0a, 0x0e, 0x77, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x5f, 0x69, 0x74, + 0x65, 0x6d, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x49, 0x74, 0x65, 0x6d, 0x52, - 0x0c, 0x77, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x49, 0x74, 0x65, 0x6d, 0x12, 0x2d, 0x0a, - 0x09, 0x63, 0x6f, 0x73, 0x74, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x10, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x73, 0x74, 0x49, 0x6e, - 0x66, 0x6f, 0x52, 0x08, 0x63, 0x6f, 0x73, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x4a, 0x0a, 0x10, - 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, - 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, - 0x46, 0x6f, 0x72, 0x65, 0x63, 0x61, 0x73, 0x74, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, - 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x52, 0x0f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, - 0x65, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x12, 0x42, 0x0a, 0x10, 0x72, 0x65, 0x73, 0x6f, - 0x75, 0x72, 0x63, 0x65, 0x5f, 0x73, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x18, 0x04, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x73, 0x6f, - 0x75, 0x72, 0x63, 0x65, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x52, 0x0f, 0x72, 0x65, 0x73, - 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x12, 0x3f, 0x0a, 0x10, - 0x63, 0x6f, 0x73, 0x74, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x5f, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, - 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, - 0x43, 0x6f, 0x73, 0x74, 0x44, 0x61, 0x74, 0x61, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x52, 0x0e, 0x63, - 0x6f, 0x73, 0x74, 0x44, 0x61, 0x74, 0x61, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x12, 0x4b, 0x0a, - 0x14, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x5f, 0x70, - 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x61, 0x70, - 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x44, 0x61, 0x74, - 0x61, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x52, 0x12, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, - 0x44, 0x61, 0x74, 0x61, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x22, 0x49, 0x0a, 0x1a, 0x47, 0x65, - 0x74, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2b, 0x0a, 0x08, 0x63, 0x6c, 0x75, 0x73, - 0x74, 0x65, 0x72, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x61, 0x70, 0x69, - 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x52, 0x08, 0x63, 0x6c, 0x75, - 0x73, 0x74, 0x65, 0x72, 0x73, 0x22, 0x49, 0x0a, 0x0e, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, - 0x45, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x6c, 0x75, 0x73, 0x74, - 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x6c, 0x75, - 0x73, 0x74, 0x65, 0x72, 0x49, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x6e, - 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, - 0x22, 0x8f, 0x01, 0x0a, 0x17, 0x47, 0x65, 0x74, 0x41, 0x6c, 0x6c, 0x4e, 0x61, 0x6d, 0x65, 0x73, - 0x70, 0x61, 0x63, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x17, 0x0a, 0x07, + 0x0d, 0x77, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x49, 0x74, 0x65, 0x6d, 0x73, 0x12, 0x2d, + 0x0a, 0x09, 0x63, 0x6f, 0x73, 0x74, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x10, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x73, 0x74, 0x49, + 0x6e, 0x66, 0x6f, 0x52, 0x08, 0x63, 0x6f, 0x73, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x4a, 0x0a, + 0x10, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, + 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, + 0x2e, 0x46, 0x6f, 0x72, 0x65, 0x63, 0x61, 0x73, 0x74, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, + 0x65, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x52, 0x0f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, + 0x63, 0x65, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x12, 0x42, 0x0a, 0x10, 0x72, 0x65, 0x73, + 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x73, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x18, 0x04, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x73, + 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x52, 0x0f, 0x72, 0x65, + 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x22, 0xbb, 0x02, + 0x0a, 0x1a, 0x47, 0x65, 0x74, 0x46, 0x6f, 0x72, 0x65, 0x63, 0x61, 0x73, 0x74, 0x57, 0x6f, 0x72, + 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x17, 0x0a, 0x07, 0x74, 0x65, 0x61, 0x6d, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x74, - 0x65, 0x61, 0x6d, 0x49, 0x64, 0x12, 0x1f, 0x0a, 0x0b, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, - 0x5f, 0x69, 0x64, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0a, 0x63, 0x6c, 0x75, 0x73, - 0x74, 0x65, 0x72, 0x49, 0x64, 0x73, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, - 0x74, 0x69, 0x6d, 0x65, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x73, 0x74, 0x61, 0x72, - 0x74, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x73, 0x74, 0x6f, 0x70, 0x5f, 0x74, 0x69, - 0x6d, 0x65, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x03, 0x52, 0x08, 0x73, 0x74, 0x6f, 0x70, 0x54, 0x69, - 0x6d, 0x65, 0x22, 0x6f, 0x0a, 0x18, 0x47, 0x65, 0x74, 0x41, 0x6c, 0x6c, 0x4e, 0x61, 0x6d, 0x65, - 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x53, - 0x0a, 0x1a, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x5f, 0x77, 0x69, 0x74, - 0x68, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, - 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6c, 0x75, 0x73, - 0x74, 0x65, 0x72, 0x45, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x17, 0x63, 0x6c, 0x75, 0x73, - 0x74, 0x65, 0x72, 0x49, 0x64, 0x57, 0x69, 0x74, 0x68, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, - 0x63, 0x65, 0x73, 0x22, 0x72, 0x0a, 0x20, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x4e, 0x61, 0x6d, + 0x65, 0x61, 0x6d, 0x49, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, + 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x6c, 0x75, 0x73, 0x74, + 0x65, 0x72, 0x49, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x69, 0x64, 0x18, + 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x69, 0x64, 0x12, 0x27, 0x0a, 0x0f, 0x69, 0x6e, + 0x63, 0x6c, 0x75, 0x64, 0x65, 0x5f, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x18, 0x0b, 0x20, + 0x01, 0x28, 0x08, 0x52, 0x0e, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x44, 0x65, 0x6c, 0x65, + 0x74, 0x65, 0x64, 0x12, 0x3e, 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x74, 0x69, 0x6d, + 0x65, 0x18, 0x15, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, + 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, + 0x61, 0x6d, 0x70, 0x48, 0x00, 0x52, 0x09, 0x73, 0x74, 0x61, 0x72, 0x74, 0x54, 0x69, 0x6d, 0x65, + 0x88, 0x01, 0x01, 0x12, 0x3a, 0x0a, 0x08, 0x65, 0x6e, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, + 0x16, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, + 0x70, 0x48, 0x01, 0x52, 0x07, 0x65, 0x6e, 0x64, 0x54, 0x69, 0x6d, 0x65, 0x88, 0x01, 0x01, 0x42, + 0x0d, 0x0a, 0x0b, 0x5f, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x42, 0x0b, + 0x0a, 0x09, 0x5f, 0x65, 0x6e, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x22, 0xa5, 0x03, 0x0a, 0x1b, + 0x47, 0x65, 0x74, 0x46, 0x6f, 0x72, 0x65, 0x63, 0x61, 0x73, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x6c, + 0x6f, 0x61, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x39, 0x0a, 0x0d, 0x77, + 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x5f, 0x69, 0x74, 0x65, 0x6d, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x57, 0x6f, 0x72, 0x6b, + 0x6c, 0x6f, 0x61, 0x64, 0x49, 0x74, 0x65, 0x6d, 0x52, 0x0c, 0x77, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, + 0x61, 0x64, 0x49, 0x74, 0x65, 0x6d, 0x12, 0x2d, 0x0a, 0x09, 0x63, 0x6f, 0x73, 0x74, 0x5f, 0x69, + 0x6e, 0x66, 0x6f, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x61, 0x70, 0x69, 0x2e, + 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x73, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x08, 0x63, 0x6f, 0x73, + 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x4a, 0x0a, 0x10, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, + 0x65, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, + 0x1f, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x46, 0x6f, 0x72, 0x65, 0x63, 0x61, 0x73, + 0x74, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, + 0x52, 0x0f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, + 0x73, 0x12, 0x42, 0x0a, 0x10, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x73, 0x75, + 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x61, 0x70, + 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x75, 0x6d, + 0x6d, 0x61, 0x72, 0x79, 0x52, 0x0f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x75, + 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x12, 0x3f, 0x0a, 0x10, 0x63, 0x6f, 0x73, 0x74, 0x5f, 0x64, 0x61, + 0x74, 0x61, 0x5f, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, + 0x15, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x73, 0x74, 0x44, 0x61, 0x74, + 0x61, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x52, 0x0e, 0x63, 0x6f, 0x73, 0x74, 0x44, 0x61, 0x74, 0x61, + 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x12, 0x4b, 0x0a, 0x14, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, + 0x63, 0x65, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x5f, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x18, 0x06, + 0x20, 0x03, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, + 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x44, 0x61, 0x74, 0x61, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x52, + 0x12, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x44, 0x61, 0x74, 0x61, 0x50, 0x6f, 0x69, + 0x6e, 0x74, 0x73, 0x22, 0x49, 0x0a, 0x1a, 0x47, 0x65, 0x74, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, + 0x72, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x12, 0x2b, 0x0a, 0x08, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x73, 0x18, 0x01, 0x20, + 0x03, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6c, 0x75, + 0x73, 0x74, 0x65, 0x72, 0x52, 0x08, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x73, 0x22, 0x49, + 0x0a, 0x0e, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x45, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, + 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, 0x64, 0x12, + 0x18, 0x0a, 0x07, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x07, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x22, 0x8f, 0x01, 0x0a, 0x17, 0x47, 0x65, + 0x74, 0x41, 0x6c, 0x6c, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x17, 0x0a, 0x07, 0x74, 0x65, 0x61, 0x6d, 0x5f, 0x69, 0x64, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x74, 0x65, 0x61, 0x6d, 0x49, 0x64, 0x12, 0x1f, + 0x0a, 0x0b, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x05, 0x20, + 0x03, 0x28, 0x09, 0x52, 0x0a, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, 0x64, 0x73, 0x12, + 0x1d, 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x0b, 0x20, + 0x01, 0x28, 0x03, 0x52, 0x09, 0x73, 0x74, 0x61, 0x72, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x1b, + 0x0a, 0x09, 0x73, 0x74, 0x6f, 0x70, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x0c, 0x20, 0x01, 0x28, + 0x03, 0x52, 0x08, 0x73, 0x74, 0x6f, 0x70, 0x54, 0x69, 0x6d, 0x65, 0x22, 0x6f, 0x0a, 0x18, 0x47, + 0x65, 0x74, 0x41, 0x6c, 0x6c, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x53, 0x0a, 0x1a, 0x63, 0x6c, 0x75, 0x73, 0x74, + 0x65, 0x72, 0x5f, 0x69, 0x64, 0x5f, 0x77, 0x69, 0x74, 0x68, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x73, + 0x70, 0x61, 0x63, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x61, 0x70, + 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x45, 0x6c, 0x65, 0x6d, + 0x65, 0x6e, 0x74, 0x52, 0x17, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, 0x64, 0x57, 0x69, + 0x74, 0x68, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x22, 0x72, 0x0a, 0x20, + 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, + 0x42, 0x79, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x12, 0x17, 0x0a, 0x07, 0x74, 0x65, 0x61, 0x6d, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x06, 0x74, 0x65, 0x61, 0x6d, 0x49, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x6c, 0x75, + 0x73, 0x74, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, + 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, 0x64, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x65, 0x61, 0x72, + 0x63, 0x68, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, + 0x22, 0x43, 0x0a, 0x21, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, + 0x61, 0x63, 0x65, 0x73, 0x42, 0x79, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, + 0x63, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0a, 0x6e, 0x61, 0x6d, 0x65, 0x73, + 0x70, 0x61, 0x63, 0x65, 0x73, 0x22, 0x58, 0x0a, 0x1e, 0x4c, 0x69, 0x73, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x42, 0x79, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x17, 0x0a, 0x07, 0x74, 0x65, 0x61, 0x6d, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x74, 0x65, 0x61, 0x6d, 0x49, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, 0x64, 0x12, - 0x16, 0x0a, 0x06, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x06, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x22, 0x43, 0x0a, 0x21, 0x53, 0x65, 0x61, 0x72, 0x63, - 0x68, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x42, 0x79, 0x43, 0x6c, 0x75, - 0x73, 0x74, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1e, 0x0a, 0x0a, - 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, - 0x52, 0x0a, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x22, 0x58, 0x0a, 0x1e, - 0x4c, 0x69, 0x73, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x42, 0x79, - 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x17, - 0x0a, 0x07, 0x74, 0x65, 0x61, 0x6d, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x06, 0x74, 0x65, 0x61, 0x6d, 0x49, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x6c, 0x75, 0x73, 0x74, - 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x6c, 0x75, - 0x73, 0x74, 0x65, 0x72, 0x49, 0x64, 0x22, 0x58, 0x0a, 0x1f, 0x4c, 0x69, 0x73, 0x74, 0x4e, 0x61, - 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x42, 0x79, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, - 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x35, 0x0a, 0x0a, 0x6e, 0x61, 0x6d, - 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x15, 0x2e, - 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, - 0x49, 0x74, 0x65, 0x6d, 0x52, 0x0a, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, - 0x22, 0x33, 0x0a, 0x0d, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x49, 0x74, 0x65, - 0x6d, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, - 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0xdb, 0x02, 0x0a, 0x1a, 0x47, 0x65, 0x74, 0x41, 0x6c, 0x6c, - 0x57, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x12, 0x17, 0x0a, 0x07, 0x74, 0x65, 0x61, 0x6d, 0x5f, 0x69, 0x64, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x74, 0x65, 0x61, 0x6d, 0x49, 0x64, 0x12, 0x1f, 0x0a, - 0x0b, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x05, 0x20, 0x03, - 0x28, 0x09, 0x52, 0x0a, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, 0x64, 0x73, 0x12, 0x1e, - 0x0a, 0x0a, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x18, 0x06, 0x20, 0x03, - 0x28, 0x09, 0x52, 0x0a, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x12, 0x2b, - 0x0a, 0x05, 0x6b, 0x69, 0x6e, 0x64, 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, 0x0e, 0x32, 0x15, 0x2e, - 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4b, 0x38, 0x73, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, - 0x4b, 0x69, 0x6e, 0x64, 0x52, 0x05, 0x6b, 0x69, 0x6e, 0x64, 0x73, 0x12, 0x27, 0x0a, 0x0f, 0x77, - 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x5f, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x18, 0x08, - 0x20, 0x03, 0x28, 0x09, 0x52, 0x0e, 0x77, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x4c, 0x61, - 0x62, 0x65, 0x6c, 0x73, 0x12, 0x28, 0x0a, 0x10, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x67, 0x72, 0x6f, - 0x75, 0x70, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x18, 0x09, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0e, - 0x6e, 0x6f, 0x64, 0x65, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x12, 0x1d, - 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x0b, 0x20, 0x01, - 0x28, 0x03, 0x52, 0x09, 0x73, 0x74, 0x61, 0x72, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x1b, 0x0a, - 0x09, 0x73, 0x74, 0x6f, 0x70, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x03, - 0x52, 0x08, 0x73, 0x74, 0x6f, 0x70, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x27, 0x0a, 0x0f, 0x69, 0x6e, - 0x63, 0x6c, 0x75, 0x64, 0x65, 0x5f, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x18, 0x15, 0x20, - 0x01, 0x28, 0x08, 0x52, 0x0e, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x44, 0x65, 0x6c, 0x65, - 0x74, 0x65, 0x64, 0x22, 0x77, 0x0a, 0x1b, 0x47, 0x65, 0x74, 0x41, 0x6c, 0x6c, 0x57, 0x6f, 0x72, - 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x12, 0x58, 0x0a, 0x1d, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x69, 0x64, - 0x5f, 0x77, 0x69, 0x74, 0x68, 0x5f, 0x77, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x5f, 0x6e, - 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x61, 0x70, 0x69, 0x2e, - 0x76, 0x31, 0x2e, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x45, 0x6c, 0x65, 0x6d, 0x65, 0x6e, - 0x74, 0x52, 0x19, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, 0x64, 0x57, 0x69, 0x74, 0x68, - 0x57, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x4e, 0x61, 0x6d, 0x65, 0x22, 0xb1, 0x02, 0x0a, - 0x1b, 0x47, 0x65, 0x74, 0x41, 0x6c, 0x6c, 0x57, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x4c, - 0x61, 0x62, 0x65, 0x6c, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x17, 0x0a, 0x07, - 0x74, 0x65, 0x61, 0x6d, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x74, - 0x65, 0x61, 0x6d, 0x49, 0x64, 0x12, 0x1f, 0x0a, 0x0b, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, - 0x5f, 0x69, 0x64, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0a, 0x63, 0x6c, 0x75, 0x73, - 0x74, 0x65, 0x72, 0x49, 0x64, 0x73, 0x12, 0x1e, 0x0a, 0x0a, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, - 0x61, 0x63, 0x65, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0a, 0x6e, 0x61, 0x6d, 0x65, - 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x12, 0x2b, 0x0a, 0x05, 0x6b, 0x69, 0x6e, 0x64, 0x73, 0x18, - 0x07, 0x20, 0x03, 0x28, 0x0e, 0x32, 0x15, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4b, - 0x38, 0x73, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x4b, 0x69, 0x6e, 0x64, 0x52, 0x05, 0x6b, 0x69, - 0x6e, 0x64, 0x73, 0x12, 0x25, 0x0a, 0x0e, 0x77, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x5f, - 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x18, 0x08, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0d, 0x77, 0x6f, 0x72, - 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x12, 0x28, 0x0a, 0x10, 0x6e, 0x6f, - 0x64, 0x65, 0x5f, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x18, 0x09, - 0x20, 0x03, 0x28, 0x09, 0x52, 0x0e, 0x6e, 0x6f, 0x64, 0x65, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4e, - 0x61, 0x6d, 0x65, 0x73, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x74, 0x69, - 0x6d, 0x65, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x73, 0x74, 0x61, 0x72, 0x74, 0x54, - 0x69, 0x6d, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x73, 0x74, 0x6f, 0x70, 0x5f, 0x74, 0x69, 0x6d, 0x65, - 0x18, 0x0c, 0x20, 0x01, 0x28, 0x03, 0x52, 0x08, 0x73, 0x74, 0x6f, 0x70, 0x54, 0x69, 0x6d, 0x65, - 0x22, 0x6b, 0x0a, 0x1c, 0x47, 0x65, 0x74, 0x41, 0x6c, 0x6c, 0x57, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, - 0x61, 0x64, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x12, 0x4b, 0x0a, 0x16, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x5f, 0x77, - 0x69, 0x74, 0x68, 0x5f, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, - 0x32, 0x16, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, - 0x72, 0x45, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x13, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, - 0x72, 0x49, 0x64, 0x57, 0x69, 0x74, 0x68, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x22, 0xbc, 0x01, - 0x0a, 0x1b, 0x47, 0x65, 0x74, 0x41, 0x6c, 0x6c, 0x4e, 0x6f, 0x64, 0x65, 0x47, 0x72, 0x6f, 0x75, - 0x70, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x17, 0x0a, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, 0x64, 0x22, + 0x58, 0x0a, 0x1f, 0x4c, 0x69, 0x73, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, + 0x73, 0x42, 0x79, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x12, 0x35, 0x0a, 0x0a, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, + 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, + 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x49, 0x74, 0x65, 0x6d, 0x52, 0x0a, 0x6e, + 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x22, 0x33, 0x0a, 0x0d, 0x4e, 0x61, 0x6d, + 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x49, 0x74, 0x65, 0x6d, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, + 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0xdb, + 0x02, 0x0a, 0x1a, 0x47, 0x65, 0x74, 0x41, 0x6c, 0x6c, 0x57, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, + 0x64, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x17, 0x0a, 0x07, 0x74, 0x65, 0x61, 0x6d, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x74, 0x65, 0x61, 0x6d, 0x49, 0x64, 0x12, 0x1f, 0x0a, 0x0b, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0a, 0x63, 0x6c, 0x75, - 0x73, 0x74, 0x65, 0x72, 0x49, 0x64, 0x73, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x72, 0x74, + 0x73, 0x74, 0x65, 0x72, 0x49, 0x64, 0x73, 0x12, 0x1e, 0x0a, 0x0a, 0x6e, 0x61, 0x6d, 0x65, 0x73, + 0x70, 0x61, 0x63, 0x65, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0a, 0x6e, 0x61, 0x6d, + 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x12, 0x2b, 0x0a, 0x05, 0x6b, 0x69, 0x6e, 0x64, 0x73, + 0x18, 0x07, 0x20, 0x03, 0x28, 0x0e, 0x32, 0x15, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, + 0x4b, 0x38, 0x73, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x4b, 0x69, 0x6e, 0x64, 0x52, 0x05, 0x6b, + 0x69, 0x6e, 0x64, 0x73, 0x12, 0x27, 0x0a, 0x0f, 0x77, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, + 0x5f, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x18, 0x08, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0e, 0x77, + 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x12, 0x28, 0x0a, + 0x10, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x6e, 0x61, 0x6d, 0x65, + 0x73, 0x18, 0x09, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0e, 0x6e, 0x6f, 0x64, 0x65, 0x47, 0x72, 0x6f, + 0x75, 0x70, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x73, 0x74, 0x61, 0x72, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x73, 0x74, 0x6f, 0x70, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x03, 0x52, 0x08, 0x73, 0x74, 0x6f, 0x70, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x27, 0x0a, 0x0f, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x5f, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x18, 0x15, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0e, 0x69, 0x6e, - 0x63, 0x6c, 0x75, 0x64, 0x65, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x22, 0x7d, 0x0a, 0x1c, - 0x47, 0x65, 0x74, 0x41, 0x6c, 0x6c, 0x4e, 0x6f, 0x64, 0x65, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4e, - 0x61, 0x6d, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x5d, 0x0a, 0x20, - 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x5f, 0x77, 0x69, 0x74, 0x68, 0x5f, - 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x73, - 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, - 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x45, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x1b, - 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, 0x64, 0x57, 0x69, 0x74, 0x68, 0x4e, 0x6f, 0x64, - 0x65, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x22, 0xd3, 0x0d, 0x0a, 0x07, - 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x17, 0x0a, 0x07, 0x74, - 0x65, 0x61, 0x6d, 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x74, 0x65, - 0x61, 0x6d, 0x49, 0x64, 0x12, 0x2a, 0x0a, 0x11, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x5f, 0x70, 0x72, - 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x0f, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x49, 0x64, - 0x12, 0x1b, 0x0a, 0x09, 0x72, 0x65, 0x67, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x05, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x08, 0x72, 0x65, 0x67, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x25, 0x0a, - 0x0e, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x5f, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x18, - 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x50, 0x72, 0x6f, 0x76, - 0x69, 0x64, 0x65, 0x72, 0x12, 0x16, 0x0a, 0x06, 0x72, 0x65, 0x67, 0x69, 0x6f, 0x6e, 0x18, 0x07, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x72, 0x65, 0x67, 0x69, 0x6f, 0x6e, 0x12, 0x1d, 0x0a, 0x0a, - 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x08, 0x20, 0x01, 0x28, 0x03, - 0x52, 0x09, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x75, - 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x09, 0x20, 0x01, 0x28, 0x03, 0x52, - 0x09, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x12, 0x42, 0x0a, 0x10, 0x72, 0x65, - 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x18, 0x0a, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, - 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x52, 0x0f, 0x72, - 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x12, 0x2d, - 0x0a, 0x09, 0x63, 0x6f, 0x73, 0x74, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x0b, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x10, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x73, 0x74, 0x49, - 0x6e, 0x66, 0x6f, 0x52, 0x08, 0x63, 0x6f, 0x73, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x2d, 0x0a, - 0x09, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x10, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x49, 0x6e, - 0x66, 0x6f, 0x52, 0x08, 0x6e, 0x6f, 0x64, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x3f, 0x0a, 0x10, - 0x63, 0x6f, 0x73, 0x74, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x5f, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, - 0x18, 0x0d, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, - 0x43, 0x6f, 0x73, 0x74, 0x44, 0x61, 0x74, 0x61, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x52, 0x0e, 0x63, - 0x6f, 0x73, 0x74, 0x44, 0x61, 0x74, 0x61, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x12, 0x4b, 0x0a, - 0x14, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x5f, 0x70, - 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x18, 0x0e, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x61, 0x70, - 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x44, 0x61, 0x74, - 0x61, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x52, 0x12, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, - 0x44, 0x61, 0x74, 0x61, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x12, 0x3c, 0x0a, 0x13, 0x6d, 0x6f, - 0x73, 0x74, 0x5f, 0x65, 0x78, 0x70, 0x65, 0x6e, 0x73, 0x69, 0x76, 0x65, 0x5f, 0x6e, 0x6f, 0x64, - 0x65, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, - 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x11, 0x6d, 0x6f, 0x73, 0x74, 0x45, 0x78, 0x70, 0x65, 0x6e, - 0x73, 0x69, 0x76, 0x65, 0x4e, 0x6f, 0x64, 0x65, 0x12, 0x3e, 0x0a, 0x14, 0x6c, 0x65, 0x61, 0x73, - 0x74, 0x5f, 0x65, 0x78, 0x70, 0x65, 0x6e, 0x73, 0x69, 0x76, 0x65, 0x5f, 0x6e, 0x6f, 0x64, 0x65, - 0x18, 0x10, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, - 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x12, 0x6c, 0x65, 0x61, 0x73, 0x74, 0x45, 0x78, 0x70, 0x65, 0x6e, - 0x73, 0x69, 0x76, 0x65, 0x4e, 0x6f, 0x64, 0x65, 0x12, 0x44, 0x0a, 0x17, 0x6d, 0x6f, 0x73, 0x74, - 0x5f, 0x75, 0x6e, 0x64, 0x65, 0x72, 0x75, 0x74, 0x69, 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x5f, 0x6e, - 0x6f, 0x64, 0x65, 0x18, 0x11, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x61, 0x70, 0x69, 0x2e, - 0x76, 0x31, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x15, 0x6d, 0x6f, 0x73, 0x74, 0x55, 0x6e, 0x64, - 0x65, 0x72, 0x75, 0x74, 0x69, 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x4e, 0x6f, 0x64, 0x65, 0x12, 0x53, - 0x0a, 0x1c, 0x6d, 0x6f, 0x73, 0x74, 0x5f, 0x75, 0x6e, 0x64, 0x65, 0x72, 0x75, 0x74, 0x69, 0x6c, - 0x69, 0x7a, 0x65, 0x64, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x18, 0x12, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, - 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x52, 0x1a, 0x6d, 0x6f, 0x73, 0x74, 0x55, 0x6e, 0x64, - 0x65, 0x72, 0x75, 0x74, 0x69, 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, - 0x6e, 0x65, 0x72, 0x12, 0x29, 0x0a, 0x11, 0x63, 0x70, 0x75, 0x5f, 0x63, 0x6f, 0x73, 0x74, 0x5f, - 0x70, 0x65, 0x72, 0x5f, 0x68, 0x6f, 0x75, 0x72, 0x18, 0x13, 0x20, 0x01, 0x28, 0x01, 0x52, 0x0e, - 0x63, 0x70, 0x75, 0x43, 0x6f, 0x73, 0x74, 0x50, 0x65, 0x72, 0x48, 0x6f, 0x75, 0x72, 0x12, 0x2f, - 0x0a, 0x14, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x5f, 0x63, 0x6f, 0x73, 0x74, 0x5f, 0x70, 0x65, - 0x72, 0x5f, 0x68, 0x6f, 0x75, 0x72, 0x18, 0x14, 0x20, 0x01, 0x28, 0x01, 0x52, 0x11, 0x6d, 0x65, - 0x6d, 0x6f, 0x72, 0x79, 0x43, 0x6f, 0x73, 0x74, 0x50, 0x65, 0x72, 0x48, 0x6f, 0x75, 0x72, 0x12, - 0x28, 0x0a, 0x10, 0x68, 0x61, 0x73, 0x5f, 0x62, 0x65, 0x65, 0x6e, 0x5f, 0x75, 0x70, 0x64, 0x61, - 0x74, 0x65, 0x64, 0x18, 0x15, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0e, 0x68, 0x61, 0x73, 0x42, 0x65, - 0x65, 0x6e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x12, 0x1f, 0x0a, 0x0b, 0x63, 0x75, 0x73, - 0x74, 0x6f, 0x6d, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x16, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, - 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x64, 0x69, - 0x73, 0x70, 0x6c, 0x61, 0x79, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x17, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x0b, 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x2c, 0x0a, - 0x12, 0x69, 0x73, 0x5f, 0x70, 0x72, 0x69, 0x63, 0x65, 0x5f, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, - 0x62, 0x6c, 0x65, 0x18, 0x18, 0x20, 0x01, 0x28, 0x08, 0x52, 0x10, 0x69, 0x73, 0x50, 0x72, 0x69, - 0x63, 0x65, 0x41, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x12, 0x29, 0x0a, 0x11, 0x67, - 0x70, 0x75, 0x5f, 0x63, 0x6f, 0x73, 0x74, 0x5f, 0x70, 0x65, 0x72, 0x5f, 0x68, 0x6f, 0x75, 0x72, - 0x18, 0x19, 0x20, 0x01, 0x28, 0x01, 0x52, 0x0e, 0x67, 0x70, 0x75, 0x43, 0x6f, 0x73, 0x74, 0x50, - 0x65, 0x72, 0x48, 0x6f, 0x75, 0x72, 0x12, 0x2c, 0x0a, 0x0f, 0x69, 0x73, 0x5f, 0x64, 0x69, 0x73, - 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x65, 0x64, 0x18, 0x28, 0x20, 0x01, 0x28, 0x08, 0x48, - 0x00, 0x52, 0x0e, 0x69, 0x73, 0x44, 0x69, 0x73, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x65, - 0x64, 0x88, 0x01, 0x01, 0x12, 0x2f, 0x0a, 0x08, 0x7a, 0x78, 0x70, 0x5f, 0x69, 0x6e, 0x66, 0x6f, - 0x18, 0x32, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, - 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x07, 0x7a, 0x78, - 0x70, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x38, 0x0a, 0x0d, 0x7a, 0x78, 0x70, 0x5f, 0x68, 0x65, 0x6c, - 0x6d, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x33, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x61, - 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x49, 0x6e, - 0x66, 0x6f, 0x52, 0x0b, 0x7a, 0x78, 0x70, 0x48, 0x65, 0x6c, 0x6d, 0x49, 0x6e, 0x66, 0x6f, 0x12, - 0x36, 0x0a, 0x0c, 0x64, 0x61, 0x6b, 0x72, 0x5f, 0x6f, 0x70, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18, - 0x3c, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4f, - 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x0a, 0x64, 0x61, 0x6b, - 0x72, 0x4f, 0x70, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x36, 0x0a, 0x0c, 0x6e, 0x6f, 0x64, 0x65, 0x5f, - 0x6f, 0x70, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x3d, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, - 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x49, - 0x6e, 0x66, 0x6f, 0x52, 0x0a, 0x6e, 0x6f, 0x64, 0x65, 0x4f, 0x70, 0x49, 0x6e, 0x66, 0x6f, 0x12, - 0x3e, 0x0a, 0x10, 0x73, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x5f, 0x6f, 0x70, 0x5f, 0x69, - 0x6e, 0x66, 0x6f, 0x18, 0x3e, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x61, 0x70, 0x69, 0x2e, - 0x76, 0x31, 0x2e, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x52, - 0x0e, 0x73, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x4f, 0x70, 0x49, 0x6e, 0x66, 0x6f, 0x12, - 0x3c, 0x0a, 0x0f, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x5f, 0x6f, 0x70, 0x5f, 0x69, 0x6e, - 0x66, 0x6f, 0x18, 0x3f, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, - 0x31, 0x2e, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x0d, - 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x4f, 0x70, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x21, 0x0a, - 0x0c, 0x69, 0x6d, 0x70, 0x61, 0x63, 0x74, 0x5f, 0x73, 0x63, 0x6f, 0x72, 0x65, 0x18, 0x46, 0x20, - 0x01, 0x28, 0x01, 0x52, 0x0b, 0x69, 0x6d, 0x70, 0x61, 0x63, 0x74, 0x53, 0x63, 0x6f, 0x72, 0x65, - 0x12, 0x2a, 0x0a, 0x10, 0x75, 0x6e, 0x64, 0x65, 0x72, 0x75, 0x74, 0x69, 0x6c, 0x69, 0x7a, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x51, 0x20, 0x01, 0x28, 0x01, 0x52, 0x10, 0x75, 0x6e, 0x64, 0x65, - 0x72, 0x75, 0x74, 0x69, 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, - 0x74, 0x61, 0x67, 0x73, 0x18, 0x5b, 0x20, 0x03, 0x28, 0x09, 0x52, 0x04, 0x74, 0x61, 0x67, 0x73, - 0x12, 0x2c, 0x0a, 0x12, 0x68, 0x61, 0x73, 0x5f, 0x61, 0x72, 0x67, 0x6f, 0x5f, 0x77, 0x6f, 0x72, - 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x73, 0x18, 0x65, 0x20, 0x01, 0x28, 0x08, 0x52, 0x10, 0x68, 0x61, - 0x73, 0x41, 0x72, 0x67, 0x6f, 0x57, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x73, 0x12, 0x2d, - 0x0a, 0x12, 0x6b, 0x75, 0x62, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x65, 0x73, 0x5f, 0x76, 0x65, 0x72, - 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x6e, 0x20, 0x01, 0x28, 0x09, 0x52, 0x11, 0x6b, 0x75, 0x62, 0x65, - 0x72, 0x6e, 0x65, 0x74, 0x65, 0x73, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x42, 0x12, 0x0a, - 0x10, 0x5f, 0x69, 0x73, 0x5f, 0x64, 0x69, 0x73, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x65, - 0x64, 0x22, 0x54, 0x0a, 0x0c, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x49, 0x6e, 0x66, - 0x6f, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x16, 0x0a, 0x06, 0x63, - 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x63, 0x6f, 0x6d, - 0x6d, 0x69, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x65, 0x18, 0x15, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x04, 0x64, 0x61, 0x74, 0x65, 0x22, 0x73, 0x0a, 0x09, 0x43, 0x6f, 0x6e, 0x74, 0x61, - 0x69, 0x6e, 0x65, 0x72, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x02, 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x42, 0x0a, 0x10, 0x72, 0x65, 0x73, 0x6f, - 0x75, 0x72, 0x63, 0x65, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x18, 0x03, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x73, 0x6f, - 0x75, 0x72, 0x63, 0x65, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x52, 0x0f, 0x72, 0x65, 0x73, - 0x6f, 0x75, 0x72, 0x63, 0x65, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x22, 0x21, 0x0a, 0x1f, - 0x47, 0x65, 0x74, 0x4c, 0x61, 0x74, 0x65, 0x73, 0x74, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, - 0x72, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, - 0xcb, 0x07, 0x0a, 0x20, 0x47, 0x65, 0x74, 0x4c, 0x61, 0x74, 0x65, 0x73, 0x74, 0x4f, 0x70, 0x65, - 0x72, 0x61, 0x74, 0x6f, 0x72, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2f, 0x0a, 0x08, 0x7a, 0x78, 0x70, 0x5f, 0x69, 0x6e, 0x66, 0x6f, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, - 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x07, 0x7a, 0x78, - 0x70, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x38, 0x0a, 0x0d, 0x7a, 0x78, 0x70, 0x5f, 0x68, 0x65, 0x6c, - 0x6d, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x61, - 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x49, 0x6e, - 0x66, 0x6f, 0x52, 0x0b, 0x7a, 0x78, 0x70, 0x48, 0x65, 0x6c, 0x6d, 0x49, 0x6e, 0x66, 0x6f, 0x12, - 0x3c, 0x0a, 0x0f, 0x7a, 0x78, 0x70, 0x5f, 0x6e, 0x65, 0x74, 0x6d, 0x6f, 0x6e, 0x5f, 0x69, 0x6e, - 0x66, 0x6f, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, - 0x31, 0x2e, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x0d, - 0x7a, 0x78, 0x70, 0x4e, 0x65, 0x74, 0x6d, 0x6f, 0x6e, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x45, 0x0a, - 0x14, 0x7a, 0x78, 0x70, 0x5f, 0x6e, 0x65, 0x74, 0x6d, 0x6f, 0x6e, 0x5f, 0x68, 0x65, 0x6c, 0x6d, - 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x61, 0x70, - 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x49, 0x6e, 0x66, - 0x6f, 0x52, 0x11, 0x7a, 0x78, 0x70, 0x4e, 0x65, 0x74, 0x6d, 0x6f, 0x6e, 0x48, 0x65, 0x6c, 0x6d, - 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x36, 0x0a, 0x0c, 0x64, 0x61, 0x6b, 0x72, 0x5f, 0x6f, 0x70, 0x5f, - 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x61, 0x70, 0x69, - 0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x49, 0x6e, 0x66, 0x6f, - 0x52, 0x0a, 0x64, 0x61, 0x6b, 0x72, 0x4f, 0x70, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x3f, 0x0a, 0x11, - 0x64, 0x61, 0x6b, 0x72, 0x5f, 0x6f, 0x70, 0x5f, 0x68, 0x65, 0x6c, 0x6d, 0x5f, 0x69, 0x6e, 0x66, - 0x6f, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, - 0x2e, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x0e, 0x64, - 0x61, 0x6b, 0x72, 0x4f, 0x70, 0x48, 0x65, 0x6c, 0x6d, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x3d, 0x0a, - 0x10, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x6f, 0x70, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x5f, 0x61, 0x77, - 0x73, 0x18, 0x15, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, - 0x2e, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x0d, 0x6e, - 0x6f, 0x64, 0x65, 0x4f, 0x70, 0x49, 0x6e, 0x66, 0x6f, 0x41, 0x77, 0x73, 0x12, 0x41, 0x0a, 0x12, - 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x6f, 0x70, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x5f, 0x61, 0x7a, 0x75, - 0x72, 0x65, 0x18, 0x16, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, - 0x31, 0x2e, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x0f, - 0x6e, 0x6f, 0x64, 0x65, 0x4f, 0x70, 0x49, 0x6e, 0x66, 0x6f, 0x41, 0x7a, 0x75, 0x72, 0x65, 0x12, - 0x3d, 0x0a, 0x10, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x6f, 0x70, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x5f, - 0x67, 0x63, 0x70, 0x18, 0x17, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x61, 0x70, 0x69, 0x2e, - 0x76, 0x31, 0x2e, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x52, - 0x0d, 0x6e, 0x6f, 0x64, 0x65, 0x4f, 0x70, 0x49, 0x6e, 0x66, 0x6f, 0x47, 0x63, 0x70, 0x12, 0x3d, - 0x0a, 0x10, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x6f, 0x70, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x5f, 0x6f, - 0x63, 0x69, 0x18, 0x18, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, - 0x31, 0x2e, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x0d, - 0x6e, 0x6f, 0x64, 0x65, 0x4f, 0x70, 0x49, 0x6e, 0x66, 0x6f, 0x4f, 0x63, 0x69, 0x12, 0x3e, 0x0a, - 0x10, 0x73, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x5f, 0x6f, 0x70, 0x5f, 0x69, 0x6e, 0x66, - 0x6f, 0x18, 0x1f, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, - 0x2e, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x0e, 0x73, - 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x4f, 0x70, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x47, 0x0a, - 0x15, 0x73, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x5f, 0x6f, 0x70, 0x5f, 0x68, 0x65, 0x6c, - 0x6d, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x20, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x61, - 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x49, 0x6e, - 0x66, 0x6f, 0x52, 0x12, 0x73, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x4f, 0x70, 0x48, 0x65, - 0x6c, 0x6d, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x33, 0x0a, 0x0a, 0x74, 0x72, 0x65, 0x7a, 0x72, 0x5f, - 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x29, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x61, 0x70, 0x69, - 0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x49, 0x6e, 0x66, 0x6f, - 0x52, 0x09, 0x74, 0x72, 0x65, 0x7a, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x3c, 0x0a, 0x0f, 0x74, - 0x72, 0x65, 0x7a, 0x72, 0x5f, 0x68, 0x65, 0x6c, 0x6d, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x2a, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x70, - 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x0d, 0x74, 0x72, 0x65, 0x7a, - 0x72, 0x48, 0x65, 0x6c, 0x6d, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x42, 0x0a, 0x12, 0x73, 0x6e, 0x61, - 0x70, 0x73, 0x68, 0x6f, 0x74, 0x5f, 0x68, 0x65, 0x6c, 0x6d, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18, - 0x33, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4f, - 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x10, 0x73, 0x6e, 0x61, - 0x70, 0x73, 0x68, 0x6f, 0x74, 0x48, 0x65, 0x6c, 0x6d, 0x49, 0x6e, 0x66, 0x6f, 0x22, 0xe5, 0x01, - 0x0a, 0x23, 0x47, 0x61, 0x6c, 0x61, 0x78, 0x79, 0x47, 0x65, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x6c, - 0x6f, 0x61, 0x64, 0x50, 0x65, 0x72, 0x73, 0x70, 0x65, 0x63, 0x74, 0x69, 0x76, 0x65, 0x52, 0x65, + 0x63, 0x6c, 0x75, 0x64, 0x65, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x22, 0x77, 0x0a, 0x1b, + 0x47, 0x65, 0x74, 0x41, 0x6c, 0x6c, 0x57, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x4e, 0x61, + 0x6d, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x58, 0x0a, 0x1d, 0x63, + 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x5f, 0x77, 0x69, 0x74, 0x68, 0x5f, 0x77, + 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x03, + 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6c, 0x75, 0x73, + 0x74, 0x65, 0x72, 0x45, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x19, 0x63, 0x6c, 0x75, 0x73, + 0x74, 0x65, 0x72, 0x49, 0x64, 0x57, 0x69, 0x74, 0x68, 0x57, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, + 0x64, 0x4e, 0x61, 0x6d, 0x65, 0x22, 0xb1, 0x02, 0x0a, 0x1b, 0x47, 0x65, 0x74, 0x41, 0x6c, 0x6c, + 0x57, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x17, 0x0a, 0x07, 0x74, 0x65, 0x61, 0x6d, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x74, 0x65, 0x61, 0x6d, 0x49, 0x64, 0x12, 0x1f, - 0x0a, 0x0b, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x02, 0x20, + 0x0a, 0x0b, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0a, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, 0x64, 0x73, 0x12, - 0x3b, 0x0a, 0x08, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x62, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, - 0x0e, 0x32, 0x20, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x61, 0x6c, 0x61, 0x78, - 0x79, 0x50, 0x4f, 0x56, 0x57, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x47, 0x72, 0x6f, 0x75, - 0x70, 0x42, 0x79, 0x52, 0x07, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x42, 0x79, 0x12, 0x47, 0x0a, 0x0e, - 0x64, 0x61, 0x74, 0x61, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x5f, 0x6f, 0x70, 0x74, 0x73, 0x18, 0x04, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x65, - 0x72, 0x73, 0x70, 0x65, 0x63, 0x74, 0x69, 0x76, 0x65, 0x44, 0x61, 0x74, 0x61, 0x70, 0x6f, 0x69, - 0x6e, 0x74, 0x4f, 0x70, 0x74, 0x73, 0x52, 0x0d, 0x64, 0x61, 0x74, 0x61, 0x70, 0x6f, 0x69, 0x6e, - 0x74, 0x4f, 0x70, 0x74, 0x73, 0x22, 0xb6, 0x03, 0x0a, 0x24, 0x47, 0x61, 0x6c, 0x61, 0x78, 0x79, - 0x47, 0x65, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x50, 0x65, 0x72, 0x73, 0x70, - 0x65, 0x63, 0x74, 0x69, 0x76, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x39, - 0x0a, 0x09, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, - 0x0b, 0x32, 0x1b, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x61, 0x6c, 0x61, 0x78, - 0x79, 0x57, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x52, 0x09, - 0x67, 0x72, 0x6f, 0x75, 0x70, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x56, 0x0a, 0x08, 0x63, 0x6c, 0x75, - 0x73, 0x74, 0x65, 0x72, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x3a, 0x2e, 0x61, 0x70, - 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x61, 0x6c, 0x61, 0x78, 0x79, 0x47, 0x65, 0x74, 0x57, 0x6f, - 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x50, 0x65, 0x72, 0x73, 0x70, 0x65, 0x63, 0x74, 0x69, 0x76, - 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, - 0x72, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x08, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, - 0x73, 0x12, 0x59, 0x0a, 0x09, 0x77, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x73, 0x18, 0x03, - 0x20, 0x03, 0x28, 0x0b, 0x32, 0x3b, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x61, + 0x1e, 0x0a, 0x0a, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x18, 0x06, 0x20, + 0x03, 0x28, 0x09, 0x52, 0x0a, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x12, + 0x2b, 0x0a, 0x05, 0x6b, 0x69, 0x6e, 0x64, 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, 0x0e, 0x32, 0x15, + 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4b, 0x38, 0x73, 0x4f, 0x62, 0x6a, 0x65, 0x63, + 0x74, 0x4b, 0x69, 0x6e, 0x64, 0x52, 0x05, 0x6b, 0x69, 0x6e, 0x64, 0x73, 0x12, 0x25, 0x0a, 0x0e, + 0x77, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x18, 0x08, + 0x20, 0x03, 0x28, 0x09, 0x52, 0x0d, 0x77, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x4e, 0x61, + 0x6d, 0x65, 0x73, 0x12, 0x28, 0x0a, 0x10, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x67, 0x72, 0x6f, 0x75, + 0x70, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x18, 0x09, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0e, 0x6e, + 0x6f, 0x64, 0x65, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x12, 0x1d, 0x0a, + 0x0a, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x0b, 0x20, 0x01, 0x28, + 0x03, 0x52, 0x09, 0x73, 0x74, 0x61, 0x72, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x1b, 0x0a, 0x09, + 0x73, 0x74, 0x6f, 0x70, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x03, 0x52, + 0x08, 0x73, 0x74, 0x6f, 0x70, 0x54, 0x69, 0x6d, 0x65, 0x22, 0x6b, 0x0a, 0x1c, 0x47, 0x65, 0x74, + 0x41, 0x6c, 0x6c, 0x57, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x4c, 0x61, 0x62, 0x65, 0x6c, + 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4b, 0x0a, 0x16, 0x63, 0x6c, 0x75, + 0x73, 0x74, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x5f, 0x77, 0x69, 0x74, 0x68, 0x5f, 0x6c, 0x61, 0x62, + 0x65, 0x6c, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x61, 0x70, 0x69, 0x2e, + 0x76, 0x31, 0x2e, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x45, 0x6c, 0x65, 0x6d, 0x65, 0x6e, + 0x74, 0x52, 0x13, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, 0x64, 0x57, 0x69, 0x74, 0x68, + 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x22, 0xbc, 0x01, 0x0a, 0x1b, 0x47, 0x65, 0x74, 0x41, 0x6c, + 0x6c, 0x4e, 0x6f, 0x64, 0x65, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x17, 0x0a, 0x07, 0x74, 0x65, 0x61, 0x6d, 0x5f, 0x69, + 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x74, 0x65, 0x61, 0x6d, 0x49, 0x64, 0x12, + 0x1f, 0x0a, 0x0b, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x05, + 0x20, 0x03, 0x28, 0x09, 0x52, 0x0a, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, 0x64, 0x73, + 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x0b, + 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x73, 0x74, 0x61, 0x72, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x12, + 0x1b, 0x0a, 0x09, 0x73, 0x74, 0x6f, 0x70, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x0c, 0x20, 0x01, + 0x28, 0x03, 0x52, 0x08, 0x73, 0x74, 0x6f, 0x70, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x27, 0x0a, 0x0f, + 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x5f, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x18, + 0x15, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0e, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x44, 0x65, + 0x6c, 0x65, 0x74, 0x65, 0x64, 0x22, 0x7d, 0x0a, 0x1c, 0x47, 0x65, 0x74, 0x41, 0x6c, 0x6c, 0x4e, + 0x6f, 0x64, 0x65, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x5d, 0x0a, 0x20, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, + 0x5f, 0x69, 0x64, 0x5f, 0x77, 0x69, 0x74, 0x68, 0x5f, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x67, 0x72, + 0x6f, 0x75, 0x70, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, + 0x16, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, + 0x45, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x1b, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, + 0x49, 0x64, 0x57, 0x69, 0x74, 0x68, 0x4e, 0x6f, 0x64, 0x65, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4e, + 0x61, 0x6d, 0x65, 0x73, 0x22, 0xd3, 0x0d, 0x0a, 0x07, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, + 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, + 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, + 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x17, 0x0a, 0x07, 0x74, 0x65, 0x61, 0x6d, 0x5f, 0x69, 0x64, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x74, 0x65, 0x61, 0x6d, 0x49, 0x64, 0x12, 0x2a, 0x0a, + 0x11, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x5f, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x5f, + 0x69, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x50, + 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x49, 0x64, 0x12, 0x1b, 0x0a, 0x09, 0x72, 0x65, 0x67, + 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x72, 0x65, + 0x67, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x25, 0x0a, 0x0e, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x5f, + 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, + 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x12, 0x16, 0x0a, + 0x06, 0x72, 0x65, 0x67, 0x69, 0x6f, 0x6e, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x72, + 0x65, 0x67, 0x69, 0x6f, 0x6e, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, + 0x5f, 0x61, 0x74, 0x18, 0x08, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x63, 0x72, 0x65, 0x61, 0x74, + 0x65, 0x64, 0x41, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x5f, + 0x61, 0x74, 0x18, 0x09, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, + 0x64, 0x41, 0x74, 0x12, 0x42, 0x0a, 0x10, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, + 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, + 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x4d, + 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x52, 0x0f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, + 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x12, 0x2d, 0x0a, 0x09, 0x63, 0x6f, 0x73, 0x74, 0x5f, + 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x61, 0x70, 0x69, + 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x73, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x08, 0x63, 0x6f, + 0x73, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x2d, 0x0a, 0x09, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x69, + 0x6e, 0x66, 0x6f, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x61, 0x70, 0x69, 0x2e, + 0x76, 0x31, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x08, 0x6e, 0x6f, 0x64, + 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x3f, 0x0a, 0x10, 0x63, 0x6f, 0x73, 0x74, 0x5f, 0x64, 0x61, + 0x74, 0x61, 0x5f, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x18, 0x0d, 0x20, 0x03, 0x28, 0x0b, 0x32, + 0x15, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x73, 0x74, 0x44, 0x61, 0x74, + 0x61, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x52, 0x0e, 0x63, 0x6f, 0x73, 0x74, 0x44, 0x61, 0x74, 0x61, + 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x12, 0x4b, 0x0a, 0x14, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, + 0x63, 0x65, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x5f, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x18, 0x0e, + 0x20, 0x03, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, + 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x44, 0x61, 0x74, 0x61, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x52, + 0x12, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x44, 0x61, 0x74, 0x61, 0x50, 0x6f, 0x69, + 0x6e, 0x74, 0x73, 0x12, 0x3c, 0x0a, 0x13, 0x6d, 0x6f, 0x73, 0x74, 0x5f, 0x65, 0x78, 0x70, 0x65, + 0x6e, 0x73, 0x69, 0x76, 0x65, 0x5f, 0x6e, 0x6f, 0x64, 0x65, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x0c, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x11, + 0x6d, 0x6f, 0x73, 0x74, 0x45, 0x78, 0x70, 0x65, 0x6e, 0x73, 0x69, 0x76, 0x65, 0x4e, 0x6f, 0x64, + 0x65, 0x12, 0x3e, 0x0a, 0x14, 0x6c, 0x65, 0x61, 0x73, 0x74, 0x5f, 0x65, 0x78, 0x70, 0x65, 0x6e, + 0x73, 0x69, 0x76, 0x65, 0x5f, 0x6e, 0x6f, 0x64, 0x65, 0x18, 0x10, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x0c, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x12, 0x6c, + 0x65, 0x61, 0x73, 0x74, 0x45, 0x78, 0x70, 0x65, 0x6e, 0x73, 0x69, 0x76, 0x65, 0x4e, 0x6f, 0x64, + 0x65, 0x12, 0x44, 0x0a, 0x17, 0x6d, 0x6f, 0x73, 0x74, 0x5f, 0x75, 0x6e, 0x64, 0x65, 0x72, 0x75, + 0x74, 0x69, 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x5f, 0x6e, 0x6f, 0x64, 0x65, 0x18, 0x11, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4e, 0x6f, 0x64, 0x65, + 0x52, 0x15, 0x6d, 0x6f, 0x73, 0x74, 0x55, 0x6e, 0x64, 0x65, 0x72, 0x75, 0x74, 0x69, 0x6c, 0x69, + 0x7a, 0x65, 0x64, 0x4e, 0x6f, 0x64, 0x65, 0x12, 0x53, 0x0a, 0x1c, 0x6d, 0x6f, 0x73, 0x74, 0x5f, + 0x75, 0x6e, 0x64, 0x65, 0x72, 0x75, 0x74, 0x69, 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x5f, 0x63, 0x6f, + 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x18, 0x12, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, + 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, + 0x52, 0x1a, 0x6d, 0x6f, 0x73, 0x74, 0x55, 0x6e, 0x64, 0x65, 0x72, 0x75, 0x74, 0x69, 0x6c, 0x69, + 0x7a, 0x65, 0x64, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x12, 0x29, 0x0a, 0x11, + 0x63, 0x70, 0x75, 0x5f, 0x63, 0x6f, 0x73, 0x74, 0x5f, 0x70, 0x65, 0x72, 0x5f, 0x68, 0x6f, 0x75, + 0x72, 0x18, 0x13, 0x20, 0x01, 0x28, 0x01, 0x52, 0x0e, 0x63, 0x70, 0x75, 0x43, 0x6f, 0x73, 0x74, + 0x50, 0x65, 0x72, 0x48, 0x6f, 0x75, 0x72, 0x12, 0x2f, 0x0a, 0x14, 0x6d, 0x65, 0x6d, 0x6f, 0x72, + 0x79, 0x5f, 0x63, 0x6f, 0x73, 0x74, 0x5f, 0x70, 0x65, 0x72, 0x5f, 0x68, 0x6f, 0x75, 0x72, 0x18, + 0x14, 0x20, 0x01, 0x28, 0x01, 0x52, 0x11, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x43, 0x6f, 0x73, + 0x74, 0x50, 0x65, 0x72, 0x48, 0x6f, 0x75, 0x72, 0x12, 0x28, 0x0a, 0x10, 0x68, 0x61, 0x73, 0x5f, + 0x62, 0x65, 0x65, 0x6e, 0x5f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x18, 0x15, 0x20, 0x01, + 0x28, 0x08, 0x52, 0x0e, 0x68, 0x61, 0x73, 0x42, 0x65, 0x65, 0x6e, 0x55, 0x70, 0x64, 0x61, 0x74, + 0x65, 0x64, 0x12, 0x1f, 0x0a, 0x0b, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x5f, 0x6e, 0x61, 0x6d, + 0x65, 0x18, 0x16, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x4e, + 0x61, 0x6d, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x5f, 0x6e, + 0x61, 0x6d, 0x65, 0x18, 0x17, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x69, 0x73, 0x70, 0x6c, + 0x61, 0x79, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x2c, 0x0a, 0x12, 0x69, 0x73, 0x5f, 0x70, 0x72, 0x69, + 0x63, 0x65, 0x5f, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x18, 0x18, 0x20, 0x01, + 0x28, 0x08, 0x52, 0x10, 0x69, 0x73, 0x50, 0x72, 0x69, 0x63, 0x65, 0x41, 0x76, 0x61, 0x69, 0x6c, + 0x61, 0x62, 0x6c, 0x65, 0x12, 0x29, 0x0a, 0x11, 0x67, 0x70, 0x75, 0x5f, 0x63, 0x6f, 0x73, 0x74, + 0x5f, 0x70, 0x65, 0x72, 0x5f, 0x68, 0x6f, 0x75, 0x72, 0x18, 0x19, 0x20, 0x01, 0x28, 0x01, 0x52, + 0x0e, 0x67, 0x70, 0x75, 0x43, 0x6f, 0x73, 0x74, 0x50, 0x65, 0x72, 0x48, 0x6f, 0x75, 0x72, 0x12, + 0x2c, 0x0a, 0x0f, 0x69, 0x73, 0x5f, 0x64, 0x69, 0x73, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, + 0x65, 0x64, 0x18, 0x28, 0x20, 0x01, 0x28, 0x08, 0x48, 0x00, 0x52, 0x0e, 0x69, 0x73, 0x44, 0x69, + 0x73, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x65, 0x64, 0x88, 0x01, 0x01, 0x12, 0x2f, 0x0a, + 0x08, 0x7a, 0x78, 0x70, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x32, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x14, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, + 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x07, 0x7a, 0x78, 0x70, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x38, + 0x0a, 0x0d, 0x7a, 0x78, 0x70, 0x5f, 0x68, 0x65, 0x6c, 0x6d, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18, + 0x33, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4f, + 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x0b, 0x7a, 0x78, 0x70, + 0x48, 0x65, 0x6c, 0x6d, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x36, 0x0a, 0x0c, 0x64, 0x61, 0x6b, 0x72, + 0x5f, 0x6f, 0x70, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x3c, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, + 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, + 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x0a, 0x64, 0x61, 0x6b, 0x72, 0x4f, 0x70, 0x49, 0x6e, 0x66, 0x6f, + 0x12, 0x36, 0x0a, 0x0c, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x6f, 0x70, 0x5f, 0x69, 0x6e, 0x66, 0x6f, + 0x18, 0x3d, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, + 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x0a, 0x6e, 0x6f, + 0x64, 0x65, 0x4f, 0x70, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x3e, 0x0a, 0x10, 0x73, 0x65, 0x63, 0x75, + 0x72, 0x69, 0x74, 0x79, 0x5f, 0x6f, 0x70, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x3e, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x70, 0x65, 0x72, + 0x61, 0x74, 0x6f, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x0e, 0x73, 0x65, 0x63, 0x75, 0x72, 0x69, + 0x74, 0x79, 0x4f, 0x70, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x3c, 0x0a, 0x0f, 0x6e, 0x65, 0x74, 0x77, + 0x6f, 0x72, 0x6b, 0x5f, 0x6f, 0x70, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x3f, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x14, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x70, 0x65, 0x72, 0x61, + 0x74, 0x6f, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x0d, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, + 0x4f, 0x70, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x21, 0x0a, 0x0c, 0x69, 0x6d, 0x70, 0x61, 0x63, 0x74, + 0x5f, 0x73, 0x63, 0x6f, 0x72, 0x65, 0x18, 0x46, 0x20, 0x01, 0x28, 0x01, 0x52, 0x0b, 0x69, 0x6d, + 0x70, 0x61, 0x63, 0x74, 0x53, 0x63, 0x6f, 0x72, 0x65, 0x12, 0x2a, 0x0a, 0x10, 0x75, 0x6e, 0x64, + 0x65, 0x72, 0x75, 0x74, 0x69, 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x51, 0x20, + 0x01, 0x28, 0x01, 0x52, 0x10, 0x75, 0x6e, 0x64, 0x65, 0x72, 0x75, 0x74, 0x69, 0x6c, 0x69, 0x7a, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x61, 0x67, 0x73, 0x18, 0x5b, 0x20, + 0x03, 0x28, 0x09, 0x52, 0x04, 0x74, 0x61, 0x67, 0x73, 0x12, 0x2c, 0x0a, 0x12, 0x68, 0x61, 0x73, + 0x5f, 0x61, 0x72, 0x67, 0x6f, 0x5f, 0x77, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x73, 0x18, + 0x65, 0x20, 0x01, 0x28, 0x08, 0x52, 0x10, 0x68, 0x61, 0x73, 0x41, 0x72, 0x67, 0x6f, 0x57, 0x6f, + 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x73, 0x12, 0x2d, 0x0a, 0x12, 0x6b, 0x75, 0x62, 0x65, 0x72, + 0x6e, 0x65, 0x74, 0x65, 0x73, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x6e, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x11, 0x6b, 0x75, 0x62, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x65, 0x73, 0x56, + 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x42, 0x12, 0x0a, 0x10, 0x5f, 0x69, 0x73, 0x5f, 0x64, 0x69, + 0x73, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x65, 0x64, 0x22, 0x54, 0x0a, 0x0c, 0x4f, 0x70, + 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, + 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x76, 0x65, 0x72, + 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x16, 0x0a, 0x06, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x18, 0x0b, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x12, 0x12, 0x0a, 0x04, + 0x64, 0x61, 0x74, 0x65, 0x18, 0x15, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x64, 0x61, 0x74, 0x65, + 0x22, 0x73, 0x0a, 0x09, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x12, 0x0e, 0x0a, + 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x12, 0x0a, + 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, + 0x65, 0x12, 0x42, 0x0a, 0x10, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x6d, 0x65, + 0x74, 0x72, 0x69, 0x63, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x61, 0x70, + 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x4d, 0x65, 0x74, + 0x72, 0x69, 0x63, 0x73, 0x52, 0x0f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x4d, 0x65, + 0x74, 0x72, 0x69, 0x63, 0x73, 0x22, 0x21, 0x0a, 0x1f, 0x47, 0x65, 0x74, 0x4c, 0x61, 0x74, 0x65, + 0x73, 0x74, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, + 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0xcb, 0x07, 0x0a, 0x20, 0x47, 0x65, 0x74, + 0x4c, 0x61, 0x74, 0x65, 0x73, 0x74, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x56, 0x65, + 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2f, 0x0a, + 0x08, 0x7a, 0x78, 0x70, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x14, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, + 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x07, 0x7a, 0x78, 0x70, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x38, + 0x0a, 0x0d, 0x7a, 0x78, 0x70, 0x5f, 0x68, 0x65, 0x6c, 0x6d, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4f, + 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x0b, 0x7a, 0x78, 0x70, + 0x48, 0x65, 0x6c, 0x6d, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x3c, 0x0a, 0x0f, 0x7a, 0x78, 0x70, 0x5f, + 0x6e, 0x65, 0x74, 0x6d, 0x6f, 0x6e, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x14, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x70, 0x65, 0x72, 0x61, + 0x74, 0x6f, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x0d, 0x7a, 0x78, 0x70, 0x4e, 0x65, 0x74, 0x6d, + 0x6f, 0x6e, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x45, 0x0a, 0x14, 0x7a, 0x78, 0x70, 0x5f, 0x6e, 0x65, + 0x74, 0x6d, 0x6f, 0x6e, 0x5f, 0x68, 0x65, 0x6c, 0x6d, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x04, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x70, + 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x11, 0x7a, 0x78, 0x70, 0x4e, + 0x65, 0x74, 0x6d, 0x6f, 0x6e, 0x48, 0x65, 0x6c, 0x6d, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x36, 0x0a, + 0x0c, 0x64, 0x61, 0x6b, 0x72, 0x5f, 0x6f, 0x70, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x0b, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x70, 0x65, + 0x72, 0x61, 0x74, 0x6f, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x0a, 0x64, 0x61, 0x6b, 0x72, 0x4f, + 0x70, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x3f, 0x0a, 0x11, 0x64, 0x61, 0x6b, 0x72, 0x5f, 0x6f, 0x70, + 0x5f, 0x68, 0x65, 0x6c, 0x6d, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x14, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, + 0x6f, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x0e, 0x64, 0x61, 0x6b, 0x72, 0x4f, 0x70, 0x48, 0x65, + 0x6c, 0x6d, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x3d, 0x0a, 0x10, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x6f, + 0x70, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x5f, 0x61, 0x77, 0x73, 0x18, 0x15, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x14, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, + 0x6f, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x0d, 0x6e, 0x6f, 0x64, 0x65, 0x4f, 0x70, 0x49, 0x6e, + 0x66, 0x6f, 0x41, 0x77, 0x73, 0x12, 0x41, 0x0a, 0x12, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x6f, 0x70, + 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x5f, 0x61, 0x7a, 0x75, 0x72, 0x65, 0x18, 0x16, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x14, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x70, 0x65, 0x72, 0x61, + 0x74, 0x6f, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x0f, 0x6e, 0x6f, 0x64, 0x65, 0x4f, 0x70, 0x49, + 0x6e, 0x66, 0x6f, 0x41, 0x7a, 0x75, 0x72, 0x65, 0x12, 0x3d, 0x0a, 0x10, 0x6e, 0x6f, 0x64, 0x65, + 0x5f, 0x6f, 0x70, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x5f, 0x67, 0x63, 0x70, 0x18, 0x17, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x70, 0x65, 0x72, + 0x61, 0x74, 0x6f, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x0d, 0x6e, 0x6f, 0x64, 0x65, 0x4f, 0x70, + 0x49, 0x6e, 0x66, 0x6f, 0x47, 0x63, 0x70, 0x12, 0x3d, 0x0a, 0x10, 0x6e, 0x6f, 0x64, 0x65, 0x5f, + 0x6f, 0x70, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x5f, 0x6f, 0x63, 0x69, 0x18, 0x18, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x14, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x70, 0x65, 0x72, 0x61, + 0x74, 0x6f, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x0d, 0x6e, 0x6f, 0x64, 0x65, 0x4f, 0x70, 0x49, + 0x6e, 0x66, 0x6f, 0x4f, 0x63, 0x69, 0x12, 0x3e, 0x0a, 0x10, 0x73, 0x65, 0x63, 0x75, 0x72, 0x69, + 0x74, 0x79, 0x5f, 0x6f, 0x70, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x1f, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x14, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, + 0x6f, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x0e, 0x73, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, + 0x4f, 0x70, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x47, 0x0a, 0x15, 0x73, 0x65, 0x63, 0x75, 0x72, 0x69, + 0x74, 0x79, 0x5f, 0x6f, 0x70, 0x5f, 0x68, 0x65, 0x6c, 0x6d, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18, + 0x20, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4f, + 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x12, 0x73, 0x65, 0x63, + 0x75, 0x72, 0x69, 0x74, 0x79, 0x4f, 0x70, 0x48, 0x65, 0x6c, 0x6d, 0x49, 0x6e, 0x66, 0x6f, 0x12, + 0x33, 0x0a, 0x0a, 0x74, 0x72, 0x65, 0x7a, 0x72, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x29, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x70, 0x65, + 0x72, 0x61, 0x74, 0x6f, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x09, 0x74, 0x72, 0x65, 0x7a, 0x72, + 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x3c, 0x0a, 0x0f, 0x74, 0x72, 0x65, 0x7a, 0x72, 0x5f, 0x68, 0x65, + 0x6c, 0x6d, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x2a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, + 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x49, + 0x6e, 0x66, 0x6f, 0x52, 0x0d, 0x74, 0x72, 0x65, 0x7a, 0x72, 0x48, 0x65, 0x6c, 0x6d, 0x49, 0x6e, + 0x66, 0x6f, 0x12, 0x42, 0x0a, 0x12, 0x73, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x5f, 0x68, + 0x65, 0x6c, 0x6d, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x33, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, + 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, + 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x10, 0x73, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x48, 0x65, + 0x6c, 0x6d, 0x49, 0x6e, 0x66, 0x6f, 0x22, 0xe5, 0x01, 0x0a, 0x23, 0x47, 0x61, 0x6c, 0x61, 0x78, + 0x79, 0x47, 0x65, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x50, 0x65, 0x72, 0x73, + 0x70, 0x65, 0x63, 0x74, 0x69, 0x76, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x17, + 0x0a, 0x07, 0x74, 0x65, 0x61, 0x6d, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x06, 0x74, 0x65, 0x61, 0x6d, 0x49, 0x64, 0x12, 0x1f, 0x0a, 0x0b, 0x63, 0x6c, 0x75, 0x73, 0x74, + 0x65, 0x72, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0a, 0x63, 0x6c, + 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, 0x64, 0x73, 0x12, 0x3b, 0x0a, 0x08, 0x67, 0x72, 0x6f, 0x75, + 0x70, 0x5f, 0x62, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x20, 0x2e, 0x61, 0x70, 0x69, + 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x61, 0x6c, 0x61, 0x78, 0x79, 0x50, 0x4f, 0x56, 0x57, 0x6f, 0x72, + 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x42, 0x79, 0x52, 0x07, 0x67, 0x72, + 0x6f, 0x75, 0x70, 0x42, 0x79, 0x12, 0x47, 0x0a, 0x0e, 0x64, 0x61, 0x74, 0x61, 0x70, 0x6f, 0x69, + 0x6e, 0x74, 0x5f, 0x6f, 0x70, 0x74, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, + 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x65, 0x72, 0x73, 0x70, 0x65, 0x63, 0x74, 0x69, + 0x76, 0x65, 0x44, 0x61, 0x74, 0x61, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x4f, 0x70, 0x74, 0x73, 0x52, + 0x0d, 0x64, 0x61, 0x74, 0x61, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x4f, 0x70, 0x74, 0x73, 0x22, 0xb6, + 0x03, 0x0a, 0x24, 0x47, 0x61, 0x6c, 0x61, 0x78, 0x79, 0x47, 0x65, 0x74, 0x57, 0x6f, 0x72, 0x6b, + 0x6c, 0x6f, 0x61, 0x64, 0x50, 0x65, 0x72, 0x73, 0x70, 0x65, 0x63, 0x74, 0x69, 0x76, 0x65, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x39, 0x0a, 0x09, 0x67, 0x72, 0x6f, 0x75, 0x70, + 0x69, 0x6e, 0x67, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x61, 0x70, 0x69, + 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x61, 0x6c, 0x61, 0x78, 0x79, 0x57, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, + 0x61, 0x64, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x52, 0x09, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x69, 0x6e, + 0x67, 0x73, 0x12, 0x56, 0x0a, 0x08, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x73, 0x18, 0x02, + 0x20, 0x03, 0x28, 0x0b, 0x32, 0x3a, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x61, 0x6c, 0x61, 0x78, 0x79, 0x47, 0x65, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x50, 0x65, 0x72, 0x73, 0x70, 0x65, 0x63, 0x74, 0x69, 0x76, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x73, 0x45, 0x6e, 0x74, 0x72, - 0x79, 0x52, 0x09, 0x77, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x73, 0x1a, 0x4c, 0x0a, 0x0d, - 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, - 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, - 0x25, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, - 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x52, - 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x52, 0x0a, 0x0e, 0x57, 0x6f, - 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, - 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x2a, - 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, - 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x49, - 0x74, 0x65, 0x6d, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0xaf, - 0x02, 0x0a, 0x13, 0x47, 0x61, 0x6c, 0x61, 0x78, 0x79, 0x57, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, - 0x64, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x12, 0x1b, 0x0a, 0x09, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, - 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x67, 0x72, 0x6f, 0x75, 0x70, - 0x4b, 0x65, 0x79, 0x12, 0x23, 0x0a, 0x0d, 0x77, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x5f, - 0x75, 0x69, 0x64, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0c, 0x77, 0x6f, 0x72, 0x6b, - 0x6c, 0x6f, 0x61, 0x64, 0x55, 0x69, 0x64, 0x73, 0x12, 0x1f, 0x0a, 0x0b, 0x63, 0x6c, 0x75, 0x73, - 0x74, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0a, 0x63, - 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, 0x64, 0x73, 0x12, 0x2d, 0x0a, 0x09, 0x63, 0x6f, 0x73, - 0x74, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x61, - 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x73, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x08, - 0x63, 0x6f, 0x73, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x42, 0x0a, 0x10, 0x72, 0x65, 0x73, 0x6f, - 0x75, 0x72, 0x63, 0x65, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x18, 0x05, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x73, 0x6f, - 0x75, 0x72, 0x63, 0x65, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x52, 0x0f, 0x72, 0x65, 0x73, - 0x6f, 0x75, 0x72, 0x63, 0x65, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x12, 0x42, 0x0a, 0x10, - 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x73, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, - 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, - 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x52, - 0x0f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, - 0x22, 0x98, 0x01, 0x0a, 0x18, 0x50, 0x65, 0x72, 0x73, 0x70, 0x65, 0x63, 0x74, 0x69, 0x76, 0x65, - 0x44, 0x61, 0x74, 0x61, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x4f, 0x70, 0x74, 0x73, 0x12, 0x1d, 0x0a, - 0x0a, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x73, 0x74, 0x61, 0x72, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x03, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x53, 0x74, 0x61, 0x72, 0x74, 0x12, 0x1b, 0x0a, 0x09, - 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x73, 0x74, 0x6f, 0x70, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, - 0x08, 0x74, 0x69, 0x6d, 0x65, 0x53, 0x74, 0x6f, 0x70, 0x12, 0x40, 0x0a, 0x1d, 0x6e, 0x75, 0x6d, - 0x5f, 0x64, 0x61, 0x74, 0x61, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x5f, 0x69, 0x6e, 0x5f, 0x74, - 0x69, 0x6d, 0x65, 0x5f, 0x70, 0x65, 0x72, 0x69, 0x6f, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, - 0x52, 0x19, 0x6e, 0x75, 0x6d, 0x44, 0x61, 0x74, 0x61, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x49, - 0x6e, 0x54, 0x69, 0x6d, 0x65, 0x50, 0x65, 0x72, 0x69, 0x6f, 0x64, 0x22, 0xa3, 0x05, 0x0a, 0x14, - 0x4c, 0x69, 0x73, 0x74, 0x41, 0x75, 0x64, 0x69, 0x74, 0x4c, 0x6f, 0x67, 0x73, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x12, 0x17, 0x0a, 0x07, 0x74, 0x65, 0x61, 0x6d, 0x5f, 0x69, 0x64, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x74, 0x65, 0x61, 0x6d, 0x49, 0x64, 0x12, 0x1d, 0x0a, - 0x0a, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x03, 0x28, - 0x09, 0x52, 0x09, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, 0x64, 0x12, 0x17, 0x0a, 0x07, - 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x06, 0x6e, - 0x6f, 0x64, 0x65, 0x49, 0x64, 0x12, 0x1f, 0x0a, 0x0b, 0x77, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, - 0x64, 0x5f, 0x69, 0x64, 0x18, 0x04, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0a, 0x77, 0x6f, 0x72, 0x6b, - 0x6c, 0x6f, 0x61, 0x64, 0x49, 0x64, 0x12, 0x3a, 0x0a, 0x0d, 0x77, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, - 0x61, 0x64, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0e, 0x32, 0x15, 0x2e, - 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4b, 0x38, 0x73, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, - 0x4b, 0x69, 0x6e, 0x64, 0x52, 0x0c, 0x77, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x54, 0x79, - 0x70, 0x65, 0x12, 0x38, 0x0a, 0x18, 0x72, 0x65, 0x63, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x64, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x5f, 0x69, 0x64, 0x18, 0x06, - 0x20, 0x03, 0x28, 0x09, 0x52, 0x16, 0x72, 0x65, 0x63, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x64, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x49, 0x64, 0x12, 0x2b, 0x0a, 0x11, - 0x72, 0x65, 0x63, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69, - 0x64, 0x18, 0x07, 0x20, 0x03, 0x28, 0x09, 0x52, 0x10, 0x72, 0x65, 0x63, 0x6f, 0x6d, 0x6d, 0x65, - 0x6e, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x2e, 0x0a, 0x13, 0x6f, 0x72, 0x69, - 0x67, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6e, 0x67, 0x5f, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x69, 0x64, - 0x18, 0x08, 0x20, 0x03, 0x28, 0x09, 0x52, 0x11, 0x6f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x61, 0x74, - 0x69, 0x6e, 0x67, 0x55, 0x73, 0x65, 0x72, 0x49, 0x64, 0x12, 0x2e, 0x0a, 0x0e, 0x65, 0x6d, 0x61, - 0x69, 0x6c, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x73, 0x18, 0x09, 0x20, 0x01, 0x28, - 0x09, 0x42, 0x02, 0x18, 0x01, 0x48, 0x00, 0x52, 0x0d, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x43, 0x6f, - 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x73, 0x88, 0x01, 0x01, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x76, 0x65, - 0x6e, 0x74, 0x18, 0x0a, 0x20, 0x03, 0x28, 0x09, 0x52, 0x05, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x12, - 0x39, 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x0b, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, - 0x09, 0x73, 0x74, 0x61, 0x72, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x35, 0x0a, 0x08, 0x65, 0x6e, - 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, + 0x73, 0x65, 0x2e, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, + 0x52, 0x08, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x73, 0x12, 0x59, 0x0a, 0x09, 0x77, 0x6f, + 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x3b, 0x2e, + 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x61, 0x6c, 0x61, 0x78, 0x79, 0x47, 0x65, 0x74, + 0x57, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x50, 0x65, 0x72, 0x73, 0x70, 0x65, 0x63, 0x74, + 0x69, 0x76, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x57, 0x6f, 0x72, 0x6b, + 0x6c, 0x6f, 0x61, 0x64, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x09, 0x77, 0x6f, 0x72, 0x6b, + 0x6c, 0x6f, 0x61, 0x64, 0x73, 0x1a, 0x4c, 0x0a, 0x0d, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, + 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x25, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, + 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, + 0x2e, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, + 0x02, 0x38, 0x01, 0x1a, 0x52, 0x0a, 0x0e, 0x57, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x73, + 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x2a, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, + 0x57, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x49, 0x74, 0x65, 0x6d, 0x52, 0x05, 0x76, 0x61, + 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0xaf, 0x02, 0x0a, 0x13, 0x47, 0x61, 0x6c, 0x61, + 0x78, 0x79, 0x57, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x12, + 0x1b, 0x0a, 0x09, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x08, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x4b, 0x65, 0x79, 0x12, 0x23, 0x0a, 0x0d, + 0x77, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x5f, 0x75, 0x69, 0x64, 0x73, 0x18, 0x02, 0x20, + 0x03, 0x28, 0x09, 0x52, 0x0c, 0x77, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x55, 0x69, 0x64, + 0x73, 0x12, 0x1f, 0x0a, 0x0b, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x73, + 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0a, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, + 0x64, 0x73, 0x12, 0x2d, 0x0a, 0x09, 0x63, 0x6f, 0x73, 0x74, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18, + 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x43, + 0x6f, 0x73, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x08, 0x63, 0x6f, 0x73, 0x74, 0x49, 0x6e, 0x66, + 0x6f, 0x12, 0x42, 0x0a, 0x10, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x6d, 0x65, + 0x74, 0x72, 0x69, 0x63, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x61, 0x70, + 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x4d, 0x65, 0x74, + 0x72, 0x69, 0x63, 0x73, 0x52, 0x0f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x4d, 0x65, + 0x74, 0x72, 0x69, 0x63, 0x73, 0x12, 0x42, 0x0a, 0x10, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, + 0x65, 0x5f, 0x73, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x17, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, + 0x65, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x52, 0x0f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, + 0x63, 0x65, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x22, 0x98, 0x01, 0x0a, 0x18, 0x50, 0x65, + 0x72, 0x73, 0x70, 0x65, 0x63, 0x74, 0x69, 0x76, 0x65, 0x44, 0x61, 0x74, 0x61, 0x70, 0x6f, 0x69, + 0x6e, 0x74, 0x4f, 0x70, 0x74, 0x73, 0x12, 0x1d, 0x0a, 0x0a, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x73, + 0x74, 0x61, 0x72, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, + 0x53, 0x74, 0x61, 0x72, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x73, 0x74, + 0x6f, 0x70, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x08, 0x74, 0x69, 0x6d, 0x65, 0x53, 0x74, + 0x6f, 0x70, 0x12, 0x40, 0x0a, 0x1d, 0x6e, 0x75, 0x6d, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x70, 0x6f, + 0x69, 0x6e, 0x74, 0x73, 0x5f, 0x69, 0x6e, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x70, 0x65, 0x72, + 0x69, 0x6f, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x19, 0x6e, 0x75, 0x6d, 0x44, 0x61, + 0x74, 0x61, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x49, 0x6e, 0x54, 0x69, 0x6d, 0x65, 0x50, 0x65, + 0x72, 0x69, 0x6f, 0x64, 0x22, 0xa3, 0x05, 0x0a, 0x14, 0x4c, 0x69, 0x73, 0x74, 0x41, 0x75, 0x64, + 0x69, 0x74, 0x4c, 0x6f, 0x67, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x17, 0x0a, + 0x07, 0x74, 0x65, 0x61, 0x6d, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, + 0x74, 0x65, 0x61, 0x6d, 0x49, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, + 0x72, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x09, 0x63, 0x6c, 0x75, 0x73, + 0x74, 0x65, 0x72, 0x49, 0x64, 0x12, 0x17, 0x0a, 0x07, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x69, 0x64, + 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x06, 0x6e, 0x6f, 0x64, 0x65, 0x49, 0x64, 0x12, 0x1f, + 0x0a, 0x0b, 0x77, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x5f, 0x69, 0x64, 0x18, 0x04, 0x20, + 0x03, 0x28, 0x09, 0x52, 0x0a, 0x77, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x49, 0x64, 0x12, + 0x3a, 0x0a, 0x0d, 0x77, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x5f, 0x74, 0x79, 0x70, 0x65, + 0x18, 0x05, 0x20, 0x03, 0x28, 0x0e, 0x32, 0x15, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, + 0x4b, 0x38, 0x73, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x4b, 0x69, 0x6e, 0x64, 0x52, 0x0c, 0x77, + 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x54, 0x79, 0x70, 0x65, 0x12, 0x38, 0x0a, 0x18, 0x72, + 0x65, 0x63, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x70, 0x6f, + 0x6c, 0x69, 0x63, 0x79, 0x5f, 0x69, 0x64, 0x18, 0x06, 0x20, 0x03, 0x28, 0x09, 0x52, 0x16, 0x72, + 0x65, 0x63, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x6f, 0x6c, + 0x69, 0x63, 0x79, 0x49, 0x64, 0x12, 0x2b, 0x0a, 0x11, 0x72, 0x65, 0x63, 0x6f, 0x6d, 0x6d, 0x65, + 0x6e, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x07, 0x20, 0x03, 0x28, 0x09, + 0x52, 0x10, 0x72, 0x65, 0x63, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x49, 0x64, 0x12, 0x2e, 0x0a, 0x13, 0x6f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6e, + 0x67, 0x5f, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x08, 0x20, 0x03, 0x28, 0x09, 0x52, + 0x11, 0x6f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6e, 0x67, 0x55, 0x73, 0x65, 0x72, + 0x49, 0x64, 0x12, 0x2e, 0x0a, 0x0e, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x5f, 0x63, 0x6f, 0x6e, 0x74, + 0x61, 0x69, 0x6e, 0x73, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x42, 0x02, 0x18, 0x01, 0x48, 0x00, + 0x52, 0x0d, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x73, 0x88, + 0x01, 0x01, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x18, 0x0a, 0x20, 0x03, 0x28, + 0x09, 0x52, 0x05, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x39, 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x72, + 0x74, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, - 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x07, 0x65, 0x6e, 0x64, 0x54, 0x69, 0x6d, - 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x5f, 0x6d, 0x61, 0x74, 0x63, 0x68, - 0x65, 0x73, 0x18, 0x0d, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0c, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x4d, - 0x61, 0x74, 0x63, 0x68, 0x65, 0x73, 0x12, 0x32, 0x0a, 0x0a, 0x70, 0x61, 0x67, 0x69, 0x6e, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x15, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x61, 0x70, 0x69, - 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x61, 0x67, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0a, - 0x70, 0x61, 0x67, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x22, 0x0a, 0x0d, 0x61, 0x75, - 0x64, 0x69, 0x74, 0x5f, 0x6c, 0x6f, 0x67, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x1f, 0x20, 0x03, 0x28, - 0x09, 0x52, 0x0b, 0x61, 0x75, 0x64, 0x69, 0x74, 0x4c, 0x6f, 0x67, 0x49, 0x64, 0x73, 0x42, 0x11, - 0x0a, 0x0f, 0x5f, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, - 0x73, 0x22, 0x76, 0x0a, 0x15, 0x4c, 0x69, 0x73, 0x74, 0x41, 0x75, 0x64, 0x69, 0x74, 0x4c, 0x6f, - 0x67, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x29, 0x0a, 0x04, 0x6c, 0x6f, - 0x67, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, - 0x31, 0x2e, 0x41, 0x75, 0x64, 0x69, 0x74, 0x4c, 0x6f, 0x67, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, - 0x04, 0x6c, 0x6f, 0x67, 0x73, 0x12, 0x32, 0x0a, 0x0a, 0x70, 0x61, 0x67, 0x69, 0x6e, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x18, 0x15, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x61, 0x70, 0x69, 0x2e, - 0x76, 0x31, 0x2e, 0x50, 0x61, 0x67, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0a, 0x70, - 0x61, 0x67, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0xd5, 0x04, 0x0a, 0x1e, 0x4c, 0x69, + 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x73, 0x74, 0x61, 0x72, 0x74, 0x54, + 0x69, 0x6d, 0x65, 0x12, 0x35, 0x0a, 0x08, 0x65, 0x6e, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, + 0x0c, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, + 0x70, 0x52, 0x07, 0x65, 0x6e, 0x64, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x65, 0x6d, + 0x61, 0x69, 0x6c, 0x5f, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x65, 0x73, 0x18, 0x0d, 0x20, 0x03, 0x28, + 0x09, 0x52, 0x0c, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x4d, 0x61, 0x74, 0x63, 0x68, 0x65, 0x73, 0x12, + 0x32, 0x0a, 0x0a, 0x70, 0x61, 0x67, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x15, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x61, 0x67, + 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0a, 0x70, 0x61, 0x67, 0x69, 0x6e, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x12, 0x22, 0x0a, 0x0d, 0x61, 0x75, 0x64, 0x69, 0x74, 0x5f, 0x6c, 0x6f, 0x67, + 0x5f, 0x69, 0x64, 0x73, 0x18, 0x1f, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0b, 0x61, 0x75, 0x64, 0x69, + 0x74, 0x4c, 0x6f, 0x67, 0x49, 0x64, 0x73, 0x42, 0x11, 0x0a, 0x0f, 0x5f, 0x65, 0x6d, 0x61, 0x69, + 0x6c, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x73, 0x22, 0x76, 0x0a, 0x15, 0x4c, 0x69, + 0x73, 0x74, 0x41, 0x75, 0x64, 0x69, 0x74, 0x4c, 0x6f, 0x67, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x12, 0x29, 0x0a, 0x04, 0x6c, 0x6f, 0x67, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, + 0x0b, 0x32, 0x15, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x75, 0x64, 0x69, 0x74, + 0x4c, 0x6f, 0x67, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x04, 0x6c, 0x6f, 0x67, 0x73, 0x12, 0x32, + 0x0a, 0x0a, 0x70, 0x61, 0x67, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x15, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x61, 0x67, 0x69, + 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0a, 0x70, 0x61, 0x67, 0x69, 0x6e, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x22, 0xd5, 0x04, 0x0a, 0x1e, 0x4c, 0x69, 0x73, 0x74, 0x41, 0x75, 0x64, 0x69, 0x74, + 0x4c, 0x6f, 0x67, 0x4f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x61, 0x74, 0x6f, 0x72, 0x73, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x17, 0x0a, 0x07, 0x74, 0x65, 0x61, 0x6d, 0x5f, 0x69, 0x64, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x74, 0x65, 0x61, 0x6d, 0x49, 0x64, 0x12, 0x1d, + 0x0a, 0x0a, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x03, + 0x28, 0x09, 0x52, 0x09, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, 0x64, 0x12, 0x17, 0x0a, + 0x07, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x06, + 0x6e, 0x6f, 0x64, 0x65, 0x49, 0x64, 0x12, 0x1f, 0x0a, 0x0b, 0x77, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, + 0x61, 0x64, 0x5f, 0x69, 0x64, 0x18, 0x04, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0a, 0x77, 0x6f, 0x72, + 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x49, 0x64, 0x12, 0x3a, 0x0a, 0x0d, 0x77, 0x6f, 0x72, 0x6b, 0x6c, + 0x6f, 0x61, 0x64, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0e, 0x32, 0x15, + 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4b, 0x38, 0x73, 0x4f, 0x62, 0x6a, 0x65, 0x63, + 0x74, 0x4b, 0x69, 0x6e, 0x64, 0x52, 0x0c, 0x77, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x54, + 0x79, 0x70, 0x65, 0x12, 0x38, 0x0a, 0x18, 0x72, 0x65, 0x63, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x64, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x5f, 0x69, 0x64, 0x18, + 0x06, 0x20, 0x03, 0x28, 0x09, 0x52, 0x16, 0x72, 0x65, 0x63, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x64, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x49, 0x64, 0x12, 0x2b, 0x0a, + 0x11, 0x72, 0x65, 0x63, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, + 0x69, 0x64, 0x18, 0x07, 0x20, 0x03, 0x28, 0x09, 0x52, 0x10, 0x72, 0x65, 0x63, 0x6f, 0x6d, 0x6d, + 0x65, 0x6e, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x2e, 0x0a, 0x13, 0x6f, 0x72, + 0x69, 0x67, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6e, 0x67, 0x5f, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x69, + 0x64, 0x18, 0x08, 0x20, 0x03, 0x28, 0x09, 0x52, 0x11, 0x6f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x61, + 0x74, 0x69, 0x6e, 0x67, 0x55, 0x73, 0x65, 0x72, 0x49, 0x64, 0x12, 0x2e, 0x0a, 0x0e, 0x65, 0x6d, + 0x61, 0x69, 0x6c, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x73, 0x18, 0x09, 0x20, 0x01, + 0x28, 0x09, 0x42, 0x02, 0x18, 0x01, 0x48, 0x00, 0x52, 0x0d, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x43, + 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x73, 0x88, 0x01, 0x01, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x76, + 0x65, 0x6e, 0x74, 0x18, 0x0a, 0x20, 0x03, 0x28, 0x09, 0x52, 0x05, 0x65, 0x76, 0x65, 0x6e, 0x74, + 0x12, 0x39, 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x0b, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, + 0x52, 0x09, 0x73, 0x74, 0x61, 0x72, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x35, 0x0a, 0x08, 0x65, + 0x6e, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, + 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, + 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x07, 0x65, 0x6e, 0x64, 0x54, 0x69, + 0x6d, 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x5f, 0x6d, 0x61, 0x74, 0x63, + 0x68, 0x65, 0x73, 0x18, 0x0d, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0c, 0x65, 0x6d, 0x61, 0x69, 0x6c, + 0x4d, 0x61, 0x74, 0x63, 0x68, 0x65, 0x73, 0x42, 0x11, 0x0a, 0x0f, 0x5f, 0x65, 0x6d, 0x61, 0x69, + 0x6c, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x73, 0x22, 0x4a, 0x0a, 0x1f, 0x4c, 0x69, 0x73, 0x74, 0x41, 0x75, 0x64, 0x69, 0x74, 0x4c, 0x6f, 0x67, 0x4f, 0x72, 0x69, 0x67, 0x69, 0x6e, - 0x61, 0x74, 0x6f, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x17, 0x0a, 0x07, - 0x74, 0x65, 0x61, 0x6d, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x74, - 0x65, 0x61, 0x6d, 0x49, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, - 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x09, 0x63, 0x6c, 0x75, 0x73, 0x74, - 0x65, 0x72, 0x49, 0x64, 0x12, 0x17, 0x0a, 0x07, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x69, 0x64, 0x18, - 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x06, 0x6e, 0x6f, 0x64, 0x65, 0x49, 0x64, 0x12, 0x1f, 0x0a, - 0x0b, 0x77, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x5f, 0x69, 0x64, 0x18, 0x04, 0x20, 0x03, - 0x28, 0x09, 0x52, 0x0a, 0x77, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x49, 0x64, 0x12, 0x3a, - 0x0a, 0x0d, 0x77, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, - 0x05, 0x20, 0x03, 0x28, 0x0e, 0x32, 0x15, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4b, - 0x38, 0x73, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x4b, 0x69, 0x6e, 0x64, 0x52, 0x0c, 0x77, 0x6f, - 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x54, 0x79, 0x70, 0x65, 0x12, 0x38, 0x0a, 0x18, 0x72, 0x65, - 0x63, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x70, 0x6f, 0x6c, - 0x69, 0x63, 0x79, 0x5f, 0x69, 0x64, 0x18, 0x06, 0x20, 0x03, 0x28, 0x09, 0x52, 0x16, 0x72, 0x65, - 0x63, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x6f, 0x6c, 0x69, - 0x63, 0x79, 0x49, 0x64, 0x12, 0x2b, 0x0a, 0x11, 0x72, 0x65, 0x63, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, - 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x07, 0x20, 0x03, 0x28, 0x09, 0x52, - 0x10, 0x72, 0x65, 0x63, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, - 0x64, 0x12, 0x2e, 0x0a, 0x13, 0x6f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6e, 0x67, - 0x5f, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x08, 0x20, 0x03, 0x28, 0x09, 0x52, 0x11, - 0x6f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6e, 0x67, 0x55, 0x73, 0x65, 0x72, 0x49, - 0x64, 0x12, 0x2e, 0x0a, 0x0e, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x61, - 0x69, 0x6e, 0x73, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x42, 0x02, 0x18, 0x01, 0x48, 0x00, 0x52, - 0x0d, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x73, 0x88, 0x01, - 0x01, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x18, 0x0a, 0x20, 0x03, 0x28, 0x09, - 0x52, 0x05, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x39, 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x72, 0x74, + 0x61, 0x74, 0x6f, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x27, 0x0a, + 0x0f, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, + 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0e, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x41, 0x64, 0x64, + 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x22, 0x9e, 0x01, 0x0a, 0x18, 0x53, 0x65, 0x6e, 0x64, 0x57, + 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x45, 0x6d, 0x61, 0x69, 0x6c, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x12, 0x17, 0x0a, 0x07, 0x74, 0x65, 0x61, 0x6d, 0x5f, 0x69, 0x64, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x74, 0x65, 0x61, 0x6d, 0x49, 0x64, 0x12, 0x1d, 0x0a, 0x0a, + 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x09, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x73, + 0x74, 0x61, 0x72, 0x74, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x03, 0x52, + 0x09, 0x73, 0x74, 0x61, 0x72, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x73, 0x74, + 0x6f, 0x70, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x03, 0x52, 0x08, 0x73, + 0x74, 0x6f, 0x70, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x74, 0x6f, 0x18, 0x15, 0x20, + 0x03, 0x28, 0x09, 0x52, 0x02, 0x74, 0x6f, 0x22, 0x35, 0x0a, 0x19, 0x53, 0x65, 0x6e, 0x64, 0x57, + 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x45, 0x6d, 0x61, 0x69, 0x6c, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x64, + 0x0a, 0x1d, 0x53, 0x65, 0x6e, 0x64, 0x57, 0x65, 0x65, 0x6b, 0x6c, 0x79, 0x53, 0x75, 0x6d, 0x6d, + 0x61, 0x72, 0x79, 0x45, 0x6d, 0x61, 0x69, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, + 0x43, 0x0a, 0x07, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x29, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x57, 0x65, + 0x65, 0x6b, 0x6c, 0x79, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x45, 0x6d, 0x61, 0x69, 0x6c, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x44, 0x61, 0x74, 0x61, 0x52, 0x07, 0x72, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x22, 0x5c, 0x0a, 0x21, 0x53, 0x65, 0x6e, 0x64, 0x57, 0x65, 0x65, 0x6b, + 0x6c, 0x79, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x45, 0x6d, 0x61, 0x69, 0x6c, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x44, 0x61, 0x74, 0x61, 0x12, 0x17, 0x0a, 0x07, 0x74, 0x65, 0x61, + 0x6d, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x74, 0x65, 0x61, 0x6d, + 0x49, 0x64, 0x12, 0x1e, 0x0a, 0x0a, 0x72, 0x65, 0x63, 0x69, 0x70, 0x69, 0x65, 0x6e, 0x74, 0x73, + 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0a, 0x72, 0x65, 0x63, 0x69, 0x70, 0x69, 0x65, 0x6e, + 0x74, 0x73, 0x22, 0x3a, 0x0a, 0x1e, 0x53, 0x65, 0x6e, 0x64, 0x57, 0x65, 0x65, 0x6b, 0x6c, 0x79, + 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x45, 0x6d, 0x61, 0x69, 0x6c, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x95, + 0x03, 0x0a, 0x1a, 0x47, 0x65, 0x74, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x73, 0x4e, 0x6f, + 0x64, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x17, 0x0a, + 0x07, 0x74, 0x65, 0x61, 0x6d, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, + 0x74, 0x65, 0x61, 0x6d, 0x49, 0x64, 0x12, 0x1f, 0x0a, 0x0b, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, + 0x72, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0a, 0x63, 0x6c, 0x75, + 0x73, 0x74, 0x65, 0x72, 0x49, 0x64, 0x73, 0x12, 0x3e, 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, - 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x73, 0x74, 0x61, 0x72, 0x74, 0x54, 0x69, - 0x6d, 0x65, 0x12, 0x35, 0x0a, 0x08, 0x65, 0x6e, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x0c, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, - 0x52, 0x07, 0x65, 0x6e, 0x64, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x65, 0x6d, 0x61, - 0x69, 0x6c, 0x5f, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x65, 0x73, 0x18, 0x0d, 0x20, 0x03, 0x28, 0x09, - 0x52, 0x0c, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x4d, 0x61, 0x74, 0x63, 0x68, 0x65, 0x73, 0x42, 0x11, - 0x0a, 0x0f, 0x5f, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, - 0x73, 0x22, 0x4a, 0x0a, 0x1f, 0x4c, 0x69, 0x73, 0x74, 0x41, 0x75, 0x64, 0x69, 0x74, 0x4c, 0x6f, - 0x67, 0x4f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x61, 0x74, 0x6f, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x27, 0x0a, 0x0f, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x5f, 0x61, 0x64, - 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0e, 0x65, - 0x6d, 0x61, 0x69, 0x6c, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x22, 0x9e, 0x01, - 0x0a, 0x18, 0x53, 0x65, 0x6e, 0x64, 0x57, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x45, 0x6d, - 0x61, 0x69, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x17, 0x0a, 0x07, 0x74, 0x65, - 0x61, 0x6d, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x74, 0x65, 0x61, - 0x6d, 0x49, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x69, - 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, - 0x49, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x74, 0x69, 0x6d, 0x65, - 0x18, 0x0b, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x73, 0x74, 0x61, 0x72, 0x74, 0x54, 0x69, 0x6d, - 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x73, 0x74, 0x6f, 0x70, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x0c, - 0x20, 0x01, 0x28, 0x03, 0x52, 0x08, 0x73, 0x74, 0x6f, 0x70, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x0e, - 0x0a, 0x02, 0x74, 0x6f, 0x18, 0x15, 0x20, 0x03, 0x28, 0x09, 0x52, 0x02, 0x74, 0x6f, 0x22, 0x35, - 0x0a, 0x19, 0x53, 0x65, 0x6e, 0x64, 0x57, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x45, 0x6d, - 0x61, 0x69, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x6d, - 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, - 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x64, 0x0a, 0x1d, 0x53, 0x65, 0x6e, 0x64, 0x57, 0x65, 0x65, - 0x6b, 0x6c, 0x79, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x45, 0x6d, 0x61, 0x69, 0x6c, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x43, 0x0a, 0x07, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x29, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, - 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x57, 0x65, 0x65, 0x6b, 0x6c, 0x79, 0x53, 0x75, 0x6d, 0x6d, 0x61, - 0x72, 0x79, 0x45, 0x6d, 0x61, 0x69, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x44, 0x61, - 0x74, 0x61, 0x52, 0x07, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x5c, 0x0a, 0x21, 0x53, - 0x65, 0x6e, 0x64, 0x57, 0x65, 0x65, 0x6b, 0x6c, 0x79, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, - 0x45, 0x6d, 0x61, 0x69, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x44, 0x61, 0x74, 0x61, - 0x12, 0x17, 0x0a, 0x07, 0x74, 0x65, 0x61, 0x6d, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x06, 0x74, 0x65, 0x61, 0x6d, 0x49, 0x64, 0x12, 0x1e, 0x0a, 0x0a, 0x72, 0x65, 0x63, - 0x69, 0x70, 0x69, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0a, 0x72, - 0x65, 0x63, 0x69, 0x70, 0x69, 0x65, 0x6e, 0x74, 0x73, 0x22, 0x3a, 0x0a, 0x1e, 0x53, 0x65, 0x6e, - 0x64, 0x57, 0x65, 0x65, 0x6b, 0x6c, 0x79, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x45, 0x6d, - 0x61, 0x69, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x6d, - 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, - 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x95, 0x03, 0x0a, 0x1a, 0x47, 0x65, 0x74, 0x43, 0x6c, 0x75, - 0x73, 0x74, 0x65, 0x72, 0x73, 0x4e, 0x6f, 0x64, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x12, 0x17, 0x0a, 0x07, 0x74, 0x65, 0x61, 0x6d, 0x5f, 0x69, 0x64, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x74, 0x65, 0x61, 0x6d, 0x49, 0x64, 0x12, 0x1f, 0x0a, - 0x0b, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x02, 0x20, 0x03, - 0x28, 0x09, 0x52, 0x0a, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, 0x64, 0x73, 0x12, 0x3e, - 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x0b, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x48, 0x00, - 0x52, 0x09, 0x73, 0x74, 0x61, 0x72, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x88, 0x01, 0x01, 0x12, 0x3a, - 0x0a, 0x08, 0x65, 0x6e, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, - 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x48, 0x01, 0x52, 0x07, - 0x65, 0x6e, 0x64, 0x54, 0x69, 0x6d, 0x65, 0x88, 0x01, 0x01, 0x12, 0x31, 0x0a, 0x15, 0x65, 0x78, - 0x63, 0x6c, 0x75, 0x64, 0x65, 0x5f, 0x6d, 0x6f, 0x73, 0x74, 0x5f, 0x65, 0x78, 0x70, 0x5f, 0x6e, - 0x6f, 0x64, 0x65, 0x18, 0x29, 0x20, 0x01, 0x28, 0x08, 0x52, 0x12, 0x65, 0x78, 0x63, 0x6c, 0x75, - 0x64, 0x65, 0x4d, 0x6f, 0x73, 0x74, 0x45, 0x78, 0x70, 0x4e, 0x6f, 0x64, 0x65, 0x12, 0x33, 0x0a, - 0x16, 0x65, 0x78, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x5f, 0x6c, 0x65, 0x61, 0x73, 0x74, 0x5f, 0x65, - 0x78, 0x70, 0x5f, 0x6e, 0x6f, 0x64, 0x65, 0x18, 0x2a, 0x20, 0x01, 0x28, 0x08, 0x52, 0x13, 0x65, - 0x78, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x4c, 0x65, 0x61, 0x73, 0x74, 0x45, 0x78, 0x70, 0x4e, 0x6f, - 0x64, 0x65, 0x12, 0x3d, 0x0a, 0x1b, 0x65, 0x78, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x5f, 0x6d, 0x6f, - 0x73, 0x74, 0x5f, 0x75, 0x6e, 0x64, 0x65, 0x72, 0x75, 0x74, 0x69, 0x6c, 0x5f, 0x6e, 0x6f, 0x64, - 0x65, 0x18, 0x2b, 0x20, 0x01, 0x28, 0x08, 0x52, 0x18, 0x65, 0x78, 0x63, 0x6c, 0x75, 0x64, 0x65, - 0x4d, 0x6f, 0x73, 0x74, 0x55, 0x6e, 0x64, 0x65, 0x72, 0x75, 0x74, 0x69, 0x6c, 0x4e, 0x6f, 0x64, - 0x65, 0x42, 0x0d, 0x0a, 0x0b, 0x5f, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x74, 0x69, 0x6d, 0x65, - 0x42, 0x0b, 0x0a, 0x09, 0x5f, 0x65, 0x6e, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x22, 0x90, 0x02, - 0x0a, 0x1b, 0x47, 0x65, 0x74, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x73, 0x4e, 0x6f, 0x64, - 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2d, 0x0a, - 0x09, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x10, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x49, 0x6e, - 0x66, 0x6f, 0x52, 0x08, 0x6e, 0x6f, 0x64, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x3c, 0x0a, 0x13, - 0x6d, 0x6f, 0x73, 0x74, 0x5f, 0x65, 0x78, 0x70, 0x65, 0x6e, 0x73, 0x69, 0x76, 0x65, 0x5f, 0x6e, - 0x6f, 0x64, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x61, 0x70, 0x69, 0x2e, - 0x76, 0x31, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x11, 0x6d, 0x6f, 0x73, 0x74, 0x45, 0x78, 0x70, - 0x65, 0x6e, 0x73, 0x69, 0x76, 0x65, 0x4e, 0x6f, 0x64, 0x65, 0x12, 0x3e, 0x0a, 0x14, 0x6c, 0x65, - 0x61, 0x73, 0x74, 0x5f, 0x65, 0x78, 0x70, 0x65, 0x6e, 0x73, 0x69, 0x76, 0x65, 0x5f, 0x6e, 0x6f, - 0x64, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, - 0x31, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x12, 0x6c, 0x65, 0x61, 0x73, 0x74, 0x45, 0x78, 0x70, - 0x65, 0x6e, 0x73, 0x69, 0x76, 0x65, 0x4e, 0x6f, 0x64, 0x65, 0x12, 0x44, 0x0a, 0x17, 0x6d, 0x6f, - 0x73, 0x74, 0x5f, 0x75, 0x6e, 0x64, 0x65, 0x72, 0x75, 0x74, 0x69, 0x6c, 0x69, 0x7a, 0x65, 0x64, - 0x5f, 0x6e, 0x6f, 0x64, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x61, 0x70, - 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x15, 0x6d, 0x6f, 0x73, 0x74, 0x55, - 0x6e, 0x64, 0x65, 0x72, 0x75, 0x74, 0x69, 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x4e, 0x6f, 0x64, 0x65, - 0x22, 0x9f, 0x01, 0x0a, 0x19, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x4b, 0x38, 0x73, 0x52, 0x65, - 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x17, - 0x0a, 0x07, 0x74, 0x65, 0x61, 0x6d, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x06, 0x74, 0x65, 0x61, 0x6d, 0x49, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x6c, 0x75, 0x73, 0x74, - 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x6c, 0x75, - 0x73, 0x74, 0x65, 0x72, 0x49, 0x64, 0x12, 0x21, 0x0a, 0x0c, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, - 0x5f, 0x71, 0x75, 0x65, 0x72, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x73, 0x65, - 0x61, 0x72, 0x63, 0x68, 0x51, 0x75, 0x65, 0x72, 0x79, 0x12, 0x27, 0x0a, 0x0f, 0x69, 0x6e, 0x63, - 0x6c, 0x75, 0x64, 0x65, 0x5f, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x18, 0x04, 0x20, 0x01, - 0x28, 0x08, 0x52, 0x0e, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x44, 0x65, 0x6c, 0x65, 0x74, - 0x65, 0x64, 0x22, 0x4f, 0x0a, 0x1a, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x4b, 0x38, 0x73, 0x52, - 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x12, 0x31, 0x0a, 0x07, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, - 0x0b, 0x32, 0x17, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4b, 0x38, 0x73, 0x53, 0x65, - 0x61, 0x72, 0x63, 0x68, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x52, 0x07, 0x72, 0x65, 0x73, 0x75, - 0x6c, 0x74, 0x73, 0x22, 0x8e, 0x01, 0x0a, 0x0f, 0x4b, 0x38, 0x73, 0x53, 0x65, 0x61, 0x72, 0x63, - 0x68, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x69, 0x64, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, - 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x12, 0x0a, - 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6b, 0x69, 0x6e, - 0x64, 0x12, 0x23, 0x0a, 0x0d, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x65, 0x64, 0x5f, 0x66, 0x69, 0x65, - 0x6c, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x65, - 0x64, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x12, 0x1c, 0x0a, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, - 0x61, 0x63, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, - 0x70, 0x61, 0x63, 0x65, 0x22, 0x78, 0x0a, 0x19, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x4b, 0x38, - 0x73, 0x57, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x12, 0x17, 0x0a, 0x07, 0x74, 0x65, 0x61, 0x6d, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x06, 0x74, 0x65, 0x61, 0x6d, 0x49, 0x64, 0x12, 0x1f, 0x0a, 0x0b, 0x63, 0x6c, - 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, - 0x0a, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, 0x64, 0x73, 0x12, 0x21, 0x0a, 0x0c, 0x73, - 0x65, 0x61, 0x72, 0x63, 0x68, 0x5f, 0x71, 0x75, 0x65, 0x72, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x0b, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x51, 0x75, 0x65, 0x72, 0x79, 0x22, 0x57, - 0x0a, 0x1a, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x4b, 0x38, 0x73, 0x57, 0x6f, 0x72, 0x6b, 0x6c, - 0x6f, 0x61, 0x64, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x39, 0x0a, 0x07, - 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1f, 0x2e, - 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4b, 0x38, 0x73, 0x57, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, - 0x61, 0x64, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x52, 0x07, - 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x22, 0x41, 0x0a, 0x17, 0x4b, 0x38, 0x73, 0x57, 0x6f, - 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x52, 0x65, 0x73, 0x75, - 0x6c, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0xc8, 0x02, 0x0a, 0x1b, 0x4d, - 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x46, 0x6f, 0x72, 0x57, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, + 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x48, 0x00, 0x52, 0x09, 0x73, 0x74, 0x61, 0x72, 0x74, + 0x54, 0x69, 0x6d, 0x65, 0x88, 0x01, 0x01, 0x12, 0x3a, 0x0a, 0x08, 0x65, 0x6e, 0x64, 0x5f, 0x74, + 0x69, 0x6d, 0x65, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, + 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, + 0x73, 0x74, 0x61, 0x6d, 0x70, 0x48, 0x01, 0x52, 0x07, 0x65, 0x6e, 0x64, 0x54, 0x69, 0x6d, 0x65, + 0x88, 0x01, 0x01, 0x12, 0x31, 0x0a, 0x15, 0x65, 0x78, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x5f, 0x6d, + 0x6f, 0x73, 0x74, 0x5f, 0x65, 0x78, 0x70, 0x5f, 0x6e, 0x6f, 0x64, 0x65, 0x18, 0x29, 0x20, 0x01, + 0x28, 0x08, 0x52, 0x12, 0x65, 0x78, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x4d, 0x6f, 0x73, 0x74, 0x45, + 0x78, 0x70, 0x4e, 0x6f, 0x64, 0x65, 0x12, 0x33, 0x0a, 0x16, 0x65, 0x78, 0x63, 0x6c, 0x75, 0x64, + 0x65, 0x5f, 0x6c, 0x65, 0x61, 0x73, 0x74, 0x5f, 0x65, 0x78, 0x70, 0x5f, 0x6e, 0x6f, 0x64, 0x65, + 0x18, 0x2a, 0x20, 0x01, 0x28, 0x08, 0x52, 0x13, 0x65, 0x78, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x4c, + 0x65, 0x61, 0x73, 0x74, 0x45, 0x78, 0x70, 0x4e, 0x6f, 0x64, 0x65, 0x12, 0x3d, 0x0a, 0x1b, 0x65, + 0x78, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x5f, 0x6d, 0x6f, 0x73, 0x74, 0x5f, 0x75, 0x6e, 0x64, 0x65, + 0x72, 0x75, 0x74, 0x69, 0x6c, 0x5f, 0x6e, 0x6f, 0x64, 0x65, 0x18, 0x2b, 0x20, 0x01, 0x28, 0x08, + 0x52, 0x18, 0x65, 0x78, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x4d, 0x6f, 0x73, 0x74, 0x55, 0x6e, 0x64, + 0x65, 0x72, 0x75, 0x74, 0x69, 0x6c, 0x4e, 0x6f, 0x64, 0x65, 0x42, 0x0d, 0x0a, 0x0b, 0x5f, 0x73, + 0x74, 0x61, 0x72, 0x74, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x42, 0x0b, 0x0a, 0x09, 0x5f, 0x65, 0x6e, + 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x22, 0x90, 0x02, 0x0a, 0x1b, 0x47, 0x65, 0x74, 0x43, 0x6c, + 0x75, 0x73, 0x74, 0x65, 0x72, 0x73, 0x4e, 0x6f, 0x64, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2d, 0x0a, 0x09, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x69, + 0x6e, 0x66, 0x6f, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x61, 0x70, 0x69, 0x2e, + 0x76, 0x31, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x08, 0x6e, 0x6f, 0x64, + 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x3c, 0x0a, 0x13, 0x6d, 0x6f, 0x73, 0x74, 0x5f, 0x65, 0x78, + 0x70, 0x65, 0x6e, 0x73, 0x69, 0x76, 0x65, 0x5f, 0x6e, 0x6f, 0x64, 0x65, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4e, 0x6f, 0x64, 0x65, + 0x52, 0x11, 0x6d, 0x6f, 0x73, 0x74, 0x45, 0x78, 0x70, 0x65, 0x6e, 0x73, 0x69, 0x76, 0x65, 0x4e, + 0x6f, 0x64, 0x65, 0x12, 0x3e, 0x0a, 0x14, 0x6c, 0x65, 0x61, 0x73, 0x74, 0x5f, 0x65, 0x78, 0x70, + 0x65, 0x6e, 0x73, 0x69, 0x76, 0x65, 0x5f, 0x6e, 0x6f, 0x64, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x0c, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x52, + 0x12, 0x6c, 0x65, 0x61, 0x73, 0x74, 0x45, 0x78, 0x70, 0x65, 0x6e, 0x73, 0x69, 0x76, 0x65, 0x4e, + 0x6f, 0x64, 0x65, 0x12, 0x44, 0x0a, 0x17, 0x6d, 0x6f, 0x73, 0x74, 0x5f, 0x75, 0x6e, 0x64, 0x65, + 0x72, 0x75, 0x74, 0x69, 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x5f, 0x6e, 0x6f, 0x64, 0x65, 0x18, 0x04, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4e, 0x6f, + 0x64, 0x65, 0x52, 0x15, 0x6d, 0x6f, 0x73, 0x74, 0x55, 0x6e, 0x64, 0x65, 0x72, 0x75, 0x74, 0x69, + 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x4e, 0x6f, 0x64, 0x65, 0x22, 0x9f, 0x01, 0x0a, 0x19, 0x53, 0x65, + 0x61, 0x72, 0x63, 0x68, 0x4b, 0x38, 0x73, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x17, 0x0a, 0x07, 0x74, 0x65, 0x61, 0x6d, 0x5f, + 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x74, 0x65, 0x61, 0x6d, 0x49, 0x64, + 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, 0x64, 0x12, + 0x21, 0x0a, 0x0c, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x5f, 0x71, 0x75, 0x65, 0x72, 0x79, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x51, 0x75, 0x65, + 0x72, 0x79, 0x12, 0x27, 0x0a, 0x0f, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x5f, 0x64, 0x65, + 0x6c, 0x65, 0x74, 0x65, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0e, 0x69, 0x6e, 0x63, + 0x6c, 0x75, 0x64, 0x65, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x22, 0x4f, 0x0a, 0x1a, 0x53, + 0x65, 0x61, 0x72, 0x63, 0x68, 0x4b, 0x38, 0x73, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, + 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x31, 0x0a, 0x07, 0x72, 0x65, 0x73, + 0x75, 0x6c, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x61, 0x70, 0x69, + 0x2e, 0x76, 0x31, 0x2e, 0x4b, 0x38, 0x73, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x52, 0x65, 0x73, + 0x75, 0x6c, 0x74, 0x52, 0x07, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x22, 0x8e, 0x01, 0x0a, + 0x0f, 0x4b, 0x38, 0x73, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, + 0x12, 0x10, 0x0a, 0x03, 0x75, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, + 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x12, 0x23, 0x0a, 0x0d, 0x6d, 0x61, + 0x74, 0x63, 0x68, 0x65, 0x64, 0x5f, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x0c, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x65, 0x64, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x12, + 0x1c, 0x0a, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x05, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x22, 0x78, 0x0a, + 0x19, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x4b, 0x38, 0x73, 0x57, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x17, 0x0a, 0x07, 0x74, 0x65, 0x61, 0x6d, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x74, 0x65, 0x61, - 0x6d, 0x49, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x69, - 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, - 0x49, 0x64, 0x12, 0x6b, 0x0a, 0x14, 0x77, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x5f, 0x75, - 0x69, 0x64, 0x5f, 0x74, 0x6f, 0x5f, 0x6b, 0x69, 0x6e, 0x64, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, - 0x32, 0x3a, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, - 0x74, 0x61, 0x46, 0x6f, 0x72, 0x57, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x73, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x55, 0x69, - 0x64, 0x54, 0x6f, 0x4b, 0x69, 0x6e, 0x64, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x11, 0x77, 0x6f, - 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x55, 0x69, 0x64, 0x54, 0x6f, 0x4b, 0x69, 0x6e, 0x64, 0x12, - 0x27, 0x0a, 0x0f, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x5f, 0x64, 0x65, 0x6c, 0x65, 0x74, - 0x65, 0x64, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0e, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, - 0x65, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x1a, 0x5b, 0x0a, 0x16, 0x57, 0x6f, 0x72, 0x6b, - 0x6c, 0x6f, 0x61, 0x64, 0x55, 0x69, 0x64, 0x54, 0x6f, 0x4b, 0x69, 0x6e, 0x64, 0x45, 0x6e, 0x74, - 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x03, 0x6b, 0x65, 0x79, 0x12, 0x2b, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x0e, 0x32, 0x15, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4b, 0x38, 0x73, - 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x4b, 0x69, 0x6e, 0x64, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, - 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0xfc, 0x01, 0x0a, 0x1c, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, - 0x74, 0x61, 0x46, 0x6f, 0x72, 0x57, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x73, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x78, 0x0a, 0x18, 0x77, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, - 0x61, 0x64, 0x5f, 0x75, 0x69, 0x64, 0x5f, 0x74, 0x6f, 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, - 0x74, 0x61, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x3f, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, - 0x31, 0x2e, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x46, 0x6f, 0x72, 0x57, 0x6f, 0x72, - 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x57, - 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x55, 0x69, 0x64, 0x54, 0x6f, 0x4d, 0x65, 0x74, 0x61, - 0x64, 0x61, 0x74, 0x61, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x15, 0x77, 0x6f, 0x72, 0x6b, 0x6c, - 0x6f, 0x61, 0x64, 0x55, 0x69, 0x64, 0x54, 0x6f, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, - 0x1a, 0x62, 0x0a, 0x1a, 0x57, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x55, 0x69, 0x64, 0x54, - 0x6f, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, - 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, - 0x12, 0x2e, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x18, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, - 0x64, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, - 0x3a, 0x02, 0x38, 0x01, 0x22, 0x63, 0x0a, 0x15, 0x41, 0x64, 0x64, 0x43, 0x6c, 0x75, 0x73, 0x74, - 0x65, 0x72, 0x54, 0x61, 0x67, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x17, 0x0a, - 0x07, 0x74, 0x65, 0x61, 0x6d, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, - 0x74, 0x65, 0x61, 0x6d, 0x49, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, - 0x72, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x6c, 0x75, 0x73, - 0x74, 0x65, 0x72, 0x49, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x61, 0x67, 0x73, 0x18, 0x03, 0x20, - 0x03, 0x28, 0x09, 0x52, 0x04, 0x74, 0x61, 0x67, 0x73, 0x22, 0x43, 0x0a, 0x16, 0x41, 0x64, 0x64, - 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x54, 0x61, 0x67, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x12, 0x29, 0x0a, 0x07, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6c, - 0x75, 0x73, 0x74, 0x65, 0x72, 0x52, 0x07, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x22, 0x66, - 0x0a, 0x18, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x54, + 0x6d, 0x49, 0x64, 0x12, 0x1f, 0x0a, 0x0b, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x69, + 0x64, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0a, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, + 0x72, 0x49, 0x64, 0x73, 0x12, 0x21, 0x0a, 0x0c, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x5f, 0x71, + 0x75, 0x65, 0x72, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x73, 0x65, 0x61, 0x72, + 0x63, 0x68, 0x51, 0x75, 0x65, 0x72, 0x79, 0x22, 0x57, 0x0a, 0x1a, 0x53, 0x65, 0x61, 0x72, 0x63, + 0x68, 0x4b, 0x38, 0x73, 0x57, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x73, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x39, 0x0a, 0x07, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, + 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, + 0x4b, 0x38, 0x73, 0x57, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x53, 0x65, 0x61, 0x72, 0x63, + 0x68, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x52, 0x07, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, + 0x22, 0x41, 0x0a, 0x17, 0x4b, 0x38, 0x73, 0x57, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x53, + 0x65, 0x61, 0x72, 0x63, 0x68, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6b, + 0x69, 0x6e, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x12, + 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, + 0x61, 0x6d, 0x65, 0x22, 0xc8, 0x02, 0x0a, 0x1b, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, + 0x46, 0x6f, 0x72, 0x57, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x73, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x12, 0x17, 0x0a, 0x07, 0x74, 0x65, 0x61, 0x6d, 0x5f, 0x69, 0x64, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x74, 0x65, 0x61, 0x6d, 0x49, 0x64, 0x12, 0x1d, 0x0a, 0x0a, + 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x09, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, 0x64, 0x12, 0x6b, 0x0a, 0x14, 0x77, + 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x5f, 0x75, 0x69, 0x64, 0x5f, 0x74, 0x6f, 0x5f, 0x6b, + 0x69, 0x6e, 0x64, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x3a, 0x2e, 0x61, 0x70, 0x69, 0x2e, + 0x76, 0x31, 0x2e, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x46, 0x6f, 0x72, 0x57, 0x6f, + 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x57, + 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x55, 0x69, 0x64, 0x54, 0x6f, 0x4b, 0x69, 0x6e, 0x64, + 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x11, 0x77, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x55, + 0x69, 0x64, 0x54, 0x6f, 0x4b, 0x69, 0x6e, 0x64, 0x12, 0x27, 0x0a, 0x0f, 0x69, 0x6e, 0x63, 0x6c, + 0x75, 0x64, 0x65, 0x5f, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x18, 0x0b, 0x20, 0x01, 0x28, + 0x08, 0x52, 0x0e, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, + 0x64, 0x1a, 0x5b, 0x0a, 0x16, 0x57, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x55, 0x69, 0x64, + 0x54, 0x6f, 0x4b, 0x69, 0x6e, 0x64, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, + 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x2b, 0x0a, + 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x15, 0x2e, 0x61, + 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4b, 0x38, 0x73, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x4b, + 0x69, 0x6e, 0x64, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0xfc, + 0x01, 0x0a, 0x1c, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x46, 0x6f, 0x72, 0x57, 0x6f, + 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, + 0x78, 0x0a, 0x18, 0x77, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x5f, 0x75, 0x69, 0x64, 0x5f, + 0x74, 0x6f, 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x03, 0x28, + 0x0b, 0x32, 0x3f, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x65, 0x74, 0x61, 0x64, + 0x61, 0x74, 0x61, 0x46, 0x6f, 0x72, 0x57, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x73, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, + 0x55, 0x69, 0x64, 0x54, 0x6f, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x45, 0x6e, 0x74, + 0x72, 0x79, 0x52, 0x15, 0x77, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x55, 0x69, 0x64, 0x54, + 0x6f, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x1a, 0x62, 0x0a, 0x1a, 0x57, 0x6f, 0x72, + 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x55, 0x69, 0x64, 0x54, 0x6f, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, + 0x74, 0x61, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x2e, 0x0a, 0x05, 0x76, 0x61, 0x6c, + 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, + 0x31, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, + 0x74, 0x61, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x63, 0x0a, + 0x15, 0x41, 0x64, 0x64, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x54, 0x61, 0x67, 0x73, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x17, 0x0a, 0x07, 0x74, 0x65, 0x61, 0x6d, 0x5f, 0x69, + 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x74, 0x65, 0x61, 0x6d, 0x49, 0x64, 0x12, + 0x1d, 0x0a, 0x0a, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, 0x64, 0x12, 0x12, + 0x0a, 0x04, 0x74, 0x61, 0x67, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x04, 0x74, 0x61, + 0x67, 0x73, 0x22, 0x43, 0x0a, 0x16, 0x41, 0x64, 0x64, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, + 0x54, 0x61, 0x67, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x29, 0x0a, 0x07, + 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, + 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x52, 0x07, + 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x22, 0x66, 0x0a, 0x18, 0x52, 0x65, 0x6d, 0x6f, 0x76, + 0x65, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x54, 0x61, 0x67, 0x73, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x12, 0x17, 0x0a, 0x07, 0x74, 0x65, 0x61, 0x6d, 0x5f, 0x69, 0x64, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x74, 0x65, 0x61, 0x6d, 0x49, 0x64, 0x12, 0x1d, 0x0a, 0x0a, + 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x09, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x74, + 0x61, 0x67, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x04, 0x74, 0x61, 0x67, 0x73, 0x22, + 0x46, 0x0a, 0x19, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, + 0x54, 0x61, 0x67, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x29, 0x0a, 0x07, + 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, + 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x52, 0x07, + 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x22, 0x2a, 0x0a, 0x0f, 0x4c, 0x69, 0x73, 0x74, 0x54, 0x61, 0x67, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x17, 0x0a, 0x07, 0x74, 0x65, 0x61, 0x6d, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x74, 0x65, 0x61, - 0x6d, 0x49, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x69, - 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, - 0x49, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x61, 0x67, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, - 0x52, 0x04, 0x74, 0x61, 0x67, 0x73, 0x22, 0x46, 0x0a, 0x19, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, - 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x54, 0x61, 0x67, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x12, 0x29, 0x0a, 0x07, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6c, - 0x75, 0x73, 0x74, 0x65, 0x72, 0x52, 0x07, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x22, 0x2a, - 0x0a, 0x0f, 0x4c, 0x69, 0x73, 0x74, 0x54, 0x61, 0x67, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x12, 0x17, 0x0a, 0x07, 0x74, 0x65, 0x61, 0x6d, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x06, 0x74, 0x65, 0x61, 0x6d, 0x49, 0x64, 0x22, 0x3f, 0x0a, 0x0a, 0x54, 0x61, - 0x67, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x74, 0x61, 0x67, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x74, 0x61, 0x67, 0x12, 0x1f, 0x0a, 0x0b, 0x63, 0x6c, - 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, - 0x0a, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, 0x64, 0x73, 0x22, 0x3a, 0x0a, 0x10, 0x4c, - 0x69, 0x73, 0x74, 0x54, 0x61, 0x67, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, - 0x26, 0x0a, 0x04, 0x74, 0x61, 0x67, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x12, 0x2e, - 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x61, 0x67, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, - 0x79, 0x52, 0x04, 0x74, 0x61, 0x67, 0x73, 0x22, 0xcc, 0x04, 0x0a, 0x10, 0x57, 0x6f, 0x72, 0x6b, - 0x6c, 0x6f, 0x61, 0x64, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x6a, 0x0a, 0x18, - 0x70, 0x6f, 0x64, 0x5f, 0x75, 0x69, 0x64, 0x5f, 0x74, 0x6f, 0x5f, 0x6e, 0x6f, 0x64, 0x65, 0x5f, - 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x32, - 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, - 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x50, 0x6f, 0x64, 0x55, 0x69, 0x64, 0x54, - 0x6f, 0x4e, 0x6f, 0x64, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x45, 0x6e, 0x74, - 0x72, 0x79, 0x52, 0x14, 0x70, 0x6f, 0x64, 0x55, 0x69, 0x64, 0x54, 0x6f, 0x4e, 0x6f, 0x64, 0x65, - 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x67, 0x0a, 0x17, 0x70, 0x6f, 0x64, 0x5f, - 0x75, 0x69, 0x64, 0x5f, 0x74, 0x6f, 0x5f, 0x70, 0x6f, 0x64, 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x64, - 0x61, 0x74, 0x61, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x31, 0x2e, 0x61, 0x70, 0x69, 0x2e, - 0x76, 0x31, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x4d, 0x65, 0x74, 0x61, 0x64, - 0x61, 0x74, 0x61, 0x2e, 0x50, 0x6f, 0x64, 0x55, 0x69, 0x64, 0x54, 0x6f, 0x50, 0x6f, 0x64, 0x4d, - 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x13, 0x70, 0x6f, - 0x64, 0x55, 0x69, 0x64, 0x54, 0x6f, 0x50, 0x6f, 0x64, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, - 0x61, 0x12, 0x14, 0x0a, 0x05, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x05, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x12, 0x1e, 0x0a, 0x0a, 0x61, 0x6e, 0x6e, 0x6f, 0x74, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x61, 0x6e, 0x6e, - 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x70, 0x65, 0x63, 0x18, - 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x73, 0x70, 0x65, 0x63, 0x12, 0x16, 0x0a, 0x06, 0x73, - 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x74, 0x61, - 0x74, 0x75, 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, - 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, - 0x29, 0x0a, 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x15, 0x2e, - 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4b, 0x38, 0x73, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, - 0x4b, 0x69, 0x6e, 0x64, 0x52, 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x1a, 0x5d, 0x0a, 0x19, 0x50, 0x6f, - 0x64, 0x55, 0x69, 0x64, 0x54, 0x6f, 0x4e, 0x6f, 0x64, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, - 0x74, 0x61, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x2a, 0x0a, 0x05, 0x76, 0x61, 0x6c, - 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, - 0x31, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x05, - 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x5b, 0x0a, 0x18, 0x50, 0x6f, 0x64, + 0x6d, 0x49, 0x64, 0x22, 0x3f, 0x0a, 0x0a, 0x54, 0x61, 0x67, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, + 0x79, 0x12, 0x10, 0x0a, 0x03, 0x74, 0x61, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, + 0x74, 0x61, 0x67, 0x12, 0x1f, 0x0a, 0x0b, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x69, + 0x64, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0a, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, + 0x72, 0x49, 0x64, 0x73, 0x22, 0x3a, 0x0a, 0x10, 0x4c, 0x69, 0x73, 0x74, 0x54, 0x61, 0x67, 0x73, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x26, 0x0a, 0x04, 0x74, 0x61, 0x67, 0x73, + 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, + 0x54, 0x61, 0x67, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x52, 0x04, 0x74, 0x61, 0x67, 0x73, + 0x22, 0xcc, 0x04, 0x0a, 0x10, 0x57, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x4d, 0x65, 0x74, + 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x6a, 0x0a, 0x18, 0x70, 0x6f, 0x64, 0x5f, 0x75, 0x69, 0x64, + 0x5f, 0x74, 0x6f, 0x5f, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, + 0x61, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x32, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, + 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, + 0x61, 0x2e, 0x50, 0x6f, 0x64, 0x55, 0x69, 0x64, 0x54, 0x6f, 0x4e, 0x6f, 0x64, 0x65, 0x4d, 0x65, + 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x14, 0x70, 0x6f, 0x64, + 0x55, 0x69, 0x64, 0x54, 0x6f, 0x4e, 0x6f, 0x64, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, + 0x61, 0x12, 0x67, 0x0a, 0x17, 0x70, 0x6f, 0x64, 0x5f, 0x75, 0x69, 0x64, 0x5f, 0x74, 0x6f, 0x5f, + 0x70, 0x6f, 0x64, 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x02, 0x20, 0x03, + 0x28, 0x0b, 0x32, 0x31, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x57, 0x6f, 0x72, 0x6b, + 0x6c, 0x6f, 0x61, 0x64, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x50, 0x6f, 0x64, 0x55, 0x69, 0x64, 0x54, 0x6f, 0x50, 0x6f, 0x64, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, - 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x29, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, - 0x50, 0x6f, 0x64, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x05, 0x76, 0x61, 0x6c, - 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x9f, 0x01, 0x0a, 0x0b, 0x50, 0x6f, 0x64, 0x4d, 0x65, - 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x6c, 0x61, + 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x13, 0x70, 0x6f, 0x64, 0x55, 0x69, 0x64, 0x54, 0x6f, 0x50, + 0x6f, 0x64, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x14, 0x0a, 0x05, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x12, 0x1e, 0x0a, 0x0a, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, @@ -12378,331 +15755,285 @@ var file_api_v1_k8s_proto_rawDesc = []byte{ 0x73, 0x70, 0x65, 0x63, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, - 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x22, 0xaa, 0x02, 0x0a, 0x0c, 0x4e, 0x6f, 0x64, - 0x65, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x1b, 0x0a, 0x09, 0x6e, 0x6f, 0x64, - 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6e, 0x6f, - 0x64, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x26, 0x0a, 0x0f, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x67, - 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x0d, 0x6e, 0x6f, 0x64, 0x65, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x26, - 0x0a, 0x0f, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x74, 0x79, 0x70, - 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x6e, 0x6f, 0x64, 0x65, 0x47, 0x72, 0x6f, - 0x75, 0x70, 0x54, 0x79, 0x70, 0x65, 0x12, 0x2f, 0x0a, 0x14, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x5f, - 0x69, 0x6e, 0x73, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x04, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x11, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x49, 0x6e, 0x73, 0x74, 0x54, - 0x79, 0x70, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x6c, 0x61, 0x62, 0x65, 0x6c, - 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x12, 0x1e, 0x0a, - 0x0a, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x07, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x0a, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a, - 0x04, 0x73, 0x70, 0x65, 0x63, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x73, 0x70, 0x65, - 0x63, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x09, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x6d, 0x65, 0x74, - 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6d, 0x65, 0x74, - 0x61, 0x64, 0x61, 0x74, 0x61, 0x22, 0xce, 0x01, 0x0a, 0x1a, 0x47, 0x65, 0x74, 0x52, 0x65, 0x6c, - 0x61, 0x74, 0x65, 0x64, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x12, 0x17, 0x0a, 0x07, 0x74, 0x65, 0x61, 0x6d, 0x5f, 0x69, 0x64, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x74, 0x65, 0x61, 0x6d, 0x49, 0x64, 0x12, 0x1d, 0x0a, - 0x0a, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x09, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, 0x64, 0x12, 0x29, 0x0a, 0x04, - 0x6b, 0x69, 0x6e, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x15, 0x2e, 0x61, 0x70, 0x69, - 0x2e, 0x76, 0x31, 0x2e, 0x4b, 0x38, 0x73, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x4b, 0x69, 0x6e, - 0x64, 0x52, 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x69, 0x64, 0x18, 0x04, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x69, 0x64, 0x12, 0x29, 0x0a, 0x0d, 0x6b, 0x61, 0x72, - 0x70, 0x65, 0x6e, 0x74, 0x65, 0x72, 0x4b, 0x69, 0x6e, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, - 0x48, 0x00, 0x52, 0x0d, 0x6b, 0x61, 0x72, 0x70, 0x65, 0x6e, 0x74, 0x65, 0x72, 0x4b, 0x69, 0x6e, - 0x64, 0x88, 0x01, 0x01, 0x42, 0x10, 0x0a, 0x0e, 0x5f, 0x6b, 0x61, 0x72, 0x70, 0x65, 0x6e, 0x74, - 0x65, 0x72, 0x4b, 0x69, 0x6e, 0x64, 0x22, 0xdd, 0x01, 0x0a, 0x1d, 0x47, 0x65, 0x74, 0x57, 0x6f, - 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x50, 0x6f, 0x64, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x77, 0x6f, 0x72, 0x6b, - 0x6c, 0x6f, 0x61, 0x64, 0x5f, 0x75, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, - 0x77, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x55, 0x69, 0x64, 0x12, 0x3c, 0x0a, 0x0c, 0x72, - 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x5f, 0x73, 0x65, 0x74, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, - 0x0b, 0x32, 0x19, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x70, 0x6c, 0x69, - 0x63, 0x61, 0x53, 0x65, 0x74, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x52, 0x0b, 0x72, 0x65, - 0x70, 0x6c, 0x69, 0x63, 0x61, 0x53, 0x65, 0x74, 0x73, 0x12, 0x33, 0x0a, 0x0b, 0x64, 0x69, 0x72, - 0x65, 0x63, 0x74, 0x5f, 0x70, 0x6f, 0x64, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x12, - 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x6f, 0x64, 0x48, 0x69, 0x73, 0x74, 0x6f, - 0x72, 0x79, 0x52, 0x0a, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x50, 0x6f, 0x64, 0x73, 0x12, 0x26, - 0x0a, 0x04, 0x6a, 0x6f, 0x62, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x61, - 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4a, 0x6f, 0x62, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, - 0x52, 0x04, 0x6a, 0x6f, 0x62, 0x73, 0x22, 0xe8, 0x01, 0x0a, 0x0a, 0x4a, 0x6f, 0x62, 0x48, 0x69, - 0x73, 0x74, 0x6f, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x03, 0x75, 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x73, - 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x74, 0x61, - 0x74, 0x75, 0x73, 0x12, 0x39, 0x0a, 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, - 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, - 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, - 0x61, 0x6d, 0x70, 0x52, 0x09, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x12, 0x39, - 0x0a, 0x0a, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x05, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, - 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x41, 0x74, 0x12, 0x26, 0x0a, 0x04, 0x70, 0x6f, 0x64, - 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, - 0x2e, 0x50, 0x6f, 0x64, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x52, 0x04, 0x70, 0x6f, 0x64, - 0x73, 0x22, 0xf3, 0x01, 0x0a, 0x11, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x53, 0x65, 0x74, - 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x69, 0x64, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, - 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x39, 0x0a, - 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, - 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x63, - 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x12, 0x39, 0x0a, 0x0a, 0x64, 0x65, 0x6c, 0x65, - 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, - 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, - 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, - 0x64, 0x41, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x72, 0x65, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x18, - 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x72, 0x65, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x12, - 0x26, 0x0a, 0x04, 0x70, 0x6f, 0x64, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x12, 0x2e, - 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x6f, 0x64, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, - 0x79, 0x52, 0x04, 0x70, 0x6f, 0x64, 0x73, 0x22, 0xf9, 0x01, 0x0a, 0x0a, 0x50, 0x6f, 0x64, 0x48, - 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x69, 0x64, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1c, 0x0a, 0x09, - 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x70, 0x68, - 0x61, 0x73, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x70, 0x68, 0x61, 0x73, 0x65, - 0x12, 0x1b, 0x0a, 0x09, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x05, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x08, 0x6e, 0x6f, 0x64, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x39, 0x0a, - 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, - 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x63, - 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x12, 0x39, 0x0a, 0x0a, 0x64, 0x65, 0x6c, 0x65, - 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, - 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, - 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, - 0x64, 0x41, 0x74, 0x22, 0xb5, 0x01, 0x0a, 0x1b, 0x47, 0x65, 0x74, 0x52, 0x65, 0x6c, 0x61, 0x74, - 0x65, 0x64, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x12, 0x38, 0x0a, 0x09, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, - 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, - 0x4b, 0x38, 0x73, 0x52, 0x65, 0x6c, 0x61, 0x74, 0x65, 0x64, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, - 0x63, 0x65, 0x52, 0x09, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x2d, 0x0a, - 0x05, 0x6e, 0x6f, 0x64, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x61, - 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4b, 0x38, 0x73, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, - 0x65, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x05, 0x6e, 0x6f, 0x64, 0x65, 0x73, 0x12, 0x2d, 0x0a, 0x05, - 0x65, 0x64, 0x67, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x61, 0x70, - 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4b, 0x38, 0x73, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, - 0x45, 0x64, 0x67, 0x65, 0x52, 0x05, 0x65, 0x64, 0x67, 0x65, 0x73, 0x22, 0x83, 0x02, 0x0a, 0x12, - 0x4b, 0x38, 0x73, 0x52, 0x65, 0x6c, 0x61, 0x74, 0x65, 0x64, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, - 0x63, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x6b, 0x69, 0x6e, - 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x4b, - 0x69, 0x6e, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x75, 0x69, - 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x55, - 0x69, 0x64, 0x12, 0x1f, 0x0a, 0x0b, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x6e, 0x61, 0x6d, - 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x4e, - 0x61, 0x6d, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x5f, 0x6b, 0x69, - 0x6e, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, - 0x4b, 0x69, 0x6e, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x5f, 0x75, - 0x69, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, - 0x55, 0x69, 0x64, 0x12, 0x1f, 0x0a, 0x0b, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x5f, 0x6e, 0x61, - 0x6d, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, - 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x2b, 0x0a, 0x11, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x73, 0x68, 0x69, 0x70, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x10, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x68, 0x69, 0x70, 0x54, 0x79, 0x70, - 0x65, 0x22, 0x49, 0x0a, 0x0f, 0x4b, 0x38, 0x73, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, - 0x4e, 0x6f, 0x64, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x02, 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, - 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0x67, 0x0a, 0x0f, - 0x4b, 0x38, 0x73, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x45, 0x64, 0x67, 0x65, 0x12, - 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, - 0x16, 0x0a, 0x06, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x06, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x74, 0x61, 0x72, 0x67, 0x65, - 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x12, - 0x14, 0x0a, 0x05, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, - 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x22, 0x4f, 0x0a, 0x15, 0x47, 0x65, 0x74, 0x43, 0x6c, 0x75, 0x73, - 0x74, 0x65, 0x72, 0x54, 0x79, 0x70, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x17, - 0x0a, 0x07, 0x74, 0x65, 0x61, 0x6d, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x06, 0x74, 0x65, 0x61, 0x6d, 0x49, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x6c, 0x75, 0x73, 0x74, - 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x6c, 0x75, - 0x73, 0x74, 0x65, 0x72, 0x49, 0x64, 0x22, 0x79, 0x0a, 0x16, 0x47, 0x65, 0x74, 0x43, 0x6c, 0x75, - 0x73, 0x74, 0x65, 0x72, 0x54, 0x79, 0x70, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x12, 0x36, 0x0a, 0x0c, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x74, 0x79, 0x70, 0x65, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x13, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, - 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x54, 0x79, 0x70, 0x65, 0x52, 0x0b, 0x63, 0x6c, 0x75, - 0x73, 0x74, 0x65, 0x72, 0x54, 0x79, 0x70, 0x65, 0x12, 0x27, 0x0a, 0x0f, 0x6b, 0x75, 0x62, 0x65, - 0x6c, 0x65, 0x74, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x0e, 0x6b, 0x75, 0x62, 0x65, 0x6c, 0x65, 0x74, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, - 0x6e, 0x22, 0x53, 0x0a, 0x19, 0x47, 0x65, 0x74, 0x4d, 0x69, 0x67, 0x50, 0x61, 0x72, 0x74, 0x65, - 0x64, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x17, - 0x0a, 0x07, 0x74, 0x65, 0x61, 0x6d, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x06, 0x74, 0x65, 0x61, 0x6d, 0x49, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x6c, 0x75, 0x73, 0x74, - 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x6c, 0x75, - 0x73, 0x74, 0x65, 0x72, 0x49, 0x64, 0x22, 0x94, 0x02, 0x0a, 0x1a, 0x47, 0x65, 0x74, 0x4d, 0x69, - 0x67, 0x50, 0x61, 0x72, 0x74, 0x65, 0x64, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x66, 0x6f, 0x75, 0x6e, 0x64, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, 0x66, 0x6f, 0x75, 0x6e, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, - 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, - 0x1c, 0x0a, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x03, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x18, 0x0a, - 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, - 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x28, 0x0a, 0x10, 0x6d, 0x69, 0x67, 0x5f, 0x63, - 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x73, 0x5f, 0x6a, 0x73, 0x6f, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x0e, 0x6d, 0x69, 0x67, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x73, 0x4a, 0x73, 0x6f, - 0x6e, 0x12, 0x10, 0x0a, 0x03, 0x72, 0x61, 0x77, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, - 0x72, 0x61, 0x77, 0x12, 0x1f, 0x0a, 0x0b, 0x70, 0x61, 0x72, 0x73, 0x65, 0x5f, 0x65, 0x72, 0x72, - 0x6f, 0x72, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x70, 0x61, 0x72, 0x73, 0x65, 0x45, - 0x72, 0x72, 0x6f, 0x72, 0x12, 0x37, 0x0a, 0x09, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x73, 0x65, 0x65, - 0x6e, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, - 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, - 0x61, 0x6d, 0x70, 0x52, 0x08, 0x6c, 0x61, 0x73, 0x74, 0x53, 0x65, 0x65, 0x6e, 0x22, 0xea, 0x01, - 0x0a, 0x18, 0x47, 0x65, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x73, 0x53, 0x74, - 0x61, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x17, 0x0a, 0x07, 0x74, 0x65, - 0x61, 0x6d, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x74, 0x65, 0x61, - 0x6d, 0x49, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x69, - 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, - 0x49, 0x64, 0x12, 0x3e, 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x74, 0x69, 0x6d, 0x65, - 0x18, 0x15, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, - 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, - 0x6d, 0x70, 0x48, 0x00, 0x52, 0x09, 0x73, 0x74, 0x61, 0x72, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x88, - 0x01, 0x01, 0x12, 0x3a, 0x0a, 0x08, 0x65, 0x6e, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x16, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, - 0x48, 0x01, 0x52, 0x07, 0x65, 0x6e, 0x64, 0x54, 0x69, 0x6d, 0x65, 0x88, 0x01, 0x01, 0x42, 0x0d, - 0x0a, 0x0b, 0x5f, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x42, 0x0b, 0x0a, - 0x09, 0x5f, 0x65, 0x6e, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x22, 0xcf, 0x01, 0x0a, 0x19, 0x47, - 0x65, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x73, 0x53, 0x74, 0x61, 0x74, 0x73, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x6f, 0x74, 0x61, - 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x12, 0x1c, - 0x0a, 0x09, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x69, 0x7a, 0x65, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x05, 0x52, 0x09, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x69, 0x7a, 0x65, 0x64, 0x12, 0x20, 0x0a, 0x0b, - 0x75, 0x6e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x69, 0x7a, 0x65, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, - 0x05, 0x52, 0x0b, 0x75, 0x6e, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x69, 0x7a, 0x65, 0x64, 0x12, 0x29, - 0x0a, 0x10, 0x77, 0x69, 0x74, 0x68, 0x6f, 0x75, 0x74, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0f, 0x77, 0x69, 0x74, 0x68, 0x6f, 0x75, - 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x73, 0x12, 0x31, 0x0a, 0x14, 0x6f, 0x70, 0x74, - 0x69, 0x6d, 0x69, 0x7a, 0x65, 0x64, 0x5f, 0x70, 0x65, 0x72, 0x63, 0x65, 0x6e, 0x74, 0x61, 0x67, - 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x05, 0x52, 0x13, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x69, 0x7a, - 0x65, 0x64, 0x50, 0x65, 0x72, 0x63, 0x65, 0x6e, 0x74, 0x61, 0x67, 0x65, 0x22, 0xc5, 0x01, 0x0a, - 0x17, 0x44, 0x61, 0x69, 0x6c, 0x79, 0x55, 0x74, 0x69, 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x17, 0x0a, 0x07, 0x74, 0x65, 0x61, 0x6d, - 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x74, 0x65, 0x61, 0x6d, 0x49, - 0x64, 0x12, 0x1f, 0x0a, 0x0b, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x73, - 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0a, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, - 0x64, 0x73, 0x12, 0x39, 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x74, 0x69, 0x6d, 0x65, - 0x18, 0x15, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, - 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, - 0x6d, 0x70, 0x52, 0x09, 0x73, 0x74, 0x61, 0x72, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x35, 0x0a, - 0x08, 0x65, 0x6e, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x16, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x29, 0x0a, 0x04, 0x6b, 0x69, 0x6e, 0x64, + 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x15, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, + 0x4b, 0x38, 0x73, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x4b, 0x69, 0x6e, 0x64, 0x52, 0x04, 0x6b, + 0x69, 0x6e, 0x64, 0x1a, 0x5d, 0x0a, 0x19, 0x50, 0x6f, 0x64, 0x55, 0x69, 0x64, 0x54, 0x6f, 0x4e, + 0x6f, 0x64, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x45, 0x6e, 0x74, 0x72, 0x79, + 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, + 0x65, 0x79, 0x12, 0x2a, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x14, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x4d, + 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, + 0x38, 0x01, 0x1a, 0x5b, 0x0a, 0x18, 0x50, 0x6f, 0x64, 0x55, 0x69, 0x64, 0x54, 0x6f, 0x50, 0x6f, + 0x64, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, + 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, + 0x12, 0x29, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x13, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x6f, 0x64, 0x4d, 0x65, 0x74, 0x61, + 0x64, 0x61, 0x74, 0x61, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, + 0x9f, 0x01, 0x0a, 0x0b, 0x50, 0x6f, 0x64, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, + 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, + 0x61, 0x6d, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x18, 0x06, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x05, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x12, 0x1e, 0x0a, 0x0a, 0x61, 0x6e, 0x6e, + 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x61, + 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x70, 0x65, + 0x63, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x73, 0x70, 0x65, 0x63, 0x12, 0x16, 0x0a, + 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, + 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, + 0x61, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, + 0x61, 0x22, 0xaa, 0x02, 0x0a, 0x0c, 0x4e, 0x6f, 0x64, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, + 0x74, 0x61, 0x12, 0x1b, 0x0a, 0x09, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6e, 0x6f, 0x64, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, + 0x26, 0x0a, 0x0f, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x6e, 0x61, + 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x6e, 0x6f, 0x64, 0x65, 0x47, 0x72, + 0x6f, 0x75, 0x70, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x26, 0x0a, 0x0f, 0x6e, 0x6f, 0x64, 0x65, 0x5f, + 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x0d, 0x6e, 0x6f, 0x64, 0x65, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x54, 0x79, 0x70, 0x65, 0x12, + 0x2f, 0x0a, 0x14, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x5f, 0x69, 0x6e, 0x73, 0x74, 0x5f, 0x74, 0x79, + 0x70, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x11, 0x63, + 0x6c, 0x6f, 0x75, 0x64, 0x49, 0x6e, 0x73, 0x74, 0x54, 0x79, 0x70, 0x65, 0x4e, 0x61, 0x6d, 0x65, + 0x12, 0x14, 0x0a, 0x05, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x05, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x12, 0x1e, 0x0a, 0x0a, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x61, 0x6e, 0x6e, 0x6f, + 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x70, 0x65, 0x63, 0x18, 0x08, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x73, 0x70, 0x65, 0x63, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, + 0x61, 0x74, 0x75, 0x73, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, + 0x75, 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x0a, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x22, 0xce, + 0x01, 0x0a, 0x1a, 0x47, 0x65, 0x74, 0x52, 0x65, 0x6c, 0x61, 0x74, 0x65, 0x64, 0x52, 0x65, 0x73, + 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x17, 0x0a, + 0x07, 0x74, 0x65, 0x61, 0x6d, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, + 0x74, 0x65, 0x61, 0x6d, 0x49, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, + 0x72, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x6c, 0x75, 0x73, + 0x74, 0x65, 0x72, 0x49, 0x64, 0x12, 0x29, 0x0a, 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x0e, 0x32, 0x15, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4b, 0x38, 0x73, + 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x4b, 0x69, 0x6e, 0x64, 0x52, 0x04, 0x6b, 0x69, 0x6e, 0x64, + 0x12, 0x10, 0x0a, 0x03, 0x75, 0x69, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, + 0x69, 0x64, 0x12, 0x29, 0x0a, 0x0d, 0x6b, 0x61, 0x72, 0x70, 0x65, 0x6e, 0x74, 0x65, 0x72, 0x4b, + 0x69, 0x6e, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x0d, 0x6b, 0x61, 0x72, + 0x70, 0x65, 0x6e, 0x74, 0x65, 0x72, 0x4b, 0x69, 0x6e, 0x64, 0x88, 0x01, 0x01, 0x42, 0x10, 0x0a, + 0x0e, 0x5f, 0x6b, 0x61, 0x72, 0x70, 0x65, 0x6e, 0x74, 0x65, 0x72, 0x4b, 0x69, 0x6e, 0x64, 0x22, + 0xdd, 0x01, 0x0a, 0x1d, 0x47, 0x65, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x50, + 0x6f, 0x64, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x77, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x5f, 0x75, 0x69, + 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x77, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, + 0x64, 0x55, 0x69, 0x64, 0x12, 0x3c, 0x0a, 0x0c, 0x72, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x5f, + 0x73, 0x65, 0x74, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x61, 0x70, 0x69, + 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x53, 0x65, 0x74, 0x48, 0x69, + 0x73, 0x74, 0x6f, 0x72, 0x79, 0x52, 0x0b, 0x72, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x53, 0x65, + 0x74, 0x73, 0x12, 0x33, 0x0a, 0x0b, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x5f, 0x70, 0x6f, 0x64, + 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, + 0x2e, 0x50, 0x6f, 0x64, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x52, 0x0a, 0x64, 0x69, 0x72, + 0x65, 0x63, 0x74, 0x50, 0x6f, 0x64, 0x73, 0x12, 0x26, 0x0a, 0x04, 0x6a, 0x6f, 0x62, 0x73, 0x18, + 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4a, + 0x6f, 0x62, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x52, 0x04, 0x6a, 0x6f, 0x62, 0x73, 0x22, + 0xe8, 0x01, 0x0a, 0x0a, 0x4a, 0x6f, 0x62, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x12, 0x10, + 0x0a, 0x03, 0x75, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x69, 0x64, + 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, + 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x39, 0x0a, 0x0a, + 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, + 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x63, 0x72, + 0x65, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x12, 0x39, 0x0a, 0x0a, 0x64, 0x65, 0x6c, 0x65, 0x74, + 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, + 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, + 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x64, + 0x41, 0x74, 0x12, 0x26, 0x0a, 0x04, 0x70, 0x6f, 0x64, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, + 0x32, 0x12, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x6f, 0x64, 0x48, 0x69, 0x73, + 0x74, 0x6f, 0x72, 0x79, 0x52, 0x04, 0x70, 0x6f, 0x64, 0x73, 0x22, 0xf3, 0x01, 0x0a, 0x11, 0x52, + 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x53, 0x65, 0x74, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, + 0x12, 0x10, 0x0a, 0x03, 0x75, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, + 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x39, 0x0a, 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, + 0x64, 0x5f, 0x61, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, + 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, + 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x41, + 0x74, 0x12, 0x39, 0x0a, 0x0a, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, + 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, + 0x70, 0x52, 0x09, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x41, 0x74, 0x12, 0x1a, 0x0a, 0x08, + 0x72, 0x65, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, + 0x72, 0x65, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x26, 0x0a, 0x04, 0x70, 0x6f, 0x64, 0x73, + 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, + 0x50, 0x6f, 0x64, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x52, 0x04, 0x70, 0x6f, 0x64, 0x73, + 0x22, 0xf9, 0x01, 0x0a, 0x0a, 0x50, 0x6f, 0x64, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x12, + 0x10, 0x0a, 0x03, 0x75, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x69, + 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, + 0x63, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, + 0x61, 0x63, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x70, 0x68, 0x61, 0x73, 0x65, 0x18, 0x04, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x05, 0x70, 0x68, 0x61, 0x73, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x6e, 0x6f, 0x64, + 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6e, 0x6f, + 0x64, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x39, 0x0a, 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, + 0x64, 0x5f, 0x61, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, + 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, + 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x41, + 0x74, 0x12, 0x39, 0x0a, 0x0a, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, + 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, + 0x70, 0x52, 0x09, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x41, 0x74, 0x22, 0xb5, 0x01, 0x0a, + 0x1b, 0x47, 0x65, 0x74, 0x52, 0x65, 0x6c, 0x61, 0x74, 0x65, 0x64, 0x52, 0x65, 0x73, 0x6f, 0x75, + 0x72, 0x63, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x38, 0x0a, 0x09, + 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, + 0x1a, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4b, 0x38, 0x73, 0x52, 0x65, 0x6c, 0x61, + 0x74, 0x65, 0x64, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x09, 0x72, 0x65, 0x6c, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x2d, 0x0a, 0x05, 0x6e, 0x6f, 0x64, 0x65, 0x73, 0x18, + 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4b, + 0x38, 0x73, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x05, + 0x6e, 0x6f, 0x64, 0x65, 0x73, 0x12, 0x2d, 0x0a, 0x05, 0x65, 0x64, 0x67, 0x65, 0x73, 0x18, 0x03, + 0x20, 0x03, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4b, 0x38, + 0x73, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x45, 0x64, 0x67, 0x65, 0x52, 0x05, 0x65, + 0x64, 0x67, 0x65, 0x73, 0x22, 0x83, 0x02, 0x0a, 0x12, 0x4b, 0x38, 0x73, 0x52, 0x65, 0x6c, 0x61, + 0x74, 0x65, 0x64, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x73, + 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x6b, 0x69, 0x6e, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x0a, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x4b, 0x69, 0x6e, 0x64, 0x12, 0x1d, 0x0a, 0x0a, + 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x75, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x09, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x55, 0x69, 0x64, 0x12, 0x1f, 0x0a, 0x0b, 0x73, + 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x0a, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x1f, 0x0a, 0x0b, + 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x5f, 0x6b, 0x69, 0x6e, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x0a, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x4b, 0x69, 0x6e, 0x64, 0x12, 0x1d, 0x0a, + 0x0a, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x5f, 0x75, 0x69, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x09, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x55, 0x69, 0x64, 0x12, 0x1f, 0x0a, 0x0b, + 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x0a, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x2b, 0x0a, + 0x11, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x68, 0x69, 0x70, 0x5f, 0x74, 0x79, + 0x70, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x73, 0x68, 0x69, 0x70, 0x54, 0x79, 0x70, 0x65, 0x22, 0x49, 0x0a, 0x0f, 0x4b, 0x38, + 0x73, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x4e, 0x6f, 0x64, 0x65, 0x12, 0x0e, 0x0a, + 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x12, 0x0a, + 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6b, 0x69, 0x6e, + 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0x67, 0x0a, 0x0f, 0x4b, 0x38, 0x73, 0x52, 0x65, 0x73, 0x6f, + 0x75, 0x72, 0x63, 0x65, 0x45, 0x64, 0x67, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x6f, 0x75, 0x72, + 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, + 0x12, 0x16, 0x0a, 0x06, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x06, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x6c, 0x61, 0x62, 0x65, + 0x6c, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x22, 0x4f, + 0x0a, 0x15, 0x47, 0x65, 0x74, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x54, 0x79, 0x70, 0x65, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x17, 0x0a, 0x07, 0x74, 0x65, 0x61, 0x6d, 0x5f, + 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x74, 0x65, 0x61, 0x6d, 0x49, 0x64, + 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, 0x64, 0x22, + 0x79, 0x0a, 0x16, 0x47, 0x65, 0x74, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x54, 0x79, 0x70, + 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x36, 0x0a, 0x0c, 0x63, 0x6c, 0x75, + 0x73, 0x74, 0x65, 0x72, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, + 0x13, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, + 0x54, 0x79, 0x70, 0x65, 0x52, 0x0b, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x54, 0x79, 0x70, + 0x65, 0x12, 0x27, 0x0a, 0x0f, 0x6b, 0x75, 0x62, 0x65, 0x6c, 0x65, 0x74, 0x5f, 0x76, 0x65, 0x72, + 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x6b, 0x75, 0x62, 0x65, + 0x6c, 0x65, 0x74, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x53, 0x0a, 0x19, 0x47, 0x65, + 0x74, 0x4d, 0x69, 0x67, 0x50, 0x61, 0x72, 0x74, 0x65, 0x64, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x17, 0x0a, 0x07, 0x74, 0x65, 0x61, 0x6d, 0x5f, + 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x74, 0x65, 0x61, 0x6d, 0x49, 0x64, + 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, 0x64, 0x22, + 0x94, 0x02, 0x0a, 0x1a, 0x47, 0x65, 0x74, 0x4d, 0x69, 0x67, 0x50, 0x61, 0x72, 0x74, 0x65, 0x64, + 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x14, + 0x0a, 0x05, 0x66, 0x6f, 0x75, 0x6e, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, 0x66, + 0x6f, 0x75, 0x6e, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x6e, 0x61, 0x6d, 0x65, + 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6e, 0x61, 0x6d, + 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, + 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, + 0x12, 0x28, 0x0a, 0x10, 0x6d, 0x69, 0x67, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x73, 0x5f, + 0x6a, 0x73, 0x6f, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x6d, 0x69, 0x67, 0x43, + 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x73, 0x4a, 0x73, 0x6f, 0x6e, 0x12, 0x10, 0x0a, 0x03, 0x72, 0x61, + 0x77, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x72, 0x61, 0x77, 0x12, 0x1f, 0x0a, 0x0b, + 0x70, 0x61, 0x72, 0x73, 0x65, 0x5f, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x07, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x0a, 0x70, 0x61, 0x72, 0x73, 0x65, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x37, 0x0a, + 0x09, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x73, 0x65, 0x65, 0x6e, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, + 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x08, 0x6c, 0x61, + 0x73, 0x74, 0x53, 0x65, 0x65, 0x6e, 0x22, 0xea, 0x01, 0x0a, 0x18, 0x47, 0x65, 0x74, 0x57, 0x6f, + 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x73, 0x53, 0x74, 0x61, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x12, 0x17, 0x0a, 0x07, 0x74, 0x65, 0x61, 0x6d, 0x5f, 0x69, 0x64, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x74, 0x65, 0x61, 0x6d, 0x49, 0x64, 0x12, 0x1d, 0x0a, 0x0a, + 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x09, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, 0x64, 0x12, 0x3e, 0x0a, 0x0a, 0x73, + 0x74, 0x61, 0x72, 0x74, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x15, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, - 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x07, 0x65, 0x6e, 0x64, - 0x54, 0x69, 0x6d, 0x65, 0x22, 0xcc, 0x05, 0x0a, 0x18, 0x44, 0x61, 0x69, 0x6c, 0x79, 0x55, 0x74, - 0x69, 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x12, 0x98, 0x01, 0x0a, 0x26, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x69, 0x64, - 0x5f, 0x74, 0x6f, 0x5f, 0x64, 0x61, 0x69, 0x6c, 0x79, 0x5f, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, - 0x63, 0x6f, 0x72, 0x65, 0x5f, 0x6d, 0x69, 0x6e, 0x75, 0x74, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, - 0x28, 0x0b, 0x32, 0x46, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x61, 0x69, 0x6c, - 0x79, 0x55, 0x74, 0x69, 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, 0x64, 0x54, 0x6f, - 0x44, 0x61, 0x69, 0x6c, 0x79, 0x54, 0x6f, 0x74, 0x61, 0x6c, 0x43, 0x6f, 0x72, 0x65, 0x4d, 0x69, - 0x6e, 0x75, 0x74, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x20, 0x63, 0x6c, 0x75, 0x73, - 0x74, 0x65, 0x72, 0x49, 0x64, 0x54, 0x6f, 0x44, 0x61, 0x69, 0x6c, 0x79, 0x54, 0x6f, 0x74, 0x61, - 0x6c, 0x43, 0x6f, 0x72, 0x65, 0x4d, 0x69, 0x6e, 0x75, 0x74, 0x65, 0x73, 0x12, 0x62, 0x0a, 0x12, - 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x5f, 0x74, 0x6f, 0x5f, 0x6d, 0x65, - 0x74, 0x61, 0x18, 0x0b, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x35, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, - 0x31, 0x2e, 0x44, 0x61, 0x69, 0x6c, 0x79, 0x55, 0x74, 0x69, 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x43, 0x6c, 0x75, 0x73, 0x74, - 0x65, 0x72, 0x49, 0x64, 0x54, 0x6f, 0x4d, 0x65, 0x74, 0x61, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, - 0x0f, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, 0x64, 0x54, 0x6f, 0x4d, 0x65, 0x74, 0x61, - 0x12, 0x3e, 0x0a, 0x1c, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x63, 0x6f, 0x72, 0x65, 0x5f, 0x6d, - 0x69, 0x6e, 0x75, 0x74, 0x65, 0x73, 0x5f, 0x69, 0x6e, 0x5f, 0x70, 0x65, 0x72, 0x69, 0x6f, 0x64, - 0x18, 0x15, 0x20, 0x01, 0x28, 0x05, 0x52, 0x18, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x43, 0x6f, 0x72, - 0x65, 0x4d, 0x69, 0x6e, 0x75, 0x74, 0x65, 0x73, 0x49, 0x6e, 0x50, 0x65, 0x72, 0x69, 0x6f, 0x64, - 0x1a, 0x58, 0x0a, 0x0a, 0x44, 0x61, 0x74, 0x61, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x12, 0x4a, - 0x0a, 0x0a, 0x64, 0x61, 0x74, 0x61, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, - 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x61, 0x69, 0x6c, - 0x79, 0x55, 0x74, 0x69, 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x44, 0x61, 0x74, 0x61, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x52, 0x0a, - 0x64, 0x61, 0x74, 0x61, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x1a, 0x3f, 0x0a, 0x09, 0x44, 0x61, - 0x74, 0x61, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, - 0x74, 0x61, 0x6d, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, - 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x06, - 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x1a, 0x80, 0x01, 0x0a, 0x25, - 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, 0x64, 0x54, 0x6f, 0x44, 0x61, 0x69, 0x6c, 0x79, - 0x54, 0x6f, 0x74, 0x61, 0x6c, 0x43, 0x6f, 0x72, 0x65, 0x4d, 0x69, 0x6e, 0x75, 0x74, 0x65, 0x73, - 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x41, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, - 0x44, 0x61, 0x69, 0x6c, 0x79, 0x55, 0x74, 0x69, 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x44, 0x61, 0x74, 0x61, 0x70, 0x6f, 0x69, - 0x6e, 0x74, 0x73, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x53, - 0x0a, 0x14, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, 0x64, 0x54, 0x6f, 0x4d, 0x65, 0x74, - 0x61, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x25, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, - 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, - 0x2e, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, - 0x02, 0x38, 0x01, 0x22, 0xd1, 0x01, 0x0a, 0x23, 0x44, 0x61, 0x69, 0x6c, 0x79, 0x55, 0x74, 0x69, - 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, - 0x54, 0x79, 0x70, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x17, 0x0a, 0x07, 0x74, - 0x65, 0x61, 0x6d, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x74, 0x65, - 0x61, 0x6d, 0x49, 0x64, 0x12, 0x1f, 0x0a, 0x0b, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, - 0x69, 0x64, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0a, 0x63, 0x6c, 0x75, 0x73, 0x74, - 0x65, 0x72, 0x49, 0x64, 0x73, 0x12, 0x39, 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x74, - 0x69, 0x6d, 0x65, 0x18, 0x15, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, - 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, - 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x73, 0x74, 0x61, 0x72, 0x74, 0x54, 0x69, 0x6d, 0x65, - 0x12, 0x35, 0x0a, 0x08, 0x65, 0x6e, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x16, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x07, - 0x65, 0x6e, 0x64, 0x54, 0x69, 0x6d, 0x65, 0x22, 0x96, 0x07, 0x0a, 0x24, 0x44, 0x61, 0x69, 0x6c, - 0x79, 0x55, 0x74, 0x69, 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x6e, 0x73, 0x74, - 0x61, 0x6e, 0x63, 0x65, 0x54, 0x79, 0x70, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x12, 0x80, 0x01, 0x0a, 0x18, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x5f, - 0x74, 0x6f, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x18, 0x01, 0x20, - 0x03, 0x28, 0x0b, 0x32, 0x47, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x61, 0x69, - 0x6c, 0x79, 0x55, 0x74, 0x69, 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x6e, 0x73, - 0x74, 0x61, 0x6e, 0x63, 0x65, 0x54, 0x79, 0x70, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x2e, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, 0x64, 0x54, 0x6f, 0x44, 0x61, 0x74, - 0x61, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x15, 0x63, 0x6c, - 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, 0x64, 0x54, 0x6f, 0x44, 0x61, 0x74, 0x61, 0x70, 0x6f, 0x69, - 0x6e, 0x74, 0x73, 0x12, 0x6e, 0x0a, 0x12, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x69, - 0x64, 0x5f, 0x74, 0x6f, 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x18, 0x0b, 0x20, 0x03, 0x28, 0x0b, 0x32, - 0x41, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x61, 0x69, 0x6c, 0x79, 0x55, 0x74, - 0x69, 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, - 0x65, 0x54, 0x79, 0x70, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x43, 0x6c, - 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, 0x64, 0x54, 0x6f, 0x4d, 0x65, 0x74, 0x61, 0x45, 0x6e, 0x74, - 0x72, 0x79, 0x52, 0x0f, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, 0x64, 0x54, 0x6f, 0x4d, - 0x65, 0x74, 0x61, 0x1a, 0x64, 0x0a, 0x0a, 0x44, 0x61, 0x74, 0x61, 0x70, 0x6f, 0x69, 0x6e, 0x74, - 0x73, 0x12, 0x56, 0x0a, 0x0a, 0x64, 0x61, 0x74, 0x61, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x18, - 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x36, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x44, - 0x61, 0x69, 0x6c, 0x79, 0x55, 0x74, 0x69, 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, - 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x54, 0x79, 0x70, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x2e, 0x44, 0x61, 0x74, 0x61, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x52, 0x0a, 0x64, - 0x61, 0x74, 0x61, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x1a, 0xbb, 0x02, 0x0a, 0x09, 0x44, 0x61, - 0x74, 0x61, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, - 0x74, 0x61, 0x6d, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, - 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0xb7, 0x01, 0x0a, 0x29, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, - 0x63, 0x65, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x5f, 0x74, 0x6f, 0x5f, 0x64, 0x61, 0x69, 0x6c, 0x79, - 0x5f, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x63, 0x6f, 0x72, 0x65, 0x5f, 0x6d, 0x69, 0x6e, 0x75, - 0x74, 0x65, 0x73, 0x18, 0x0b, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x5f, 0x2e, 0x61, 0x70, 0x69, 0x2e, - 0x76, 0x31, 0x2e, 0x44, 0x61, 0x69, 0x6c, 0x79, 0x55, 0x74, 0x69, 0x6c, 0x69, 0x7a, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x54, 0x79, 0x70, 0x65, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x44, 0x61, 0x74, 0x61, 0x70, 0x6f, 0x69, 0x6e, - 0x74, 0x2e, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x54, 0x79, 0x70, 0x65, 0x54, 0x6f, + 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x48, 0x00, 0x52, 0x09, 0x73, + 0x74, 0x61, 0x72, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x88, 0x01, 0x01, 0x12, 0x3a, 0x0a, 0x08, 0x65, + 0x6e, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x16, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, + 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, + 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x48, 0x01, 0x52, 0x07, 0x65, 0x6e, 0x64, + 0x54, 0x69, 0x6d, 0x65, 0x88, 0x01, 0x01, 0x42, 0x0d, 0x0a, 0x0b, 0x5f, 0x73, 0x74, 0x61, 0x72, + 0x74, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x42, 0x0b, 0x0a, 0x09, 0x5f, 0x65, 0x6e, 0x64, 0x5f, 0x74, + 0x69, 0x6d, 0x65, 0x22, 0xcf, 0x01, 0x0a, 0x19, 0x47, 0x65, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x6c, + 0x6f, 0x61, 0x64, 0x73, 0x53, 0x74, 0x61, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, + 0x52, 0x05, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x12, 0x1c, 0x0a, 0x09, 0x6f, 0x70, 0x74, 0x69, 0x6d, + 0x69, 0x7a, 0x65, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x09, 0x6f, 0x70, 0x74, 0x69, + 0x6d, 0x69, 0x7a, 0x65, 0x64, 0x12, 0x20, 0x0a, 0x0b, 0x75, 0x6e, 0x6f, 0x70, 0x74, 0x69, 0x6d, + 0x69, 0x7a, 0x65, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0b, 0x75, 0x6e, 0x6f, 0x70, + 0x74, 0x69, 0x6d, 0x69, 0x7a, 0x65, 0x64, 0x12, 0x29, 0x0a, 0x10, 0x77, 0x69, 0x74, 0x68, 0x6f, + 0x75, 0x74, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, + 0x05, 0x52, 0x0f, 0x77, 0x69, 0x74, 0x68, 0x6f, 0x75, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x73, 0x12, 0x31, 0x0a, 0x14, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x69, 0x7a, 0x65, 0x64, 0x5f, + 0x70, 0x65, 0x72, 0x63, 0x65, 0x6e, 0x74, 0x61, 0x67, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x05, + 0x52, 0x13, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x69, 0x7a, 0x65, 0x64, 0x50, 0x65, 0x72, 0x63, 0x65, + 0x6e, 0x74, 0x61, 0x67, 0x65, 0x22, 0xc5, 0x01, 0x0a, 0x17, 0x44, 0x61, 0x69, 0x6c, 0x79, 0x55, + 0x74, 0x69, 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x12, 0x17, 0x0a, 0x07, 0x74, 0x65, 0x61, 0x6d, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x06, 0x74, 0x65, 0x61, 0x6d, 0x49, 0x64, 0x12, 0x1f, 0x0a, 0x0b, 0x63, 0x6c, + 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, + 0x0a, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, 0x64, 0x73, 0x12, 0x39, 0x0a, 0x0a, 0x73, + 0x74, 0x61, 0x72, 0x74, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x15, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, + 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x73, 0x74, 0x61, + 0x72, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x35, 0x0a, 0x08, 0x65, 0x6e, 0x64, 0x5f, 0x74, 0x69, + 0x6d, 0x65, 0x18, 0x16, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, + 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, + 0x74, 0x61, 0x6d, 0x70, 0x52, 0x07, 0x65, 0x6e, 0x64, 0x54, 0x69, 0x6d, 0x65, 0x22, 0xcc, 0x05, + 0x0a, 0x18, 0x44, 0x61, 0x69, 0x6c, 0x79, 0x55, 0x74, 0x69, 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x98, 0x01, 0x0a, 0x26, 0x63, + 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x5f, 0x74, 0x6f, 0x5f, 0x64, 0x61, 0x69, + 0x6c, 0x79, 0x5f, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x63, 0x6f, 0x72, 0x65, 0x5f, 0x6d, 0x69, + 0x6e, 0x75, 0x74, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x46, 0x2e, 0x61, 0x70, + 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x61, 0x69, 0x6c, 0x79, 0x55, 0x74, 0x69, 0x6c, 0x69, 0x7a, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x43, 0x6c, + 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, 0x64, 0x54, 0x6f, 0x44, 0x61, 0x69, 0x6c, 0x79, 0x54, 0x6f, + 0x74, 0x61, 0x6c, 0x43, 0x6f, 0x72, 0x65, 0x4d, 0x69, 0x6e, 0x75, 0x74, 0x65, 0x73, 0x45, 0x6e, + 0x74, 0x72, 0x79, 0x52, 0x20, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, 0x64, 0x54, 0x6f, 0x44, 0x61, 0x69, 0x6c, 0x79, 0x54, 0x6f, 0x74, 0x61, 0x6c, 0x43, 0x6f, 0x72, 0x65, 0x4d, 0x69, - 0x6e, 0x75, 0x74, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x23, 0x69, 0x6e, 0x73, 0x74, - 0x61, 0x6e, 0x63, 0x65, 0x54, 0x79, 0x70, 0x65, 0x54, 0x6f, 0x44, 0x61, 0x69, 0x6c, 0x79, 0x54, - 0x6f, 0x74, 0x61, 0x6c, 0x43, 0x6f, 0x72, 0x65, 0x4d, 0x69, 0x6e, 0x75, 0x74, 0x65, 0x73, 0x1a, - 0x56, 0x0a, 0x28, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x54, 0x79, 0x70, 0x65, 0x54, - 0x6f, 0x44, 0x61, 0x69, 0x6c, 0x79, 0x54, 0x6f, 0x74, 0x61, 0x6c, 0x43, 0x6f, 0x72, 0x65, 0x4d, - 0x69, 0x6e, 0x75, 0x74, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, - 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, - 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x76, 0x61, - 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x81, 0x01, 0x0a, 0x1a, 0x43, 0x6c, 0x75, 0x73, - 0x74, 0x65, 0x72, 0x49, 0x64, 0x54, 0x6f, 0x44, 0x61, 0x74, 0x61, 0x70, 0x6f, 0x69, 0x6e, 0x74, - 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x4d, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, - 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x37, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, - 0x2e, 0x44, 0x61, 0x69, 0x6c, 0x79, 0x55, 0x74, 0x69, 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x54, 0x79, 0x70, 0x65, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x44, 0x61, 0x74, 0x61, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, - 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x53, 0x0a, 0x14, 0x43, - 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, 0x64, 0x54, 0x6f, 0x4d, 0x65, 0x74, 0x61, 0x45, 0x6e, - 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x25, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6c, - 0x75, 0x73, 0x74, 0x65, 0x72, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, - 0x22, 0xcd, 0x01, 0x0a, 0x1f, 0x44, 0x61, 0x69, 0x6c, 0x79, 0x55, 0x74, 0x69, 0x6c, 0x69, 0x7a, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4e, 0x6f, 0x64, 0x65, 0x54, 0x79, 0x70, 0x65, 0x52, 0x65, 0x71, + 0x6e, 0x75, 0x74, 0x65, 0x73, 0x12, 0x62, 0x0a, 0x12, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, + 0x5f, 0x69, 0x64, 0x5f, 0x74, 0x6f, 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x18, 0x0b, 0x20, 0x03, 0x28, + 0x0b, 0x32, 0x35, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x61, 0x69, 0x6c, 0x79, + 0x55, 0x74, 0x69, 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x2e, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, 0x64, 0x54, 0x6f, 0x4d, + 0x65, 0x74, 0x61, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0f, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, + 0x72, 0x49, 0x64, 0x54, 0x6f, 0x4d, 0x65, 0x74, 0x61, 0x12, 0x3e, 0x0a, 0x1c, 0x74, 0x6f, 0x74, + 0x61, 0x6c, 0x5f, 0x63, 0x6f, 0x72, 0x65, 0x5f, 0x6d, 0x69, 0x6e, 0x75, 0x74, 0x65, 0x73, 0x5f, + 0x69, 0x6e, 0x5f, 0x70, 0x65, 0x72, 0x69, 0x6f, 0x64, 0x18, 0x15, 0x20, 0x01, 0x28, 0x05, 0x52, + 0x18, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x43, 0x6f, 0x72, 0x65, 0x4d, 0x69, 0x6e, 0x75, 0x74, 0x65, + 0x73, 0x49, 0x6e, 0x50, 0x65, 0x72, 0x69, 0x6f, 0x64, 0x1a, 0x58, 0x0a, 0x0a, 0x44, 0x61, 0x74, + 0x61, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x12, 0x4a, 0x0a, 0x0a, 0x64, 0x61, 0x74, 0x61, 0x70, + 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x61, 0x70, + 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x61, 0x69, 0x6c, 0x79, 0x55, 0x74, 0x69, 0x6c, 0x69, 0x7a, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x44, 0x61, + 0x74, 0x61, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x52, 0x0a, 0x64, 0x61, 0x74, 0x61, 0x70, 0x6f, 0x69, + 0x6e, 0x74, 0x73, 0x1a, 0x3f, 0x0a, 0x09, 0x44, 0x61, 0x74, 0x61, 0x70, 0x6f, 0x69, 0x6e, 0x74, + 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x03, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x14, + 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x76, + 0x61, 0x6c, 0x75, 0x65, 0x1a, 0x80, 0x01, 0x0a, 0x25, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, + 0x49, 0x64, 0x54, 0x6f, 0x44, 0x61, 0x69, 0x6c, 0x79, 0x54, 0x6f, 0x74, 0x61, 0x6c, 0x43, 0x6f, + 0x72, 0x65, 0x4d, 0x69, 0x6e, 0x75, 0x74, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, + 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, + 0x12, 0x41, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x2b, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x61, 0x69, 0x6c, 0x79, 0x55, 0x74, + 0x69, 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x2e, 0x44, 0x61, 0x74, 0x61, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x52, 0x05, 0x76, 0x61, + 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x53, 0x0a, 0x14, 0x43, 0x6c, 0x75, 0x73, 0x74, + 0x65, 0x72, 0x49, 0x64, 0x54, 0x6f, 0x4d, 0x65, 0x74, 0x61, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, + 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, + 0x79, 0x12, 0x25, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x0f, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, + 0x72, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0xd1, 0x01, 0x0a, + 0x23, 0x44, 0x61, 0x69, 0x6c, 0x79, 0x55, 0x74, 0x69, 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x54, 0x79, 0x70, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x17, 0x0a, 0x07, 0x74, 0x65, 0x61, 0x6d, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x74, 0x65, 0x61, 0x6d, 0x49, 0x64, 0x12, 0x1f, 0x0a, 0x0b, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x02, 0x20, 0x03, @@ -12714,534 +16045,713 @@ var file_api_v1_k8s_proto_rawDesc = []byte{ 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x16, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x07, 0x65, 0x6e, 0x64, 0x54, 0x69, 0x6d, 0x65, - 0x22, 0xec, 0x06, 0x0a, 0x20, 0x44, 0x61, 0x69, 0x6c, 0x79, 0x55, 0x74, 0x69, 0x6c, 0x69, 0x7a, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4e, 0x6f, 0x64, 0x65, 0x54, 0x79, 0x70, 0x65, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x7c, 0x0a, 0x18, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, - 0x5f, 0x69, 0x64, 0x5f, 0x74, 0x6f, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x70, 0x6f, 0x69, 0x6e, 0x74, - 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x43, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, - 0x2e, 0x44, 0x61, 0x69, 0x6c, 0x79, 0x55, 0x74, 0x69, 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x4e, 0x6f, 0x64, 0x65, 0x54, 0x79, 0x70, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x2e, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, 0x64, 0x54, 0x6f, 0x44, 0x61, 0x74, - 0x61, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x15, 0x63, 0x6c, - 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, 0x64, 0x54, 0x6f, 0x44, 0x61, 0x74, 0x61, 0x70, 0x6f, 0x69, - 0x6e, 0x74, 0x73, 0x12, 0x6a, 0x0a, 0x12, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x69, - 0x64, 0x5f, 0x74, 0x6f, 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x18, 0x0b, 0x20, 0x03, 0x28, 0x0b, 0x32, - 0x3d, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x61, 0x69, 0x6c, 0x79, 0x55, 0x74, - 0x69, 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4e, 0x6f, 0x64, 0x65, 0x54, 0x79, 0x70, - 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, - 0x72, 0x49, 0x64, 0x54, 0x6f, 0x4d, 0x65, 0x74, 0x61, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0f, - 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, 0x64, 0x54, 0x6f, 0x4d, 0x65, 0x74, 0x61, 0x1a, - 0x60, 0x0a, 0x0a, 0x44, 0x61, 0x74, 0x61, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x12, 0x52, 0x0a, - 0x0a, 0x64, 0x61, 0x74, 0x61, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, - 0x0b, 0x32, 0x32, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x61, 0x69, 0x6c, 0x79, - 0x55, 0x74, 0x69, 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4e, 0x6f, 0x64, 0x65, 0x54, - 0x79, 0x70, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x44, 0x61, 0x74, 0x61, - 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x52, 0x0a, 0x64, 0x61, 0x74, 0x61, 0x70, 0x6f, 0x69, 0x6e, 0x74, - 0x73, 0x1a, 0xa7, 0x02, 0x0a, 0x09, 0x44, 0x61, 0x74, 0x61, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x12, - 0x1c, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x03, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0xa7, 0x01, - 0x0a, 0x25, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x5f, 0x74, 0x6f, 0x5f, 0x64, - 0x61, 0x69, 0x6c, 0x79, 0x5f, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x63, 0x6f, 0x72, 0x65, 0x5f, - 0x6d, 0x69, 0x6e, 0x75, 0x74, 0x65, 0x73, 0x18, 0x0b, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x57, 0x2e, - 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x61, 0x69, 0x6c, 0x79, 0x55, 0x74, 0x69, 0x6c, - 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4e, 0x6f, 0x64, 0x65, 0x54, 0x79, 0x70, 0x65, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x44, 0x61, 0x74, 0x61, 0x70, 0x6f, 0x69, 0x6e, - 0x74, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x54, 0x79, 0x70, 0x65, 0x54, 0x6f, 0x44, 0x61, 0x69, 0x6c, - 0x79, 0x54, 0x6f, 0x74, 0x61, 0x6c, 0x43, 0x6f, 0x72, 0x65, 0x4d, 0x69, 0x6e, 0x75, 0x74, 0x65, - 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x1f, 0x6e, 0x6f, 0x64, 0x65, 0x54, 0x79, 0x70, 0x65, - 0x54, 0x6f, 0x44, 0x61, 0x69, 0x6c, 0x79, 0x54, 0x6f, 0x74, 0x61, 0x6c, 0x43, 0x6f, 0x72, 0x65, - 0x4d, 0x69, 0x6e, 0x75, 0x74, 0x65, 0x73, 0x1a, 0x52, 0x0a, 0x24, 0x4e, 0x6f, 0x64, 0x65, 0x54, - 0x79, 0x70, 0x65, 0x54, 0x6f, 0x44, 0x61, 0x69, 0x6c, 0x79, 0x54, 0x6f, 0x74, 0x61, 0x6c, 0x43, - 0x6f, 0x72, 0x65, 0x4d, 0x69, 0x6e, 0x75, 0x74, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, + 0x22, 0x96, 0x07, 0x0a, 0x24, 0x44, 0x61, 0x69, 0x6c, 0x79, 0x55, 0x74, 0x69, 0x6c, 0x69, 0x7a, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x54, 0x79, 0x70, + 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x80, 0x01, 0x0a, 0x18, 0x63, 0x6c, + 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x5f, 0x74, 0x6f, 0x5f, 0x64, 0x61, 0x74, 0x61, + 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x47, 0x2e, 0x61, + 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x61, 0x69, 0x6c, 0x79, 0x55, 0x74, 0x69, 0x6c, 0x69, + 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x54, 0x79, + 0x70, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x43, 0x6c, 0x75, 0x73, 0x74, + 0x65, 0x72, 0x49, 0x64, 0x54, 0x6f, 0x44, 0x61, 0x74, 0x61, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, + 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x15, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, 0x64, + 0x54, 0x6f, 0x44, 0x61, 0x74, 0x61, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x12, 0x6e, 0x0a, 0x12, + 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x5f, 0x74, 0x6f, 0x5f, 0x6d, 0x65, + 0x74, 0x61, 0x18, 0x0b, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x41, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, + 0x31, 0x2e, 0x44, 0x61, 0x69, 0x6c, 0x79, 0x55, 0x74, 0x69, 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x54, 0x79, 0x70, 0x65, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, 0x64, + 0x54, 0x6f, 0x4d, 0x65, 0x74, 0x61, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0f, 0x63, 0x6c, 0x75, + 0x73, 0x74, 0x65, 0x72, 0x49, 0x64, 0x54, 0x6f, 0x4d, 0x65, 0x74, 0x61, 0x1a, 0x64, 0x0a, 0x0a, + 0x44, 0x61, 0x74, 0x61, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x12, 0x56, 0x0a, 0x0a, 0x64, 0x61, + 0x74, 0x61, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x36, + 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x61, 0x69, 0x6c, 0x79, 0x55, 0x74, 0x69, + 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, + 0x54, 0x79, 0x70, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x44, 0x61, 0x74, + 0x61, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x52, 0x0a, 0x64, 0x61, 0x74, 0x61, 0x70, 0x6f, 0x69, 0x6e, + 0x74, 0x73, 0x1a, 0xbb, 0x02, 0x0a, 0x09, 0x44, 0x61, 0x74, 0x61, 0x70, 0x6f, 0x69, 0x6e, 0x74, + 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x03, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0xb7, + 0x01, 0x0a, 0x29, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x5f, 0x74, 0x79, 0x70, 0x65, + 0x5f, 0x74, 0x6f, 0x5f, 0x64, 0x61, 0x69, 0x6c, 0x79, 0x5f, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, + 0x63, 0x6f, 0x72, 0x65, 0x5f, 0x6d, 0x69, 0x6e, 0x75, 0x74, 0x65, 0x73, 0x18, 0x0b, 0x20, 0x03, + 0x28, 0x0b, 0x32, 0x5f, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x61, 0x69, 0x6c, + 0x79, 0x55, 0x74, 0x69, 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x6e, 0x73, 0x74, + 0x61, 0x6e, 0x63, 0x65, 0x54, 0x79, 0x70, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x2e, 0x44, 0x61, 0x74, 0x61, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x2e, 0x49, 0x6e, 0x73, 0x74, 0x61, + 0x6e, 0x63, 0x65, 0x54, 0x79, 0x70, 0x65, 0x54, 0x6f, 0x44, 0x61, 0x69, 0x6c, 0x79, 0x54, 0x6f, + 0x74, 0x61, 0x6c, 0x43, 0x6f, 0x72, 0x65, 0x4d, 0x69, 0x6e, 0x75, 0x74, 0x65, 0x73, 0x45, 0x6e, + 0x74, 0x72, 0x79, 0x52, 0x23, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x54, 0x79, 0x70, + 0x65, 0x54, 0x6f, 0x44, 0x61, 0x69, 0x6c, 0x79, 0x54, 0x6f, 0x74, 0x61, 0x6c, 0x43, 0x6f, 0x72, + 0x65, 0x4d, 0x69, 0x6e, 0x75, 0x74, 0x65, 0x73, 0x1a, 0x56, 0x0a, 0x28, 0x49, 0x6e, 0x73, 0x74, + 0x61, 0x6e, 0x63, 0x65, 0x54, 0x79, 0x70, 0x65, 0x54, 0x6f, 0x44, 0x61, 0x69, 0x6c, 0x79, 0x54, + 0x6f, 0x74, 0x61, 0x6c, 0x43, 0x6f, 0x72, 0x65, 0x4d, 0x69, 0x6e, 0x75, 0x74, 0x65, 0x73, 0x45, + 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, + 0x1a, 0x81, 0x01, 0x0a, 0x1a, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, 0x64, 0x54, 0x6f, + 0x44, 0x61, 0x74, 0x61, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, - 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, - 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x7d, 0x0a, 0x1a, 0x43, - 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, 0x64, 0x54, 0x6f, 0x44, 0x61, 0x74, 0x61, 0x70, 0x6f, - 0x69, 0x6e, 0x74, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x49, 0x0a, 0x05, 0x76, - 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x33, 0x2e, 0x61, 0x70, 0x69, + 0x79, 0x12, 0x4d, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x37, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x61, 0x69, 0x6c, 0x79, 0x55, + 0x74, 0x69, 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, + 0x63, 0x65, 0x54, 0x79, 0x70, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x44, + 0x61, 0x74, 0x61, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, + 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x53, 0x0a, 0x14, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, + 0x64, 0x54, 0x6f, 0x4d, 0x65, 0x74, 0x61, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, + 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x25, + 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, + 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x52, 0x05, + 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0xcd, 0x01, 0x0a, 0x1f, 0x44, 0x61, + 0x69, 0x6c, 0x79, 0x55, 0x74, 0x69, 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4e, 0x6f, + 0x64, 0x65, 0x54, 0x79, 0x70, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x17, 0x0a, + 0x07, 0x74, 0x65, 0x61, 0x6d, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, + 0x74, 0x65, 0x61, 0x6d, 0x49, 0x64, 0x12, 0x1f, 0x0a, 0x0b, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, + 0x72, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0a, 0x63, 0x6c, 0x75, + 0x73, 0x74, 0x65, 0x72, 0x49, 0x64, 0x73, 0x12, 0x39, 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x72, 0x74, + 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x15, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, + 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, + 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x73, 0x74, 0x61, 0x72, 0x74, 0x54, 0x69, + 0x6d, 0x65, 0x12, 0x35, 0x0a, 0x08, 0x65, 0x6e, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x16, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, + 0x52, 0x07, 0x65, 0x6e, 0x64, 0x54, 0x69, 0x6d, 0x65, 0x22, 0xec, 0x06, 0x0a, 0x20, 0x44, 0x61, + 0x69, 0x6c, 0x79, 0x55, 0x74, 0x69, 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4e, 0x6f, + 0x64, 0x65, 0x54, 0x79, 0x70, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x7c, + 0x0a, 0x18, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x5f, 0x74, 0x6f, 0x5f, + 0x64, 0x61, 0x74, 0x61, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, + 0x32, 0x43, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x61, 0x69, 0x6c, 0x79, 0x55, + 0x74, 0x69, 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4e, 0x6f, 0x64, 0x65, 0x54, 0x79, + 0x70, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x43, 0x6c, 0x75, 0x73, 0x74, + 0x65, 0x72, 0x49, 0x64, 0x54, 0x6f, 0x44, 0x61, 0x74, 0x61, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, + 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x15, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, 0x64, + 0x54, 0x6f, 0x44, 0x61, 0x74, 0x61, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x12, 0x6a, 0x0a, 0x12, + 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x5f, 0x74, 0x6f, 0x5f, 0x6d, 0x65, + 0x74, 0x61, 0x18, 0x0b, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x3d, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, + 0x31, 0x2e, 0x44, 0x61, 0x69, 0x6c, 0x79, 0x55, 0x74, 0x69, 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x4e, 0x6f, 0x64, 0x65, 0x54, 0x79, 0x70, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x2e, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, 0x64, 0x54, 0x6f, 0x4d, 0x65, + 0x74, 0x61, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0f, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, + 0x49, 0x64, 0x54, 0x6f, 0x4d, 0x65, 0x74, 0x61, 0x1a, 0x60, 0x0a, 0x0a, 0x44, 0x61, 0x74, 0x61, + 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x12, 0x52, 0x0a, 0x0a, 0x64, 0x61, 0x74, 0x61, 0x70, 0x6f, + 0x69, 0x6e, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x32, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x61, 0x69, 0x6c, 0x79, 0x55, 0x74, 0x69, 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4e, 0x6f, 0x64, 0x65, 0x54, 0x79, 0x70, 0x65, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x44, 0x61, 0x74, 0x61, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x52, - 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x53, 0x0a, 0x14, 0x43, 0x6c, - 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, 0x64, 0x54, 0x6f, 0x4d, 0x65, 0x74, 0x61, 0x45, 0x6e, 0x74, + 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x44, 0x61, 0x74, 0x61, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x52, 0x0a, + 0x64, 0x61, 0x74, 0x61, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x1a, 0xa7, 0x02, 0x0a, 0x09, 0x44, + 0x61, 0x74, 0x61, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, + 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x74, 0x69, 0x6d, + 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0xa7, 0x01, 0x0a, 0x25, 0x6e, 0x6f, 0x64, 0x65, 0x5f, + 0x74, 0x79, 0x70, 0x65, 0x5f, 0x74, 0x6f, 0x5f, 0x64, 0x61, 0x69, 0x6c, 0x79, 0x5f, 0x74, 0x6f, + 0x74, 0x61, 0x6c, 0x5f, 0x63, 0x6f, 0x72, 0x65, 0x5f, 0x6d, 0x69, 0x6e, 0x75, 0x74, 0x65, 0x73, + 0x18, 0x0b, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x57, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, + 0x44, 0x61, 0x69, 0x6c, 0x79, 0x55, 0x74, 0x69, 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x4e, 0x6f, 0x64, 0x65, 0x54, 0x79, 0x70, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x2e, 0x44, 0x61, 0x74, 0x61, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x54, + 0x79, 0x70, 0x65, 0x54, 0x6f, 0x44, 0x61, 0x69, 0x6c, 0x79, 0x54, 0x6f, 0x74, 0x61, 0x6c, 0x43, + 0x6f, 0x72, 0x65, 0x4d, 0x69, 0x6e, 0x75, 0x74, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, + 0x1f, 0x6e, 0x6f, 0x64, 0x65, 0x54, 0x79, 0x70, 0x65, 0x54, 0x6f, 0x44, 0x61, 0x69, 0x6c, 0x79, + 0x54, 0x6f, 0x74, 0x61, 0x6c, 0x43, 0x6f, 0x72, 0x65, 0x4d, 0x69, 0x6e, 0x75, 0x74, 0x65, 0x73, + 0x1a, 0x52, 0x0a, 0x24, 0x4e, 0x6f, 0x64, 0x65, 0x54, 0x79, 0x70, 0x65, 0x54, 0x6f, 0x44, 0x61, + 0x69, 0x6c, 0x79, 0x54, 0x6f, 0x74, 0x61, 0x6c, 0x43, 0x6f, 0x72, 0x65, 0x4d, 0x69, 0x6e, 0x75, + 0x74, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, + 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, + 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x7d, 0x0a, 0x1a, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, + 0x64, 0x54, 0x6f, 0x44, 0x61, 0x74, 0x61, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x03, 0x6b, 0x65, 0x79, 0x12, 0x25, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6c, 0x75, - 0x73, 0x74, 0x65, 0x72, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, - 0x6c, 0x0a, 0x19, 0x4c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x4e, 0x6f, 0x64, 0x65, 0x49, 0x6e, 0x73, - 0x74, 0x61, 0x6e, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x17, 0x0a, 0x07, - 0x74, 0x65, 0x61, 0x6d, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x74, - 0x65, 0x61, 0x6d, 0x49, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, - 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x6c, 0x75, 0x73, 0x74, - 0x65, 0x72, 0x49, 0x64, 0x12, 0x17, 0x0a, 0x07, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x69, 0x64, 0x18, - 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x6e, 0x6f, 0x64, 0x65, 0x49, 0x64, 0x22, 0xe1, 0x01, - 0x0a, 0x14, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x4c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, - 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x12, 0x2a, 0x0a, 0x11, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x5f, - 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x03, 0x52, 0x0f, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, - 0x49, 0x64, 0x12, 0x1b, 0x0a, 0x09, 0x72, 0x65, 0x67, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x08, 0x72, 0x65, 0x67, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, - 0x2b, 0x0a, 0x11, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, 0x5f, - 0x7a, 0x6f, 0x6e, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x61, 0x76, 0x61, 0x69, - 0x6c, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, 0x5a, 0x6f, 0x6e, 0x65, 0x12, 0x23, 0x0a, 0x0d, - 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x04, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x0c, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x54, 0x79, 0x70, - 0x65, 0x12, 0x2e, 0x0a, 0x13, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6e, 0x67, 0x5f, 0x73, - 0x79, 0x73, 0x74, 0x65, 0x6d, 0x5f, 0x69, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x11, - 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6e, 0x67, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x49, - 0x64, 0x22, 0xc1, 0x02, 0x0a, 0x1a, 0x4c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x4e, 0x6f, 0x64, 0x65, - 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x12, 0x3b, 0x0a, 0x10, 0x64, 0x79, 0x6e, 0x61, 0x6d, 0x69, 0x63, 0x5f, 0x69, 0x6e, 0x73, 0x74, - 0x61, 0x6e, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x61, 0x70, 0x69, - 0x2e, 0x76, 0x31, 0x2e, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x52, 0x0f, 0x64, 0x79, - 0x6e, 0x61, 0x6d, 0x69, 0x63, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x12, 0x39, 0x0a, - 0x0f, 0x63, 0x61, 0x63, 0x68, 0x65, 0x64, 0x5f, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, - 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x52, 0x0e, 0x63, 0x61, 0x63, 0x68, 0x65, 0x64, - 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x12, 0x41, 0x0a, 0x0d, 0x6c, 0x6f, 0x6f, 0x6b, - 0x75, 0x70, 0x5f, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x1c, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, - 0x65, 0x4c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x52, 0x0c, 0x6c, - 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x12, 0x34, 0x0a, 0x16, 0x64, - 0x79, 0x6e, 0x61, 0x6d, 0x69, 0x63, 0x5f, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x5f, - 0x66, 0x6f, 0x75, 0x6e, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x14, 0x64, 0x79, 0x6e, - 0x61, 0x6d, 0x69, 0x63, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x46, 0x6f, 0x75, 0x6e, - 0x64, 0x12, 0x32, 0x0a, 0x15, 0x63, 0x61, 0x63, 0x68, 0x65, 0x64, 0x5f, 0x69, 0x6e, 0x73, 0x74, - 0x61, 0x6e, 0x63, 0x65, 0x5f, 0x66, 0x6f, 0x75, 0x6e, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, - 0x52, 0x13, 0x63, 0x61, 0x63, 0x68, 0x65, 0x64, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, - 0x46, 0x6f, 0x75, 0x6e, 0x64, 0x2a, 0xb3, 0x04, 0x0a, 0x14, 0x47, 0x61, 0x6c, 0x61, 0x78, 0x79, - 0x50, 0x4f, 0x56, 0x4e, 0x6f, 0x64, 0x65, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x42, 0x79, 0x12, 0x28, - 0x0a, 0x24, 0x47, 0x41, 0x4c, 0x41, 0x58, 0x59, 0x5f, 0x50, 0x4f, 0x56, 0x5f, 0x4e, 0x4f, 0x44, - 0x45, 0x5f, 0x47, 0x52, 0x4f, 0x55, 0x50, 0x5f, 0x42, 0x59, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, - 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x22, 0x0a, 0x1e, 0x47, 0x41, 0x4c, 0x41, + 0x03, 0x6b, 0x65, 0x79, 0x12, 0x49, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x33, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x61, 0x69, + 0x6c, 0x79, 0x55, 0x74, 0x69, 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4e, 0x6f, 0x64, + 0x65, 0x54, 0x79, 0x70, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x44, 0x61, + 0x74, 0x61, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, + 0x02, 0x38, 0x01, 0x1a, 0x53, 0x0a, 0x14, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, 0x64, + 0x54, 0x6f, 0x4d, 0x65, 0x74, 0x61, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, + 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x25, 0x0a, + 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x61, + 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x52, 0x05, 0x76, + 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x6c, 0x0a, 0x19, 0x4c, 0x6f, 0x6f, 0x6b, + 0x75, 0x70, 0x4e, 0x6f, 0x64, 0x65, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x17, 0x0a, 0x07, 0x74, 0x65, 0x61, 0x6d, 0x5f, 0x69, 0x64, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x74, 0x65, 0x61, 0x6d, 0x49, 0x64, 0x12, 0x1d, + 0x0a, 0x0a, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x09, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, 0x64, 0x12, 0x17, 0x0a, + 0x07, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, + 0x6e, 0x6f, 0x64, 0x65, 0x49, 0x64, 0x22, 0xe1, 0x01, 0x0a, 0x14, 0x49, 0x6e, 0x73, 0x74, 0x61, + 0x6e, 0x63, 0x65, 0x4c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x12, + 0x2a, 0x0a, 0x11, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x5f, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, + 0x72, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0f, 0x63, 0x6c, 0x6f, 0x75, + 0x64, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x49, 0x64, 0x12, 0x1b, 0x0a, 0x09, 0x72, + 0x65, 0x67, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x08, + 0x72, 0x65, 0x67, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x2b, 0x0a, 0x11, 0x61, 0x76, 0x61, 0x69, + 0x6c, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, 0x5f, 0x7a, 0x6f, 0x6e, 0x65, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x10, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, + 0x79, 0x5a, 0x6f, 0x6e, 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, + 0x65, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x69, 0x6e, + 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x54, 0x79, 0x70, 0x65, 0x12, 0x2e, 0x0a, 0x13, 0x6f, 0x70, + 0x65, 0x72, 0x61, 0x74, 0x69, 0x6e, 0x67, 0x5f, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x5f, 0x69, + 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x11, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, + 0x6e, 0x67, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x49, 0x64, 0x22, 0xc1, 0x02, 0x0a, 0x1a, 0x4c, + 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x4e, 0x6f, 0x64, 0x65, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, + 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3b, 0x0a, 0x10, 0x64, 0x79, 0x6e, + 0x61, 0x6d, 0x69, 0x63, 0x5f, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x49, 0x6e, 0x73, + 0x74, 0x61, 0x6e, 0x63, 0x65, 0x52, 0x0f, 0x64, 0x79, 0x6e, 0x61, 0x6d, 0x69, 0x63, 0x49, 0x6e, + 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x12, 0x39, 0x0a, 0x0f, 0x63, 0x61, 0x63, 0x68, 0x65, 0x64, + 0x5f, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x10, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, + 0x65, 0x52, 0x0e, 0x63, 0x61, 0x63, 0x68, 0x65, 0x64, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, + 0x65, 0x12, 0x41, 0x0a, 0x0d, 0x6c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x5f, 0x70, 0x61, 0x72, 0x61, + 0x6d, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, + 0x31, 0x2e, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x4c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, + 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x52, 0x0c, 0x6c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x50, 0x61, + 0x72, 0x61, 0x6d, 0x73, 0x12, 0x34, 0x0a, 0x16, 0x64, 0x79, 0x6e, 0x61, 0x6d, 0x69, 0x63, 0x5f, + 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x5f, 0x66, 0x6f, 0x75, 0x6e, 0x64, 0x18, 0x04, + 0x20, 0x01, 0x28, 0x08, 0x52, 0x14, 0x64, 0x79, 0x6e, 0x61, 0x6d, 0x69, 0x63, 0x49, 0x6e, 0x73, + 0x74, 0x61, 0x6e, 0x63, 0x65, 0x46, 0x6f, 0x75, 0x6e, 0x64, 0x12, 0x32, 0x0a, 0x15, 0x63, 0x61, + 0x63, 0x68, 0x65, 0x64, 0x5f, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x5f, 0x66, 0x6f, + 0x75, 0x6e, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x13, 0x63, 0x61, 0x63, 0x68, 0x65, + 0x64, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x46, 0x6f, 0x75, 0x6e, 0x64, 0x2a, 0x7a, + 0x0a, 0x12, 0x4e, 0x6f, 0x64, 0x65, 0x49, 0x6e, 0x76, 0x65, 0x6e, 0x74, 0x6f, 0x72, 0x79, 0x53, + 0x63, 0x6f, 0x70, 0x65, 0x12, 0x24, 0x0a, 0x20, 0x4e, 0x4f, 0x44, 0x45, 0x5f, 0x49, 0x4e, 0x56, + 0x45, 0x4e, 0x54, 0x4f, 0x52, 0x59, 0x5f, 0x53, 0x43, 0x4f, 0x50, 0x45, 0x5f, 0x55, 0x4e, 0x53, + 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x1e, 0x0a, 0x1a, 0x4e, 0x4f, + 0x44, 0x45, 0x5f, 0x49, 0x4e, 0x56, 0x45, 0x4e, 0x54, 0x4f, 0x52, 0x59, 0x5f, 0x53, 0x43, 0x4f, + 0x50, 0x45, 0x5f, 0x50, 0x4f, 0x4f, 0x4c, 0x53, 0x10, 0x01, 0x12, 0x1e, 0x0a, 0x1a, 0x4e, 0x4f, + 0x44, 0x45, 0x5f, 0x49, 0x4e, 0x56, 0x45, 0x4e, 0x54, 0x4f, 0x52, 0x59, 0x5f, 0x53, 0x43, 0x4f, + 0x50, 0x45, 0x5f, 0x4e, 0x4f, 0x44, 0x45, 0x53, 0x10, 0x02, 0x2a, 0xa2, 0x01, 0x0a, 0x09, 0x4e, + 0x6f, 0x64, 0x65, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x1a, 0x0a, 0x16, 0x4e, 0x4f, 0x44, 0x45, + 0x5f, 0x53, 0x54, 0x41, 0x54, 0x45, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, + 0x45, 0x44, 0x10, 0x00, 0x12, 0x14, 0x0a, 0x10, 0x4e, 0x4f, 0x44, 0x45, 0x5f, 0x53, 0x54, 0x41, + 0x54, 0x45, 0x5f, 0x52, 0x45, 0x41, 0x44, 0x59, 0x10, 0x01, 0x12, 0x17, 0x0a, 0x13, 0x4e, 0x4f, + 0x44, 0x45, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x45, 0x5f, 0x43, 0x4f, 0x52, 0x44, 0x4f, 0x4e, 0x45, + 0x44, 0x10, 0x02, 0x12, 0x17, 0x0a, 0x13, 0x4e, 0x4f, 0x44, 0x45, 0x5f, 0x53, 0x54, 0x41, 0x54, + 0x45, 0x5f, 0x44, 0x52, 0x41, 0x49, 0x4e, 0x49, 0x4e, 0x47, 0x10, 0x03, 0x12, 0x18, 0x0a, 0x14, + 0x4e, 0x4f, 0x44, 0x45, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x45, 0x5f, 0x4e, 0x4f, 0x54, 0x5f, 0x52, + 0x45, 0x41, 0x44, 0x59, 0x10, 0x04, 0x12, 0x17, 0x0a, 0x13, 0x4e, 0x4f, 0x44, 0x45, 0x5f, 0x53, + 0x54, 0x41, 0x54, 0x45, 0x5f, 0x44, 0x45, 0x4c, 0x45, 0x54, 0x49, 0x4e, 0x47, 0x10, 0x05, 0x2a, + 0x97, 0x02, 0x0a, 0x1b, 0x4e, 0x6f, 0x64, 0x65, 0x43, 0x6c, 0x61, 0x69, 0x6d, 0x4e, 0x6f, 0x74, + 0x44, 0x65, 0x6c, 0x65, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x52, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x12, + 0x2f, 0x0a, 0x2b, 0x4e, 0x4f, 0x44, 0x45, 0x5f, 0x43, 0x4c, 0x41, 0x49, 0x4d, 0x5f, 0x4e, 0x4f, + 0x54, 0x5f, 0x44, 0x45, 0x4c, 0x45, 0x54, 0x41, 0x42, 0x4c, 0x45, 0x5f, 0x52, 0x45, 0x41, 0x53, + 0x4f, 0x4e, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, + 0x12, 0x31, 0x0a, 0x2d, 0x4e, 0x4f, 0x44, 0x45, 0x5f, 0x43, 0x4c, 0x41, 0x49, 0x4d, 0x5f, 0x4e, + 0x4f, 0x54, 0x5f, 0x44, 0x45, 0x4c, 0x45, 0x54, 0x41, 0x42, 0x4c, 0x45, 0x5f, 0x52, 0x45, 0x41, + 0x53, 0x4f, 0x4e, 0x5f, 0x4e, 0x4f, 0x44, 0x45, 0x5f, 0x4e, 0x4f, 0x54, 0x5f, 0x4c, 0x49, 0x56, + 0x45, 0x10, 0x01, 0x12, 0x2f, 0x0a, 0x2b, 0x4e, 0x4f, 0x44, 0x45, 0x5f, 0x43, 0x4c, 0x41, 0x49, + 0x4d, 0x5f, 0x4e, 0x4f, 0x54, 0x5f, 0x44, 0x45, 0x4c, 0x45, 0x54, 0x41, 0x42, 0x4c, 0x45, 0x5f, + 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x54, 0x45, 0x52, 0x4d, 0x49, 0x4e, 0x41, 0x54, 0x49, + 0x4e, 0x47, 0x10, 0x02, 0x12, 0x2d, 0x0a, 0x29, 0x4e, 0x4f, 0x44, 0x45, 0x5f, 0x43, 0x4c, 0x41, + 0x49, 0x4d, 0x5f, 0x4e, 0x4f, 0x54, 0x5f, 0x44, 0x45, 0x4c, 0x45, 0x54, 0x41, 0x42, 0x4c, 0x45, + 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x49, 0x4e, 0x5f, 0x46, 0x4c, 0x49, 0x47, 0x48, + 0x54, 0x10, 0x03, 0x12, 0x34, 0x0a, 0x30, 0x4e, 0x4f, 0x44, 0x45, 0x5f, 0x43, 0x4c, 0x41, 0x49, + 0x4d, 0x5f, 0x4e, 0x4f, 0x54, 0x5f, 0x44, 0x45, 0x4c, 0x45, 0x54, 0x41, 0x42, 0x4c, 0x45, 0x5f, + 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x4f, 0x50, 0x45, 0x52, 0x41, 0x54, 0x4f, 0x52, 0x5f, + 0x56, 0x45, 0x52, 0x53, 0x49, 0x4f, 0x4e, 0x10, 0x04, 0x2a, 0x83, 0x02, 0x0a, 0x16, 0x4e, 0x6f, + 0x64, 0x65, 0x43, 0x6c, 0x61, 0x69, 0x6d, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x69, 0x6f, 0x6e, 0x53, + 0x74, 0x61, 0x74, 0x65, 0x12, 0x29, 0x0a, 0x25, 0x4e, 0x4f, 0x44, 0x45, 0x5f, 0x43, 0x4c, 0x41, + 0x49, 0x4d, 0x5f, 0x44, 0x45, 0x4c, 0x45, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x53, 0x54, 0x41, 0x54, + 0x45, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, + 0x25, 0x0a, 0x21, 0x4e, 0x4f, 0x44, 0x45, 0x5f, 0x43, 0x4c, 0x41, 0x49, 0x4d, 0x5f, 0x44, 0x45, + 0x4c, 0x45, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x45, 0x5f, 0x50, 0x45, 0x4e, + 0x44, 0x49, 0x4e, 0x47, 0x10, 0x01, 0x12, 0x22, 0x0a, 0x1e, 0x4e, 0x4f, 0x44, 0x45, 0x5f, 0x43, + 0x4c, 0x41, 0x49, 0x4d, 0x5f, 0x44, 0x45, 0x4c, 0x45, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x53, 0x54, + 0x41, 0x54, 0x45, 0x5f, 0x53, 0x45, 0x4e, 0x54, 0x10, 0x02, 0x12, 0x26, 0x0a, 0x22, 0x4e, 0x4f, + 0x44, 0x45, 0x5f, 0x43, 0x4c, 0x41, 0x49, 0x4d, 0x5f, 0x44, 0x45, 0x4c, 0x45, 0x54, 0x49, 0x4f, + 0x4e, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x45, 0x5f, 0x41, 0x43, 0x43, 0x45, 0x50, 0x54, 0x45, 0x44, + 0x10, 0x03, 0x12, 0x25, 0x0a, 0x21, 0x4e, 0x4f, 0x44, 0x45, 0x5f, 0x43, 0x4c, 0x41, 0x49, 0x4d, + 0x5f, 0x44, 0x45, 0x4c, 0x45, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x45, 0x5f, + 0x44, 0x45, 0x4c, 0x45, 0x54, 0x45, 0x44, 0x10, 0x04, 0x12, 0x24, 0x0a, 0x20, 0x4e, 0x4f, 0x44, + 0x45, 0x5f, 0x43, 0x4c, 0x41, 0x49, 0x4d, 0x5f, 0x44, 0x45, 0x4c, 0x45, 0x54, 0x49, 0x4f, 0x4e, + 0x5f, 0x53, 0x54, 0x41, 0x54, 0x45, 0x5f, 0x46, 0x41, 0x49, 0x4c, 0x45, 0x44, 0x10, 0x05, 0x2a, + 0xb3, 0x04, 0x0a, 0x14, 0x47, 0x61, 0x6c, 0x61, 0x78, 0x79, 0x50, 0x4f, 0x56, 0x4e, 0x6f, 0x64, + 0x65, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x42, 0x79, 0x12, 0x28, 0x0a, 0x24, 0x47, 0x41, 0x4c, 0x41, 0x58, 0x59, 0x5f, 0x50, 0x4f, 0x56, 0x5f, 0x4e, 0x4f, 0x44, 0x45, 0x5f, 0x47, 0x52, 0x4f, 0x55, - 0x50, 0x5f, 0x42, 0x59, 0x5f, 0x54, 0x41, 0x49, 0x4e, 0x54, 0x10, 0x01, 0x12, 0x21, 0x0a, 0x1d, - 0x47, 0x41, 0x4c, 0x41, 0x58, 0x59, 0x5f, 0x50, 0x4f, 0x56, 0x5f, 0x4e, 0x4f, 0x44, 0x45, 0x5f, - 0x47, 0x52, 0x4f, 0x55, 0x50, 0x5f, 0x42, 0x59, 0x5f, 0x41, 0x52, 0x43, 0x48, 0x10, 0x02, 0x12, - 0x2a, 0x0a, 0x26, 0x47, 0x41, 0x4c, 0x41, 0x58, 0x59, 0x5f, 0x50, 0x4f, 0x56, 0x5f, 0x4e, 0x4f, - 0x44, 0x45, 0x5f, 0x47, 0x52, 0x4f, 0x55, 0x50, 0x5f, 0x42, 0x59, 0x5f, 0x43, 0x41, 0x50, 0x41, - 0x43, 0x49, 0x54, 0x59, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x10, 0x03, 0x12, 0x26, 0x0a, 0x22, 0x47, - 0x41, 0x4c, 0x41, 0x58, 0x59, 0x5f, 0x50, 0x4f, 0x56, 0x5f, 0x4e, 0x4f, 0x44, 0x45, 0x5f, 0x47, - 0x52, 0x4f, 0x55, 0x50, 0x5f, 0x42, 0x59, 0x5f, 0x47, 0x50, 0x55, 0x5f, 0x4d, 0x4f, 0x44, 0x45, - 0x4c, 0x10, 0x04, 0x12, 0x2e, 0x0a, 0x2a, 0x47, 0x41, 0x4c, 0x41, 0x58, 0x59, 0x5f, 0x50, 0x4f, - 0x56, 0x5f, 0x4e, 0x4f, 0x44, 0x45, 0x5f, 0x47, 0x52, 0x4f, 0x55, 0x50, 0x5f, 0x42, 0x59, 0x5f, - 0x43, 0x4f, 0x4e, 0x54, 0x41, 0x49, 0x4e, 0x45, 0x52, 0x5f, 0x52, 0x55, 0x4e, 0x54, 0x49, 0x4d, - 0x45, 0x10, 0x05, 0x12, 0x24, 0x0a, 0x20, 0x47, 0x41, 0x4c, 0x41, 0x58, 0x59, 0x5f, 0x50, 0x4f, + 0x50, 0x5f, 0x42, 0x59, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, + 0x10, 0x00, 0x12, 0x22, 0x0a, 0x1e, 0x47, 0x41, 0x4c, 0x41, 0x58, 0x59, 0x5f, 0x50, 0x4f, 0x56, + 0x5f, 0x4e, 0x4f, 0x44, 0x45, 0x5f, 0x47, 0x52, 0x4f, 0x55, 0x50, 0x5f, 0x42, 0x59, 0x5f, 0x54, + 0x41, 0x49, 0x4e, 0x54, 0x10, 0x01, 0x12, 0x21, 0x0a, 0x1d, 0x47, 0x41, 0x4c, 0x41, 0x58, 0x59, + 0x5f, 0x50, 0x4f, 0x56, 0x5f, 0x4e, 0x4f, 0x44, 0x45, 0x5f, 0x47, 0x52, 0x4f, 0x55, 0x50, 0x5f, + 0x42, 0x59, 0x5f, 0x41, 0x52, 0x43, 0x48, 0x10, 0x02, 0x12, 0x2a, 0x0a, 0x26, 0x47, 0x41, 0x4c, + 0x41, 0x58, 0x59, 0x5f, 0x50, 0x4f, 0x56, 0x5f, 0x4e, 0x4f, 0x44, 0x45, 0x5f, 0x47, 0x52, 0x4f, + 0x55, 0x50, 0x5f, 0x42, 0x59, 0x5f, 0x43, 0x41, 0x50, 0x41, 0x43, 0x49, 0x54, 0x59, 0x5f, 0x54, + 0x59, 0x50, 0x45, 0x10, 0x03, 0x12, 0x26, 0x0a, 0x22, 0x47, 0x41, 0x4c, 0x41, 0x58, 0x59, 0x5f, + 0x50, 0x4f, 0x56, 0x5f, 0x4e, 0x4f, 0x44, 0x45, 0x5f, 0x47, 0x52, 0x4f, 0x55, 0x50, 0x5f, 0x42, + 0x59, 0x5f, 0x47, 0x50, 0x55, 0x5f, 0x4d, 0x4f, 0x44, 0x45, 0x4c, 0x10, 0x04, 0x12, 0x2e, 0x0a, + 0x2a, 0x47, 0x41, 0x4c, 0x41, 0x58, 0x59, 0x5f, 0x50, 0x4f, 0x56, 0x5f, 0x4e, 0x4f, 0x44, 0x45, + 0x5f, 0x47, 0x52, 0x4f, 0x55, 0x50, 0x5f, 0x42, 0x59, 0x5f, 0x43, 0x4f, 0x4e, 0x54, 0x41, 0x49, + 0x4e, 0x45, 0x52, 0x5f, 0x52, 0x55, 0x4e, 0x54, 0x49, 0x4d, 0x45, 0x10, 0x05, 0x12, 0x24, 0x0a, + 0x20, 0x47, 0x41, 0x4c, 0x41, 0x58, 0x59, 0x5f, 0x50, 0x4f, 0x56, 0x5f, 0x4e, 0x4f, 0x44, 0x45, + 0x5f, 0x47, 0x52, 0x4f, 0x55, 0x50, 0x5f, 0x42, 0x59, 0x5f, 0x43, 0x4c, 0x55, 0x53, 0x54, 0x45, + 0x52, 0x10, 0x06, 0x12, 0x23, 0x0a, 0x1f, 0x47, 0x41, 0x4c, 0x41, 0x58, 0x59, 0x5f, 0x50, 0x4f, 0x56, 0x5f, 0x4e, 0x4f, 0x44, 0x45, 0x5f, 0x47, 0x52, 0x4f, 0x55, 0x50, 0x5f, 0x42, 0x59, 0x5f, - 0x43, 0x4c, 0x55, 0x53, 0x54, 0x45, 0x52, 0x10, 0x06, 0x12, 0x23, 0x0a, 0x1f, 0x47, 0x41, 0x4c, + 0x52, 0x45, 0x47, 0x49, 0x4f, 0x4e, 0x10, 0x07, 0x12, 0x2e, 0x0a, 0x2a, 0x47, 0x41, 0x4c, 0x41, + 0x58, 0x59, 0x5f, 0x50, 0x4f, 0x56, 0x5f, 0x4e, 0x4f, 0x44, 0x45, 0x5f, 0x47, 0x52, 0x4f, 0x55, + 0x50, 0x5f, 0x42, 0x59, 0x5f, 0x41, 0x56, 0x41, 0x49, 0x4c, 0x41, 0x42, 0x49, 0x4c, 0x49, 0x54, + 0x59, 0x5f, 0x5a, 0x4f, 0x4e, 0x45, 0x10, 0x08, 0x12, 0x2a, 0x0a, 0x26, 0x47, 0x41, 0x4c, 0x41, + 0x58, 0x59, 0x5f, 0x50, 0x4f, 0x56, 0x5f, 0x4e, 0x4f, 0x44, 0x45, 0x5f, 0x47, 0x52, 0x4f, 0x55, + 0x50, 0x5f, 0x42, 0x59, 0x5f, 0x50, 0x43, 0x54, 0x5f, 0x4f, 0x50, 0x54, 0x49, 0x4d, 0x49, 0x5a, + 0x45, 0x44, 0x10, 0x09, 0x12, 0x2a, 0x0a, 0x26, 0x47, 0x41, 0x4c, 0x41, 0x58, 0x59, 0x5f, 0x50, + 0x4f, 0x56, 0x5f, 0x4e, 0x4f, 0x44, 0x45, 0x5f, 0x47, 0x52, 0x4f, 0x55, 0x50, 0x5f, 0x42, 0x59, + 0x5f, 0x43, 0x4f, 0x53, 0x54, 0x5f, 0x50, 0x45, 0x52, 0x5f, 0x48, 0x4f, 0x55, 0x52, 0x10, 0x0a, + 0x12, 0x27, 0x0a, 0x23, 0x47, 0x41, 0x4c, 0x41, 0x58, 0x59, 0x5f, 0x50, 0x4f, 0x56, 0x5f, 0x4e, + 0x4f, 0x44, 0x45, 0x5f, 0x47, 0x52, 0x4f, 0x55, 0x50, 0x5f, 0x42, 0x59, 0x5f, 0x4e, 0x4f, 0x44, + 0x45, 0x5f, 0x47, 0x52, 0x4f, 0x55, 0x50, 0x10, 0x0b, 0x12, 0x2a, 0x0a, 0x26, 0x47, 0x41, 0x4c, 0x41, 0x58, 0x59, 0x5f, 0x50, 0x4f, 0x56, 0x5f, 0x4e, 0x4f, 0x44, 0x45, 0x5f, 0x47, 0x52, 0x4f, - 0x55, 0x50, 0x5f, 0x42, 0x59, 0x5f, 0x52, 0x45, 0x47, 0x49, 0x4f, 0x4e, 0x10, 0x07, 0x12, 0x2e, - 0x0a, 0x2a, 0x47, 0x41, 0x4c, 0x41, 0x58, 0x59, 0x5f, 0x50, 0x4f, 0x56, 0x5f, 0x4e, 0x4f, 0x44, - 0x45, 0x5f, 0x47, 0x52, 0x4f, 0x55, 0x50, 0x5f, 0x42, 0x59, 0x5f, 0x41, 0x56, 0x41, 0x49, 0x4c, - 0x41, 0x42, 0x49, 0x4c, 0x49, 0x54, 0x59, 0x5f, 0x5a, 0x4f, 0x4e, 0x45, 0x10, 0x08, 0x12, 0x2a, - 0x0a, 0x26, 0x47, 0x41, 0x4c, 0x41, 0x58, 0x59, 0x5f, 0x50, 0x4f, 0x56, 0x5f, 0x4e, 0x4f, 0x44, - 0x45, 0x5f, 0x47, 0x52, 0x4f, 0x55, 0x50, 0x5f, 0x42, 0x59, 0x5f, 0x50, 0x43, 0x54, 0x5f, 0x4f, - 0x50, 0x54, 0x49, 0x4d, 0x49, 0x5a, 0x45, 0x44, 0x10, 0x09, 0x12, 0x2a, 0x0a, 0x26, 0x47, 0x41, - 0x4c, 0x41, 0x58, 0x59, 0x5f, 0x50, 0x4f, 0x56, 0x5f, 0x4e, 0x4f, 0x44, 0x45, 0x5f, 0x47, 0x52, - 0x4f, 0x55, 0x50, 0x5f, 0x42, 0x59, 0x5f, 0x43, 0x4f, 0x53, 0x54, 0x5f, 0x50, 0x45, 0x52, 0x5f, - 0x48, 0x4f, 0x55, 0x52, 0x10, 0x0a, 0x12, 0x27, 0x0a, 0x23, 0x47, 0x41, 0x4c, 0x41, 0x58, 0x59, - 0x5f, 0x50, 0x4f, 0x56, 0x5f, 0x4e, 0x4f, 0x44, 0x45, 0x5f, 0x47, 0x52, 0x4f, 0x55, 0x50, 0x5f, - 0x42, 0x59, 0x5f, 0x4e, 0x4f, 0x44, 0x45, 0x5f, 0x47, 0x52, 0x4f, 0x55, 0x50, 0x10, 0x0b, 0x12, - 0x2a, 0x0a, 0x26, 0x47, 0x41, 0x4c, 0x41, 0x58, 0x59, 0x5f, 0x50, 0x4f, 0x56, 0x5f, 0x4e, 0x4f, - 0x44, 0x45, 0x5f, 0x47, 0x52, 0x4f, 0x55, 0x50, 0x5f, 0x42, 0x59, 0x5f, 0x49, 0x4e, 0x53, 0x54, - 0x41, 0x4e, 0x43, 0x45, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x10, 0x0c, 0x2a, 0xca, 0x05, 0x0a, 0x18, - 0x47, 0x61, 0x6c, 0x61, 0x78, 0x79, 0x50, 0x4f, 0x56, 0x57, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, - 0x64, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x42, 0x79, 0x12, 0x2c, 0x0a, 0x28, 0x47, 0x41, 0x4c, 0x41, - 0x58, 0x59, 0x5f, 0x50, 0x4f, 0x56, 0x5f, 0x57, 0x4f, 0x52, 0x4b, 0x4c, 0x4f, 0x41, 0x44, 0x5f, - 0x47, 0x52, 0x4f, 0x55, 0x50, 0x5f, 0x42, 0x59, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, - 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x29, 0x0a, 0x25, 0x47, 0x41, 0x4c, 0x41, 0x58, 0x59, - 0x5f, 0x50, 0x4f, 0x56, 0x5f, 0x57, 0x4f, 0x52, 0x4b, 0x4c, 0x4f, 0x41, 0x44, 0x5f, 0x47, 0x52, - 0x4f, 0x55, 0x50, 0x5f, 0x42, 0x59, 0x5f, 0x4b, 0x38, 0x53, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x10, - 0x01, 0x12, 0x28, 0x0a, 0x24, 0x47, 0x41, 0x4c, 0x41, 0x58, 0x59, 0x5f, 0x50, 0x4f, 0x56, 0x5f, - 0x57, 0x4f, 0x52, 0x4b, 0x4c, 0x4f, 0x41, 0x44, 0x5f, 0x47, 0x52, 0x4f, 0x55, 0x50, 0x5f, 0x42, - 0x59, 0x5f, 0x43, 0x4c, 0x55, 0x53, 0x54, 0x45, 0x52, 0x10, 0x02, 0x12, 0x2e, 0x0a, 0x2a, 0x47, - 0x41, 0x4c, 0x41, 0x58, 0x59, 0x5f, 0x50, 0x4f, 0x56, 0x5f, 0x57, 0x4f, 0x52, 0x4b, 0x4c, 0x4f, - 0x41, 0x44, 0x5f, 0x47, 0x52, 0x4f, 0x55, 0x50, 0x5f, 0x42, 0x59, 0x5f, 0x50, 0x43, 0x54, 0x5f, - 0x4f, 0x50, 0x54, 0x49, 0x4d, 0x49, 0x5a, 0x45, 0x44, 0x10, 0x03, 0x12, 0x2e, 0x0a, 0x2a, 0x47, + 0x55, 0x50, 0x5f, 0x42, 0x59, 0x5f, 0x49, 0x4e, 0x53, 0x54, 0x41, 0x4e, 0x43, 0x45, 0x5f, 0x54, + 0x59, 0x50, 0x45, 0x10, 0x0c, 0x2a, 0xca, 0x05, 0x0a, 0x18, 0x47, 0x61, 0x6c, 0x61, 0x78, 0x79, + 0x50, 0x4f, 0x56, 0x57, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x47, 0x72, 0x6f, 0x75, 0x70, + 0x42, 0x79, 0x12, 0x2c, 0x0a, 0x28, 0x47, 0x41, 0x4c, 0x41, 0x58, 0x59, 0x5f, 0x50, 0x4f, 0x56, + 0x5f, 0x57, 0x4f, 0x52, 0x4b, 0x4c, 0x4f, 0x41, 0x44, 0x5f, 0x47, 0x52, 0x4f, 0x55, 0x50, 0x5f, + 0x42, 0x59, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, + 0x12, 0x29, 0x0a, 0x25, 0x47, 0x41, 0x4c, 0x41, 0x58, 0x59, 0x5f, 0x50, 0x4f, 0x56, 0x5f, 0x57, + 0x4f, 0x52, 0x4b, 0x4c, 0x4f, 0x41, 0x44, 0x5f, 0x47, 0x52, 0x4f, 0x55, 0x50, 0x5f, 0x42, 0x59, + 0x5f, 0x4b, 0x38, 0x53, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x10, 0x01, 0x12, 0x28, 0x0a, 0x24, 0x47, 0x41, 0x4c, 0x41, 0x58, 0x59, 0x5f, 0x50, 0x4f, 0x56, 0x5f, 0x57, 0x4f, 0x52, 0x4b, 0x4c, 0x4f, - 0x41, 0x44, 0x5f, 0x47, 0x52, 0x4f, 0x55, 0x50, 0x5f, 0x42, 0x59, 0x5f, 0x43, 0x4f, 0x53, 0x54, - 0x5f, 0x50, 0x45, 0x52, 0x5f, 0x48, 0x4f, 0x55, 0x52, 0x10, 0x04, 0x12, 0x2a, 0x0a, 0x26, 0x47, - 0x41, 0x4c, 0x41, 0x58, 0x59, 0x5f, 0x50, 0x4f, 0x56, 0x5f, 0x57, 0x4f, 0x52, 0x4b, 0x4c, 0x4f, - 0x41, 0x44, 0x5f, 0x47, 0x52, 0x4f, 0x55, 0x50, 0x5f, 0x42, 0x59, 0x5f, 0x47, 0x50, 0x55, 0x5f, - 0x4d, 0x4f, 0x44, 0x45, 0x4c, 0x10, 0x05, 0x12, 0x2a, 0x0a, 0x26, 0x47, 0x41, 0x4c, 0x41, 0x58, - 0x59, 0x5f, 0x50, 0x4f, 0x56, 0x5f, 0x57, 0x4f, 0x52, 0x4b, 0x4c, 0x4f, 0x41, 0x44, 0x5f, 0x47, - 0x52, 0x4f, 0x55, 0x50, 0x5f, 0x42, 0x59, 0x5f, 0x4e, 0x41, 0x4d, 0x45, 0x53, 0x50, 0x41, 0x43, - 0x45, 0x10, 0x06, 0x12, 0x29, 0x0a, 0x25, 0x47, 0x41, 0x4c, 0x41, 0x58, 0x59, 0x5f, 0x50, 0x4f, - 0x56, 0x5f, 0x57, 0x4f, 0x52, 0x4b, 0x4c, 0x4f, 0x41, 0x44, 0x5f, 0x47, 0x52, 0x4f, 0x55, 0x50, - 0x5f, 0x42, 0x59, 0x5f, 0x41, 0x46, 0x46, 0x49, 0x4e, 0x49, 0x54, 0x59, 0x10, 0x07, 0x12, 0x2e, - 0x0a, 0x2a, 0x47, 0x41, 0x4c, 0x41, 0x58, 0x59, 0x5f, 0x50, 0x4f, 0x56, 0x5f, 0x57, 0x4f, 0x52, - 0x4b, 0x4c, 0x4f, 0x41, 0x44, 0x5f, 0x47, 0x52, 0x4f, 0x55, 0x50, 0x5f, 0x42, 0x59, 0x5f, 0x4e, - 0x4f, 0x44, 0x45, 0x5f, 0x53, 0x45, 0x4c, 0x45, 0x43, 0x54, 0x4f, 0x52, 0x10, 0x08, 0x12, 0x26, - 0x0a, 0x22, 0x47, 0x41, 0x4c, 0x41, 0x58, 0x59, 0x5f, 0x50, 0x4f, 0x56, 0x5f, 0x57, 0x4f, 0x52, - 0x4b, 0x4c, 0x4f, 0x41, 0x44, 0x5f, 0x47, 0x52, 0x4f, 0x55, 0x50, 0x5f, 0x42, 0x59, 0x5f, 0x4c, - 0x41, 0x42, 0x45, 0x4c, 0x10, 0x09, 0x12, 0x2c, 0x0a, 0x28, 0x47, 0x41, 0x4c, 0x41, 0x58, 0x59, - 0x5f, 0x50, 0x4f, 0x56, 0x5f, 0x57, 0x4f, 0x52, 0x4b, 0x4c, 0x4f, 0x41, 0x44, 0x5f, 0x47, 0x52, - 0x4f, 0x55, 0x50, 0x5f, 0x42, 0x59, 0x5f, 0x54, 0x4f, 0x4c, 0x45, 0x52, 0x41, 0x54, 0x49, 0x4f, - 0x4e, 0x53, 0x10, 0x0a, 0x12, 0x2f, 0x0a, 0x2b, 0x47, 0x41, 0x4c, 0x41, 0x58, 0x59, 0x5f, 0x50, - 0x4f, 0x56, 0x5f, 0x57, 0x4f, 0x52, 0x4b, 0x4c, 0x4f, 0x41, 0x44, 0x5f, 0x47, 0x52, 0x4f, 0x55, - 0x50, 0x5f, 0x42, 0x59, 0x5f, 0x50, 0x52, 0x49, 0x4f, 0x52, 0x49, 0x54, 0x59, 0x5f, 0x43, 0x4c, - 0x41, 0x53, 0x53, 0x10, 0x0b, 0x12, 0x30, 0x0a, 0x2c, 0x47, 0x41, 0x4c, 0x41, 0x58, 0x59, 0x5f, + 0x41, 0x44, 0x5f, 0x47, 0x52, 0x4f, 0x55, 0x50, 0x5f, 0x42, 0x59, 0x5f, 0x43, 0x4c, 0x55, 0x53, + 0x54, 0x45, 0x52, 0x10, 0x02, 0x12, 0x2e, 0x0a, 0x2a, 0x47, 0x41, 0x4c, 0x41, 0x58, 0x59, 0x5f, + 0x50, 0x4f, 0x56, 0x5f, 0x57, 0x4f, 0x52, 0x4b, 0x4c, 0x4f, 0x41, 0x44, 0x5f, 0x47, 0x52, 0x4f, + 0x55, 0x50, 0x5f, 0x42, 0x59, 0x5f, 0x50, 0x43, 0x54, 0x5f, 0x4f, 0x50, 0x54, 0x49, 0x4d, 0x49, + 0x5a, 0x45, 0x44, 0x10, 0x03, 0x12, 0x2e, 0x0a, 0x2a, 0x47, 0x41, 0x4c, 0x41, 0x58, 0x59, 0x5f, 0x50, 0x4f, 0x56, 0x5f, 0x57, 0x4f, 0x52, 0x4b, 0x4c, 0x4f, 0x41, 0x44, 0x5f, 0x47, 0x52, 0x4f, - 0x55, 0x50, 0x5f, 0x42, 0x59, 0x5f, 0x53, 0x45, 0x52, 0x56, 0x49, 0x43, 0x45, 0x5f, 0x41, 0x43, - 0x43, 0x4f, 0x55, 0x4e, 0x54, 0x10, 0x0c, 0x12, 0x2f, 0x0a, 0x2b, 0x47, 0x41, 0x4c, 0x41, 0x58, - 0x59, 0x5f, 0x50, 0x4f, 0x56, 0x5f, 0x57, 0x4f, 0x52, 0x4b, 0x4c, 0x4f, 0x41, 0x44, 0x5f, 0x47, - 0x52, 0x4f, 0x55, 0x50, 0x5f, 0x42, 0x59, 0x5f, 0x52, 0x45, 0x53, 0x54, 0x41, 0x52, 0x54, 0x5f, - 0x50, 0x4f, 0x4c, 0x49, 0x43, 0x59, 0x10, 0x0d, 0x12, 0x2e, 0x0a, 0x2a, 0x47, 0x41, 0x4c, 0x41, + 0x55, 0x50, 0x5f, 0x42, 0x59, 0x5f, 0x43, 0x4f, 0x53, 0x54, 0x5f, 0x50, 0x45, 0x52, 0x5f, 0x48, + 0x4f, 0x55, 0x52, 0x10, 0x04, 0x12, 0x2a, 0x0a, 0x26, 0x47, 0x41, 0x4c, 0x41, 0x58, 0x59, 0x5f, + 0x50, 0x4f, 0x56, 0x5f, 0x57, 0x4f, 0x52, 0x4b, 0x4c, 0x4f, 0x41, 0x44, 0x5f, 0x47, 0x52, 0x4f, + 0x55, 0x50, 0x5f, 0x42, 0x59, 0x5f, 0x47, 0x50, 0x55, 0x5f, 0x4d, 0x4f, 0x44, 0x45, 0x4c, 0x10, + 0x05, 0x12, 0x2a, 0x0a, 0x26, 0x47, 0x41, 0x4c, 0x41, 0x58, 0x59, 0x5f, 0x50, 0x4f, 0x56, 0x5f, + 0x57, 0x4f, 0x52, 0x4b, 0x4c, 0x4f, 0x41, 0x44, 0x5f, 0x47, 0x52, 0x4f, 0x55, 0x50, 0x5f, 0x42, + 0x59, 0x5f, 0x4e, 0x41, 0x4d, 0x45, 0x53, 0x50, 0x41, 0x43, 0x45, 0x10, 0x06, 0x12, 0x29, 0x0a, + 0x25, 0x47, 0x41, 0x4c, 0x41, 0x58, 0x59, 0x5f, 0x50, 0x4f, 0x56, 0x5f, 0x57, 0x4f, 0x52, 0x4b, + 0x4c, 0x4f, 0x41, 0x44, 0x5f, 0x47, 0x52, 0x4f, 0x55, 0x50, 0x5f, 0x42, 0x59, 0x5f, 0x41, 0x46, + 0x46, 0x49, 0x4e, 0x49, 0x54, 0x59, 0x10, 0x07, 0x12, 0x2e, 0x0a, 0x2a, 0x47, 0x41, 0x4c, 0x41, 0x58, 0x59, 0x5f, 0x50, 0x4f, 0x56, 0x5f, 0x57, 0x4f, 0x52, 0x4b, 0x4c, 0x4f, 0x41, 0x44, 0x5f, - 0x47, 0x52, 0x4f, 0x55, 0x50, 0x5f, 0x42, 0x59, 0x5f, 0x52, 0x55, 0x4e, 0x54, 0x49, 0x4d, 0x45, - 0x5f, 0x43, 0x4c, 0x41, 0x53, 0x53, 0x10, 0x0e, 0x2a, 0x75, 0x0a, 0x0b, 0x43, 0x6c, 0x75, 0x73, - 0x74, 0x65, 0x72, 0x54, 0x79, 0x70, 0x65, 0x12, 0x1c, 0x0a, 0x18, 0x43, 0x4c, 0x55, 0x53, 0x54, - 0x45, 0x52, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, - 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x14, 0x0a, 0x10, 0x43, 0x4c, 0x55, 0x53, 0x54, 0x45, 0x52, - 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x4b, 0x33, 0x53, 0x10, 0x01, 0x12, 0x15, 0x0a, 0x11, 0x43, - 0x4c, 0x55, 0x53, 0x54, 0x45, 0x52, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x52, 0x4b, 0x45, 0x32, - 0x10, 0x02, 0x12, 0x1b, 0x0a, 0x17, 0x43, 0x4c, 0x55, 0x53, 0x54, 0x45, 0x52, 0x5f, 0x54, 0x59, - 0x50, 0x45, 0x5f, 0x43, 0x4f, 0x4e, 0x54, 0x41, 0x49, 0x4e, 0x45, 0x52, 0x44, 0x10, 0x03, 0x32, - 0xb3, 0x24, 0x0a, 0x0a, 0x4b, 0x38, 0x53, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x58, - 0x0a, 0x11, 0x47, 0x65, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x73, 0x53, 0x74, - 0x61, 0x74, 0x73, 0x12, 0x20, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, - 0x57, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x73, 0x53, 0x74, 0x61, 0x74, 0x73, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, - 0x65, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x73, 0x53, 0x74, 0x61, 0x74, 0x73, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x46, 0x0a, 0x0b, 0x47, 0x65, 0x74, 0x43, - 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x73, 0x12, 0x1a, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, - 0x2e, 0x47, 0x65, 0x74, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, - 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x12, 0x4e, 0x0a, 0x0c, 0x4c, 0x69, 0x73, 0x74, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x73, - 0x12, 0x1b, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x43, 0x6c, - 0x75, 0x73, 0x74, 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, - 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x43, 0x6c, 0x75, 0x73, 0x74, - 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x03, 0x88, 0x02, 0x01, - 0x12, 0x43, 0x0a, 0x0a, 0x47, 0x65, 0x74, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x12, 0x19, - 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x6c, 0x75, 0x73, 0x74, - 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x61, 0x70, 0x69, 0x2e, - 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x5b, 0x0a, 0x12, 0x47, 0x65, 0x74, 0x43, 0x6c, 0x75, 0x73, - 0x74, 0x65, 0x72, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x21, 0x2e, 0x61, 0x70, - 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x4d, - 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, - 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x6c, 0x75, 0x73, 0x74, - 0x65, 0x72, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x12, 0x55, 0x0a, 0x10, 0x47, 0x65, 0x74, 0x41, 0x6c, 0x6c, 0x4e, 0x61, 0x6d, 0x65, - 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x12, 0x1f, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, + 0x47, 0x52, 0x4f, 0x55, 0x50, 0x5f, 0x42, 0x59, 0x5f, 0x4e, 0x4f, 0x44, 0x45, 0x5f, 0x53, 0x45, + 0x4c, 0x45, 0x43, 0x54, 0x4f, 0x52, 0x10, 0x08, 0x12, 0x26, 0x0a, 0x22, 0x47, 0x41, 0x4c, 0x41, + 0x58, 0x59, 0x5f, 0x50, 0x4f, 0x56, 0x5f, 0x57, 0x4f, 0x52, 0x4b, 0x4c, 0x4f, 0x41, 0x44, 0x5f, + 0x47, 0x52, 0x4f, 0x55, 0x50, 0x5f, 0x42, 0x59, 0x5f, 0x4c, 0x41, 0x42, 0x45, 0x4c, 0x10, 0x09, + 0x12, 0x2c, 0x0a, 0x28, 0x47, 0x41, 0x4c, 0x41, 0x58, 0x59, 0x5f, 0x50, 0x4f, 0x56, 0x5f, 0x57, + 0x4f, 0x52, 0x4b, 0x4c, 0x4f, 0x41, 0x44, 0x5f, 0x47, 0x52, 0x4f, 0x55, 0x50, 0x5f, 0x42, 0x59, + 0x5f, 0x54, 0x4f, 0x4c, 0x45, 0x52, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x53, 0x10, 0x0a, 0x12, 0x2f, + 0x0a, 0x2b, 0x47, 0x41, 0x4c, 0x41, 0x58, 0x59, 0x5f, 0x50, 0x4f, 0x56, 0x5f, 0x57, 0x4f, 0x52, + 0x4b, 0x4c, 0x4f, 0x41, 0x44, 0x5f, 0x47, 0x52, 0x4f, 0x55, 0x50, 0x5f, 0x42, 0x59, 0x5f, 0x50, + 0x52, 0x49, 0x4f, 0x52, 0x49, 0x54, 0x59, 0x5f, 0x43, 0x4c, 0x41, 0x53, 0x53, 0x10, 0x0b, 0x12, + 0x30, 0x0a, 0x2c, 0x47, 0x41, 0x4c, 0x41, 0x58, 0x59, 0x5f, 0x50, 0x4f, 0x56, 0x5f, 0x57, 0x4f, + 0x52, 0x4b, 0x4c, 0x4f, 0x41, 0x44, 0x5f, 0x47, 0x52, 0x4f, 0x55, 0x50, 0x5f, 0x42, 0x59, 0x5f, + 0x53, 0x45, 0x52, 0x56, 0x49, 0x43, 0x45, 0x5f, 0x41, 0x43, 0x43, 0x4f, 0x55, 0x4e, 0x54, 0x10, + 0x0c, 0x12, 0x2f, 0x0a, 0x2b, 0x47, 0x41, 0x4c, 0x41, 0x58, 0x59, 0x5f, 0x50, 0x4f, 0x56, 0x5f, + 0x57, 0x4f, 0x52, 0x4b, 0x4c, 0x4f, 0x41, 0x44, 0x5f, 0x47, 0x52, 0x4f, 0x55, 0x50, 0x5f, 0x42, + 0x59, 0x5f, 0x52, 0x45, 0x53, 0x54, 0x41, 0x52, 0x54, 0x5f, 0x50, 0x4f, 0x4c, 0x49, 0x43, 0x59, + 0x10, 0x0d, 0x12, 0x2e, 0x0a, 0x2a, 0x47, 0x41, 0x4c, 0x41, 0x58, 0x59, 0x5f, 0x50, 0x4f, 0x56, + 0x5f, 0x57, 0x4f, 0x52, 0x4b, 0x4c, 0x4f, 0x41, 0x44, 0x5f, 0x47, 0x52, 0x4f, 0x55, 0x50, 0x5f, + 0x42, 0x59, 0x5f, 0x52, 0x55, 0x4e, 0x54, 0x49, 0x4d, 0x45, 0x5f, 0x43, 0x4c, 0x41, 0x53, 0x53, + 0x10, 0x0e, 0x2a, 0x75, 0x0a, 0x0b, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x54, 0x79, 0x70, + 0x65, 0x12, 0x1c, 0x0a, 0x18, 0x43, 0x4c, 0x55, 0x53, 0x54, 0x45, 0x52, 0x5f, 0x54, 0x59, 0x50, + 0x45, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, + 0x14, 0x0a, 0x10, 0x43, 0x4c, 0x55, 0x53, 0x54, 0x45, 0x52, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, + 0x4b, 0x33, 0x53, 0x10, 0x01, 0x12, 0x15, 0x0a, 0x11, 0x43, 0x4c, 0x55, 0x53, 0x54, 0x45, 0x52, + 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x52, 0x4b, 0x45, 0x32, 0x10, 0x02, 0x12, 0x1b, 0x0a, 0x17, + 0x43, 0x4c, 0x55, 0x53, 0x54, 0x45, 0x52, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x43, 0x4f, 0x4e, + 0x54, 0x41, 0x49, 0x4e, 0x45, 0x52, 0x44, 0x10, 0x03, 0x32, 0xb2, 0x2b, 0x0a, 0x0a, 0x4b, 0x38, + 0x53, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x58, 0x0a, 0x11, 0x47, 0x65, 0x74, 0x57, + 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x73, 0x53, 0x74, 0x61, 0x74, 0x73, 0x12, 0x20, 0x2e, + 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, + 0x61, 0x64, 0x73, 0x53, 0x74, 0x61, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x21, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x57, 0x6f, 0x72, 0x6b, + 0x6c, 0x6f, 0x61, 0x64, 0x73, 0x53, 0x74, 0x61, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x12, 0x46, 0x0a, 0x0b, 0x47, 0x65, 0x74, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, + 0x73, 0x12, 0x1a, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x6c, + 0x75, 0x73, 0x74, 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, + 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, + 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4e, 0x0a, 0x0c, 0x4c, 0x69, + 0x73, 0x74, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x73, 0x12, 0x1b, 0x2e, 0x61, 0x70, 0x69, + 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x73, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, + 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x03, 0x88, 0x02, 0x01, 0x12, 0x43, 0x0a, 0x0a, 0x47, 0x65, + 0x74, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x12, 0x19, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, + 0x31, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, + 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, + 0x5b, 0x0a, 0x12, 0x47, 0x65, 0x74, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x4d, 0x65, 0x74, + 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x21, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, + 0x65, 0x74, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, + 0x61, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, + 0x31, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x4d, 0x65, 0x74, 0x61, + 0x64, 0x61, 0x74, 0x61, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x55, 0x0a, 0x10, 0x47, 0x65, 0x74, 0x41, 0x6c, 0x6c, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, - 0x2e, 0x47, 0x65, 0x74, 0x41, 0x6c, 0x6c, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, - 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x70, 0x0a, 0x19, 0x53, 0x65, 0x61, - 0x72, 0x63, 0x68, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x42, 0x79, 0x43, - 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x12, 0x28, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, - 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, - 0x42, 0x79, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x29, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, + 0x12, 0x1f, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x6c, 0x6c, + 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x20, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x6c, + 0x6c, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x12, 0x70, 0x0a, 0x19, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x4e, 0x61, 0x6d, + 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x42, 0x79, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, + 0x12, 0x28, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x42, 0x79, 0x43, 0x6c, 0x75, 0x73, - 0x74, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x6a, 0x0a, 0x17, 0x4c, - 0x69, 0x73, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x42, 0x79, 0x43, - 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x12, 0x26, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, - 0x4c, 0x69, 0x73, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x42, 0x79, - 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x27, - 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x4e, 0x61, 0x6d, 0x65, - 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x42, 0x79, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x5e, 0x0a, 0x13, 0x47, 0x65, 0x74, 0x41, 0x6c, - 0x6c, 0x57, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x12, 0x22, + 0x74, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x29, 0x2e, 0x61, 0x70, 0x69, + 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, + 0x61, 0x63, 0x65, 0x73, 0x42, 0x79, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x6a, 0x0a, 0x17, 0x4c, 0x69, 0x73, 0x74, 0x4e, 0x61, 0x6d, + 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x42, 0x79, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, + 0x12, 0x26, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x4e, 0x61, + 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x42, 0x79, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, + 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x27, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, + 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, + 0x42, 0x79, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x12, 0x5e, 0x0a, 0x13, 0x47, 0x65, 0x74, 0x41, 0x6c, 0x6c, 0x57, 0x6f, 0x72, 0x6b, 0x6c, + 0x6f, 0x61, 0x64, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x12, 0x22, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, + 0x31, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x6c, 0x6c, 0x57, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, + 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x61, + 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x6c, 0x6c, 0x57, 0x6f, 0x72, 0x6b, + 0x6c, 0x6f, 0x61, 0x64, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x12, 0x61, 0x0a, 0x14, 0x47, 0x65, 0x74, 0x41, 0x6c, 0x6c, 0x57, 0x6f, 0x72, 0x6b, 0x6c, + 0x6f, 0x61, 0x64, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x12, 0x23, 0x2e, 0x61, 0x70, 0x69, 0x2e, + 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x6c, 0x6c, 0x57, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, + 0x64, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x24, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x6c, 0x6c, 0x57, 0x6f, - 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x41, - 0x6c, 0x6c, 0x57, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x61, 0x0a, 0x14, 0x47, 0x65, 0x74, 0x41, 0x6c, - 0x6c, 0x57, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x12, - 0x23, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x6c, 0x6c, 0x57, - 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x24, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, - 0x74, 0x41, 0x6c, 0x6c, 0x57, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x4c, 0x61, 0x62, 0x65, - 0x6c, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x61, 0x0a, 0x14, 0x47, 0x65, - 0x74, 0x41, 0x6c, 0x6c, 0x4e, 0x6f, 0x64, 0x65, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4e, 0x61, 0x6d, - 0x65, 0x73, 0x12, 0x23, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x41, - 0x6c, 0x6c, 0x4e, 0x6f, 0x64, 0x65, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4e, 0x61, 0x6d, 0x65, 0x73, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x24, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, - 0x2e, 0x47, 0x65, 0x74, 0x41, 0x6c, 0x6c, 0x4e, 0x6f, 0x64, 0x65, 0x47, 0x72, 0x6f, 0x75, 0x70, - 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x61, 0x0a, - 0x14, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x46, 0x6f, 0x72, 0x57, 0x6f, 0x72, 0x6b, - 0x6c, 0x6f, 0x61, 0x64, 0x73, 0x12, 0x23, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4d, - 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x46, 0x6f, 0x72, 0x57, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, - 0x61, 0x64, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x24, 0x2e, 0x61, 0x70, 0x69, - 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x46, 0x6f, 0x72, 0x57, - 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x12, 0x4c, 0x0a, 0x0d, 0x47, 0x65, 0x74, 0x4e, 0x6f, 0x64, 0x65, 0x47, 0x72, 0x6f, 0x75, 0x70, - 0x73, 0x12, 0x1c, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x4e, 0x6f, - 0x64, 0x65, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, - 0x1d, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x4e, 0x6f, 0x64, 0x65, - 0x47, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x55, - 0x0a, 0x10, 0x47, 0x65, 0x74, 0x41, 0x6c, 0x6c, 0x4e, 0x6f, 0x64, 0x65, 0x47, 0x72, 0x6f, 0x75, - 0x70, 0x73, 0x12, 0x1f, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x41, - 0x6c, 0x6c, 0x4e, 0x6f, 0x64, 0x65, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, - 0x41, 0x6c, 0x6c, 0x4e, 0x6f, 0x64, 0x65, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x6d, 0x0a, 0x18, 0x47, 0x65, 0x74, 0x4e, 0x6f, 0x64, 0x65, - 0x47, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x55, 0x74, 0x69, 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x12, 0x27, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x4e, 0x6f, - 0x64, 0x65, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x55, 0x74, 0x69, 0x6c, 0x69, 0x7a, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x28, 0x2e, 0x61, 0x70, 0x69, - 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x4e, 0x6f, 0x64, 0x65, 0x47, 0x72, 0x6f, 0x75, 0x70, - 0x73, 0x55, 0x74, 0x69, 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x49, 0x0a, 0x0c, 0x47, 0x65, 0x74, 0x4e, 0x6f, 0x64, 0x65, 0x47, - 0x72, 0x6f, 0x75, 0x70, 0x12, 0x1b, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, - 0x74, 0x4e, 0x6f, 0x64, 0x65, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x1c, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x4e, 0x6f, - 0x64, 0x65, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, - 0x3a, 0x0a, 0x07, 0x47, 0x65, 0x74, 0x4e, 0x6f, 0x64, 0x65, 0x12, 0x16, 0x2e, 0x61, 0x70, 0x69, - 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x4e, - 0x6f, 0x64, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x49, 0x0a, 0x0c, 0x47, - 0x65, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x73, 0x12, 0x1b, 0x2e, 0x61, 0x70, + 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x61, 0x0a, 0x14, 0x47, 0x65, 0x74, 0x41, 0x6c, 0x6c, 0x4e, 0x6f, + 0x64, 0x65, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x12, 0x23, 0x2e, 0x61, + 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x6c, 0x6c, 0x4e, 0x6f, 0x64, 0x65, + 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x24, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x6c, + 0x6c, 0x4e, 0x6f, 0x64, 0x65, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x61, 0x0a, 0x14, 0x4d, 0x65, 0x74, 0x61, 0x64, + 0x61, 0x74, 0x61, 0x46, 0x6f, 0x72, 0x57, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x73, 0x12, + 0x23, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, + 0x61, 0x46, 0x6f, 0x72, 0x57, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x73, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x24, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x65, + 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x46, 0x6f, 0x72, 0x57, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, + 0x64, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4c, 0x0a, 0x0d, 0x47, 0x65, + 0x74, 0x4e, 0x6f, 0x64, 0x65, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x12, 0x1c, 0x2e, 0x61, 0x70, + 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x4e, 0x6f, 0x64, 0x65, 0x47, 0x72, 0x6f, 0x75, + 0x70, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x61, 0x70, 0x69, 0x2e, + 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x4e, 0x6f, 0x64, 0x65, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x73, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x55, 0x0a, 0x10, 0x47, 0x65, 0x74, 0x41, + 0x6c, 0x6c, 0x4e, 0x6f, 0x64, 0x65, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x12, 0x1f, 0x2e, 0x61, + 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x6c, 0x6c, 0x4e, 0x6f, 0x64, 0x65, + 0x47, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, + 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x6c, 0x6c, 0x4e, 0x6f, 0x64, + 0x65, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, + 0x6d, 0x0a, 0x18, 0x47, 0x65, 0x74, 0x4e, 0x6f, 0x64, 0x65, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x73, + 0x55, 0x74, 0x69, 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x27, 0x2e, 0x61, 0x70, + 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x4e, 0x6f, 0x64, 0x65, 0x47, 0x72, 0x6f, 0x75, + 0x70, 0x73, 0x55, 0x74, 0x69, 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x28, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, + 0x74, 0x4e, 0x6f, 0x64, 0x65, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x55, 0x74, 0x69, 0x6c, 0x69, + 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x6a, + 0x0a, 0x17, 0x47, 0x65, 0x74, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x4e, 0x6f, 0x64, 0x65, + 0x49, 0x6e, 0x76, 0x65, 0x6e, 0x74, 0x6f, 0x72, 0x79, 0x12, 0x26, 0x2e, 0x61, 0x70, 0x69, 0x2e, + 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x4e, 0x6f, 0x64, + 0x65, 0x49, 0x6e, 0x76, 0x65, 0x6e, 0x74, 0x6f, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x27, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x6c, + 0x75, 0x73, 0x74, 0x65, 0x72, 0x4e, 0x6f, 0x64, 0x65, 0x49, 0x6e, 0x76, 0x65, 0x6e, 0x74, 0x6f, + 0x72, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x70, 0x0a, 0x19, 0x47, 0x65, + 0x74, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x4e, 0x6f, 0x64, 0x65, 0x46, 0x69, 0x6c, 0x74, + 0x65, 0x72, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x12, 0x28, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, + 0x2e, 0x47, 0x65, 0x74, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x4e, 0x6f, 0x64, 0x65, 0x46, + 0x69, 0x6c, 0x74, 0x65, 0x72, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x29, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x6c, + 0x75, 0x73, 0x74, 0x65, 0x72, 0x4e, 0x6f, 0x64, 0x65, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x49, + 0x6e, 0x64, 0x65, 0x78, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x49, 0x0a, 0x0c, + 0x47, 0x65, 0x74, 0x4e, 0x6f, 0x64, 0x65, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x12, 0x1b, 0x2e, 0x61, + 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x4e, 0x6f, 0x64, 0x65, 0x47, 0x72, 0x6f, + 0x75, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x61, 0x70, 0x69, 0x2e, + 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x4e, 0x6f, 0x64, 0x65, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3a, 0x0a, 0x07, 0x47, 0x65, 0x74, 0x4e, 0x6f, + 0x64, 0x65, 0x12, 0x16, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x4e, + 0x6f, 0x64, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x61, 0x70, 0x69, + 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x12, 0x6d, 0x0a, 0x18, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x4e, 0x6f, + 0x64, 0x65, 0x43, 0x6c, 0x61, 0x69, 0x6d, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x69, 0x6f, 0x6e, 0x12, + 0x27, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x4e, 0x6f, 0x64, 0x65, 0x43, 0x6c, 0x61, 0x69, 0x6d, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x69, 0x6f, + 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x28, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, + 0x31, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x4e, 0x6f, 0x64, 0x65, 0x43, 0x6c, 0x61, + 0x69, 0x6d, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x12, 0x73, 0x0a, 0x1a, 0x47, 0x65, 0x74, 0x4e, 0x6f, 0x64, 0x65, 0x43, 0x6c, 0x61, + 0x69, 0x6d, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, + 0x12, 0x29, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x4e, 0x6f, 0x64, + 0x65, 0x43, 0x6c, 0x61, 0x69, 0x6d, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, + 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2a, 0x2e, 0x61, 0x70, + 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x4e, 0x6f, 0x64, 0x65, 0x43, 0x6c, 0x61, 0x69, + 0x6d, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x55, 0x0a, 0x10, 0x47, 0x65, 0x74, 0x4e, 0x6f, + 0x64, 0x65, 0x43, 0x6c, 0x61, 0x69, 0x6d, 0x52, 0x65, 0x66, 0x73, 0x12, 0x1f, 0x2e, 0x61, 0x70, + 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x4e, 0x6f, 0x64, 0x65, 0x43, 0x6c, 0x61, 0x69, + 0x6d, 0x52, 0x65, 0x66, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x61, + 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x4e, 0x6f, 0x64, 0x65, 0x43, 0x6c, 0x61, + 0x69, 0x6d, 0x52, 0x65, 0x66, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x70, + 0x0a, 0x19, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x4e, 0x6f, 0x64, 0x65, 0x43, 0x6c, 0x61, + 0x69, 0x6d, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x28, 0x2e, 0x61, 0x70, + 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x4e, 0x6f, 0x64, 0x65, + 0x43, 0x6c, 0x61, 0x69, 0x6d, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x29, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x4e, 0x6f, 0x64, 0x65, 0x43, 0x6c, 0x61, 0x69, 0x6d, 0x44, + 0x65, 0x6c, 0x65, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x12, 0x49, 0x0a, 0x0c, 0x47, 0x65, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x73, + 0x12, 0x1b, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x57, 0x6f, 0x72, + 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, + 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, + 0x61, 0x64, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x67, 0x0a, 0x16, 0x4c, + 0x69, 0x73, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x73, 0x50, 0x61, 0x67, 0x69, + 0x6e, 0x61, 0x74, 0x65, 0x64, 0x12, 0x25, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4c, + 0x69, 0x73, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x73, 0x50, 0x61, 0x67, 0x69, + 0x6e, 0x61, 0x74, 0x65, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x26, 0x2e, 0x61, + 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, + 0x61, 0x64, 0x73, 0x50, 0x61, 0x67, 0x69, 0x6e, 0x61, 0x74, 0x65, 0x64, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x5e, 0x0a, 0x13, 0x47, 0x65, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x6c, + 0x6f, 0x61, 0x64, 0x73, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x12, 0x22, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, - 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, - 0x31, 0x2e, 0x47, 0x65, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x73, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x67, 0x0a, 0x16, 0x4c, 0x69, 0x73, 0x74, 0x57, 0x6f, - 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x73, 0x50, 0x61, 0x67, 0x69, 0x6e, 0x61, 0x74, 0x65, 0x64, - 0x12, 0x25, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x57, 0x6f, - 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x73, 0x50, 0x61, 0x67, 0x69, 0x6e, 0x61, 0x74, 0x65, 0x64, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x26, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, - 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x73, 0x50, 0x61, - 0x67, 0x69, 0x6e, 0x61, 0x74, 0x65, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, - 0x5e, 0x0a, 0x13, 0x47, 0x65, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x73, 0x53, - 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x12, 0x22, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, - 0x47, 0x65, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x73, 0x53, 0x75, 0x6d, 0x6d, - 0x61, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x61, 0x70, 0x69, - 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x73, - 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, - 0x46, 0x0a, 0x0b, 0x47, 0x65, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x12, 0x1a, + 0x73, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x23, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x57, 0x6f, 0x72, 0x6b, + 0x6c, 0x6f, 0x61, 0x64, 0x73, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x6d, 0x0a, 0x18, 0x47, 0x65, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x6c, + 0x6f, 0x61, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x54, 0x72, 0x65, 0x6e, 0x64, 0x73, + 0x12, 0x27, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x57, 0x6f, 0x72, + 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x54, 0x72, 0x65, 0x6e, + 0x64, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x28, 0x2e, 0x61, 0x70, 0x69, 0x2e, + 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x54, 0x72, 0x65, 0x6e, 0x64, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x12, 0x46, 0x0a, 0x0b, 0x47, 0x65, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, + 0x61, 0x64, 0x12, 0x1a, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x57, + 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x6c, - 0x6f, 0x61, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x61, 0x70, 0x69, - 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x82, 0x01, 0x0a, 0x1f, 0x47, 0x65, 0x74, 0x57, - 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, - 0x50, 0x65, 0x72, 0x63, 0x65, 0x6e, 0x74, 0x69, 0x6c, 0x65, 0x73, 0x12, 0x2e, 0x2e, 0x61, 0x70, - 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, - 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x50, 0x65, 0x72, 0x63, 0x65, 0x6e, 0x74, - 0x69, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2f, 0x2e, 0x61, 0x70, - 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, - 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x50, 0x65, 0x72, 0x63, 0x65, 0x6e, 0x74, - 0x69, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x7f, 0x0a, 0x1e, + 0x6f, 0x61, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x82, 0x01, 0x0a, 0x1f, 0x47, 0x65, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x43, 0x6f, 0x6e, 0x74, 0x61, - 0x69, 0x6e, 0x65, 0x72, 0x54, 0x69, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x69, 0x65, 0x73, 0x12, 0x2d, - 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x6c, - 0x6f, 0x61, 0x64, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x54, 0x69, 0x6d, 0x65, - 0x53, 0x65, 0x72, 0x69, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2e, 0x2e, - 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, - 0x61, 0x64, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x54, 0x69, 0x6d, 0x65, 0x53, - 0x65, 0x72, 0x69, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x82, 0x01, - 0x0a, 0x1f, 0x47, 0x65, 0x74, 0x4c, 0x61, 0x74, 0x65, 0x73, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x61, - 0x69, 0x6e, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x4c, 0x69, 0x6d, 0x69, 0x74, - 0x73, 0x12, 0x2e, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x4c, 0x61, - 0x74, 0x65, 0x73, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x2f, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x4c, 0x61, - 0x74, 0x65, 0x73, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x12, 0x67, 0x0a, 0x16, 0x47, 0x65, 0x74, 0x4e, 0x6f, 0x64, 0x65, 0x47, 0x50, 0x55, - 0x4d, 0x69, 0x67, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x73, 0x12, 0x25, 0x2e, 0x61, - 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x4e, 0x6f, 0x64, 0x65, 0x47, 0x50, 0x55, - 0x4d, 0x69, 0x67, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x26, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, + 0x69, 0x6e, 0x65, 0x72, 0x50, 0x65, 0x72, 0x63, 0x65, 0x6e, 0x74, 0x69, 0x6c, 0x65, 0x73, 0x12, + 0x2e, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x57, 0x6f, 0x72, 0x6b, + 0x6c, 0x6f, 0x61, 0x64, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x50, 0x65, 0x72, + 0x63, 0x65, 0x6e, 0x74, 0x69, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x2f, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x57, 0x6f, 0x72, 0x6b, + 0x6c, 0x6f, 0x61, 0x64, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x50, 0x65, 0x72, + 0x63, 0x65, 0x6e, 0x74, 0x69, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x12, 0x7f, 0x0a, 0x1e, 0x47, 0x65, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x43, + 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x54, 0x69, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x69, + 0x65, 0x73, 0x12, 0x2d, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x57, + 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, + 0x54, 0x69, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x69, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x2e, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x57, 0x6f, + 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x54, + 0x69, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x69, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x12, 0x82, 0x01, 0x0a, 0x1f, 0x47, 0x65, 0x74, 0x4c, 0x61, 0x74, 0x65, 0x73, 0x74, 0x43, + 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x4c, + 0x69, 0x6d, 0x69, 0x74, 0x73, 0x12, 0x2e, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, + 0x65, 0x74, 0x4c, 0x61, 0x74, 0x65, 0x73, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, + 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x73, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2f, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, + 0x65, 0x74, 0x4c, 0x61, 0x74, 0x65, 0x73, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, + 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x73, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x67, 0x0a, 0x16, 0x47, 0x65, 0x74, 0x4e, 0x6f, 0x64, + 0x65, 0x47, 0x50, 0x55, 0x4d, 0x69, 0x67, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x73, + 0x12, 0x25, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x4e, 0x6f, 0x64, + 0x65, 0x47, 0x50, 0x55, 0x4d, 0x69, 0x67, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x73, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x26, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, + 0x2e, 0x47, 0x65, 0x74, 0x4e, 0x6f, 0x64, 0x65, 0x47, 0x50, 0x55, 0x4d, 0x69, 0x67, 0x49, 0x6e, + 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, + 0x82, 0x01, 0x0a, 0x1f, 0x47, 0x65, 0x74, 0x4e, 0x6f, 0x64, 0x65, 0x47, 0x50, 0x55, 0x4d, 0x69, + 0x67, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x65, 0x72, + 0x69, 0x65, 0x73, 0x12, 0x2e, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x4e, 0x6f, 0x64, 0x65, 0x47, 0x50, 0x55, 0x4d, 0x69, 0x67, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, - 0x63, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x85, 0x01, 0x0a, 0x20, - 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x49, 0x6e, 0x64, 0x69, - 0x76, 0x69, 0x64, 0x75, 0x61, 0x6c, 0x47, 0x50, 0x55, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, - 0x12, 0x2f, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, - 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x49, 0x6e, 0x64, 0x69, 0x76, 0x69, 0x64, 0x75, 0x61, 0x6c, - 0x47, 0x50, 0x55, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x30, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x6f, - 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x49, 0x6e, 0x64, 0x69, 0x76, 0x69, 0x64, 0x75, 0x61, - 0x6c, 0x47, 0x50, 0x55, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x12, 0x61, 0x0a, 0x14, 0x47, 0x65, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, - 0x61, 0x64, 0x4c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x73, 0x12, 0x23, 0x2e, 0x61, 0x70, - 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, - 0x4c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x24, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x57, 0x6f, 0x72, - 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x4c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x73, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x66, 0x0a, 0x14, 0x47, 0x65, 0x74, 0x46, 0x6f, 0x72, - 0x65, 0x63, 0x61, 0x73, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x73, 0x12, 0x23, + 0x63, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x65, 0x72, 0x69, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x2f, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, + 0x4e, 0x6f, 0x64, 0x65, 0x47, 0x50, 0x55, 0x4d, 0x69, 0x67, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, + 0x63, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x65, 0x72, 0x69, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x85, 0x01, 0x0a, 0x20, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x74, + 0x61, 0x69, 0x6e, 0x65, 0x72, 0x49, 0x6e, 0x64, 0x69, 0x76, 0x69, 0x64, 0x75, 0x61, 0x6c, 0x47, + 0x50, 0x55, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x12, 0x2f, 0x2e, 0x61, 0x70, 0x69, 0x2e, + 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x49, + 0x6e, 0x64, 0x69, 0x76, 0x69, 0x64, 0x75, 0x61, 0x6c, 0x47, 0x50, 0x55, 0x4d, 0x65, 0x74, 0x72, + 0x69, 0x63, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x30, 0x2e, 0x61, 0x70, 0x69, + 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, + 0x49, 0x6e, 0x64, 0x69, 0x76, 0x69, 0x64, 0x75, 0x61, 0x6c, 0x47, 0x50, 0x55, 0x4d, 0x65, 0x74, + 0x72, 0x69, 0x63, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x61, 0x0a, 0x14, + 0x47, 0x65, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x4c, 0x61, 0x6e, 0x67, 0x75, + 0x61, 0x67, 0x65, 0x73, 0x12, 0x23, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, + 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x4c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, + 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x24, 0x2e, 0x61, 0x70, 0x69, 0x2e, + 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x4c, 0x61, + 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, + 0x66, 0x0a, 0x14, 0x47, 0x65, 0x74, 0x46, 0x6f, 0x72, 0x65, 0x63, 0x61, 0x73, 0x74, 0x57, 0x6f, + 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x73, 0x12, 0x23, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, + 0x2e, 0x47, 0x65, 0x74, 0x46, 0x6f, 0x72, 0x65, 0x63, 0x61, 0x73, 0x74, 0x57, 0x6f, 0x72, 0x6b, + 0x6c, 0x6f, 0x61, 0x64, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x24, 0x2e, 0x61, + 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x46, 0x6f, 0x72, 0x65, 0x63, 0x61, 0x73, + 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x22, 0x03, 0x88, 0x02, 0x01, 0x12, 0x63, 0x0a, 0x13, 0x47, 0x65, 0x74, 0x46, 0x6f, + 0x72, 0x65, 0x63, 0x61, 0x73, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x12, 0x22, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x46, 0x6f, 0x72, 0x65, 0x63, - 0x61, 0x73, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x73, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x24, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, - 0x46, 0x6f, 0x72, 0x65, 0x63, 0x61, 0x73, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, - 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x03, 0x88, 0x02, 0x01, 0x12, 0x63, - 0x0a, 0x13, 0x47, 0x65, 0x74, 0x46, 0x6f, 0x72, 0x65, 0x63, 0x61, 0x73, 0x74, 0x57, 0x6f, 0x72, - 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x12, 0x22, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, - 0x65, 0x74, 0x46, 0x6f, 0x72, 0x65, 0x63, 0x61, 0x73, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, - 0x61, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x61, 0x70, 0x69, 0x2e, - 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x46, 0x6f, 0x72, 0x65, 0x63, 0x61, 0x73, 0x74, 0x57, 0x6f, - 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x03, - 0x88, 0x02, 0x01, 0x12, 0x49, 0x0a, 0x0c, 0x47, 0x65, 0x74, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, - 0x63, 0x65, 0x73, 0x12, 0x1b, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, - 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x1c, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x65, 0x73, - 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x6d, - 0x0a, 0x18, 0x47, 0x65, 0x74, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x46, 0x69, 0x6c, - 0x74, 0x65, 0x72, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x27, 0x2e, 0x61, 0x70, 0x69, - 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x46, - 0x69, 0x6c, 0x74, 0x65, 0x72, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x28, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, + 0x61, 0x73, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x46, + 0x6f, 0x72, 0x65, 0x63, 0x61, 0x73, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x03, 0x88, 0x02, 0x01, 0x12, 0x49, 0x0a, 0x0c, + 0x47, 0x65, 0x74, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x12, 0x1b, 0x2e, 0x61, + 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, + 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x61, 0x70, 0x69, 0x2e, + 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x6d, 0x0a, 0x18, 0x47, 0x65, 0x74, 0x52, 0x65, + 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x4f, 0x70, 0x74, 0x69, + 0x6f, 0x6e, 0x73, 0x12, 0x27, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x4f, 0x70, - 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3a, 0x0a, - 0x07, 0x47, 0x65, 0x74, 0x50, 0x6f, 0x64, 0x73, 0x12, 0x16, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, - 0x31, 0x2e, 0x47, 0x65, 0x74, 0x50, 0x6f, 0x64, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x17, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x50, 0x6f, 0x64, - 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x6d, 0x0a, 0x18, 0x47, 0x65, 0x74, - 0x4c, 0x61, 0x74, 0x65, 0x73, 0x74, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x56, 0x65, - 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x27, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, - 0x65, 0x74, 0x4c, 0x61, 0x74, 0x65, 0x73, 0x74, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, - 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x28, + 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x28, 0x2e, 0x61, + 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, + 0x65, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3a, 0x0a, 0x07, 0x47, 0x65, 0x74, 0x50, 0x6f, 0x64, + 0x73, 0x12, 0x16, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x50, 0x6f, + 0x64, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x61, 0x70, 0x69, 0x2e, + 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x50, 0x6f, 0x64, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x12, 0x6d, 0x0a, 0x18, 0x47, 0x65, 0x74, 0x4c, 0x61, 0x74, 0x65, 0x73, 0x74, 0x4f, + 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x27, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x4c, 0x61, 0x74, 0x65, 0x73, 0x74, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x79, 0x0a, 0x1c, 0x47, 0x61, 0x6c, 0x61, - 0x78, 0x79, 0x47, 0x65, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x50, 0x65, 0x72, - 0x73, 0x70, 0x65, 0x63, 0x74, 0x69, 0x76, 0x65, 0x12, 0x2b, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, - 0x31, 0x2e, 0x47, 0x61, 0x6c, 0x61, 0x78, 0x79, 0x47, 0x65, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x6c, - 0x6f, 0x61, 0x64, 0x50, 0x65, 0x72, 0x73, 0x70, 0x65, 0x63, 0x74, 0x69, 0x76, 0x65, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2c, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, - 0x61, 0x6c, 0x61, 0x78, 0x79, 0x47, 0x65, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, - 0x50, 0x65, 0x72, 0x73, 0x70, 0x65, 0x63, 0x74, 0x69, 0x76, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x12, 0x4c, 0x0a, 0x0d, 0x4c, 0x69, 0x73, 0x74, 0x41, 0x75, 0x64, 0x69, 0x74, - 0x4c, 0x6f, 0x67, 0x73, 0x12, 0x1c, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, - 0x73, 0x74, 0x41, 0x75, 0x64, 0x69, 0x74, 0x4c, 0x6f, 0x67, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, - 0x41, 0x75, 0x64, 0x69, 0x74, 0x4c, 0x6f, 0x67, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x12, 0x6a, 0x0a, 0x17, 0x4c, 0x69, 0x73, 0x74, 0x41, 0x75, 0x64, 0x69, 0x74, 0x4c, 0x6f, - 0x67, 0x4f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x61, 0x74, 0x6f, 0x72, 0x73, 0x12, 0x26, 0x2e, 0x61, - 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x41, 0x75, 0x64, 0x69, 0x74, 0x4c, - 0x6f, 0x67, 0x4f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x61, 0x74, 0x6f, 0x72, 0x73, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x27, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x28, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, + 0x2e, 0x47, 0x65, 0x74, 0x4c, 0x61, 0x74, 0x65, 0x73, 0x74, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, + 0x6f, 0x72, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x12, 0x79, 0x0a, 0x1c, 0x47, 0x61, 0x6c, 0x61, 0x78, 0x79, 0x47, 0x65, 0x74, 0x57, 0x6f, + 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x50, 0x65, 0x72, 0x73, 0x70, 0x65, 0x63, 0x74, 0x69, 0x76, + 0x65, 0x12, 0x2b, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x61, 0x6c, 0x61, 0x78, + 0x79, 0x47, 0x65, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x50, 0x65, 0x72, 0x73, + 0x70, 0x65, 0x63, 0x74, 0x69, 0x76, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2c, + 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x61, 0x6c, 0x61, 0x78, 0x79, 0x47, 0x65, + 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x50, 0x65, 0x72, 0x73, 0x70, 0x65, 0x63, + 0x74, 0x69, 0x76, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4c, 0x0a, 0x0d, + 0x4c, 0x69, 0x73, 0x74, 0x41, 0x75, 0x64, 0x69, 0x74, 0x4c, 0x6f, 0x67, 0x73, 0x12, 0x1c, 0x2e, + 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x41, 0x75, 0x64, 0x69, 0x74, + 0x4c, 0x6f, 0x67, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x61, 0x70, + 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x41, 0x75, 0x64, 0x69, 0x74, 0x4c, 0x6f, + 0x67, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x6a, 0x0a, 0x17, 0x4c, 0x69, 0x73, 0x74, 0x41, 0x75, 0x64, 0x69, 0x74, 0x4c, 0x6f, 0x67, 0x4f, 0x72, 0x69, 0x67, 0x69, 0x6e, - 0x61, 0x74, 0x6f, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x58, 0x0a, - 0x11, 0x53, 0x65, 0x6e, 0x64, 0x57, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x45, 0x6d, 0x61, - 0x69, 0x6c, 0x12, 0x20, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x6e, 0x64, - 0x57, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x45, 0x6d, 0x61, 0x69, 0x6c, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, - 0x6e, 0x64, 0x57, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x45, 0x6d, 0x61, 0x69, 0x6c, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x67, 0x0a, 0x16, 0x53, 0x65, 0x6e, 0x64, 0x57, - 0x65, 0x65, 0x6b, 0x6c, 0x79, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x45, 0x6d, 0x61, 0x69, - 0x6c, 0x12, 0x25, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x57, + 0x61, 0x74, 0x6f, 0x72, 0x73, 0x12, 0x26, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4c, + 0x69, 0x73, 0x74, 0x41, 0x75, 0x64, 0x69, 0x74, 0x4c, 0x6f, 0x67, 0x4f, 0x72, 0x69, 0x67, 0x69, + 0x6e, 0x61, 0x74, 0x6f, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x27, 0x2e, + 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x41, 0x75, 0x64, 0x69, 0x74, + 0x4c, 0x6f, 0x67, 0x4f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x61, 0x74, 0x6f, 0x72, 0x73, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x58, 0x0a, 0x11, 0x53, 0x65, 0x6e, 0x64, 0x57, 0x6f, + 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x45, 0x6d, 0x61, 0x69, 0x6c, 0x12, 0x20, 0x2e, 0x61, 0x70, + 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x57, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, + 0x64, 0x45, 0x6d, 0x61, 0x69, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, + 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x57, 0x6f, 0x72, 0x6b, 0x6c, + 0x6f, 0x61, 0x64, 0x45, 0x6d, 0x61, 0x69, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x12, 0x67, 0x0a, 0x16, 0x53, 0x65, 0x6e, 0x64, 0x57, 0x65, 0x65, 0x6b, 0x6c, 0x79, 0x53, 0x75, + 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x45, 0x6d, 0x61, 0x69, 0x6c, 0x12, 0x25, 0x2e, 0x61, 0x70, 0x69, + 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x57, 0x65, 0x65, 0x6b, 0x6c, 0x79, 0x53, 0x75, + 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x45, 0x6d, 0x61, 0x69, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x26, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x57, 0x65, 0x65, 0x6b, 0x6c, 0x79, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x45, 0x6d, 0x61, 0x69, - 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x26, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, - 0x31, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x57, 0x65, 0x65, 0x6b, 0x6c, 0x79, 0x53, 0x75, 0x6d, 0x6d, - 0x61, 0x72, 0x79, 0x45, 0x6d, 0x61, 0x69, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x12, 0x5e, 0x0a, 0x13, 0x47, 0x65, 0x74, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x73, 0x4e, - 0x6f, 0x64, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x22, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, - 0x2e, 0x47, 0x65, 0x74, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x73, 0x4e, 0x6f, 0x64, 0x65, - 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x61, 0x70, - 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x73, - 0x4e, 0x6f, 0x64, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x12, 0x5b, 0x0a, 0x12, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x4b, 0x38, 0x73, 0x52, 0x65, 0x73, - 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x12, 0x21, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, - 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x4b, 0x38, 0x73, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, - 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x61, 0x70, 0x69, 0x2e, - 0x76, 0x31, 0x2e, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x4b, 0x38, 0x73, 0x52, 0x65, 0x73, 0x6f, - 0x75, 0x72, 0x63, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x5b, 0x0a, - 0x12, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x4b, 0x38, 0x73, 0x57, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, - 0x61, 0x64, 0x73, 0x12, 0x21, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x61, - 0x72, 0x63, 0x68, 0x4b, 0x38, 0x73, 0x57, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x73, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, - 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x4b, 0x38, 0x73, 0x57, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, - 0x64, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4f, 0x0a, 0x0e, 0x47, 0x65, - 0x74, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x54, 0x79, 0x70, 0x65, 0x12, 0x1d, 0x2e, 0x61, - 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, - 0x54, 0x79, 0x70, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x61, 0x70, - 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x54, - 0x79, 0x70, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x5e, 0x0a, 0x13, 0x47, - 0x65, 0x74, 0x52, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x46, 0x6f, 0x72, 0x4b, 0x69, - 0x6e, 0x64, 0x12, 0x22, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x52, - 0x65, 0x6c, 0x61, 0x74, 0x65, 0x64, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, - 0x47, 0x65, 0x74, 0x52, 0x65, 0x6c, 0x61, 0x74, 0x65, 0x64, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, - 0x63, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x5b, 0x0a, 0x12, 0x4c, - 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x4e, 0x6f, 0x64, 0x65, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, - 0x65, 0x12, 0x21, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x6f, 0x6f, 0x6b, 0x75, - 0x70, 0x4e, 0x6f, 0x64, 0x65, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x6f, - 0x6f, 0x6b, 0x75, 0x70, 0x4e, 0x6f, 0x64, 0x65, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x64, 0x0a, 0x15, 0x47, 0x65, 0x74, 0x57, - 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x50, 0x6f, 0x64, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, - 0x79, 0x12, 0x24, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x57, 0x6f, - 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x50, 0x6f, 0x64, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x25, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, - 0x2e, 0x47, 0x65, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x50, 0x6f, 0x64, 0x48, - 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4f, - 0x0a, 0x0e, 0x41, 0x64, 0x64, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x54, 0x61, 0x67, 0x73, - 0x12, 0x1d, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x64, 0x64, 0x43, 0x6c, 0x75, + 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x5e, 0x0a, 0x13, 0x47, 0x65, 0x74, + 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x73, 0x4e, 0x6f, 0x64, 0x65, 0x49, 0x6e, 0x66, 0x6f, + 0x12, 0x22, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x6c, 0x75, + 0x73, 0x74, 0x65, 0x72, 0x73, 0x4e, 0x6f, 0x64, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, + 0x74, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x73, 0x4e, 0x6f, 0x64, 0x65, 0x49, 0x6e, 0x66, + 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x5b, 0x0a, 0x12, 0x53, 0x65, 0x61, + 0x72, 0x63, 0x68, 0x4b, 0x38, 0x73, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x12, + 0x21, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x4b, + 0x38, 0x73, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x61, 0x72, + 0x63, 0x68, 0x4b, 0x38, 0x73, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x5b, 0x0a, 0x12, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, + 0x4b, 0x38, 0x73, 0x57, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x73, 0x12, 0x21, 0x2e, 0x61, + 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x4b, 0x38, 0x73, 0x57, + 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x22, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x4b, + 0x38, 0x73, 0x57, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x12, 0x4f, 0x0a, 0x0e, 0x47, 0x65, 0x74, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, + 0x72, 0x54, 0x79, 0x70, 0x65, 0x12, 0x1d, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, + 0x65, 0x74, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x54, 0x79, 0x70, 0x65, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, + 0x74, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x54, 0x79, 0x70, 0x65, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x5e, 0x0a, 0x13, 0x47, 0x65, 0x74, 0x52, 0x65, 0x6c, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x73, 0x46, 0x6f, 0x72, 0x4b, 0x69, 0x6e, 0x64, 0x12, 0x22, 0x2e, 0x61, 0x70, + 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x65, 0x6c, 0x61, 0x74, 0x65, 0x64, 0x52, + 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x23, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x65, 0x6c, 0x61, + 0x74, 0x65, 0x64, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x5b, 0x0a, 0x12, 0x4c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x4e, 0x6f, + 0x64, 0x65, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x12, 0x21, 0x2e, 0x61, 0x70, 0x69, + 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x4e, 0x6f, 0x64, 0x65, 0x49, 0x6e, + 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, + 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x4e, 0x6f, 0x64, + 0x65, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x12, 0x64, 0x0a, 0x15, 0x47, 0x65, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, + 0x50, 0x6f, 0x64, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x12, 0x24, 0x2e, 0x61, 0x70, 0x69, + 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x50, + 0x6f, 0x64, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x25, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x57, 0x6f, 0x72, + 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x50, 0x6f, 0x64, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4f, 0x0a, 0x0e, 0x41, 0x64, 0x64, 0x43, 0x6c, + 0x75, 0x73, 0x74, 0x65, 0x72, 0x54, 0x61, 0x67, 0x73, 0x12, 0x1d, 0x2e, 0x61, 0x70, 0x69, 0x2e, + 0x76, 0x31, 0x2e, 0x41, 0x64, 0x64, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x54, 0x61, 0x67, + 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, + 0x31, 0x2e, 0x41, 0x64, 0x64, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x54, 0x61, 0x67, 0x73, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x58, 0x0a, 0x11, 0x52, 0x65, 0x6d, 0x6f, + 0x76, 0x65, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x54, 0x61, 0x67, 0x73, 0x12, 0x20, 0x2e, + 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x54, 0x61, 0x67, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, - 0x1e, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x64, 0x64, 0x43, 0x6c, 0x75, 0x73, - 0x74, 0x65, 0x72, 0x54, 0x61, 0x67, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, - 0x58, 0x0a, 0x11, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, - 0x54, 0x61, 0x67, 0x73, 0x12, 0x20, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, - 0x6d, 0x6f, 0x76, 0x65, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x54, 0x61, 0x67, 0x73, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, - 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x54, 0x61, 0x67, - 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3d, 0x0a, 0x08, 0x4c, 0x69, 0x73, - 0x74, 0x54, 0x61, 0x67, 0x73, 0x12, 0x17, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4c, - 0x69, 0x73, 0x74, 0x54, 0x61, 0x67, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18, + 0x21, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x43, + 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x54, 0x61, 0x67, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x12, 0x3d, 0x0a, 0x08, 0x4c, 0x69, 0x73, 0x74, 0x54, 0x61, 0x67, 0x73, 0x12, 0x17, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x54, 0x61, 0x67, 0x73, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x5b, 0x0a, 0x12, 0x47, 0x65, 0x74, 0x4d, - 0x69, 0x67, 0x50, 0x61, 0x72, 0x74, 0x65, 0x64, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x21, - 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x4d, 0x69, 0x67, 0x50, 0x61, - 0x72, 0x74, 0x65, 0x64, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x22, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x4d, 0x69, - 0x67, 0x50, 0x61, 0x72, 0x74, 0x65, 0x64, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x32, 0xdc, 0x02, 0x0a, 0x16, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, - 0x72, 0x4d, 0x75, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, - 0x12, 0x4c, 0x0a, 0x0d, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, - 0x72, 0x12, 0x1c, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, - 0x65, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, - 0x1d, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x43, - 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4c, - 0x0a, 0x0d, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x12, - 0x1c, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x43, - 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, - 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x43, 0x6c, 0x75, - 0x73, 0x74, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4c, 0x0a, 0x0d, - 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x12, 0x1c, 0x2e, - 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x43, 0x6c, 0x75, - 0x73, 0x74, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x61, 0x70, - 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x43, 0x6c, 0x75, 0x73, 0x74, - 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x58, 0x0a, 0x11, 0x52, 0x65, - 0x73, 0x65, 0x74, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x12, - 0x20, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x73, 0x65, 0x74, 0x43, 0x6c, - 0x75, 0x73, 0x74, 0x65, 0x72, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x21, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x73, 0x65, 0x74, - 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x32, 0xd5, 0x02, 0x0a, 0x12, 0x55, 0x74, 0x69, 0x6c, 0x69, 0x7a, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x55, 0x0a, 0x10, 0x44, - 0x61, 0x69, 0x6c, 0x79, 0x55, 0x74, 0x69, 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, - 0x1f, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x61, 0x69, 0x6c, 0x79, 0x55, 0x74, - 0x69, 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x20, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x61, 0x69, 0x6c, 0x79, 0x55, - 0x74, 0x69, 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x12, 0x79, 0x0a, 0x1c, 0x44, 0x61, 0x69, 0x6c, 0x79, 0x55, 0x74, 0x69, 0x6c, 0x69, - 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x54, 0x79, - 0x70, 0x65, 0x12, 0x2b, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x61, 0x69, 0x6c, - 0x79, 0x55, 0x74, 0x69, 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x6e, 0x73, 0x74, - 0x61, 0x6e, 0x63, 0x65, 0x54, 0x79, 0x70, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, - 0x2c, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x61, 0x69, 0x6c, 0x79, 0x55, 0x74, - 0x69, 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, - 0x65, 0x54, 0x79, 0x70, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x6d, 0x0a, - 0x18, 0x44, 0x61, 0x69, 0x6c, 0x79, 0x55, 0x74, 0x69, 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x4e, 0x6f, 0x64, 0x65, 0x54, 0x79, 0x70, 0x65, 0x12, 0x27, 0x2e, 0x61, 0x70, 0x69, 0x2e, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, + 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x54, 0x61, 0x67, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x12, 0x5b, 0x0a, 0x12, 0x47, 0x65, 0x74, 0x4d, 0x69, 0x67, 0x50, 0x61, 0x72, 0x74, 0x65, + 0x64, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x21, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, + 0x2e, 0x47, 0x65, 0x74, 0x4d, 0x69, 0x67, 0x50, 0x61, 0x72, 0x74, 0x65, 0x64, 0x43, 0x6f, 0x6e, + 0x66, 0x69, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x61, 0x70, 0x69, + 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x4d, 0x69, 0x67, 0x50, 0x61, 0x72, 0x74, 0x65, 0x64, + 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x32, 0xdc, + 0x02, 0x0a, 0x16, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x4d, 0x75, 0x74, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x4c, 0x0a, 0x0d, 0x43, 0x72, 0x65, + 0x61, 0x74, 0x65, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x12, 0x1c, 0x2e, 0x61, 0x70, 0x69, + 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, + 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, + 0x31, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4c, 0x0a, 0x0d, 0x44, 0x65, 0x6c, 0x65, 0x74, + 0x65, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x12, 0x1c, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, + 0x31, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, + 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4c, 0x0a, 0x0d, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x43, + 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x12, 0x1c, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, + 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x70, + 0x64, 0x61, 0x74, 0x65, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x12, 0x58, 0x0a, 0x11, 0x52, 0x65, 0x73, 0x65, 0x74, 0x43, 0x6c, 0x75, 0x73, + 0x74, 0x65, 0x72, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x20, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, + 0x31, 0x2e, 0x52, 0x65, 0x73, 0x65, 0x74, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x54, 0x6f, + 0x6b, 0x65, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x61, 0x70, 0x69, + 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x73, 0x65, 0x74, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, + 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x32, 0xd5, 0x02, + 0x0a, 0x12, 0x55, 0x74, 0x69, 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x65, 0x72, + 0x76, 0x69, 0x63, 0x65, 0x12, 0x55, 0x0a, 0x10, 0x44, 0x61, 0x69, 0x6c, 0x79, 0x55, 0x74, 0x69, + 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1f, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, + 0x31, 0x2e, 0x44, 0x61, 0x69, 0x6c, 0x79, 0x55, 0x74, 0x69, 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x61, 0x69, 0x6c, 0x79, 0x55, 0x74, 0x69, 0x6c, 0x69, 0x7a, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x4e, 0x6f, 0x64, 0x65, 0x54, 0x79, 0x70, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x28, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x61, 0x69, 0x6c, + 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x79, 0x0a, 0x1c, 0x44, + 0x61, 0x69, 0x6c, 0x79, 0x55, 0x74, 0x69, 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, + 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x54, 0x79, 0x70, 0x65, 0x12, 0x2b, 0x2e, 0x61, 0x70, + 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x61, 0x69, 0x6c, 0x79, 0x55, 0x74, 0x69, 0x6c, 0x69, 0x7a, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x54, 0x79, 0x70, + 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2c, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, + 0x31, 0x2e, 0x44, 0x61, 0x69, 0x6c, 0x79, 0x55, 0x74, 0x69, 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x54, 0x79, 0x70, 0x65, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x6d, 0x0a, 0x18, 0x44, 0x61, 0x69, 0x6c, 0x79, 0x55, + 0x74, 0x69, 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4e, 0x6f, 0x64, 0x65, 0x54, 0x79, + 0x70, 0x65, 0x12, 0x27, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x61, 0x69, 0x6c, 0x79, 0x55, 0x74, 0x69, 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4e, 0x6f, 0x64, 0x65, - 0x54, 0x79, 0x70, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x81, 0x01, 0x0a, - 0x0a, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x42, 0x08, 0x4b, 0x38, 0x73, - 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x30, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, - 0x63, 0x6f, 0x6d, 0x2f, 0x64, 0x65, 0x76, 0x7a, 0x65, 0x72, 0x6f, 0x2d, 0x69, 0x6e, 0x63, 0x2f, - 0x7a, 0x78, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x72, 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x61, 0x70, 0x69, - 0x2f, 0x76, 0x31, 0x3b, 0x61, 0x70, 0x69, 0x76, 0x31, 0xa2, 0x02, 0x03, 0x41, 0x58, 0x58, 0xaa, - 0x02, 0x06, 0x41, 0x70, 0x69, 0x2e, 0x56, 0x31, 0xca, 0x02, 0x06, 0x41, 0x70, 0x69, 0x5c, 0x56, - 0x31, 0xe2, 0x02, 0x12, 0x41, 0x70, 0x69, 0x5c, 0x56, 0x31, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, - 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x07, 0x41, 0x70, 0x69, 0x3a, 0x3a, 0x56, 0x31, - 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x54, 0x79, 0x70, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x28, 0x2e, 0x61, 0x70, + 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x61, 0x69, 0x6c, 0x79, 0x55, 0x74, 0x69, 0x6c, 0x69, 0x7a, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4e, 0x6f, 0x64, 0x65, 0x54, 0x79, 0x70, 0x65, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x86, 0x01, 0x0a, 0x0a, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, + 0x69, 0x2e, 0x76, 0x31, 0x42, 0x08, 0x4b, 0x38, 0x73, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, + 0x5a, 0x35, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x64, 0x65, 0x76, + 0x7a, 0x65, 0x72, 0x6f, 0x2d, 0x69, 0x6e, 0x63, 0x2f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, + 0x73, 0x2f, 0x64, 0x61, 0x6b, 0x72, 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, + 0x31, 0x3b, 0x61, 0x70, 0x69, 0x76, 0x31, 0xa2, 0x02, 0x03, 0x41, 0x58, 0x58, 0xaa, 0x02, 0x06, + 0x41, 0x70, 0x69, 0x2e, 0x56, 0x31, 0xca, 0x02, 0x06, 0x41, 0x70, 0x69, 0x5c, 0x56, 0x31, 0xe2, + 0x02, 0x12, 0x41, 0x70, 0x69, 0x5c, 0x56, 0x31, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, + 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x07, 0x41, 0x70, 0x69, 0x3a, 0x3a, 0x56, 0x31, 0x62, 0x06, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -13256,561 +16766,673 @@ func file_api_v1_k8s_proto_rawDescGZIP() []byte { return file_api_v1_k8s_proto_rawDescData } -var file_api_v1_k8s_proto_enumTypes = make([]protoimpl.EnumInfo, 3) -var file_api_v1_k8s_proto_msgTypes = make([]protoimpl.MessageInfo, 164) +var file_api_v1_k8s_proto_enumTypes = make([]protoimpl.EnumInfo, 7) +var file_api_v1_k8s_proto_msgTypes = make([]protoimpl.MessageInfo, 199) var file_api_v1_k8s_proto_goTypes = []interface{}{ - (GalaxyPOVNodeGroupBy)(0), // 0: api.v1.GalaxyPOVNodeGroupBy - (GalaxyPOVWorkloadGroupBy)(0), // 1: api.v1.GalaxyPOVWorkloadGroupBy - (ClusterType)(0), // 2: api.v1.ClusterType - (*GetNodeRequest)(nil), // 3: api.v1.GetNodeRequest - (*GetWorkloadPodHistoryRequest)(nil), // 4: api.v1.GetWorkloadPodHistoryRequest - (*GetClusterMetadataRequest)(nil), // 5: api.v1.GetClusterMetadataRequest - (*GetNodeGroupRequest)(nil), // 6: api.v1.GetNodeGroupRequest - (*GetWorkloadsRequest)(nil), // 7: api.v1.GetWorkloadsRequest - (*ListWorkloadsPaginatedRequest)(nil), // 8: api.v1.ListWorkloadsPaginatedRequest - (*GetWorkloadsSummaryRequest)(nil), // 9: api.v1.GetWorkloadsSummaryRequest - (*WorkloadMetricEntry)(nil), // 10: api.v1.WorkloadMetricEntry - (*WorkloadMetricBreakdown)(nil), // 11: api.v1.WorkloadMetricBreakdown - (*GetWorkloadsSummaryResponse)(nil), // 12: api.v1.GetWorkloadsSummaryResponse - (*ListWorkloadsPaginatedResponse)(nil), // 13: api.v1.ListWorkloadsPaginatedResponse - (*GetResourcesRequest)(nil), // 14: api.v1.GetResourcesRequest - (*GetPodsRequest)(nil), // 15: api.v1.GetPodsRequest - (*GetPodsResponse)(nil), // 16: api.v1.GetPodsResponse - (*GetWorkloadRequest)(nil), // 17: api.v1.GetWorkloadRequest - (*GetClustersRequest)(nil), // 18: api.v1.GetClustersRequest - (*ListClustersRequest)(nil), // 19: api.v1.ListClustersRequest - (*GetClusterRequest)(nil), // 20: api.v1.GetClusterRequest - (*DeleteClusterRequest)(nil), // 21: api.v1.DeleteClusterRequest - (*DeleteClusterResponse)(nil), // 22: api.v1.DeleteClusterResponse - (*UpdateClusterRequest)(nil), // 23: api.v1.UpdateClusterRequest - (*UpdateClusterResponse)(nil), // 24: api.v1.UpdateClusterResponse - (*GetResourcesResponse)(nil), // 25: api.v1.GetResourcesResponse - (*GetResourceFilterOptionsRequest)(nil), // 26: api.v1.GetResourceFilterOptionsRequest - (*GetResourceFilterOptionsResponse)(nil), // 27: api.v1.GetResourceFilterOptionsResponse - (*CreateClusterRequest)(nil), // 28: api.v1.CreateClusterRequest - (*CreateClusterResponse)(nil), // 29: api.v1.CreateClusterResponse - (*ResetClusterTokenRequest)(nil), // 30: api.v1.ResetClusterTokenRequest - (*ResetClusterTokenResponse)(nil), // 31: api.v1.ResetClusterTokenResponse - (*GetNodeGroupsRequest)(nil), // 32: api.v1.GetNodeGroupsRequest - (*GetAllNodeGroupsRequest)(nil), // 33: api.v1.GetAllNodeGroupsRequest - (*GetNodeGroupsResponse)(nil), // 34: api.v1.GetNodeGroupsResponse - (*GetAllNodeGroupsResponse)(nil), // 35: api.v1.GetAllNodeGroupsResponse - (*NodeGroupSet)(nil), // 36: api.v1.NodeGroupSet - (*GetNodeGroupsUtilizationRequest)(nil), // 37: api.v1.GetNodeGroupsUtilizationRequest - (*GetNodeGroupsUtilizationResponse)(nil), // 38: api.v1.GetNodeGroupsUtilizationResponse - (*NodeGroupMetric)(nil), // 39: api.v1.NodeGroupMetric - (*GetNodeGroupResponse)(nil), // 40: api.v1.GetNodeGroupResponse - (*GetClusterResponse)(nil), // 41: api.v1.GetClusterResponse - (*GetClustersResponse)(nil), // 42: api.v1.GetClustersResponse - (*ListClustersResponse)(nil), // 43: api.v1.ListClustersResponse - (*GetNodeResponse)(nil), // 44: api.v1.GetNodeResponse - (*GetWorkloadsResponse)(nil), // 45: api.v1.GetWorkloadsResponse - (*GetWorkloadResponse)(nil), // 46: api.v1.GetWorkloadResponse - (*GetWorkloadContainerPercentilesRequest)(nil), // 47: api.v1.GetWorkloadContainerPercentilesRequest - (*GetWorkloadContainerPercentilesResponse)(nil), // 48: api.v1.GetWorkloadContainerPercentilesResponse - (*GetWorkloadContainerTimeSeriesRequest)(nil), // 49: api.v1.GetWorkloadContainerTimeSeriesRequest - (*GetWorkloadContainerTimeSeriesResponse)(nil), // 50: api.v1.GetWorkloadContainerTimeSeriesResponse - (*GetLatestContainerRequestLimitsRequest)(nil), // 51: api.v1.GetLatestContainerRequestLimitsRequest - (*GetLatestContainerRequestLimitsResponse)(nil), // 52: api.v1.GetLatestContainerRequestLimitsResponse - (*GetNodeGPUMigInstancesRequest)(nil), // 53: api.v1.GetNodeGPUMigInstancesRequest - (*GetNodeGPUMigInstancesResponse)(nil), // 54: api.v1.GetNodeGPUMigInstancesResponse - (*GetContainerIndividualGPUMetricsRequest)(nil), // 55: api.v1.GetContainerIndividualGPUMetricsRequest - (*GetContainerIndividualGPUMetricsResponse)(nil), // 56: api.v1.GetContainerIndividualGPUMetricsResponse - (*LanguageVersions)(nil), // 57: api.v1.LanguageVersions - (*GetWorkloadLanguagesRequest)(nil), // 58: api.v1.GetWorkloadLanguagesRequest - (*GetWorkloadLanguagesResponse)(nil), // 59: api.v1.GetWorkloadLanguagesResponse - (*GetForecastWorkloadsRequest)(nil), // 60: api.v1.GetForecastWorkloadsRequest - (*GetForecastWorkloadsResponse)(nil), // 61: api.v1.GetForecastWorkloadsResponse - (*GetForecastWorkloadRequest)(nil), // 62: api.v1.GetForecastWorkloadRequest - (*GetForecastWorkloadResponse)(nil), // 63: api.v1.GetForecastWorkloadResponse - (*GetClusterMetadataResponse)(nil), // 64: api.v1.GetClusterMetadataResponse - (*ClusterElement)(nil), // 65: api.v1.ClusterElement - (*GetAllNamespacesRequest)(nil), // 66: api.v1.GetAllNamespacesRequest - (*GetAllNamespacesResponse)(nil), // 67: api.v1.GetAllNamespacesResponse - (*SearchNamespacesByClusterRequest)(nil), // 68: api.v1.SearchNamespacesByClusterRequest - (*SearchNamespacesByClusterResponse)(nil), // 69: api.v1.SearchNamespacesByClusterResponse - (*ListNamespacesByClusterRequest)(nil), // 70: api.v1.ListNamespacesByClusterRequest - (*ListNamespacesByClusterResponse)(nil), // 71: api.v1.ListNamespacesByClusterResponse - (*NamespaceItem)(nil), // 72: api.v1.NamespaceItem - (*GetAllWorkloadNamesRequest)(nil), // 73: api.v1.GetAllWorkloadNamesRequest - (*GetAllWorkloadNamesResponse)(nil), // 74: api.v1.GetAllWorkloadNamesResponse - (*GetAllWorkloadLabelsRequest)(nil), // 75: api.v1.GetAllWorkloadLabelsRequest - (*GetAllWorkloadLabelsResponse)(nil), // 76: api.v1.GetAllWorkloadLabelsResponse - (*GetAllNodeGroupNamesRequest)(nil), // 77: api.v1.GetAllNodeGroupNamesRequest - (*GetAllNodeGroupNamesResponse)(nil), // 78: api.v1.GetAllNodeGroupNamesResponse - (*Cluster)(nil), // 79: api.v1.Cluster - (*OperatorInfo)(nil), // 80: api.v1.OperatorInfo - (*Container)(nil), // 81: api.v1.Container - (*GetLatestOperatorVersionRequest)(nil), // 82: api.v1.GetLatestOperatorVersionRequest - (*GetLatestOperatorVersionResponse)(nil), // 83: api.v1.GetLatestOperatorVersionResponse - (*GalaxyGetWorkloadPerspectiveRequest)(nil), // 84: api.v1.GalaxyGetWorkloadPerspectiveRequest - (*GalaxyGetWorkloadPerspectiveResponse)(nil), // 85: api.v1.GalaxyGetWorkloadPerspectiveResponse - (*GalaxyWorkloadGroup)(nil), // 86: api.v1.GalaxyWorkloadGroup - (*PerspectiveDatapointOpts)(nil), // 87: api.v1.PerspectiveDatapointOpts - (*ListAuditLogsRequest)(nil), // 88: api.v1.ListAuditLogsRequest - (*ListAuditLogsResponse)(nil), // 89: api.v1.ListAuditLogsResponse - (*ListAuditLogOriginatorsRequest)(nil), // 90: api.v1.ListAuditLogOriginatorsRequest - (*ListAuditLogOriginatorsResponse)(nil), // 91: api.v1.ListAuditLogOriginatorsResponse - (*SendWorkloadEmailRequest)(nil), // 92: api.v1.SendWorkloadEmailRequest - (*SendWorkloadEmailResponse)(nil), // 93: api.v1.SendWorkloadEmailResponse - (*SendWeeklySummaryEmailRequest)(nil), // 94: api.v1.SendWeeklySummaryEmailRequest - (*SendWeeklySummaryEmailRequestData)(nil), // 95: api.v1.SendWeeklySummaryEmailRequestData - (*SendWeeklySummaryEmailResponse)(nil), // 96: api.v1.SendWeeklySummaryEmailResponse - (*GetClustersNodeInfoRequest)(nil), // 97: api.v1.GetClustersNodeInfoRequest - (*GetClustersNodeInfoResponse)(nil), // 98: api.v1.GetClustersNodeInfoResponse - (*SearchK8SResourcesRequest)(nil), // 99: api.v1.SearchK8sResourcesRequest - (*SearchK8SResourcesResponse)(nil), // 100: api.v1.SearchK8sResourcesResponse - (*K8SSearchResult)(nil), // 101: api.v1.K8sSearchResult - (*SearchK8SWorkloadsRequest)(nil), // 102: api.v1.SearchK8sWorkloadsRequest - (*SearchK8SWorkloadsResponse)(nil), // 103: api.v1.SearchK8sWorkloadsResponse - (*K8SWorkloadSearchResult)(nil), // 104: api.v1.K8sWorkloadSearchResult - (*MetadataForWorkloadsRequest)(nil), // 105: api.v1.MetadataForWorkloadsRequest - (*MetadataForWorkloadsResponse)(nil), // 106: api.v1.MetadataForWorkloadsResponse - (*AddClusterTagsRequest)(nil), // 107: api.v1.AddClusterTagsRequest - (*AddClusterTagsResponse)(nil), // 108: api.v1.AddClusterTagsResponse - (*RemoveClusterTagsRequest)(nil), // 109: api.v1.RemoveClusterTagsRequest - (*RemoveClusterTagsResponse)(nil), // 110: api.v1.RemoveClusterTagsResponse - (*ListTagsRequest)(nil), // 111: api.v1.ListTagsRequest - (*TagSummary)(nil), // 112: api.v1.TagSummary - (*ListTagsResponse)(nil), // 113: api.v1.ListTagsResponse - (*WorkloadMetadata)(nil), // 114: api.v1.WorkloadMetadata - (*PodMetadata)(nil), // 115: api.v1.PodMetadata - (*NodeMetadata)(nil), // 116: api.v1.NodeMetadata - (*GetRelatedResourcesRequest)(nil), // 117: api.v1.GetRelatedResourcesRequest - (*GetWorkloadPodHistoryResponse)(nil), // 118: api.v1.GetWorkloadPodHistoryResponse - (*JobHistory)(nil), // 119: api.v1.JobHistory - (*ReplicaSetHistory)(nil), // 120: api.v1.ReplicaSetHistory - (*PodHistory)(nil), // 121: api.v1.PodHistory - (*GetRelatedResourcesResponse)(nil), // 122: api.v1.GetRelatedResourcesResponse - (*K8SRelatedResource)(nil), // 123: api.v1.K8sRelatedResource - (*K8SResourceNode)(nil), // 124: api.v1.K8sResourceNode - (*K8SResourceEdge)(nil), // 125: api.v1.K8sResourceEdge - (*GetClusterTypeRequest)(nil), // 126: api.v1.GetClusterTypeRequest - (*GetClusterTypeResponse)(nil), // 127: api.v1.GetClusterTypeResponse - (*GetMigPartedConfigRequest)(nil), // 128: api.v1.GetMigPartedConfigRequest - (*GetMigPartedConfigResponse)(nil), // 129: api.v1.GetMigPartedConfigResponse - (*GetWorkloadsStatsRequest)(nil), // 130: api.v1.GetWorkloadsStatsRequest - (*GetWorkloadsStatsResponse)(nil), // 131: api.v1.GetWorkloadsStatsResponse - (*DailyUtilizationRequest)(nil), // 132: api.v1.DailyUtilizationRequest - (*DailyUtilizationResponse)(nil), // 133: api.v1.DailyUtilizationResponse - (*DailyUtilizationInstanceTypeRequest)(nil), // 134: api.v1.DailyUtilizationInstanceTypeRequest - (*DailyUtilizationInstanceTypeResponse)(nil), // 135: api.v1.DailyUtilizationInstanceTypeResponse - (*DailyUtilizationNodeTypeRequest)(nil), // 136: api.v1.DailyUtilizationNodeTypeRequest - (*DailyUtilizationNodeTypeResponse)(nil), // 137: api.v1.DailyUtilizationNodeTypeResponse - (*LookupNodeInstanceRequest)(nil), // 138: api.v1.LookupNodeInstanceRequest - (*InstanceLookupParams)(nil), // 139: api.v1.InstanceLookupParams - (*LookupNodeInstanceResponse)(nil), // 140: api.v1.LookupNodeInstanceResponse - nil, // 141: api.v1.GetAllNodeGroupsResponse.NodeGroupMapEntry - (*GetNodeGroupsUtilizationResponse_ListNodeGroupMetrics)(nil), // 142: api.v1.GetNodeGroupsUtilizationResponse.ListNodeGroupMetrics - nil, // 143: api.v1.GetNodeGroupsUtilizationResponse.NodeGroupToUtilMetricsEntry - nil, // 144: api.v1.GetNodeGroupsUtilizationResponse.InstanceTypeToUtilMetricsEntry - nil, // 145: api.v1.GetNodeGroupsUtilizationResponse.ReservationTypeToUtilMetricsEntry - nil, // 146: api.v1.GetNodeGroupsUtilizationResponse.LogicalAzToUtilMetricsEntry - nil, // 147: api.v1.GalaxyGetWorkloadPerspectiveResponse.ClustersEntry - nil, // 148: api.v1.GalaxyGetWorkloadPerspectiveResponse.WorkloadsEntry - nil, // 149: api.v1.MetadataForWorkloadsRequest.WorkloadUidToKindEntry - nil, // 150: api.v1.MetadataForWorkloadsResponse.WorkloadUidToMetadataEntry - nil, // 151: api.v1.WorkloadMetadata.PodUidToNodeMetadataEntry - nil, // 152: api.v1.WorkloadMetadata.PodUidToPodMetadataEntry - (*DailyUtilizationResponse_Datapoints)(nil), // 153: api.v1.DailyUtilizationResponse.Datapoints - (*DailyUtilizationResponse_Datapoint)(nil), // 154: api.v1.DailyUtilizationResponse.Datapoint - nil, // 155: api.v1.DailyUtilizationResponse.ClusterIdToDailyTotalCoreMinutesEntry - nil, // 156: api.v1.DailyUtilizationResponse.ClusterIdToMetaEntry - (*DailyUtilizationInstanceTypeResponse_Datapoints)(nil), // 157: api.v1.DailyUtilizationInstanceTypeResponse.Datapoints - (*DailyUtilizationInstanceTypeResponse_Datapoint)(nil), // 158: api.v1.DailyUtilizationInstanceTypeResponse.Datapoint - nil, // 159: api.v1.DailyUtilizationInstanceTypeResponse.ClusterIdToDatapointsEntry - nil, // 160: api.v1.DailyUtilizationInstanceTypeResponse.ClusterIdToMetaEntry - nil, // 161: api.v1.DailyUtilizationInstanceTypeResponse.Datapoint.InstanceTypeToDailyTotalCoreMinutesEntry - (*DailyUtilizationNodeTypeResponse_Datapoints)(nil), // 162: api.v1.DailyUtilizationNodeTypeResponse.Datapoints - (*DailyUtilizationNodeTypeResponse_Datapoint)(nil), // 163: api.v1.DailyUtilizationNodeTypeResponse.Datapoint - nil, // 164: api.v1.DailyUtilizationNodeTypeResponse.ClusterIdToDatapointsEntry - nil, // 165: api.v1.DailyUtilizationNodeTypeResponse.ClusterIdToMetaEntry - nil, // 166: api.v1.DailyUtilizationNodeTypeResponse.Datapoint.NodeTypeToDailyTotalCoreMinutesEntry - (*timestamppb.Timestamp)(nil), // 167: google.protobuf.Timestamp - (*WorkloadFilters)(nil), // 168: api.v1.WorkloadFilters - (*WorkloadItem)(nil), // 169: api.v1.WorkloadItem - (K8SObjectKind)(0), // 170: api.v1.K8sObjectKind - (*Pagination)(nil), // 171: api.v1.Pagination - (*ResourceFieldFilter)(nil), // 172: api.v1.ResourceFieldFilter - (*ResourceFilterFieldOptions)(nil), // 173: api.v1.ResourceFilterFieldOptions - (*NodeGroup)(nil), // 174: api.v1.NodeGroup - (*money.Money)(nil), // 175: google.type.Money - (*ResourceMetrics)(nil), // 176: api.v1.ResourceMetrics - (*CostInfo)(nil), // 177: api.v1.CostInfo - (*NodeInfo)(nil), // 178: api.v1.NodeInfo - (*CostDataPoint)(nil), // 179: api.v1.CostDataPoint - (*ResourceDataPoint)(nil), // 180: api.v1.ResourceDataPoint - (*Node)(nil), // 181: api.v1.Node - (*ResourceSummary)(nil), // 182: api.v1.ResourceSummary - (*ContainerPercentileSummary)(nil), // 183: api.v1.ContainerPercentileSummary - (*ContainerTimeSeries)(nil), // 184: api.v1.ContainerTimeSeries - (*ContainerRequestLimits)(nil), // 185: api.v1.ContainerRequestLimits - (*GPUMigInstance)(nil), // 186: api.v1.GPUMigInstance - (*ContainerIndividualGPUMetrics)(nil), // 187: api.v1.ContainerIndividualGPUMetrics - (*ForecastResourceMetrics)(nil), // 188: api.v1.ForecastResourceMetrics - (*AuditLogEntry)(nil), // 189: api.v1.AuditLogEntry - (*Instance)(nil), // 190: api.v1.Instance + (NodeInventoryScope)(0), // 0: api.v1.NodeInventoryScope + (NodeState)(0), // 1: api.v1.NodeState + (NodeClaimNotDeletableReason)(0), // 2: api.v1.NodeClaimNotDeletableReason + (NodeClaimDeletionState)(0), // 3: api.v1.NodeClaimDeletionState + (GalaxyPOVNodeGroupBy)(0), // 4: api.v1.GalaxyPOVNodeGroupBy + (GalaxyPOVWorkloadGroupBy)(0), // 5: api.v1.GalaxyPOVWorkloadGroupBy + (ClusterType)(0), // 6: api.v1.ClusterType + (*GetNodeRequest)(nil), // 7: api.v1.GetNodeRequest + (*GetWorkloadPodHistoryRequest)(nil), // 8: api.v1.GetWorkloadPodHistoryRequest + (*GetClusterMetadataRequest)(nil), // 9: api.v1.GetClusterMetadataRequest + (*GetNodeGroupRequest)(nil), // 10: api.v1.GetNodeGroupRequest + (*GetWorkloadsRequest)(nil), // 11: api.v1.GetWorkloadsRequest + (*ListWorkloadsPaginatedRequest)(nil), // 12: api.v1.ListWorkloadsPaginatedRequest + (*GetWorkloadsSummaryRequest)(nil), // 13: api.v1.GetWorkloadsSummaryRequest + (*WorkloadMetricEntry)(nil), // 14: api.v1.WorkloadMetricEntry + (*WorkloadMetricBreakdown)(nil), // 15: api.v1.WorkloadMetricBreakdown + (*GetWorkloadsSummaryResponse)(nil), // 16: api.v1.GetWorkloadsSummaryResponse + (*GetWorkloadRequestTrendsRequest)(nil), // 17: api.v1.GetWorkloadRequestTrendsRequest + (*RequestTrendPoint)(nil), // 18: api.v1.RequestTrendPoint + (*RequestTrendSeries)(nil), // 19: api.v1.RequestTrendSeries + (*GetWorkloadRequestTrendsResponse)(nil), // 20: api.v1.GetWorkloadRequestTrendsResponse + (*ListWorkloadsPaginatedResponse)(nil), // 21: api.v1.ListWorkloadsPaginatedResponse + (*GetResourcesRequest)(nil), // 22: api.v1.GetResourcesRequest + (*GetPodsRequest)(nil), // 23: api.v1.GetPodsRequest + (*GetPodsResponse)(nil), // 24: api.v1.GetPodsResponse + (*GetWorkloadRequest)(nil), // 25: api.v1.GetWorkloadRequest + (*GetClustersRequest)(nil), // 26: api.v1.GetClustersRequest + (*ListClustersRequest)(nil), // 27: api.v1.ListClustersRequest + (*GetClusterRequest)(nil), // 28: api.v1.GetClusterRequest + (*DeleteClusterRequest)(nil), // 29: api.v1.DeleteClusterRequest + (*DeleteClusterResponse)(nil), // 30: api.v1.DeleteClusterResponse + (*UpdateClusterRequest)(nil), // 31: api.v1.UpdateClusterRequest + (*UpdateClusterResponse)(nil), // 32: api.v1.UpdateClusterResponse + (*GetResourcesResponse)(nil), // 33: api.v1.GetResourcesResponse + (*GetResourceFilterOptionsRequest)(nil), // 34: api.v1.GetResourceFilterOptionsRequest + (*GetResourceFilterOptionsResponse)(nil), // 35: api.v1.GetResourceFilterOptionsResponse + (*CreateClusterRequest)(nil), // 36: api.v1.CreateClusterRequest + (*CreateClusterResponse)(nil), // 37: api.v1.CreateClusterResponse + (*ResetClusterTokenRequest)(nil), // 38: api.v1.ResetClusterTokenRequest + (*ResetClusterTokenResponse)(nil), // 39: api.v1.ResetClusterTokenResponse + (*GetNodeGroupsRequest)(nil), // 40: api.v1.GetNodeGroupsRequest + (*GetAllNodeGroupsRequest)(nil), // 41: api.v1.GetAllNodeGroupsRequest + (*GetNodeGroupsResponse)(nil), // 42: api.v1.GetNodeGroupsResponse + (*GetAllNodeGroupsResponse)(nil), // 43: api.v1.GetAllNodeGroupsResponse + (*NodeGroupSet)(nil), // 44: api.v1.NodeGroupSet + (*GetClusterNodeFilterIndexRequest)(nil), // 45: api.v1.GetClusterNodeFilterIndexRequest + (*NodeLabelSet)(nil), // 46: api.v1.NodeLabelSet + (*NodeLabelRef)(nil), // 47: api.v1.NodeLabelRef + (*NodePoolFields)(nil), // 48: api.v1.NodePoolFields + (*GetClusterNodeFilterIndexResponse)(nil), // 49: api.v1.GetClusterNodeFilterIndexResponse + (*GetNodeGroupsUtilizationRequest)(nil), // 50: api.v1.GetNodeGroupsUtilizationRequest + (*GetNodeGroupsUtilizationResponse)(nil), // 51: api.v1.GetNodeGroupsUtilizationResponse + (*GetClusterNodeInventoryRequest)(nil), // 52: api.v1.GetClusterNodeInventoryRequest + (*GetClusterNodeInventoryResponse)(nil), // 53: api.v1.GetClusterNodeInventoryResponse + (*NodeInventoryRollup)(nil), // 54: api.v1.NodeInventoryRollup + (*NodeStateCounts)(nil), // 55: api.v1.NodeStateCounts + (*NodePoolInventory)(nil), // 56: api.v1.NodePoolInventory + (*InstanceTypeCount)(nil), // 57: api.v1.InstanceTypeCount + (*NodeInventoryRow)(nil), // 58: api.v1.NodeInventoryRow + (*NodeGroupMetric)(nil), // 59: api.v1.NodeGroupMetric + (*GetNodeGroupResponse)(nil), // 60: api.v1.GetNodeGroupResponse + (*GetClusterResponse)(nil), // 61: api.v1.GetClusterResponse + (*GetClustersResponse)(nil), // 62: api.v1.GetClustersResponse + (*ListClustersResponse)(nil), // 63: api.v1.ListClustersResponse + (*GetNodeResponse)(nil), // 64: api.v1.GetNodeResponse + (*NodeClaimRef)(nil), // 65: api.v1.NodeClaimRef + (*RequestNodeClaimDeletionRequest)(nil), // 66: api.v1.RequestNodeClaimDeletionRequest + (*RequestNodeClaimDeletionResponse)(nil), // 67: api.v1.RequestNodeClaimDeletionResponse + (*GetNodeClaimDeletionStatusRequest)(nil), // 68: api.v1.GetNodeClaimDeletionStatusRequest + (*GetNodeClaimDeletionStatusResponse)(nil), // 69: api.v1.GetNodeClaimDeletionStatusResponse + (*GetNodeClaimRefsRequest)(nil), // 70: api.v1.GetNodeClaimRefsRequest + (*NodeClaimRefEntry)(nil), // 71: api.v1.NodeClaimRefEntry + (*GetNodeClaimRefsResponse)(nil), // 72: api.v1.GetNodeClaimRefsResponse + (*NodeClaimDeletionItem)(nil), // 73: api.v1.NodeClaimDeletionItem + (*RequestNodeClaimDeletionsRequest)(nil), // 74: api.v1.RequestNodeClaimDeletionsRequest + (*NodeClaimDeletionResult)(nil), // 75: api.v1.NodeClaimDeletionResult + (*RequestNodeClaimDeletionsResponse)(nil), // 76: api.v1.RequestNodeClaimDeletionsResponse + (*NodeClaimDeletionStatus)(nil), // 77: api.v1.NodeClaimDeletionStatus + (*GetWorkloadsResponse)(nil), // 78: api.v1.GetWorkloadsResponse + (*GetWorkloadResponse)(nil), // 79: api.v1.GetWorkloadResponse + (*GetWorkloadContainerPercentilesRequest)(nil), // 80: api.v1.GetWorkloadContainerPercentilesRequest + (*GetWorkloadContainerPercentilesResponse)(nil), // 81: api.v1.GetWorkloadContainerPercentilesResponse + (*GetWorkloadContainerTimeSeriesRequest)(nil), // 82: api.v1.GetWorkloadContainerTimeSeriesRequest + (*GetWorkloadContainerTimeSeriesResponse)(nil), // 83: api.v1.GetWorkloadContainerTimeSeriesResponse + (*GetLatestContainerRequestLimitsRequest)(nil), // 84: api.v1.GetLatestContainerRequestLimitsRequest + (*GetLatestContainerRequestLimitsResponse)(nil), // 85: api.v1.GetLatestContainerRequestLimitsResponse + (*GetNodeGPUMigInstancesRequest)(nil), // 86: api.v1.GetNodeGPUMigInstancesRequest + (*GetNodeGPUMigInstancesResponse)(nil), // 87: api.v1.GetNodeGPUMigInstancesResponse + (*GetNodeGPUMigInstanceTimeseriesRequest)(nil), // 88: api.v1.GetNodeGPUMigInstanceTimeseriesRequest + (*GPUMigInstanceDataPoint)(nil), // 89: api.v1.GPUMigInstanceDataPoint + (*GPUMigInstanceTimeseries)(nil), // 90: api.v1.GPUMigInstanceTimeseries + (*GetNodeGPUMigInstanceTimeseriesResponse)(nil), // 91: api.v1.GetNodeGPUMigInstanceTimeseriesResponse + (*GetContainerIndividualGPUMetricsRequest)(nil), // 92: api.v1.GetContainerIndividualGPUMetricsRequest + (*GetContainerIndividualGPUMetricsResponse)(nil), // 93: api.v1.GetContainerIndividualGPUMetricsResponse + (*LanguageVersions)(nil), // 94: api.v1.LanguageVersions + (*GetWorkloadLanguagesRequest)(nil), // 95: api.v1.GetWorkloadLanguagesRequest + (*GetWorkloadLanguagesResponse)(nil), // 96: api.v1.GetWorkloadLanguagesResponse + (*GetForecastWorkloadsRequest)(nil), // 97: api.v1.GetForecastWorkloadsRequest + (*GetForecastWorkloadsResponse)(nil), // 98: api.v1.GetForecastWorkloadsResponse + (*GetForecastWorkloadRequest)(nil), // 99: api.v1.GetForecastWorkloadRequest + (*GetForecastWorkloadResponse)(nil), // 100: api.v1.GetForecastWorkloadResponse + (*GetClusterMetadataResponse)(nil), // 101: api.v1.GetClusterMetadataResponse + (*ClusterElement)(nil), // 102: api.v1.ClusterElement + (*GetAllNamespacesRequest)(nil), // 103: api.v1.GetAllNamespacesRequest + (*GetAllNamespacesResponse)(nil), // 104: api.v1.GetAllNamespacesResponse + (*SearchNamespacesByClusterRequest)(nil), // 105: api.v1.SearchNamespacesByClusterRequest + (*SearchNamespacesByClusterResponse)(nil), // 106: api.v1.SearchNamespacesByClusterResponse + (*ListNamespacesByClusterRequest)(nil), // 107: api.v1.ListNamespacesByClusterRequest + (*ListNamespacesByClusterResponse)(nil), // 108: api.v1.ListNamespacesByClusterResponse + (*NamespaceItem)(nil), // 109: api.v1.NamespaceItem + (*GetAllWorkloadNamesRequest)(nil), // 110: api.v1.GetAllWorkloadNamesRequest + (*GetAllWorkloadNamesResponse)(nil), // 111: api.v1.GetAllWorkloadNamesResponse + (*GetAllWorkloadLabelsRequest)(nil), // 112: api.v1.GetAllWorkloadLabelsRequest + (*GetAllWorkloadLabelsResponse)(nil), // 113: api.v1.GetAllWorkloadLabelsResponse + (*GetAllNodeGroupNamesRequest)(nil), // 114: api.v1.GetAllNodeGroupNamesRequest + (*GetAllNodeGroupNamesResponse)(nil), // 115: api.v1.GetAllNodeGroupNamesResponse + (*Cluster)(nil), // 116: api.v1.Cluster + (*OperatorInfo)(nil), // 117: api.v1.OperatorInfo + (*Container)(nil), // 118: api.v1.Container + (*GetLatestOperatorVersionRequest)(nil), // 119: api.v1.GetLatestOperatorVersionRequest + (*GetLatestOperatorVersionResponse)(nil), // 120: api.v1.GetLatestOperatorVersionResponse + (*GalaxyGetWorkloadPerspectiveRequest)(nil), // 121: api.v1.GalaxyGetWorkloadPerspectiveRequest + (*GalaxyGetWorkloadPerspectiveResponse)(nil), // 122: api.v1.GalaxyGetWorkloadPerspectiveResponse + (*GalaxyWorkloadGroup)(nil), // 123: api.v1.GalaxyWorkloadGroup + (*PerspectiveDatapointOpts)(nil), // 124: api.v1.PerspectiveDatapointOpts + (*ListAuditLogsRequest)(nil), // 125: api.v1.ListAuditLogsRequest + (*ListAuditLogsResponse)(nil), // 126: api.v1.ListAuditLogsResponse + (*ListAuditLogOriginatorsRequest)(nil), // 127: api.v1.ListAuditLogOriginatorsRequest + (*ListAuditLogOriginatorsResponse)(nil), // 128: api.v1.ListAuditLogOriginatorsResponse + (*SendWorkloadEmailRequest)(nil), // 129: api.v1.SendWorkloadEmailRequest + (*SendWorkloadEmailResponse)(nil), // 130: api.v1.SendWorkloadEmailResponse + (*SendWeeklySummaryEmailRequest)(nil), // 131: api.v1.SendWeeklySummaryEmailRequest + (*SendWeeklySummaryEmailRequestData)(nil), // 132: api.v1.SendWeeklySummaryEmailRequestData + (*SendWeeklySummaryEmailResponse)(nil), // 133: api.v1.SendWeeklySummaryEmailResponse + (*GetClustersNodeInfoRequest)(nil), // 134: api.v1.GetClustersNodeInfoRequest + (*GetClustersNodeInfoResponse)(nil), // 135: api.v1.GetClustersNodeInfoResponse + (*SearchK8SResourcesRequest)(nil), // 136: api.v1.SearchK8sResourcesRequest + (*SearchK8SResourcesResponse)(nil), // 137: api.v1.SearchK8sResourcesResponse + (*K8SSearchResult)(nil), // 138: api.v1.K8sSearchResult + (*SearchK8SWorkloadsRequest)(nil), // 139: api.v1.SearchK8sWorkloadsRequest + (*SearchK8SWorkloadsResponse)(nil), // 140: api.v1.SearchK8sWorkloadsResponse + (*K8SWorkloadSearchResult)(nil), // 141: api.v1.K8sWorkloadSearchResult + (*MetadataForWorkloadsRequest)(nil), // 142: api.v1.MetadataForWorkloadsRequest + (*MetadataForWorkloadsResponse)(nil), // 143: api.v1.MetadataForWorkloadsResponse + (*AddClusterTagsRequest)(nil), // 144: api.v1.AddClusterTagsRequest + (*AddClusterTagsResponse)(nil), // 145: api.v1.AddClusterTagsResponse + (*RemoveClusterTagsRequest)(nil), // 146: api.v1.RemoveClusterTagsRequest + (*RemoveClusterTagsResponse)(nil), // 147: api.v1.RemoveClusterTagsResponse + (*ListTagsRequest)(nil), // 148: api.v1.ListTagsRequest + (*TagSummary)(nil), // 149: api.v1.TagSummary + (*ListTagsResponse)(nil), // 150: api.v1.ListTagsResponse + (*WorkloadMetadata)(nil), // 151: api.v1.WorkloadMetadata + (*PodMetadata)(nil), // 152: api.v1.PodMetadata + (*NodeMetadata)(nil), // 153: api.v1.NodeMetadata + (*GetRelatedResourcesRequest)(nil), // 154: api.v1.GetRelatedResourcesRequest + (*GetWorkloadPodHistoryResponse)(nil), // 155: api.v1.GetWorkloadPodHistoryResponse + (*JobHistory)(nil), // 156: api.v1.JobHistory + (*ReplicaSetHistory)(nil), // 157: api.v1.ReplicaSetHistory + (*PodHistory)(nil), // 158: api.v1.PodHistory + (*GetRelatedResourcesResponse)(nil), // 159: api.v1.GetRelatedResourcesResponse + (*K8SRelatedResource)(nil), // 160: api.v1.K8sRelatedResource + (*K8SResourceNode)(nil), // 161: api.v1.K8sResourceNode + (*K8SResourceEdge)(nil), // 162: api.v1.K8sResourceEdge + (*GetClusterTypeRequest)(nil), // 163: api.v1.GetClusterTypeRequest + (*GetClusterTypeResponse)(nil), // 164: api.v1.GetClusterTypeResponse + (*GetMigPartedConfigRequest)(nil), // 165: api.v1.GetMigPartedConfigRequest + (*GetMigPartedConfigResponse)(nil), // 166: api.v1.GetMigPartedConfigResponse + (*GetWorkloadsStatsRequest)(nil), // 167: api.v1.GetWorkloadsStatsRequest + (*GetWorkloadsStatsResponse)(nil), // 168: api.v1.GetWorkloadsStatsResponse + (*DailyUtilizationRequest)(nil), // 169: api.v1.DailyUtilizationRequest + (*DailyUtilizationResponse)(nil), // 170: api.v1.DailyUtilizationResponse + (*DailyUtilizationInstanceTypeRequest)(nil), // 171: api.v1.DailyUtilizationInstanceTypeRequest + (*DailyUtilizationInstanceTypeResponse)(nil), // 172: api.v1.DailyUtilizationInstanceTypeResponse + (*DailyUtilizationNodeTypeRequest)(nil), // 173: api.v1.DailyUtilizationNodeTypeRequest + (*DailyUtilizationNodeTypeResponse)(nil), // 174: api.v1.DailyUtilizationNodeTypeResponse + (*LookupNodeInstanceRequest)(nil), // 175: api.v1.LookupNodeInstanceRequest + (*InstanceLookupParams)(nil), // 176: api.v1.InstanceLookupParams + (*LookupNodeInstanceResponse)(nil), // 177: api.v1.LookupNodeInstanceResponse + nil, // 178: api.v1.GetAllNodeGroupsResponse.NodeGroupMapEntry + nil, // 179: api.v1.NodeLabelSet.LabelsEntry + nil, // 180: api.v1.NodePoolFields.FieldsEntry + (*GetNodeGroupsUtilizationResponse_ListNodeGroupMetrics)(nil), // 181: api.v1.GetNodeGroupsUtilizationResponse.ListNodeGroupMetrics + nil, // 182: api.v1.GetNodeGroupsUtilizationResponse.NodeGroupToUtilMetricsEntry + nil, // 183: api.v1.GetNodeGroupsUtilizationResponse.InstanceTypeToUtilMetricsEntry + nil, // 184: api.v1.GetNodeGroupsUtilizationResponse.ReservationTypeToUtilMetricsEntry + nil, // 185: api.v1.GetNodeGroupsUtilizationResponse.LogicalAzToUtilMetricsEntry + nil, // 186: api.v1.GalaxyGetWorkloadPerspectiveResponse.ClustersEntry + nil, // 187: api.v1.GalaxyGetWorkloadPerspectiveResponse.WorkloadsEntry + nil, // 188: api.v1.MetadataForWorkloadsRequest.WorkloadUidToKindEntry + nil, // 189: api.v1.MetadataForWorkloadsResponse.WorkloadUidToMetadataEntry + nil, // 190: api.v1.WorkloadMetadata.PodUidToNodeMetadataEntry + nil, // 191: api.v1.WorkloadMetadata.PodUidToPodMetadataEntry + (*DailyUtilizationResponse_Datapoints)(nil), // 192: api.v1.DailyUtilizationResponse.Datapoints + (*DailyUtilizationResponse_Datapoint)(nil), // 193: api.v1.DailyUtilizationResponse.Datapoint + nil, // 194: api.v1.DailyUtilizationResponse.ClusterIdToDailyTotalCoreMinutesEntry + nil, // 195: api.v1.DailyUtilizationResponse.ClusterIdToMetaEntry + (*DailyUtilizationInstanceTypeResponse_Datapoints)(nil), // 196: api.v1.DailyUtilizationInstanceTypeResponse.Datapoints + (*DailyUtilizationInstanceTypeResponse_Datapoint)(nil), // 197: api.v1.DailyUtilizationInstanceTypeResponse.Datapoint + nil, // 198: api.v1.DailyUtilizationInstanceTypeResponse.ClusterIdToDatapointsEntry + nil, // 199: api.v1.DailyUtilizationInstanceTypeResponse.ClusterIdToMetaEntry + nil, // 200: api.v1.DailyUtilizationInstanceTypeResponse.Datapoint.InstanceTypeToDailyTotalCoreMinutesEntry + (*DailyUtilizationNodeTypeResponse_Datapoints)(nil), // 201: api.v1.DailyUtilizationNodeTypeResponse.Datapoints + (*DailyUtilizationNodeTypeResponse_Datapoint)(nil), // 202: api.v1.DailyUtilizationNodeTypeResponse.Datapoint + nil, // 203: api.v1.DailyUtilizationNodeTypeResponse.ClusterIdToDatapointsEntry + nil, // 204: api.v1.DailyUtilizationNodeTypeResponse.ClusterIdToMetaEntry + nil, // 205: api.v1.DailyUtilizationNodeTypeResponse.Datapoint.NodeTypeToDailyTotalCoreMinutesEntry + (*timestamppb.Timestamp)(nil), // 206: google.protobuf.Timestamp + (*WorkloadFilters)(nil), // 207: api.v1.WorkloadFilters + (*WorkloadItem)(nil), // 208: api.v1.WorkloadItem + (K8SObjectKind)(0), // 209: api.v1.K8sObjectKind + (*Pagination)(nil), // 210: api.v1.Pagination + (*ResourceFieldFilter)(nil), // 211: api.v1.ResourceFieldFilter + (*ResourceFilterFieldOptions)(nil), // 212: api.v1.ResourceFilterFieldOptions + (*NodeGroup)(nil), // 213: api.v1.NodeGroup + (*NodeInfo)(nil), // 214: api.v1.NodeInfo + (NodeGroupProvisioner)(0), // 215: api.v1.NodeGroupProvisioner + (*money.Money)(nil), // 216: google.type.Money + (*ResourceMetrics)(nil), // 217: api.v1.ResourceMetrics + (*CostInfo)(nil), // 218: api.v1.CostInfo + (*CostDataPoint)(nil), // 219: api.v1.CostDataPoint + (*ResourceDataPoint)(nil), // 220: api.v1.ResourceDataPoint + (*Node)(nil), // 221: api.v1.Node + (*ResourceSummary)(nil), // 222: api.v1.ResourceSummary + (*ContainerPercentileSummary)(nil), // 223: api.v1.ContainerPercentileSummary + (*ContainerTimeSeries)(nil), // 224: api.v1.ContainerTimeSeries + (*ContainerRequestLimits)(nil), // 225: api.v1.ContainerRequestLimits + (*GPUMigInstance)(nil), // 226: api.v1.GPUMigInstance + (*ContainerIndividualGPUMetrics)(nil), // 227: api.v1.ContainerIndividualGPUMetrics + (*ForecastResourceMetrics)(nil), // 228: api.v1.ForecastResourceMetrics + (*AuditLogEntry)(nil), // 229: api.v1.AuditLogEntry + (*Instance)(nil), // 230: api.v1.Instance } var file_api_v1_k8s_proto_depIdxs = []int32{ - 167, // 0: api.v1.GetNodeRequest.start_time:type_name -> google.protobuf.Timestamp - 167, // 1: api.v1.GetNodeRequest.end_time:type_name -> google.protobuf.Timestamp - 167, // 2: api.v1.GetWorkloadPodHistoryRequest.start_time:type_name -> google.protobuf.Timestamp - 167, // 3: api.v1.GetWorkloadPodHistoryRequest.end_time:type_name -> google.protobuf.Timestamp - 167, // 4: api.v1.GetNodeGroupRequest.start_time:type_name -> google.protobuf.Timestamp - 167, // 5: api.v1.GetNodeGroupRequest.end_time:type_name -> google.protobuf.Timestamp - 167, // 6: api.v1.GetWorkloadsRequest.start_time:type_name -> google.protobuf.Timestamp - 167, // 7: api.v1.GetWorkloadsRequest.end_time:type_name -> google.protobuf.Timestamp - 168, // 8: api.v1.GetWorkloadsRequest.filters:type_name -> api.v1.WorkloadFilters - 167, // 9: api.v1.ListWorkloadsPaginatedRequest.start_time:type_name -> google.protobuf.Timestamp - 167, // 10: api.v1.ListWorkloadsPaginatedRequest.end_time:type_name -> google.protobuf.Timestamp - 168, // 11: api.v1.ListWorkloadsPaginatedRequest.filters:type_name -> api.v1.WorkloadFilters - 167, // 12: api.v1.GetWorkloadsSummaryRequest.start_time:type_name -> google.protobuf.Timestamp - 167, // 13: api.v1.GetWorkloadsSummaryRequest.end_time:type_name -> google.protobuf.Timestamp - 168, // 14: api.v1.GetWorkloadsSummaryRequest.filters:type_name -> api.v1.WorkloadFilters - 10, // 15: api.v1.WorkloadMetricBreakdown.top_entries:type_name -> api.v1.WorkloadMetricEntry - 11, // 16: api.v1.GetWorkloadsSummaryResponse.cost:type_name -> api.v1.WorkloadMetricBreakdown - 11, // 17: api.v1.GetWorkloadsSummaryResponse.cpu_usage:type_name -> api.v1.WorkloadMetricBreakdown - 11, // 18: api.v1.GetWorkloadsSummaryResponse.memory_usage:type_name -> api.v1.WorkloadMetricBreakdown - 11, // 19: api.v1.GetWorkloadsSummaryResponse.gpu_usage:type_name -> api.v1.WorkloadMetricBreakdown - 11, // 20: api.v1.GetWorkloadsSummaryResponse.optimization_potential_cost:type_name -> api.v1.WorkloadMetricBreakdown - 11, // 21: api.v1.GetWorkloadsSummaryResponse.optimization_potential_cpu:type_name -> api.v1.WorkloadMetricBreakdown - 11, // 22: api.v1.GetWorkloadsSummaryResponse.optimization_potential_memory:type_name -> api.v1.WorkloadMetricBreakdown - 11, // 23: api.v1.GetWorkloadsSummaryResponse.optimization_potential_gpu:type_name -> api.v1.WorkloadMetricBreakdown - 169, // 24: api.v1.ListWorkloadsPaginatedResponse.workload_items:type_name -> api.v1.WorkloadItem - 170, // 25: api.v1.GetResourcesRequest.kind:type_name -> api.v1.K8sObjectKind - 171, // 26: api.v1.GetResourcesRequest.pagination:type_name -> api.v1.Pagination - 172, // 27: api.v1.GetResourcesRequest.filters:type_name -> api.v1.ResourceFieldFilter - 171, // 28: api.v1.GetPodsRequest.pagination:type_name -> api.v1.Pagination - 167, // 29: api.v1.GetPodsRequest.start_time:type_name -> google.protobuf.Timestamp - 167, // 30: api.v1.GetPodsRequest.end_time:type_name -> google.protobuf.Timestamp - 169, // 31: api.v1.GetPodsResponse.workload_items:type_name -> api.v1.WorkloadItem - 171, // 32: api.v1.GetPodsResponse.pagination:type_name -> api.v1.Pagination - 167, // 33: api.v1.GetWorkloadRequest.start_time:type_name -> google.protobuf.Timestamp - 167, // 34: api.v1.GetWorkloadRequest.end_time:type_name -> google.protobuf.Timestamp - 167, // 35: api.v1.GetClustersRequest.start_time:type_name -> google.protobuf.Timestamp - 167, // 36: api.v1.GetClustersRequest.end_time:type_name -> google.protobuf.Timestamp - 171, // 37: api.v1.ListClustersRequest.pagination:type_name -> api.v1.Pagination - 167, // 38: api.v1.GetClusterRequest.start_time:type_name -> google.protobuf.Timestamp - 167, // 39: api.v1.GetClusterRequest.end_time:type_name -> google.protobuf.Timestamp - 79, // 40: api.v1.UpdateClusterResponse.cluster:type_name -> api.v1.Cluster - 169, // 41: api.v1.GetResourcesResponse.workload_items:type_name -> api.v1.WorkloadItem - 171, // 42: api.v1.GetResourcesResponse.pagination:type_name -> api.v1.Pagination - 170, // 43: api.v1.GetResourceFilterOptionsRequest.kind:type_name -> api.v1.K8sObjectKind - 173, // 44: api.v1.GetResourceFilterOptionsResponse.options:type_name -> api.v1.ResourceFilterFieldOptions - 79, // 45: api.v1.CreateClusterResponse.cluster:type_name -> api.v1.Cluster - 167, // 46: api.v1.GetNodeGroupsRequest.start_time:type_name -> google.protobuf.Timestamp - 167, // 47: api.v1.GetNodeGroupsRequest.end_time:type_name -> google.protobuf.Timestamp - 174, // 48: api.v1.GetNodeGroupsResponse.node_groups:type_name -> api.v1.NodeGroup - 141, // 49: api.v1.GetAllNodeGroupsResponse.node_group_map:type_name -> api.v1.GetAllNodeGroupsResponse.NodeGroupMapEntry - 174, // 50: api.v1.NodeGroupSet.node_groups:type_name -> api.v1.NodeGroup - 167, // 51: api.v1.GetNodeGroupsUtilizationRequest.start_time:type_name -> google.protobuf.Timestamp - 167, // 52: api.v1.GetNodeGroupsUtilizationRequest.end_time:type_name -> google.protobuf.Timestamp - 143, // 53: api.v1.GetNodeGroupsUtilizationResponse.node_group_to_util_metrics:type_name -> api.v1.GetNodeGroupsUtilizationResponse.NodeGroupToUtilMetricsEntry - 144, // 54: api.v1.GetNodeGroupsUtilizationResponse.instance_type_to_util_metrics:type_name -> api.v1.GetNodeGroupsUtilizationResponse.InstanceTypeToUtilMetricsEntry - 145, // 55: api.v1.GetNodeGroupsUtilizationResponse.reservation_type_to_util_metrics:type_name -> api.v1.GetNodeGroupsUtilizationResponse.ReservationTypeToUtilMetricsEntry - 146, // 56: api.v1.GetNodeGroupsUtilizationResponse.logical_az_to_util_metrics:type_name -> api.v1.GetNodeGroupsUtilizationResponse.LogicalAzToUtilMetricsEntry - 175, // 57: api.v1.NodeGroupMetric.hourly_cost:type_name -> google.type.Money - 175, // 58: api.v1.NodeGroupMetric.hourly_cpu_cost:type_name -> google.type.Money - 175, // 59: api.v1.NodeGroupMetric.hourly_memory_cost:type_name -> google.type.Money - 175, // 60: api.v1.NodeGroupMetric.hourly_gpu_cost:type_name -> google.type.Money - 167, // 61: api.v1.NodeGroupMetric.bucket_time:type_name -> google.protobuf.Timestamp - 174, // 62: api.v1.GetNodeGroupResponse.node_group:type_name -> api.v1.NodeGroup - 79, // 63: api.v1.GetClusterResponse.cluster:type_name -> api.v1.Cluster - 79, // 64: api.v1.GetClustersResponse.clusters:type_name -> api.v1.Cluster - 176, // 65: api.v1.GetClustersResponse.resource_metrics:type_name -> api.v1.ResourceMetrics - 177, // 66: api.v1.GetClustersResponse.cost_info:type_name -> api.v1.CostInfo - 178, // 67: api.v1.GetClustersResponse.node_info:type_name -> api.v1.NodeInfo - 179, // 68: api.v1.GetClustersResponse.cost_data_points:type_name -> api.v1.CostDataPoint - 180, // 69: api.v1.GetClustersResponse.resource_data_points:type_name -> api.v1.ResourceDataPoint - 79, // 70: api.v1.ListClustersResponse.clusters:type_name -> api.v1.Cluster - 171, // 71: api.v1.ListClustersResponse.pagination:type_name -> api.v1.Pagination - 181, // 72: api.v1.GetNodeResponse.node:type_name -> api.v1.Node - 176, // 73: api.v1.GetNodeResponse.resource_metrics:type_name -> api.v1.ResourceMetrics - 177, // 74: api.v1.GetNodeResponse.cost_info:type_name -> api.v1.CostInfo - 179, // 75: api.v1.GetNodeResponse.cost_data_points:type_name -> api.v1.CostDataPoint - 180, // 76: api.v1.GetNodeResponse.resource_data_points:type_name -> api.v1.ResourceDataPoint - 169, // 77: api.v1.GetWorkloadsResponse.workload_items:type_name -> api.v1.WorkloadItem - 177, // 78: api.v1.GetWorkloadsResponse.cost_info:type_name -> api.v1.CostInfo - 176, // 79: api.v1.GetWorkloadsResponse.resource_metrics:type_name -> api.v1.ResourceMetrics - 182, // 80: api.v1.GetWorkloadsResponse.resource_summary:type_name -> api.v1.ResourceSummary - 169, // 81: api.v1.GetWorkloadResponse.workload_item:type_name -> api.v1.WorkloadItem - 177, // 82: api.v1.GetWorkloadResponse.cost_info:type_name -> api.v1.CostInfo - 176, // 83: api.v1.GetWorkloadResponse.resource_metrics:type_name -> api.v1.ResourceMetrics - 182, // 84: api.v1.GetWorkloadResponse.resource_summary:type_name -> api.v1.ResourceSummary - 179, // 85: api.v1.GetWorkloadResponse.cost_data_points:type_name -> api.v1.CostDataPoint - 180, // 86: api.v1.GetWorkloadResponse.resource_data_points:type_name -> api.v1.ResourceDataPoint - 167, // 87: api.v1.GetWorkloadContainerPercentilesRequest.start_time:type_name -> google.protobuf.Timestamp - 167, // 88: api.v1.GetWorkloadContainerPercentilesRequest.end_time:type_name -> google.protobuf.Timestamp - 183, // 89: api.v1.GetWorkloadContainerPercentilesResponse.containers:type_name -> api.v1.ContainerPercentileSummary - 167, // 90: api.v1.GetWorkloadContainerTimeSeriesRequest.start_time:type_name -> google.protobuf.Timestamp - 167, // 91: api.v1.GetWorkloadContainerTimeSeriesRequest.end_time:type_name -> google.protobuf.Timestamp - 184, // 92: api.v1.GetWorkloadContainerTimeSeriesResponse.containers:type_name -> api.v1.ContainerTimeSeries - 185, // 93: api.v1.GetLatestContainerRequestLimitsResponse.containers:type_name -> api.v1.ContainerRequestLimits - 186, // 94: api.v1.GetNodeGPUMigInstancesResponse.instances:type_name -> api.v1.GPUMigInstance - 167, // 95: api.v1.GetNodeGPUMigInstancesResponse.collected_at:type_name -> google.protobuf.Timestamp - 187, // 96: api.v1.GetContainerIndividualGPUMetricsResponse.containers:type_name -> api.v1.ContainerIndividualGPUMetrics - 57, // 97: api.v1.GetWorkloadLanguagesResponse.languages:type_name -> api.v1.LanguageVersions - 167, // 98: api.v1.GetForecastWorkloadsRequest.start_time:type_name -> google.protobuf.Timestamp - 167, // 99: api.v1.GetForecastWorkloadsRequest.end_time:type_name -> google.protobuf.Timestamp - 169, // 100: api.v1.GetForecastWorkloadsResponse.workload_items:type_name -> api.v1.WorkloadItem - 177, // 101: api.v1.GetForecastWorkloadsResponse.cost_info:type_name -> api.v1.CostInfo - 188, // 102: api.v1.GetForecastWorkloadsResponse.resource_metrics:type_name -> api.v1.ForecastResourceMetrics - 182, // 103: api.v1.GetForecastWorkloadsResponse.resource_summary:type_name -> api.v1.ResourceSummary - 167, // 104: api.v1.GetForecastWorkloadRequest.start_time:type_name -> google.protobuf.Timestamp - 167, // 105: api.v1.GetForecastWorkloadRequest.end_time:type_name -> google.protobuf.Timestamp - 169, // 106: api.v1.GetForecastWorkloadResponse.workload_item:type_name -> api.v1.WorkloadItem - 177, // 107: api.v1.GetForecastWorkloadResponse.cost_info:type_name -> api.v1.CostInfo - 188, // 108: api.v1.GetForecastWorkloadResponse.resource_metrics:type_name -> api.v1.ForecastResourceMetrics - 182, // 109: api.v1.GetForecastWorkloadResponse.resource_summary:type_name -> api.v1.ResourceSummary - 179, // 110: api.v1.GetForecastWorkloadResponse.cost_data_points:type_name -> api.v1.CostDataPoint - 180, // 111: api.v1.GetForecastWorkloadResponse.resource_data_points:type_name -> api.v1.ResourceDataPoint - 79, // 112: api.v1.GetClusterMetadataResponse.clusters:type_name -> api.v1.Cluster - 65, // 113: api.v1.GetAllNamespacesResponse.cluster_id_with_namespaces:type_name -> api.v1.ClusterElement - 72, // 114: api.v1.ListNamespacesByClusterResponse.namespaces:type_name -> api.v1.NamespaceItem - 170, // 115: api.v1.GetAllWorkloadNamesRequest.kinds:type_name -> api.v1.K8sObjectKind - 65, // 116: api.v1.GetAllWorkloadNamesResponse.cluster_id_with_workload_name:type_name -> api.v1.ClusterElement - 170, // 117: api.v1.GetAllWorkloadLabelsRequest.kinds:type_name -> api.v1.K8sObjectKind - 65, // 118: api.v1.GetAllWorkloadLabelsResponse.cluster_id_with_labels:type_name -> api.v1.ClusterElement - 65, // 119: api.v1.GetAllNodeGroupNamesResponse.cluster_id_with_node_group_names:type_name -> api.v1.ClusterElement - 176, // 120: api.v1.Cluster.resource_metrics:type_name -> api.v1.ResourceMetrics - 177, // 121: api.v1.Cluster.cost_info:type_name -> api.v1.CostInfo - 178, // 122: api.v1.Cluster.node_info:type_name -> api.v1.NodeInfo - 179, // 123: api.v1.Cluster.cost_data_points:type_name -> api.v1.CostDataPoint - 180, // 124: api.v1.Cluster.resource_data_points:type_name -> api.v1.ResourceDataPoint - 181, // 125: api.v1.Cluster.most_expensive_node:type_name -> api.v1.Node - 181, // 126: api.v1.Cluster.least_expensive_node:type_name -> api.v1.Node - 181, // 127: api.v1.Cluster.most_underutilized_node:type_name -> api.v1.Node - 81, // 128: api.v1.Cluster.most_underutilized_container:type_name -> api.v1.Container - 80, // 129: api.v1.Cluster.zxp_info:type_name -> api.v1.OperatorInfo - 80, // 130: api.v1.Cluster.zxp_helm_info:type_name -> api.v1.OperatorInfo - 80, // 131: api.v1.Cluster.dakr_op_info:type_name -> api.v1.OperatorInfo - 80, // 132: api.v1.Cluster.node_op_info:type_name -> api.v1.OperatorInfo - 80, // 133: api.v1.Cluster.security_op_info:type_name -> api.v1.OperatorInfo - 80, // 134: api.v1.Cluster.network_op_info:type_name -> api.v1.OperatorInfo - 176, // 135: api.v1.Container.resource_metrics:type_name -> api.v1.ResourceMetrics - 80, // 136: api.v1.GetLatestOperatorVersionResponse.zxp_info:type_name -> api.v1.OperatorInfo - 80, // 137: api.v1.GetLatestOperatorVersionResponse.zxp_helm_info:type_name -> api.v1.OperatorInfo - 80, // 138: api.v1.GetLatestOperatorVersionResponse.zxp_netmon_info:type_name -> api.v1.OperatorInfo - 80, // 139: api.v1.GetLatestOperatorVersionResponse.zxp_netmon_helm_info:type_name -> api.v1.OperatorInfo - 80, // 140: api.v1.GetLatestOperatorVersionResponse.dakr_op_info:type_name -> api.v1.OperatorInfo - 80, // 141: api.v1.GetLatestOperatorVersionResponse.dakr_op_helm_info:type_name -> api.v1.OperatorInfo - 80, // 142: api.v1.GetLatestOperatorVersionResponse.node_op_info_aws:type_name -> api.v1.OperatorInfo - 80, // 143: api.v1.GetLatestOperatorVersionResponse.node_op_info_azure:type_name -> api.v1.OperatorInfo - 80, // 144: api.v1.GetLatestOperatorVersionResponse.node_op_info_gcp:type_name -> api.v1.OperatorInfo - 80, // 145: api.v1.GetLatestOperatorVersionResponse.node_op_info_oci:type_name -> api.v1.OperatorInfo - 80, // 146: api.v1.GetLatestOperatorVersionResponse.security_op_info:type_name -> api.v1.OperatorInfo - 80, // 147: api.v1.GetLatestOperatorVersionResponse.security_op_helm_info:type_name -> api.v1.OperatorInfo - 80, // 148: api.v1.GetLatestOperatorVersionResponse.trezr_info:type_name -> api.v1.OperatorInfo - 80, // 149: api.v1.GetLatestOperatorVersionResponse.trezr_helm_info:type_name -> api.v1.OperatorInfo - 80, // 150: api.v1.GetLatestOperatorVersionResponse.snapshot_helm_info:type_name -> api.v1.OperatorInfo - 1, // 151: api.v1.GalaxyGetWorkloadPerspectiveRequest.group_by:type_name -> api.v1.GalaxyPOVWorkloadGroupBy - 87, // 152: api.v1.GalaxyGetWorkloadPerspectiveRequest.datapoint_opts:type_name -> api.v1.PerspectiveDatapointOpts - 86, // 153: api.v1.GalaxyGetWorkloadPerspectiveResponse.groupings:type_name -> api.v1.GalaxyWorkloadGroup - 147, // 154: api.v1.GalaxyGetWorkloadPerspectiveResponse.clusters:type_name -> api.v1.GalaxyGetWorkloadPerspectiveResponse.ClustersEntry - 148, // 155: api.v1.GalaxyGetWorkloadPerspectiveResponse.workloads:type_name -> api.v1.GalaxyGetWorkloadPerspectiveResponse.WorkloadsEntry - 177, // 156: api.v1.GalaxyWorkloadGroup.cost_info:type_name -> api.v1.CostInfo - 176, // 157: api.v1.GalaxyWorkloadGroup.resource_metrics:type_name -> api.v1.ResourceMetrics - 182, // 158: api.v1.GalaxyWorkloadGroup.resource_summary:type_name -> api.v1.ResourceSummary - 170, // 159: api.v1.ListAuditLogsRequest.workload_type:type_name -> api.v1.K8sObjectKind - 167, // 160: api.v1.ListAuditLogsRequest.start_time:type_name -> google.protobuf.Timestamp - 167, // 161: api.v1.ListAuditLogsRequest.end_time:type_name -> google.protobuf.Timestamp - 171, // 162: api.v1.ListAuditLogsRequest.pagination:type_name -> api.v1.Pagination - 189, // 163: api.v1.ListAuditLogsResponse.logs:type_name -> api.v1.AuditLogEntry - 171, // 164: api.v1.ListAuditLogsResponse.pagination:type_name -> api.v1.Pagination - 170, // 165: api.v1.ListAuditLogOriginatorsRequest.workload_type:type_name -> api.v1.K8sObjectKind - 167, // 166: api.v1.ListAuditLogOriginatorsRequest.start_time:type_name -> google.protobuf.Timestamp - 167, // 167: api.v1.ListAuditLogOriginatorsRequest.end_time:type_name -> google.protobuf.Timestamp - 95, // 168: api.v1.SendWeeklySummaryEmailRequest.request:type_name -> api.v1.SendWeeklySummaryEmailRequestData - 167, // 169: api.v1.GetClustersNodeInfoRequest.start_time:type_name -> google.protobuf.Timestamp - 167, // 170: api.v1.GetClustersNodeInfoRequest.end_time:type_name -> google.protobuf.Timestamp - 178, // 171: api.v1.GetClustersNodeInfoResponse.node_info:type_name -> api.v1.NodeInfo - 181, // 172: api.v1.GetClustersNodeInfoResponse.most_expensive_node:type_name -> api.v1.Node - 181, // 173: api.v1.GetClustersNodeInfoResponse.least_expensive_node:type_name -> api.v1.Node - 181, // 174: api.v1.GetClustersNodeInfoResponse.most_underutilized_node:type_name -> api.v1.Node - 101, // 175: api.v1.SearchK8sResourcesResponse.results:type_name -> api.v1.K8sSearchResult - 104, // 176: api.v1.SearchK8sWorkloadsResponse.results:type_name -> api.v1.K8sWorkloadSearchResult - 149, // 177: api.v1.MetadataForWorkloadsRequest.workload_uid_to_kind:type_name -> api.v1.MetadataForWorkloadsRequest.WorkloadUidToKindEntry - 150, // 178: api.v1.MetadataForWorkloadsResponse.workload_uid_to_metadata:type_name -> api.v1.MetadataForWorkloadsResponse.WorkloadUidToMetadataEntry - 79, // 179: api.v1.AddClusterTagsResponse.cluster:type_name -> api.v1.Cluster - 79, // 180: api.v1.RemoveClusterTagsResponse.cluster:type_name -> api.v1.Cluster - 112, // 181: api.v1.ListTagsResponse.tags:type_name -> api.v1.TagSummary - 151, // 182: api.v1.WorkloadMetadata.pod_uid_to_node_metadata:type_name -> api.v1.WorkloadMetadata.PodUidToNodeMetadataEntry - 152, // 183: api.v1.WorkloadMetadata.pod_uid_to_pod_metadata:type_name -> api.v1.WorkloadMetadata.PodUidToPodMetadataEntry - 170, // 184: api.v1.WorkloadMetadata.kind:type_name -> api.v1.K8sObjectKind - 170, // 185: api.v1.GetRelatedResourcesRequest.kind:type_name -> api.v1.K8sObjectKind - 120, // 186: api.v1.GetWorkloadPodHistoryResponse.replica_sets:type_name -> api.v1.ReplicaSetHistory - 121, // 187: api.v1.GetWorkloadPodHistoryResponse.direct_pods:type_name -> api.v1.PodHistory - 119, // 188: api.v1.GetWorkloadPodHistoryResponse.jobs:type_name -> api.v1.JobHistory - 167, // 189: api.v1.JobHistory.created_at:type_name -> google.protobuf.Timestamp - 167, // 190: api.v1.JobHistory.deleted_at:type_name -> google.protobuf.Timestamp - 121, // 191: api.v1.JobHistory.pods:type_name -> api.v1.PodHistory - 167, // 192: api.v1.ReplicaSetHistory.created_at:type_name -> google.protobuf.Timestamp - 167, // 193: api.v1.ReplicaSetHistory.deleted_at:type_name -> google.protobuf.Timestamp - 121, // 194: api.v1.ReplicaSetHistory.pods:type_name -> api.v1.PodHistory - 167, // 195: api.v1.PodHistory.created_at:type_name -> google.protobuf.Timestamp - 167, // 196: api.v1.PodHistory.deleted_at:type_name -> google.protobuf.Timestamp - 123, // 197: api.v1.GetRelatedResourcesResponse.relations:type_name -> api.v1.K8sRelatedResource - 124, // 198: api.v1.GetRelatedResourcesResponse.nodes:type_name -> api.v1.K8sResourceNode - 125, // 199: api.v1.GetRelatedResourcesResponse.edges:type_name -> api.v1.K8sResourceEdge - 2, // 200: api.v1.GetClusterTypeResponse.cluster_type:type_name -> api.v1.ClusterType - 167, // 201: api.v1.GetMigPartedConfigResponse.last_seen:type_name -> google.protobuf.Timestamp - 167, // 202: api.v1.GetWorkloadsStatsRequest.start_time:type_name -> google.protobuf.Timestamp - 167, // 203: api.v1.GetWorkloadsStatsRequest.end_time:type_name -> google.protobuf.Timestamp - 167, // 204: api.v1.DailyUtilizationRequest.start_time:type_name -> google.protobuf.Timestamp - 167, // 205: api.v1.DailyUtilizationRequest.end_time:type_name -> google.protobuf.Timestamp - 155, // 206: api.v1.DailyUtilizationResponse.cluster_id_to_daily_total_core_minutes:type_name -> api.v1.DailyUtilizationResponse.ClusterIdToDailyTotalCoreMinutesEntry - 156, // 207: api.v1.DailyUtilizationResponse.cluster_id_to_meta:type_name -> api.v1.DailyUtilizationResponse.ClusterIdToMetaEntry - 167, // 208: api.v1.DailyUtilizationInstanceTypeRequest.start_time:type_name -> google.protobuf.Timestamp - 167, // 209: api.v1.DailyUtilizationInstanceTypeRequest.end_time:type_name -> google.protobuf.Timestamp - 159, // 210: api.v1.DailyUtilizationInstanceTypeResponse.cluster_id_to_datapoints:type_name -> api.v1.DailyUtilizationInstanceTypeResponse.ClusterIdToDatapointsEntry - 160, // 211: api.v1.DailyUtilizationInstanceTypeResponse.cluster_id_to_meta:type_name -> api.v1.DailyUtilizationInstanceTypeResponse.ClusterIdToMetaEntry - 167, // 212: api.v1.DailyUtilizationNodeTypeRequest.start_time:type_name -> google.protobuf.Timestamp - 167, // 213: api.v1.DailyUtilizationNodeTypeRequest.end_time:type_name -> google.protobuf.Timestamp - 164, // 214: api.v1.DailyUtilizationNodeTypeResponse.cluster_id_to_datapoints:type_name -> api.v1.DailyUtilizationNodeTypeResponse.ClusterIdToDatapointsEntry - 165, // 215: api.v1.DailyUtilizationNodeTypeResponse.cluster_id_to_meta:type_name -> api.v1.DailyUtilizationNodeTypeResponse.ClusterIdToMetaEntry - 190, // 216: api.v1.LookupNodeInstanceResponse.dynamic_instance:type_name -> api.v1.Instance - 190, // 217: api.v1.LookupNodeInstanceResponse.cached_instance:type_name -> api.v1.Instance - 139, // 218: api.v1.LookupNodeInstanceResponse.lookup_params:type_name -> api.v1.InstanceLookupParams - 174, // 219: api.v1.GetAllNodeGroupsResponse.NodeGroupMapEntry.value:type_name -> api.v1.NodeGroup - 39, // 220: api.v1.GetNodeGroupsUtilizationResponse.ListNodeGroupMetrics.node_group_metrics:type_name -> api.v1.NodeGroupMetric - 142, // 221: api.v1.GetNodeGroupsUtilizationResponse.NodeGroupToUtilMetricsEntry.value:type_name -> api.v1.GetNodeGroupsUtilizationResponse.ListNodeGroupMetrics - 142, // 222: api.v1.GetNodeGroupsUtilizationResponse.InstanceTypeToUtilMetricsEntry.value:type_name -> api.v1.GetNodeGroupsUtilizationResponse.ListNodeGroupMetrics - 142, // 223: api.v1.GetNodeGroupsUtilizationResponse.ReservationTypeToUtilMetricsEntry.value:type_name -> api.v1.GetNodeGroupsUtilizationResponse.ListNodeGroupMetrics - 142, // 224: api.v1.GetNodeGroupsUtilizationResponse.LogicalAzToUtilMetricsEntry.value:type_name -> api.v1.GetNodeGroupsUtilizationResponse.ListNodeGroupMetrics - 79, // 225: api.v1.GalaxyGetWorkloadPerspectiveResponse.ClustersEntry.value:type_name -> api.v1.Cluster - 169, // 226: api.v1.GalaxyGetWorkloadPerspectiveResponse.WorkloadsEntry.value:type_name -> api.v1.WorkloadItem - 170, // 227: api.v1.MetadataForWorkloadsRequest.WorkloadUidToKindEntry.value:type_name -> api.v1.K8sObjectKind - 114, // 228: api.v1.MetadataForWorkloadsResponse.WorkloadUidToMetadataEntry.value:type_name -> api.v1.WorkloadMetadata - 116, // 229: api.v1.WorkloadMetadata.PodUidToNodeMetadataEntry.value:type_name -> api.v1.NodeMetadata - 115, // 230: api.v1.WorkloadMetadata.PodUidToPodMetadataEntry.value:type_name -> api.v1.PodMetadata - 154, // 231: api.v1.DailyUtilizationResponse.Datapoints.datapoints:type_name -> api.v1.DailyUtilizationResponse.Datapoint - 153, // 232: api.v1.DailyUtilizationResponse.ClusterIdToDailyTotalCoreMinutesEntry.value:type_name -> api.v1.DailyUtilizationResponse.Datapoints - 79, // 233: api.v1.DailyUtilizationResponse.ClusterIdToMetaEntry.value:type_name -> api.v1.Cluster - 158, // 234: api.v1.DailyUtilizationInstanceTypeResponse.Datapoints.datapoints:type_name -> api.v1.DailyUtilizationInstanceTypeResponse.Datapoint - 161, // 235: api.v1.DailyUtilizationInstanceTypeResponse.Datapoint.instance_type_to_daily_total_core_minutes:type_name -> api.v1.DailyUtilizationInstanceTypeResponse.Datapoint.InstanceTypeToDailyTotalCoreMinutesEntry - 157, // 236: api.v1.DailyUtilizationInstanceTypeResponse.ClusterIdToDatapointsEntry.value:type_name -> api.v1.DailyUtilizationInstanceTypeResponse.Datapoints - 79, // 237: api.v1.DailyUtilizationInstanceTypeResponse.ClusterIdToMetaEntry.value:type_name -> api.v1.Cluster - 163, // 238: api.v1.DailyUtilizationNodeTypeResponse.Datapoints.datapoints:type_name -> api.v1.DailyUtilizationNodeTypeResponse.Datapoint - 166, // 239: api.v1.DailyUtilizationNodeTypeResponse.Datapoint.node_type_to_daily_total_core_minutes:type_name -> api.v1.DailyUtilizationNodeTypeResponse.Datapoint.NodeTypeToDailyTotalCoreMinutesEntry - 162, // 240: api.v1.DailyUtilizationNodeTypeResponse.ClusterIdToDatapointsEntry.value:type_name -> api.v1.DailyUtilizationNodeTypeResponse.Datapoints - 79, // 241: api.v1.DailyUtilizationNodeTypeResponse.ClusterIdToMetaEntry.value:type_name -> api.v1.Cluster - 130, // 242: api.v1.K8SService.GetWorkloadsStats:input_type -> api.v1.GetWorkloadsStatsRequest - 18, // 243: api.v1.K8SService.GetClusters:input_type -> api.v1.GetClustersRequest - 19, // 244: api.v1.K8SService.ListClusters:input_type -> api.v1.ListClustersRequest - 20, // 245: api.v1.K8SService.GetCluster:input_type -> api.v1.GetClusterRequest - 5, // 246: api.v1.K8SService.GetClusterMetadata:input_type -> api.v1.GetClusterMetadataRequest - 66, // 247: api.v1.K8SService.GetAllNamespaces:input_type -> api.v1.GetAllNamespacesRequest - 68, // 248: api.v1.K8SService.SearchNamespacesByCluster:input_type -> api.v1.SearchNamespacesByClusterRequest - 70, // 249: api.v1.K8SService.ListNamespacesByCluster:input_type -> api.v1.ListNamespacesByClusterRequest - 73, // 250: api.v1.K8SService.GetAllWorkloadNames:input_type -> api.v1.GetAllWorkloadNamesRequest - 75, // 251: api.v1.K8SService.GetAllWorkloadLabels:input_type -> api.v1.GetAllWorkloadLabelsRequest - 77, // 252: api.v1.K8SService.GetAllNodeGroupNames:input_type -> api.v1.GetAllNodeGroupNamesRequest - 105, // 253: api.v1.K8SService.MetadataForWorkloads:input_type -> api.v1.MetadataForWorkloadsRequest - 32, // 254: api.v1.K8SService.GetNodeGroups:input_type -> api.v1.GetNodeGroupsRequest - 33, // 255: api.v1.K8SService.GetAllNodeGroups:input_type -> api.v1.GetAllNodeGroupsRequest - 37, // 256: api.v1.K8SService.GetNodeGroupsUtilization:input_type -> api.v1.GetNodeGroupsUtilizationRequest - 6, // 257: api.v1.K8SService.GetNodeGroup:input_type -> api.v1.GetNodeGroupRequest - 3, // 258: api.v1.K8SService.GetNode:input_type -> api.v1.GetNodeRequest - 7, // 259: api.v1.K8SService.GetWorkloads:input_type -> api.v1.GetWorkloadsRequest - 8, // 260: api.v1.K8SService.ListWorkloadsPaginated:input_type -> api.v1.ListWorkloadsPaginatedRequest - 9, // 261: api.v1.K8SService.GetWorkloadsSummary:input_type -> api.v1.GetWorkloadsSummaryRequest - 17, // 262: api.v1.K8SService.GetWorkload:input_type -> api.v1.GetWorkloadRequest - 47, // 263: api.v1.K8SService.GetWorkloadContainerPercentiles:input_type -> api.v1.GetWorkloadContainerPercentilesRequest - 49, // 264: api.v1.K8SService.GetWorkloadContainerTimeSeries:input_type -> api.v1.GetWorkloadContainerTimeSeriesRequest - 51, // 265: api.v1.K8SService.GetLatestContainerRequestLimits:input_type -> api.v1.GetLatestContainerRequestLimitsRequest - 53, // 266: api.v1.K8SService.GetNodeGPUMigInstances:input_type -> api.v1.GetNodeGPUMigInstancesRequest - 55, // 267: api.v1.K8SService.GetContainerIndividualGPUMetrics:input_type -> api.v1.GetContainerIndividualGPUMetricsRequest - 58, // 268: api.v1.K8SService.GetWorkloadLanguages:input_type -> api.v1.GetWorkloadLanguagesRequest - 60, // 269: api.v1.K8SService.GetForecastWorkloads:input_type -> api.v1.GetForecastWorkloadsRequest - 62, // 270: api.v1.K8SService.GetForecastWorkload:input_type -> api.v1.GetForecastWorkloadRequest - 14, // 271: api.v1.K8SService.GetResources:input_type -> api.v1.GetResourcesRequest - 26, // 272: api.v1.K8SService.GetResourceFilterOptions:input_type -> api.v1.GetResourceFilterOptionsRequest - 15, // 273: api.v1.K8SService.GetPods:input_type -> api.v1.GetPodsRequest - 82, // 274: api.v1.K8SService.GetLatestOperatorVersion:input_type -> api.v1.GetLatestOperatorVersionRequest - 84, // 275: api.v1.K8SService.GalaxyGetWorkloadPerspective:input_type -> api.v1.GalaxyGetWorkloadPerspectiveRequest - 88, // 276: api.v1.K8SService.ListAuditLogs:input_type -> api.v1.ListAuditLogsRequest - 90, // 277: api.v1.K8SService.ListAuditLogOriginators:input_type -> api.v1.ListAuditLogOriginatorsRequest - 92, // 278: api.v1.K8SService.SendWorkloadEmail:input_type -> api.v1.SendWorkloadEmailRequest - 94, // 279: api.v1.K8SService.SendWeeklySummaryEmail:input_type -> api.v1.SendWeeklySummaryEmailRequest - 97, // 280: api.v1.K8SService.GetClustersNodeInfo:input_type -> api.v1.GetClustersNodeInfoRequest - 99, // 281: api.v1.K8SService.SearchK8sResources:input_type -> api.v1.SearchK8sResourcesRequest - 102, // 282: api.v1.K8SService.SearchK8sWorkloads:input_type -> api.v1.SearchK8sWorkloadsRequest - 126, // 283: api.v1.K8SService.GetClusterType:input_type -> api.v1.GetClusterTypeRequest - 117, // 284: api.v1.K8SService.GetRelationsForKind:input_type -> api.v1.GetRelatedResourcesRequest - 138, // 285: api.v1.K8SService.LookupNodeInstance:input_type -> api.v1.LookupNodeInstanceRequest - 4, // 286: api.v1.K8SService.GetWorkloadPodHistory:input_type -> api.v1.GetWorkloadPodHistoryRequest - 107, // 287: api.v1.K8SService.AddClusterTags:input_type -> api.v1.AddClusterTagsRequest - 109, // 288: api.v1.K8SService.RemoveClusterTags:input_type -> api.v1.RemoveClusterTagsRequest - 111, // 289: api.v1.K8SService.ListTags:input_type -> api.v1.ListTagsRequest - 128, // 290: api.v1.K8SService.GetMigPartedConfig:input_type -> api.v1.GetMigPartedConfigRequest - 28, // 291: api.v1.ClusterMutationService.CreateCluster:input_type -> api.v1.CreateClusterRequest - 21, // 292: api.v1.ClusterMutationService.DeleteCluster:input_type -> api.v1.DeleteClusterRequest - 23, // 293: api.v1.ClusterMutationService.UpdateCluster:input_type -> api.v1.UpdateClusterRequest - 30, // 294: api.v1.ClusterMutationService.ResetClusterToken:input_type -> api.v1.ResetClusterTokenRequest - 132, // 295: api.v1.UtilizationService.DailyUtilization:input_type -> api.v1.DailyUtilizationRequest - 134, // 296: api.v1.UtilizationService.DailyUtilizationInstanceType:input_type -> api.v1.DailyUtilizationInstanceTypeRequest - 136, // 297: api.v1.UtilizationService.DailyUtilizationNodeType:input_type -> api.v1.DailyUtilizationNodeTypeRequest - 131, // 298: api.v1.K8SService.GetWorkloadsStats:output_type -> api.v1.GetWorkloadsStatsResponse - 42, // 299: api.v1.K8SService.GetClusters:output_type -> api.v1.GetClustersResponse - 43, // 300: api.v1.K8SService.ListClusters:output_type -> api.v1.ListClustersResponse - 41, // 301: api.v1.K8SService.GetCluster:output_type -> api.v1.GetClusterResponse - 64, // 302: api.v1.K8SService.GetClusterMetadata:output_type -> api.v1.GetClusterMetadataResponse - 67, // 303: api.v1.K8SService.GetAllNamespaces:output_type -> api.v1.GetAllNamespacesResponse - 69, // 304: api.v1.K8SService.SearchNamespacesByCluster:output_type -> api.v1.SearchNamespacesByClusterResponse - 71, // 305: api.v1.K8SService.ListNamespacesByCluster:output_type -> api.v1.ListNamespacesByClusterResponse - 74, // 306: api.v1.K8SService.GetAllWorkloadNames:output_type -> api.v1.GetAllWorkloadNamesResponse - 76, // 307: api.v1.K8SService.GetAllWorkloadLabels:output_type -> api.v1.GetAllWorkloadLabelsResponse - 78, // 308: api.v1.K8SService.GetAllNodeGroupNames:output_type -> api.v1.GetAllNodeGroupNamesResponse - 106, // 309: api.v1.K8SService.MetadataForWorkloads:output_type -> api.v1.MetadataForWorkloadsResponse - 34, // 310: api.v1.K8SService.GetNodeGroups:output_type -> api.v1.GetNodeGroupsResponse - 35, // 311: api.v1.K8SService.GetAllNodeGroups:output_type -> api.v1.GetAllNodeGroupsResponse - 38, // 312: api.v1.K8SService.GetNodeGroupsUtilization:output_type -> api.v1.GetNodeGroupsUtilizationResponse - 40, // 313: api.v1.K8SService.GetNodeGroup:output_type -> api.v1.GetNodeGroupResponse - 44, // 314: api.v1.K8SService.GetNode:output_type -> api.v1.GetNodeResponse - 45, // 315: api.v1.K8SService.GetWorkloads:output_type -> api.v1.GetWorkloadsResponse - 13, // 316: api.v1.K8SService.ListWorkloadsPaginated:output_type -> api.v1.ListWorkloadsPaginatedResponse - 12, // 317: api.v1.K8SService.GetWorkloadsSummary:output_type -> api.v1.GetWorkloadsSummaryResponse - 46, // 318: api.v1.K8SService.GetWorkload:output_type -> api.v1.GetWorkloadResponse - 48, // 319: api.v1.K8SService.GetWorkloadContainerPercentiles:output_type -> api.v1.GetWorkloadContainerPercentilesResponse - 50, // 320: api.v1.K8SService.GetWorkloadContainerTimeSeries:output_type -> api.v1.GetWorkloadContainerTimeSeriesResponse - 52, // 321: api.v1.K8SService.GetLatestContainerRequestLimits:output_type -> api.v1.GetLatestContainerRequestLimitsResponse - 54, // 322: api.v1.K8SService.GetNodeGPUMigInstances:output_type -> api.v1.GetNodeGPUMigInstancesResponse - 56, // 323: api.v1.K8SService.GetContainerIndividualGPUMetrics:output_type -> api.v1.GetContainerIndividualGPUMetricsResponse - 59, // 324: api.v1.K8SService.GetWorkloadLanguages:output_type -> api.v1.GetWorkloadLanguagesResponse - 61, // 325: api.v1.K8SService.GetForecastWorkloads:output_type -> api.v1.GetForecastWorkloadsResponse - 63, // 326: api.v1.K8SService.GetForecastWorkload:output_type -> api.v1.GetForecastWorkloadResponse - 25, // 327: api.v1.K8SService.GetResources:output_type -> api.v1.GetResourcesResponse - 27, // 328: api.v1.K8SService.GetResourceFilterOptions:output_type -> api.v1.GetResourceFilterOptionsResponse - 16, // 329: api.v1.K8SService.GetPods:output_type -> api.v1.GetPodsResponse - 83, // 330: api.v1.K8SService.GetLatestOperatorVersion:output_type -> api.v1.GetLatestOperatorVersionResponse - 85, // 331: api.v1.K8SService.GalaxyGetWorkloadPerspective:output_type -> api.v1.GalaxyGetWorkloadPerspectiveResponse - 89, // 332: api.v1.K8SService.ListAuditLogs:output_type -> api.v1.ListAuditLogsResponse - 91, // 333: api.v1.K8SService.ListAuditLogOriginators:output_type -> api.v1.ListAuditLogOriginatorsResponse - 93, // 334: api.v1.K8SService.SendWorkloadEmail:output_type -> api.v1.SendWorkloadEmailResponse - 96, // 335: api.v1.K8SService.SendWeeklySummaryEmail:output_type -> api.v1.SendWeeklySummaryEmailResponse - 98, // 336: api.v1.K8SService.GetClustersNodeInfo:output_type -> api.v1.GetClustersNodeInfoResponse - 100, // 337: api.v1.K8SService.SearchK8sResources:output_type -> api.v1.SearchK8sResourcesResponse - 103, // 338: api.v1.K8SService.SearchK8sWorkloads:output_type -> api.v1.SearchK8sWorkloadsResponse - 127, // 339: api.v1.K8SService.GetClusterType:output_type -> api.v1.GetClusterTypeResponse - 122, // 340: api.v1.K8SService.GetRelationsForKind:output_type -> api.v1.GetRelatedResourcesResponse - 140, // 341: api.v1.K8SService.LookupNodeInstance:output_type -> api.v1.LookupNodeInstanceResponse - 118, // 342: api.v1.K8SService.GetWorkloadPodHistory:output_type -> api.v1.GetWorkloadPodHistoryResponse - 108, // 343: api.v1.K8SService.AddClusterTags:output_type -> api.v1.AddClusterTagsResponse - 110, // 344: api.v1.K8SService.RemoveClusterTags:output_type -> api.v1.RemoveClusterTagsResponse - 113, // 345: api.v1.K8SService.ListTags:output_type -> api.v1.ListTagsResponse - 129, // 346: api.v1.K8SService.GetMigPartedConfig:output_type -> api.v1.GetMigPartedConfigResponse - 29, // 347: api.v1.ClusterMutationService.CreateCluster:output_type -> api.v1.CreateClusterResponse - 22, // 348: api.v1.ClusterMutationService.DeleteCluster:output_type -> api.v1.DeleteClusterResponse - 24, // 349: api.v1.ClusterMutationService.UpdateCluster:output_type -> api.v1.UpdateClusterResponse - 31, // 350: api.v1.ClusterMutationService.ResetClusterToken:output_type -> api.v1.ResetClusterTokenResponse - 133, // 351: api.v1.UtilizationService.DailyUtilization:output_type -> api.v1.DailyUtilizationResponse - 135, // 352: api.v1.UtilizationService.DailyUtilizationInstanceType:output_type -> api.v1.DailyUtilizationInstanceTypeResponse - 137, // 353: api.v1.UtilizationService.DailyUtilizationNodeType:output_type -> api.v1.DailyUtilizationNodeTypeResponse - 298, // [298:354] is the sub-list for method output_type - 242, // [242:298] is the sub-list for method input_type - 242, // [242:242] is the sub-list for extension type_name - 242, // [242:242] is the sub-list for extension extendee - 0, // [0:242] is the sub-list for field type_name + 206, // 0: api.v1.GetNodeRequest.start_time:type_name -> google.protobuf.Timestamp + 206, // 1: api.v1.GetNodeRequest.end_time:type_name -> google.protobuf.Timestamp + 206, // 2: api.v1.GetWorkloadPodHistoryRequest.start_time:type_name -> google.protobuf.Timestamp + 206, // 3: api.v1.GetWorkloadPodHistoryRequest.end_time:type_name -> google.protobuf.Timestamp + 206, // 4: api.v1.GetNodeGroupRequest.start_time:type_name -> google.protobuf.Timestamp + 206, // 5: api.v1.GetNodeGroupRequest.end_time:type_name -> google.protobuf.Timestamp + 206, // 6: api.v1.GetWorkloadsRequest.start_time:type_name -> google.protobuf.Timestamp + 206, // 7: api.v1.GetWorkloadsRequest.end_time:type_name -> google.protobuf.Timestamp + 207, // 8: api.v1.GetWorkloadsRequest.filters:type_name -> api.v1.WorkloadFilters + 206, // 9: api.v1.ListWorkloadsPaginatedRequest.start_time:type_name -> google.protobuf.Timestamp + 206, // 10: api.v1.ListWorkloadsPaginatedRequest.end_time:type_name -> google.protobuf.Timestamp + 207, // 11: api.v1.ListWorkloadsPaginatedRequest.filters:type_name -> api.v1.WorkloadFilters + 206, // 12: api.v1.GetWorkloadsSummaryRequest.start_time:type_name -> google.protobuf.Timestamp + 206, // 13: api.v1.GetWorkloadsSummaryRequest.end_time:type_name -> google.protobuf.Timestamp + 207, // 14: api.v1.GetWorkloadsSummaryRequest.filters:type_name -> api.v1.WorkloadFilters + 14, // 15: api.v1.WorkloadMetricBreakdown.top_entries:type_name -> api.v1.WorkloadMetricEntry + 15, // 16: api.v1.GetWorkloadsSummaryResponse.cost:type_name -> api.v1.WorkloadMetricBreakdown + 15, // 17: api.v1.GetWorkloadsSummaryResponse.cpu_usage:type_name -> api.v1.WorkloadMetricBreakdown + 15, // 18: api.v1.GetWorkloadsSummaryResponse.memory_usage:type_name -> api.v1.WorkloadMetricBreakdown + 15, // 19: api.v1.GetWorkloadsSummaryResponse.gpu_usage:type_name -> api.v1.WorkloadMetricBreakdown + 15, // 20: api.v1.GetWorkloadsSummaryResponse.optimization_potential_cost:type_name -> api.v1.WorkloadMetricBreakdown + 15, // 21: api.v1.GetWorkloadsSummaryResponse.optimization_potential_cpu:type_name -> api.v1.WorkloadMetricBreakdown + 15, // 22: api.v1.GetWorkloadsSummaryResponse.optimization_potential_memory:type_name -> api.v1.WorkloadMetricBreakdown + 15, // 23: api.v1.GetWorkloadsSummaryResponse.optimization_potential_gpu:type_name -> api.v1.WorkloadMetricBreakdown + 206, // 24: api.v1.GetWorkloadRequestTrendsRequest.start_time:type_name -> google.protobuf.Timestamp + 206, // 25: api.v1.GetWorkloadRequestTrendsRequest.end_time:type_name -> google.protobuf.Timestamp + 207, // 26: api.v1.GetWorkloadRequestTrendsRequest.filters:type_name -> api.v1.WorkloadFilters + 18, // 27: api.v1.RequestTrendSeries.points:type_name -> api.v1.RequestTrendPoint + 19, // 28: api.v1.GetWorkloadRequestTrendsResponse.unmanaged_cpu_requested:type_name -> api.v1.RequestTrendSeries + 19, // 29: api.v1.GetWorkloadRequestTrendsResponse.unmanaged_memory_requested:type_name -> api.v1.RequestTrendSeries + 19, // 30: api.v1.GetWorkloadRequestTrendsResponse.managed_cpu_original:type_name -> api.v1.RequestTrendSeries + 19, // 31: api.v1.GetWorkloadRequestTrendsResponse.managed_cpu_optimized:type_name -> api.v1.RequestTrendSeries + 19, // 32: api.v1.GetWorkloadRequestTrendsResponse.managed_memory_original:type_name -> api.v1.RequestTrendSeries + 19, // 33: api.v1.GetWorkloadRequestTrendsResponse.managed_memory_optimized:type_name -> api.v1.RequestTrendSeries + 19, // 34: api.v1.GetWorkloadRequestTrendsResponse.unmanaged_cpu_used:type_name -> api.v1.RequestTrendSeries + 19, // 35: api.v1.GetWorkloadRequestTrendsResponse.unmanaged_memory_used:type_name -> api.v1.RequestTrendSeries + 19, // 36: api.v1.GetWorkloadRequestTrendsResponse.managed_cpu_used:type_name -> api.v1.RequestTrendSeries + 19, // 37: api.v1.GetWorkloadRequestTrendsResponse.managed_memory_used:type_name -> api.v1.RequestTrendSeries + 19, // 38: api.v1.GetWorkloadRequestTrendsResponse.managed_cpu_recommended:type_name -> api.v1.RequestTrendSeries + 19, // 39: api.v1.GetWorkloadRequestTrendsResponse.managed_memory_recommended:type_name -> api.v1.RequestTrendSeries + 208, // 40: api.v1.ListWorkloadsPaginatedResponse.workload_items:type_name -> api.v1.WorkloadItem + 209, // 41: api.v1.GetResourcesRequest.kind:type_name -> api.v1.K8sObjectKind + 210, // 42: api.v1.GetResourcesRequest.pagination:type_name -> api.v1.Pagination + 211, // 43: api.v1.GetResourcesRequest.filters:type_name -> api.v1.ResourceFieldFilter + 210, // 44: api.v1.GetPodsRequest.pagination:type_name -> api.v1.Pagination + 206, // 45: api.v1.GetPodsRequest.start_time:type_name -> google.protobuf.Timestamp + 206, // 46: api.v1.GetPodsRequest.end_time:type_name -> google.protobuf.Timestamp + 208, // 47: api.v1.GetPodsResponse.workload_items:type_name -> api.v1.WorkloadItem + 210, // 48: api.v1.GetPodsResponse.pagination:type_name -> api.v1.Pagination + 206, // 49: api.v1.GetWorkloadRequest.start_time:type_name -> google.protobuf.Timestamp + 206, // 50: api.v1.GetWorkloadRequest.end_time:type_name -> google.protobuf.Timestamp + 206, // 51: api.v1.GetClustersRequest.start_time:type_name -> google.protobuf.Timestamp + 206, // 52: api.v1.GetClustersRequest.end_time:type_name -> google.protobuf.Timestamp + 210, // 53: api.v1.ListClustersRequest.pagination:type_name -> api.v1.Pagination + 206, // 54: api.v1.GetClusterRequest.start_time:type_name -> google.protobuf.Timestamp + 206, // 55: api.v1.GetClusterRequest.end_time:type_name -> google.protobuf.Timestamp + 116, // 56: api.v1.UpdateClusterResponse.cluster:type_name -> api.v1.Cluster + 208, // 57: api.v1.GetResourcesResponse.workload_items:type_name -> api.v1.WorkloadItem + 210, // 58: api.v1.GetResourcesResponse.pagination:type_name -> api.v1.Pagination + 209, // 59: api.v1.GetResourceFilterOptionsRequest.kind:type_name -> api.v1.K8sObjectKind + 212, // 60: api.v1.GetResourceFilterOptionsResponse.options:type_name -> api.v1.ResourceFilterFieldOptions + 116, // 61: api.v1.CreateClusterResponse.cluster:type_name -> api.v1.Cluster + 206, // 62: api.v1.GetNodeGroupsRequest.start_time:type_name -> google.protobuf.Timestamp + 206, // 63: api.v1.GetNodeGroupsRequest.end_time:type_name -> google.protobuf.Timestamp + 213, // 64: api.v1.GetNodeGroupsResponse.node_groups:type_name -> api.v1.NodeGroup + 178, // 65: api.v1.GetAllNodeGroupsResponse.node_group_map:type_name -> api.v1.GetAllNodeGroupsResponse.NodeGroupMapEntry + 213, // 66: api.v1.NodeGroupSet.node_groups:type_name -> api.v1.NodeGroup + 206, // 67: api.v1.GetClusterNodeFilterIndexRequest.start_time:type_name -> google.protobuf.Timestamp + 206, // 68: api.v1.GetClusterNodeFilterIndexRequest.end_time:type_name -> google.protobuf.Timestamp + 179, // 69: api.v1.NodeLabelSet.labels:type_name -> api.v1.NodeLabelSet.LabelsEntry + 180, // 70: api.v1.NodePoolFields.fields:type_name -> api.v1.NodePoolFields.FieldsEntry + 46, // 71: api.v1.GetClusterNodeFilterIndexResponse.label_sets:type_name -> api.v1.NodeLabelSet + 47, // 72: api.v1.GetClusterNodeFilterIndexResponse.nodes:type_name -> api.v1.NodeLabelRef + 48, // 73: api.v1.GetClusterNodeFilterIndexResponse.node_pools:type_name -> api.v1.NodePoolFields + 206, // 74: api.v1.GetNodeGroupsUtilizationRequest.start_time:type_name -> google.protobuf.Timestamp + 206, // 75: api.v1.GetNodeGroupsUtilizationRequest.end_time:type_name -> google.protobuf.Timestamp + 182, // 76: api.v1.GetNodeGroupsUtilizationResponse.node_group_to_util_metrics:type_name -> api.v1.GetNodeGroupsUtilizationResponse.NodeGroupToUtilMetricsEntry + 183, // 77: api.v1.GetNodeGroupsUtilizationResponse.instance_type_to_util_metrics:type_name -> api.v1.GetNodeGroupsUtilizationResponse.InstanceTypeToUtilMetricsEntry + 184, // 78: api.v1.GetNodeGroupsUtilizationResponse.reservation_type_to_util_metrics:type_name -> api.v1.GetNodeGroupsUtilizationResponse.ReservationTypeToUtilMetricsEntry + 185, // 79: api.v1.GetNodeGroupsUtilizationResponse.logical_az_to_util_metrics:type_name -> api.v1.GetNodeGroupsUtilizationResponse.LogicalAzToUtilMetricsEntry + 0, // 80: api.v1.GetClusterNodeInventoryRequest.scope:type_name -> api.v1.NodeInventoryScope + 1, // 81: api.v1.GetClusterNodeInventoryRequest.states:type_name -> api.v1.NodeState + 206, // 82: api.v1.GetClusterNodeInventoryResponse.as_of:type_name -> google.protobuf.Timestamp + 54, // 83: api.v1.GetClusterNodeInventoryResponse.totals:type_name -> api.v1.NodeInventoryRollup + 56, // 84: api.v1.GetClusterNodeInventoryResponse.pools:type_name -> api.v1.NodePoolInventory + 58, // 85: api.v1.GetClusterNodeInventoryResponse.nodes:type_name -> api.v1.NodeInventoryRow + 214, // 86: api.v1.NodeInventoryRollup.node_info:type_name -> api.v1.NodeInfo + 55, // 87: api.v1.NodeInventoryRollup.states:type_name -> api.v1.NodeStateCounts + 215, // 88: api.v1.NodePoolInventory.provisioner:type_name -> api.v1.NodeGroupProvisioner + 54, // 89: api.v1.NodePoolInventory.rollup:type_name -> api.v1.NodeInventoryRollup + 57, // 90: api.v1.NodePoolInventory.instance_types:type_name -> api.v1.InstanceTypeCount + 58, // 91: api.v1.NodePoolInventory.nodes:type_name -> api.v1.NodeInventoryRow + 1, // 92: api.v1.NodeInventoryRow.state:type_name -> api.v1.NodeState + 216, // 93: api.v1.NodeGroupMetric.hourly_cost:type_name -> google.type.Money + 216, // 94: api.v1.NodeGroupMetric.hourly_cpu_cost:type_name -> google.type.Money + 216, // 95: api.v1.NodeGroupMetric.hourly_memory_cost:type_name -> google.type.Money + 216, // 96: api.v1.NodeGroupMetric.hourly_gpu_cost:type_name -> google.type.Money + 206, // 97: api.v1.NodeGroupMetric.bucket_time:type_name -> google.protobuf.Timestamp + 213, // 98: api.v1.GetNodeGroupResponse.node_group:type_name -> api.v1.NodeGroup + 116, // 99: api.v1.GetClusterResponse.cluster:type_name -> api.v1.Cluster + 116, // 100: api.v1.GetClustersResponse.clusters:type_name -> api.v1.Cluster + 217, // 101: api.v1.GetClustersResponse.resource_metrics:type_name -> api.v1.ResourceMetrics + 218, // 102: api.v1.GetClustersResponse.cost_info:type_name -> api.v1.CostInfo + 214, // 103: api.v1.GetClustersResponse.node_info:type_name -> api.v1.NodeInfo + 219, // 104: api.v1.GetClustersResponse.cost_data_points:type_name -> api.v1.CostDataPoint + 220, // 105: api.v1.GetClustersResponse.resource_data_points:type_name -> api.v1.ResourceDataPoint + 116, // 106: api.v1.ListClustersResponse.clusters:type_name -> api.v1.Cluster + 210, // 107: api.v1.ListClustersResponse.pagination:type_name -> api.v1.Pagination + 221, // 108: api.v1.GetNodeResponse.node:type_name -> api.v1.Node + 217, // 109: api.v1.GetNodeResponse.resource_metrics:type_name -> api.v1.ResourceMetrics + 218, // 110: api.v1.GetNodeResponse.cost_info:type_name -> api.v1.CostInfo + 219, // 111: api.v1.GetNodeResponse.cost_data_points:type_name -> api.v1.CostDataPoint + 220, // 112: api.v1.GetNodeResponse.resource_data_points:type_name -> api.v1.ResourceDataPoint + 65, // 113: api.v1.GetNodeResponse.node_claim:type_name -> api.v1.NodeClaimRef + 2, // 114: api.v1.NodeClaimRef.not_deletable_reason:type_name -> api.v1.NodeClaimNotDeletableReason + 77, // 115: api.v1.NodeClaimRef.latest_status:type_name -> api.v1.NodeClaimDeletionStatus + 77, // 116: api.v1.RequestNodeClaimDeletionResponse.status:type_name -> api.v1.NodeClaimDeletionStatus + 77, // 117: api.v1.GetNodeClaimDeletionStatusResponse.status:type_name -> api.v1.NodeClaimDeletionStatus + 65, // 118: api.v1.NodeClaimRefEntry.ref:type_name -> api.v1.NodeClaimRef + 71, // 119: api.v1.GetNodeClaimRefsResponse.entries:type_name -> api.v1.NodeClaimRefEntry + 73, // 120: api.v1.RequestNodeClaimDeletionsRequest.items:type_name -> api.v1.NodeClaimDeletionItem + 77, // 121: api.v1.NodeClaimDeletionResult.status:type_name -> api.v1.NodeClaimDeletionStatus + 75, // 122: api.v1.RequestNodeClaimDeletionsResponse.results:type_name -> api.v1.NodeClaimDeletionResult + 3, // 123: api.v1.NodeClaimDeletionStatus.state:type_name -> api.v1.NodeClaimDeletionState + 206, // 124: api.v1.NodeClaimDeletionStatus.requested_at:type_name -> google.protobuf.Timestamp + 206, // 125: api.v1.NodeClaimDeletionStatus.first_sent_at:type_name -> google.protobuf.Timestamp + 206, // 126: api.v1.NodeClaimDeletionStatus.accepted_at:type_name -> google.protobuf.Timestamp + 206, // 127: api.v1.NodeClaimDeletionStatus.resolved_at:type_name -> google.protobuf.Timestamp + 208, // 128: api.v1.GetWorkloadsResponse.workload_items:type_name -> api.v1.WorkloadItem + 218, // 129: api.v1.GetWorkloadsResponse.cost_info:type_name -> api.v1.CostInfo + 217, // 130: api.v1.GetWorkloadsResponse.resource_metrics:type_name -> api.v1.ResourceMetrics + 222, // 131: api.v1.GetWorkloadsResponse.resource_summary:type_name -> api.v1.ResourceSummary + 208, // 132: api.v1.GetWorkloadResponse.workload_item:type_name -> api.v1.WorkloadItem + 218, // 133: api.v1.GetWorkloadResponse.cost_info:type_name -> api.v1.CostInfo + 217, // 134: api.v1.GetWorkloadResponse.resource_metrics:type_name -> api.v1.ResourceMetrics + 222, // 135: api.v1.GetWorkloadResponse.resource_summary:type_name -> api.v1.ResourceSummary + 219, // 136: api.v1.GetWorkloadResponse.cost_data_points:type_name -> api.v1.CostDataPoint + 220, // 137: api.v1.GetWorkloadResponse.resource_data_points:type_name -> api.v1.ResourceDataPoint + 206, // 138: api.v1.GetWorkloadContainerPercentilesRequest.start_time:type_name -> google.protobuf.Timestamp + 206, // 139: api.v1.GetWorkloadContainerPercentilesRequest.end_time:type_name -> google.protobuf.Timestamp + 223, // 140: api.v1.GetWorkloadContainerPercentilesResponse.containers:type_name -> api.v1.ContainerPercentileSummary + 206, // 141: api.v1.GetWorkloadContainerTimeSeriesRequest.start_time:type_name -> google.protobuf.Timestamp + 206, // 142: api.v1.GetWorkloadContainerTimeSeriesRequest.end_time:type_name -> google.protobuf.Timestamp + 224, // 143: api.v1.GetWorkloadContainerTimeSeriesResponse.containers:type_name -> api.v1.ContainerTimeSeries + 225, // 144: api.v1.GetLatestContainerRequestLimitsResponse.containers:type_name -> api.v1.ContainerRequestLimits + 226, // 145: api.v1.GetNodeGPUMigInstancesResponse.instances:type_name -> api.v1.GPUMigInstance + 206, // 146: api.v1.GetNodeGPUMigInstancesResponse.collected_at:type_name -> google.protobuf.Timestamp + 206, // 147: api.v1.GetNodeGPUMigInstanceTimeseriesRequest.start_time:type_name -> google.protobuf.Timestamp + 206, // 148: api.v1.GetNodeGPUMigInstanceTimeseriesRequest.end_time:type_name -> google.protobuf.Timestamp + 206, // 149: api.v1.GPUMigInstanceDataPoint.timestamp:type_name -> google.protobuf.Timestamp + 89, // 150: api.v1.GPUMigInstanceTimeseries.points:type_name -> api.v1.GPUMigInstanceDataPoint + 90, // 151: api.v1.GetNodeGPUMigInstanceTimeseriesResponse.instances:type_name -> api.v1.GPUMigInstanceTimeseries + 227, // 152: api.v1.GetContainerIndividualGPUMetricsResponse.containers:type_name -> api.v1.ContainerIndividualGPUMetrics + 94, // 153: api.v1.GetWorkloadLanguagesResponse.languages:type_name -> api.v1.LanguageVersions + 206, // 154: api.v1.GetForecastWorkloadsRequest.start_time:type_name -> google.protobuf.Timestamp + 206, // 155: api.v1.GetForecastWorkloadsRequest.end_time:type_name -> google.protobuf.Timestamp + 208, // 156: api.v1.GetForecastWorkloadsResponse.workload_items:type_name -> api.v1.WorkloadItem + 218, // 157: api.v1.GetForecastWorkloadsResponse.cost_info:type_name -> api.v1.CostInfo + 228, // 158: api.v1.GetForecastWorkloadsResponse.resource_metrics:type_name -> api.v1.ForecastResourceMetrics + 222, // 159: api.v1.GetForecastWorkloadsResponse.resource_summary:type_name -> api.v1.ResourceSummary + 206, // 160: api.v1.GetForecastWorkloadRequest.start_time:type_name -> google.protobuf.Timestamp + 206, // 161: api.v1.GetForecastWorkloadRequest.end_time:type_name -> google.protobuf.Timestamp + 208, // 162: api.v1.GetForecastWorkloadResponse.workload_item:type_name -> api.v1.WorkloadItem + 218, // 163: api.v1.GetForecastWorkloadResponse.cost_info:type_name -> api.v1.CostInfo + 228, // 164: api.v1.GetForecastWorkloadResponse.resource_metrics:type_name -> api.v1.ForecastResourceMetrics + 222, // 165: api.v1.GetForecastWorkloadResponse.resource_summary:type_name -> api.v1.ResourceSummary + 219, // 166: api.v1.GetForecastWorkloadResponse.cost_data_points:type_name -> api.v1.CostDataPoint + 220, // 167: api.v1.GetForecastWorkloadResponse.resource_data_points:type_name -> api.v1.ResourceDataPoint + 116, // 168: api.v1.GetClusterMetadataResponse.clusters:type_name -> api.v1.Cluster + 102, // 169: api.v1.GetAllNamespacesResponse.cluster_id_with_namespaces:type_name -> api.v1.ClusterElement + 109, // 170: api.v1.ListNamespacesByClusterResponse.namespaces:type_name -> api.v1.NamespaceItem + 209, // 171: api.v1.GetAllWorkloadNamesRequest.kinds:type_name -> api.v1.K8sObjectKind + 102, // 172: api.v1.GetAllWorkloadNamesResponse.cluster_id_with_workload_name:type_name -> api.v1.ClusterElement + 209, // 173: api.v1.GetAllWorkloadLabelsRequest.kinds:type_name -> api.v1.K8sObjectKind + 102, // 174: api.v1.GetAllWorkloadLabelsResponse.cluster_id_with_labels:type_name -> api.v1.ClusterElement + 102, // 175: api.v1.GetAllNodeGroupNamesResponse.cluster_id_with_node_group_names:type_name -> api.v1.ClusterElement + 217, // 176: api.v1.Cluster.resource_metrics:type_name -> api.v1.ResourceMetrics + 218, // 177: api.v1.Cluster.cost_info:type_name -> api.v1.CostInfo + 214, // 178: api.v1.Cluster.node_info:type_name -> api.v1.NodeInfo + 219, // 179: api.v1.Cluster.cost_data_points:type_name -> api.v1.CostDataPoint + 220, // 180: api.v1.Cluster.resource_data_points:type_name -> api.v1.ResourceDataPoint + 221, // 181: api.v1.Cluster.most_expensive_node:type_name -> api.v1.Node + 221, // 182: api.v1.Cluster.least_expensive_node:type_name -> api.v1.Node + 221, // 183: api.v1.Cluster.most_underutilized_node:type_name -> api.v1.Node + 118, // 184: api.v1.Cluster.most_underutilized_container:type_name -> api.v1.Container + 117, // 185: api.v1.Cluster.zxp_info:type_name -> api.v1.OperatorInfo + 117, // 186: api.v1.Cluster.zxp_helm_info:type_name -> api.v1.OperatorInfo + 117, // 187: api.v1.Cluster.dakr_op_info:type_name -> api.v1.OperatorInfo + 117, // 188: api.v1.Cluster.node_op_info:type_name -> api.v1.OperatorInfo + 117, // 189: api.v1.Cluster.security_op_info:type_name -> api.v1.OperatorInfo + 117, // 190: api.v1.Cluster.network_op_info:type_name -> api.v1.OperatorInfo + 217, // 191: api.v1.Container.resource_metrics:type_name -> api.v1.ResourceMetrics + 117, // 192: api.v1.GetLatestOperatorVersionResponse.zxp_info:type_name -> api.v1.OperatorInfo + 117, // 193: api.v1.GetLatestOperatorVersionResponse.zxp_helm_info:type_name -> api.v1.OperatorInfo + 117, // 194: api.v1.GetLatestOperatorVersionResponse.zxp_netmon_info:type_name -> api.v1.OperatorInfo + 117, // 195: api.v1.GetLatestOperatorVersionResponse.zxp_netmon_helm_info:type_name -> api.v1.OperatorInfo + 117, // 196: api.v1.GetLatestOperatorVersionResponse.dakr_op_info:type_name -> api.v1.OperatorInfo + 117, // 197: api.v1.GetLatestOperatorVersionResponse.dakr_op_helm_info:type_name -> api.v1.OperatorInfo + 117, // 198: api.v1.GetLatestOperatorVersionResponse.node_op_info_aws:type_name -> api.v1.OperatorInfo + 117, // 199: api.v1.GetLatestOperatorVersionResponse.node_op_info_azure:type_name -> api.v1.OperatorInfo + 117, // 200: api.v1.GetLatestOperatorVersionResponse.node_op_info_gcp:type_name -> api.v1.OperatorInfo + 117, // 201: api.v1.GetLatestOperatorVersionResponse.node_op_info_oci:type_name -> api.v1.OperatorInfo + 117, // 202: api.v1.GetLatestOperatorVersionResponse.security_op_info:type_name -> api.v1.OperatorInfo + 117, // 203: api.v1.GetLatestOperatorVersionResponse.security_op_helm_info:type_name -> api.v1.OperatorInfo + 117, // 204: api.v1.GetLatestOperatorVersionResponse.trezr_info:type_name -> api.v1.OperatorInfo + 117, // 205: api.v1.GetLatestOperatorVersionResponse.trezr_helm_info:type_name -> api.v1.OperatorInfo + 117, // 206: api.v1.GetLatestOperatorVersionResponse.snapshot_helm_info:type_name -> api.v1.OperatorInfo + 5, // 207: api.v1.GalaxyGetWorkloadPerspectiveRequest.group_by:type_name -> api.v1.GalaxyPOVWorkloadGroupBy + 124, // 208: api.v1.GalaxyGetWorkloadPerspectiveRequest.datapoint_opts:type_name -> api.v1.PerspectiveDatapointOpts + 123, // 209: api.v1.GalaxyGetWorkloadPerspectiveResponse.groupings:type_name -> api.v1.GalaxyWorkloadGroup + 186, // 210: api.v1.GalaxyGetWorkloadPerspectiveResponse.clusters:type_name -> api.v1.GalaxyGetWorkloadPerspectiveResponse.ClustersEntry + 187, // 211: api.v1.GalaxyGetWorkloadPerspectiveResponse.workloads:type_name -> api.v1.GalaxyGetWorkloadPerspectiveResponse.WorkloadsEntry + 218, // 212: api.v1.GalaxyWorkloadGroup.cost_info:type_name -> api.v1.CostInfo + 217, // 213: api.v1.GalaxyWorkloadGroup.resource_metrics:type_name -> api.v1.ResourceMetrics + 222, // 214: api.v1.GalaxyWorkloadGroup.resource_summary:type_name -> api.v1.ResourceSummary + 209, // 215: api.v1.ListAuditLogsRequest.workload_type:type_name -> api.v1.K8sObjectKind + 206, // 216: api.v1.ListAuditLogsRequest.start_time:type_name -> google.protobuf.Timestamp + 206, // 217: api.v1.ListAuditLogsRequest.end_time:type_name -> google.protobuf.Timestamp + 210, // 218: api.v1.ListAuditLogsRequest.pagination:type_name -> api.v1.Pagination + 229, // 219: api.v1.ListAuditLogsResponse.logs:type_name -> api.v1.AuditLogEntry + 210, // 220: api.v1.ListAuditLogsResponse.pagination:type_name -> api.v1.Pagination + 209, // 221: api.v1.ListAuditLogOriginatorsRequest.workload_type:type_name -> api.v1.K8sObjectKind + 206, // 222: api.v1.ListAuditLogOriginatorsRequest.start_time:type_name -> google.protobuf.Timestamp + 206, // 223: api.v1.ListAuditLogOriginatorsRequest.end_time:type_name -> google.protobuf.Timestamp + 132, // 224: api.v1.SendWeeklySummaryEmailRequest.request:type_name -> api.v1.SendWeeklySummaryEmailRequestData + 206, // 225: api.v1.GetClustersNodeInfoRequest.start_time:type_name -> google.protobuf.Timestamp + 206, // 226: api.v1.GetClustersNodeInfoRequest.end_time:type_name -> google.protobuf.Timestamp + 214, // 227: api.v1.GetClustersNodeInfoResponse.node_info:type_name -> api.v1.NodeInfo + 221, // 228: api.v1.GetClustersNodeInfoResponse.most_expensive_node:type_name -> api.v1.Node + 221, // 229: api.v1.GetClustersNodeInfoResponse.least_expensive_node:type_name -> api.v1.Node + 221, // 230: api.v1.GetClustersNodeInfoResponse.most_underutilized_node:type_name -> api.v1.Node + 138, // 231: api.v1.SearchK8sResourcesResponse.results:type_name -> api.v1.K8sSearchResult + 141, // 232: api.v1.SearchK8sWorkloadsResponse.results:type_name -> api.v1.K8sWorkloadSearchResult + 188, // 233: api.v1.MetadataForWorkloadsRequest.workload_uid_to_kind:type_name -> api.v1.MetadataForWorkloadsRequest.WorkloadUidToKindEntry + 189, // 234: api.v1.MetadataForWorkloadsResponse.workload_uid_to_metadata:type_name -> api.v1.MetadataForWorkloadsResponse.WorkloadUidToMetadataEntry + 116, // 235: api.v1.AddClusterTagsResponse.cluster:type_name -> api.v1.Cluster + 116, // 236: api.v1.RemoveClusterTagsResponse.cluster:type_name -> api.v1.Cluster + 149, // 237: api.v1.ListTagsResponse.tags:type_name -> api.v1.TagSummary + 190, // 238: api.v1.WorkloadMetadata.pod_uid_to_node_metadata:type_name -> api.v1.WorkloadMetadata.PodUidToNodeMetadataEntry + 191, // 239: api.v1.WorkloadMetadata.pod_uid_to_pod_metadata:type_name -> api.v1.WorkloadMetadata.PodUidToPodMetadataEntry + 209, // 240: api.v1.WorkloadMetadata.kind:type_name -> api.v1.K8sObjectKind + 209, // 241: api.v1.GetRelatedResourcesRequest.kind:type_name -> api.v1.K8sObjectKind + 157, // 242: api.v1.GetWorkloadPodHistoryResponse.replica_sets:type_name -> api.v1.ReplicaSetHistory + 158, // 243: api.v1.GetWorkloadPodHistoryResponse.direct_pods:type_name -> api.v1.PodHistory + 156, // 244: api.v1.GetWorkloadPodHistoryResponse.jobs:type_name -> api.v1.JobHistory + 206, // 245: api.v1.JobHistory.created_at:type_name -> google.protobuf.Timestamp + 206, // 246: api.v1.JobHistory.deleted_at:type_name -> google.protobuf.Timestamp + 158, // 247: api.v1.JobHistory.pods:type_name -> api.v1.PodHistory + 206, // 248: api.v1.ReplicaSetHistory.created_at:type_name -> google.protobuf.Timestamp + 206, // 249: api.v1.ReplicaSetHistory.deleted_at:type_name -> google.protobuf.Timestamp + 158, // 250: api.v1.ReplicaSetHistory.pods:type_name -> api.v1.PodHistory + 206, // 251: api.v1.PodHistory.created_at:type_name -> google.protobuf.Timestamp + 206, // 252: api.v1.PodHistory.deleted_at:type_name -> google.protobuf.Timestamp + 160, // 253: api.v1.GetRelatedResourcesResponse.relations:type_name -> api.v1.K8sRelatedResource + 161, // 254: api.v1.GetRelatedResourcesResponse.nodes:type_name -> api.v1.K8sResourceNode + 162, // 255: api.v1.GetRelatedResourcesResponse.edges:type_name -> api.v1.K8sResourceEdge + 6, // 256: api.v1.GetClusterTypeResponse.cluster_type:type_name -> api.v1.ClusterType + 206, // 257: api.v1.GetMigPartedConfigResponse.last_seen:type_name -> google.protobuf.Timestamp + 206, // 258: api.v1.GetWorkloadsStatsRequest.start_time:type_name -> google.protobuf.Timestamp + 206, // 259: api.v1.GetWorkloadsStatsRequest.end_time:type_name -> google.protobuf.Timestamp + 206, // 260: api.v1.DailyUtilizationRequest.start_time:type_name -> google.protobuf.Timestamp + 206, // 261: api.v1.DailyUtilizationRequest.end_time:type_name -> google.protobuf.Timestamp + 194, // 262: api.v1.DailyUtilizationResponse.cluster_id_to_daily_total_core_minutes:type_name -> api.v1.DailyUtilizationResponse.ClusterIdToDailyTotalCoreMinutesEntry + 195, // 263: api.v1.DailyUtilizationResponse.cluster_id_to_meta:type_name -> api.v1.DailyUtilizationResponse.ClusterIdToMetaEntry + 206, // 264: api.v1.DailyUtilizationInstanceTypeRequest.start_time:type_name -> google.protobuf.Timestamp + 206, // 265: api.v1.DailyUtilizationInstanceTypeRequest.end_time:type_name -> google.protobuf.Timestamp + 198, // 266: api.v1.DailyUtilizationInstanceTypeResponse.cluster_id_to_datapoints:type_name -> api.v1.DailyUtilizationInstanceTypeResponse.ClusterIdToDatapointsEntry + 199, // 267: api.v1.DailyUtilizationInstanceTypeResponse.cluster_id_to_meta:type_name -> api.v1.DailyUtilizationInstanceTypeResponse.ClusterIdToMetaEntry + 206, // 268: api.v1.DailyUtilizationNodeTypeRequest.start_time:type_name -> google.protobuf.Timestamp + 206, // 269: api.v1.DailyUtilizationNodeTypeRequest.end_time:type_name -> google.protobuf.Timestamp + 203, // 270: api.v1.DailyUtilizationNodeTypeResponse.cluster_id_to_datapoints:type_name -> api.v1.DailyUtilizationNodeTypeResponse.ClusterIdToDatapointsEntry + 204, // 271: api.v1.DailyUtilizationNodeTypeResponse.cluster_id_to_meta:type_name -> api.v1.DailyUtilizationNodeTypeResponse.ClusterIdToMetaEntry + 230, // 272: api.v1.LookupNodeInstanceResponse.dynamic_instance:type_name -> api.v1.Instance + 230, // 273: api.v1.LookupNodeInstanceResponse.cached_instance:type_name -> api.v1.Instance + 176, // 274: api.v1.LookupNodeInstanceResponse.lookup_params:type_name -> api.v1.InstanceLookupParams + 213, // 275: api.v1.GetAllNodeGroupsResponse.NodeGroupMapEntry.value:type_name -> api.v1.NodeGroup + 59, // 276: api.v1.GetNodeGroupsUtilizationResponse.ListNodeGroupMetrics.node_group_metrics:type_name -> api.v1.NodeGroupMetric + 181, // 277: api.v1.GetNodeGroupsUtilizationResponse.NodeGroupToUtilMetricsEntry.value:type_name -> api.v1.GetNodeGroupsUtilizationResponse.ListNodeGroupMetrics + 181, // 278: api.v1.GetNodeGroupsUtilizationResponse.InstanceTypeToUtilMetricsEntry.value:type_name -> api.v1.GetNodeGroupsUtilizationResponse.ListNodeGroupMetrics + 181, // 279: api.v1.GetNodeGroupsUtilizationResponse.ReservationTypeToUtilMetricsEntry.value:type_name -> api.v1.GetNodeGroupsUtilizationResponse.ListNodeGroupMetrics + 181, // 280: api.v1.GetNodeGroupsUtilizationResponse.LogicalAzToUtilMetricsEntry.value:type_name -> api.v1.GetNodeGroupsUtilizationResponse.ListNodeGroupMetrics + 116, // 281: api.v1.GalaxyGetWorkloadPerspectiveResponse.ClustersEntry.value:type_name -> api.v1.Cluster + 208, // 282: api.v1.GalaxyGetWorkloadPerspectiveResponse.WorkloadsEntry.value:type_name -> api.v1.WorkloadItem + 209, // 283: api.v1.MetadataForWorkloadsRequest.WorkloadUidToKindEntry.value:type_name -> api.v1.K8sObjectKind + 151, // 284: api.v1.MetadataForWorkloadsResponse.WorkloadUidToMetadataEntry.value:type_name -> api.v1.WorkloadMetadata + 153, // 285: api.v1.WorkloadMetadata.PodUidToNodeMetadataEntry.value:type_name -> api.v1.NodeMetadata + 152, // 286: api.v1.WorkloadMetadata.PodUidToPodMetadataEntry.value:type_name -> api.v1.PodMetadata + 193, // 287: api.v1.DailyUtilizationResponse.Datapoints.datapoints:type_name -> api.v1.DailyUtilizationResponse.Datapoint + 192, // 288: api.v1.DailyUtilizationResponse.ClusterIdToDailyTotalCoreMinutesEntry.value:type_name -> api.v1.DailyUtilizationResponse.Datapoints + 116, // 289: api.v1.DailyUtilizationResponse.ClusterIdToMetaEntry.value:type_name -> api.v1.Cluster + 197, // 290: api.v1.DailyUtilizationInstanceTypeResponse.Datapoints.datapoints:type_name -> api.v1.DailyUtilizationInstanceTypeResponse.Datapoint + 200, // 291: api.v1.DailyUtilizationInstanceTypeResponse.Datapoint.instance_type_to_daily_total_core_minutes:type_name -> api.v1.DailyUtilizationInstanceTypeResponse.Datapoint.InstanceTypeToDailyTotalCoreMinutesEntry + 196, // 292: api.v1.DailyUtilizationInstanceTypeResponse.ClusterIdToDatapointsEntry.value:type_name -> api.v1.DailyUtilizationInstanceTypeResponse.Datapoints + 116, // 293: api.v1.DailyUtilizationInstanceTypeResponse.ClusterIdToMetaEntry.value:type_name -> api.v1.Cluster + 202, // 294: api.v1.DailyUtilizationNodeTypeResponse.Datapoints.datapoints:type_name -> api.v1.DailyUtilizationNodeTypeResponse.Datapoint + 205, // 295: api.v1.DailyUtilizationNodeTypeResponse.Datapoint.node_type_to_daily_total_core_minutes:type_name -> api.v1.DailyUtilizationNodeTypeResponse.Datapoint.NodeTypeToDailyTotalCoreMinutesEntry + 201, // 296: api.v1.DailyUtilizationNodeTypeResponse.ClusterIdToDatapointsEntry.value:type_name -> api.v1.DailyUtilizationNodeTypeResponse.Datapoints + 116, // 297: api.v1.DailyUtilizationNodeTypeResponse.ClusterIdToMetaEntry.value:type_name -> api.v1.Cluster + 167, // 298: api.v1.K8SService.GetWorkloadsStats:input_type -> api.v1.GetWorkloadsStatsRequest + 26, // 299: api.v1.K8SService.GetClusters:input_type -> api.v1.GetClustersRequest + 27, // 300: api.v1.K8SService.ListClusters:input_type -> api.v1.ListClustersRequest + 28, // 301: api.v1.K8SService.GetCluster:input_type -> api.v1.GetClusterRequest + 9, // 302: api.v1.K8SService.GetClusterMetadata:input_type -> api.v1.GetClusterMetadataRequest + 103, // 303: api.v1.K8SService.GetAllNamespaces:input_type -> api.v1.GetAllNamespacesRequest + 105, // 304: api.v1.K8SService.SearchNamespacesByCluster:input_type -> api.v1.SearchNamespacesByClusterRequest + 107, // 305: api.v1.K8SService.ListNamespacesByCluster:input_type -> api.v1.ListNamespacesByClusterRequest + 110, // 306: api.v1.K8SService.GetAllWorkloadNames:input_type -> api.v1.GetAllWorkloadNamesRequest + 112, // 307: api.v1.K8SService.GetAllWorkloadLabels:input_type -> api.v1.GetAllWorkloadLabelsRequest + 114, // 308: api.v1.K8SService.GetAllNodeGroupNames:input_type -> api.v1.GetAllNodeGroupNamesRequest + 142, // 309: api.v1.K8SService.MetadataForWorkloads:input_type -> api.v1.MetadataForWorkloadsRequest + 40, // 310: api.v1.K8SService.GetNodeGroups:input_type -> api.v1.GetNodeGroupsRequest + 41, // 311: api.v1.K8SService.GetAllNodeGroups:input_type -> api.v1.GetAllNodeGroupsRequest + 50, // 312: api.v1.K8SService.GetNodeGroupsUtilization:input_type -> api.v1.GetNodeGroupsUtilizationRequest + 52, // 313: api.v1.K8SService.GetClusterNodeInventory:input_type -> api.v1.GetClusterNodeInventoryRequest + 45, // 314: api.v1.K8SService.GetClusterNodeFilterIndex:input_type -> api.v1.GetClusterNodeFilterIndexRequest + 10, // 315: api.v1.K8SService.GetNodeGroup:input_type -> api.v1.GetNodeGroupRequest + 7, // 316: api.v1.K8SService.GetNode:input_type -> api.v1.GetNodeRequest + 66, // 317: api.v1.K8SService.RequestNodeClaimDeletion:input_type -> api.v1.RequestNodeClaimDeletionRequest + 68, // 318: api.v1.K8SService.GetNodeClaimDeletionStatus:input_type -> api.v1.GetNodeClaimDeletionStatusRequest + 70, // 319: api.v1.K8SService.GetNodeClaimRefs:input_type -> api.v1.GetNodeClaimRefsRequest + 74, // 320: api.v1.K8SService.RequestNodeClaimDeletions:input_type -> api.v1.RequestNodeClaimDeletionsRequest + 11, // 321: api.v1.K8SService.GetWorkloads:input_type -> api.v1.GetWorkloadsRequest + 12, // 322: api.v1.K8SService.ListWorkloadsPaginated:input_type -> api.v1.ListWorkloadsPaginatedRequest + 13, // 323: api.v1.K8SService.GetWorkloadsSummary:input_type -> api.v1.GetWorkloadsSummaryRequest + 17, // 324: api.v1.K8SService.GetWorkloadRequestTrends:input_type -> api.v1.GetWorkloadRequestTrendsRequest + 25, // 325: api.v1.K8SService.GetWorkload:input_type -> api.v1.GetWorkloadRequest + 80, // 326: api.v1.K8SService.GetWorkloadContainerPercentiles:input_type -> api.v1.GetWorkloadContainerPercentilesRequest + 82, // 327: api.v1.K8SService.GetWorkloadContainerTimeSeries:input_type -> api.v1.GetWorkloadContainerTimeSeriesRequest + 84, // 328: api.v1.K8SService.GetLatestContainerRequestLimits:input_type -> api.v1.GetLatestContainerRequestLimitsRequest + 86, // 329: api.v1.K8SService.GetNodeGPUMigInstances:input_type -> api.v1.GetNodeGPUMigInstancesRequest + 88, // 330: api.v1.K8SService.GetNodeGPUMigInstanceTimeseries:input_type -> api.v1.GetNodeGPUMigInstanceTimeseriesRequest + 92, // 331: api.v1.K8SService.GetContainerIndividualGPUMetrics:input_type -> api.v1.GetContainerIndividualGPUMetricsRequest + 95, // 332: api.v1.K8SService.GetWorkloadLanguages:input_type -> api.v1.GetWorkloadLanguagesRequest + 97, // 333: api.v1.K8SService.GetForecastWorkloads:input_type -> api.v1.GetForecastWorkloadsRequest + 99, // 334: api.v1.K8SService.GetForecastWorkload:input_type -> api.v1.GetForecastWorkloadRequest + 22, // 335: api.v1.K8SService.GetResources:input_type -> api.v1.GetResourcesRequest + 34, // 336: api.v1.K8SService.GetResourceFilterOptions:input_type -> api.v1.GetResourceFilterOptionsRequest + 23, // 337: api.v1.K8SService.GetPods:input_type -> api.v1.GetPodsRequest + 119, // 338: api.v1.K8SService.GetLatestOperatorVersion:input_type -> api.v1.GetLatestOperatorVersionRequest + 121, // 339: api.v1.K8SService.GalaxyGetWorkloadPerspective:input_type -> api.v1.GalaxyGetWorkloadPerspectiveRequest + 125, // 340: api.v1.K8SService.ListAuditLogs:input_type -> api.v1.ListAuditLogsRequest + 127, // 341: api.v1.K8SService.ListAuditLogOriginators:input_type -> api.v1.ListAuditLogOriginatorsRequest + 129, // 342: api.v1.K8SService.SendWorkloadEmail:input_type -> api.v1.SendWorkloadEmailRequest + 131, // 343: api.v1.K8SService.SendWeeklySummaryEmail:input_type -> api.v1.SendWeeklySummaryEmailRequest + 134, // 344: api.v1.K8SService.GetClustersNodeInfo:input_type -> api.v1.GetClustersNodeInfoRequest + 136, // 345: api.v1.K8SService.SearchK8sResources:input_type -> api.v1.SearchK8sResourcesRequest + 139, // 346: api.v1.K8SService.SearchK8sWorkloads:input_type -> api.v1.SearchK8sWorkloadsRequest + 163, // 347: api.v1.K8SService.GetClusterType:input_type -> api.v1.GetClusterTypeRequest + 154, // 348: api.v1.K8SService.GetRelationsForKind:input_type -> api.v1.GetRelatedResourcesRequest + 175, // 349: api.v1.K8SService.LookupNodeInstance:input_type -> api.v1.LookupNodeInstanceRequest + 8, // 350: api.v1.K8SService.GetWorkloadPodHistory:input_type -> api.v1.GetWorkloadPodHistoryRequest + 144, // 351: api.v1.K8SService.AddClusterTags:input_type -> api.v1.AddClusterTagsRequest + 146, // 352: api.v1.K8SService.RemoveClusterTags:input_type -> api.v1.RemoveClusterTagsRequest + 148, // 353: api.v1.K8SService.ListTags:input_type -> api.v1.ListTagsRequest + 165, // 354: api.v1.K8SService.GetMigPartedConfig:input_type -> api.v1.GetMigPartedConfigRequest + 36, // 355: api.v1.ClusterMutationService.CreateCluster:input_type -> api.v1.CreateClusterRequest + 29, // 356: api.v1.ClusterMutationService.DeleteCluster:input_type -> api.v1.DeleteClusterRequest + 31, // 357: api.v1.ClusterMutationService.UpdateCluster:input_type -> api.v1.UpdateClusterRequest + 38, // 358: api.v1.ClusterMutationService.ResetClusterToken:input_type -> api.v1.ResetClusterTokenRequest + 169, // 359: api.v1.UtilizationService.DailyUtilization:input_type -> api.v1.DailyUtilizationRequest + 171, // 360: api.v1.UtilizationService.DailyUtilizationInstanceType:input_type -> api.v1.DailyUtilizationInstanceTypeRequest + 173, // 361: api.v1.UtilizationService.DailyUtilizationNodeType:input_type -> api.v1.DailyUtilizationNodeTypeRequest + 168, // 362: api.v1.K8SService.GetWorkloadsStats:output_type -> api.v1.GetWorkloadsStatsResponse + 62, // 363: api.v1.K8SService.GetClusters:output_type -> api.v1.GetClustersResponse + 63, // 364: api.v1.K8SService.ListClusters:output_type -> api.v1.ListClustersResponse + 61, // 365: api.v1.K8SService.GetCluster:output_type -> api.v1.GetClusterResponse + 101, // 366: api.v1.K8SService.GetClusterMetadata:output_type -> api.v1.GetClusterMetadataResponse + 104, // 367: api.v1.K8SService.GetAllNamespaces:output_type -> api.v1.GetAllNamespacesResponse + 106, // 368: api.v1.K8SService.SearchNamespacesByCluster:output_type -> api.v1.SearchNamespacesByClusterResponse + 108, // 369: api.v1.K8SService.ListNamespacesByCluster:output_type -> api.v1.ListNamespacesByClusterResponse + 111, // 370: api.v1.K8SService.GetAllWorkloadNames:output_type -> api.v1.GetAllWorkloadNamesResponse + 113, // 371: api.v1.K8SService.GetAllWorkloadLabels:output_type -> api.v1.GetAllWorkloadLabelsResponse + 115, // 372: api.v1.K8SService.GetAllNodeGroupNames:output_type -> api.v1.GetAllNodeGroupNamesResponse + 143, // 373: api.v1.K8SService.MetadataForWorkloads:output_type -> api.v1.MetadataForWorkloadsResponse + 42, // 374: api.v1.K8SService.GetNodeGroups:output_type -> api.v1.GetNodeGroupsResponse + 43, // 375: api.v1.K8SService.GetAllNodeGroups:output_type -> api.v1.GetAllNodeGroupsResponse + 51, // 376: api.v1.K8SService.GetNodeGroupsUtilization:output_type -> api.v1.GetNodeGroupsUtilizationResponse + 53, // 377: api.v1.K8SService.GetClusterNodeInventory:output_type -> api.v1.GetClusterNodeInventoryResponse + 49, // 378: api.v1.K8SService.GetClusterNodeFilterIndex:output_type -> api.v1.GetClusterNodeFilterIndexResponse + 60, // 379: api.v1.K8SService.GetNodeGroup:output_type -> api.v1.GetNodeGroupResponse + 64, // 380: api.v1.K8SService.GetNode:output_type -> api.v1.GetNodeResponse + 67, // 381: api.v1.K8SService.RequestNodeClaimDeletion:output_type -> api.v1.RequestNodeClaimDeletionResponse + 69, // 382: api.v1.K8SService.GetNodeClaimDeletionStatus:output_type -> api.v1.GetNodeClaimDeletionStatusResponse + 72, // 383: api.v1.K8SService.GetNodeClaimRefs:output_type -> api.v1.GetNodeClaimRefsResponse + 76, // 384: api.v1.K8SService.RequestNodeClaimDeletions:output_type -> api.v1.RequestNodeClaimDeletionsResponse + 78, // 385: api.v1.K8SService.GetWorkloads:output_type -> api.v1.GetWorkloadsResponse + 21, // 386: api.v1.K8SService.ListWorkloadsPaginated:output_type -> api.v1.ListWorkloadsPaginatedResponse + 16, // 387: api.v1.K8SService.GetWorkloadsSummary:output_type -> api.v1.GetWorkloadsSummaryResponse + 20, // 388: api.v1.K8SService.GetWorkloadRequestTrends:output_type -> api.v1.GetWorkloadRequestTrendsResponse + 79, // 389: api.v1.K8SService.GetWorkload:output_type -> api.v1.GetWorkloadResponse + 81, // 390: api.v1.K8SService.GetWorkloadContainerPercentiles:output_type -> api.v1.GetWorkloadContainerPercentilesResponse + 83, // 391: api.v1.K8SService.GetWorkloadContainerTimeSeries:output_type -> api.v1.GetWorkloadContainerTimeSeriesResponse + 85, // 392: api.v1.K8SService.GetLatestContainerRequestLimits:output_type -> api.v1.GetLatestContainerRequestLimitsResponse + 87, // 393: api.v1.K8SService.GetNodeGPUMigInstances:output_type -> api.v1.GetNodeGPUMigInstancesResponse + 91, // 394: api.v1.K8SService.GetNodeGPUMigInstanceTimeseries:output_type -> api.v1.GetNodeGPUMigInstanceTimeseriesResponse + 93, // 395: api.v1.K8SService.GetContainerIndividualGPUMetrics:output_type -> api.v1.GetContainerIndividualGPUMetricsResponse + 96, // 396: api.v1.K8SService.GetWorkloadLanguages:output_type -> api.v1.GetWorkloadLanguagesResponse + 98, // 397: api.v1.K8SService.GetForecastWorkloads:output_type -> api.v1.GetForecastWorkloadsResponse + 100, // 398: api.v1.K8SService.GetForecastWorkload:output_type -> api.v1.GetForecastWorkloadResponse + 33, // 399: api.v1.K8SService.GetResources:output_type -> api.v1.GetResourcesResponse + 35, // 400: api.v1.K8SService.GetResourceFilterOptions:output_type -> api.v1.GetResourceFilterOptionsResponse + 24, // 401: api.v1.K8SService.GetPods:output_type -> api.v1.GetPodsResponse + 120, // 402: api.v1.K8SService.GetLatestOperatorVersion:output_type -> api.v1.GetLatestOperatorVersionResponse + 122, // 403: api.v1.K8SService.GalaxyGetWorkloadPerspective:output_type -> api.v1.GalaxyGetWorkloadPerspectiveResponse + 126, // 404: api.v1.K8SService.ListAuditLogs:output_type -> api.v1.ListAuditLogsResponse + 128, // 405: api.v1.K8SService.ListAuditLogOriginators:output_type -> api.v1.ListAuditLogOriginatorsResponse + 130, // 406: api.v1.K8SService.SendWorkloadEmail:output_type -> api.v1.SendWorkloadEmailResponse + 133, // 407: api.v1.K8SService.SendWeeklySummaryEmail:output_type -> api.v1.SendWeeklySummaryEmailResponse + 135, // 408: api.v1.K8SService.GetClustersNodeInfo:output_type -> api.v1.GetClustersNodeInfoResponse + 137, // 409: api.v1.K8SService.SearchK8sResources:output_type -> api.v1.SearchK8sResourcesResponse + 140, // 410: api.v1.K8SService.SearchK8sWorkloads:output_type -> api.v1.SearchK8sWorkloadsResponse + 164, // 411: api.v1.K8SService.GetClusterType:output_type -> api.v1.GetClusterTypeResponse + 159, // 412: api.v1.K8SService.GetRelationsForKind:output_type -> api.v1.GetRelatedResourcesResponse + 177, // 413: api.v1.K8SService.LookupNodeInstance:output_type -> api.v1.LookupNodeInstanceResponse + 155, // 414: api.v1.K8SService.GetWorkloadPodHistory:output_type -> api.v1.GetWorkloadPodHistoryResponse + 145, // 415: api.v1.K8SService.AddClusterTags:output_type -> api.v1.AddClusterTagsResponse + 147, // 416: api.v1.K8SService.RemoveClusterTags:output_type -> api.v1.RemoveClusterTagsResponse + 150, // 417: api.v1.K8SService.ListTags:output_type -> api.v1.ListTagsResponse + 166, // 418: api.v1.K8SService.GetMigPartedConfig:output_type -> api.v1.GetMigPartedConfigResponse + 37, // 419: api.v1.ClusterMutationService.CreateCluster:output_type -> api.v1.CreateClusterResponse + 30, // 420: api.v1.ClusterMutationService.DeleteCluster:output_type -> api.v1.DeleteClusterResponse + 32, // 421: api.v1.ClusterMutationService.UpdateCluster:output_type -> api.v1.UpdateClusterResponse + 39, // 422: api.v1.ClusterMutationService.ResetClusterToken:output_type -> api.v1.ResetClusterTokenResponse + 170, // 423: api.v1.UtilizationService.DailyUtilization:output_type -> api.v1.DailyUtilizationResponse + 172, // 424: api.v1.UtilizationService.DailyUtilizationInstanceType:output_type -> api.v1.DailyUtilizationInstanceTypeResponse + 174, // 425: api.v1.UtilizationService.DailyUtilizationNodeType:output_type -> api.v1.DailyUtilizationNodeTypeResponse + 362, // [362:426] is the sub-list for method output_type + 298, // [298:362] is the sub-list for method input_type + 298, // [298:298] is the sub-list for extension type_name + 298, // [298:298] is the sub-list for extension extendee + 0, // [0:298] is the sub-list for field type_name } func init() { file_api_v1_k8s_proto_init() } @@ -13833,8 +17455,224 @@ func file_api_v1_k8s_proto_init() { return nil } } - file_api_v1_k8s_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetWorkloadPodHistoryRequest); i { + file_api_v1_k8s_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetWorkloadPodHistoryRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_api_v1_k8s_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetClusterMetadataRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_api_v1_k8s_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetNodeGroupRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_api_v1_k8s_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetWorkloadsRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_api_v1_k8s_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ListWorkloadsPaginatedRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_api_v1_k8s_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetWorkloadsSummaryRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_api_v1_k8s_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*WorkloadMetricEntry); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_api_v1_k8s_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*WorkloadMetricBreakdown); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_api_v1_k8s_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetWorkloadsSummaryResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_api_v1_k8s_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetWorkloadRequestTrendsRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_api_v1_k8s_proto_msgTypes[11].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*RequestTrendPoint); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_api_v1_k8s_proto_msgTypes[12].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*RequestTrendSeries); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_api_v1_k8s_proto_msgTypes[13].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetWorkloadRequestTrendsResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_api_v1_k8s_proto_msgTypes[14].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ListWorkloadsPaginatedResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_api_v1_k8s_proto_msgTypes[15].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetResourcesRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_api_v1_k8s_proto_msgTypes[16].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetPodsRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_api_v1_k8s_proto_msgTypes[17].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetPodsResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_api_v1_k8s_proto_msgTypes[18].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetWorkloadRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_api_v1_k8s_proto_msgTypes[19].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetClustersRequest); i { case 0: return &v.state case 1: @@ -13845,8 +17683,8 @@ func file_api_v1_k8s_proto_init() { return nil } } - file_api_v1_k8s_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetClusterMetadataRequest); i { + file_api_v1_k8s_proto_msgTypes[20].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ListClustersRequest); i { case 0: return &v.state case 1: @@ -13857,8 +17695,8 @@ func file_api_v1_k8s_proto_init() { return nil } } - file_api_v1_k8s_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetNodeGroupRequest); i { + file_api_v1_k8s_proto_msgTypes[21].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetClusterRequest); i { case 0: return &v.state case 1: @@ -13869,8 +17707,8 @@ func file_api_v1_k8s_proto_init() { return nil } } - file_api_v1_k8s_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetWorkloadsRequest); i { + file_api_v1_k8s_proto_msgTypes[22].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*DeleteClusterRequest); i { case 0: return &v.state case 1: @@ -13881,8 +17719,8 @@ func file_api_v1_k8s_proto_init() { return nil } } - file_api_v1_k8s_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ListWorkloadsPaginatedRequest); i { + file_api_v1_k8s_proto_msgTypes[23].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*DeleteClusterResponse); i { case 0: return &v.state case 1: @@ -13893,8 +17731,8 @@ func file_api_v1_k8s_proto_init() { return nil } } - file_api_v1_k8s_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetWorkloadsSummaryRequest); i { + file_api_v1_k8s_proto_msgTypes[24].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*UpdateClusterRequest); i { case 0: return &v.state case 1: @@ -13905,8 +17743,8 @@ func file_api_v1_k8s_proto_init() { return nil } } - file_api_v1_k8s_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*WorkloadMetricEntry); i { + file_api_v1_k8s_proto_msgTypes[25].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*UpdateClusterResponse); i { case 0: return &v.state case 1: @@ -13917,8 +17755,8 @@ func file_api_v1_k8s_proto_init() { return nil } } - file_api_v1_k8s_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*WorkloadMetricBreakdown); i { + file_api_v1_k8s_proto_msgTypes[26].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetResourcesResponse); i { case 0: return &v.state case 1: @@ -13929,8 +17767,8 @@ func file_api_v1_k8s_proto_init() { return nil } } - file_api_v1_k8s_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetWorkloadsSummaryResponse); i { + file_api_v1_k8s_proto_msgTypes[27].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetResourceFilterOptionsRequest); i { case 0: return &v.state case 1: @@ -13941,8 +17779,8 @@ func file_api_v1_k8s_proto_init() { return nil } } - file_api_v1_k8s_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ListWorkloadsPaginatedResponse); i { + file_api_v1_k8s_proto_msgTypes[28].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetResourceFilterOptionsResponse); i { case 0: return &v.state case 1: @@ -13953,8 +17791,8 @@ func file_api_v1_k8s_proto_init() { return nil } } - file_api_v1_k8s_proto_msgTypes[11].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetResourcesRequest); i { + file_api_v1_k8s_proto_msgTypes[29].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*CreateClusterRequest); i { case 0: return &v.state case 1: @@ -13965,8 +17803,8 @@ func file_api_v1_k8s_proto_init() { return nil } } - file_api_v1_k8s_proto_msgTypes[12].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetPodsRequest); i { + file_api_v1_k8s_proto_msgTypes[30].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*CreateClusterResponse); i { case 0: return &v.state case 1: @@ -13977,8 +17815,8 @@ func file_api_v1_k8s_proto_init() { return nil } } - file_api_v1_k8s_proto_msgTypes[13].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetPodsResponse); i { + file_api_v1_k8s_proto_msgTypes[31].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ResetClusterTokenRequest); i { case 0: return &v.state case 1: @@ -13989,8 +17827,8 @@ func file_api_v1_k8s_proto_init() { return nil } } - file_api_v1_k8s_proto_msgTypes[14].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetWorkloadRequest); i { + file_api_v1_k8s_proto_msgTypes[32].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ResetClusterTokenResponse); i { case 0: return &v.state case 1: @@ -14001,8 +17839,8 @@ func file_api_v1_k8s_proto_init() { return nil } } - file_api_v1_k8s_proto_msgTypes[15].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetClustersRequest); i { + file_api_v1_k8s_proto_msgTypes[33].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetNodeGroupsRequest); i { case 0: return &v.state case 1: @@ -14013,8 +17851,8 @@ func file_api_v1_k8s_proto_init() { return nil } } - file_api_v1_k8s_proto_msgTypes[16].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ListClustersRequest); i { + file_api_v1_k8s_proto_msgTypes[34].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetAllNodeGroupsRequest); i { case 0: return &v.state case 1: @@ -14025,8 +17863,8 @@ func file_api_v1_k8s_proto_init() { return nil } } - file_api_v1_k8s_proto_msgTypes[17].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetClusterRequest); i { + file_api_v1_k8s_proto_msgTypes[35].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetNodeGroupsResponse); i { case 0: return &v.state case 1: @@ -14037,8 +17875,8 @@ func file_api_v1_k8s_proto_init() { return nil } } - file_api_v1_k8s_proto_msgTypes[18].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*DeleteClusterRequest); i { + file_api_v1_k8s_proto_msgTypes[36].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetAllNodeGroupsResponse); i { case 0: return &v.state case 1: @@ -14049,8 +17887,8 @@ func file_api_v1_k8s_proto_init() { return nil } } - file_api_v1_k8s_proto_msgTypes[19].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*DeleteClusterResponse); i { + file_api_v1_k8s_proto_msgTypes[37].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*NodeGroupSet); i { case 0: return &v.state case 1: @@ -14061,8 +17899,8 @@ func file_api_v1_k8s_proto_init() { return nil } } - file_api_v1_k8s_proto_msgTypes[20].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*UpdateClusterRequest); i { + file_api_v1_k8s_proto_msgTypes[38].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetClusterNodeFilterIndexRequest); i { case 0: return &v.state case 1: @@ -14073,8 +17911,188 @@ func file_api_v1_k8s_proto_init() { return nil } } - file_api_v1_k8s_proto_msgTypes[21].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*UpdateClusterResponse); i { + file_api_v1_k8s_proto_msgTypes[39].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*NodeLabelSet); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_api_v1_k8s_proto_msgTypes[40].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*NodeLabelRef); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_api_v1_k8s_proto_msgTypes[41].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*NodePoolFields); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_api_v1_k8s_proto_msgTypes[42].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetClusterNodeFilterIndexResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_api_v1_k8s_proto_msgTypes[43].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetNodeGroupsUtilizationRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_api_v1_k8s_proto_msgTypes[44].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetNodeGroupsUtilizationResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_api_v1_k8s_proto_msgTypes[45].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetClusterNodeInventoryRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_api_v1_k8s_proto_msgTypes[46].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetClusterNodeInventoryResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_api_v1_k8s_proto_msgTypes[47].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*NodeInventoryRollup); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_api_v1_k8s_proto_msgTypes[48].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*NodeStateCounts); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_api_v1_k8s_proto_msgTypes[49].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*NodePoolInventory); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_api_v1_k8s_proto_msgTypes[50].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*InstanceTypeCount); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_api_v1_k8s_proto_msgTypes[51].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*NodeInventoryRow); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_api_v1_k8s_proto_msgTypes[52].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*NodeGroupMetric); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_api_v1_k8s_proto_msgTypes[53].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetNodeGroupResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_api_v1_k8s_proto_msgTypes[54].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetClusterResponse); i { case 0: return &v.state case 1: @@ -14085,8 +18103,8 @@ func file_api_v1_k8s_proto_init() { return nil } } - file_api_v1_k8s_proto_msgTypes[22].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetResourcesResponse); i { + file_api_v1_k8s_proto_msgTypes[55].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetClustersResponse); i { case 0: return &v.state case 1: @@ -14097,8 +18115,8 @@ func file_api_v1_k8s_proto_init() { return nil } } - file_api_v1_k8s_proto_msgTypes[23].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetResourceFilterOptionsRequest); i { + file_api_v1_k8s_proto_msgTypes[56].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ListClustersResponse); i { case 0: return &v.state case 1: @@ -14109,8 +18127,8 @@ func file_api_v1_k8s_proto_init() { return nil } } - file_api_v1_k8s_proto_msgTypes[24].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetResourceFilterOptionsResponse); i { + file_api_v1_k8s_proto_msgTypes[57].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetNodeResponse); i { case 0: return &v.state case 1: @@ -14121,8 +18139,8 @@ func file_api_v1_k8s_proto_init() { return nil } } - file_api_v1_k8s_proto_msgTypes[25].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*CreateClusterRequest); i { + file_api_v1_k8s_proto_msgTypes[58].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*NodeClaimRef); i { case 0: return &v.state case 1: @@ -14133,8 +18151,8 @@ func file_api_v1_k8s_proto_init() { return nil } } - file_api_v1_k8s_proto_msgTypes[26].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*CreateClusterResponse); i { + file_api_v1_k8s_proto_msgTypes[59].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*RequestNodeClaimDeletionRequest); i { case 0: return &v.state case 1: @@ -14145,8 +18163,8 @@ func file_api_v1_k8s_proto_init() { return nil } } - file_api_v1_k8s_proto_msgTypes[27].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ResetClusterTokenRequest); i { + file_api_v1_k8s_proto_msgTypes[60].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*RequestNodeClaimDeletionResponse); i { case 0: return &v.state case 1: @@ -14157,8 +18175,8 @@ func file_api_v1_k8s_proto_init() { return nil } } - file_api_v1_k8s_proto_msgTypes[28].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ResetClusterTokenResponse); i { + file_api_v1_k8s_proto_msgTypes[61].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetNodeClaimDeletionStatusRequest); i { case 0: return &v.state case 1: @@ -14169,8 +18187,8 @@ func file_api_v1_k8s_proto_init() { return nil } } - file_api_v1_k8s_proto_msgTypes[29].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetNodeGroupsRequest); i { + file_api_v1_k8s_proto_msgTypes[62].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetNodeClaimDeletionStatusResponse); i { case 0: return &v.state case 1: @@ -14181,8 +18199,8 @@ func file_api_v1_k8s_proto_init() { return nil } } - file_api_v1_k8s_proto_msgTypes[30].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetAllNodeGroupsRequest); i { + file_api_v1_k8s_proto_msgTypes[63].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetNodeClaimRefsRequest); i { case 0: return &v.state case 1: @@ -14193,8 +18211,8 @@ func file_api_v1_k8s_proto_init() { return nil } } - file_api_v1_k8s_proto_msgTypes[31].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetNodeGroupsResponse); i { + file_api_v1_k8s_proto_msgTypes[64].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*NodeClaimRefEntry); i { case 0: return &v.state case 1: @@ -14205,8 +18223,8 @@ func file_api_v1_k8s_proto_init() { return nil } } - file_api_v1_k8s_proto_msgTypes[32].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetAllNodeGroupsResponse); i { + file_api_v1_k8s_proto_msgTypes[65].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetNodeClaimRefsResponse); i { case 0: return &v.state case 1: @@ -14217,8 +18235,8 @@ func file_api_v1_k8s_proto_init() { return nil } } - file_api_v1_k8s_proto_msgTypes[33].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*NodeGroupSet); i { + file_api_v1_k8s_proto_msgTypes[66].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*NodeClaimDeletionItem); i { case 0: return &v.state case 1: @@ -14229,8 +18247,8 @@ func file_api_v1_k8s_proto_init() { return nil } } - file_api_v1_k8s_proto_msgTypes[34].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetNodeGroupsUtilizationRequest); i { + file_api_v1_k8s_proto_msgTypes[67].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*RequestNodeClaimDeletionsRequest); i { case 0: return &v.state case 1: @@ -14241,8 +18259,8 @@ func file_api_v1_k8s_proto_init() { return nil } } - file_api_v1_k8s_proto_msgTypes[35].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetNodeGroupsUtilizationResponse); i { + file_api_v1_k8s_proto_msgTypes[68].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*NodeClaimDeletionResult); i { case 0: return &v.state case 1: @@ -14253,8 +18271,8 @@ func file_api_v1_k8s_proto_init() { return nil } } - file_api_v1_k8s_proto_msgTypes[36].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*NodeGroupMetric); i { + file_api_v1_k8s_proto_msgTypes[69].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*RequestNodeClaimDeletionsResponse); i { case 0: return &v.state case 1: @@ -14265,8 +18283,8 @@ func file_api_v1_k8s_proto_init() { return nil } } - file_api_v1_k8s_proto_msgTypes[37].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetNodeGroupResponse); i { + file_api_v1_k8s_proto_msgTypes[70].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*NodeClaimDeletionStatus); i { case 0: return &v.state case 1: @@ -14277,8 +18295,8 @@ func file_api_v1_k8s_proto_init() { return nil } } - file_api_v1_k8s_proto_msgTypes[38].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetClusterResponse); i { + file_api_v1_k8s_proto_msgTypes[71].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetWorkloadsResponse); i { case 0: return &v.state case 1: @@ -14289,8 +18307,8 @@ func file_api_v1_k8s_proto_init() { return nil } } - file_api_v1_k8s_proto_msgTypes[39].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetClustersResponse); i { + file_api_v1_k8s_proto_msgTypes[72].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetWorkloadResponse); i { case 0: return &v.state case 1: @@ -14301,8 +18319,8 @@ func file_api_v1_k8s_proto_init() { return nil } } - file_api_v1_k8s_proto_msgTypes[40].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ListClustersResponse); i { + file_api_v1_k8s_proto_msgTypes[73].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetWorkloadContainerPercentilesRequest); i { case 0: return &v.state case 1: @@ -14313,8 +18331,8 @@ func file_api_v1_k8s_proto_init() { return nil } } - file_api_v1_k8s_proto_msgTypes[41].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetNodeResponse); i { + file_api_v1_k8s_proto_msgTypes[74].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetWorkloadContainerPercentilesResponse); i { case 0: return &v.state case 1: @@ -14325,8 +18343,8 @@ func file_api_v1_k8s_proto_init() { return nil } } - file_api_v1_k8s_proto_msgTypes[42].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetWorkloadsResponse); i { + file_api_v1_k8s_proto_msgTypes[75].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetWorkloadContainerTimeSeriesRequest); i { case 0: return &v.state case 1: @@ -14337,8 +18355,8 @@ func file_api_v1_k8s_proto_init() { return nil } } - file_api_v1_k8s_proto_msgTypes[43].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetWorkloadResponse); i { + file_api_v1_k8s_proto_msgTypes[76].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetWorkloadContainerTimeSeriesResponse); i { case 0: return &v.state case 1: @@ -14349,8 +18367,8 @@ func file_api_v1_k8s_proto_init() { return nil } } - file_api_v1_k8s_proto_msgTypes[44].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetWorkloadContainerPercentilesRequest); i { + file_api_v1_k8s_proto_msgTypes[77].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetLatestContainerRequestLimitsRequest); i { case 0: return &v.state case 1: @@ -14361,8 +18379,8 @@ func file_api_v1_k8s_proto_init() { return nil } } - file_api_v1_k8s_proto_msgTypes[45].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetWorkloadContainerPercentilesResponse); i { + file_api_v1_k8s_proto_msgTypes[78].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetLatestContainerRequestLimitsResponse); i { case 0: return &v.state case 1: @@ -14373,8 +18391,8 @@ func file_api_v1_k8s_proto_init() { return nil } } - file_api_v1_k8s_proto_msgTypes[46].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetWorkloadContainerTimeSeriesRequest); i { + file_api_v1_k8s_proto_msgTypes[79].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetNodeGPUMigInstancesRequest); i { case 0: return &v.state case 1: @@ -14385,8 +18403,8 @@ func file_api_v1_k8s_proto_init() { return nil } } - file_api_v1_k8s_proto_msgTypes[47].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetWorkloadContainerTimeSeriesResponse); i { + file_api_v1_k8s_proto_msgTypes[80].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetNodeGPUMigInstancesResponse); i { case 0: return &v.state case 1: @@ -14397,8 +18415,8 @@ func file_api_v1_k8s_proto_init() { return nil } } - file_api_v1_k8s_proto_msgTypes[48].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetLatestContainerRequestLimitsRequest); i { + file_api_v1_k8s_proto_msgTypes[81].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetNodeGPUMigInstanceTimeseriesRequest); i { case 0: return &v.state case 1: @@ -14409,8 +18427,8 @@ func file_api_v1_k8s_proto_init() { return nil } } - file_api_v1_k8s_proto_msgTypes[49].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetLatestContainerRequestLimitsResponse); i { + file_api_v1_k8s_proto_msgTypes[82].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GPUMigInstanceDataPoint); i { case 0: return &v.state case 1: @@ -14421,8 +18439,8 @@ func file_api_v1_k8s_proto_init() { return nil } } - file_api_v1_k8s_proto_msgTypes[50].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetNodeGPUMigInstancesRequest); i { + file_api_v1_k8s_proto_msgTypes[83].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GPUMigInstanceTimeseries); i { case 0: return &v.state case 1: @@ -14433,8 +18451,8 @@ func file_api_v1_k8s_proto_init() { return nil } } - file_api_v1_k8s_proto_msgTypes[51].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetNodeGPUMigInstancesResponse); i { + file_api_v1_k8s_proto_msgTypes[84].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetNodeGPUMigInstanceTimeseriesResponse); i { case 0: return &v.state case 1: @@ -14445,7 +18463,7 @@ func file_api_v1_k8s_proto_init() { return nil } } - file_api_v1_k8s_proto_msgTypes[52].Exporter = func(v interface{}, i int) interface{} { + file_api_v1_k8s_proto_msgTypes[85].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*GetContainerIndividualGPUMetricsRequest); i { case 0: return &v.state @@ -14457,7 +18475,7 @@ func file_api_v1_k8s_proto_init() { return nil } } - file_api_v1_k8s_proto_msgTypes[53].Exporter = func(v interface{}, i int) interface{} { + file_api_v1_k8s_proto_msgTypes[86].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*GetContainerIndividualGPUMetricsResponse); i { case 0: return &v.state @@ -14469,7 +18487,7 @@ func file_api_v1_k8s_proto_init() { return nil } } - file_api_v1_k8s_proto_msgTypes[54].Exporter = func(v interface{}, i int) interface{} { + file_api_v1_k8s_proto_msgTypes[87].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*LanguageVersions); i { case 0: return &v.state @@ -14481,7 +18499,7 @@ func file_api_v1_k8s_proto_init() { return nil } } - file_api_v1_k8s_proto_msgTypes[55].Exporter = func(v interface{}, i int) interface{} { + file_api_v1_k8s_proto_msgTypes[88].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*GetWorkloadLanguagesRequest); i { case 0: return &v.state @@ -14493,7 +18511,7 @@ func file_api_v1_k8s_proto_init() { return nil } } - file_api_v1_k8s_proto_msgTypes[56].Exporter = func(v interface{}, i int) interface{} { + file_api_v1_k8s_proto_msgTypes[89].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*GetWorkloadLanguagesResponse); i { case 0: return &v.state @@ -14505,7 +18523,7 @@ func file_api_v1_k8s_proto_init() { return nil } } - file_api_v1_k8s_proto_msgTypes[57].Exporter = func(v interface{}, i int) interface{} { + file_api_v1_k8s_proto_msgTypes[90].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*GetForecastWorkloadsRequest); i { case 0: return &v.state @@ -14517,7 +18535,7 @@ func file_api_v1_k8s_proto_init() { return nil } } - file_api_v1_k8s_proto_msgTypes[58].Exporter = func(v interface{}, i int) interface{} { + file_api_v1_k8s_proto_msgTypes[91].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*GetForecastWorkloadsResponse); i { case 0: return &v.state @@ -14529,7 +18547,7 @@ func file_api_v1_k8s_proto_init() { return nil } } - file_api_v1_k8s_proto_msgTypes[59].Exporter = func(v interface{}, i int) interface{} { + file_api_v1_k8s_proto_msgTypes[92].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*GetForecastWorkloadRequest); i { case 0: return &v.state @@ -14541,7 +18559,7 @@ func file_api_v1_k8s_proto_init() { return nil } } - file_api_v1_k8s_proto_msgTypes[60].Exporter = func(v interface{}, i int) interface{} { + file_api_v1_k8s_proto_msgTypes[93].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*GetForecastWorkloadResponse); i { case 0: return &v.state @@ -14553,7 +18571,7 @@ func file_api_v1_k8s_proto_init() { return nil } } - file_api_v1_k8s_proto_msgTypes[61].Exporter = func(v interface{}, i int) interface{} { + file_api_v1_k8s_proto_msgTypes[94].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*GetClusterMetadataResponse); i { case 0: return &v.state @@ -14565,7 +18583,7 @@ func file_api_v1_k8s_proto_init() { return nil } } - file_api_v1_k8s_proto_msgTypes[62].Exporter = func(v interface{}, i int) interface{} { + file_api_v1_k8s_proto_msgTypes[95].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*ClusterElement); i { case 0: return &v.state @@ -14577,7 +18595,7 @@ func file_api_v1_k8s_proto_init() { return nil } } - file_api_v1_k8s_proto_msgTypes[63].Exporter = func(v interface{}, i int) interface{} { + file_api_v1_k8s_proto_msgTypes[96].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*GetAllNamespacesRequest); i { case 0: return &v.state @@ -14589,7 +18607,7 @@ func file_api_v1_k8s_proto_init() { return nil } } - file_api_v1_k8s_proto_msgTypes[64].Exporter = func(v interface{}, i int) interface{} { + file_api_v1_k8s_proto_msgTypes[97].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*GetAllNamespacesResponse); i { case 0: return &v.state @@ -14601,7 +18619,7 @@ func file_api_v1_k8s_proto_init() { return nil } } - file_api_v1_k8s_proto_msgTypes[65].Exporter = func(v interface{}, i int) interface{} { + file_api_v1_k8s_proto_msgTypes[98].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*SearchNamespacesByClusterRequest); i { case 0: return &v.state @@ -14613,7 +18631,7 @@ func file_api_v1_k8s_proto_init() { return nil } } - file_api_v1_k8s_proto_msgTypes[66].Exporter = func(v interface{}, i int) interface{} { + file_api_v1_k8s_proto_msgTypes[99].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*SearchNamespacesByClusterResponse); i { case 0: return &v.state @@ -14625,7 +18643,7 @@ func file_api_v1_k8s_proto_init() { return nil } } - file_api_v1_k8s_proto_msgTypes[67].Exporter = func(v interface{}, i int) interface{} { + file_api_v1_k8s_proto_msgTypes[100].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*ListNamespacesByClusterRequest); i { case 0: return &v.state @@ -14637,7 +18655,7 @@ func file_api_v1_k8s_proto_init() { return nil } } - file_api_v1_k8s_proto_msgTypes[68].Exporter = func(v interface{}, i int) interface{} { + file_api_v1_k8s_proto_msgTypes[101].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*ListNamespacesByClusterResponse); i { case 0: return &v.state @@ -14649,7 +18667,7 @@ func file_api_v1_k8s_proto_init() { return nil } } - file_api_v1_k8s_proto_msgTypes[69].Exporter = func(v interface{}, i int) interface{} { + file_api_v1_k8s_proto_msgTypes[102].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*NamespaceItem); i { case 0: return &v.state @@ -14661,7 +18679,7 @@ func file_api_v1_k8s_proto_init() { return nil } } - file_api_v1_k8s_proto_msgTypes[70].Exporter = func(v interface{}, i int) interface{} { + file_api_v1_k8s_proto_msgTypes[103].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*GetAllWorkloadNamesRequest); i { case 0: return &v.state @@ -14673,7 +18691,7 @@ func file_api_v1_k8s_proto_init() { return nil } } - file_api_v1_k8s_proto_msgTypes[71].Exporter = func(v interface{}, i int) interface{} { + file_api_v1_k8s_proto_msgTypes[104].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*GetAllWorkloadNamesResponse); i { case 0: return &v.state @@ -14685,7 +18703,7 @@ func file_api_v1_k8s_proto_init() { return nil } } - file_api_v1_k8s_proto_msgTypes[72].Exporter = func(v interface{}, i int) interface{} { + file_api_v1_k8s_proto_msgTypes[105].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*GetAllWorkloadLabelsRequest); i { case 0: return &v.state @@ -14697,7 +18715,7 @@ func file_api_v1_k8s_proto_init() { return nil } } - file_api_v1_k8s_proto_msgTypes[73].Exporter = func(v interface{}, i int) interface{} { + file_api_v1_k8s_proto_msgTypes[106].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*GetAllWorkloadLabelsResponse); i { case 0: return &v.state @@ -14709,7 +18727,7 @@ func file_api_v1_k8s_proto_init() { return nil } } - file_api_v1_k8s_proto_msgTypes[74].Exporter = func(v interface{}, i int) interface{} { + file_api_v1_k8s_proto_msgTypes[107].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*GetAllNodeGroupNamesRequest); i { case 0: return &v.state @@ -14721,7 +18739,7 @@ func file_api_v1_k8s_proto_init() { return nil } } - file_api_v1_k8s_proto_msgTypes[75].Exporter = func(v interface{}, i int) interface{} { + file_api_v1_k8s_proto_msgTypes[108].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*GetAllNodeGroupNamesResponse); i { case 0: return &v.state @@ -14733,7 +18751,7 @@ func file_api_v1_k8s_proto_init() { return nil } } - file_api_v1_k8s_proto_msgTypes[76].Exporter = func(v interface{}, i int) interface{} { + file_api_v1_k8s_proto_msgTypes[109].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*Cluster); i { case 0: return &v.state @@ -14745,7 +18763,7 @@ func file_api_v1_k8s_proto_init() { return nil } } - file_api_v1_k8s_proto_msgTypes[77].Exporter = func(v interface{}, i int) interface{} { + file_api_v1_k8s_proto_msgTypes[110].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*OperatorInfo); i { case 0: return &v.state @@ -14757,7 +18775,7 @@ func file_api_v1_k8s_proto_init() { return nil } } - file_api_v1_k8s_proto_msgTypes[78].Exporter = func(v interface{}, i int) interface{} { + file_api_v1_k8s_proto_msgTypes[111].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*Container); i { case 0: return &v.state @@ -14769,7 +18787,7 @@ func file_api_v1_k8s_proto_init() { return nil } } - file_api_v1_k8s_proto_msgTypes[79].Exporter = func(v interface{}, i int) interface{} { + file_api_v1_k8s_proto_msgTypes[112].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*GetLatestOperatorVersionRequest); i { case 0: return &v.state @@ -14781,7 +18799,7 @@ func file_api_v1_k8s_proto_init() { return nil } } - file_api_v1_k8s_proto_msgTypes[80].Exporter = func(v interface{}, i int) interface{} { + file_api_v1_k8s_proto_msgTypes[113].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*GetLatestOperatorVersionResponse); i { case 0: return &v.state @@ -14793,7 +18811,7 @@ func file_api_v1_k8s_proto_init() { return nil } } - file_api_v1_k8s_proto_msgTypes[81].Exporter = func(v interface{}, i int) interface{} { + file_api_v1_k8s_proto_msgTypes[114].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*GalaxyGetWorkloadPerspectiveRequest); i { case 0: return &v.state @@ -14805,7 +18823,7 @@ func file_api_v1_k8s_proto_init() { return nil } } - file_api_v1_k8s_proto_msgTypes[82].Exporter = func(v interface{}, i int) interface{} { + file_api_v1_k8s_proto_msgTypes[115].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*GalaxyGetWorkloadPerspectiveResponse); i { case 0: return &v.state @@ -14817,7 +18835,7 @@ func file_api_v1_k8s_proto_init() { return nil } } - file_api_v1_k8s_proto_msgTypes[83].Exporter = func(v interface{}, i int) interface{} { + file_api_v1_k8s_proto_msgTypes[116].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*GalaxyWorkloadGroup); i { case 0: return &v.state @@ -14829,7 +18847,7 @@ func file_api_v1_k8s_proto_init() { return nil } } - file_api_v1_k8s_proto_msgTypes[84].Exporter = func(v interface{}, i int) interface{} { + file_api_v1_k8s_proto_msgTypes[117].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*PerspectiveDatapointOpts); i { case 0: return &v.state @@ -14841,7 +18859,7 @@ func file_api_v1_k8s_proto_init() { return nil } } - file_api_v1_k8s_proto_msgTypes[85].Exporter = func(v interface{}, i int) interface{} { + file_api_v1_k8s_proto_msgTypes[118].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*ListAuditLogsRequest); i { case 0: return &v.state @@ -14853,7 +18871,7 @@ func file_api_v1_k8s_proto_init() { return nil } } - file_api_v1_k8s_proto_msgTypes[86].Exporter = func(v interface{}, i int) interface{} { + file_api_v1_k8s_proto_msgTypes[119].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*ListAuditLogsResponse); i { case 0: return &v.state @@ -14865,7 +18883,7 @@ func file_api_v1_k8s_proto_init() { return nil } } - file_api_v1_k8s_proto_msgTypes[87].Exporter = func(v interface{}, i int) interface{} { + file_api_v1_k8s_proto_msgTypes[120].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*ListAuditLogOriginatorsRequest); i { case 0: return &v.state @@ -14877,7 +18895,7 @@ func file_api_v1_k8s_proto_init() { return nil } } - file_api_v1_k8s_proto_msgTypes[88].Exporter = func(v interface{}, i int) interface{} { + file_api_v1_k8s_proto_msgTypes[121].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*ListAuditLogOriginatorsResponse); i { case 0: return &v.state @@ -14889,7 +18907,7 @@ func file_api_v1_k8s_proto_init() { return nil } } - file_api_v1_k8s_proto_msgTypes[89].Exporter = func(v interface{}, i int) interface{} { + file_api_v1_k8s_proto_msgTypes[122].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*SendWorkloadEmailRequest); i { case 0: return &v.state @@ -14901,7 +18919,7 @@ func file_api_v1_k8s_proto_init() { return nil } } - file_api_v1_k8s_proto_msgTypes[90].Exporter = func(v interface{}, i int) interface{} { + file_api_v1_k8s_proto_msgTypes[123].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*SendWorkloadEmailResponse); i { case 0: return &v.state @@ -14913,7 +18931,7 @@ func file_api_v1_k8s_proto_init() { return nil } } - file_api_v1_k8s_proto_msgTypes[91].Exporter = func(v interface{}, i int) interface{} { + file_api_v1_k8s_proto_msgTypes[124].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*SendWeeklySummaryEmailRequest); i { case 0: return &v.state @@ -14925,7 +18943,7 @@ func file_api_v1_k8s_proto_init() { return nil } } - file_api_v1_k8s_proto_msgTypes[92].Exporter = func(v interface{}, i int) interface{} { + file_api_v1_k8s_proto_msgTypes[125].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*SendWeeklySummaryEmailRequestData); i { case 0: return &v.state @@ -14937,7 +18955,7 @@ func file_api_v1_k8s_proto_init() { return nil } } - file_api_v1_k8s_proto_msgTypes[93].Exporter = func(v interface{}, i int) interface{} { + file_api_v1_k8s_proto_msgTypes[126].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*SendWeeklySummaryEmailResponse); i { case 0: return &v.state @@ -14949,7 +18967,7 @@ func file_api_v1_k8s_proto_init() { return nil } } - file_api_v1_k8s_proto_msgTypes[94].Exporter = func(v interface{}, i int) interface{} { + file_api_v1_k8s_proto_msgTypes[127].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*GetClustersNodeInfoRequest); i { case 0: return &v.state @@ -14961,7 +18979,7 @@ func file_api_v1_k8s_proto_init() { return nil } } - file_api_v1_k8s_proto_msgTypes[95].Exporter = func(v interface{}, i int) interface{} { + file_api_v1_k8s_proto_msgTypes[128].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*GetClustersNodeInfoResponse); i { case 0: return &v.state @@ -14973,7 +18991,7 @@ func file_api_v1_k8s_proto_init() { return nil } } - file_api_v1_k8s_proto_msgTypes[96].Exporter = func(v interface{}, i int) interface{} { + file_api_v1_k8s_proto_msgTypes[129].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*SearchK8SResourcesRequest); i { case 0: return &v.state @@ -14985,7 +19003,7 @@ func file_api_v1_k8s_proto_init() { return nil } } - file_api_v1_k8s_proto_msgTypes[97].Exporter = func(v interface{}, i int) interface{} { + file_api_v1_k8s_proto_msgTypes[130].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*SearchK8SResourcesResponse); i { case 0: return &v.state @@ -14997,7 +19015,7 @@ func file_api_v1_k8s_proto_init() { return nil } } - file_api_v1_k8s_proto_msgTypes[98].Exporter = func(v interface{}, i int) interface{} { + file_api_v1_k8s_proto_msgTypes[131].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*K8SSearchResult); i { case 0: return &v.state @@ -15009,7 +19027,7 @@ func file_api_v1_k8s_proto_init() { return nil } } - file_api_v1_k8s_proto_msgTypes[99].Exporter = func(v interface{}, i int) interface{} { + file_api_v1_k8s_proto_msgTypes[132].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*SearchK8SWorkloadsRequest); i { case 0: return &v.state @@ -15021,7 +19039,7 @@ func file_api_v1_k8s_proto_init() { return nil } } - file_api_v1_k8s_proto_msgTypes[100].Exporter = func(v interface{}, i int) interface{} { + file_api_v1_k8s_proto_msgTypes[133].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*SearchK8SWorkloadsResponse); i { case 0: return &v.state @@ -15033,7 +19051,7 @@ func file_api_v1_k8s_proto_init() { return nil } } - file_api_v1_k8s_proto_msgTypes[101].Exporter = func(v interface{}, i int) interface{} { + file_api_v1_k8s_proto_msgTypes[134].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*K8SWorkloadSearchResult); i { case 0: return &v.state @@ -15045,7 +19063,7 @@ func file_api_v1_k8s_proto_init() { return nil } } - file_api_v1_k8s_proto_msgTypes[102].Exporter = func(v interface{}, i int) interface{} { + file_api_v1_k8s_proto_msgTypes[135].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*MetadataForWorkloadsRequest); i { case 0: return &v.state @@ -15057,7 +19075,7 @@ func file_api_v1_k8s_proto_init() { return nil } } - file_api_v1_k8s_proto_msgTypes[103].Exporter = func(v interface{}, i int) interface{} { + file_api_v1_k8s_proto_msgTypes[136].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*MetadataForWorkloadsResponse); i { case 0: return &v.state @@ -15069,7 +19087,7 @@ func file_api_v1_k8s_proto_init() { return nil } } - file_api_v1_k8s_proto_msgTypes[104].Exporter = func(v interface{}, i int) interface{} { + file_api_v1_k8s_proto_msgTypes[137].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*AddClusterTagsRequest); i { case 0: return &v.state @@ -15081,7 +19099,7 @@ func file_api_v1_k8s_proto_init() { return nil } } - file_api_v1_k8s_proto_msgTypes[105].Exporter = func(v interface{}, i int) interface{} { + file_api_v1_k8s_proto_msgTypes[138].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*AddClusterTagsResponse); i { case 0: return &v.state @@ -15093,7 +19111,7 @@ func file_api_v1_k8s_proto_init() { return nil } } - file_api_v1_k8s_proto_msgTypes[106].Exporter = func(v interface{}, i int) interface{} { + file_api_v1_k8s_proto_msgTypes[139].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*RemoveClusterTagsRequest); i { case 0: return &v.state @@ -15105,7 +19123,7 @@ func file_api_v1_k8s_proto_init() { return nil } } - file_api_v1_k8s_proto_msgTypes[107].Exporter = func(v interface{}, i int) interface{} { + file_api_v1_k8s_proto_msgTypes[140].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*RemoveClusterTagsResponse); i { case 0: return &v.state @@ -15117,7 +19135,7 @@ func file_api_v1_k8s_proto_init() { return nil } } - file_api_v1_k8s_proto_msgTypes[108].Exporter = func(v interface{}, i int) interface{} { + file_api_v1_k8s_proto_msgTypes[141].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*ListTagsRequest); i { case 0: return &v.state @@ -15129,7 +19147,7 @@ func file_api_v1_k8s_proto_init() { return nil } } - file_api_v1_k8s_proto_msgTypes[109].Exporter = func(v interface{}, i int) interface{} { + file_api_v1_k8s_proto_msgTypes[142].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*TagSummary); i { case 0: return &v.state @@ -15141,7 +19159,7 @@ func file_api_v1_k8s_proto_init() { return nil } } - file_api_v1_k8s_proto_msgTypes[110].Exporter = func(v interface{}, i int) interface{} { + file_api_v1_k8s_proto_msgTypes[143].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*ListTagsResponse); i { case 0: return &v.state @@ -15153,7 +19171,7 @@ func file_api_v1_k8s_proto_init() { return nil } } - file_api_v1_k8s_proto_msgTypes[111].Exporter = func(v interface{}, i int) interface{} { + file_api_v1_k8s_proto_msgTypes[144].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*WorkloadMetadata); i { case 0: return &v.state @@ -15165,7 +19183,7 @@ func file_api_v1_k8s_proto_init() { return nil } } - file_api_v1_k8s_proto_msgTypes[112].Exporter = func(v interface{}, i int) interface{} { + file_api_v1_k8s_proto_msgTypes[145].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*PodMetadata); i { case 0: return &v.state @@ -15177,7 +19195,7 @@ func file_api_v1_k8s_proto_init() { return nil } } - file_api_v1_k8s_proto_msgTypes[113].Exporter = func(v interface{}, i int) interface{} { + file_api_v1_k8s_proto_msgTypes[146].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*NodeMetadata); i { case 0: return &v.state @@ -15189,7 +19207,7 @@ func file_api_v1_k8s_proto_init() { return nil } } - file_api_v1_k8s_proto_msgTypes[114].Exporter = func(v interface{}, i int) interface{} { + file_api_v1_k8s_proto_msgTypes[147].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*GetRelatedResourcesRequest); i { case 0: return &v.state @@ -15201,7 +19219,7 @@ func file_api_v1_k8s_proto_init() { return nil } } - file_api_v1_k8s_proto_msgTypes[115].Exporter = func(v interface{}, i int) interface{} { + file_api_v1_k8s_proto_msgTypes[148].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*GetWorkloadPodHistoryResponse); i { case 0: return &v.state @@ -15213,7 +19231,7 @@ func file_api_v1_k8s_proto_init() { return nil } } - file_api_v1_k8s_proto_msgTypes[116].Exporter = func(v interface{}, i int) interface{} { + file_api_v1_k8s_proto_msgTypes[149].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*JobHistory); i { case 0: return &v.state @@ -15225,7 +19243,7 @@ func file_api_v1_k8s_proto_init() { return nil } } - file_api_v1_k8s_proto_msgTypes[117].Exporter = func(v interface{}, i int) interface{} { + file_api_v1_k8s_proto_msgTypes[150].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*ReplicaSetHistory); i { case 0: return &v.state @@ -15237,7 +19255,7 @@ func file_api_v1_k8s_proto_init() { return nil } } - file_api_v1_k8s_proto_msgTypes[118].Exporter = func(v interface{}, i int) interface{} { + file_api_v1_k8s_proto_msgTypes[151].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*PodHistory); i { case 0: return &v.state @@ -15249,7 +19267,7 @@ func file_api_v1_k8s_proto_init() { return nil } } - file_api_v1_k8s_proto_msgTypes[119].Exporter = func(v interface{}, i int) interface{} { + file_api_v1_k8s_proto_msgTypes[152].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*GetRelatedResourcesResponse); i { case 0: return &v.state @@ -15261,7 +19279,7 @@ func file_api_v1_k8s_proto_init() { return nil } } - file_api_v1_k8s_proto_msgTypes[120].Exporter = func(v interface{}, i int) interface{} { + file_api_v1_k8s_proto_msgTypes[153].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*K8SRelatedResource); i { case 0: return &v.state @@ -15273,7 +19291,7 @@ func file_api_v1_k8s_proto_init() { return nil } } - file_api_v1_k8s_proto_msgTypes[121].Exporter = func(v interface{}, i int) interface{} { + file_api_v1_k8s_proto_msgTypes[154].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*K8SResourceNode); i { case 0: return &v.state @@ -15285,7 +19303,7 @@ func file_api_v1_k8s_proto_init() { return nil } } - file_api_v1_k8s_proto_msgTypes[122].Exporter = func(v interface{}, i int) interface{} { + file_api_v1_k8s_proto_msgTypes[155].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*K8SResourceEdge); i { case 0: return &v.state @@ -15297,7 +19315,7 @@ func file_api_v1_k8s_proto_init() { return nil } } - file_api_v1_k8s_proto_msgTypes[123].Exporter = func(v interface{}, i int) interface{} { + file_api_v1_k8s_proto_msgTypes[156].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*GetClusterTypeRequest); i { case 0: return &v.state @@ -15309,7 +19327,7 @@ func file_api_v1_k8s_proto_init() { return nil } } - file_api_v1_k8s_proto_msgTypes[124].Exporter = func(v interface{}, i int) interface{} { + file_api_v1_k8s_proto_msgTypes[157].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*GetClusterTypeResponse); i { case 0: return &v.state @@ -15321,7 +19339,7 @@ func file_api_v1_k8s_proto_init() { return nil } } - file_api_v1_k8s_proto_msgTypes[125].Exporter = func(v interface{}, i int) interface{} { + file_api_v1_k8s_proto_msgTypes[158].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*GetMigPartedConfigRequest); i { case 0: return &v.state @@ -15333,7 +19351,7 @@ func file_api_v1_k8s_proto_init() { return nil } } - file_api_v1_k8s_proto_msgTypes[126].Exporter = func(v interface{}, i int) interface{} { + file_api_v1_k8s_proto_msgTypes[159].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*GetMigPartedConfigResponse); i { case 0: return &v.state @@ -15345,7 +19363,7 @@ func file_api_v1_k8s_proto_init() { return nil } } - file_api_v1_k8s_proto_msgTypes[127].Exporter = func(v interface{}, i int) interface{} { + file_api_v1_k8s_proto_msgTypes[160].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*GetWorkloadsStatsRequest); i { case 0: return &v.state @@ -15357,7 +19375,7 @@ func file_api_v1_k8s_proto_init() { return nil } } - file_api_v1_k8s_proto_msgTypes[128].Exporter = func(v interface{}, i int) interface{} { + file_api_v1_k8s_proto_msgTypes[161].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*GetWorkloadsStatsResponse); i { case 0: return &v.state @@ -15369,7 +19387,7 @@ func file_api_v1_k8s_proto_init() { return nil } } - file_api_v1_k8s_proto_msgTypes[129].Exporter = func(v interface{}, i int) interface{} { + file_api_v1_k8s_proto_msgTypes[162].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*DailyUtilizationRequest); i { case 0: return &v.state @@ -15381,7 +19399,7 @@ func file_api_v1_k8s_proto_init() { return nil } } - file_api_v1_k8s_proto_msgTypes[130].Exporter = func(v interface{}, i int) interface{} { + file_api_v1_k8s_proto_msgTypes[163].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*DailyUtilizationResponse); i { case 0: return &v.state @@ -15393,7 +19411,7 @@ func file_api_v1_k8s_proto_init() { return nil } } - file_api_v1_k8s_proto_msgTypes[131].Exporter = func(v interface{}, i int) interface{} { + file_api_v1_k8s_proto_msgTypes[164].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*DailyUtilizationInstanceTypeRequest); i { case 0: return &v.state @@ -15405,7 +19423,7 @@ func file_api_v1_k8s_proto_init() { return nil } } - file_api_v1_k8s_proto_msgTypes[132].Exporter = func(v interface{}, i int) interface{} { + file_api_v1_k8s_proto_msgTypes[165].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*DailyUtilizationInstanceTypeResponse); i { case 0: return &v.state @@ -15417,7 +19435,7 @@ func file_api_v1_k8s_proto_init() { return nil } } - file_api_v1_k8s_proto_msgTypes[133].Exporter = func(v interface{}, i int) interface{} { + file_api_v1_k8s_proto_msgTypes[166].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*DailyUtilizationNodeTypeRequest); i { case 0: return &v.state @@ -15429,7 +19447,7 @@ func file_api_v1_k8s_proto_init() { return nil } } - file_api_v1_k8s_proto_msgTypes[134].Exporter = func(v interface{}, i int) interface{} { + file_api_v1_k8s_proto_msgTypes[167].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*DailyUtilizationNodeTypeResponse); i { case 0: return &v.state @@ -15441,7 +19459,7 @@ func file_api_v1_k8s_proto_init() { return nil } } - file_api_v1_k8s_proto_msgTypes[135].Exporter = func(v interface{}, i int) interface{} { + file_api_v1_k8s_proto_msgTypes[168].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*LookupNodeInstanceRequest); i { case 0: return &v.state @@ -15453,7 +19471,7 @@ func file_api_v1_k8s_proto_init() { return nil } } - file_api_v1_k8s_proto_msgTypes[136].Exporter = func(v interface{}, i int) interface{} { + file_api_v1_k8s_proto_msgTypes[169].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*InstanceLookupParams); i { case 0: return &v.state @@ -15465,7 +19483,7 @@ func file_api_v1_k8s_proto_init() { return nil } } - file_api_v1_k8s_proto_msgTypes[137].Exporter = func(v interface{}, i int) interface{} { + file_api_v1_k8s_proto_msgTypes[170].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*LookupNodeInstanceResponse); i { case 0: return &v.state @@ -15477,7 +19495,7 @@ func file_api_v1_k8s_proto_init() { return nil } } - file_api_v1_k8s_proto_msgTypes[139].Exporter = func(v interface{}, i int) interface{} { + file_api_v1_k8s_proto_msgTypes[174].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*GetNodeGroupsUtilizationResponse_ListNodeGroupMetrics); i { case 0: return &v.state @@ -15489,7 +19507,7 @@ func file_api_v1_k8s_proto_init() { return nil } } - file_api_v1_k8s_proto_msgTypes[150].Exporter = func(v interface{}, i int) interface{} { + file_api_v1_k8s_proto_msgTypes[185].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*DailyUtilizationResponse_Datapoints); i { case 0: return &v.state @@ -15501,7 +19519,7 @@ func file_api_v1_k8s_proto_init() { return nil } } - file_api_v1_k8s_proto_msgTypes[151].Exporter = func(v interface{}, i int) interface{} { + file_api_v1_k8s_proto_msgTypes[186].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*DailyUtilizationResponse_Datapoint); i { case 0: return &v.state @@ -15513,7 +19531,7 @@ func file_api_v1_k8s_proto_init() { return nil } } - file_api_v1_k8s_proto_msgTypes[154].Exporter = func(v interface{}, i int) interface{} { + file_api_v1_k8s_proto_msgTypes[189].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*DailyUtilizationInstanceTypeResponse_Datapoints); i { case 0: return &v.state @@ -15525,7 +19543,7 @@ func file_api_v1_k8s_proto_init() { return nil } } - file_api_v1_k8s_proto_msgTypes[155].Exporter = func(v interface{}, i int) interface{} { + file_api_v1_k8s_proto_msgTypes[190].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*DailyUtilizationInstanceTypeResponse_Datapoint); i { case 0: return &v.state @@ -15537,7 +19555,7 @@ func file_api_v1_k8s_proto_init() { return nil } } - file_api_v1_k8s_proto_msgTypes[159].Exporter = func(v interface{}, i int) interface{} { + file_api_v1_k8s_proto_msgTypes[194].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*DailyUtilizationNodeTypeResponse_Datapoints); i { case 0: return &v.state @@ -15549,7 +19567,7 @@ func file_api_v1_k8s_proto_init() { return nil } } - file_api_v1_k8s_proto_msgTypes[160].Exporter = func(v interface{}, i int) interface{} { + file_api_v1_k8s_proto_msgTypes[195].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*DailyUtilizationNodeTypeResponse_Datapoint); i { case 0: return &v.state @@ -15568,34 +19586,39 @@ func file_api_v1_k8s_proto_init() { file_api_v1_k8s_proto_msgTypes[4].OneofWrappers = []interface{}{} file_api_v1_k8s_proto_msgTypes[5].OneofWrappers = []interface{}{} file_api_v1_k8s_proto_msgTypes[6].OneofWrappers = []interface{}{} - file_api_v1_k8s_proto_msgTypes[11].OneofWrappers = []interface{}{} - file_api_v1_k8s_proto_msgTypes[12].OneofWrappers = []interface{}{} - file_api_v1_k8s_proto_msgTypes[14].OneofWrappers = []interface{}{} + file_api_v1_k8s_proto_msgTypes[10].OneofWrappers = []interface{}{} file_api_v1_k8s_proto_msgTypes[15].OneofWrappers = []interface{}{} file_api_v1_k8s_proto_msgTypes[16].OneofWrappers = []interface{}{} - file_api_v1_k8s_proto_msgTypes[17].OneofWrappers = []interface{}{} - file_api_v1_k8s_proto_msgTypes[23].OneofWrappers = []interface{}{} - file_api_v1_k8s_proto_msgTypes[29].OneofWrappers = []interface{}{} - file_api_v1_k8s_proto_msgTypes[34].OneofWrappers = []interface{}{} - file_api_v1_k8s_proto_msgTypes[44].OneofWrappers = []interface{}{} - file_api_v1_k8s_proto_msgTypes[46].OneofWrappers = []interface{}{} - file_api_v1_k8s_proto_msgTypes[50].OneofWrappers = []interface{}{} - file_api_v1_k8s_proto_msgTypes[51].OneofWrappers = []interface{}{} - file_api_v1_k8s_proto_msgTypes[57].OneofWrappers = []interface{}{} + file_api_v1_k8s_proto_msgTypes[18].OneofWrappers = []interface{}{} + file_api_v1_k8s_proto_msgTypes[19].OneofWrappers = []interface{}{} + file_api_v1_k8s_proto_msgTypes[20].OneofWrappers = []interface{}{} + file_api_v1_k8s_proto_msgTypes[21].OneofWrappers = []interface{}{} + file_api_v1_k8s_proto_msgTypes[27].OneofWrappers = []interface{}{} + file_api_v1_k8s_proto_msgTypes[33].OneofWrappers = []interface{}{} + file_api_v1_k8s_proto_msgTypes[38].OneofWrappers = []interface{}{} + file_api_v1_k8s_proto_msgTypes[43].OneofWrappers = []interface{}{} file_api_v1_k8s_proto_msgTypes[59].OneofWrappers = []interface{}{} - file_api_v1_k8s_proto_msgTypes[76].OneofWrappers = []interface{}{} - file_api_v1_k8s_proto_msgTypes[85].OneofWrappers = []interface{}{} - file_api_v1_k8s_proto_msgTypes[87].OneofWrappers = []interface{}{} - file_api_v1_k8s_proto_msgTypes[94].OneofWrappers = []interface{}{} - file_api_v1_k8s_proto_msgTypes[114].OneofWrappers = []interface{}{} + file_api_v1_k8s_proto_msgTypes[61].OneofWrappers = []interface{}{} + file_api_v1_k8s_proto_msgTypes[73].OneofWrappers = []interface{}{} + file_api_v1_k8s_proto_msgTypes[75].OneofWrappers = []interface{}{} + file_api_v1_k8s_proto_msgTypes[79].OneofWrappers = []interface{}{} + file_api_v1_k8s_proto_msgTypes[80].OneofWrappers = []interface{}{} + file_api_v1_k8s_proto_msgTypes[81].OneofWrappers = []interface{}{} + file_api_v1_k8s_proto_msgTypes[90].OneofWrappers = []interface{}{} + file_api_v1_k8s_proto_msgTypes[92].OneofWrappers = []interface{}{} + file_api_v1_k8s_proto_msgTypes[109].OneofWrappers = []interface{}{} + file_api_v1_k8s_proto_msgTypes[118].OneofWrappers = []interface{}{} + file_api_v1_k8s_proto_msgTypes[120].OneofWrappers = []interface{}{} file_api_v1_k8s_proto_msgTypes[127].OneofWrappers = []interface{}{} + file_api_v1_k8s_proto_msgTypes[147].OneofWrappers = []interface{}{} + file_api_v1_k8s_proto_msgTypes[160].OneofWrappers = []interface{}{} type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_api_v1_k8s_proto_rawDesc, - NumEnums: 3, - NumMessages: 164, + NumEnums: 7, + NumMessages: 199, NumExtensions: 0, NumServices: 3, }, diff --git a/gen/api/v1/k8s_grpc.pb.go b/gen/api/v1/k8s_grpc.pb.go index 9831c30a..92866391 100644 --- a/gen/api/v1/k8s_grpc.pb.go +++ b/gen/api/v1/k8s_grpc.pb.go @@ -34,16 +34,24 @@ const ( K8SService_GetNodeGroups_FullMethodName = "/api.v1.K8SService/GetNodeGroups" K8SService_GetAllNodeGroups_FullMethodName = "/api.v1.K8SService/GetAllNodeGroups" K8SService_GetNodeGroupsUtilization_FullMethodName = "/api.v1.K8SService/GetNodeGroupsUtilization" + K8SService_GetClusterNodeInventory_FullMethodName = "/api.v1.K8SService/GetClusterNodeInventory" + K8SService_GetClusterNodeFilterIndex_FullMethodName = "/api.v1.K8SService/GetClusterNodeFilterIndex" K8SService_GetNodeGroup_FullMethodName = "/api.v1.K8SService/GetNodeGroup" K8SService_GetNode_FullMethodName = "/api.v1.K8SService/GetNode" + K8SService_RequestNodeClaimDeletion_FullMethodName = "/api.v1.K8SService/RequestNodeClaimDeletion" + K8SService_GetNodeClaimDeletionStatus_FullMethodName = "/api.v1.K8SService/GetNodeClaimDeletionStatus" + K8SService_GetNodeClaimRefs_FullMethodName = "/api.v1.K8SService/GetNodeClaimRefs" + K8SService_RequestNodeClaimDeletions_FullMethodName = "/api.v1.K8SService/RequestNodeClaimDeletions" K8SService_GetWorkloads_FullMethodName = "/api.v1.K8SService/GetWorkloads" K8SService_ListWorkloadsPaginated_FullMethodName = "/api.v1.K8SService/ListWorkloadsPaginated" K8SService_GetWorkloadsSummary_FullMethodName = "/api.v1.K8SService/GetWorkloadsSummary" + K8SService_GetWorkloadRequestTrends_FullMethodName = "/api.v1.K8SService/GetWorkloadRequestTrends" K8SService_GetWorkload_FullMethodName = "/api.v1.K8SService/GetWorkload" K8SService_GetWorkloadContainerPercentiles_FullMethodName = "/api.v1.K8SService/GetWorkloadContainerPercentiles" K8SService_GetWorkloadContainerTimeSeries_FullMethodName = "/api.v1.K8SService/GetWorkloadContainerTimeSeries" K8SService_GetLatestContainerRequestLimits_FullMethodName = "/api.v1.K8SService/GetLatestContainerRequestLimits" K8SService_GetNodeGPUMigInstances_FullMethodName = "/api.v1.K8SService/GetNodeGPUMigInstances" + K8SService_GetNodeGPUMigInstanceTimeseries_FullMethodName = "/api.v1.K8SService/GetNodeGPUMigInstanceTimeseries" K8SService_GetContainerIndividualGPUMetrics_FullMethodName = "/api.v1.K8SService/GetContainerIndividualGPUMetrics" K8SService_GetWorkloadLanguages_FullMethodName = "/api.v1.K8SService/GetWorkloadLanguages" K8SService_GetForecastWorkloads_FullMethodName = "/api.v1.K8SService/GetForecastWorkloads" @@ -105,10 +113,33 @@ type K8SServiceClient interface { GetAllNodeGroups(ctx context.Context, in *GetAllNodeGroupsRequest, opts ...grpc.CallOption) (*GetAllNodeGroupsResponse, error) // GetNodeGroupsUtilization retrieves utilization data for node groups in a specific cluster. GetNodeGroupsUtilization(ctx context.Context, in *GetNodeGroupsUtilizationRequest, opts ...grpc.CallOption) (*GetNodeGroupsUtilizationResponse, error) + // GetClusterNodeInventory returns the current node inventory of a cluster: + // one row per node pool, or one keyset page of nodes, plus a whole-cluster + // rollup on every response. Postgres only; nothing in it reads a time range. + GetClusterNodeInventory(ctx context.Context, in *GetClusterNodeInventoryRequest, opts ...grpc.CallOption) (*GetClusterNodeInventoryResponse, error) + // GetClusterNodeFilterIndex retrieves the filterable node attributes for a + // cluster: distinct node label sets with their per-node attribution, and the + // scalar spec fields of the Karpenter NodePools backing its node groups. + GetClusterNodeFilterIndex(ctx context.Context, in *GetClusterNodeFilterIndexRequest, opts ...grpc.CallOption) (*GetClusterNodeFilterIndexResponse, error) // GetNodeGroup retrieves node group for a specific cluster. GetNodeGroup(ctx context.Context, in *GetNodeGroupRequest, opts ...grpc.CallOption) (*GetNodeGroupResponse, error) // GetNode retrieves detailed information for a specific node. GetNode(ctx context.Context, in *GetNodeRequest, opts ...grpc.CallOption) (*GetNodeResponse, error) + // RequestNodeClaimDeletion queues a delete of the Karpenter NodeClaim that + // owns a live node. The operator performs the delete; poll + // GetNodeClaimDeletionStatus for progress. + RequestNodeClaimDeletion(ctx context.Context, in *RequestNodeClaimDeletionRequest, opts ...grpc.CallOption) (*RequestNodeClaimDeletionResponse, error) + // GetNodeClaimDeletionStatus returns the newest deletion request for a node. + GetNodeClaimDeletionStatus(ctx context.Context, in *GetNodeClaimDeletionStatusRequest, opts ...grpc.CallOption) (*GetNodeClaimDeletionStatusResponse, error) + // GetNodeClaimRefs is the batch form of the node_claim field on + // GetNodeResponse: one call answers "may this NodeClaim be deleted right now" + // for a page of nodes, so a table does not fetch a node at a time. + GetNodeClaimRefs(ctx context.Context, in *GetNodeClaimRefsRequest, opts ...grpc.CallOption) (*GetNodeClaimRefsResponse, error) + // RequestNodeClaimDeletions queues deletes for several nodes in one call so + // they reach Karpenter inside one provisioning batch window, which is what + // lets it consolidate rather than replace each node it loses. Refusals are + // reported per item; the queued items stay queued. + RequestNodeClaimDeletions(ctx context.Context, in *RequestNodeClaimDeletionsRequest, opts ...grpc.CallOption) (*RequestNodeClaimDeletionsResponse, error) // GetWorkloads retrieves all workloads for a specific cluster. GetWorkloads(ctx context.Context, in *GetWorkloadsRequest, opts ...grpc.CallOption) (*GetWorkloadsResponse, error) // ListWorkloadsPaginated retrieves a single keyset-paginated page of top-level @@ -118,6 +149,13 @@ type K8SServiceClient interface { // memory/gpu top-N + other) over the full filtered workload set in one pass. // Companion to ListWorkloadsPaginated for summary cards, top-N lists, treemaps. GetWorkloadsSummary(ctx context.Context, in *GetWorkloadsSummaryRequest, opts ...grpc.CallOption) (*GetWorkloadsSummaryResponse, error) + // GetWorkloadRequestTrends returns CPU and memory requested over time for the + // same filtered top-level workload set as GetWorkloadsSummary, split into + // managed workloads (tracked by an enabled legacy policy target) and + // unmanaged ones. For managed workloads it also returns the original request: + // the baseline requests captured before the first DevZero change, times the + // pods running in each bucket. + GetWorkloadRequestTrends(ctx context.Context, in *GetWorkloadRequestTrendsRequest, opts ...grpc.CallOption) (*GetWorkloadRequestTrendsResponse, error) // GetWorkload retrieves detailed information for a specific workload. GetWorkload(ctx context.Context, in *GetWorkloadRequest, opts ...grpc.CallOption) (*GetWorkloadResponse, error) // GetWorkloadContainerPercentiles retrieves per-container percentile metrics for a workload. @@ -132,6 +170,10 @@ type K8SServiceClient interface { // GetNodeGPUMigInstances retrieves the most recent MIG-instance telemetry for a node. // Returns an empty instances list for nodes with no MIG-partitioned GPUs. GetNodeGPUMigInstances(ctx context.Context, in *GetNodeGPUMigInstancesRequest, opts ...grpc.CallOption) (*GetNodeGPUMigInstancesResponse, error) + // GetNodeGPUMigInstanceTimeseries retrieves per-MIG-instance utilization as + // a time series for a node over a time range. Returns an empty instances + // list for nodes with no MIG-partitioned GPUs. + GetNodeGPUMigInstanceTimeseries(ctx context.Context, in *GetNodeGPUMigInstanceTimeseriesRequest, opts ...grpc.CallOption) (*GetNodeGPUMigInstanceTimeseriesResponse, error) // GetContainerIndividualGPUMetrics retrieves the most recent per-GPU breakdown for every // container in a workload that has GPU data. GetContainerIndividualGPUMetrics(ctx context.Context, in *GetContainerIndividualGPUMetricsRequest, opts ...grpc.CallOption) (*GetContainerIndividualGPUMetricsResponse, error) @@ -324,6 +366,24 @@ func (c *k8SServiceClient) GetNodeGroupsUtilization(ctx context.Context, in *Get return out, nil } +func (c *k8SServiceClient) GetClusterNodeInventory(ctx context.Context, in *GetClusterNodeInventoryRequest, opts ...grpc.CallOption) (*GetClusterNodeInventoryResponse, error) { + out := new(GetClusterNodeInventoryResponse) + err := c.cc.Invoke(ctx, K8SService_GetClusterNodeInventory_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *k8SServiceClient) GetClusterNodeFilterIndex(ctx context.Context, in *GetClusterNodeFilterIndexRequest, opts ...grpc.CallOption) (*GetClusterNodeFilterIndexResponse, error) { + out := new(GetClusterNodeFilterIndexResponse) + err := c.cc.Invoke(ctx, K8SService_GetClusterNodeFilterIndex_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + func (c *k8SServiceClient) GetNodeGroup(ctx context.Context, in *GetNodeGroupRequest, opts ...grpc.CallOption) (*GetNodeGroupResponse, error) { out := new(GetNodeGroupResponse) err := c.cc.Invoke(ctx, K8SService_GetNodeGroup_FullMethodName, in, out, opts...) @@ -342,6 +402,42 @@ func (c *k8SServiceClient) GetNode(ctx context.Context, in *GetNodeRequest, opts return out, nil } +func (c *k8SServiceClient) RequestNodeClaimDeletion(ctx context.Context, in *RequestNodeClaimDeletionRequest, opts ...grpc.CallOption) (*RequestNodeClaimDeletionResponse, error) { + out := new(RequestNodeClaimDeletionResponse) + err := c.cc.Invoke(ctx, K8SService_RequestNodeClaimDeletion_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *k8SServiceClient) GetNodeClaimDeletionStatus(ctx context.Context, in *GetNodeClaimDeletionStatusRequest, opts ...grpc.CallOption) (*GetNodeClaimDeletionStatusResponse, error) { + out := new(GetNodeClaimDeletionStatusResponse) + err := c.cc.Invoke(ctx, K8SService_GetNodeClaimDeletionStatus_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *k8SServiceClient) GetNodeClaimRefs(ctx context.Context, in *GetNodeClaimRefsRequest, opts ...grpc.CallOption) (*GetNodeClaimRefsResponse, error) { + out := new(GetNodeClaimRefsResponse) + err := c.cc.Invoke(ctx, K8SService_GetNodeClaimRefs_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *k8SServiceClient) RequestNodeClaimDeletions(ctx context.Context, in *RequestNodeClaimDeletionsRequest, opts ...grpc.CallOption) (*RequestNodeClaimDeletionsResponse, error) { + out := new(RequestNodeClaimDeletionsResponse) + err := c.cc.Invoke(ctx, K8SService_RequestNodeClaimDeletions_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + func (c *k8SServiceClient) GetWorkloads(ctx context.Context, in *GetWorkloadsRequest, opts ...grpc.CallOption) (*GetWorkloadsResponse, error) { out := new(GetWorkloadsResponse) err := c.cc.Invoke(ctx, K8SService_GetWorkloads_FullMethodName, in, out, opts...) @@ -369,6 +465,15 @@ func (c *k8SServiceClient) GetWorkloadsSummary(ctx context.Context, in *GetWorkl return out, nil } +func (c *k8SServiceClient) GetWorkloadRequestTrends(ctx context.Context, in *GetWorkloadRequestTrendsRequest, opts ...grpc.CallOption) (*GetWorkloadRequestTrendsResponse, error) { + out := new(GetWorkloadRequestTrendsResponse) + err := c.cc.Invoke(ctx, K8SService_GetWorkloadRequestTrends_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + func (c *k8SServiceClient) GetWorkload(ctx context.Context, in *GetWorkloadRequest, opts ...grpc.CallOption) (*GetWorkloadResponse, error) { out := new(GetWorkloadResponse) err := c.cc.Invoke(ctx, K8SService_GetWorkload_FullMethodName, in, out, opts...) @@ -414,6 +519,15 @@ func (c *k8SServiceClient) GetNodeGPUMigInstances(ctx context.Context, in *GetNo return out, nil } +func (c *k8SServiceClient) GetNodeGPUMigInstanceTimeseries(ctx context.Context, in *GetNodeGPUMigInstanceTimeseriesRequest, opts ...grpc.CallOption) (*GetNodeGPUMigInstanceTimeseriesResponse, error) { + out := new(GetNodeGPUMigInstanceTimeseriesResponse) + err := c.cc.Invoke(ctx, K8SService_GetNodeGPUMigInstanceTimeseries_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + func (c *k8SServiceClient) GetContainerIndividualGPUMetrics(ctx context.Context, in *GetContainerIndividualGPUMetricsRequest, opts ...grpc.CallOption) (*GetContainerIndividualGPUMetricsResponse, error) { out := new(GetContainerIndividualGPUMetricsResponse) err := c.cc.Invoke(ctx, K8SService_GetContainerIndividualGPUMetrics_FullMethodName, in, out, opts...) @@ -667,10 +781,33 @@ type K8SServiceServer interface { GetAllNodeGroups(context.Context, *GetAllNodeGroupsRequest) (*GetAllNodeGroupsResponse, error) // GetNodeGroupsUtilization retrieves utilization data for node groups in a specific cluster. GetNodeGroupsUtilization(context.Context, *GetNodeGroupsUtilizationRequest) (*GetNodeGroupsUtilizationResponse, error) + // GetClusterNodeInventory returns the current node inventory of a cluster: + // one row per node pool, or one keyset page of nodes, plus a whole-cluster + // rollup on every response. Postgres only; nothing in it reads a time range. + GetClusterNodeInventory(context.Context, *GetClusterNodeInventoryRequest) (*GetClusterNodeInventoryResponse, error) + // GetClusterNodeFilterIndex retrieves the filterable node attributes for a + // cluster: distinct node label sets with their per-node attribution, and the + // scalar spec fields of the Karpenter NodePools backing its node groups. + GetClusterNodeFilterIndex(context.Context, *GetClusterNodeFilterIndexRequest) (*GetClusterNodeFilterIndexResponse, error) // GetNodeGroup retrieves node group for a specific cluster. GetNodeGroup(context.Context, *GetNodeGroupRequest) (*GetNodeGroupResponse, error) // GetNode retrieves detailed information for a specific node. GetNode(context.Context, *GetNodeRequest) (*GetNodeResponse, error) + // RequestNodeClaimDeletion queues a delete of the Karpenter NodeClaim that + // owns a live node. The operator performs the delete; poll + // GetNodeClaimDeletionStatus for progress. + RequestNodeClaimDeletion(context.Context, *RequestNodeClaimDeletionRequest) (*RequestNodeClaimDeletionResponse, error) + // GetNodeClaimDeletionStatus returns the newest deletion request for a node. + GetNodeClaimDeletionStatus(context.Context, *GetNodeClaimDeletionStatusRequest) (*GetNodeClaimDeletionStatusResponse, error) + // GetNodeClaimRefs is the batch form of the node_claim field on + // GetNodeResponse: one call answers "may this NodeClaim be deleted right now" + // for a page of nodes, so a table does not fetch a node at a time. + GetNodeClaimRefs(context.Context, *GetNodeClaimRefsRequest) (*GetNodeClaimRefsResponse, error) + // RequestNodeClaimDeletions queues deletes for several nodes in one call so + // they reach Karpenter inside one provisioning batch window, which is what + // lets it consolidate rather than replace each node it loses. Refusals are + // reported per item; the queued items stay queued. + RequestNodeClaimDeletions(context.Context, *RequestNodeClaimDeletionsRequest) (*RequestNodeClaimDeletionsResponse, error) // GetWorkloads retrieves all workloads for a specific cluster. GetWorkloads(context.Context, *GetWorkloadsRequest) (*GetWorkloadsResponse, error) // ListWorkloadsPaginated retrieves a single keyset-paginated page of top-level @@ -680,6 +817,13 @@ type K8SServiceServer interface { // memory/gpu top-N + other) over the full filtered workload set in one pass. // Companion to ListWorkloadsPaginated for summary cards, top-N lists, treemaps. GetWorkloadsSummary(context.Context, *GetWorkloadsSummaryRequest) (*GetWorkloadsSummaryResponse, error) + // GetWorkloadRequestTrends returns CPU and memory requested over time for the + // same filtered top-level workload set as GetWorkloadsSummary, split into + // managed workloads (tracked by an enabled legacy policy target) and + // unmanaged ones. For managed workloads it also returns the original request: + // the baseline requests captured before the first DevZero change, times the + // pods running in each bucket. + GetWorkloadRequestTrends(context.Context, *GetWorkloadRequestTrendsRequest) (*GetWorkloadRequestTrendsResponse, error) // GetWorkload retrieves detailed information for a specific workload. GetWorkload(context.Context, *GetWorkloadRequest) (*GetWorkloadResponse, error) // GetWorkloadContainerPercentiles retrieves per-container percentile metrics for a workload. @@ -694,6 +838,10 @@ type K8SServiceServer interface { // GetNodeGPUMigInstances retrieves the most recent MIG-instance telemetry for a node. // Returns an empty instances list for nodes with no MIG-partitioned GPUs. GetNodeGPUMigInstances(context.Context, *GetNodeGPUMigInstancesRequest) (*GetNodeGPUMigInstancesResponse, error) + // GetNodeGPUMigInstanceTimeseries retrieves per-MIG-instance utilization as + // a time series for a node over a time range. Returns an empty instances + // list for nodes with no MIG-partitioned GPUs. + GetNodeGPUMigInstanceTimeseries(context.Context, *GetNodeGPUMigInstanceTimeseriesRequest) (*GetNodeGPUMigInstanceTimeseriesResponse, error) // GetContainerIndividualGPUMetrics retrieves the most recent per-GPU breakdown for every // container in a workload that has GPU data. GetContainerIndividualGPUMetrics(context.Context, *GetContainerIndividualGPUMetricsRequest) (*GetContainerIndividualGPUMetricsResponse, error) @@ -792,12 +940,30 @@ func (UnimplementedK8SServiceServer) GetAllNodeGroups(context.Context, *GetAllNo func (UnimplementedK8SServiceServer) GetNodeGroupsUtilization(context.Context, *GetNodeGroupsUtilizationRequest) (*GetNodeGroupsUtilizationResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method GetNodeGroupsUtilization not implemented") } +func (UnimplementedK8SServiceServer) GetClusterNodeInventory(context.Context, *GetClusterNodeInventoryRequest) (*GetClusterNodeInventoryResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetClusterNodeInventory not implemented") +} +func (UnimplementedK8SServiceServer) GetClusterNodeFilterIndex(context.Context, *GetClusterNodeFilterIndexRequest) (*GetClusterNodeFilterIndexResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetClusterNodeFilterIndex not implemented") +} func (UnimplementedK8SServiceServer) GetNodeGroup(context.Context, *GetNodeGroupRequest) (*GetNodeGroupResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method GetNodeGroup not implemented") } func (UnimplementedK8SServiceServer) GetNode(context.Context, *GetNodeRequest) (*GetNodeResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method GetNode not implemented") } +func (UnimplementedK8SServiceServer) RequestNodeClaimDeletion(context.Context, *RequestNodeClaimDeletionRequest) (*RequestNodeClaimDeletionResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method RequestNodeClaimDeletion not implemented") +} +func (UnimplementedK8SServiceServer) GetNodeClaimDeletionStatus(context.Context, *GetNodeClaimDeletionStatusRequest) (*GetNodeClaimDeletionStatusResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetNodeClaimDeletionStatus not implemented") +} +func (UnimplementedK8SServiceServer) GetNodeClaimRefs(context.Context, *GetNodeClaimRefsRequest) (*GetNodeClaimRefsResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetNodeClaimRefs not implemented") +} +func (UnimplementedK8SServiceServer) RequestNodeClaimDeletions(context.Context, *RequestNodeClaimDeletionsRequest) (*RequestNodeClaimDeletionsResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method RequestNodeClaimDeletions not implemented") +} func (UnimplementedK8SServiceServer) GetWorkloads(context.Context, *GetWorkloadsRequest) (*GetWorkloadsResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method GetWorkloads not implemented") } @@ -807,6 +973,9 @@ func (UnimplementedK8SServiceServer) ListWorkloadsPaginated(context.Context, *Li func (UnimplementedK8SServiceServer) GetWorkloadsSummary(context.Context, *GetWorkloadsSummaryRequest) (*GetWorkloadsSummaryResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method GetWorkloadsSummary not implemented") } +func (UnimplementedK8SServiceServer) GetWorkloadRequestTrends(context.Context, *GetWorkloadRequestTrendsRequest) (*GetWorkloadRequestTrendsResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetWorkloadRequestTrends not implemented") +} func (UnimplementedK8SServiceServer) GetWorkload(context.Context, *GetWorkloadRequest) (*GetWorkloadResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method GetWorkload not implemented") } @@ -822,6 +991,9 @@ func (UnimplementedK8SServiceServer) GetLatestContainerRequestLimits(context.Con func (UnimplementedK8SServiceServer) GetNodeGPUMigInstances(context.Context, *GetNodeGPUMigInstancesRequest) (*GetNodeGPUMigInstancesResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method GetNodeGPUMigInstances not implemented") } +func (UnimplementedK8SServiceServer) GetNodeGPUMigInstanceTimeseries(context.Context, *GetNodeGPUMigInstanceTimeseriesRequest) (*GetNodeGPUMigInstanceTimeseriesResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetNodeGPUMigInstanceTimeseries not implemented") +} func (UnimplementedK8SServiceServer) GetContainerIndividualGPUMetrics(context.Context, *GetContainerIndividualGPUMetricsRequest) (*GetContainerIndividualGPUMetricsResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method GetContainerIndividualGPUMetrics not implemented") } @@ -1177,6 +1349,42 @@ func _K8SService_GetNodeGroupsUtilization_Handler(srv interface{}, ctx context.C return interceptor(ctx, in, info, handler) } +func _K8SService_GetClusterNodeInventory_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(GetClusterNodeInventoryRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(K8SServiceServer).GetClusterNodeInventory(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: K8SService_GetClusterNodeInventory_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(K8SServiceServer).GetClusterNodeInventory(ctx, req.(*GetClusterNodeInventoryRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _K8SService_GetClusterNodeFilterIndex_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(GetClusterNodeFilterIndexRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(K8SServiceServer).GetClusterNodeFilterIndex(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: K8SService_GetClusterNodeFilterIndex_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(K8SServiceServer).GetClusterNodeFilterIndex(ctx, req.(*GetClusterNodeFilterIndexRequest)) + } + return interceptor(ctx, in, info, handler) +} + func _K8SService_GetNodeGroup_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(GetNodeGroupRequest) if err := dec(in); err != nil { @@ -1213,6 +1421,78 @@ func _K8SService_GetNode_Handler(srv interface{}, ctx context.Context, dec func( return interceptor(ctx, in, info, handler) } +func _K8SService_RequestNodeClaimDeletion_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(RequestNodeClaimDeletionRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(K8SServiceServer).RequestNodeClaimDeletion(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: K8SService_RequestNodeClaimDeletion_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(K8SServiceServer).RequestNodeClaimDeletion(ctx, req.(*RequestNodeClaimDeletionRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _K8SService_GetNodeClaimDeletionStatus_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(GetNodeClaimDeletionStatusRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(K8SServiceServer).GetNodeClaimDeletionStatus(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: K8SService_GetNodeClaimDeletionStatus_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(K8SServiceServer).GetNodeClaimDeletionStatus(ctx, req.(*GetNodeClaimDeletionStatusRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _K8SService_GetNodeClaimRefs_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(GetNodeClaimRefsRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(K8SServiceServer).GetNodeClaimRefs(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: K8SService_GetNodeClaimRefs_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(K8SServiceServer).GetNodeClaimRefs(ctx, req.(*GetNodeClaimRefsRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _K8SService_RequestNodeClaimDeletions_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(RequestNodeClaimDeletionsRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(K8SServiceServer).RequestNodeClaimDeletions(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: K8SService_RequestNodeClaimDeletions_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(K8SServiceServer).RequestNodeClaimDeletions(ctx, req.(*RequestNodeClaimDeletionsRequest)) + } + return interceptor(ctx, in, info, handler) +} + func _K8SService_GetWorkloads_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(GetWorkloadsRequest) if err := dec(in); err != nil { @@ -1267,6 +1547,24 @@ func _K8SService_GetWorkloadsSummary_Handler(srv interface{}, ctx context.Contex return interceptor(ctx, in, info, handler) } +func _K8SService_GetWorkloadRequestTrends_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(GetWorkloadRequestTrendsRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(K8SServiceServer).GetWorkloadRequestTrends(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: K8SService_GetWorkloadRequestTrends_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(K8SServiceServer).GetWorkloadRequestTrends(ctx, req.(*GetWorkloadRequestTrendsRequest)) + } + return interceptor(ctx, in, info, handler) +} + func _K8SService_GetWorkload_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(GetWorkloadRequest) if err := dec(in); err != nil { @@ -1357,6 +1655,24 @@ func _K8SService_GetNodeGPUMigInstances_Handler(srv interface{}, ctx context.Con return interceptor(ctx, in, info, handler) } +func _K8SService_GetNodeGPUMigInstanceTimeseries_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(GetNodeGPUMigInstanceTimeseriesRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(K8SServiceServer).GetNodeGPUMigInstanceTimeseries(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: K8SService_GetNodeGPUMigInstanceTimeseries_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(K8SServiceServer).GetNodeGPUMigInstanceTimeseries(ctx, req.(*GetNodeGPUMigInstanceTimeseriesRequest)) + } + return interceptor(ctx, in, info, handler) +} + func _K8SService_GetContainerIndividualGPUMetrics_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(GetContainerIndividualGPUMetricsRequest) if err := dec(in); err != nil { @@ -1856,6 +2172,14 @@ var K8SService_ServiceDesc = grpc.ServiceDesc{ MethodName: "GetNodeGroupsUtilization", Handler: _K8SService_GetNodeGroupsUtilization_Handler, }, + { + MethodName: "GetClusterNodeInventory", + Handler: _K8SService_GetClusterNodeInventory_Handler, + }, + { + MethodName: "GetClusterNodeFilterIndex", + Handler: _K8SService_GetClusterNodeFilterIndex_Handler, + }, { MethodName: "GetNodeGroup", Handler: _K8SService_GetNodeGroup_Handler, @@ -1864,6 +2188,22 @@ var K8SService_ServiceDesc = grpc.ServiceDesc{ MethodName: "GetNode", Handler: _K8SService_GetNode_Handler, }, + { + MethodName: "RequestNodeClaimDeletion", + Handler: _K8SService_RequestNodeClaimDeletion_Handler, + }, + { + MethodName: "GetNodeClaimDeletionStatus", + Handler: _K8SService_GetNodeClaimDeletionStatus_Handler, + }, + { + MethodName: "GetNodeClaimRefs", + Handler: _K8SService_GetNodeClaimRefs_Handler, + }, + { + MethodName: "RequestNodeClaimDeletions", + Handler: _K8SService_RequestNodeClaimDeletions_Handler, + }, { MethodName: "GetWorkloads", Handler: _K8SService_GetWorkloads_Handler, @@ -1876,6 +2216,10 @@ var K8SService_ServiceDesc = grpc.ServiceDesc{ MethodName: "GetWorkloadsSummary", Handler: _K8SService_GetWorkloadsSummary_Handler, }, + { + MethodName: "GetWorkloadRequestTrends", + Handler: _K8SService_GetWorkloadRequestTrends_Handler, + }, { MethodName: "GetWorkload", Handler: _K8SService_GetWorkload_Handler, @@ -1896,6 +2240,10 @@ var K8SService_ServiceDesc = grpc.ServiceDesc{ MethodName: "GetNodeGPUMigInstances", Handler: _K8SService_GetNodeGPUMigInstances_Handler, }, + { + MethodName: "GetNodeGPUMigInstanceTimeseries", + Handler: _K8SService_GetNodeGPUMigInstanceTimeseries_Handler, + }, { MethodName: "GetContainerIndividualGPUMetrics", Handler: _K8SService_GetContainerIndividualGPUMetrics_Handler, diff --git a/gen/api/v1/metrics_collector.pb.go b/gen/api/v1/metrics_collector.pb.go index 60e2305f..c32948dc 100644 --- a/gen/api/v1/metrics_collector.pb.go +++ b/gen/api/v1/metrics_collector.pb.go @@ -225,159 +225,296 @@ const ( // NVIDIA mig-parted ConfigMap holding the declared MIG partitioning // profiles (desired state; applied state rides node labels). ResourceType_RESOURCE_TYPE_MIG_PARTED_CONFIG ResourceType = 85 + // Per-MIG-instance GPU utilization time series, one row per (node, + // mig_instance, minute). Exploded from NODE_RESOURCE's GPUMigInstances + // on ingestion; see minute_bucketed_gpu_mig_instance_metrics. + ResourceType_RESOURCE_TYPE_GPU_MIG_INSTANCE_METRICS ResourceType = 86 + // Scheduler placement-metrics pipeline (#10022, #10023): one row per (metric + // series, scrape) scraped from dz-scheduler's Prometheus `/metrics` endpoint + // (contract documented in the dz-scheduler repo). Covers placement + // counters/gauges plus exploded histogram bucket/sum/count rows; dakr writes + // it to scheduler_placement_metrics. Allow-listed like every other + // ResourceType here: dz-scheduler is a trusted first-party emitter, but the + // enum still gates which resource kinds the ingestion path will accept. + ResourceType_RESOURCE_TYPE_SCHEDULER_PLACEMENT_METRIC ResourceType = 87 + // Argo CD Application (argoproj.io/v1alpha1). Emitted by zxporter's Argo CD + // collector. dakr stores the object in k8s_argo_applications and explodes + // status.resources[] into k8s_argo_application_resources. + ResourceType_RESOURCE_TYPE_ARGO_APPLICATION ResourceType = 88 + // Argo CD ApplicationSet (argoproj.io/v1alpha1). Stored in + // k8s_argo_application_sets; status.resources[] names generated Applications. + ResourceType_RESOURCE_TYPE_ARGO_APPLICATION_SET ResourceType = 89 + // One entry of an Argo CD Application status.resources[], exploded server-side + // into k8s_argo_application_resources. Derived by dakr from + // RESOURCE_TYPE_ARGO_APPLICATION, never sent on the wire by zxporter -- the + // same arrangement as RESOURCE_TYPE_WORKLOAD_RULE_OOM. + ResourceType_RESOURCE_TYPE_ARGO_APPLICATION_RESOURCE ResourceType = 90 + // FluxCD GitRepository (source.toolkit.fluxcd.io/v1). Full explorer support. + ResourceType_RESOURCE_TYPE_FLUX_GIT_REPOSITORY ResourceType = 91 + // FluxCD OCIRepository (source.toolkit.fluxcd.io/v1). Full explorer support. + ResourceType_RESOURCE_TYPE_FLUX_OCI_REPOSITORY ResourceType = 92 + // FluxCD HelmRepository (source.toolkit.fluxcd.io/v1). Full explorer support. + ResourceType_RESOURCE_TYPE_FLUX_HELM_REPOSITORY ResourceType = 93 + // FluxCD HelmChart (source.toolkit.fluxcd.io/v1). Full explorer support. + ResourceType_RESOURCE_TYPE_FLUX_HELM_CHART ResourceType = 94 + // FluxCD Bucket (source.toolkit.fluxcd.io/v1). Full explorer support. + ResourceType_RESOURCE_TYPE_FLUX_BUCKET ResourceType = 95 + // FluxCD Kustomization (kustomize.toolkit.fluxcd.io/v1). Full explorer support, + // with a sourceRef relation to the Flux source kinds above. + ResourceType_RESOURCE_TYPE_FLUX_KUSTOMIZATION ResourceType = 96 + // FluxCD HelmRelease (helm.toolkit.fluxcd.io/v2). Full explorer support, with a + // chartRef/chart.sourceRef relation to the Flux source kinds above. + ResourceType_RESOURCE_TYPE_FLUX_HELM_RELEASE ResourceType = 97 + // FluxCD ExternalArtifact (source.toolkit.fluxcd.io/v1). Collector + storage + // only -- no bespoke explorer detail panel yet, see FluxGenericDetails. + ResourceType_RESOURCE_TYPE_FLUX_EXTERNAL_ARTIFACT ResourceType = 98 + // FluxCD Alert (notification.toolkit.fluxcd.io/v1beta3). Collector + storage + // only -- no bespoke explorer detail panel yet, see FluxGenericDetails. + ResourceType_RESOURCE_TYPE_FLUX_ALERT ResourceType = 99 + // FluxCD Provider (notification.toolkit.fluxcd.io/v1beta3). Collector + storage + // only -- no bespoke explorer detail panel yet, see FluxGenericDetails. + ResourceType_RESOURCE_TYPE_FLUX_PROVIDER ResourceType = 100 + // FluxCD Receiver (notification.toolkit.fluxcd.io/v1). Collector + storage + // only -- no bespoke explorer detail panel yet, see FluxGenericDetails. + ResourceType_RESOURCE_TYPE_FLUX_RECEIVER ResourceType = 101 + // Flux Operator FluxInstance (fluxcd.controlplane.io/v1). Collector + storage + // only -- no bespoke explorer detail panel yet, see FluxGenericDetails. + ResourceType_RESOURCE_TYPE_FLUX_INSTANCE ResourceType = 102 + // Flux Operator FluxReport (fluxcd.controlplane.io/v1). Collector + storage + // only -- no bespoke explorer detail panel yet, see FluxGenericDetails. + ResourceType_RESOURCE_TYPE_FLUX_REPORT ResourceType = 103 + // Flux Operator ResourceSetInputProvider (fluxcd.controlplane.io/v1). Collector + // + storage only -- no bespoke explorer detail panel yet, see FluxGenericDetails. + ResourceType_RESOURCE_TYPE_FLUX_RESOURCE_SET_INPUT_PROVIDER ResourceType = 104 + // Flux Operator ResourceSet (fluxcd.controlplane.io/v1). Collector + storage + // only -- no bespoke explorer detail panel yet, see FluxGenericDetails. + ResourceType_RESOURCE_TYPE_FLUX_RESOURCE_SET ResourceType = 105 + // FluxCD ImageRepository (image.toolkit.fluxcd.io/v1). Collector + storage + // only -- no bespoke explorer detail panel yet, see FluxGenericDetails. + ResourceType_RESOURCE_TYPE_FLUX_IMAGE_REPOSITORY ResourceType = 106 + // FluxCD ImagePolicy (image.toolkit.fluxcd.io/v1). Collector + storage only + // -- no bespoke explorer detail panel yet, see FluxGenericDetails. + ResourceType_RESOURCE_TYPE_FLUX_IMAGE_POLICY ResourceType = 107 + // FluxCD ImageUpdateAutomation (image.toolkit.fluxcd.io/v1). Collector + + // storage only -- no bespoke explorer detail panel yet, see FluxGenericDetails. + ResourceType_RESOURCE_TYPE_FLUX_IMAGE_UPDATE_AUTOMATION ResourceType = 108 + // Flagger Canary (flagger.app/v1beta1). Full explorer support, with a + // targetRef relation to the workload it drives and an autoscalerRef relation + // to that workload HPA or ScaledObject. + ResourceType_RESOURCE_TYPE_FLAGGER_CANARY ResourceType = 109 + // Flagger MetricTemplate (flagger.app/v1beta1). Full explorer support; the + // referenced half of a Canary analysis metric templateRef. + ResourceType_RESOURCE_TYPE_FLAGGER_METRIC_TEMPLATE ResourceType = 110 + // Flagger AlertProvider (flagger.app/v1beta1). Listed tier -- explorer + // columns and search but no bespoke detail panel yet, see + // FlaggerAlertProviderDetails. The referenced half of a Canary alert + // providerRef. + ResourceType_RESOURCE_TYPE_FLAGGER_ALERT_PROVIDER ResourceType = 111 ) // Enum value maps for ResourceType. var ( ResourceType_name = map[int32]string{ - 0: "RESOURCE_TYPE_UNSPECIFIED", - 1: "RESOURCE_TYPE_NODE", - 2: "RESOURCE_TYPE_POD", - 3: "RESOURCE_TYPE_NAMESPACE", - 4: "RESOURCE_TYPE_EVENT", - 5: "RESOURCE_TYPE_ENDPOINTS", - 6: "RESOURCE_TYPE_SERVICE_ACCOUNT", - 7: "RESOURCE_TYPE_LIMIT_RANGE", - 8: "RESOURCE_TYPE_RESOURCE_QUOTA", - 9: "RESOURCE_TYPE_DEPLOYMENT", - 10: "RESOURCE_TYPE_STATEFUL_SET", - 11: "RESOURCE_TYPE_DAEMON_SET", - 12: "RESOURCE_TYPE_REPLICA_SET", - 13: "RESOURCE_TYPE_REPLICATION_CONTROLLER", - 14: "RESOURCE_TYPE_JOB", - 15: "RESOURCE_TYPE_CRON_JOB", - 16: "RESOURCE_TYPE_PERSISTENT_VOLUME_CLAIM", - 17: "RESOURCE_TYPE_PERSISTENT_VOLUME", - 18: "RESOURCE_TYPE_STORAGE_CLASS", - 19: "RESOURCE_TYPE_SERVICE", - 20: "RESOURCE_TYPE_INGRESS", - 21: "RESOURCE_TYPE_INGRESS_CLASS", - 22: "RESOURCE_TYPE_NETWORK_POLICY", - 23: "RESOURCE_TYPE_ROLE", - 24: "RESOURCE_TYPE_ROLE_BINDING", - 25: "RESOURCE_TYPE_CLUSTER_ROLE", - 26: "RESOURCE_TYPE_CLUSTER_ROLE_BINDING", - 27: "RESOURCE_TYPE_HORIZONTAL_POD_AUTOSCALER", - 28: "RESOURCE_TYPE_VERTICAL_POD_AUTOSCALER", - 29: "RESOURCE_TYPE_POD_DISRUPTION_BUDGET", - 30: "RESOURCE_TYPE_POD_SECURITY_POLICY", - 31: "RESOURCE_TYPE_CUSTOM_RESOURCE_DEFINITION", - 32: "RESOURCE_TYPE_CUSTOM_RESOURCE", - 33: "RESOURCE_TYPE_CONFIG_MAP", - 34: "RESOURCE_TYPE_SECRET", - 35: "RESOURCE_TYPE_CONTAINER", - 36: "RESOURCE_TYPE_NODE_RESOURCE", - 37: "RESOURCE_TYPE_CONTAINER_RESOURCE", - 38: "RESOURCE_TYPE_CLUSTER", - 39: "RESOURCE_TYPE_CSI_NODE", - 40: "RESOURCE_TYPE_KARPENTER", - 41: "RESOURCE_TYPE_DATADOG", - 42: "RESOURCE_TYPE_ARGO_ROLLOUTS", - 43: "RESOURCE_TYPE_KEDA", - 44: "RESOURCE_TYPE_KEDA_SCALED_OBJECT", - 45: "RESOURCE_TYPE_KEDA_SCALED_JOB", - 46: "RESOURCE_TYPE_CSI_DRIVER", - 47: "RESOURCE_TYPE_CSI_STORAGE_CAPACITY", - 48: "RESOURCE_TYPE_VOLUME_ATTACHMENT", - 49: "RESOURCE_TYPE_KUBEFLOW_NOTEBOOK", - 50: "RESOURCE_TYPE_VOLCANO_JOB", - 51: "RESOURCE_TYPE_SPARK_APPLICATION", - 52: "RESOURCE_TYPE_SCHEDULED_SPARK_APPLICATION", - 53: "RESOURCE_TYPE_CRON_VOLCANO_JOB", - 54: "RESOURCE_TYPE_PVC_METRICS", - 55: "RESOURCE_TYPE_WORKLOAD_RECOMMENDATION", - 56: "RESOURCE_TYPE_WORKLOAD_RULE", - 57: "RESOURCE_TYPE_WORKLOAD_RULE_OOM", - 58: "RESOURCE_TYPE_WORKLOAD_RULE_CPU_THROTTLE", - 59: "RESOURCE_TYPE_CNPG_CLUSTER", - 60: "RESOURCE_TYPE_CONTAINER_OOM_EVENT", - 61: "RESOURCE_TYPE_CONTAINER_CRASHLOOP_EVENT", - 62: "RESOURCE_TYPE_CONTAINER_STARTUP_LIFECYCLE", - 63: "RESOURCE_TYPE_CONTAINER_CPU_THROTTLE_EVENT", - 77: "RESOURCE_TYPE_CLUSTER_SNAPSHOT", - 78: "RESOURCE_TYPE_KARPENTER_SETTINGS", - 79: "RESOURCE_TYPE_NODE_LIFECYCLE_TRANSITION", - 80: "RESOURCE_TYPE_POD_UNSCHEDULABLE_EVENT", - 81: "RESOURCE_TYPE_KYVERNO_POLICY", - 82: "RESOURCE_TYPE_KYVERNO_POLICY_REPORT", - 83: "RESOURCE_TYPE_GATEKEEPER_CONSTRAINT_TEMPLATE", - 84: "RESOURCE_TYPE_GATEKEEPER_CONSTRAINT", - 85: "RESOURCE_TYPE_MIG_PARTED_CONFIG", + 0: "RESOURCE_TYPE_UNSPECIFIED", + 1: "RESOURCE_TYPE_NODE", + 2: "RESOURCE_TYPE_POD", + 3: "RESOURCE_TYPE_NAMESPACE", + 4: "RESOURCE_TYPE_EVENT", + 5: "RESOURCE_TYPE_ENDPOINTS", + 6: "RESOURCE_TYPE_SERVICE_ACCOUNT", + 7: "RESOURCE_TYPE_LIMIT_RANGE", + 8: "RESOURCE_TYPE_RESOURCE_QUOTA", + 9: "RESOURCE_TYPE_DEPLOYMENT", + 10: "RESOURCE_TYPE_STATEFUL_SET", + 11: "RESOURCE_TYPE_DAEMON_SET", + 12: "RESOURCE_TYPE_REPLICA_SET", + 13: "RESOURCE_TYPE_REPLICATION_CONTROLLER", + 14: "RESOURCE_TYPE_JOB", + 15: "RESOURCE_TYPE_CRON_JOB", + 16: "RESOURCE_TYPE_PERSISTENT_VOLUME_CLAIM", + 17: "RESOURCE_TYPE_PERSISTENT_VOLUME", + 18: "RESOURCE_TYPE_STORAGE_CLASS", + 19: "RESOURCE_TYPE_SERVICE", + 20: "RESOURCE_TYPE_INGRESS", + 21: "RESOURCE_TYPE_INGRESS_CLASS", + 22: "RESOURCE_TYPE_NETWORK_POLICY", + 23: "RESOURCE_TYPE_ROLE", + 24: "RESOURCE_TYPE_ROLE_BINDING", + 25: "RESOURCE_TYPE_CLUSTER_ROLE", + 26: "RESOURCE_TYPE_CLUSTER_ROLE_BINDING", + 27: "RESOURCE_TYPE_HORIZONTAL_POD_AUTOSCALER", + 28: "RESOURCE_TYPE_VERTICAL_POD_AUTOSCALER", + 29: "RESOURCE_TYPE_POD_DISRUPTION_BUDGET", + 30: "RESOURCE_TYPE_POD_SECURITY_POLICY", + 31: "RESOURCE_TYPE_CUSTOM_RESOURCE_DEFINITION", + 32: "RESOURCE_TYPE_CUSTOM_RESOURCE", + 33: "RESOURCE_TYPE_CONFIG_MAP", + 34: "RESOURCE_TYPE_SECRET", + 35: "RESOURCE_TYPE_CONTAINER", + 36: "RESOURCE_TYPE_NODE_RESOURCE", + 37: "RESOURCE_TYPE_CONTAINER_RESOURCE", + 38: "RESOURCE_TYPE_CLUSTER", + 39: "RESOURCE_TYPE_CSI_NODE", + 40: "RESOURCE_TYPE_KARPENTER", + 41: "RESOURCE_TYPE_DATADOG", + 42: "RESOURCE_TYPE_ARGO_ROLLOUTS", + 43: "RESOURCE_TYPE_KEDA", + 44: "RESOURCE_TYPE_KEDA_SCALED_OBJECT", + 45: "RESOURCE_TYPE_KEDA_SCALED_JOB", + 46: "RESOURCE_TYPE_CSI_DRIVER", + 47: "RESOURCE_TYPE_CSI_STORAGE_CAPACITY", + 48: "RESOURCE_TYPE_VOLUME_ATTACHMENT", + 49: "RESOURCE_TYPE_KUBEFLOW_NOTEBOOK", + 50: "RESOURCE_TYPE_VOLCANO_JOB", + 51: "RESOURCE_TYPE_SPARK_APPLICATION", + 52: "RESOURCE_TYPE_SCHEDULED_SPARK_APPLICATION", + 53: "RESOURCE_TYPE_CRON_VOLCANO_JOB", + 54: "RESOURCE_TYPE_PVC_METRICS", + 55: "RESOURCE_TYPE_WORKLOAD_RECOMMENDATION", + 56: "RESOURCE_TYPE_WORKLOAD_RULE", + 57: "RESOURCE_TYPE_WORKLOAD_RULE_OOM", + 58: "RESOURCE_TYPE_WORKLOAD_RULE_CPU_THROTTLE", + 59: "RESOURCE_TYPE_CNPG_CLUSTER", + 60: "RESOURCE_TYPE_CONTAINER_OOM_EVENT", + 61: "RESOURCE_TYPE_CONTAINER_CRASHLOOP_EVENT", + 62: "RESOURCE_TYPE_CONTAINER_STARTUP_LIFECYCLE", + 63: "RESOURCE_TYPE_CONTAINER_CPU_THROTTLE_EVENT", + 77: "RESOURCE_TYPE_CLUSTER_SNAPSHOT", + 78: "RESOURCE_TYPE_KARPENTER_SETTINGS", + 79: "RESOURCE_TYPE_NODE_LIFECYCLE_TRANSITION", + 80: "RESOURCE_TYPE_POD_UNSCHEDULABLE_EVENT", + 81: "RESOURCE_TYPE_KYVERNO_POLICY", + 82: "RESOURCE_TYPE_KYVERNO_POLICY_REPORT", + 83: "RESOURCE_TYPE_GATEKEEPER_CONSTRAINT_TEMPLATE", + 84: "RESOURCE_TYPE_GATEKEEPER_CONSTRAINT", + 85: "RESOURCE_TYPE_MIG_PARTED_CONFIG", + 86: "RESOURCE_TYPE_GPU_MIG_INSTANCE_METRICS", + 87: "RESOURCE_TYPE_SCHEDULER_PLACEMENT_METRIC", + 88: "RESOURCE_TYPE_ARGO_APPLICATION", + 89: "RESOURCE_TYPE_ARGO_APPLICATION_SET", + 90: "RESOURCE_TYPE_ARGO_APPLICATION_RESOURCE", + 91: "RESOURCE_TYPE_FLUX_GIT_REPOSITORY", + 92: "RESOURCE_TYPE_FLUX_OCI_REPOSITORY", + 93: "RESOURCE_TYPE_FLUX_HELM_REPOSITORY", + 94: "RESOURCE_TYPE_FLUX_HELM_CHART", + 95: "RESOURCE_TYPE_FLUX_BUCKET", + 96: "RESOURCE_TYPE_FLUX_KUSTOMIZATION", + 97: "RESOURCE_TYPE_FLUX_HELM_RELEASE", + 98: "RESOURCE_TYPE_FLUX_EXTERNAL_ARTIFACT", + 99: "RESOURCE_TYPE_FLUX_ALERT", + 100: "RESOURCE_TYPE_FLUX_PROVIDER", + 101: "RESOURCE_TYPE_FLUX_RECEIVER", + 102: "RESOURCE_TYPE_FLUX_INSTANCE", + 103: "RESOURCE_TYPE_FLUX_REPORT", + 104: "RESOURCE_TYPE_FLUX_RESOURCE_SET_INPUT_PROVIDER", + 105: "RESOURCE_TYPE_FLUX_RESOURCE_SET", + 106: "RESOURCE_TYPE_FLUX_IMAGE_REPOSITORY", + 107: "RESOURCE_TYPE_FLUX_IMAGE_POLICY", + 108: "RESOURCE_TYPE_FLUX_IMAGE_UPDATE_AUTOMATION", + 109: "RESOURCE_TYPE_FLAGGER_CANARY", + 110: "RESOURCE_TYPE_FLAGGER_METRIC_TEMPLATE", + 111: "RESOURCE_TYPE_FLAGGER_ALERT_PROVIDER", } ResourceType_value = map[string]int32{ - "RESOURCE_TYPE_UNSPECIFIED": 0, - "RESOURCE_TYPE_NODE": 1, - "RESOURCE_TYPE_POD": 2, - "RESOURCE_TYPE_NAMESPACE": 3, - "RESOURCE_TYPE_EVENT": 4, - "RESOURCE_TYPE_ENDPOINTS": 5, - "RESOURCE_TYPE_SERVICE_ACCOUNT": 6, - "RESOURCE_TYPE_LIMIT_RANGE": 7, - "RESOURCE_TYPE_RESOURCE_QUOTA": 8, - "RESOURCE_TYPE_DEPLOYMENT": 9, - "RESOURCE_TYPE_STATEFUL_SET": 10, - "RESOURCE_TYPE_DAEMON_SET": 11, - "RESOURCE_TYPE_REPLICA_SET": 12, - "RESOURCE_TYPE_REPLICATION_CONTROLLER": 13, - "RESOURCE_TYPE_JOB": 14, - "RESOURCE_TYPE_CRON_JOB": 15, - "RESOURCE_TYPE_PERSISTENT_VOLUME_CLAIM": 16, - "RESOURCE_TYPE_PERSISTENT_VOLUME": 17, - "RESOURCE_TYPE_STORAGE_CLASS": 18, - "RESOURCE_TYPE_SERVICE": 19, - "RESOURCE_TYPE_INGRESS": 20, - "RESOURCE_TYPE_INGRESS_CLASS": 21, - "RESOURCE_TYPE_NETWORK_POLICY": 22, - "RESOURCE_TYPE_ROLE": 23, - "RESOURCE_TYPE_ROLE_BINDING": 24, - "RESOURCE_TYPE_CLUSTER_ROLE": 25, - "RESOURCE_TYPE_CLUSTER_ROLE_BINDING": 26, - "RESOURCE_TYPE_HORIZONTAL_POD_AUTOSCALER": 27, - "RESOURCE_TYPE_VERTICAL_POD_AUTOSCALER": 28, - "RESOURCE_TYPE_POD_DISRUPTION_BUDGET": 29, - "RESOURCE_TYPE_POD_SECURITY_POLICY": 30, - "RESOURCE_TYPE_CUSTOM_RESOURCE_DEFINITION": 31, - "RESOURCE_TYPE_CUSTOM_RESOURCE": 32, - "RESOURCE_TYPE_CONFIG_MAP": 33, - "RESOURCE_TYPE_SECRET": 34, - "RESOURCE_TYPE_CONTAINER": 35, - "RESOURCE_TYPE_NODE_RESOURCE": 36, - "RESOURCE_TYPE_CONTAINER_RESOURCE": 37, - "RESOURCE_TYPE_CLUSTER": 38, - "RESOURCE_TYPE_CSI_NODE": 39, - "RESOURCE_TYPE_KARPENTER": 40, - "RESOURCE_TYPE_DATADOG": 41, - "RESOURCE_TYPE_ARGO_ROLLOUTS": 42, - "RESOURCE_TYPE_KEDA": 43, - "RESOURCE_TYPE_KEDA_SCALED_OBJECT": 44, - "RESOURCE_TYPE_KEDA_SCALED_JOB": 45, - "RESOURCE_TYPE_CSI_DRIVER": 46, - "RESOURCE_TYPE_CSI_STORAGE_CAPACITY": 47, - "RESOURCE_TYPE_VOLUME_ATTACHMENT": 48, - "RESOURCE_TYPE_KUBEFLOW_NOTEBOOK": 49, - "RESOURCE_TYPE_VOLCANO_JOB": 50, - "RESOURCE_TYPE_SPARK_APPLICATION": 51, - "RESOURCE_TYPE_SCHEDULED_SPARK_APPLICATION": 52, - "RESOURCE_TYPE_CRON_VOLCANO_JOB": 53, - "RESOURCE_TYPE_PVC_METRICS": 54, - "RESOURCE_TYPE_WORKLOAD_RECOMMENDATION": 55, - "RESOURCE_TYPE_WORKLOAD_RULE": 56, - "RESOURCE_TYPE_WORKLOAD_RULE_OOM": 57, - "RESOURCE_TYPE_WORKLOAD_RULE_CPU_THROTTLE": 58, - "RESOURCE_TYPE_CNPG_CLUSTER": 59, - "RESOURCE_TYPE_CONTAINER_OOM_EVENT": 60, - "RESOURCE_TYPE_CONTAINER_CRASHLOOP_EVENT": 61, - "RESOURCE_TYPE_CONTAINER_STARTUP_LIFECYCLE": 62, - "RESOURCE_TYPE_CONTAINER_CPU_THROTTLE_EVENT": 63, - "RESOURCE_TYPE_CLUSTER_SNAPSHOT": 77, - "RESOURCE_TYPE_KARPENTER_SETTINGS": 78, - "RESOURCE_TYPE_NODE_LIFECYCLE_TRANSITION": 79, - "RESOURCE_TYPE_POD_UNSCHEDULABLE_EVENT": 80, - "RESOURCE_TYPE_KYVERNO_POLICY": 81, - "RESOURCE_TYPE_KYVERNO_POLICY_REPORT": 82, - "RESOURCE_TYPE_GATEKEEPER_CONSTRAINT_TEMPLATE": 83, - "RESOURCE_TYPE_GATEKEEPER_CONSTRAINT": 84, - "RESOURCE_TYPE_MIG_PARTED_CONFIG": 85, + "RESOURCE_TYPE_UNSPECIFIED": 0, + "RESOURCE_TYPE_NODE": 1, + "RESOURCE_TYPE_POD": 2, + "RESOURCE_TYPE_NAMESPACE": 3, + "RESOURCE_TYPE_EVENT": 4, + "RESOURCE_TYPE_ENDPOINTS": 5, + "RESOURCE_TYPE_SERVICE_ACCOUNT": 6, + "RESOURCE_TYPE_LIMIT_RANGE": 7, + "RESOURCE_TYPE_RESOURCE_QUOTA": 8, + "RESOURCE_TYPE_DEPLOYMENT": 9, + "RESOURCE_TYPE_STATEFUL_SET": 10, + "RESOURCE_TYPE_DAEMON_SET": 11, + "RESOURCE_TYPE_REPLICA_SET": 12, + "RESOURCE_TYPE_REPLICATION_CONTROLLER": 13, + "RESOURCE_TYPE_JOB": 14, + "RESOURCE_TYPE_CRON_JOB": 15, + "RESOURCE_TYPE_PERSISTENT_VOLUME_CLAIM": 16, + "RESOURCE_TYPE_PERSISTENT_VOLUME": 17, + "RESOURCE_TYPE_STORAGE_CLASS": 18, + "RESOURCE_TYPE_SERVICE": 19, + "RESOURCE_TYPE_INGRESS": 20, + "RESOURCE_TYPE_INGRESS_CLASS": 21, + "RESOURCE_TYPE_NETWORK_POLICY": 22, + "RESOURCE_TYPE_ROLE": 23, + "RESOURCE_TYPE_ROLE_BINDING": 24, + "RESOURCE_TYPE_CLUSTER_ROLE": 25, + "RESOURCE_TYPE_CLUSTER_ROLE_BINDING": 26, + "RESOURCE_TYPE_HORIZONTAL_POD_AUTOSCALER": 27, + "RESOURCE_TYPE_VERTICAL_POD_AUTOSCALER": 28, + "RESOURCE_TYPE_POD_DISRUPTION_BUDGET": 29, + "RESOURCE_TYPE_POD_SECURITY_POLICY": 30, + "RESOURCE_TYPE_CUSTOM_RESOURCE_DEFINITION": 31, + "RESOURCE_TYPE_CUSTOM_RESOURCE": 32, + "RESOURCE_TYPE_CONFIG_MAP": 33, + "RESOURCE_TYPE_SECRET": 34, + "RESOURCE_TYPE_CONTAINER": 35, + "RESOURCE_TYPE_NODE_RESOURCE": 36, + "RESOURCE_TYPE_CONTAINER_RESOURCE": 37, + "RESOURCE_TYPE_CLUSTER": 38, + "RESOURCE_TYPE_CSI_NODE": 39, + "RESOURCE_TYPE_KARPENTER": 40, + "RESOURCE_TYPE_DATADOG": 41, + "RESOURCE_TYPE_ARGO_ROLLOUTS": 42, + "RESOURCE_TYPE_KEDA": 43, + "RESOURCE_TYPE_KEDA_SCALED_OBJECT": 44, + "RESOURCE_TYPE_KEDA_SCALED_JOB": 45, + "RESOURCE_TYPE_CSI_DRIVER": 46, + "RESOURCE_TYPE_CSI_STORAGE_CAPACITY": 47, + "RESOURCE_TYPE_VOLUME_ATTACHMENT": 48, + "RESOURCE_TYPE_KUBEFLOW_NOTEBOOK": 49, + "RESOURCE_TYPE_VOLCANO_JOB": 50, + "RESOURCE_TYPE_SPARK_APPLICATION": 51, + "RESOURCE_TYPE_SCHEDULED_SPARK_APPLICATION": 52, + "RESOURCE_TYPE_CRON_VOLCANO_JOB": 53, + "RESOURCE_TYPE_PVC_METRICS": 54, + "RESOURCE_TYPE_WORKLOAD_RECOMMENDATION": 55, + "RESOURCE_TYPE_WORKLOAD_RULE": 56, + "RESOURCE_TYPE_WORKLOAD_RULE_OOM": 57, + "RESOURCE_TYPE_WORKLOAD_RULE_CPU_THROTTLE": 58, + "RESOURCE_TYPE_CNPG_CLUSTER": 59, + "RESOURCE_TYPE_CONTAINER_OOM_EVENT": 60, + "RESOURCE_TYPE_CONTAINER_CRASHLOOP_EVENT": 61, + "RESOURCE_TYPE_CONTAINER_STARTUP_LIFECYCLE": 62, + "RESOURCE_TYPE_CONTAINER_CPU_THROTTLE_EVENT": 63, + "RESOURCE_TYPE_CLUSTER_SNAPSHOT": 77, + "RESOURCE_TYPE_KARPENTER_SETTINGS": 78, + "RESOURCE_TYPE_NODE_LIFECYCLE_TRANSITION": 79, + "RESOURCE_TYPE_POD_UNSCHEDULABLE_EVENT": 80, + "RESOURCE_TYPE_KYVERNO_POLICY": 81, + "RESOURCE_TYPE_KYVERNO_POLICY_REPORT": 82, + "RESOURCE_TYPE_GATEKEEPER_CONSTRAINT_TEMPLATE": 83, + "RESOURCE_TYPE_GATEKEEPER_CONSTRAINT": 84, + "RESOURCE_TYPE_MIG_PARTED_CONFIG": 85, + "RESOURCE_TYPE_GPU_MIG_INSTANCE_METRICS": 86, + "RESOURCE_TYPE_SCHEDULER_PLACEMENT_METRIC": 87, + "RESOURCE_TYPE_ARGO_APPLICATION": 88, + "RESOURCE_TYPE_ARGO_APPLICATION_SET": 89, + "RESOURCE_TYPE_ARGO_APPLICATION_RESOURCE": 90, + "RESOURCE_TYPE_FLUX_GIT_REPOSITORY": 91, + "RESOURCE_TYPE_FLUX_OCI_REPOSITORY": 92, + "RESOURCE_TYPE_FLUX_HELM_REPOSITORY": 93, + "RESOURCE_TYPE_FLUX_HELM_CHART": 94, + "RESOURCE_TYPE_FLUX_BUCKET": 95, + "RESOURCE_TYPE_FLUX_KUSTOMIZATION": 96, + "RESOURCE_TYPE_FLUX_HELM_RELEASE": 97, + "RESOURCE_TYPE_FLUX_EXTERNAL_ARTIFACT": 98, + "RESOURCE_TYPE_FLUX_ALERT": 99, + "RESOURCE_TYPE_FLUX_PROVIDER": 100, + "RESOURCE_TYPE_FLUX_RECEIVER": 101, + "RESOURCE_TYPE_FLUX_INSTANCE": 102, + "RESOURCE_TYPE_FLUX_REPORT": 103, + "RESOURCE_TYPE_FLUX_RESOURCE_SET_INPUT_PROVIDER": 104, + "RESOURCE_TYPE_FLUX_RESOURCE_SET": 105, + "RESOURCE_TYPE_FLUX_IMAGE_REPOSITORY": 106, + "RESOURCE_TYPE_FLUX_IMAGE_POLICY": 107, + "RESOURCE_TYPE_FLUX_IMAGE_UPDATE_AUTOMATION": 108, + "RESOURCE_TYPE_FLAGGER_CANARY": 109, + "RESOURCE_TYPE_FLAGGER_METRIC_TEMPLATE": 110, + "RESOURCE_TYPE_FLAGGER_ALERT_PROVIDER": 111, } ) @@ -2580,6 +2717,25 @@ type NetworkTrafficItem struct { TxPackets int64 `protobuf:"varint,9,opt,name=tx_packets,json=txPackets,proto3" json:"tx_packets,omitempty"` RxPackets int64 `protobuf:"varint,10,opt,name=rx_packets,json=rxPackets,proto3" json:"rx_packets,omitempty"` Timestamp *timestamppb.Timestamp `protobuf:"bytes,11,opt,name=timestamp,proto3" json:"timestamp,omitempty"` + // The post-DNAT destination, when the kernel translated one. + // + // A connection to a ClusterIP is DNAT'd by kube-proxy to one backend pod, so + // the address the client dialed (dst_ip, carried above, from the conntrack + // ORIGINAL tuple) is not the address that answered. conntrack's REPLY tuple + // source is the backend that actually served the connection, and that is + // what these two fields carry. + // + // Both are empty/zero when no translation happened, which is the common case + // for pod-to-pod and egress traffic. When they are set, dst_ip still names + // the service the client asked for -- useful for display -- while + // dst_ip_translated names the pod whose node decides whether the flow + // crossed an availability zone. + // + // Only the conntrack collection path can populate these. The Cilium eBPF path + // reads the CT map, whose key holds a single tuple and no backend reference, + // so it leaves them empty. + DstIpTranslated string `protobuf:"bytes,12,opt,name=dst_ip_translated,json=dstIpTranslated,proto3" json:"dst_ip_translated,omitempty"` + DstPortTranslated int32 `protobuf:"varint,13,opt,name=dst_port_translated,json=dstPortTranslated,proto3" json:"dst_port_translated,omitempty"` } func (x *NetworkTrafficItem) Reset() { @@ -2691,6 +2847,20 @@ func (x *NetworkTrafficItem) GetTimestamp() *timestamppb.Timestamp { return nil } +func (x *NetworkTrafficItem) GetDstIpTranslated() string { + if x != nil { + return x.DstIpTranslated + } + return "" +} + +func (x *NetworkTrafficItem) GetDstPortTranslated() int32 { + if x != nil { + return x.DstPortTranslated + } + return 0 +} + type DnsLookupItem struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -2778,6 +2948,134 @@ func (x *DnsLookupItem) GetSrcPodNamespace() string { return "" } +// NodeEgressPath describes how the reporting node's traffic leaves the cloud +// network, as read from the instance metadata service of the node it runs on. +// +// It exists because bytes measured leaving a pod do not carry their own price. +// The same byte costs nothing, a cross-zone rate, a NAT-gateway processing rate +// or a public-IP data-transfer rate depending entirely on where the node sits +// and whether it has a public address -- facts only the node itself can see. +// +// Every field is best-effort. A field the metadata service does not answer for +// is left empty rather than guessed, and an entirely absent metadata service +// (bare metal, an unrecognised provider, a blocked link-local route) means the +// whole message is absent. Empty always reads as "unknown", never as a default. +type NodeEgressPath struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Lowercase provider id: "aws", "azure" or "gcp". Empty when no metadata + // service answered. + CloudProvider string `protobuf:"bytes,1,opt,name=cloud_provider,json=cloudProvider,proto3" json:"cloud_provider,omitempty"` + // Region, in the provider's own naming, e.g. "us-west-2". + Region string `protobuf:"bytes,2,opt,name=region,proto3" json:"region,omitempty"` + // Availability zone, in the provider's own naming, e.g. "us-west-2a". + Zone string `protobuf:"bytes,3,opt,name=zone,proto3" json:"zone,omitempty"` + // Virtual network id: AWS VPC id, GCP network name, Azure vnet id. Empty when + // the provider does not expose one. + VpcId string `protobuf:"bytes,4,opt,name=vpc_id,json=vpcId,proto3" json:"vpc_id,omitempty"` + // Subnet id of the node's primary interface. + SubnetId string `protobuf:"bytes,5,opt,name=subnet_id,json=subnetId,proto3" json:"subnet_id,omitempty"` + // Whether the node's primary interface has a public address of its own. This + // is the fact that separates traffic leaving through a NAT gateway (priced + // per processed GB) from traffic leaving directly (priced as public-IP data + // transfer), and the two differ by roughly 4x. + // + // OPTIONAL ON PURPOSE. A plain bool cannot say "we could not find out", and + // the two wrong answers are not symmetric: defaulting to false bills a + // node's egress at the NAT rate on nothing more than a timed-out metadata + // request. The agent sets this only on a definite answer from the metadata + // service -- the address exists, or it provably does not. Every other + // outcome (an unreadable interface, a throttled or timed-out read, an + // expired token) leaves the field absent, and absent means unknown: the + // control plane records no egress path rather than guessing one. + HasPublicIp *bool `protobuf:"varint,6,opt,name=has_public_ip,json=hasPublicIp,proto3,oneof" json:"has_public_ip,omitempty"` + // Primary private address of the node. + PrivateIp string `protobuf:"bytes,7,opt,name=private_ip,json=privateIp,proto3" json:"private_ip,omitempty"` +} + +func (x *NodeEgressPath) Reset() { + *x = NodeEgressPath{} + if protoimpl.UnsafeEnabled { + mi := &file_api_v1_metrics_collector_proto_msgTypes[25] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *NodeEgressPath) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*NodeEgressPath) ProtoMessage() {} + +func (x *NodeEgressPath) ProtoReflect() protoreflect.Message { + mi := &file_api_v1_metrics_collector_proto_msgTypes[25] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use NodeEgressPath.ProtoReflect.Descriptor instead. +func (*NodeEgressPath) Descriptor() ([]byte, []int) { + return file_api_v1_metrics_collector_proto_rawDescGZIP(), []int{25} +} + +func (x *NodeEgressPath) GetCloudProvider() string { + if x != nil { + return x.CloudProvider + } + return "" +} + +func (x *NodeEgressPath) GetRegion() string { + if x != nil { + return x.Region + } + return "" +} + +func (x *NodeEgressPath) GetZone() string { + if x != nil { + return x.Zone + } + return "" +} + +func (x *NodeEgressPath) GetVpcId() string { + if x != nil { + return x.VpcId + } + return "" +} + +func (x *NodeEgressPath) GetSubnetId() string { + if x != nil { + return x.SubnetId + } + return "" +} + +func (x *NodeEgressPath) GetHasPublicIp() bool { + if x != nil && x.HasPublicIp != nil { + return *x.HasPublicIp + } + return false +} + +func (x *NodeEgressPath) GetPrivateIp() string { + if x != nil { + return x.PrivateIp + } + return "" +} + type SendNetworkTrafficMetricsRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -2793,12 +3091,16 @@ type SendNetworkTrafficMetricsRequest struct { OperatorVersion *string `protobuf:"bytes,7,opt,name=operator_version,json=operatorVersion,proto3,oneof" json:"operator_version,omitempty"` // Network operator git commit for health tracking OperatorCommit *string `protobuf:"bytes,8,opt,name=operator_commit,json=operatorCommit,proto3,oneof" json:"operator_commit,omitempty"` + // How this node's traffic leaves the cloud network. Reported once per request + // because it describes the node, not any single flow. Absent when the node's + // instance metadata service could not be read. + NodeEgress *NodeEgressPath `protobuf:"bytes,9,opt,name=node_egress,json=nodeEgress,proto3,oneof" json:"node_egress,omitempty"` } func (x *SendNetworkTrafficMetricsRequest) Reset() { *x = SendNetworkTrafficMetricsRequest{} if protoimpl.UnsafeEnabled { - mi := &file_api_v1_metrics_collector_proto_msgTypes[25] + mi := &file_api_v1_metrics_collector_proto_msgTypes[26] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2811,7 +3113,7 @@ func (x *SendNetworkTrafficMetricsRequest) String() string { func (*SendNetworkTrafficMetricsRequest) ProtoMessage() {} func (x *SendNetworkTrafficMetricsRequest) ProtoReflect() protoreflect.Message { - mi := &file_api_v1_metrics_collector_proto_msgTypes[25] + mi := &file_api_v1_metrics_collector_proto_msgTypes[26] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2824,7 +3126,7 @@ func (x *SendNetworkTrafficMetricsRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use SendNetworkTrafficMetricsRequest.ProtoReflect.Descriptor instead. func (*SendNetworkTrafficMetricsRequest) Descriptor() ([]byte, []int) { - return file_api_v1_metrics_collector_proto_rawDescGZIP(), []int{25} + return file_api_v1_metrics_collector_proto_rawDescGZIP(), []int{26} } func (x *SendNetworkTrafficMetricsRequest) GetClusterId() string { @@ -2883,6 +3185,13 @@ func (x *SendNetworkTrafficMetricsRequest) GetOperatorCommit() string { return "" } +func (x *SendNetworkTrafficMetricsRequest) GetNodeEgress() *NodeEgressPath { + if x != nil { + return x.NodeEgress + } + return nil +} + type SendNetworkTrafficMetricsResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -2894,7 +3203,7 @@ type SendNetworkTrafficMetricsResponse struct { func (x *SendNetworkTrafficMetricsResponse) Reset() { *x = SendNetworkTrafficMetricsResponse{} if protoimpl.UnsafeEnabled { - mi := &file_api_v1_metrics_collector_proto_msgTypes[26] + mi := &file_api_v1_metrics_collector_proto_msgTypes[27] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2907,7 +3216,556 @@ func (x *SendNetworkTrafficMetricsResponse) String() string { func (*SendNetworkTrafficMetricsResponse) ProtoMessage() {} func (x *SendNetworkTrafficMetricsResponse) ProtoReflect() protoreflect.Message { - mi := &file_api_v1_metrics_collector_proto_msgTypes[26] + mi := &file_api_v1_metrics_collector_proto_msgTypes[27] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SendNetworkTrafficMetricsResponse.ProtoReflect.Descriptor instead. +func (*SendNetworkTrafficMetricsResponse) Descriptor() ([]byte, []int) { + return file_api_v1_metrics_collector_proto_rawDescGZIP(), []int{27} +} + +func (x *SendNetworkTrafficMetricsResponse) GetProcessedCount() int32 { + if x != nil { + return x.ProcessedCount + } + return 0 +} + +// ContainerRedMetricsItem is one container's RED (rate, errors, duration) measurements +// for one minute bucket, as observed inbound by the netmon agent on one node. +// +// EVERY COUNTER IS A DELTA FOR THAT MINUTE, NOT A RUNNING TOTAL. The destination is a +// ClickHouse SummingMergeTree, so rows sharing a key are added together during merges. +// That is what lets two agents and two flushes contribute to the same minute without +// coordination, and it is also why nothing in the export path may retry: a re-sent item +// is indistinguishable from real extra traffic. +type ContainerRedMetricsItem struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Kubernetes-native identity. The agent does not know DevZero container, pod or node + // UUIDs; the server resolves these four fields to them and drops the item if it cannot. + // The node is not among them: one agent per node sends one request per flush, so + // node_name sits on the request rather than being repeated on every item, and the node + // UUID is resolved from the container anyway. + PodName string `protobuf:"bytes,1,opt,name=pod_name,json=podName,proto3" json:"pod_name,omitempty"` + PodNamespace string `protobuf:"bytes,2,opt,name=pod_namespace,json=podNamespace,proto3" json:"pod_namespace,omitempty"` + PodUid string `protobuf:"bytes,3,opt,name=pod_uid,json=podUid,proto3" json:"pod_uid,omitempty"` + ContainerName string `protobuf:"bytes,4,opt,name=container_name,json=containerName,proto3" json:"container_name,omitempty"` + // Start of the minute this item covers, already truncated to the minute by the agent. + Timestamp *timestamppb.Timestamp `protobuf:"bytes,5,opt,name=timestamp,proto3" json:"timestamp,omitempty"` + // Service requests observed inbound during the minute, INCLUDING ones whose response + // was never observed. Those contribute to no latency bucket and nothing to + // duration_sum_us, which is why mean latency divides by the last bucket rather than by + // request_count. + RequestCount uint64 `protobuf:"varint,6,opt,name=request_count,json=requestCount,proto3" json:"request_count,omitempty"` + // Requests answered with a 5XX or a non-zero gRPC status. A 4XX is deliberately not an + // error: it is the client being wrong, not the container. + ErrorCount uint64 `protobuf:"varint,7,opt,name=error_count,json=errorCount,proto3" json:"error_count,omitempty"` + // Summed duration of answered requests, in microseconds. + DurationSumUs uint64 `protobuf:"varint,8,opt,name=duration_sum_us,json=durationSumUs,proto3" json:"duration_sum_us,omitempty"` + // Cumulative latency histogram: exactly fourteen entries, in ascending bound order, + // each counting requests answered in at most that bound. Every entry + // therefore includes the ones below it, and the last entry is the total number of + // answered requests. + // + // This is one repeated field rather than fourteen named ones so that a length mismatch + // between agent and server is DETECTABLE. Fourteen named fields would let a skewed + // agent populate a subset silently, filing latencies under the wrong bound with no + // error anywhere. The server rejects the WHOLE REQUEST if any array is not fourteen + // long: that means agent and server disagree about the ladder, which is a skew to + // surface loudly rather than a per-item condition to drop quietly. The bounds + // themselves are fixed for the life of the feature; see + // docs/features/red-metrics/INVARIANTS.md rule 3. + DurationBuckets []uint64 `protobuf:"varint,9,rep,packed,name=duration_buckets,json=durationBuckets,proto3" json:"duration_buckets,omitempty"` + // Slowest answered request in the bucket, in microseconds. Stored under a max + // aggregate rather than summed. + DurationMaxUs uint64 `protobuf:"varint,10,opt,name=duration_max_us,json=durationMaxUs,proto3" json:"duration_max_us,omitempty"` + // Inbound TCP connections whose application protocol the agent could not decode. This + // is the denominator that says how much traffic the decoders did not understand, so a + // silent decoder regression shows up as a number rather than as missing rows. + ConnCount uint64 `protobuf:"varint,11,opt,name=conn_count,json=connCount,proto3" json:"conn_count,omitempty"` +} + +func (x *ContainerRedMetricsItem) Reset() { + *x = ContainerRedMetricsItem{} + if protoimpl.UnsafeEnabled { + mi := &file_api_v1_metrics_collector_proto_msgTypes[28] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ContainerRedMetricsItem) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ContainerRedMetricsItem) ProtoMessage() {} + +func (x *ContainerRedMetricsItem) ProtoReflect() protoreflect.Message { + mi := &file_api_v1_metrics_collector_proto_msgTypes[28] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ContainerRedMetricsItem.ProtoReflect.Descriptor instead. +func (*ContainerRedMetricsItem) Descriptor() ([]byte, []int) { + return file_api_v1_metrics_collector_proto_rawDescGZIP(), []int{28} +} + +func (x *ContainerRedMetricsItem) GetPodName() string { + if x != nil { + return x.PodName + } + return "" +} + +func (x *ContainerRedMetricsItem) GetPodNamespace() string { + if x != nil { + return x.PodNamespace + } + return "" +} + +func (x *ContainerRedMetricsItem) GetPodUid() string { + if x != nil { + return x.PodUid + } + return "" +} + +func (x *ContainerRedMetricsItem) GetContainerName() string { + if x != nil { + return x.ContainerName + } + return "" +} + +func (x *ContainerRedMetricsItem) GetTimestamp() *timestamppb.Timestamp { + if x != nil { + return x.Timestamp + } + return nil +} + +func (x *ContainerRedMetricsItem) GetRequestCount() uint64 { + if x != nil { + return x.RequestCount + } + return 0 +} + +func (x *ContainerRedMetricsItem) GetErrorCount() uint64 { + if x != nil { + return x.ErrorCount + } + return 0 +} + +func (x *ContainerRedMetricsItem) GetDurationSumUs() uint64 { + if x != nil { + return x.DurationSumUs + } + return 0 +} + +func (x *ContainerRedMetricsItem) GetDurationBuckets() []uint64 { + if x != nil { + return x.DurationBuckets + } + return nil +} + +func (x *ContainerRedMetricsItem) GetDurationMaxUs() uint64 { + if x != nil { + return x.DurationMaxUs + } + return 0 +} + +func (x *ContainerRedMetricsItem) GetConnCount() uint64 { + if x != nil { + return x.ConnCount + } + return 0 +} + +type SendContainerRedMetricsRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + ClusterId string `protobuf:"bytes,1,opt,name=cluster_id,json=clusterId,proto3" json:"cluster_id,omitempty"` + TeamId string `protobuf:"bytes,2,opt,name=team_id,json=teamId,proto3" json:"team_id,omitempty"` + NodeName string `protobuf:"bytes,3,opt,name=node_name,json=nodeName,proto3" json:"node_name,omitempty"` + Items []*ContainerRedMetricsItem `protobuf:"bytes,4,rep,name=items,proto3" json:"items,omitempty"` + // Network operator version for health tracking + OperatorVersion *string `protobuf:"bytes,5,opt,name=operator_version,json=operatorVersion,proto3,oneof" json:"operator_version,omitempty"` + // Network operator git commit for health tracking + OperatorCommit *string `protobuf:"bytes,6,opt,name=operator_commit,json=operatorCommit,proto3,oneof" json:"operator_commit,omitempty"` +} + +func (x *SendContainerRedMetricsRequest) Reset() { + *x = SendContainerRedMetricsRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_api_v1_metrics_collector_proto_msgTypes[29] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *SendContainerRedMetricsRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SendContainerRedMetricsRequest) ProtoMessage() {} + +func (x *SendContainerRedMetricsRequest) ProtoReflect() protoreflect.Message { + mi := &file_api_v1_metrics_collector_proto_msgTypes[29] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SendContainerRedMetricsRequest.ProtoReflect.Descriptor instead. +func (*SendContainerRedMetricsRequest) Descriptor() ([]byte, []int) { + return file_api_v1_metrics_collector_proto_rawDescGZIP(), []int{29} +} + +func (x *SendContainerRedMetricsRequest) GetClusterId() string { + if x != nil { + return x.ClusterId + } + return "" +} + +func (x *SendContainerRedMetricsRequest) GetTeamId() string { + if x != nil { + return x.TeamId + } + return "" +} + +func (x *SendContainerRedMetricsRequest) GetNodeName() string { + if x != nil { + return x.NodeName + } + return "" +} + +func (x *SendContainerRedMetricsRequest) GetItems() []*ContainerRedMetricsItem { + if x != nil { + return x.Items + } + return nil +} + +func (x *SendContainerRedMetricsRequest) GetOperatorVersion() string { + if x != nil && x.OperatorVersion != nil { + return *x.OperatorVersion + } + return "" +} + +func (x *SendContainerRedMetricsRequest) GetOperatorCommit() string { + if x != nil && x.OperatorCommit != nil { + return *x.OperatorCommit + } + return "" +} + +type SendContainerRedMetricsResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Items written. Items whose container identity could not be resolved are dropped and + // are not counted here, so a caller can see partial attribution without an error. + ProcessedCount int32 `protobuf:"varint,1,opt,name=processed_count,json=processedCount,proto3" json:"processed_count,omitempty"` +} + +func (x *SendContainerRedMetricsResponse) Reset() { + *x = SendContainerRedMetricsResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_api_v1_metrics_collector_proto_msgTypes[30] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *SendContainerRedMetricsResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SendContainerRedMetricsResponse) ProtoMessage() {} + +func (x *SendContainerRedMetricsResponse) ProtoReflect() protoreflect.Message { + mi := &file_api_v1_metrics_collector_proto_msgTypes[30] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SendContainerRedMetricsResponse.ProtoReflect.Descriptor instead. +func (*SendContainerRedMetricsResponse) Descriptor() ([]byte, []int) { + return file_api_v1_metrics_collector_proto_rawDescGZIP(), []int{30} +} + +func (x *SendContainerRedMetricsResponse) GetProcessedCount() int32 { + if x != nil { + return x.ProcessedCount + } + return 0 +} + +// LogEntry represents a single log message +type LogEntry struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Timestamp of the log message + Timestamp *timestamppb.Timestamp `protobuf:"bytes,1,opt,name=timestamp,proto3" json:"timestamp,omitempty"` + // Severity level of the log + Level LogLevel `protobuf:"varint,2,opt,name=level,proto3,enum=api.v1.LogLevel" json:"level,omitempty"` + // The actual log message + Message string `protobuf:"bytes,3,opt,name=message,proto3" json:"message,omitempty"` + // Structured fields associated with the log entry + Fields map[string]string `protobuf:"bytes,6,rep,name=fields,proto3" json:"fields,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + // Error string, if any + Error string `protobuf:"bytes,7,opt,name=error,proto3" json:"error,omitempty"` + // The source or component generating the log (e.g., pod name, container ID) + Source string `protobuf:"bytes,11,opt,name=source,proto3" json:"source,omitempty"` +} + +func (x *LogEntry) Reset() { + *x = LogEntry{} + if protoimpl.UnsafeEnabled { + mi := &file_api_v1_metrics_collector_proto_msgTypes[31] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *LogEntry) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*LogEntry) ProtoMessage() {} + +func (x *LogEntry) ProtoReflect() protoreflect.Message { + mi := &file_api_v1_metrics_collector_proto_msgTypes[31] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use LogEntry.ProtoReflect.Descriptor instead. +func (*LogEntry) Descriptor() ([]byte, []int) { + return file_api_v1_metrics_collector_proto_rawDescGZIP(), []int{31} +} + +func (x *LogEntry) GetTimestamp() *timestamppb.Timestamp { + if x != nil { + return x.Timestamp + } + return nil +} + +func (x *LogEntry) GetLevel() LogLevel { + if x != nil { + return x.Level + } + return LogLevel_LOG_LEVEL_UNSPECIFIED +} + +func (x *LogEntry) GetMessage() string { + if x != nil { + return x.Message + } + return "" +} + +func (x *LogEntry) GetFields() map[string]string { + if x != nil { + return x.Fields + } + return nil +} + +func (x *LogEntry) GetError() string { + if x != nil { + return x.Error + } + return "" +} + +func (x *LogEntry) GetSource() string { + if x != nil { + return x.Source + } + return "" +} + +// SendTelemetryLogsResponse is the response for log ingestion +type SendTelemetryLogsResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Number of logs successfully processed + ProcessedCount int32 `protobuf:"varint,1,opt,name=processed_count,json=processedCount,proto3" json:"processed_count,omitempty"` +} + +func (x *SendTelemetryLogsResponse) Reset() { + *x = SendTelemetryLogsResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_api_v1_metrics_collector_proto_msgTypes[32] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *SendTelemetryLogsResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SendTelemetryLogsResponse) ProtoMessage() {} + +func (x *SendTelemetryLogsResponse) ProtoReflect() protoreflect.Message { + mi := &file_api_v1_metrics_collector_proto_msgTypes[32] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SendTelemetryLogsResponse.ProtoReflect.Descriptor instead. +func (*SendTelemetryLogsResponse) Descriptor() ([]byte, []int) { + return file_api_v1_metrics_collector_proto_rawDescGZIP(), []int{32} +} + +func (x *SendTelemetryLogsResponse) GetProcessedCount() int32 { + if x != nil { + return x.ProcessedCount + } + return 0 +} + +type NodeMetadataRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + TeamId string `protobuf:"bytes,1,opt,name=team_id,json=teamId,proto3" json:"team_id,omitempty"` // Unique identifier for the team. + ClusterId string `protobuf:"bytes,2,opt,name=cluster_id,json=clusterId,proto3" json:"cluster_id,omitempty"` // Unique identifier for the cluster. +} + +func (x *NodeMetadataRequest) Reset() { + *x = NodeMetadataRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_api_v1_metrics_collector_proto_msgTypes[33] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *NodeMetadataRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*NodeMetadataRequest) ProtoMessage() {} + +func (x *NodeMetadataRequest) ProtoReflect() protoreflect.Message { + mi := &file_api_v1_metrics_collector_proto_msgTypes[33] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use NodeMetadataRequest.ProtoReflect.Descriptor instead. +func (*NodeMetadataRequest) Descriptor() ([]byte, []int) { + return file_api_v1_metrics_collector_proto_rawDescGZIP(), []int{33} +} + +func (x *NodeMetadataRequest) GetTeamId() string { + if x != nil { + return x.TeamId + } + return "" +} + +func (x *NodeMetadataRequest) GetClusterId() string { + if x != nil { + return x.ClusterId + } + return "" +} + +type NodeMetadataResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + NodeToMeta map[string]*Node `protobuf:"bytes,1,rep,name=node_to_meta,json=nodeToMeta,proto3" json:"node_to_meta,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` // map is node name to node +} + +func (x *NodeMetadataResponse) Reset() { + *x = NodeMetadataResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_api_v1_metrics_collector_proto_msgTypes[34] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *NodeMetadataResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*NodeMetadataResponse) ProtoMessage() {} + +func (x *NodeMetadataResponse) ProtoReflect() protoreflect.Message { + mi := &file_api_v1_metrics_collector_proto_msgTypes[34] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2918,55 +3776,54 @@ func (x *SendNetworkTrafficMetricsResponse) ProtoReflect() protoreflect.Message return mi.MessageOf(x) } -// Deprecated: Use SendNetworkTrafficMetricsResponse.ProtoReflect.Descriptor instead. -func (*SendNetworkTrafficMetricsResponse) Descriptor() ([]byte, []int) { - return file_api_v1_metrics_collector_proto_rawDescGZIP(), []int{26} +// Deprecated: Use NodeMetadataResponse.ProtoReflect.Descriptor instead. +func (*NodeMetadataResponse) Descriptor() ([]byte, []int) { + return file_api_v1_metrics_collector_proto_rawDescGZIP(), []int{34} } -func (x *SendNetworkTrafficMetricsResponse) GetProcessedCount() int32 { +func (x *NodeMetadataResponse) GetNodeToMeta() map[string]*Node { if x != nil { - return x.ProcessedCount + return x.NodeToMeta } - return 0 + return nil } -// LogEntry represents a single log message -type LogEntry struct { +// ScalerMetricSample is a single KEDA scaler metric observation for one trigger on a ScaledObject. +type ScalerMetricSample struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - // Timestamp of the log message - Timestamp *timestamppb.Timestamp `protobuf:"bytes,1,opt,name=timestamp,proto3" json:"timestamp,omitempty"` - // Severity level of the log - Level LogLevel `protobuf:"varint,2,opt,name=level,proto3,enum=api.v1.LogLevel" json:"level,omitempty"` - // The actual log message - Message string `protobuf:"bytes,3,opt,name=message,proto3" json:"message,omitempty"` - // Structured fields associated with the log entry - Fields map[string]string `protobuf:"bytes,6,rep,name=fields,proto3" json:"fields,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` - // Error string, if any - Error string `protobuf:"bytes,7,opt,name=error,proto3" json:"error,omitempty"` - // The source or component generating the log (e.g., pod name, container ID) - Source string `protobuf:"bytes,11,opt,name=source,proto3" json:"source,omitempty"` -} - -func (x *LogEntry) Reset() { - *x = LogEntry{} + Scaledobject string `protobuf:"bytes,1,opt,name=scaledobject,proto3" json:"scaledobject,omitempty"` + Namespace string `protobuf:"bytes,2,opt,name=namespace,proto3" json:"namespace,omitempty"` + WorkloadKind string `protobuf:"bytes,3,opt,name=workload_kind,json=workloadKind,proto3" json:"workload_kind,omitempty"` + WorkloadName string `protobuf:"bytes,4,opt,name=workload_name,json=workloadName,proto3" json:"workload_name,omitempty"` + TriggerIndex int32 `protobuf:"varint,5,opt,name=trigger_index,json=triggerIndex,proto3" json:"trigger_index,omitempty"` + MetricName string `protobuf:"bytes,6,opt,name=metric_name,json=metricName,proto3" json:"metric_name,omitempty"` + ScalerType string `protobuf:"bytes,7,opt,name=scaler_type,json=scalerType,proto3" json:"scaler_type,omitempty"` + Value float64 `protobuf:"fixed64,8,opt,name=value,proto3" json:"value,omitempty"` + Active bool `protobuf:"varint,9,opt,name=active,proto3" json:"active,omitempty"` + TargetValue *float64 `protobuf:"fixed64,10,opt,name=target_value,json=targetValue,proto3,oneof" json:"target_value,omitempty"` + Ts *timestamppb.Timestamp `protobuf:"bytes,11,opt,name=ts,proto3" json:"ts,omitempty"` +} + +func (x *ScalerMetricSample) Reset() { + *x = ScalerMetricSample{} if protoimpl.UnsafeEnabled { - mi := &file_api_v1_metrics_collector_proto_msgTypes[27] + mi := &file_api_v1_metrics_collector_proto_msgTypes[35] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } -func (x *LogEntry) String() string { +func (x *ScalerMetricSample) String() string { return protoimpl.X.MessageStringOf(x) } -func (*LogEntry) ProtoMessage() {} +func (*ScalerMetricSample) ProtoMessage() {} -func (x *LogEntry) ProtoReflect() protoreflect.Message { - mi := &file_api_v1_metrics_collector_proto_msgTypes[27] +func (x *ScalerMetricSample) ProtoReflect() protoreflect.Message { + mi := &file_api_v1_metrics_collector_proto_msgTypes[35] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2977,128 +3834,115 @@ func (x *LogEntry) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use LogEntry.ProtoReflect.Descriptor instead. -func (*LogEntry) Descriptor() ([]byte, []int) { - return file_api_v1_metrics_collector_proto_rawDescGZIP(), []int{27} +// Deprecated: Use ScalerMetricSample.ProtoReflect.Descriptor instead. +func (*ScalerMetricSample) Descriptor() ([]byte, []int) { + return file_api_v1_metrics_collector_proto_rawDescGZIP(), []int{35} } -func (x *LogEntry) GetTimestamp() *timestamppb.Timestamp { +func (x *ScalerMetricSample) GetScaledobject() string { if x != nil { - return x.Timestamp + return x.Scaledobject } - return nil + return "" } -func (x *LogEntry) GetLevel() LogLevel { +func (x *ScalerMetricSample) GetNamespace() string { if x != nil { - return x.Level + return x.Namespace } - return LogLevel_LOG_LEVEL_UNSPECIFIED + return "" } -func (x *LogEntry) GetMessage() string { +func (x *ScalerMetricSample) GetWorkloadKind() string { if x != nil { - return x.Message + return x.WorkloadKind } return "" } -func (x *LogEntry) GetFields() map[string]string { +func (x *ScalerMetricSample) GetWorkloadName() string { if x != nil { - return x.Fields + return x.WorkloadName } - return nil + return "" } -func (x *LogEntry) GetError() string { +func (x *ScalerMetricSample) GetTriggerIndex() int32 { if x != nil { - return x.Error + return x.TriggerIndex } - return "" + return 0 } -func (x *LogEntry) GetSource() string { +func (x *ScalerMetricSample) GetMetricName() string { if x != nil { - return x.Source + return x.MetricName } return "" } -// SendTelemetryLogsResponse is the response for log ingestion -type SendTelemetryLogsResponse struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - // Number of logs successfully processed - ProcessedCount int32 `protobuf:"varint,1,opt,name=processed_count,json=processedCount,proto3" json:"processed_count,omitempty"` -} - -func (x *SendTelemetryLogsResponse) Reset() { - *x = SendTelemetryLogsResponse{} - if protoimpl.UnsafeEnabled { - mi := &file_api_v1_metrics_collector_proto_msgTypes[28] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) +func (x *ScalerMetricSample) GetScalerType() string { + if x != nil { + return x.ScalerType } + return "" } -func (x *SendTelemetryLogsResponse) String() string { - return protoimpl.X.MessageStringOf(x) +func (x *ScalerMetricSample) GetValue() float64 { + if x != nil { + return x.Value + } + return 0 } -func (*SendTelemetryLogsResponse) ProtoMessage() {} - -func (x *SendTelemetryLogsResponse) ProtoReflect() protoreflect.Message { - mi := &file_api_v1_metrics_collector_proto_msgTypes[28] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms +func (x *ScalerMetricSample) GetActive() bool { + if x != nil { + return x.Active } - return mi.MessageOf(x) + return false } -// Deprecated: Use SendTelemetryLogsResponse.ProtoReflect.Descriptor instead. -func (*SendTelemetryLogsResponse) Descriptor() ([]byte, []int) { - return file_api_v1_metrics_collector_proto_rawDescGZIP(), []int{28} +func (x *ScalerMetricSample) GetTargetValue() float64 { + if x != nil && x.TargetValue != nil { + return *x.TargetValue + } + return 0 } -func (x *SendTelemetryLogsResponse) GetProcessedCount() int32 { +func (x *ScalerMetricSample) GetTs() *timestamppb.Timestamp { if x != nil { - return x.ProcessedCount + return x.Ts } - return 0 + return nil } -type NodeMetadataRequest struct { +type SendScalerMetricsRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - TeamId string `protobuf:"bytes,1,opt,name=team_id,json=teamId,proto3" json:"team_id,omitempty"` // Unique identifier for the team. - ClusterId string `protobuf:"bytes,2,opt,name=cluster_id,json=clusterId,proto3" json:"cluster_id,omitempty"` // Unique identifier for the cluster. + ClusterId string `protobuf:"bytes,1,opt,name=cluster_id,json=clusterId,proto3" json:"cluster_id,omitempty"` + TeamId string `protobuf:"bytes,2,opt,name=team_id,json=teamId,proto3" json:"team_id,omitempty"` + Samples []*ScalerMetricSample `protobuf:"bytes,3,rep,name=samples,proto3" json:"samples,omitempty"` } -func (x *NodeMetadataRequest) Reset() { - *x = NodeMetadataRequest{} +func (x *SendScalerMetricsRequest) Reset() { + *x = SendScalerMetricsRequest{} if protoimpl.UnsafeEnabled { - mi := &file_api_v1_metrics_collector_proto_msgTypes[29] + mi := &file_api_v1_metrics_collector_proto_msgTypes[36] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } -func (x *NodeMetadataRequest) String() string { +func (x *SendScalerMetricsRequest) String() string { return protoimpl.X.MessageStringOf(x) } -func (*NodeMetadataRequest) ProtoMessage() {} +func (*SendScalerMetricsRequest) ProtoMessage() {} -func (x *NodeMetadataRequest) ProtoReflect() protoreflect.Message { - mi := &file_api_v1_metrics_collector_proto_msgTypes[29] +func (x *SendScalerMetricsRequest) ProtoReflect() protoreflect.Message { + mi := &file_api_v1_metrics_collector_proto_msgTypes[36] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3109,50 +3953,55 @@ func (x *NodeMetadataRequest) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use NodeMetadataRequest.ProtoReflect.Descriptor instead. -func (*NodeMetadataRequest) Descriptor() ([]byte, []int) { - return file_api_v1_metrics_collector_proto_rawDescGZIP(), []int{29} +// Deprecated: Use SendScalerMetricsRequest.ProtoReflect.Descriptor instead. +func (*SendScalerMetricsRequest) Descriptor() ([]byte, []int) { + return file_api_v1_metrics_collector_proto_rawDescGZIP(), []int{36} } -func (x *NodeMetadataRequest) GetTeamId() string { +func (x *SendScalerMetricsRequest) GetClusterId() string { if x != nil { - return x.TeamId + return x.ClusterId } return "" } -func (x *NodeMetadataRequest) GetClusterId() string { +func (x *SendScalerMetricsRequest) GetTeamId() string { if x != nil { - return x.ClusterId + return x.TeamId } return "" } -type NodeMetadataResponse struct { +func (x *SendScalerMetricsRequest) GetSamples() []*ScalerMetricSample { + if x != nil { + return x.Samples + } + return nil +} + +type SendScalerMetricsResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - - NodeToMeta map[string]*Node `protobuf:"bytes,1,rep,name=node_to_meta,json=nodeToMeta,proto3" json:"node_to_meta,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` // map is node name to node } -func (x *NodeMetadataResponse) Reset() { - *x = NodeMetadataResponse{} +func (x *SendScalerMetricsResponse) Reset() { + *x = SendScalerMetricsResponse{} if protoimpl.UnsafeEnabled { - mi := &file_api_v1_metrics_collector_proto_msgTypes[30] + mi := &file_api_v1_metrics_collector_proto_msgTypes[37] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } -func (x *NodeMetadataResponse) String() string { +func (x *SendScalerMetricsResponse) String() string { return protoimpl.X.MessageStringOf(x) } -func (*NodeMetadataResponse) ProtoMessage() {} +func (*SendScalerMetricsResponse) ProtoMessage() {} -func (x *NodeMetadataResponse) ProtoReflect() protoreflect.Message { - mi := &file_api_v1_metrics_collector_proto_msgTypes[30] +func (x *SendScalerMetricsResponse) ProtoReflect() protoreflect.Message { + mi := &file_api_v1_metrics_collector_proto_msgTypes[37] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3163,16 +4012,9 @@ func (x *NodeMetadataResponse) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use NodeMetadataResponse.ProtoReflect.Descriptor instead. -func (*NodeMetadataResponse) Descriptor() ([]byte, []int) { - return file_api_v1_metrics_collector_proto_rawDescGZIP(), []int{30} -} - -func (x *NodeMetadataResponse) GetNodeToMeta() map[string]*Node { - if x != nil { - return x.NodeToMeta - } - return nil +// Deprecated: Use SendScalerMetricsResponse.ProtoReflect.Descriptor instead. +func (*SendScalerMetricsResponse) Descriptor() ([]byte, []int) { + return file_api_v1_metrics_collector_proto_rawDescGZIP(), []int{37} } var File_api_v1_metrics_collector_proto protoreflect.FileDescriptor @@ -3912,7 +4754,7 @@ var file_api_v1_metrics_collector_proto_rawDesc = []byte{ 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x74, 0x65, 0x61, 0x6d, 0x49, 0x64, 0x12, 0x24, 0x0a, 0x04, 0x6c, 0x6f, 0x67, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x6f, 0x67, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x04, 0x6c, - 0x6f, 0x67, 0x73, 0x22, 0xf5, 0x02, 0x0a, 0x12, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x54, + 0x6f, 0x67, 0x73, 0x22, 0xd1, 0x03, 0x0a, 0x12, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x54, 0x72, 0x61, 0x66, 0x66, 0x69, 0x63, 0x49, 0x74, 0x65, 0x6d, 0x12, 0x15, 0x0a, 0x06, 0x73, 0x72, 0x63, 0x5f, 0x69, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x73, 0x72, 0x63, 0x49, 0x70, 0x12, 0x15, 0x0a, 0x06, 0x64, 0x73, 0x74, 0x5f, 0x69, 0x70, 0x18, 0x02, 0x20, 0x01, 0x28, @@ -3935,22 +4777,43 @@ var file_api_v1_metrics_collector_proto_rawDesc = []byte{ 0x73, 0x12, 0x38, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, - 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x22, 0xef, 0x01, 0x0a, 0x0d, - 0x44, 0x6e, 0x73, 0x4c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x49, 0x74, 0x65, 0x6d, 0x12, 0x1b, 0x0a, - 0x09, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x08, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x49, 0x70, 0x12, 0x16, 0x0a, 0x06, 0x64, 0x6f, - 0x6d, 0x61, 0x69, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x64, 0x6f, 0x6d, 0x61, - 0x69, 0x6e, 0x12, 0x21, 0x0a, 0x0c, 0x72, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x64, 0x5f, 0x69, - 0x70, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0b, 0x72, 0x65, 0x73, 0x6f, 0x6c, 0x76, - 0x65, 0x64, 0x49, 0x70, 0x73, 0x12, 0x38, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, - 0x6d, 0x70, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, - 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, - 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, - 0x20, 0x0a, 0x0c, 0x73, 0x72, 0x63, 0x5f, 0x70, 0x6f, 0x64, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, - 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x73, 0x72, 0x63, 0x50, 0x6f, 0x64, 0x4e, 0x61, 0x6d, - 0x65, 0x12, 0x2a, 0x0a, 0x11, 0x73, 0x72, 0x63, 0x5f, 0x70, 0x6f, 0x64, 0x5f, 0x6e, 0x61, 0x6d, - 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x73, 0x72, - 0x63, 0x50, 0x6f, 0x64, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x22, 0xfd, 0x03, + 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x2a, 0x0a, 0x11, 0x64, + 0x73, 0x74, 0x5f, 0x69, 0x70, 0x5f, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x6c, 0x61, 0x74, 0x65, 0x64, + 0x18, 0x0c, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x64, 0x73, 0x74, 0x49, 0x70, 0x54, 0x72, 0x61, + 0x6e, 0x73, 0x6c, 0x61, 0x74, 0x65, 0x64, 0x12, 0x2e, 0x0a, 0x13, 0x64, 0x73, 0x74, 0x5f, 0x70, + 0x6f, 0x72, 0x74, 0x5f, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x6c, 0x61, 0x74, 0x65, 0x64, 0x18, 0x0d, + 0x20, 0x01, 0x28, 0x05, 0x52, 0x11, 0x64, 0x73, 0x74, 0x50, 0x6f, 0x72, 0x74, 0x54, 0x72, 0x61, + 0x6e, 0x73, 0x6c, 0x61, 0x74, 0x65, 0x64, 0x22, 0xef, 0x01, 0x0a, 0x0d, 0x44, 0x6e, 0x73, 0x4c, + 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x49, 0x74, 0x65, 0x6d, 0x12, 0x1b, 0x0a, 0x09, 0x63, 0x6c, 0x69, + 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x63, 0x6c, + 0x69, 0x65, 0x6e, 0x74, 0x49, 0x70, 0x12, 0x16, 0x0a, 0x06, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x12, 0x21, + 0x0a, 0x0c, 0x72, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x64, 0x5f, 0x69, 0x70, 0x73, 0x18, 0x03, + 0x20, 0x03, 0x28, 0x09, 0x52, 0x0b, 0x72, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x64, 0x49, 0x70, + 0x73, 0x12, 0x38, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x04, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, + 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x20, 0x0a, 0x0c, 0x73, + 0x72, 0x63, 0x5f, 0x70, 0x6f, 0x64, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x0a, 0x73, 0x72, 0x63, 0x50, 0x6f, 0x64, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x2a, 0x0a, + 0x11, 0x73, 0x72, 0x63, 0x5f, 0x70, 0x6f, 0x64, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, + 0x63, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x73, 0x72, 0x63, 0x50, 0x6f, 0x64, + 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x22, 0xf1, 0x01, 0x0a, 0x0e, 0x4e, 0x6f, + 0x64, 0x65, 0x45, 0x67, 0x72, 0x65, 0x73, 0x73, 0x50, 0x61, 0x74, 0x68, 0x12, 0x25, 0x0a, 0x0e, + 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x5f, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x50, 0x72, 0x6f, 0x76, 0x69, + 0x64, 0x65, 0x72, 0x12, 0x16, 0x0a, 0x06, 0x72, 0x65, 0x67, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x06, 0x72, 0x65, 0x67, 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x7a, + 0x6f, 0x6e, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x7a, 0x6f, 0x6e, 0x65, 0x12, + 0x15, 0x0a, 0x06, 0x76, 0x70, 0x63, 0x5f, 0x69, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x05, 0x76, 0x70, 0x63, 0x49, 0x64, 0x12, 0x1b, 0x0a, 0x09, 0x73, 0x75, 0x62, 0x6e, 0x65, 0x74, + 0x5f, 0x69, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x73, 0x75, 0x62, 0x6e, 0x65, + 0x74, 0x49, 0x64, 0x12, 0x27, 0x0a, 0x0d, 0x68, 0x61, 0x73, 0x5f, 0x70, 0x75, 0x62, 0x6c, 0x69, + 0x63, 0x5f, 0x69, 0x70, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x48, 0x00, 0x52, 0x0b, 0x68, 0x61, + 0x73, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x49, 0x70, 0x88, 0x01, 0x01, 0x12, 0x1d, 0x0a, 0x0a, + 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x5f, 0x69, 0x70, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x09, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x49, 0x70, 0x42, 0x10, 0x0a, 0x0e, 0x5f, + 0x68, 0x61, 0x73, 0x5f, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x69, 0x70, 0x22, 0xcb, 0x04, 0x0a, 0x20, 0x53, 0x65, 0x6e, 0x64, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x54, 0x72, 0x61, 0x66, 0x66, 0x69, 0x63, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x69, 0x64, @@ -3976,303 +4839,471 @@ var file_api_v1_metrics_collector_proto_rawDesc = []byte{ 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x88, 0x01, 0x01, 0x12, 0x2c, 0x0a, 0x0f, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x5f, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x48, 0x01, 0x52, 0x0e, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x43, 0x6f, 0x6d, - 0x6d, 0x69, 0x74, 0x88, 0x01, 0x01, 0x1a, 0x3c, 0x0a, 0x0e, 0x49, 0x70, 0x32, 0x64, 0x6f, 0x6d, - 0x61, 0x69, 0x6e, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, - 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, - 0x3a, 0x02, 0x38, 0x01, 0x42, 0x13, 0x0a, 0x11, 0x5f, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, - 0x72, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x42, 0x12, 0x0a, 0x10, 0x5f, 0x6f, 0x70, - 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x5f, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x22, 0x4c, 0x0a, - 0x21, 0x53, 0x65, 0x6e, 0x64, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x54, 0x72, 0x61, 0x66, - 0x66, 0x69, 0x63, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x12, 0x27, 0x0a, 0x0f, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x65, 0x64, 0x5f, - 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0e, 0x70, 0x72, 0x6f, - 0x63, 0x65, 0x73, 0x73, 0x65, 0x64, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x22, 0xa5, 0x02, 0x0a, 0x08, - 0x4c, 0x6f, 0x67, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x38, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, - 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, - 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, - 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, - 0x6d, 0x70, 0x12, 0x26, 0x0a, 0x05, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x0e, 0x32, 0x10, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x6f, 0x67, 0x4c, 0x65, - 0x76, 0x65, 0x6c, 0x52, 0x05, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, - 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, - 0x73, 0x61, 0x67, 0x65, 0x12, 0x34, 0x0a, 0x06, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x18, 0x06, - 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x6f, - 0x67, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x2e, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x45, 0x6e, 0x74, - 0x72, 0x79, 0x52, 0x06, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x72, - 0x72, 0x6f, 0x72, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, - 0x12, 0x16, 0x0a, 0x06, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x06, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x1a, 0x39, 0x0a, 0x0b, 0x46, 0x69, 0x65, 0x6c, - 0x64, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, - 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, - 0x02, 0x38, 0x01, 0x22, 0x44, 0x0a, 0x19, 0x53, 0x65, 0x6e, 0x64, 0x54, 0x65, 0x6c, 0x65, 0x6d, - 0x65, 0x74, 0x72, 0x79, 0x4c, 0x6f, 0x67, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x6d, 0x69, 0x74, 0x88, 0x01, 0x01, 0x12, 0x3c, 0x0a, 0x0b, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x65, + 0x67, 0x72, 0x65, 0x73, 0x73, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x61, 0x70, + 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x45, 0x67, 0x72, 0x65, 0x73, 0x73, 0x50, + 0x61, 0x74, 0x68, 0x48, 0x02, 0x52, 0x0a, 0x6e, 0x6f, 0x64, 0x65, 0x45, 0x67, 0x72, 0x65, 0x73, + 0x73, 0x88, 0x01, 0x01, 0x1a, 0x3c, 0x0a, 0x0e, 0x49, 0x70, 0x32, 0x64, 0x6f, 0x6d, 0x61, 0x69, + 0x6e, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, + 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, + 0x38, 0x01, 0x42, 0x13, 0x0a, 0x11, 0x5f, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x5f, + 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x42, 0x12, 0x0a, 0x10, 0x5f, 0x6f, 0x70, 0x65, 0x72, + 0x61, 0x74, 0x6f, 0x72, 0x5f, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x42, 0x0e, 0x0a, 0x0c, 0x5f, + 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x65, 0x67, 0x72, 0x65, 0x73, 0x73, 0x22, 0x4c, 0x0a, 0x21, 0x53, + 0x65, 0x6e, 0x64, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x54, 0x72, 0x61, 0x66, 0x66, 0x69, + 0x63, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x27, 0x0a, 0x0f, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x65, 0x64, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0e, 0x70, 0x72, 0x6f, 0x63, 0x65, - 0x73, 0x73, 0x65, 0x64, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x22, 0x4d, 0x0a, 0x13, 0x4e, 0x6f, 0x64, - 0x65, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x12, 0x17, 0x0a, 0x07, 0x74, 0x65, 0x61, 0x6d, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x06, 0x74, 0x65, 0x61, 0x6d, 0x49, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x6c, 0x75, - 0x73, 0x74, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, - 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, 0x64, 0x22, 0xb3, 0x01, 0x0a, 0x14, 0x4e, 0x6f, 0x64, - 0x65, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x12, 0x4e, 0x0a, 0x0c, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x74, 0x6f, 0x5f, 0x6d, 0x65, 0x74, - 0x61, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2c, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, - 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x54, 0x6f, 0x4d, 0x65, 0x74, 0x61, - 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0a, 0x6e, 0x6f, 0x64, 0x65, 0x54, 0x6f, 0x4d, 0x65, 0x74, - 0x61, 0x1a, 0x4b, 0x0a, 0x0f, 0x4e, 0x6f, 0x64, 0x65, 0x54, 0x6f, 0x4d, 0x65, 0x74, 0x61, 0x45, - 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x22, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4e, - 0x6f, 0x64, 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x2a, 0xa3, - 0x02, 0x0a, 0x09, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x1a, 0x0a, 0x16, - 0x45, 0x56, 0x45, 0x4e, 0x54, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, - 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x12, 0x0a, 0x0e, 0x45, 0x56, 0x45, 0x4e, - 0x54, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x41, 0x44, 0x44, 0x10, 0x01, 0x12, 0x15, 0x0a, 0x11, - 0x45, 0x56, 0x45, 0x4e, 0x54, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x55, 0x50, 0x44, 0x41, 0x54, - 0x45, 0x10, 0x02, 0x12, 0x15, 0x0a, 0x11, 0x45, 0x56, 0x45, 0x4e, 0x54, 0x5f, 0x54, 0x59, 0x50, - 0x45, 0x5f, 0x44, 0x45, 0x4c, 0x45, 0x54, 0x45, 0x10, 0x03, 0x12, 0x17, 0x0a, 0x13, 0x45, 0x56, - 0x45, 0x4e, 0x54, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x4d, 0x45, 0x54, 0x41, 0x44, 0x41, 0x54, - 0x41, 0x10, 0x04, 0x12, 0x16, 0x0a, 0x12, 0x45, 0x56, 0x45, 0x4e, 0x54, 0x5f, 0x54, 0x59, 0x50, - 0x45, 0x5f, 0x4d, 0x45, 0x54, 0x52, 0x49, 0x43, 0x53, 0x10, 0x05, 0x12, 0x20, 0x0a, 0x1c, 0x45, - 0x56, 0x45, 0x4e, 0x54, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x43, 0x4f, 0x4e, 0x54, 0x41, 0x49, - 0x4e, 0x45, 0x52, 0x5f, 0x53, 0x54, 0x41, 0x52, 0x54, 0x45, 0x44, 0x10, 0x06, 0x12, 0x20, 0x0a, - 0x1c, 0x45, 0x56, 0x45, 0x4e, 0x54, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x43, 0x4f, 0x4e, 0x54, - 0x41, 0x49, 0x4e, 0x45, 0x52, 0x5f, 0x53, 0x54, 0x4f, 0x50, 0x50, 0x45, 0x44, 0x10, 0x07, 0x12, - 0x22, 0x0a, 0x1e, 0x45, 0x56, 0x45, 0x4e, 0x54, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x43, 0x4f, - 0x4e, 0x54, 0x41, 0x49, 0x4e, 0x45, 0x52, 0x5f, 0x52, 0x45, 0x53, 0x54, 0x41, 0x52, 0x54, 0x45, - 0x44, 0x10, 0x08, 0x12, 0x1f, 0x0a, 0x1b, 0x45, 0x56, 0x45, 0x4e, 0x54, 0x5f, 0x54, 0x59, 0x50, - 0x45, 0x5f, 0x43, 0x4c, 0x55, 0x53, 0x54, 0x45, 0x52, 0x5f, 0x53, 0x4e, 0x41, 0x50, 0x53, 0x48, - 0x4f, 0x54, 0x10, 0x09, 0x2a, 0x80, 0x14, 0x0a, 0x0c, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, - 0x65, 0x54, 0x79, 0x70, 0x65, 0x12, 0x1d, 0x0a, 0x19, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, - 0x45, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, - 0x45, 0x44, 0x10, 0x00, 0x12, 0x16, 0x0a, 0x12, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, - 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x4e, 0x4f, 0x44, 0x45, 0x10, 0x01, 0x12, 0x15, 0x0a, 0x11, - 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x50, 0x4f, - 0x44, 0x10, 0x02, 0x12, 0x1b, 0x0a, 0x17, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, - 0x54, 0x59, 0x50, 0x45, 0x5f, 0x4e, 0x41, 0x4d, 0x45, 0x53, 0x50, 0x41, 0x43, 0x45, 0x10, 0x03, - 0x12, 0x17, 0x0a, 0x13, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, 0x54, 0x59, 0x50, - 0x45, 0x5f, 0x45, 0x56, 0x45, 0x4e, 0x54, 0x10, 0x04, 0x12, 0x1b, 0x0a, 0x17, 0x52, 0x45, 0x53, - 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x45, 0x4e, 0x44, 0x50, 0x4f, - 0x49, 0x4e, 0x54, 0x53, 0x10, 0x05, 0x12, 0x21, 0x0a, 0x1d, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, - 0x43, 0x45, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x53, 0x45, 0x52, 0x56, 0x49, 0x43, 0x45, 0x5f, - 0x41, 0x43, 0x43, 0x4f, 0x55, 0x4e, 0x54, 0x10, 0x06, 0x12, 0x1d, 0x0a, 0x19, 0x52, 0x45, 0x53, - 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x4c, 0x49, 0x4d, 0x49, 0x54, - 0x5f, 0x52, 0x41, 0x4e, 0x47, 0x45, 0x10, 0x07, 0x12, 0x20, 0x0a, 0x1c, 0x52, 0x45, 0x53, 0x4f, - 0x55, 0x52, 0x43, 0x45, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, - 0x43, 0x45, 0x5f, 0x51, 0x55, 0x4f, 0x54, 0x41, 0x10, 0x08, 0x12, 0x1c, 0x0a, 0x18, 0x52, 0x45, - 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x44, 0x45, 0x50, 0x4c, - 0x4f, 0x59, 0x4d, 0x45, 0x4e, 0x54, 0x10, 0x09, 0x12, 0x1e, 0x0a, 0x1a, 0x52, 0x45, 0x53, 0x4f, - 0x55, 0x52, 0x43, 0x45, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x45, 0x46, - 0x55, 0x4c, 0x5f, 0x53, 0x45, 0x54, 0x10, 0x0a, 0x12, 0x1c, 0x0a, 0x18, 0x52, 0x45, 0x53, 0x4f, - 0x55, 0x52, 0x43, 0x45, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x44, 0x41, 0x45, 0x4d, 0x4f, 0x4e, - 0x5f, 0x53, 0x45, 0x54, 0x10, 0x0b, 0x12, 0x1d, 0x0a, 0x19, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, - 0x43, 0x45, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x52, 0x45, 0x50, 0x4c, 0x49, 0x43, 0x41, 0x5f, - 0x53, 0x45, 0x54, 0x10, 0x0c, 0x12, 0x28, 0x0a, 0x24, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, - 0x45, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x52, 0x45, 0x50, 0x4c, 0x49, 0x43, 0x41, 0x54, 0x49, - 0x4f, 0x4e, 0x5f, 0x43, 0x4f, 0x4e, 0x54, 0x52, 0x4f, 0x4c, 0x4c, 0x45, 0x52, 0x10, 0x0d, 0x12, - 0x15, 0x0a, 0x11, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, 0x54, 0x59, 0x50, 0x45, - 0x5f, 0x4a, 0x4f, 0x42, 0x10, 0x0e, 0x12, 0x1a, 0x0a, 0x16, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, - 0x43, 0x45, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x43, 0x52, 0x4f, 0x4e, 0x5f, 0x4a, 0x4f, 0x42, - 0x10, 0x0f, 0x12, 0x29, 0x0a, 0x25, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, 0x54, - 0x59, 0x50, 0x45, 0x5f, 0x50, 0x45, 0x52, 0x53, 0x49, 0x53, 0x54, 0x45, 0x4e, 0x54, 0x5f, 0x56, - 0x4f, 0x4c, 0x55, 0x4d, 0x45, 0x5f, 0x43, 0x4c, 0x41, 0x49, 0x4d, 0x10, 0x10, 0x12, 0x23, 0x0a, - 0x1f, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x50, + 0x73, 0x73, 0x65, 0x64, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x22, 0xb3, 0x03, 0x0a, 0x17, 0x43, 0x6f, + 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x52, 0x65, 0x64, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, + 0x73, 0x49, 0x74, 0x65, 0x6d, 0x12, 0x19, 0x0a, 0x08, 0x70, 0x6f, 0x64, 0x5f, 0x6e, 0x61, 0x6d, + 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x70, 0x6f, 0x64, 0x4e, 0x61, 0x6d, 0x65, + 0x12, 0x23, 0x0a, 0x0d, 0x70, 0x6f, 0x64, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, + 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x70, 0x6f, 0x64, 0x4e, 0x61, 0x6d, 0x65, + 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x17, 0x0a, 0x07, 0x70, 0x6f, 0x64, 0x5f, 0x75, 0x69, 0x64, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x70, 0x6f, 0x64, 0x55, 0x69, 0x64, 0x12, 0x25, + 0x0a, 0x0e, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x6e, 0x61, 0x6d, 0x65, + 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, + 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x38, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, + 0x6d, 0x70, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, + 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, + 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, + 0x23, 0x0a, 0x0d, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, + 0x18, 0x06, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0c, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x43, + 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x1f, 0x0a, 0x0b, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x5f, 0x63, 0x6f, + 0x75, 0x6e, 0x74, 0x18, 0x07, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, 0x65, 0x72, 0x72, 0x6f, 0x72, + 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x26, 0x0a, 0x0f, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x5f, 0x73, 0x75, 0x6d, 0x5f, 0x75, 0x73, 0x18, 0x08, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0d, + 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x75, 0x6d, 0x55, 0x73, 0x12, 0x29, 0x0a, + 0x10, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x62, 0x75, 0x63, 0x6b, 0x65, 0x74, + 0x73, 0x18, 0x09, 0x20, 0x03, 0x28, 0x04, 0x52, 0x0f, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x42, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x73, 0x12, 0x26, 0x0a, 0x0f, 0x64, 0x75, 0x72, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6d, 0x61, 0x78, 0x5f, 0x75, 0x73, 0x18, 0x0a, 0x20, 0x01, 0x28, + 0x04, 0x52, 0x0d, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4d, 0x61, 0x78, 0x55, 0x73, + 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x6f, 0x6e, 0x6e, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x0b, + 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x63, 0x6f, 0x6e, 0x6e, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x22, + 0xb3, 0x02, 0x0a, 0x1e, 0x53, 0x65, 0x6e, 0x64, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, + 0x72, 0x52, 0x65, 0x64, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x69, 0x64, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, + 0x64, 0x12, 0x17, 0x0a, 0x07, 0x74, 0x65, 0x61, 0x6d, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x06, 0x74, 0x65, 0x61, 0x6d, 0x49, 0x64, 0x12, 0x1b, 0x0a, 0x09, 0x6e, 0x6f, + 0x64, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6e, + 0x6f, 0x64, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x35, 0x0a, 0x05, 0x69, 0x74, 0x65, 0x6d, 0x73, + 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, + 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x52, 0x65, 0x64, 0x4d, 0x65, 0x74, 0x72, + 0x69, 0x63, 0x73, 0x49, 0x74, 0x65, 0x6d, 0x52, 0x05, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x12, 0x2e, + 0x0a, 0x10, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, + 0x6f, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x0f, 0x6f, 0x70, 0x65, 0x72, + 0x61, 0x74, 0x6f, 0x72, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x88, 0x01, 0x01, 0x12, 0x2c, + 0x0a, 0x0f, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x5f, 0x63, 0x6f, 0x6d, 0x6d, 0x69, + 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x48, 0x01, 0x52, 0x0e, 0x6f, 0x70, 0x65, 0x72, 0x61, + 0x74, 0x6f, 0x72, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x88, 0x01, 0x01, 0x42, 0x13, 0x0a, 0x11, + 0x5f, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, + 0x6e, 0x42, 0x12, 0x0a, 0x10, 0x5f, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x5f, 0x63, + 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x22, 0x4a, 0x0a, 0x1f, 0x53, 0x65, 0x6e, 0x64, 0x43, 0x6f, 0x6e, + 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x52, 0x65, 0x64, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x27, 0x0a, 0x0f, 0x70, 0x72, 0x6f, 0x63, + 0x65, 0x73, 0x73, 0x65, 0x64, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x05, 0x52, 0x0e, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x65, 0x64, 0x43, 0x6f, 0x75, 0x6e, + 0x74, 0x22, 0xa5, 0x02, 0x0a, 0x08, 0x4c, 0x6f, 0x67, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x38, + 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x74, + 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x26, 0x0a, 0x05, 0x6c, 0x65, 0x76, 0x65, + 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x10, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, + 0x2e, 0x4c, 0x6f, 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x52, 0x05, 0x6c, 0x65, 0x76, 0x65, 0x6c, + 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x34, 0x0a, 0x06, 0x66, 0x69, + 0x65, 0x6c, 0x64, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x61, 0x70, 0x69, + 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x6f, 0x67, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x2e, 0x46, 0x69, 0x65, + 0x6c, 0x64, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x06, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x73, + 0x12, 0x14, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, + 0x18, 0x0b, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x1a, 0x39, + 0x0a, 0x0b, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, + 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, + 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, + 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x44, 0x0a, 0x19, 0x53, 0x65, 0x6e, + 0x64, 0x54, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x74, 0x72, 0x79, 0x4c, 0x6f, 0x67, 0x73, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x27, 0x0a, 0x0f, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, + 0x73, 0x65, 0x64, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, + 0x0e, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x65, 0x64, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x22, + 0x4d, 0x0a, 0x13, 0x4e, 0x6f, 0x64, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x17, 0x0a, 0x07, 0x74, 0x65, 0x61, 0x6d, 0x5f, 0x69, + 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x74, 0x65, 0x61, 0x6d, 0x49, 0x64, 0x12, + 0x1d, 0x0a, 0x0a, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, 0x64, 0x22, 0xb3, + 0x01, 0x0a, 0x14, 0x4e, 0x6f, 0x64, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4e, 0x0a, 0x0c, 0x6e, 0x6f, 0x64, 0x65, 0x5f, + 0x74, 0x6f, 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2c, 0x2e, + 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x64, + 0x61, 0x74, 0x61, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x4e, 0x6f, 0x64, 0x65, + 0x54, 0x6f, 0x4d, 0x65, 0x74, 0x61, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0a, 0x6e, 0x6f, 0x64, + 0x65, 0x54, 0x6f, 0x4d, 0x65, 0x74, 0x61, 0x1a, 0x4b, 0x0a, 0x0f, 0x4e, 0x6f, 0x64, 0x65, 0x54, + 0x6f, 0x4d, 0x65, 0x74, 0x61, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, + 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x22, 0x0a, 0x05, + 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x61, 0x70, + 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, + 0x3a, 0x02, 0x38, 0x01, 0x22, 0x9a, 0x03, 0x0a, 0x12, 0x53, 0x63, 0x61, 0x6c, 0x65, 0x72, 0x4d, + 0x65, 0x74, 0x72, 0x69, 0x63, 0x53, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x12, 0x22, 0x0a, 0x0c, 0x73, + 0x63, 0x61, 0x6c, 0x65, 0x64, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x0c, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x64, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x12, + 0x1c, 0x0a, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x23, 0x0a, + 0x0d, 0x77, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x5f, 0x6b, 0x69, 0x6e, 0x64, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x77, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x4b, 0x69, + 0x6e, 0x64, 0x12, 0x23, 0x0a, 0x0d, 0x77, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x5f, 0x6e, + 0x61, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x77, 0x6f, 0x72, 0x6b, 0x6c, + 0x6f, 0x61, 0x64, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x74, 0x72, 0x69, 0x67, 0x67, + 0x65, 0x72, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x05, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0c, + 0x74, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x12, 0x1f, 0x0a, 0x0b, + 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x0a, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x1f, 0x0a, + 0x0b, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x72, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x07, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x0a, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x72, 0x54, 0x79, 0x70, 0x65, 0x12, 0x14, + 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x01, 0x52, 0x05, 0x76, + 0x61, 0x6c, 0x75, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x61, 0x63, 0x74, 0x69, 0x76, 0x65, 0x18, 0x09, + 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x61, 0x63, 0x74, 0x69, 0x76, 0x65, 0x12, 0x26, 0x0a, 0x0c, + 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x0a, 0x20, 0x01, + 0x28, 0x01, 0x48, 0x00, 0x52, 0x0b, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x56, 0x61, 0x6c, 0x75, + 0x65, 0x88, 0x01, 0x01, 0x12, 0x2a, 0x0a, 0x02, 0x74, 0x73, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, + 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x02, 0x74, 0x73, + 0x42, 0x0f, 0x0a, 0x0d, 0x5f, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x5f, 0x76, 0x61, 0x6c, 0x75, + 0x65, 0x22, 0x88, 0x01, 0x0a, 0x18, 0x53, 0x65, 0x6e, 0x64, 0x53, 0x63, 0x61, 0x6c, 0x65, 0x72, + 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, + 0x0a, 0x0a, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x09, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, 0x64, 0x12, 0x17, 0x0a, + 0x07, 0x74, 0x65, 0x61, 0x6d, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, + 0x74, 0x65, 0x61, 0x6d, 0x49, 0x64, 0x12, 0x34, 0x0a, 0x07, 0x73, 0x61, 0x6d, 0x70, 0x6c, 0x65, + 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, + 0x2e, 0x53, 0x63, 0x61, 0x6c, 0x65, 0x72, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x53, 0x61, 0x6d, + 0x70, 0x6c, 0x65, 0x52, 0x07, 0x73, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x73, 0x22, 0x1b, 0x0a, 0x19, + 0x53, 0x65, 0x6e, 0x64, 0x53, 0x63, 0x61, 0x6c, 0x65, 0x72, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, + 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2a, 0xa3, 0x02, 0x0a, 0x09, 0x45, 0x76, + 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x1a, 0x0a, 0x16, 0x45, 0x56, 0x45, 0x4e, 0x54, + 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, + 0x44, 0x10, 0x00, 0x12, 0x12, 0x0a, 0x0e, 0x45, 0x56, 0x45, 0x4e, 0x54, 0x5f, 0x54, 0x59, 0x50, + 0x45, 0x5f, 0x41, 0x44, 0x44, 0x10, 0x01, 0x12, 0x15, 0x0a, 0x11, 0x45, 0x56, 0x45, 0x4e, 0x54, + 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x55, 0x50, 0x44, 0x41, 0x54, 0x45, 0x10, 0x02, 0x12, 0x15, + 0x0a, 0x11, 0x45, 0x56, 0x45, 0x4e, 0x54, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x44, 0x45, 0x4c, + 0x45, 0x54, 0x45, 0x10, 0x03, 0x12, 0x17, 0x0a, 0x13, 0x45, 0x56, 0x45, 0x4e, 0x54, 0x5f, 0x54, + 0x59, 0x50, 0x45, 0x5f, 0x4d, 0x45, 0x54, 0x41, 0x44, 0x41, 0x54, 0x41, 0x10, 0x04, 0x12, 0x16, + 0x0a, 0x12, 0x45, 0x56, 0x45, 0x4e, 0x54, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x4d, 0x45, 0x54, + 0x52, 0x49, 0x43, 0x53, 0x10, 0x05, 0x12, 0x20, 0x0a, 0x1c, 0x45, 0x56, 0x45, 0x4e, 0x54, 0x5f, + 0x54, 0x59, 0x50, 0x45, 0x5f, 0x43, 0x4f, 0x4e, 0x54, 0x41, 0x49, 0x4e, 0x45, 0x52, 0x5f, 0x53, + 0x54, 0x41, 0x52, 0x54, 0x45, 0x44, 0x10, 0x06, 0x12, 0x20, 0x0a, 0x1c, 0x45, 0x56, 0x45, 0x4e, + 0x54, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x43, 0x4f, 0x4e, 0x54, 0x41, 0x49, 0x4e, 0x45, 0x52, + 0x5f, 0x53, 0x54, 0x4f, 0x50, 0x50, 0x45, 0x44, 0x10, 0x07, 0x12, 0x22, 0x0a, 0x1e, 0x45, 0x56, + 0x45, 0x4e, 0x54, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x43, 0x4f, 0x4e, 0x54, 0x41, 0x49, 0x4e, + 0x45, 0x52, 0x5f, 0x52, 0x45, 0x53, 0x54, 0x41, 0x52, 0x54, 0x45, 0x44, 0x10, 0x08, 0x12, 0x1f, + 0x0a, 0x1b, 0x45, 0x56, 0x45, 0x4e, 0x54, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x43, 0x4c, 0x55, + 0x53, 0x54, 0x45, 0x52, 0x5f, 0x53, 0x4e, 0x41, 0x50, 0x53, 0x48, 0x4f, 0x54, 0x10, 0x09, 0x2a, + 0xee, 0x1b, 0x0a, 0x0c, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x54, 0x79, 0x70, 0x65, + 0x12, 0x1d, 0x0a, 0x19, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, 0x54, 0x59, 0x50, + 0x45, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, + 0x16, 0x0a, 0x12, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, 0x54, 0x59, 0x50, 0x45, + 0x5f, 0x4e, 0x4f, 0x44, 0x45, 0x10, 0x01, 0x12, 0x15, 0x0a, 0x11, 0x52, 0x45, 0x53, 0x4f, 0x55, + 0x52, 0x43, 0x45, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x50, 0x4f, 0x44, 0x10, 0x02, 0x12, 0x1b, + 0x0a, 0x17, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, + 0x4e, 0x41, 0x4d, 0x45, 0x53, 0x50, 0x41, 0x43, 0x45, 0x10, 0x03, 0x12, 0x17, 0x0a, 0x13, 0x52, + 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x45, 0x56, 0x45, + 0x4e, 0x54, 0x10, 0x04, 0x12, 0x1b, 0x0a, 0x17, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, + 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x45, 0x4e, 0x44, 0x50, 0x4f, 0x49, 0x4e, 0x54, 0x53, 0x10, + 0x05, 0x12, 0x21, 0x0a, 0x1d, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, 0x54, 0x59, + 0x50, 0x45, 0x5f, 0x53, 0x45, 0x52, 0x56, 0x49, 0x43, 0x45, 0x5f, 0x41, 0x43, 0x43, 0x4f, 0x55, + 0x4e, 0x54, 0x10, 0x06, 0x12, 0x1d, 0x0a, 0x19, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, + 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x4c, 0x49, 0x4d, 0x49, 0x54, 0x5f, 0x52, 0x41, 0x4e, 0x47, + 0x45, 0x10, 0x07, 0x12, 0x20, 0x0a, 0x1c, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, + 0x54, 0x59, 0x50, 0x45, 0x5f, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, 0x51, 0x55, + 0x4f, 0x54, 0x41, 0x10, 0x08, 0x12, 0x1c, 0x0a, 0x18, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, + 0x45, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x44, 0x45, 0x50, 0x4c, 0x4f, 0x59, 0x4d, 0x45, 0x4e, + 0x54, 0x10, 0x09, 0x12, 0x1e, 0x0a, 0x1a, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, + 0x54, 0x59, 0x50, 0x45, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x45, 0x46, 0x55, 0x4c, 0x5f, 0x53, 0x45, + 0x54, 0x10, 0x0a, 0x12, 0x1c, 0x0a, 0x18, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, + 0x54, 0x59, 0x50, 0x45, 0x5f, 0x44, 0x41, 0x45, 0x4d, 0x4f, 0x4e, 0x5f, 0x53, 0x45, 0x54, 0x10, + 0x0b, 0x12, 0x1d, 0x0a, 0x19, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, 0x54, 0x59, + 0x50, 0x45, 0x5f, 0x52, 0x45, 0x50, 0x4c, 0x49, 0x43, 0x41, 0x5f, 0x53, 0x45, 0x54, 0x10, 0x0c, + 0x12, 0x28, 0x0a, 0x24, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, 0x54, 0x59, 0x50, + 0x45, 0x5f, 0x52, 0x45, 0x50, 0x4c, 0x49, 0x43, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x43, 0x4f, + 0x4e, 0x54, 0x52, 0x4f, 0x4c, 0x4c, 0x45, 0x52, 0x10, 0x0d, 0x12, 0x15, 0x0a, 0x11, 0x52, 0x45, + 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x4a, 0x4f, 0x42, 0x10, + 0x0e, 0x12, 0x1a, 0x0a, 0x16, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, 0x54, 0x59, + 0x50, 0x45, 0x5f, 0x43, 0x52, 0x4f, 0x4e, 0x5f, 0x4a, 0x4f, 0x42, 0x10, 0x0f, 0x12, 0x29, 0x0a, + 0x25, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x50, 0x45, 0x52, 0x53, 0x49, 0x53, 0x54, 0x45, 0x4e, 0x54, 0x5f, 0x56, 0x4f, 0x4c, 0x55, 0x4d, 0x45, - 0x10, 0x11, 0x12, 0x1f, 0x0a, 0x1b, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, 0x54, - 0x59, 0x50, 0x45, 0x5f, 0x53, 0x54, 0x4f, 0x52, 0x41, 0x47, 0x45, 0x5f, 0x43, 0x4c, 0x41, 0x53, - 0x53, 0x10, 0x12, 0x12, 0x19, 0x0a, 0x15, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, - 0x54, 0x59, 0x50, 0x45, 0x5f, 0x53, 0x45, 0x52, 0x56, 0x49, 0x43, 0x45, 0x10, 0x13, 0x12, 0x19, + 0x5f, 0x43, 0x4c, 0x41, 0x49, 0x4d, 0x10, 0x10, 0x12, 0x23, 0x0a, 0x1f, 0x52, 0x45, 0x53, 0x4f, + 0x55, 0x52, 0x43, 0x45, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x50, 0x45, 0x52, 0x53, 0x49, 0x53, + 0x54, 0x45, 0x4e, 0x54, 0x5f, 0x56, 0x4f, 0x4c, 0x55, 0x4d, 0x45, 0x10, 0x11, 0x12, 0x1f, 0x0a, + 0x1b, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x53, + 0x54, 0x4f, 0x52, 0x41, 0x47, 0x45, 0x5f, 0x43, 0x4c, 0x41, 0x53, 0x53, 0x10, 0x12, 0x12, 0x19, 0x0a, 0x15, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, - 0x49, 0x4e, 0x47, 0x52, 0x45, 0x53, 0x53, 0x10, 0x14, 0x12, 0x1f, 0x0a, 0x1b, 0x52, 0x45, 0x53, + 0x53, 0x45, 0x52, 0x56, 0x49, 0x43, 0x45, 0x10, 0x13, 0x12, 0x19, 0x0a, 0x15, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x49, 0x4e, 0x47, 0x52, 0x45, - 0x53, 0x53, 0x5f, 0x43, 0x4c, 0x41, 0x53, 0x53, 0x10, 0x15, 0x12, 0x20, 0x0a, 0x1c, 0x52, 0x45, - 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x4e, 0x45, 0x54, 0x57, - 0x4f, 0x52, 0x4b, 0x5f, 0x50, 0x4f, 0x4c, 0x49, 0x43, 0x59, 0x10, 0x16, 0x12, 0x16, 0x0a, 0x12, - 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x52, 0x4f, - 0x4c, 0x45, 0x10, 0x17, 0x12, 0x1e, 0x0a, 0x1a, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, - 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x52, 0x4f, 0x4c, 0x45, 0x5f, 0x42, 0x49, 0x4e, 0x44, 0x49, - 0x4e, 0x47, 0x10, 0x18, 0x12, 0x1e, 0x0a, 0x1a, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, - 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x43, 0x4c, 0x55, 0x53, 0x54, 0x45, 0x52, 0x5f, 0x52, 0x4f, - 0x4c, 0x45, 0x10, 0x19, 0x12, 0x26, 0x0a, 0x22, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, - 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x43, 0x4c, 0x55, 0x53, 0x54, 0x45, 0x52, 0x5f, 0x52, 0x4f, - 0x4c, 0x45, 0x5f, 0x42, 0x49, 0x4e, 0x44, 0x49, 0x4e, 0x47, 0x10, 0x1a, 0x12, 0x2b, 0x0a, 0x27, - 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x48, 0x4f, - 0x52, 0x49, 0x5a, 0x4f, 0x4e, 0x54, 0x41, 0x4c, 0x5f, 0x50, 0x4f, 0x44, 0x5f, 0x41, 0x55, 0x54, - 0x4f, 0x53, 0x43, 0x41, 0x4c, 0x45, 0x52, 0x10, 0x1b, 0x12, 0x29, 0x0a, 0x25, 0x52, 0x45, 0x53, - 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x56, 0x45, 0x52, 0x54, 0x49, - 0x43, 0x41, 0x4c, 0x5f, 0x50, 0x4f, 0x44, 0x5f, 0x41, 0x55, 0x54, 0x4f, 0x53, 0x43, 0x41, 0x4c, - 0x45, 0x52, 0x10, 0x1c, 0x12, 0x27, 0x0a, 0x23, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, - 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x50, 0x4f, 0x44, 0x5f, 0x44, 0x49, 0x53, 0x52, 0x55, 0x50, - 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x42, 0x55, 0x44, 0x47, 0x45, 0x54, 0x10, 0x1d, 0x12, 0x25, 0x0a, - 0x21, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x50, - 0x4f, 0x44, 0x5f, 0x53, 0x45, 0x43, 0x55, 0x52, 0x49, 0x54, 0x59, 0x5f, 0x50, 0x4f, 0x4c, 0x49, - 0x43, 0x59, 0x10, 0x1e, 0x12, 0x2c, 0x0a, 0x28, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, - 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x43, 0x55, 0x53, 0x54, 0x4f, 0x4d, 0x5f, 0x52, 0x45, 0x53, - 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, 0x44, 0x45, 0x46, 0x49, 0x4e, 0x49, 0x54, 0x49, 0x4f, 0x4e, - 0x10, 0x1f, 0x12, 0x21, 0x0a, 0x1d, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, 0x54, - 0x59, 0x50, 0x45, 0x5f, 0x43, 0x55, 0x53, 0x54, 0x4f, 0x4d, 0x5f, 0x52, 0x45, 0x53, 0x4f, 0x55, - 0x52, 0x43, 0x45, 0x10, 0x20, 0x12, 0x1c, 0x0a, 0x18, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, - 0x45, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x43, 0x4f, 0x4e, 0x46, 0x49, 0x47, 0x5f, 0x4d, 0x41, - 0x50, 0x10, 0x21, 0x12, 0x18, 0x0a, 0x14, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, - 0x54, 0x59, 0x50, 0x45, 0x5f, 0x53, 0x45, 0x43, 0x52, 0x45, 0x54, 0x10, 0x22, 0x12, 0x1b, 0x0a, - 0x17, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x43, - 0x4f, 0x4e, 0x54, 0x41, 0x49, 0x4e, 0x45, 0x52, 0x10, 0x23, 0x12, 0x1f, 0x0a, 0x1b, 0x52, 0x45, - 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x4e, 0x4f, 0x44, 0x45, - 0x5f, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x10, 0x24, 0x12, 0x24, 0x0a, 0x20, 0x52, - 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x43, 0x4f, 0x4e, - 0x54, 0x41, 0x49, 0x4e, 0x45, 0x52, 0x5f, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x10, - 0x25, 0x12, 0x19, 0x0a, 0x15, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, 0x54, 0x59, - 0x50, 0x45, 0x5f, 0x43, 0x4c, 0x55, 0x53, 0x54, 0x45, 0x52, 0x10, 0x26, 0x12, 0x1a, 0x0a, 0x16, - 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x43, 0x53, - 0x49, 0x5f, 0x4e, 0x4f, 0x44, 0x45, 0x10, 0x27, 0x12, 0x1b, 0x0a, 0x17, 0x52, 0x45, 0x53, 0x4f, - 0x55, 0x52, 0x43, 0x45, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x4b, 0x41, 0x52, 0x50, 0x45, 0x4e, - 0x54, 0x45, 0x52, 0x10, 0x28, 0x12, 0x19, 0x0a, 0x15, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, - 0x45, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x44, 0x41, 0x54, 0x41, 0x44, 0x4f, 0x47, 0x10, 0x29, - 0x12, 0x1f, 0x0a, 0x1b, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, 0x54, 0x59, 0x50, - 0x45, 0x5f, 0x41, 0x52, 0x47, 0x4f, 0x5f, 0x52, 0x4f, 0x4c, 0x4c, 0x4f, 0x55, 0x54, 0x53, 0x10, - 0x2a, 0x12, 0x16, 0x0a, 0x12, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, 0x54, 0x59, - 0x50, 0x45, 0x5f, 0x4b, 0x45, 0x44, 0x41, 0x10, 0x2b, 0x12, 0x24, 0x0a, 0x20, 0x52, 0x45, 0x53, - 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x4b, 0x45, 0x44, 0x41, 0x5f, - 0x53, 0x43, 0x41, 0x4c, 0x45, 0x44, 0x5f, 0x4f, 0x42, 0x4a, 0x45, 0x43, 0x54, 0x10, 0x2c, 0x12, - 0x21, 0x0a, 0x1d, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, 0x54, 0x59, 0x50, 0x45, - 0x5f, 0x4b, 0x45, 0x44, 0x41, 0x5f, 0x53, 0x43, 0x41, 0x4c, 0x45, 0x44, 0x5f, 0x4a, 0x4f, 0x42, - 0x10, 0x2d, 0x12, 0x1c, 0x0a, 0x18, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, 0x54, - 0x59, 0x50, 0x45, 0x5f, 0x43, 0x53, 0x49, 0x5f, 0x44, 0x52, 0x49, 0x56, 0x45, 0x52, 0x10, 0x2e, - 0x12, 0x26, 0x0a, 0x22, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, 0x54, 0x59, 0x50, - 0x45, 0x5f, 0x43, 0x53, 0x49, 0x5f, 0x53, 0x54, 0x4f, 0x52, 0x41, 0x47, 0x45, 0x5f, 0x43, 0x41, - 0x50, 0x41, 0x43, 0x49, 0x54, 0x59, 0x10, 0x2f, 0x12, 0x23, 0x0a, 0x1f, 0x52, 0x45, 0x53, 0x4f, - 0x55, 0x52, 0x43, 0x45, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x56, 0x4f, 0x4c, 0x55, 0x4d, 0x45, - 0x5f, 0x41, 0x54, 0x54, 0x41, 0x43, 0x48, 0x4d, 0x45, 0x4e, 0x54, 0x10, 0x30, 0x12, 0x23, 0x0a, - 0x1f, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x4b, - 0x55, 0x42, 0x45, 0x46, 0x4c, 0x4f, 0x57, 0x5f, 0x4e, 0x4f, 0x54, 0x45, 0x42, 0x4f, 0x4f, 0x4b, - 0x10, 0x31, 0x12, 0x1d, 0x0a, 0x19, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, 0x54, - 0x59, 0x50, 0x45, 0x5f, 0x56, 0x4f, 0x4c, 0x43, 0x41, 0x4e, 0x4f, 0x5f, 0x4a, 0x4f, 0x42, 0x10, - 0x32, 0x12, 0x23, 0x0a, 0x1f, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, 0x54, 0x59, - 0x50, 0x45, 0x5f, 0x53, 0x50, 0x41, 0x52, 0x4b, 0x5f, 0x41, 0x50, 0x50, 0x4c, 0x49, 0x43, 0x41, - 0x54, 0x49, 0x4f, 0x4e, 0x10, 0x33, 0x12, 0x2d, 0x0a, 0x29, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, - 0x43, 0x45, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x53, 0x43, 0x48, 0x45, 0x44, 0x55, 0x4c, 0x45, - 0x44, 0x5f, 0x53, 0x50, 0x41, 0x52, 0x4b, 0x5f, 0x41, 0x50, 0x50, 0x4c, 0x49, 0x43, 0x41, 0x54, - 0x49, 0x4f, 0x4e, 0x10, 0x34, 0x12, 0x22, 0x0a, 0x1e, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, - 0x45, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x43, 0x52, 0x4f, 0x4e, 0x5f, 0x56, 0x4f, 0x4c, 0x43, - 0x41, 0x4e, 0x4f, 0x5f, 0x4a, 0x4f, 0x42, 0x10, 0x35, 0x12, 0x1d, 0x0a, 0x19, 0x52, 0x45, 0x53, - 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x50, 0x56, 0x43, 0x5f, 0x4d, - 0x45, 0x54, 0x52, 0x49, 0x43, 0x53, 0x10, 0x36, 0x12, 0x29, 0x0a, 0x25, 0x52, 0x45, 0x53, 0x4f, - 0x55, 0x52, 0x43, 0x45, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x57, 0x4f, 0x52, 0x4b, 0x4c, 0x4f, - 0x41, 0x44, 0x5f, 0x52, 0x45, 0x43, 0x4f, 0x4d, 0x4d, 0x45, 0x4e, 0x44, 0x41, 0x54, 0x49, 0x4f, - 0x4e, 0x10, 0x37, 0x12, 0x1f, 0x0a, 0x1b, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, - 0x54, 0x59, 0x50, 0x45, 0x5f, 0x57, 0x4f, 0x52, 0x4b, 0x4c, 0x4f, 0x41, 0x44, 0x5f, 0x52, 0x55, - 0x4c, 0x45, 0x10, 0x38, 0x12, 0x23, 0x0a, 0x1f, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, + 0x53, 0x53, 0x10, 0x14, 0x12, 0x1f, 0x0a, 0x1b, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, + 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x49, 0x4e, 0x47, 0x52, 0x45, 0x53, 0x53, 0x5f, 0x43, 0x4c, + 0x41, 0x53, 0x53, 0x10, 0x15, 0x12, 0x20, 0x0a, 0x1c, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, + 0x45, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x4e, 0x45, 0x54, 0x57, 0x4f, 0x52, 0x4b, 0x5f, 0x50, + 0x4f, 0x4c, 0x49, 0x43, 0x59, 0x10, 0x16, 0x12, 0x16, 0x0a, 0x12, 0x52, 0x45, 0x53, 0x4f, 0x55, + 0x52, 0x43, 0x45, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x52, 0x4f, 0x4c, 0x45, 0x10, 0x17, 0x12, + 0x1e, 0x0a, 0x1a, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, 0x54, 0x59, 0x50, 0x45, + 0x5f, 0x52, 0x4f, 0x4c, 0x45, 0x5f, 0x42, 0x49, 0x4e, 0x44, 0x49, 0x4e, 0x47, 0x10, 0x18, 0x12, + 0x1e, 0x0a, 0x1a, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, 0x54, 0x59, 0x50, 0x45, + 0x5f, 0x43, 0x4c, 0x55, 0x53, 0x54, 0x45, 0x52, 0x5f, 0x52, 0x4f, 0x4c, 0x45, 0x10, 0x19, 0x12, + 0x26, 0x0a, 0x22, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, 0x54, 0x59, 0x50, 0x45, + 0x5f, 0x43, 0x4c, 0x55, 0x53, 0x54, 0x45, 0x52, 0x5f, 0x52, 0x4f, 0x4c, 0x45, 0x5f, 0x42, 0x49, + 0x4e, 0x44, 0x49, 0x4e, 0x47, 0x10, 0x1a, 0x12, 0x2b, 0x0a, 0x27, 0x52, 0x45, 0x53, 0x4f, 0x55, + 0x52, 0x43, 0x45, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x48, 0x4f, 0x52, 0x49, 0x5a, 0x4f, 0x4e, + 0x54, 0x41, 0x4c, 0x5f, 0x50, 0x4f, 0x44, 0x5f, 0x41, 0x55, 0x54, 0x4f, 0x53, 0x43, 0x41, 0x4c, + 0x45, 0x52, 0x10, 0x1b, 0x12, 0x29, 0x0a, 0x25, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, + 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x56, 0x45, 0x52, 0x54, 0x49, 0x43, 0x41, 0x4c, 0x5f, 0x50, + 0x4f, 0x44, 0x5f, 0x41, 0x55, 0x54, 0x4f, 0x53, 0x43, 0x41, 0x4c, 0x45, 0x52, 0x10, 0x1c, 0x12, + 0x27, 0x0a, 0x23, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, 0x54, 0x59, 0x50, 0x45, + 0x5f, 0x50, 0x4f, 0x44, 0x5f, 0x44, 0x49, 0x53, 0x52, 0x55, 0x50, 0x54, 0x49, 0x4f, 0x4e, 0x5f, + 0x42, 0x55, 0x44, 0x47, 0x45, 0x54, 0x10, 0x1d, 0x12, 0x25, 0x0a, 0x21, 0x52, 0x45, 0x53, 0x4f, + 0x55, 0x52, 0x43, 0x45, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x50, 0x4f, 0x44, 0x5f, 0x53, 0x45, + 0x43, 0x55, 0x52, 0x49, 0x54, 0x59, 0x5f, 0x50, 0x4f, 0x4c, 0x49, 0x43, 0x59, 0x10, 0x1e, 0x12, + 0x2c, 0x0a, 0x28, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, 0x54, 0x59, 0x50, 0x45, + 0x5f, 0x43, 0x55, 0x53, 0x54, 0x4f, 0x4d, 0x5f, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, + 0x5f, 0x44, 0x45, 0x46, 0x49, 0x4e, 0x49, 0x54, 0x49, 0x4f, 0x4e, 0x10, 0x1f, 0x12, 0x21, 0x0a, + 0x1d, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x43, + 0x55, 0x53, 0x54, 0x4f, 0x4d, 0x5f, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x10, 0x20, + 0x12, 0x1c, 0x0a, 0x18, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, 0x54, 0x59, 0x50, + 0x45, 0x5f, 0x43, 0x4f, 0x4e, 0x46, 0x49, 0x47, 0x5f, 0x4d, 0x41, 0x50, 0x10, 0x21, 0x12, 0x18, + 0x0a, 0x14, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, + 0x53, 0x45, 0x43, 0x52, 0x45, 0x54, 0x10, 0x22, 0x12, 0x1b, 0x0a, 0x17, 0x52, 0x45, 0x53, 0x4f, + 0x55, 0x52, 0x43, 0x45, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x43, 0x4f, 0x4e, 0x54, 0x41, 0x49, + 0x4e, 0x45, 0x52, 0x10, 0x23, 0x12, 0x1f, 0x0a, 0x1b, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, + 0x45, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x4e, 0x4f, 0x44, 0x45, 0x5f, 0x52, 0x45, 0x53, 0x4f, + 0x55, 0x52, 0x43, 0x45, 0x10, 0x24, 0x12, 0x24, 0x0a, 0x20, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, + 0x43, 0x45, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x43, 0x4f, 0x4e, 0x54, 0x41, 0x49, 0x4e, 0x45, + 0x52, 0x5f, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x10, 0x25, 0x12, 0x19, 0x0a, 0x15, + 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x43, 0x4c, + 0x55, 0x53, 0x54, 0x45, 0x52, 0x10, 0x26, 0x12, 0x1a, 0x0a, 0x16, 0x52, 0x45, 0x53, 0x4f, 0x55, + 0x52, 0x43, 0x45, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x43, 0x53, 0x49, 0x5f, 0x4e, 0x4f, 0x44, + 0x45, 0x10, 0x27, 0x12, 0x1b, 0x0a, 0x17, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, + 0x54, 0x59, 0x50, 0x45, 0x5f, 0x4b, 0x41, 0x52, 0x50, 0x45, 0x4e, 0x54, 0x45, 0x52, 0x10, 0x28, + 0x12, 0x19, 0x0a, 0x15, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, 0x54, 0x59, 0x50, + 0x45, 0x5f, 0x44, 0x41, 0x54, 0x41, 0x44, 0x4f, 0x47, 0x10, 0x29, 0x12, 0x1f, 0x0a, 0x1b, 0x52, + 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x41, 0x52, 0x47, + 0x4f, 0x5f, 0x52, 0x4f, 0x4c, 0x4c, 0x4f, 0x55, 0x54, 0x53, 0x10, 0x2a, 0x12, 0x16, 0x0a, 0x12, + 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x4b, 0x45, + 0x44, 0x41, 0x10, 0x2b, 0x12, 0x24, 0x0a, 0x20, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, + 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x4b, 0x45, 0x44, 0x41, 0x5f, 0x53, 0x43, 0x41, 0x4c, 0x45, + 0x44, 0x5f, 0x4f, 0x42, 0x4a, 0x45, 0x43, 0x54, 0x10, 0x2c, 0x12, 0x21, 0x0a, 0x1d, 0x52, 0x45, + 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x4b, 0x45, 0x44, 0x41, + 0x5f, 0x53, 0x43, 0x41, 0x4c, 0x45, 0x44, 0x5f, 0x4a, 0x4f, 0x42, 0x10, 0x2d, 0x12, 0x1c, 0x0a, + 0x18, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x43, + 0x53, 0x49, 0x5f, 0x44, 0x52, 0x49, 0x56, 0x45, 0x52, 0x10, 0x2e, 0x12, 0x26, 0x0a, 0x22, 0x52, + 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x43, 0x53, 0x49, + 0x5f, 0x53, 0x54, 0x4f, 0x52, 0x41, 0x47, 0x45, 0x5f, 0x43, 0x41, 0x50, 0x41, 0x43, 0x49, 0x54, + 0x59, 0x10, 0x2f, 0x12, 0x23, 0x0a, 0x1f, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, + 0x54, 0x59, 0x50, 0x45, 0x5f, 0x56, 0x4f, 0x4c, 0x55, 0x4d, 0x45, 0x5f, 0x41, 0x54, 0x54, 0x41, + 0x43, 0x48, 0x4d, 0x45, 0x4e, 0x54, 0x10, 0x30, 0x12, 0x23, 0x0a, 0x1f, 0x52, 0x45, 0x53, 0x4f, + 0x55, 0x52, 0x43, 0x45, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x4b, 0x55, 0x42, 0x45, 0x46, 0x4c, + 0x4f, 0x57, 0x5f, 0x4e, 0x4f, 0x54, 0x45, 0x42, 0x4f, 0x4f, 0x4b, 0x10, 0x31, 0x12, 0x1d, 0x0a, + 0x19, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x56, + 0x4f, 0x4c, 0x43, 0x41, 0x4e, 0x4f, 0x5f, 0x4a, 0x4f, 0x42, 0x10, 0x32, 0x12, 0x23, 0x0a, 0x1f, + 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x53, 0x50, + 0x41, 0x52, 0x4b, 0x5f, 0x41, 0x50, 0x50, 0x4c, 0x49, 0x43, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x10, + 0x33, 0x12, 0x2d, 0x0a, 0x29, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, 0x54, 0x59, + 0x50, 0x45, 0x5f, 0x53, 0x43, 0x48, 0x45, 0x44, 0x55, 0x4c, 0x45, 0x44, 0x5f, 0x53, 0x50, 0x41, + 0x52, 0x4b, 0x5f, 0x41, 0x50, 0x50, 0x4c, 0x49, 0x43, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x10, 0x34, + 0x12, 0x22, 0x0a, 0x1e, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, 0x54, 0x59, 0x50, + 0x45, 0x5f, 0x43, 0x52, 0x4f, 0x4e, 0x5f, 0x56, 0x4f, 0x4c, 0x43, 0x41, 0x4e, 0x4f, 0x5f, 0x4a, + 0x4f, 0x42, 0x10, 0x35, 0x12, 0x1d, 0x0a, 0x19, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, + 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x50, 0x56, 0x43, 0x5f, 0x4d, 0x45, 0x54, 0x52, 0x49, 0x43, + 0x53, 0x10, 0x36, 0x12, 0x29, 0x0a, 0x25, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, + 0x54, 0x59, 0x50, 0x45, 0x5f, 0x57, 0x4f, 0x52, 0x4b, 0x4c, 0x4f, 0x41, 0x44, 0x5f, 0x52, 0x45, + 0x43, 0x4f, 0x4d, 0x4d, 0x45, 0x4e, 0x44, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x10, 0x37, 0x12, 0x1f, + 0x0a, 0x1b, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, + 0x57, 0x4f, 0x52, 0x4b, 0x4c, 0x4f, 0x41, 0x44, 0x5f, 0x52, 0x55, 0x4c, 0x45, 0x10, 0x38, 0x12, + 0x23, 0x0a, 0x1f, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, 0x54, 0x59, 0x50, 0x45, + 0x5f, 0x57, 0x4f, 0x52, 0x4b, 0x4c, 0x4f, 0x41, 0x44, 0x5f, 0x52, 0x55, 0x4c, 0x45, 0x5f, 0x4f, + 0x4f, 0x4d, 0x10, 0x39, 0x12, 0x2c, 0x0a, 0x28, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x57, 0x4f, 0x52, 0x4b, 0x4c, 0x4f, 0x41, 0x44, 0x5f, 0x52, - 0x55, 0x4c, 0x45, 0x5f, 0x4f, 0x4f, 0x4d, 0x10, 0x39, 0x12, 0x2c, 0x0a, 0x28, 0x52, 0x45, 0x53, - 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x57, 0x4f, 0x52, 0x4b, 0x4c, - 0x4f, 0x41, 0x44, 0x5f, 0x52, 0x55, 0x4c, 0x45, 0x5f, 0x43, 0x50, 0x55, 0x5f, 0x54, 0x48, 0x52, - 0x4f, 0x54, 0x54, 0x4c, 0x45, 0x10, 0x3a, 0x12, 0x1e, 0x0a, 0x1a, 0x52, 0x45, 0x53, 0x4f, 0x55, - 0x52, 0x43, 0x45, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x43, 0x4e, 0x50, 0x47, 0x5f, 0x43, 0x4c, - 0x55, 0x53, 0x54, 0x45, 0x52, 0x10, 0x3b, 0x12, 0x25, 0x0a, 0x21, 0x52, 0x45, 0x53, 0x4f, 0x55, - 0x52, 0x43, 0x45, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x43, 0x4f, 0x4e, 0x54, 0x41, 0x49, 0x4e, - 0x45, 0x52, 0x5f, 0x4f, 0x4f, 0x4d, 0x5f, 0x45, 0x56, 0x45, 0x4e, 0x54, 0x10, 0x3c, 0x12, 0x2b, - 0x0a, 0x27, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, - 0x43, 0x4f, 0x4e, 0x54, 0x41, 0x49, 0x4e, 0x45, 0x52, 0x5f, 0x43, 0x52, 0x41, 0x53, 0x48, 0x4c, - 0x4f, 0x4f, 0x50, 0x5f, 0x45, 0x56, 0x45, 0x4e, 0x54, 0x10, 0x3d, 0x12, 0x2d, 0x0a, 0x29, 0x52, - 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x43, 0x4f, 0x4e, - 0x54, 0x41, 0x49, 0x4e, 0x45, 0x52, 0x5f, 0x53, 0x54, 0x41, 0x52, 0x54, 0x55, 0x50, 0x5f, 0x4c, - 0x49, 0x46, 0x45, 0x43, 0x59, 0x43, 0x4c, 0x45, 0x10, 0x3e, 0x12, 0x2e, 0x0a, 0x2a, 0x52, 0x45, - 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x43, 0x4f, 0x4e, 0x54, - 0x41, 0x49, 0x4e, 0x45, 0x52, 0x5f, 0x43, 0x50, 0x55, 0x5f, 0x54, 0x48, 0x52, 0x4f, 0x54, 0x54, - 0x4c, 0x45, 0x5f, 0x45, 0x56, 0x45, 0x4e, 0x54, 0x10, 0x3f, 0x12, 0x22, 0x0a, 0x1e, 0x52, 0x45, - 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x43, 0x4c, 0x55, 0x53, - 0x54, 0x45, 0x52, 0x5f, 0x53, 0x4e, 0x41, 0x50, 0x53, 0x48, 0x4f, 0x54, 0x10, 0x4d, 0x12, 0x24, - 0x0a, 0x20, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, - 0x4b, 0x41, 0x52, 0x50, 0x45, 0x4e, 0x54, 0x45, 0x52, 0x5f, 0x53, 0x45, 0x54, 0x54, 0x49, 0x4e, - 0x47, 0x53, 0x10, 0x4e, 0x12, 0x2b, 0x0a, 0x27, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, - 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x4e, 0x4f, 0x44, 0x45, 0x5f, 0x4c, 0x49, 0x46, 0x45, 0x43, - 0x59, 0x43, 0x4c, 0x45, 0x5f, 0x54, 0x52, 0x41, 0x4e, 0x53, 0x49, 0x54, 0x49, 0x4f, 0x4e, 0x10, - 0x4f, 0x12, 0x29, 0x0a, 0x25, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, 0x54, 0x59, - 0x50, 0x45, 0x5f, 0x50, 0x4f, 0x44, 0x5f, 0x55, 0x4e, 0x53, 0x43, 0x48, 0x45, 0x44, 0x55, 0x4c, - 0x41, 0x42, 0x4c, 0x45, 0x5f, 0x45, 0x56, 0x45, 0x4e, 0x54, 0x10, 0x50, 0x12, 0x20, 0x0a, 0x1c, - 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x4b, 0x59, - 0x56, 0x45, 0x52, 0x4e, 0x4f, 0x5f, 0x50, 0x4f, 0x4c, 0x49, 0x43, 0x59, 0x10, 0x51, 0x12, 0x27, - 0x0a, 0x23, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, - 0x4b, 0x59, 0x56, 0x45, 0x52, 0x4e, 0x4f, 0x5f, 0x50, 0x4f, 0x4c, 0x49, 0x43, 0x59, 0x5f, 0x52, - 0x45, 0x50, 0x4f, 0x52, 0x54, 0x10, 0x52, 0x12, 0x30, 0x0a, 0x2c, 0x52, 0x45, 0x53, 0x4f, 0x55, - 0x52, 0x43, 0x45, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x47, 0x41, 0x54, 0x45, 0x4b, 0x45, 0x45, - 0x50, 0x45, 0x52, 0x5f, 0x43, 0x4f, 0x4e, 0x53, 0x54, 0x52, 0x41, 0x49, 0x4e, 0x54, 0x5f, 0x54, - 0x45, 0x4d, 0x50, 0x4c, 0x41, 0x54, 0x45, 0x10, 0x53, 0x12, 0x27, 0x0a, 0x23, 0x52, 0x45, 0x53, - 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x47, 0x41, 0x54, 0x45, 0x4b, - 0x45, 0x45, 0x50, 0x45, 0x52, 0x5f, 0x43, 0x4f, 0x4e, 0x53, 0x54, 0x52, 0x41, 0x49, 0x4e, 0x54, - 0x10, 0x54, 0x12, 0x23, 0x0a, 0x1f, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, 0x54, - 0x59, 0x50, 0x45, 0x5f, 0x4d, 0x49, 0x47, 0x5f, 0x50, 0x41, 0x52, 0x54, 0x45, 0x44, 0x5f, 0x43, - 0x4f, 0x4e, 0x46, 0x49, 0x47, 0x10, 0x55, 0x2a, 0x8c, 0x01, 0x0a, 0x08, 0x4c, 0x6f, 0x67, 0x4c, - 0x65, 0x76, 0x65, 0x6c, 0x12, 0x19, 0x0a, 0x15, 0x4c, 0x4f, 0x47, 0x5f, 0x4c, 0x45, 0x56, 0x45, - 0x4c, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, - 0x13, 0x0a, 0x0f, 0x4c, 0x4f, 0x47, 0x5f, 0x4c, 0x45, 0x56, 0x45, 0x4c, 0x5f, 0x44, 0x45, 0x42, - 0x55, 0x47, 0x10, 0x01, 0x12, 0x12, 0x0a, 0x0e, 0x4c, 0x4f, 0x47, 0x5f, 0x4c, 0x45, 0x56, 0x45, - 0x4c, 0x5f, 0x49, 0x4e, 0x46, 0x4f, 0x10, 0x02, 0x12, 0x12, 0x0a, 0x0e, 0x4c, 0x4f, 0x47, 0x5f, - 0x4c, 0x45, 0x56, 0x45, 0x4c, 0x5f, 0x57, 0x41, 0x52, 0x4e, 0x10, 0x03, 0x12, 0x13, 0x0a, 0x0f, - 0x4c, 0x4f, 0x47, 0x5f, 0x4c, 0x45, 0x56, 0x45, 0x4c, 0x5f, 0x45, 0x52, 0x52, 0x4f, 0x52, 0x10, - 0x04, 0x12, 0x13, 0x0a, 0x0f, 0x4c, 0x4f, 0x47, 0x5f, 0x4c, 0x45, 0x56, 0x45, 0x4c, 0x5f, 0x46, - 0x41, 0x54, 0x41, 0x4c, 0x10, 0x05, 0x32, 0x8a, 0x06, 0x0a, 0x17, 0x4d, 0x65, 0x74, 0x72, 0x69, - 0x63, 0x73, 0x43, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x53, 0x65, 0x72, 0x76, 0x69, - 0x63, 0x65, 0x12, 0x49, 0x0a, 0x0c, 0x53, 0x65, 0x6e, 0x64, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, - 0x63, 0x65, 0x12, 0x1b, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x6e, 0x64, - 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, - 0x1c, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x52, 0x65, 0x73, - 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x58, 0x0a, - 0x11, 0x53, 0x65, 0x6e, 0x64, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x42, 0x61, 0x74, - 0x63, 0x68, 0x12, 0x20, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x6e, 0x64, - 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x42, 0x61, 0x74, 0x63, 0x68, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, - 0x6e, 0x64, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x42, 0x61, 0x74, 0x63, 0x68, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x61, 0x0a, 0x14, 0x53, 0x65, 0x6e, 0x64, 0x54, - 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x74, 0x72, 0x79, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x12, - 0x23, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x54, 0x65, 0x6c, - 0x65, 0x6d, 0x65, 0x74, 0x72, 0x79, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x24, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, - 0x6e, 0x64, 0x54, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x74, 0x72, 0x79, 0x4d, 0x65, 0x74, 0x72, 0x69, - 0x63, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x66, 0x0a, 0x19, 0x53, 0x65, - 0x6e, 0x64, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x53, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, - 0x74, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x12, 0x1c, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, - 0x2e, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x53, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, - 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x1a, 0x29, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x53, - 0x65, 0x6e, 0x64, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x53, 0x6e, 0x61, 0x70, 0x73, 0x68, - 0x6f, 0x74, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x28, 0x01, 0x12, 0x68, 0x0a, 0x1a, 0x53, 0x65, 0x6e, 0x64, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, - 0x72, 0x53, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x42, 0x61, 0x74, 0x63, 0x68, 0x65, 0x64, + 0x55, 0x4c, 0x45, 0x5f, 0x43, 0x50, 0x55, 0x5f, 0x54, 0x48, 0x52, 0x4f, 0x54, 0x54, 0x4c, 0x45, + 0x10, 0x3a, 0x12, 0x1e, 0x0a, 0x1a, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, 0x54, + 0x59, 0x50, 0x45, 0x5f, 0x43, 0x4e, 0x50, 0x47, 0x5f, 0x43, 0x4c, 0x55, 0x53, 0x54, 0x45, 0x52, + 0x10, 0x3b, 0x12, 0x25, 0x0a, 0x21, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, 0x54, + 0x59, 0x50, 0x45, 0x5f, 0x43, 0x4f, 0x4e, 0x54, 0x41, 0x49, 0x4e, 0x45, 0x52, 0x5f, 0x4f, 0x4f, + 0x4d, 0x5f, 0x45, 0x56, 0x45, 0x4e, 0x54, 0x10, 0x3c, 0x12, 0x2b, 0x0a, 0x27, 0x52, 0x45, 0x53, + 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x43, 0x4f, 0x4e, 0x54, 0x41, + 0x49, 0x4e, 0x45, 0x52, 0x5f, 0x43, 0x52, 0x41, 0x53, 0x48, 0x4c, 0x4f, 0x4f, 0x50, 0x5f, 0x45, + 0x56, 0x45, 0x4e, 0x54, 0x10, 0x3d, 0x12, 0x2d, 0x0a, 0x29, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, + 0x43, 0x45, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x43, 0x4f, 0x4e, 0x54, 0x41, 0x49, 0x4e, 0x45, + 0x52, 0x5f, 0x53, 0x54, 0x41, 0x52, 0x54, 0x55, 0x50, 0x5f, 0x4c, 0x49, 0x46, 0x45, 0x43, 0x59, + 0x43, 0x4c, 0x45, 0x10, 0x3e, 0x12, 0x2e, 0x0a, 0x2a, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, + 0x45, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x43, 0x4f, 0x4e, 0x54, 0x41, 0x49, 0x4e, 0x45, 0x52, + 0x5f, 0x43, 0x50, 0x55, 0x5f, 0x54, 0x48, 0x52, 0x4f, 0x54, 0x54, 0x4c, 0x45, 0x5f, 0x45, 0x56, + 0x45, 0x4e, 0x54, 0x10, 0x3f, 0x12, 0x22, 0x0a, 0x1e, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, + 0x45, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x43, 0x4c, 0x55, 0x53, 0x54, 0x45, 0x52, 0x5f, 0x53, + 0x4e, 0x41, 0x50, 0x53, 0x48, 0x4f, 0x54, 0x10, 0x4d, 0x12, 0x24, 0x0a, 0x20, 0x52, 0x45, 0x53, + 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x4b, 0x41, 0x52, 0x50, 0x45, + 0x4e, 0x54, 0x45, 0x52, 0x5f, 0x53, 0x45, 0x54, 0x54, 0x49, 0x4e, 0x47, 0x53, 0x10, 0x4e, 0x12, + 0x2b, 0x0a, 0x27, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, 0x54, 0x59, 0x50, 0x45, + 0x5f, 0x4e, 0x4f, 0x44, 0x45, 0x5f, 0x4c, 0x49, 0x46, 0x45, 0x43, 0x59, 0x43, 0x4c, 0x45, 0x5f, + 0x54, 0x52, 0x41, 0x4e, 0x53, 0x49, 0x54, 0x49, 0x4f, 0x4e, 0x10, 0x4f, 0x12, 0x29, 0x0a, 0x25, + 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x50, 0x4f, + 0x44, 0x5f, 0x55, 0x4e, 0x53, 0x43, 0x48, 0x45, 0x44, 0x55, 0x4c, 0x41, 0x42, 0x4c, 0x45, 0x5f, + 0x45, 0x56, 0x45, 0x4e, 0x54, 0x10, 0x50, 0x12, 0x20, 0x0a, 0x1c, 0x52, 0x45, 0x53, 0x4f, 0x55, + 0x52, 0x43, 0x45, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x4b, 0x59, 0x56, 0x45, 0x52, 0x4e, 0x4f, + 0x5f, 0x50, 0x4f, 0x4c, 0x49, 0x43, 0x59, 0x10, 0x51, 0x12, 0x27, 0x0a, 0x23, 0x52, 0x45, 0x53, + 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x4b, 0x59, 0x56, 0x45, 0x52, + 0x4e, 0x4f, 0x5f, 0x50, 0x4f, 0x4c, 0x49, 0x43, 0x59, 0x5f, 0x52, 0x45, 0x50, 0x4f, 0x52, 0x54, + 0x10, 0x52, 0x12, 0x30, 0x0a, 0x2c, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, 0x54, + 0x59, 0x50, 0x45, 0x5f, 0x47, 0x41, 0x54, 0x45, 0x4b, 0x45, 0x45, 0x50, 0x45, 0x52, 0x5f, 0x43, + 0x4f, 0x4e, 0x53, 0x54, 0x52, 0x41, 0x49, 0x4e, 0x54, 0x5f, 0x54, 0x45, 0x4d, 0x50, 0x4c, 0x41, + 0x54, 0x45, 0x10, 0x53, 0x12, 0x27, 0x0a, 0x23, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, + 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x47, 0x41, 0x54, 0x45, 0x4b, 0x45, 0x45, 0x50, 0x45, 0x52, + 0x5f, 0x43, 0x4f, 0x4e, 0x53, 0x54, 0x52, 0x41, 0x49, 0x4e, 0x54, 0x10, 0x54, 0x12, 0x23, 0x0a, + 0x1f, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x4d, + 0x49, 0x47, 0x5f, 0x50, 0x41, 0x52, 0x54, 0x45, 0x44, 0x5f, 0x43, 0x4f, 0x4e, 0x46, 0x49, 0x47, + 0x10, 0x55, 0x12, 0x2a, 0x0a, 0x26, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, 0x54, + 0x59, 0x50, 0x45, 0x5f, 0x47, 0x50, 0x55, 0x5f, 0x4d, 0x49, 0x47, 0x5f, 0x49, 0x4e, 0x53, 0x54, + 0x41, 0x4e, 0x43, 0x45, 0x5f, 0x4d, 0x45, 0x54, 0x52, 0x49, 0x43, 0x53, 0x10, 0x56, 0x12, 0x2c, + 0x0a, 0x28, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, + 0x53, 0x43, 0x48, 0x45, 0x44, 0x55, 0x4c, 0x45, 0x52, 0x5f, 0x50, 0x4c, 0x41, 0x43, 0x45, 0x4d, + 0x45, 0x4e, 0x54, 0x5f, 0x4d, 0x45, 0x54, 0x52, 0x49, 0x43, 0x10, 0x57, 0x12, 0x22, 0x0a, 0x1e, + 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x41, 0x52, + 0x47, 0x4f, 0x5f, 0x41, 0x50, 0x50, 0x4c, 0x49, 0x43, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x10, 0x58, + 0x12, 0x26, 0x0a, 0x22, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, 0x54, 0x59, 0x50, + 0x45, 0x5f, 0x41, 0x52, 0x47, 0x4f, 0x5f, 0x41, 0x50, 0x50, 0x4c, 0x49, 0x43, 0x41, 0x54, 0x49, + 0x4f, 0x4e, 0x5f, 0x53, 0x45, 0x54, 0x10, 0x59, 0x12, 0x2b, 0x0a, 0x27, 0x52, 0x45, 0x53, 0x4f, + 0x55, 0x52, 0x43, 0x45, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x41, 0x52, 0x47, 0x4f, 0x5f, 0x41, + 0x50, 0x50, 0x4c, 0x49, 0x43, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x52, 0x45, 0x53, 0x4f, 0x55, + 0x52, 0x43, 0x45, 0x10, 0x5a, 0x12, 0x25, 0x0a, 0x21, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, + 0x45, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x46, 0x4c, 0x55, 0x58, 0x5f, 0x47, 0x49, 0x54, 0x5f, + 0x52, 0x45, 0x50, 0x4f, 0x53, 0x49, 0x54, 0x4f, 0x52, 0x59, 0x10, 0x5b, 0x12, 0x25, 0x0a, 0x21, + 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x46, 0x4c, + 0x55, 0x58, 0x5f, 0x4f, 0x43, 0x49, 0x5f, 0x52, 0x45, 0x50, 0x4f, 0x53, 0x49, 0x54, 0x4f, 0x52, + 0x59, 0x10, 0x5c, 0x12, 0x26, 0x0a, 0x22, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, + 0x54, 0x59, 0x50, 0x45, 0x5f, 0x46, 0x4c, 0x55, 0x58, 0x5f, 0x48, 0x45, 0x4c, 0x4d, 0x5f, 0x52, + 0x45, 0x50, 0x4f, 0x53, 0x49, 0x54, 0x4f, 0x52, 0x59, 0x10, 0x5d, 0x12, 0x21, 0x0a, 0x1d, 0x52, + 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x46, 0x4c, 0x55, + 0x58, 0x5f, 0x48, 0x45, 0x4c, 0x4d, 0x5f, 0x43, 0x48, 0x41, 0x52, 0x54, 0x10, 0x5e, 0x12, 0x1d, + 0x0a, 0x19, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, + 0x46, 0x4c, 0x55, 0x58, 0x5f, 0x42, 0x55, 0x43, 0x4b, 0x45, 0x54, 0x10, 0x5f, 0x12, 0x24, 0x0a, + 0x20, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x46, + 0x4c, 0x55, 0x58, 0x5f, 0x4b, 0x55, 0x53, 0x54, 0x4f, 0x4d, 0x49, 0x5a, 0x41, 0x54, 0x49, 0x4f, + 0x4e, 0x10, 0x60, 0x12, 0x23, 0x0a, 0x1f, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, + 0x54, 0x59, 0x50, 0x45, 0x5f, 0x46, 0x4c, 0x55, 0x58, 0x5f, 0x48, 0x45, 0x4c, 0x4d, 0x5f, 0x52, + 0x45, 0x4c, 0x45, 0x41, 0x53, 0x45, 0x10, 0x61, 0x12, 0x28, 0x0a, 0x24, 0x52, 0x45, 0x53, 0x4f, + 0x55, 0x52, 0x43, 0x45, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x46, 0x4c, 0x55, 0x58, 0x5f, 0x45, + 0x58, 0x54, 0x45, 0x52, 0x4e, 0x41, 0x4c, 0x5f, 0x41, 0x52, 0x54, 0x49, 0x46, 0x41, 0x43, 0x54, + 0x10, 0x62, 0x12, 0x1c, 0x0a, 0x18, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, 0x54, + 0x59, 0x50, 0x45, 0x5f, 0x46, 0x4c, 0x55, 0x58, 0x5f, 0x41, 0x4c, 0x45, 0x52, 0x54, 0x10, 0x63, + 0x12, 0x1f, 0x0a, 0x1b, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, 0x54, 0x59, 0x50, + 0x45, 0x5f, 0x46, 0x4c, 0x55, 0x58, 0x5f, 0x50, 0x52, 0x4f, 0x56, 0x49, 0x44, 0x45, 0x52, 0x10, + 0x64, 0x12, 0x1f, 0x0a, 0x1b, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, 0x54, 0x59, + 0x50, 0x45, 0x5f, 0x46, 0x4c, 0x55, 0x58, 0x5f, 0x52, 0x45, 0x43, 0x45, 0x49, 0x56, 0x45, 0x52, + 0x10, 0x65, 0x12, 0x1f, 0x0a, 0x1b, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, 0x54, + 0x59, 0x50, 0x45, 0x5f, 0x46, 0x4c, 0x55, 0x58, 0x5f, 0x49, 0x4e, 0x53, 0x54, 0x41, 0x4e, 0x43, + 0x45, 0x10, 0x66, 0x12, 0x1d, 0x0a, 0x19, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, + 0x54, 0x59, 0x50, 0x45, 0x5f, 0x46, 0x4c, 0x55, 0x58, 0x5f, 0x52, 0x45, 0x50, 0x4f, 0x52, 0x54, + 0x10, 0x67, 0x12, 0x32, 0x0a, 0x2e, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, 0x54, + 0x59, 0x50, 0x45, 0x5f, 0x46, 0x4c, 0x55, 0x58, 0x5f, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, + 0x45, 0x5f, 0x53, 0x45, 0x54, 0x5f, 0x49, 0x4e, 0x50, 0x55, 0x54, 0x5f, 0x50, 0x52, 0x4f, 0x56, + 0x49, 0x44, 0x45, 0x52, 0x10, 0x68, 0x12, 0x23, 0x0a, 0x1f, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, + 0x43, 0x45, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x46, 0x4c, 0x55, 0x58, 0x5f, 0x52, 0x45, 0x53, + 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, 0x53, 0x45, 0x54, 0x10, 0x69, 0x12, 0x27, 0x0a, 0x23, 0x52, + 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x46, 0x4c, 0x55, + 0x58, 0x5f, 0x49, 0x4d, 0x41, 0x47, 0x45, 0x5f, 0x52, 0x45, 0x50, 0x4f, 0x53, 0x49, 0x54, 0x4f, + 0x52, 0x59, 0x10, 0x6a, 0x12, 0x23, 0x0a, 0x1f, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, + 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x46, 0x4c, 0x55, 0x58, 0x5f, 0x49, 0x4d, 0x41, 0x47, 0x45, + 0x5f, 0x50, 0x4f, 0x4c, 0x49, 0x43, 0x59, 0x10, 0x6b, 0x12, 0x2e, 0x0a, 0x2a, 0x52, 0x45, 0x53, + 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x46, 0x4c, 0x55, 0x58, 0x5f, + 0x49, 0x4d, 0x41, 0x47, 0x45, 0x5f, 0x55, 0x50, 0x44, 0x41, 0x54, 0x45, 0x5f, 0x41, 0x55, 0x54, + 0x4f, 0x4d, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x10, 0x6c, 0x12, 0x20, 0x0a, 0x1c, 0x52, 0x45, 0x53, + 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x46, 0x4c, 0x41, 0x47, 0x47, + 0x45, 0x52, 0x5f, 0x43, 0x41, 0x4e, 0x41, 0x52, 0x59, 0x10, 0x6d, 0x12, 0x29, 0x0a, 0x25, 0x52, + 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x46, 0x4c, 0x41, + 0x47, 0x47, 0x45, 0x52, 0x5f, 0x4d, 0x45, 0x54, 0x52, 0x49, 0x43, 0x5f, 0x54, 0x45, 0x4d, 0x50, + 0x4c, 0x41, 0x54, 0x45, 0x10, 0x6e, 0x12, 0x28, 0x0a, 0x24, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, + 0x43, 0x45, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x46, 0x4c, 0x41, 0x47, 0x47, 0x45, 0x52, 0x5f, + 0x41, 0x4c, 0x45, 0x52, 0x54, 0x5f, 0x50, 0x52, 0x4f, 0x56, 0x49, 0x44, 0x45, 0x52, 0x10, 0x6f, + 0x2a, 0x8c, 0x01, 0x0a, 0x08, 0x4c, 0x6f, 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x12, 0x19, 0x0a, + 0x15, 0x4c, 0x4f, 0x47, 0x5f, 0x4c, 0x45, 0x56, 0x45, 0x4c, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, + 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x13, 0x0a, 0x0f, 0x4c, 0x4f, 0x47, 0x5f, + 0x4c, 0x45, 0x56, 0x45, 0x4c, 0x5f, 0x44, 0x45, 0x42, 0x55, 0x47, 0x10, 0x01, 0x12, 0x12, 0x0a, + 0x0e, 0x4c, 0x4f, 0x47, 0x5f, 0x4c, 0x45, 0x56, 0x45, 0x4c, 0x5f, 0x49, 0x4e, 0x46, 0x4f, 0x10, + 0x02, 0x12, 0x12, 0x0a, 0x0e, 0x4c, 0x4f, 0x47, 0x5f, 0x4c, 0x45, 0x56, 0x45, 0x4c, 0x5f, 0x57, + 0x41, 0x52, 0x4e, 0x10, 0x03, 0x12, 0x13, 0x0a, 0x0f, 0x4c, 0x4f, 0x47, 0x5f, 0x4c, 0x45, 0x56, + 0x45, 0x4c, 0x5f, 0x45, 0x52, 0x52, 0x4f, 0x52, 0x10, 0x04, 0x12, 0x13, 0x0a, 0x0f, 0x4c, 0x4f, + 0x47, 0x5f, 0x4c, 0x45, 0x56, 0x45, 0x4c, 0x5f, 0x46, 0x41, 0x54, 0x41, 0x4c, 0x10, 0x05, 0x32, + 0xd0, 0x07, 0x0a, 0x17, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x43, 0x6f, 0x6c, 0x6c, 0x65, + 0x63, 0x74, 0x6f, 0x72, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x49, 0x0a, 0x0c, 0x53, + 0x65, 0x6e, 0x64, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x1b, 0x2e, 0x61, 0x70, + 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, + 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, + 0x31, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x58, 0x0a, 0x11, 0x53, 0x65, 0x6e, 0x64, 0x52, 0x65, + 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x42, 0x61, 0x74, 0x63, 0x68, 0x12, 0x20, 0x2e, 0x61, 0x70, + 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, + 0x65, 0x42, 0x61, 0x74, 0x63, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, + 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x52, 0x65, 0x73, 0x6f, 0x75, + 0x72, 0x63, 0x65, 0x42, 0x61, 0x74, 0x63, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x12, 0x61, 0x0a, 0x14, 0x53, 0x65, 0x6e, 0x64, 0x54, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x74, 0x72, + 0x79, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x12, 0x23, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, + 0x31, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x54, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x74, 0x72, 0x79, 0x4d, + 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x24, 0x2e, + 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x54, 0x65, 0x6c, 0x65, 0x6d, + 0x65, 0x74, 0x72, 0x79, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x12, 0x66, 0x0a, 0x19, 0x53, 0x65, 0x6e, 0x64, 0x43, 0x6c, 0x75, 0x73, 0x74, + 0x65, 0x72, 0x53, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x12, 0x1c, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, - 0x72, 0x53, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x42, 0x61, 0x74, 0x63, 0x68, 0x1a, 0x2a, + 0x72, 0x53, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x1a, 0x29, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x43, 0x6c, 0x75, 0x73, - 0x74, 0x65, 0x72, 0x53, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x42, 0x61, 0x74, 0x63, 0x68, - 0x65, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x28, 0x01, 0x12, 0x58, 0x0a, 0x11, - 0x53, 0x65, 0x6e, 0x64, 0x54, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x74, 0x72, 0x79, 0x4c, 0x6f, 0x67, - 0x73, 0x12, 0x20, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x54, - 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x74, 0x72, 0x79, 0x4c, 0x6f, 0x67, 0x73, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x6e, - 0x64, 0x54, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x74, 0x72, 0x79, 0x4c, 0x6f, 0x67, 0x73, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x70, 0x0a, 0x19, 0x53, 0x65, 0x6e, 0x64, 0x4e, 0x65, - 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x54, 0x72, 0x61, 0x66, 0x66, 0x69, 0x63, 0x4d, 0x65, 0x74, 0x72, - 0x69, 0x63, 0x73, 0x12, 0x28, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x6e, - 0x64, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x54, 0x72, 0x61, 0x66, 0x66, 0x69, 0x63, 0x4d, - 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x29, 0x2e, - 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x4e, 0x65, 0x74, 0x77, 0x6f, - 0x72, 0x6b, 0x54, 0x72, 0x61, 0x66, 0x66, 0x69, 0x63, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x49, 0x0a, 0x0c, 0x4e, 0x6f, 0x64, 0x65, - 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x1b, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, - 0x31, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4e, - 0x6f, 0x64, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x42, 0x8e, 0x01, 0x0a, 0x0a, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x69, 0x2e, - 0x76, 0x31, 0x42, 0x15, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x43, 0x6f, 0x6c, 0x6c, 0x65, - 0x63, 0x74, 0x6f, 0x72, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x30, 0x67, 0x69, 0x74, - 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x64, 0x65, 0x76, 0x7a, 0x65, 0x72, 0x6f, 0x2d, - 0x69, 0x6e, 0x63, 0x2f, 0x7a, 0x78, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x72, 0x2f, 0x67, 0x65, 0x6e, - 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x3b, 0x61, 0x70, 0x69, 0x76, 0x31, 0xa2, 0x02, 0x03, - 0x41, 0x58, 0x58, 0xaa, 0x02, 0x06, 0x41, 0x70, 0x69, 0x2e, 0x56, 0x31, 0xca, 0x02, 0x06, 0x41, - 0x70, 0x69, 0x5c, 0x56, 0x31, 0xe2, 0x02, 0x12, 0x41, 0x70, 0x69, 0x5c, 0x56, 0x31, 0x5c, 0x47, - 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x07, 0x41, 0x70, 0x69, - 0x3a, 0x3a, 0x56, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x74, 0x65, 0x72, 0x53, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x53, 0x74, 0x72, 0x65, 0x61, + 0x6d, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x28, 0x01, 0x12, 0x68, 0x0a, 0x1a, 0x53, + 0x65, 0x6e, 0x64, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x53, 0x6e, 0x61, 0x70, 0x73, 0x68, + 0x6f, 0x74, 0x42, 0x61, 0x74, 0x63, 0x68, 0x65, 0x64, 0x12, 0x1c, 0x2e, 0x61, 0x70, 0x69, 0x2e, + 0x76, 0x31, 0x2e, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x53, 0x6e, 0x61, 0x70, 0x73, 0x68, + 0x6f, 0x74, 0x42, 0x61, 0x74, 0x63, 0x68, 0x1a, 0x2a, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, + 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x53, 0x6e, 0x61, 0x70, + 0x73, 0x68, 0x6f, 0x74, 0x42, 0x61, 0x74, 0x63, 0x68, 0x65, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x28, 0x01, 0x12, 0x58, 0x0a, 0x11, 0x53, 0x65, 0x6e, 0x64, 0x54, 0x65, 0x6c, + 0x65, 0x6d, 0x65, 0x74, 0x72, 0x79, 0x4c, 0x6f, 0x67, 0x73, 0x12, 0x20, 0x2e, 0x61, 0x70, 0x69, + 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x54, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x74, 0x72, + 0x79, 0x4c, 0x6f, 0x67, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x61, + 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x54, 0x65, 0x6c, 0x65, 0x6d, 0x65, + 0x74, 0x72, 0x79, 0x4c, 0x6f, 0x67, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, + 0x70, 0x0a, 0x19, 0x53, 0x65, 0x6e, 0x64, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x54, 0x72, + 0x61, 0x66, 0x66, 0x69, 0x63, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x12, 0x28, 0x2e, 0x61, + 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, + 0x6b, 0x54, 0x72, 0x61, 0x66, 0x66, 0x69, 0x63, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x29, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, + 0x53, 0x65, 0x6e, 0x64, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x54, 0x72, 0x61, 0x66, 0x66, + 0x69, 0x63, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x12, 0x58, 0x0a, 0x11, 0x53, 0x65, 0x6e, 0x64, 0x53, 0x63, 0x61, 0x6c, 0x65, 0x72, 0x4d, + 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x12, 0x20, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, + 0x53, 0x65, 0x6e, 0x64, 0x53, 0x63, 0x61, 0x6c, 0x65, 0x72, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, + 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, + 0x31, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x53, 0x63, 0x61, 0x6c, 0x65, 0x72, 0x4d, 0x65, 0x74, 0x72, + 0x69, 0x63, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x6a, 0x0a, 0x17, 0x53, + 0x65, 0x6e, 0x64, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x52, 0x65, 0x64, 0x4d, + 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x12, 0x26, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, + 0x53, 0x65, 0x6e, 0x64, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x52, 0x65, 0x64, + 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x27, + 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x43, 0x6f, 0x6e, 0x74, + 0x61, 0x69, 0x6e, 0x65, 0x72, 0x52, 0x65, 0x64, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x49, 0x0a, 0x0c, 0x4e, 0x6f, 0x64, 0x65, 0x4d, + 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x1b, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, + 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4e, 0x6f, + 0x64, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x42, 0x93, 0x01, 0x0a, 0x0a, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, + 0x31, 0x42, 0x15, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x43, 0x6f, 0x6c, 0x6c, 0x65, 0x63, + 0x74, 0x6f, 0x72, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x35, 0x67, 0x69, 0x74, 0x68, + 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x64, 0x65, 0x76, 0x7a, 0x65, 0x72, 0x6f, 0x2d, 0x69, + 0x6e, 0x63, 0x2f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2f, 0x64, 0x61, 0x6b, 0x72, + 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x3b, 0x61, 0x70, 0x69, 0x76, + 0x31, 0xa2, 0x02, 0x03, 0x41, 0x58, 0x58, 0xaa, 0x02, 0x06, 0x41, 0x70, 0x69, 0x2e, 0x56, 0x31, + 0xca, 0x02, 0x06, 0x41, 0x70, 0x69, 0x5c, 0x56, 0x31, 0xe2, 0x02, 0x12, 0x41, 0x70, 0x69, 0x5c, + 0x56, 0x31, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, + 0x07, 0x41, 0x70, 0x69, 0x3a, 0x3a, 0x56, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -4288,7 +5319,7 @@ func file_api_v1_metrics_collector_proto_rawDescGZIP() []byte { } var file_api_v1_metrics_collector_proto_enumTypes = make([]protoimpl.EnumInfo, 3) -var file_api_v1_metrics_collector_proto_msgTypes = make([]protoimpl.MessageInfo, 76) +var file_api_v1_metrics_collector_proto_msgTypes = make([]protoimpl.MessageInfo, 83) var file_api_v1_metrics_collector_proto_goTypes = []interface{}{ (EventType)(0), // 0: api.v1.EventType (ResourceType)(0), // 1: api.v1.ResourceType @@ -4318,209 +5349,225 @@ var file_api_v1_metrics_collector_proto_goTypes = []interface{}{ (*SendTelemetryLogsRequest)(nil), // 25: api.v1.SendTelemetryLogsRequest (*NetworkTrafficItem)(nil), // 26: api.v1.NetworkTrafficItem (*DnsLookupItem)(nil), // 27: api.v1.DnsLookupItem - (*SendNetworkTrafficMetricsRequest)(nil), // 28: api.v1.SendNetworkTrafficMetricsRequest - (*SendNetworkTrafficMetricsResponse)(nil), // 29: api.v1.SendNetworkTrafficMetricsResponse - (*LogEntry)(nil), // 30: api.v1.LogEntry - (*SendTelemetryLogsResponse)(nil), // 31: api.v1.SendTelemetryLogsResponse - (*NodeMetadataRequest)(nil), // 32: api.v1.NodeMetadataRequest - (*NodeMetadataResponse)(nil), // 33: api.v1.NodeMetadataResponse - nil, // 34: api.v1.NodeData.PodsEntry - nil, // 35: api.v1.Namespace.DeploymentsEntry - nil, // 36: api.v1.Namespace.StatefulSetsEntry - nil, // 37: api.v1.Namespace.DaemonSetsEntry - nil, // 38: api.v1.Namespace.ReplicaSetsEntry - nil, // 39: api.v1.Namespace.ServicesEntry - nil, // 40: api.v1.Namespace.ConfigMapsEntry - nil, // 41: api.v1.Namespace.SecretsEntry - nil, // 42: api.v1.Namespace.PvcsEntry - nil, // 43: api.v1.Namespace.JobsEntry - nil, // 44: api.v1.Namespace.CronJobsEntry - nil, // 45: api.v1.Namespace.IngressesEntry - nil, // 46: api.v1.Namespace.NetworkPoliciesEntry - nil, // 47: api.v1.Namespace.ServiceAccountsEntry - nil, // 48: api.v1.Namespace.RolesEntry - nil, // 49: api.v1.Namespace.RoleBindingsEntry - nil, // 50: api.v1.Namespace.PodDisruptionBudgetsEntry - nil, // 51: api.v1.Namespace.EndpointsEntry - nil, // 52: api.v1.Namespace.LimitRangesEntry - nil, // 53: api.v1.Namespace.ResourceQuotasEntry - nil, // 54: api.v1.Namespace.UnscheduledPodsEntry - nil, // 55: api.v1.Namespace.HorizontalPodAutoscalersEntry - nil, // 56: api.v1.Namespace.EventsEntry - nil, // 57: api.v1.Namespace.KedaScaledJobsEntry - nil, // 58: api.v1.Namespace.KedaScaledObjectsEntry - nil, // 59: api.v1.Namespace.CsiStorageCapacitiesEntry - nil, // 60: api.v1.Namespace.KubeflowNotebooksEntry - nil, // 61: api.v1.Namespace.VolcanoJobsEntry - nil, // 62: api.v1.Namespace.SparkApplicationsEntry - nil, // 63: api.v1.Namespace.ScheduledSparkApplicationsEntry - nil, // 64: api.v1.Namespace.CnpgClustersEntry - nil, // 65: api.v1.ClusterScopedSnapshot.PersistentVolumesEntry - nil, // 66: api.v1.ClusterScopedSnapshot.StorageClassesEntry - nil, // 67: api.v1.ClusterScopedSnapshot.ClusterRolesEntry - nil, // 68: api.v1.ClusterScopedSnapshot.ClusterRoleBindingsEntry - nil, // 69: api.v1.ClusterScopedSnapshot.CustomResourceDefinitionsEntry - nil, // 70: api.v1.ClusterScopedSnapshot.IngressClassesEntry - nil, // 71: api.v1.ClusterScopedSnapshot.CsiNodesEntry - nil, // 72: api.v1.ClusterScopedSnapshot.CsiDriversEntry - nil, // 73: api.v1.ClusterScopedSnapshot.VolumeAttachmentsEntry - nil, // 74: api.v1.ClusterSnapshot.NodesEntry - nil, // 75: api.v1.ClusterSnapshot.NamespacesEntry - nil, // 76: api.v1.SendNetworkTrafficMetricsRequest.Ip2domainEntry - nil, // 77: api.v1.LogEntry.FieldsEntry - nil, // 78: api.v1.NodeMetadataResponse.NodeToMetaEntry - (*timestamppb.Timestamp)(nil), // 79: google.protobuf.Timestamp - (*structpb.Struct)(nil), // 80: google.protobuf.Struct - (*_go.MetricFamily)(nil), // 81: io.prometheus.client.MetricFamily - (*Node)(nil), // 82: api.v1.Node + (*NodeEgressPath)(nil), // 28: api.v1.NodeEgressPath + (*SendNetworkTrafficMetricsRequest)(nil), // 29: api.v1.SendNetworkTrafficMetricsRequest + (*SendNetworkTrafficMetricsResponse)(nil), // 30: api.v1.SendNetworkTrafficMetricsResponse + (*ContainerRedMetricsItem)(nil), // 31: api.v1.ContainerRedMetricsItem + (*SendContainerRedMetricsRequest)(nil), // 32: api.v1.SendContainerRedMetricsRequest + (*SendContainerRedMetricsResponse)(nil), // 33: api.v1.SendContainerRedMetricsResponse + (*LogEntry)(nil), // 34: api.v1.LogEntry + (*SendTelemetryLogsResponse)(nil), // 35: api.v1.SendTelemetryLogsResponse + (*NodeMetadataRequest)(nil), // 36: api.v1.NodeMetadataRequest + (*NodeMetadataResponse)(nil), // 37: api.v1.NodeMetadataResponse + (*ScalerMetricSample)(nil), // 38: api.v1.ScalerMetricSample + (*SendScalerMetricsRequest)(nil), // 39: api.v1.SendScalerMetricsRequest + (*SendScalerMetricsResponse)(nil), // 40: api.v1.SendScalerMetricsResponse + nil, // 41: api.v1.NodeData.PodsEntry + nil, // 42: api.v1.Namespace.DeploymentsEntry + nil, // 43: api.v1.Namespace.StatefulSetsEntry + nil, // 44: api.v1.Namespace.DaemonSetsEntry + nil, // 45: api.v1.Namespace.ReplicaSetsEntry + nil, // 46: api.v1.Namespace.ServicesEntry + nil, // 47: api.v1.Namespace.ConfigMapsEntry + nil, // 48: api.v1.Namespace.SecretsEntry + nil, // 49: api.v1.Namespace.PvcsEntry + nil, // 50: api.v1.Namespace.JobsEntry + nil, // 51: api.v1.Namespace.CronJobsEntry + nil, // 52: api.v1.Namespace.IngressesEntry + nil, // 53: api.v1.Namespace.NetworkPoliciesEntry + nil, // 54: api.v1.Namespace.ServiceAccountsEntry + nil, // 55: api.v1.Namespace.RolesEntry + nil, // 56: api.v1.Namespace.RoleBindingsEntry + nil, // 57: api.v1.Namespace.PodDisruptionBudgetsEntry + nil, // 58: api.v1.Namespace.EndpointsEntry + nil, // 59: api.v1.Namespace.LimitRangesEntry + nil, // 60: api.v1.Namespace.ResourceQuotasEntry + nil, // 61: api.v1.Namespace.UnscheduledPodsEntry + nil, // 62: api.v1.Namespace.HorizontalPodAutoscalersEntry + nil, // 63: api.v1.Namespace.EventsEntry + nil, // 64: api.v1.Namespace.KedaScaledJobsEntry + nil, // 65: api.v1.Namespace.KedaScaledObjectsEntry + nil, // 66: api.v1.Namespace.CsiStorageCapacitiesEntry + nil, // 67: api.v1.Namespace.KubeflowNotebooksEntry + nil, // 68: api.v1.Namespace.VolcanoJobsEntry + nil, // 69: api.v1.Namespace.SparkApplicationsEntry + nil, // 70: api.v1.Namespace.ScheduledSparkApplicationsEntry + nil, // 71: api.v1.Namespace.CnpgClustersEntry + nil, // 72: api.v1.ClusterScopedSnapshot.PersistentVolumesEntry + nil, // 73: api.v1.ClusterScopedSnapshot.StorageClassesEntry + nil, // 74: api.v1.ClusterScopedSnapshot.ClusterRolesEntry + nil, // 75: api.v1.ClusterScopedSnapshot.ClusterRoleBindingsEntry + nil, // 76: api.v1.ClusterScopedSnapshot.CustomResourceDefinitionsEntry + nil, // 77: api.v1.ClusterScopedSnapshot.IngressClassesEntry + nil, // 78: api.v1.ClusterScopedSnapshot.CsiNodesEntry + nil, // 79: api.v1.ClusterScopedSnapshot.CsiDriversEntry + nil, // 80: api.v1.ClusterScopedSnapshot.VolumeAttachmentsEntry + nil, // 81: api.v1.ClusterSnapshot.NodesEntry + nil, // 82: api.v1.ClusterSnapshot.NamespacesEntry + nil, // 83: api.v1.SendNetworkTrafficMetricsRequest.Ip2domainEntry + nil, // 84: api.v1.LogEntry.FieldsEntry + nil, // 85: api.v1.NodeMetadataResponse.NodeToMetaEntry + (*timestamppb.Timestamp)(nil), // 86: google.protobuf.Timestamp + (*structpb.Struct)(nil), // 87: google.protobuf.Struct + (*_go.MetricFamily)(nil), // 88: io.prometheus.client.MetricFamily + (*Node)(nil), // 89: api.v1.Node } var file_api_v1_metrics_collector_proto_depIdxs = []int32{ - 79, // 0: api.v1.ResourceItem.timestamp:type_name -> google.protobuf.Timestamp + 86, // 0: api.v1.ResourceItem.timestamp:type_name -> google.protobuf.Timestamp 0, // 1: api.v1.ResourceItem.event_type:type_name -> api.v1.EventType - 80, // 2: api.v1.ResourceItem.data:type_name -> google.protobuf.Struct + 87, // 2: api.v1.ResourceItem.data:type_name -> google.protobuf.Struct 1, // 3: api.v1.ResourceItem.resource_type:type_name -> api.v1.ResourceType 1, // 4: api.v1.SendResourceRequest.resource_type:type_name -> api.v1.ResourceType - 79, // 5: api.v1.SendResourceRequest.timestamp:type_name -> google.protobuf.Timestamp + 86, // 5: api.v1.SendResourceRequest.timestamp:type_name -> google.protobuf.Timestamp 0, // 6: api.v1.SendResourceRequest.event_type:type_name -> api.v1.EventType - 80, // 7: api.v1.SendResourceRequest.data:type_name -> google.protobuf.Struct + 87, // 7: api.v1.SendResourceRequest.data:type_name -> google.protobuf.Struct 3, // 8: api.v1.SendResourceBatchRequest.resources:type_name -> api.v1.ResourceItem - 79, // 9: api.v1.GetForecastMetricsRequest.start_time:type_name -> google.protobuf.Timestamp - 79, // 10: api.v1.GetForecastMetricsRequest.end_time:type_name -> google.protobuf.Timestamp + 86, // 9: api.v1.GetForecastMetricsRequest.start_time:type_name -> google.protobuf.Timestamp + 86, // 10: api.v1.GetForecastMetricsRequest.end_time:type_name -> google.protobuf.Timestamp 8, // 11: api.v1.GetForecastMetricsResponse.metrics:type_name -> api.v1.ContainerForecastMetric - 79, // 12: api.v1.ContainerForecastMetric.timestamp:type_name -> google.protobuf.Timestamp - 79, // 13: api.v1.ContainerForecastMetric.created_at:type_name -> google.protobuf.Timestamp - 79, // 14: api.v1.ContainerForecastMetric.updated_at:type_name -> google.protobuf.Timestamp + 86, // 12: api.v1.ContainerForecastMetric.timestamp:type_name -> google.protobuf.Timestamp + 86, // 13: api.v1.ContainerForecastMetric.created_at:type_name -> google.protobuf.Timestamp + 86, // 14: api.v1.ContainerForecastMetric.updated_at:type_name -> google.protobuf.Timestamp 1, // 15: api.v1.SendResourceResponse.resource_type:type_name -> api.v1.ResourceType 1, // 16: api.v1.SendResourceBatchResponse.resource_type:type_name -> api.v1.ResourceType - 81, // 17: api.v1.SendTelemetryMetricsRequest.metric_families:type_name -> io.prometheus.client.MetricFamily - 79, // 18: api.v1.ClusterSnapshotChunk.timestamp:type_name -> google.protobuf.Timestamp + 88, // 17: api.v1.SendTelemetryMetricsRequest.metric_families:type_name -> io.prometheus.client.MetricFamily + 86, // 18: api.v1.ClusterSnapshotChunk.timestamp:type_name -> google.protobuf.Timestamp 24, // 19: api.v1.SendClusterSnapshotStreamResponse.missing_resources:type_name -> api.v1.ClusterSnapshot - 79, // 20: api.v1.ClusterSnapshotBatch.timestamp:type_name -> google.protobuf.Timestamp + 86, // 20: api.v1.ClusterSnapshotBatch.timestamp:type_name -> google.protobuf.Timestamp 1, // 21: api.v1.ClusterSnapshotBatch.resource_type:type_name -> api.v1.ResourceType 15, // 22: api.v1.ClusterSnapshotBatch.entries:type_name -> api.v1.SnapshotEntry 1, // 23: api.v1.MissingResource.resource_type:type_name -> api.v1.ResourceType 17, // 24: api.v1.SendClusterSnapshotBatchedResponse.missing_resources:type_name -> api.v1.MissingResource 19, // 25: api.v1.NodeData.node:type_name -> api.v1.ResourceIdentifier - 34, // 26: api.v1.NodeData.pods:type_name -> api.v1.NodeData.PodsEntry + 41, // 26: api.v1.NodeData.pods:type_name -> api.v1.NodeData.PodsEntry 19, // 27: api.v1.Namespace.namespace:type_name -> api.v1.ResourceIdentifier - 35, // 28: api.v1.Namespace.deployments:type_name -> api.v1.Namespace.DeploymentsEntry - 36, // 29: api.v1.Namespace.stateful_sets:type_name -> api.v1.Namespace.StatefulSetsEntry - 37, // 30: api.v1.Namespace.daemon_sets:type_name -> api.v1.Namespace.DaemonSetsEntry - 38, // 31: api.v1.Namespace.replica_sets:type_name -> api.v1.Namespace.ReplicaSetsEntry - 39, // 32: api.v1.Namespace.services:type_name -> api.v1.Namespace.ServicesEntry - 40, // 33: api.v1.Namespace.config_maps:type_name -> api.v1.Namespace.ConfigMapsEntry - 41, // 34: api.v1.Namespace.secrets:type_name -> api.v1.Namespace.SecretsEntry - 42, // 35: api.v1.Namespace.pvcs:type_name -> api.v1.Namespace.PvcsEntry - 43, // 36: api.v1.Namespace.jobs:type_name -> api.v1.Namespace.JobsEntry - 44, // 37: api.v1.Namespace.cron_jobs:type_name -> api.v1.Namespace.CronJobsEntry - 45, // 38: api.v1.Namespace.ingresses:type_name -> api.v1.Namespace.IngressesEntry - 46, // 39: api.v1.Namespace.network_policies:type_name -> api.v1.Namespace.NetworkPoliciesEntry - 47, // 40: api.v1.Namespace.service_accounts:type_name -> api.v1.Namespace.ServiceAccountsEntry - 48, // 41: api.v1.Namespace.roles:type_name -> api.v1.Namespace.RolesEntry - 49, // 42: api.v1.Namespace.role_bindings:type_name -> api.v1.Namespace.RoleBindingsEntry - 50, // 43: api.v1.Namespace.pod_disruption_budgets:type_name -> api.v1.Namespace.PodDisruptionBudgetsEntry - 51, // 44: api.v1.Namespace.endpoints:type_name -> api.v1.Namespace.EndpointsEntry - 52, // 45: api.v1.Namespace.limit_ranges:type_name -> api.v1.Namespace.LimitRangesEntry - 53, // 46: api.v1.Namespace.resource_quotas:type_name -> api.v1.Namespace.ResourceQuotasEntry - 54, // 47: api.v1.Namespace.unscheduled_pods:type_name -> api.v1.Namespace.UnscheduledPodsEntry - 55, // 48: api.v1.Namespace.horizontal_pod_autoscalers:type_name -> api.v1.Namespace.HorizontalPodAutoscalersEntry - 56, // 49: api.v1.Namespace.events:type_name -> api.v1.Namespace.EventsEntry - 57, // 50: api.v1.Namespace.keda_scaled_jobs:type_name -> api.v1.Namespace.KedaScaledJobsEntry - 58, // 51: api.v1.Namespace.keda_scaled_objects:type_name -> api.v1.Namespace.KedaScaledObjectsEntry - 59, // 52: api.v1.Namespace.csi_storage_capacities:type_name -> api.v1.Namespace.CsiStorageCapacitiesEntry - 60, // 53: api.v1.Namespace.kubeflow_notebooks:type_name -> api.v1.Namespace.KubeflowNotebooksEntry - 61, // 54: api.v1.Namespace.volcano_jobs:type_name -> api.v1.Namespace.VolcanoJobsEntry - 62, // 55: api.v1.Namespace.spark_applications:type_name -> api.v1.Namespace.SparkApplicationsEntry - 63, // 56: api.v1.Namespace.scheduled_spark_applications:type_name -> api.v1.Namespace.ScheduledSparkApplicationsEntry - 64, // 57: api.v1.Namespace.cnpg_clusters:type_name -> api.v1.Namespace.CnpgClustersEntry - 65, // 58: api.v1.ClusterScopedSnapshot.persistent_volumes:type_name -> api.v1.ClusterScopedSnapshot.PersistentVolumesEntry - 66, // 59: api.v1.ClusterScopedSnapshot.storage_classes:type_name -> api.v1.ClusterScopedSnapshot.StorageClassesEntry - 67, // 60: api.v1.ClusterScopedSnapshot.cluster_roles:type_name -> api.v1.ClusterScopedSnapshot.ClusterRolesEntry - 68, // 61: api.v1.ClusterScopedSnapshot.cluster_role_bindings:type_name -> api.v1.ClusterScopedSnapshot.ClusterRoleBindingsEntry - 69, // 62: api.v1.ClusterScopedSnapshot.custom_resource_definitions:type_name -> api.v1.ClusterScopedSnapshot.CustomResourceDefinitionsEntry - 70, // 63: api.v1.ClusterScopedSnapshot.ingress_classes:type_name -> api.v1.ClusterScopedSnapshot.IngressClassesEntry - 71, // 64: api.v1.ClusterScopedSnapshot.csi_nodes:type_name -> api.v1.ClusterScopedSnapshot.CsiNodesEntry - 72, // 65: api.v1.ClusterScopedSnapshot.csi_drivers:type_name -> api.v1.ClusterScopedSnapshot.CsiDriversEntry - 73, // 66: api.v1.ClusterScopedSnapshot.volume_attachments:type_name -> api.v1.ClusterScopedSnapshot.VolumeAttachmentsEntry + 42, // 28: api.v1.Namespace.deployments:type_name -> api.v1.Namespace.DeploymentsEntry + 43, // 29: api.v1.Namespace.stateful_sets:type_name -> api.v1.Namespace.StatefulSetsEntry + 44, // 30: api.v1.Namespace.daemon_sets:type_name -> api.v1.Namespace.DaemonSetsEntry + 45, // 31: api.v1.Namespace.replica_sets:type_name -> api.v1.Namespace.ReplicaSetsEntry + 46, // 32: api.v1.Namespace.services:type_name -> api.v1.Namespace.ServicesEntry + 47, // 33: api.v1.Namespace.config_maps:type_name -> api.v1.Namespace.ConfigMapsEntry + 48, // 34: api.v1.Namespace.secrets:type_name -> api.v1.Namespace.SecretsEntry + 49, // 35: api.v1.Namespace.pvcs:type_name -> api.v1.Namespace.PvcsEntry + 50, // 36: api.v1.Namespace.jobs:type_name -> api.v1.Namespace.JobsEntry + 51, // 37: api.v1.Namespace.cron_jobs:type_name -> api.v1.Namespace.CronJobsEntry + 52, // 38: api.v1.Namespace.ingresses:type_name -> api.v1.Namespace.IngressesEntry + 53, // 39: api.v1.Namespace.network_policies:type_name -> api.v1.Namespace.NetworkPoliciesEntry + 54, // 40: api.v1.Namespace.service_accounts:type_name -> api.v1.Namespace.ServiceAccountsEntry + 55, // 41: api.v1.Namespace.roles:type_name -> api.v1.Namespace.RolesEntry + 56, // 42: api.v1.Namespace.role_bindings:type_name -> api.v1.Namespace.RoleBindingsEntry + 57, // 43: api.v1.Namespace.pod_disruption_budgets:type_name -> api.v1.Namespace.PodDisruptionBudgetsEntry + 58, // 44: api.v1.Namespace.endpoints:type_name -> api.v1.Namespace.EndpointsEntry + 59, // 45: api.v1.Namespace.limit_ranges:type_name -> api.v1.Namespace.LimitRangesEntry + 60, // 46: api.v1.Namespace.resource_quotas:type_name -> api.v1.Namespace.ResourceQuotasEntry + 61, // 47: api.v1.Namespace.unscheduled_pods:type_name -> api.v1.Namespace.UnscheduledPodsEntry + 62, // 48: api.v1.Namespace.horizontal_pod_autoscalers:type_name -> api.v1.Namespace.HorizontalPodAutoscalersEntry + 63, // 49: api.v1.Namespace.events:type_name -> api.v1.Namespace.EventsEntry + 64, // 50: api.v1.Namespace.keda_scaled_jobs:type_name -> api.v1.Namespace.KedaScaledJobsEntry + 65, // 51: api.v1.Namespace.keda_scaled_objects:type_name -> api.v1.Namespace.KedaScaledObjectsEntry + 66, // 52: api.v1.Namespace.csi_storage_capacities:type_name -> api.v1.Namespace.CsiStorageCapacitiesEntry + 67, // 53: api.v1.Namespace.kubeflow_notebooks:type_name -> api.v1.Namespace.KubeflowNotebooksEntry + 68, // 54: api.v1.Namespace.volcano_jobs:type_name -> api.v1.Namespace.VolcanoJobsEntry + 69, // 55: api.v1.Namespace.spark_applications:type_name -> api.v1.Namespace.SparkApplicationsEntry + 70, // 56: api.v1.Namespace.scheduled_spark_applications:type_name -> api.v1.Namespace.ScheduledSparkApplicationsEntry + 71, // 57: api.v1.Namespace.cnpg_clusters:type_name -> api.v1.Namespace.CnpgClustersEntry + 72, // 58: api.v1.ClusterScopedSnapshot.persistent_volumes:type_name -> api.v1.ClusterScopedSnapshot.PersistentVolumesEntry + 73, // 59: api.v1.ClusterScopedSnapshot.storage_classes:type_name -> api.v1.ClusterScopedSnapshot.StorageClassesEntry + 74, // 60: api.v1.ClusterScopedSnapshot.cluster_roles:type_name -> api.v1.ClusterScopedSnapshot.ClusterRolesEntry + 75, // 61: api.v1.ClusterScopedSnapshot.cluster_role_bindings:type_name -> api.v1.ClusterScopedSnapshot.ClusterRoleBindingsEntry + 76, // 62: api.v1.ClusterScopedSnapshot.custom_resource_definitions:type_name -> api.v1.ClusterScopedSnapshot.CustomResourceDefinitionsEntry + 77, // 63: api.v1.ClusterScopedSnapshot.ingress_classes:type_name -> api.v1.ClusterScopedSnapshot.IngressClassesEntry + 78, // 64: api.v1.ClusterScopedSnapshot.csi_nodes:type_name -> api.v1.ClusterScopedSnapshot.CsiNodesEntry + 79, // 65: api.v1.ClusterScopedSnapshot.csi_drivers:type_name -> api.v1.ClusterScopedSnapshot.CsiDriversEntry + 80, // 66: api.v1.ClusterScopedSnapshot.volume_attachments:type_name -> api.v1.ClusterScopedSnapshot.VolumeAttachmentsEntry 23, // 67: api.v1.ClusterSnapshot.cluster_info:type_name -> api.v1.ClusterInfo - 74, // 68: api.v1.ClusterSnapshot.nodes:type_name -> api.v1.ClusterSnapshot.NodesEntry - 75, // 69: api.v1.ClusterSnapshot.namespaces:type_name -> api.v1.ClusterSnapshot.NamespacesEntry + 81, // 68: api.v1.ClusterSnapshot.nodes:type_name -> api.v1.ClusterSnapshot.NodesEntry + 82, // 69: api.v1.ClusterSnapshot.namespaces:type_name -> api.v1.ClusterSnapshot.NamespacesEntry 22, // 70: api.v1.ClusterSnapshot.cluster_scoped:type_name -> api.v1.ClusterScopedSnapshot - 79, // 71: api.v1.ClusterSnapshot.timestamp:type_name -> google.protobuf.Timestamp - 30, // 72: api.v1.SendTelemetryLogsRequest.logs:type_name -> api.v1.LogEntry - 79, // 73: api.v1.NetworkTrafficItem.timestamp:type_name -> google.protobuf.Timestamp - 79, // 74: api.v1.DnsLookupItem.timestamp:type_name -> google.protobuf.Timestamp + 86, // 71: api.v1.ClusterSnapshot.timestamp:type_name -> google.protobuf.Timestamp + 34, // 72: api.v1.SendTelemetryLogsRequest.logs:type_name -> api.v1.LogEntry + 86, // 73: api.v1.NetworkTrafficItem.timestamp:type_name -> google.protobuf.Timestamp + 86, // 74: api.v1.DnsLookupItem.timestamp:type_name -> google.protobuf.Timestamp 26, // 75: api.v1.SendNetworkTrafficMetricsRequest.items:type_name -> api.v1.NetworkTrafficItem 27, // 76: api.v1.SendNetworkTrafficMetricsRequest.dns_lookups:type_name -> api.v1.DnsLookupItem - 76, // 77: api.v1.SendNetworkTrafficMetricsRequest.ip2domain:type_name -> api.v1.SendNetworkTrafficMetricsRequest.Ip2domainEntry - 79, // 78: api.v1.LogEntry.timestamp:type_name -> google.protobuf.Timestamp - 2, // 79: api.v1.LogEntry.level:type_name -> api.v1.LogLevel - 77, // 80: api.v1.LogEntry.fields:type_name -> api.v1.LogEntry.FieldsEntry - 78, // 81: api.v1.NodeMetadataResponse.node_to_meta:type_name -> api.v1.NodeMetadataResponse.NodeToMetaEntry - 19, // 82: api.v1.NodeData.PodsEntry.value:type_name -> api.v1.ResourceIdentifier - 19, // 83: api.v1.Namespace.DeploymentsEntry.value:type_name -> api.v1.ResourceIdentifier - 19, // 84: api.v1.Namespace.StatefulSetsEntry.value:type_name -> api.v1.ResourceIdentifier - 19, // 85: api.v1.Namespace.DaemonSetsEntry.value:type_name -> api.v1.ResourceIdentifier - 19, // 86: api.v1.Namespace.ReplicaSetsEntry.value:type_name -> api.v1.ResourceIdentifier - 19, // 87: api.v1.Namespace.ServicesEntry.value:type_name -> api.v1.ResourceIdentifier - 19, // 88: api.v1.Namespace.ConfigMapsEntry.value:type_name -> api.v1.ResourceIdentifier - 19, // 89: api.v1.Namespace.SecretsEntry.value:type_name -> api.v1.ResourceIdentifier - 19, // 90: api.v1.Namespace.PvcsEntry.value:type_name -> api.v1.ResourceIdentifier - 19, // 91: api.v1.Namespace.JobsEntry.value:type_name -> api.v1.ResourceIdentifier - 19, // 92: api.v1.Namespace.CronJobsEntry.value:type_name -> api.v1.ResourceIdentifier - 19, // 93: api.v1.Namespace.IngressesEntry.value:type_name -> api.v1.ResourceIdentifier - 19, // 94: api.v1.Namespace.NetworkPoliciesEntry.value:type_name -> api.v1.ResourceIdentifier - 19, // 95: api.v1.Namespace.ServiceAccountsEntry.value:type_name -> api.v1.ResourceIdentifier - 19, // 96: api.v1.Namespace.RolesEntry.value:type_name -> api.v1.ResourceIdentifier - 19, // 97: api.v1.Namespace.RoleBindingsEntry.value:type_name -> api.v1.ResourceIdentifier - 19, // 98: api.v1.Namespace.PodDisruptionBudgetsEntry.value:type_name -> api.v1.ResourceIdentifier - 19, // 99: api.v1.Namespace.EndpointsEntry.value:type_name -> api.v1.ResourceIdentifier - 19, // 100: api.v1.Namespace.LimitRangesEntry.value:type_name -> api.v1.ResourceIdentifier - 19, // 101: api.v1.Namespace.ResourceQuotasEntry.value:type_name -> api.v1.ResourceIdentifier - 19, // 102: api.v1.Namespace.UnscheduledPodsEntry.value:type_name -> api.v1.ResourceIdentifier - 19, // 103: api.v1.Namespace.HorizontalPodAutoscalersEntry.value:type_name -> api.v1.ResourceIdentifier - 19, // 104: api.v1.Namespace.EventsEntry.value:type_name -> api.v1.ResourceIdentifier - 19, // 105: api.v1.Namespace.KedaScaledJobsEntry.value:type_name -> api.v1.ResourceIdentifier - 19, // 106: api.v1.Namespace.KedaScaledObjectsEntry.value:type_name -> api.v1.ResourceIdentifier - 19, // 107: api.v1.Namespace.CsiStorageCapacitiesEntry.value:type_name -> api.v1.ResourceIdentifier - 19, // 108: api.v1.Namespace.KubeflowNotebooksEntry.value:type_name -> api.v1.ResourceIdentifier - 19, // 109: api.v1.Namespace.VolcanoJobsEntry.value:type_name -> api.v1.ResourceIdentifier - 19, // 110: api.v1.Namespace.SparkApplicationsEntry.value:type_name -> api.v1.ResourceIdentifier - 19, // 111: api.v1.Namespace.ScheduledSparkApplicationsEntry.value:type_name -> api.v1.ResourceIdentifier - 19, // 112: api.v1.Namespace.CnpgClustersEntry.value:type_name -> api.v1.ResourceIdentifier - 19, // 113: api.v1.ClusterScopedSnapshot.PersistentVolumesEntry.value:type_name -> api.v1.ResourceIdentifier - 19, // 114: api.v1.ClusterScopedSnapshot.StorageClassesEntry.value:type_name -> api.v1.ResourceIdentifier - 19, // 115: api.v1.ClusterScopedSnapshot.ClusterRolesEntry.value:type_name -> api.v1.ResourceIdentifier - 19, // 116: api.v1.ClusterScopedSnapshot.ClusterRoleBindingsEntry.value:type_name -> api.v1.ResourceIdentifier - 19, // 117: api.v1.ClusterScopedSnapshot.CustomResourceDefinitionsEntry.value:type_name -> api.v1.ResourceIdentifier - 19, // 118: api.v1.ClusterScopedSnapshot.IngressClassesEntry.value:type_name -> api.v1.ResourceIdentifier - 19, // 119: api.v1.ClusterScopedSnapshot.CsiNodesEntry.value:type_name -> api.v1.ResourceIdentifier - 19, // 120: api.v1.ClusterScopedSnapshot.CsiDriversEntry.value:type_name -> api.v1.ResourceIdentifier - 19, // 121: api.v1.ClusterScopedSnapshot.VolumeAttachmentsEntry.value:type_name -> api.v1.ResourceIdentifier - 20, // 122: api.v1.ClusterSnapshot.NodesEntry.value:type_name -> api.v1.NodeData - 21, // 123: api.v1.ClusterSnapshot.NamespacesEntry.value:type_name -> api.v1.Namespace - 82, // 124: api.v1.NodeMetadataResponse.NodeToMetaEntry.value:type_name -> api.v1.Node - 4, // 125: api.v1.MetricsCollectorService.SendResource:input_type -> api.v1.SendResourceRequest - 5, // 126: api.v1.MetricsCollectorService.SendResourceBatch:input_type -> api.v1.SendResourceBatchRequest - 11, // 127: api.v1.MetricsCollectorService.SendTelemetryMetrics:input_type -> api.v1.SendTelemetryMetricsRequest - 13, // 128: api.v1.MetricsCollectorService.SendClusterSnapshotStream:input_type -> api.v1.ClusterSnapshotChunk - 16, // 129: api.v1.MetricsCollectorService.SendClusterSnapshotBatched:input_type -> api.v1.ClusterSnapshotBatch - 25, // 130: api.v1.MetricsCollectorService.SendTelemetryLogs:input_type -> api.v1.SendTelemetryLogsRequest - 28, // 131: api.v1.MetricsCollectorService.SendNetworkTrafficMetrics:input_type -> api.v1.SendNetworkTrafficMetricsRequest - 32, // 132: api.v1.MetricsCollectorService.NodeMetadata:input_type -> api.v1.NodeMetadataRequest - 9, // 133: api.v1.MetricsCollectorService.SendResource:output_type -> api.v1.SendResourceResponse - 10, // 134: api.v1.MetricsCollectorService.SendResourceBatch:output_type -> api.v1.SendResourceBatchResponse - 12, // 135: api.v1.MetricsCollectorService.SendTelemetryMetrics:output_type -> api.v1.SendTelemetryMetricsResponse - 14, // 136: api.v1.MetricsCollectorService.SendClusterSnapshotStream:output_type -> api.v1.SendClusterSnapshotStreamResponse - 18, // 137: api.v1.MetricsCollectorService.SendClusterSnapshotBatched:output_type -> api.v1.SendClusterSnapshotBatchedResponse - 31, // 138: api.v1.MetricsCollectorService.SendTelemetryLogs:output_type -> api.v1.SendTelemetryLogsResponse - 29, // 139: api.v1.MetricsCollectorService.SendNetworkTrafficMetrics:output_type -> api.v1.SendNetworkTrafficMetricsResponse - 33, // 140: api.v1.MetricsCollectorService.NodeMetadata:output_type -> api.v1.NodeMetadataResponse - 133, // [133:141] is the sub-list for method output_type - 125, // [125:133] is the sub-list for method input_type - 125, // [125:125] is the sub-list for extension type_name - 125, // [125:125] is the sub-list for extension extendee - 0, // [0:125] is the sub-list for field type_name + 83, // 77: api.v1.SendNetworkTrafficMetricsRequest.ip2domain:type_name -> api.v1.SendNetworkTrafficMetricsRequest.Ip2domainEntry + 28, // 78: api.v1.SendNetworkTrafficMetricsRequest.node_egress:type_name -> api.v1.NodeEgressPath + 86, // 79: api.v1.ContainerRedMetricsItem.timestamp:type_name -> google.protobuf.Timestamp + 31, // 80: api.v1.SendContainerRedMetricsRequest.items:type_name -> api.v1.ContainerRedMetricsItem + 86, // 81: api.v1.LogEntry.timestamp:type_name -> google.protobuf.Timestamp + 2, // 82: api.v1.LogEntry.level:type_name -> api.v1.LogLevel + 84, // 83: api.v1.LogEntry.fields:type_name -> api.v1.LogEntry.FieldsEntry + 85, // 84: api.v1.NodeMetadataResponse.node_to_meta:type_name -> api.v1.NodeMetadataResponse.NodeToMetaEntry + 86, // 85: api.v1.ScalerMetricSample.ts:type_name -> google.protobuf.Timestamp + 38, // 86: api.v1.SendScalerMetricsRequest.samples:type_name -> api.v1.ScalerMetricSample + 19, // 87: api.v1.NodeData.PodsEntry.value:type_name -> api.v1.ResourceIdentifier + 19, // 88: api.v1.Namespace.DeploymentsEntry.value:type_name -> api.v1.ResourceIdentifier + 19, // 89: api.v1.Namespace.StatefulSetsEntry.value:type_name -> api.v1.ResourceIdentifier + 19, // 90: api.v1.Namespace.DaemonSetsEntry.value:type_name -> api.v1.ResourceIdentifier + 19, // 91: api.v1.Namespace.ReplicaSetsEntry.value:type_name -> api.v1.ResourceIdentifier + 19, // 92: api.v1.Namespace.ServicesEntry.value:type_name -> api.v1.ResourceIdentifier + 19, // 93: api.v1.Namespace.ConfigMapsEntry.value:type_name -> api.v1.ResourceIdentifier + 19, // 94: api.v1.Namespace.SecretsEntry.value:type_name -> api.v1.ResourceIdentifier + 19, // 95: api.v1.Namespace.PvcsEntry.value:type_name -> api.v1.ResourceIdentifier + 19, // 96: api.v1.Namespace.JobsEntry.value:type_name -> api.v1.ResourceIdentifier + 19, // 97: api.v1.Namespace.CronJobsEntry.value:type_name -> api.v1.ResourceIdentifier + 19, // 98: api.v1.Namespace.IngressesEntry.value:type_name -> api.v1.ResourceIdentifier + 19, // 99: api.v1.Namespace.NetworkPoliciesEntry.value:type_name -> api.v1.ResourceIdentifier + 19, // 100: api.v1.Namespace.ServiceAccountsEntry.value:type_name -> api.v1.ResourceIdentifier + 19, // 101: api.v1.Namespace.RolesEntry.value:type_name -> api.v1.ResourceIdentifier + 19, // 102: api.v1.Namespace.RoleBindingsEntry.value:type_name -> api.v1.ResourceIdentifier + 19, // 103: api.v1.Namespace.PodDisruptionBudgetsEntry.value:type_name -> api.v1.ResourceIdentifier + 19, // 104: api.v1.Namespace.EndpointsEntry.value:type_name -> api.v1.ResourceIdentifier + 19, // 105: api.v1.Namespace.LimitRangesEntry.value:type_name -> api.v1.ResourceIdentifier + 19, // 106: api.v1.Namespace.ResourceQuotasEntry.value:type_name -> api.v1.ResourceIdentifier + 19, // 107: api.v1.Namespace.UnscheduledPodsEntry.value:type_name -> api.v1.ResourceIdentifier + 19, // 108: api.v1.Namespace.HorizontalPodAutoscalersEntry.value:type_name -> api.v1.ResourceIdentifier + 19, // 109: api.v1.Namespace.EventsEntry.value:type_name -> api.v1.ResourceIdentifier + 19, // 110: api.v1.Namespace.KedaScaledJobsEntry.value:type_name -> api.v1.ResourceIdentifier + 19, // 111: api.v1.Namespace.KedaScaledObjectsEntry.value:type_name -> api.v1.ResourceIdentifier + 19, // 112: api.v1.Namespace.CsiStorageCapacitiesEntry.value:type_name -> api.v1.ResourceIdentifier + 19, // 113: api.v1.Namespace.KubeflowNotebooksEntry.value:type_name -> api.v1.ResourceIdentifier + 19, // 114: api.v1.Namespace.VolcanoJobsEntry.value:type_name -> api.v1.ResourceIdentifier + 19, // 115: api.v1.Namespace.SparkApplicationsEntry.value:type_name -> api.v1.ResourceIdentifier + 19, // 116: api.v1.Namespace.ScheduledSparkApplicationsEntry.value:type_name -> api.v1.ResourceIdentifier + 19, // 117: api.v1.Namespace.CnpgClustersEntry.value:type_name -> api.v1.ResourceIdentifier + 19, // 118: api.v1.ClusterScopedSnapshot.PersistentVolumesEntry.value:type_name -> api.v1.ResourceIdentifier + 19, // 119: api.v1.ClusterScopedSnapshot.StorageClassesEntry.value:type_name -> api.v1.ResourceIdentifier + 19, // 120: api.v1.ClusterScopedSnapshot.ClusterRolesEntry.value:type_name -> api.v1.ResourceIdentifier + 19, // 121: api.v1.ClusterScopedSnapshot.ClusterRoleBindingsEntry.value:type_name -> api.v1.ResourceIdentifier + 19, // 122: api.v1.ClusterScopedSnapshot.CustomResourceDefinitionsEntry.value:type_name -> api.v1.ResourceIdentifier + 19, // 123: api.v1.ClusterScopedSnapshot.IngressClassesEntry.value:type_name -> api.v1.ResourceIdentifier + 19, // 124: api.v1.ClusterScopedSnapshot.CsiNodesEntry.value:type_name -> api.v1.ResourceIdentifier + 19, // 125: api.v1.ClusterScopedSnapshot.CsiDriversEntry.value:type_name -> api.v1.ResourceIdentifier + 19, // 126: api.v1.ClusterScopedSnapshot.VolumeAttachmentsEntry.value:type_name -> api.v1.ResourceIdentifier + 20, // 127: api.v1.ClusterSnapshot.NodesEntry.value:type_name -> api.v1.NodeData + 21, // 128: api.v1.ClusterSnapshot.NamespacesEntry.value:type_name -> api.v1.Namespace + 89, // 129: api.v1.NodeMetadataResponse.NodeToMetaEntry.value:type_name -> api.v1.Node + 4, // 130: api.v1.MetricsCollectorService.SendResource:input_type -> api.v1.SendResourceRequest + 5, // 131: api.v1.MetricsCollectorService.SendResourceBatch:input_type -> api.v1.SendResourceBatchRequest + 11, // 132: api.v1.MetricsCollectorService.SendTelemetryMetrics:input_type -> api.v1.SendTelemetryMetricsRequest + 13, // 133: api.v1.MetricsCollectorService.SendClusterSnapshotStream:input_type -> api.v1.ClusterSnapshotChunk + 16, // 134: api.v1.MetricsCollectorService.SendClusterSnapshotBatched:input_type -> api.v1.ClusterSnapshotBatch + 25, // 135: api.v1.MetricsCollectorService.SendTelemetryLogs:input_type -> api.v1.SendTelemetryLogsRequest + 29, // 136: api.v1.MetricsCollectorService.SendNetworkTrafficMetrics:input_type -> api.v1.SendNetworkTrafficMetricsRequest + 39, // 137: api.v1.MetricsCollectorService.SendScalerMetrics:input_type -> api.v1.SendScalerMetricsRequest + 32, // 138: api.v1.MetricsCollectorService.SendContainerRedMetrics:input_type -> api.v1.SendContainerRedMetricsRequest + 36, // 139: api.v1.MetricsCollectorService.NodeMetadata:input_type -> api.v1.NodeMetadataRequest + 9, // 140: api.v1.MetricsCollectorService.SendResource:output_type -> api.v1.SendResourceResponse + 10, // 141: api.v1.MetricsCollectorService.SendResourceBatch:output_type -> api.v1.SendResourceBatchResponse + 12, // 142: api.v1.MetricsCollectorService.SendTelemetryMetrics:output_type -> api.v1.SendTelemetryMetricsResponse + 14, // 143: api.v1.MetricsCollectorService.SendClusterSnapshotStream:output_type -> api.v1.SendClusterSnapshotStreamResponse + 18, // 144: api.v1.MetricsCollectorService.SendClusterSnapshotBatched:output_type -> api.v1.SendClusterSnapshotBatchedResponse + 35, // 145: api.v1.MetricsCollectorService.SendTelemetryLogs:output_type -> api.v1.SendTelemetryLogsResponse + 30, // 146: api.v1.MetricsCollectorService.SendNetworkTrafficMetrics:output_type -> api.v1.SendNetworkTrafficMetricsResponse + 40, // 147: api.v1.MetricsCollectorService.SendScalerMetrics:output_type -> api.v1.SendScalerMetricsResponse + 33, // 148: api.v1.MetricsCollectorService.SendContainerRedMetrics:output_type -> api.v1.SendContainerRedMetricsResponse + 37, // 149: api.v1.MetricsCollectorService.NodeMetadata:output_type -> api.v1.NodeMetadataResponse + 140, // [140:150] is the sub-list for method output_type + 130, // [130:140] is the sub-list for method input_type + 130, // [130:130] is the sub-list for extension type_name + 130, // [130:130] is the sub-list for extension extendee + 0, // [0:130] is the sub-list for field type_name } func init() { file_api_v1_metrics_collector_proto_init() } @@ -4831,7 +5878,7 @@ func file_api_v1_metrics_collector_proto_init() { } } file_api_v1_metrics_collector_proto_msgTypes[25].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*SendNetworkTrafficMetricsRequest); i { + switch v := v.(*NodeEgressPath); i { case 0: return &v.state case 1: @@ -4843,7 +5890,7 @@ func file_api_v1_metrics_collector_proto_init() { } } file_api_v1_metrics_collector_proto_msgTypes[26].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*SendNetworkTrafficMetricsResponse); i { + switch v := v.(*SendNetworkTrafficMetricsRequest); i { case 0: return &v.state case 1: @@ -4855,7 +5902,7 @@ func file_api_v1_metrics_collector_proto_init() { } } file_api_v1_metrics_collector_proto_msgTypes[27].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*LogEntry); i { + switch v := v.(*SendNetworkTrafficMetricsResponse); i { case 0: return &v.state case 1: @@ -4867,7 +5914,7 @@ func file_api_v1_metrics_collector_proto_init() { } } file_api_v1_metrics_collector_proto_msgTypes[28].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*SendTelemetryLogsResponse); i { + switch v := v.(*ContainerRedMetricsItem); i { case 0: return &v.state case 1: @@ -4879,7 +5926,7 @@ func file_api_v1_metrics_collector_proto_init() { } } file_api_v1_metrics_collector_proto_msgTypes[29].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*NodeMetadataRequest); i { + switch v := v.(*SendContainerRedMetricsRequest); i { case 0: return &v.state case 1: @@ -4891,6 +5938,54 @@ func file_api_v1_metrics_collector_proto_init() { } } file_api_v1_metrics_collector_proto_msgTypes[30].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*SendContainerRedMetricsResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_api_v1_metrics_collector_proto_msgTypes[31].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*LogEntry); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_api_v1_metrics_collector_proto_msgTypes[32].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*SendTelemetryLogsResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_api_v1_metrics_collector_proto_msgTypes[33].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*NodeMetadataRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_api_v1_metrics_collector_proto_msgTypes[34].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*NodeMetadataResponse); i { case 0: return &v.state @@ -4902,15 +5997,54 @@ func file_api_v1_metrics_collector_proto_init() { return nil } } + file_api_v1_metrics_collector_proto_msgTypes[35].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ScalerMetricSample); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_api_v1_metrics_collector_proto_msgTypes[36].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*SendScalerMetricsRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_api_v1_metrics_collector_proto_msgTypes[37].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*SendScalerMetricsResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } } file_api_v1_metrics_collector_proto_msgTypes[25].OneofWrappers = []interface{}{} + file_api_v1_metrics_collector_proto_msgTypes[26].OneofWrappers = []interface{}{} + file_api_v1_metrics_collector_proto_msgTypes[29].OneofWrappers = []interface{}{} + file_api_v1_metrics_collector_proto_msgTypes[35].OneofWrappers = []interface{}{} type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_api_v1_metrics_collector_proto_rawDesc, NumEnums: 3, - NumMessages: 76, + NumMessages: 83, NumExtensions: 0, NumServices: 1, }, diff --git a/gen/api/v1/metrics_collector_grpc.pb.go b/gen/api/v1/metrics_collector_grpc.pb.go index 8a82cee4..fd3eabf4 100644 --- a/gen/api/v1/metrics_collector_grpc.pb.go +++ b/gen/api/v1/metrics_collector_grpc.pb.go @@ -26,6 +26,8 @@ const ( MetricsCollectorService_SendClusterSnapshotBatched_FullMethodName = "/api.v1.MetricsCollectorService/SendClusterSnapshotBatched" MetricsCollectorService_SendTelemetryLogs_FullMethodName = "/api.v1.MetricsCollectorService/SendTelemetryLogs" MetricsCollectorService_SendNetworkTrafficMetrics_FullMethodName = "/api.v1.MetricsCollectorService/SendNetworkTrafficMetrics" + MetricsCollectorService_SendScalerMetrics_FullMethodName = "/api.v1.MetricsCollectorService/SendScalerMetrics" + MetricsCollectorService_SendContainerRedMetrics_FullMethodName = "/api.v1.MetricsCollectorService/SendContainerRedMetrics" MetricsCollectorService_NodeMetadata_FullMethodName = "/api.v1.MetricsCollectorService/NodeMetadata" ) @@ -49,6 +51,10 @@ type MetricsCollectorServiceClient interface { SendTelemetryLogs(ctx context.Context, in *SendTelemetryLogsRequest, opts ...grpc.CallOption) (*SendTelemetryLogsResponse, error) // SendNetworkTrafficMetrics pushes network traffic metrics from a node. SendNetworkTrafficMetrics(ctx context.Context, in *SendNetworkTrafficMetricsRequest, opts ...grpc.CallOption) (*SendNetworkTrafficMetricsResponse, error) + // SendScalerMetrics pushes a batch of KEDA scaler metric samples from a cluster. + SendScalerMetrics(ctx context.Context, in *SendScalerMetricsRequest, opts ...grpc.CallOption) (*SendScalerMetricsResponse, error) + // SendContainerRedMetrics pushes per-container inbound RED metrics from a node. + SendContainerRedMetrics(ctx context.Context, in *SendContainerRedMetricsRequest, opts ...grpc.CallOption) (*SendContainerRedMetricsResponse, error) NodeMetadata(ctx context.Context, in *NodeMetadataRequest, opts ...grpc.CallOption) (*NodeMetadataResponse, error) } @@ -173,6 +179,24 @@ func (c *metricsCollectorServiceClient) SendNetworkTrafficMetrics(ctx context.Co return out, nil } +func (c *metricsCollectorServiceClient) SendScalerMetrics(ctx context.Context, in *SendScalerMetricsRequest, opts ...grpc.CallOption) (*SendScalerMetricsResponse, error) { + out := new(SendScalerMetricsResponse) + err := c.cc.Invoke(ctx, MetricsCollectorService_SendScalerMetrics_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *metricsCollectorServiceClient) SendContainerRedMetrics(ctx context.Context, in *SendContainerRedMetricsRequest, opts ...grpc.CallOption) (*SendContainerRedMetricsResponse, error) { + out := new(SendContainerRedMetricsResponse) + err := c.cc.Invoke(ctx, MetricsCollectorService_SendContainerRedMetrics_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + func (c *metricsCollectorServiceClient) NodeMetadata(ctx context.Context, in *NodeMetadataRequest, opts ...grpc.CallOption) (*NodeMetadataResponse, error) { out := new(NodeMetadataResponse) err := c.cc.Invoke(ctx, MetricsCollectorService_NodeMetadata_FullMethodName, in, out, opts...) @@ -202,6 +226,10 @@ type MetricsCollectorServiceServer interface { SendTelemetryLogs(context.Context, *SendTelemetryLogsRequest) (*SendTelemetryLogsResponse, error) // SendNetworkTrafficMetrics pushes network traffic metrics from a node. SendNetworkTrafficMetrics(context.Context, *SendNetworkTrafficMetricsRequest) (*SendNetworkTrafficMetricsResponse, error) + // SendScalerMetrics pushes a batch of KEDA scaler metric samples from a cluster. + SendScalerMetrics(context.Context, *SendScalerMetricsRequest) (*SendScalerMetricsResponse, error) + // SendContainerRedMetrics pushes per-container inbound RED metrics from a node. + SendContainerRedMetrics(context.Context, *SendContainerRedMetricsRequest) (*SendContainerRedMetricsResponse, error) NodeMetadata(context.Context, *NodeMetadataRequest) (*NodeMetadataResponse, error) mustEmbedUnimplementedMetricsCollectorServiceServer() } @@ -231,6 +259,12 @@ func (UnimplementedMetricsCollectorServiceServer) SendTelemetryLogs(context.Cont func (UnimplementedMetricsCollectorServiceServer) SendNetworkTrafficMetrics(context.Context, *SendNetworkTrafficMetricsRequest) (*SendNetworkTrafficMetricsResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method SendNetworkTrafficMetrics not implemented") } +func (UnimplementedMetricsCollectorServiceServer) SendScalerMetrics(context.Context, *SendScalerMetricsRequest) (*SendScalerMetricsResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method SendScalerMetrics not implemented") +} +func (UnimplementedMetricsCollectorServiceServer) SendContainerRedMetrics(context.Context, *SendContainerRedMetricsRequest) (*SendContainerRedMetricsResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method SendContainerRedMetrics not implemented") +} func (UnimplementedMetricsCollectorServiceServer) NodeMetadata(context.Context, *NodeMetadataRequest) (*NodeMetadataResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method NodeMetadata not implemented") } @@ -390,6 +424,42 @@ func _MetricsCollectorService_SendNetworkTrafficMetrics_Handler(srv interface{}, return interceptor(ctx, in, info, handler) } +func _MetricsCollectorService_SendScalerMetrics_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(SendScalerMetricsRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(MetricsCollectorServiceServer).SendScalerMetrics(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: MetricsCollectorService_SendScalerMetrics_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(MetricsCollectorServiceServer).SendScalerMetrics(ctx, req.(*SendScalerMetricsRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _MetricsCollectorService_SendContainerRedMetrics_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(SendContainerRedMetricsRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(MetricsCollectorServiceServer).SendContainerRedMetrics(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: MetricsCollectorService_SendContainerRedMetrics_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(MetricsCollectorServiceServer).SendContainerRedMetrics(ctx, req.(*SendContainerRedMetricsRequest)) + } + return interceptor(ctx, in, info, handler) +} + func _MetricsCollectorService_NodeMetadata_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(NodeMetadataRequest) if err := dec(in); err != nil { @@ -435,6 +505,14 @@ var MetricsCollectorService_ServiceDesc = grpc.ServiceDesc{ MethodName: "SendNetworkTrafficMetrics", Handler: _MetricsCollectorService_SendNetworkTrafficMetrics_Handler, }, + { + MethodName: "SendScalerMetrics", + Handler: _MetricsCollectorService_SendScalerMetrics_Handler, + }, + { + MethodName: "SendContainerRedMetrics", + Handler: _MetricsCollectorService_SendContainerRedMetrics_Handler, + }, { MethodName: "NodeMetadata", Handler: _MetricsCollectorService_NodeMetadata_Handler, diff --git a/gen/api/v1/operator.pb.go b/gen/api/v1/operator.pb.go index 147b0c3e..3ab3bdf3 100644 --- a/gen/api/v1/operator.pb.go +++ b/gen/api/v1/operator.pb.go @@ -194,22 +194,93 @@ func (OperatorInstallMethod) EnumDescriptor() ([]byte, []int) { return file_api_v1_operator_proto_rawDescGZIP(), []int{2} } +// OperatorVersionStatus places a cluster's installed operator version against +// the newest release dakr advertises and the support floor (KUB-155 §5.4). +// Versions compare in SemVer order, so a cluster on an rc of the next release +// or ahead of a rolled-back latest is CURRENT, not OUTDATED. +type OperatorVersionStatus int32 + +const ( + // Not installed, a version that does not parse as SemVer, or no latest + // version configured for the component. Never shown as an update. + OperatorVersionStatus_OPERATOR_VERSION_STATUS_UNSPECIFIED OperatorVersionStatus = 0 + // At the latest release or newer. + OperatorVersionStatus_OPERATOR_VERSION_STATUS_CURRENT OperatorVersionStatus = 1 + // Below the latest release, at or above the support floor. + OperatorVersionStatus_OPERATOR_VERSION_STATUS_OUTDATED OperatorVersionStatus = 2 + // Below the support floor: dakr no longer supports this version and the + // cluster must upgrade. + OperatorVersionStatus_OPERATOR_VERSION_STATUS_UNSUPPORTED OperatorVersionStatus = 3 +) + +// Enum value maps for OperatorVersionStatus. +var ( + OperatorVersionStatus_name = map[int32]string{ + 0: "OPERATOR_VERSION_STATUS_UNSPECIFIED", + 1: "OPERATOR_VERSION_STATUS_CURRENT", + 2: "OPERATOR_VERSION_STATUS_OUTDATED", + 3: "OPERATOR_VERSION_STATUS_UNSUPPORTED", + } + OperatorVersionStatus_value = map[string]int32{ + "OPERATOR_VERSION_STATUS_UNSPECIFIED": 0, + "OPERATOR_VERSION_STATUS_CURRENT": 1, + "OPERATOR_VERSION_STATUS_OUTDATED": 2, + "OPERATOR_VERSION_STATUS_UNSUPPORTED": 3, + } +) + +func (x OperatorVersionStatus) Enum() *OperatorVersionStatus { + p := new(OperatorVersionStatus) + *p = x + return p +} + +func (x OperatorVersionStatus) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (OperatorVersionStatus) Descriptor() protoreflect.EnumDescriptor { + return file_api_v1_operator_proto_enumTypes[3].Descriptor() +} + +func (OperatorVersionStatus) Type() protoreflect.EnumType { + return &file_api_v1_operator_proto_enumTypes[3] +} + +func (x OperatorVersionStatus) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use OperatorVersionStatus.Descriptor instead. +func (OperatorVersionStatus) EnumDescriptor() ([]byte, []int) { + return file_api_v1_operator_proto_rawDescGZIP(), []int{3} +} + // Operator describes basic details for any type of operator type Operator struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Type OperatorType `protobuf:"varint,1,opt,name=type,proto3,enum=api.v1.OperatorType" json:"type,omitempty"` - InstallMethod OperatorInstallMethod `protobuf:"varint,2,opt,name=install_method,json=installMethod,proto3,enum=api.v1.OperatorInstallMethod" json:"install_method,omitempty"` - State OperatorState `protobuf:"varint,3,opt,name=state,proto3,enum=api.v1.OperatorState" json:"state,omitempty"` - DisplayName string `protobuf:"bytes,4,opt,name=display_name,json=displayName,proto3" json:"display_name,omitempty"` - Description string `protobuf:"bytes,5,opt,name=description,proto3" json:"description,omitempty"` - Version string `protobuf:"bytes,6,opt,name=version,proto3" json:"version,omitempty"` - Commit string `protobuf:"bytes,7,opt,name=commit,proto3" json:"commit,omitempty"` + Type OperatorType `protobuf:"varint,1,opt,name=type,proto3,enum=api.v1.OperatorType" json:"type,omitempty"` + InstallMethod OperatorInstallMethod `protobuf:"varint,2,opt,name=install_method,json=installMethod,proto3,enum=api.v1.OperatorInstallMethod" json:"install_method,omitempty"` + State OperatorState `protobuf:"varint,3,opt,name=state,proto3,enum=api.v1.OperatorState" json:"state,omitempty"` + DisplayName string `protobuf:"bytes,4,opt,name=display_name,json=displayName,proto3" json:"display_name,omitempty"` + Description string `protobuf:"bytes,5,opt,name=description,proto3" json:"description,omitempty"` + Version string `protobuf:"bytes,6,opt,name=version,proto3" json:"version,omitempty"` + Commit string `protobuf:"bytes,7,opt,name=commit,proto3" json:"commit,omitempty"` + // True when version is below latest_available_version in SemVer order, + // including when it is also below the support floor (see version_status). IsOutdated bool `protobuf:"varint,8,opt,name=is_outdated,json=isOutdated,proto3" json:"is_outdated,omitempty"` LatestAvailableVersion string `protobuf:"bytes,9,opt,name=latest_available_version,json=latestAvailableVersion,proto3" json:"latest_available_version,omitempty"` ConnectedAt *timestamppb.Timestamp `protobuf:"bytes,10,opt,name=connected_at,json=connectedAt,proto3" json:"connected_at,omitempty"` + VersionStatus OperatorVersionStatus `protobuf:"varint,11,opt,name=version_status,json=versionStatus,proto3,enum=api.v1.OperatorVersionStatus" json:"version_status,omitempty"` + // The oldest supported version; below it version_status is UNSUPPORTED. + // Empty or 0.0.0 means every version is supported. + MinSupportedVersion string `protobuf:"bytes,12,opt,name=min_supported_version,json=minSupportedVersion,proto3" json:"min_supported_version,omitempty"` + // Release notes for latest_available_version, the version to upgrade to. + // Empty when dakr knows no notes page for the component. + ReleaseNotesUrl string `protobuf:"bytes,13,opt,name=release_notes_url,json=releaseNotesUrl,proto3" json:"release_notes_url,omitempty"` } func (x *Operator) Reset() { @@ -314,6 +385,27 @@ func (x *Operator) GetConnectedAt() *timestamppb.Timestamp { return nil } +func (x *Operator) GetVersionStatus() OperatorVersionStatus { + if x != nil { + return x.VersionStatus + } + return OperatorVersionStatus_OPERATOR_VERSION_STATUS_UNSPECIFIED +} + +func (x *Operator) GetMinSupportedVersion() string { + if x != nil { + return x.MinSupportedVersion + } + return "" +} + +func (x *Operator) GetReleaseNotesUrl() string { + if x != nil { + return x.ReleaseNotesUrl + } + return "" +} + // Request and response for GetOperators type GetOperatorsRequest struct { state protoimpl.MessageState @@ -1234,7 +1326,7 @@ var file_api_v1_operator_proto_rawDesc = []byte{ 0x72, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x06, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x1a, 0x1f, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, - 0x22, 0xb8, 0x03, 0x0a, 0x08, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x12, 0x28, 0x0a, + 0x22, 0xde, 0x04, 0x0a, 0x08, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x12, 0x28, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x14, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x54, 0x79, 0x70, 0x65, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x44, 0x0a, 0x0e, 0x69, 0x6e, 0x73, 0x74, 0x61, @@ -1261,213 +1353,235 @@ var file_api_v1_operator_proto_rawDesc = []byte{ 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0b, - 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x65, 0x64, 0x41, 0x74, 0x22, 0x15, 0x0a, 0x13, 0x47, - 0x65, 0x74, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x22, 0x46, 0x0a, 0x14, 0x47, 0x65, 0x74, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, - 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2e, 0x0a, 0x09, 0x6f, 0x70, - 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x10, 0x2e, - 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x52, - 0x09, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x73, 0x22, 0xad, 0x01, 0x0a, 0x10, 0x43, - 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x12, - 0x21, 0x0a, 0x0c, 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x4e, 0x61, - 0x6d, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, - 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, - 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1b, 0x0a, 0x09, 0x69, 0x73, 0x5f, 0x61, 0x63, 0x74, 0x69, 0x76, - 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x69, 0x73, 0x41, 0x63, 0x74, 0x69, 0x76, - 0x65, 0x12, 0x37, 0x0a, 0x09, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x73, 0x65, 0x65, 0x6e, 0x18, 0x04, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, - 0x52, 0x08, 0x6c, 0x61, 0x73, 0x74, 0x53, 0x65, 0x65, 0x6e, 0x22, 0x7e, 0x0a, 0x1a, 0x47, 0x65, - 0x74, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, - 0x61, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x28, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x14, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, - 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x54, 0x79, 0x70, 0x65, 0x52, 0x04, 0x74, 0x79, - 0x70, 0x65, 0x12, 0x17, 0x0a, 0x07, 0x74, 0x65, 0x61, 0x6d, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x06, 0x74, 0x65, 0x61, 0x6d, 0x49, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x63, - 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x09, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, 0x64, 0x22, 0xc8, 0x02, 0x0a, 0x0c, 0x52, - 0x65, 0x61, 0x64, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x12, 0x2c, 0x0a, 0x08, 0x6f, - 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, - 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x52, - 0x08, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x12, 0x45, 0x0a, 0x11, 0x63, 0x6f, 0x6e, - 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, - 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x52, 0x10, - 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, - 0x12, 0x3b, 0x0a, 0x0c, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, - 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, - 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, - 0x52, 0x0b, 0x6e, 0x6f, 0x64, 0x65, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x12, 0x43, 0x0a, - 0x10, 0x73, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, - 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, - 0x2e, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x4d, 0x65, 0x74, 0x72, 0x69, - 0x63, 0x52, 0x0f, 0x73, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x4d, 0x65, 0x74, 0x72, 0x69, - 0x63, 0x73, 0x12, 0x41, 0x0a, 0x0f, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x5f, 0x6d, 0x65, - 0x74, 0x72, 0x69, 0x63, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x61, 0x70, + 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x65, 0x64, 0x41, 0x74, 0x12, 0x44, 0x0a, 0x0e, 0x76, + 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x0b, 0x20, + 0x01, 0x28, 0x0e, 0x32, 0x1d, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x70, 0x65, + 0x72, 0x61, 0x74, 0x6f, 0x72, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x61, 0x74, + 0x75, 0x73, 0x52, 0x0d, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x75, + 0x73, 0x12, 0x32, 0x0a, 0x15, 0x6d, 0x69, 0x6e, 0x5f, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, + 0x65, 0x64, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x13, 0x6d, 0x69, 0x6e, 0x53, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x56, 0x65, + 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x2a, 0x0a, 0x11, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, + 0x5f, 0x6e, 0x6f, 0x74, 0x65, 0x73, 0x5f, 0x75, 0x72, 0x6c, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x0f, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x4e, 0x6f, 0x74, 0x65, 0x73, 0x55, 0x72, + 0x6c, 0x22, 0x15, 0x0a, 0x13, 0x47, 0x65, 0x74, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, + 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x46, 0x0a, 0x14, 0x47, 0x65, 0x74, 0x4f, + 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x12, 0x2e, 0x0a, 0x09, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x73, 0x18, 0x01, 0x20, + 0x03, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x70, 0x65, + 0x72, 0x61, 0x74, 0x6f, 0x72, 0x52, 0x09, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x73, + 0x22, 0xad, 0x01, 0x0a, 0x10, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x4d, + 0x65, 0x74, 0x72, 0x69, 0x63, 0x12, 0x21, 0x0a, 0x0c, 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, + 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x69, 0x73, + 0x70, 0x6c, 0x61, 0x79, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, + 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, + 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1b, 0x0a, 0x09, 0x69, 0x73, + 0x5f, 0x61, 0x63, 0x74, 0x69, 0x76, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x69, + 0x73, 0x41, 0x63, 0x74, 0x69, 0x76, 0x65, 0x12, 0x37, 0x0a, 0x09, 0x6c, 0x61, 0x73, 0x74, 0x5f, + 0x73, 0x65, 0x65, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, + 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, + 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x08, 0x6c, 0x61, 0x73, 0x74, 0x53, 0x65, 0x65, 0x6e, + 0x22, 0x7e, 0x0a, 0x1a, 0x47, 0x65, 0x74, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x4d, + 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x28, + 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x14, 0x2e, 0x61, + 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x54, 0x79, + 0x70, 0x65, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x17, 0x0a, 0x07, 0x74, 0x65, 0x61, 0x6d, + 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x74, 0x65, 0x61, 0x6d, 0x49, + 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, 0x64, + 0x22, 0xc8, 0x02, 0x0a, 0x0c, 0x52, 0x65, 0x61, 0x64, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, + 0x72, 0x12, 0x2c, 0x0a, 0x08, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x70, 0x65, + 0x72, 0x61, 0x74, 0x6f, 0x72, 0x52, 0x08, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x12, + 0x45, 0x0a, 0x11, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x6d, 0x65, 0x74, + 0x72, 0x69, 0x63, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x61, 0x70, 0x69, + 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x4d, 0x65, + 0x74, 0x72, 0x69, 0x63, 0x52, 0x10, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x4d, + 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x12, 0x3b, 0x0a, 0x0c, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x6d, + 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x61, + 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, + 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x52, 0x0b, 0x6e, 0x6f, 0x64, 0x65, 0x4d, 0x65, 0x74, 0x72, + 0x69, 0x63, 0x73, 0x12, 0x43, 0x0a, 0x10, 0x73, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x5f, + 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, + 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, + 0x6e, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x52, 0x0f, 0x73, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, + 0x74, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x12, 0x41, 0x0a, 0x0f, 0x73, 0x74, 0x6f, 0x72, + 0x61, 0x67, 0x65, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x18, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x6e, 0x65, + 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x52, 0x0e, 0x73, 0x74, 0x6f, + 0x72, 0x61, 0x67, 0x65, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x22, 0x8e, 0x01, 0x0a, 0x0d, + 0x57, 0x72, 0x69, 0x74, 0x65, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x12, 0x2c, 0x0a, + 0x08, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x10, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, + 0x72, 0x52, 0x08, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x12, 0x4f, 0x0a, 0x16, 0x72, + 0x65, 0x63, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6d, 0x65, + 0x74, 0x72, 0x69, 0x63, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x4d, - 0x65, 0x74, 0x72, 0x69, 0x63, 0x52, 0x0e, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x4d, 0x65, - 0x74, 0x72, 0x69, 0x63, 0x73, 0x22, 0x8e, 0x01, 0x0a, 0x0d, 0x57, 0x72, 0x69, 0x74, 0x65, 0x4f, - 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x12, 0x2c, 0x0a, 0x08, 0x6f, 0x70, 0x65, 0x72, 0x61, - 0x74, 0x6f, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x61, 0x70, 0x69, 0x2e, - 0x76, 0x31, 0x2e, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x52, 0x08, 0x6f, 0x70, 0x65, - 0x72, 0x61, 0x74, 0x6f, 0x72, 0x12, 0x4f, 0x0a, 0x16, 0x72, 0x65, 0x63, 0x6f, 0x6d, 0x6d, 0x65, - 0x6e, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x43, - 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x52, - 0x15, 0x72, 0x65, 0x63, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4d, - 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x22, 0xd0, 0x01, 0x0a, 0x0c, 0x4e, 0x6f, 0x64, 0x65, 0x4f, - 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x12, 0x2c, 0x0a, 0x08, 0x6f, 0x70, 0x65, 0x72, 0x61, - 0x74, 0x6f, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x61, 0x70, 0x69, 0x2e, - 0x76, 0x31, 0x2e, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x52, 0x08, 0x6f, 0x70, 0x65, - 0x72, 0x61, 0x74, 0x6f, 0x72, 0x12, 0x4b, 0x0a, 0x14, 0x6b, 0x61, 0x72, 0x70, 0x65, 0x6e, 0x74, - 0x65, 0x72, 0x5f, 0x64, 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x18, 0x02, 0x20, + 0x65, 0x74, 0x72, 0x69, 0x63, 0x52, 0x15, 0x72, 0x65, 0x63, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x64, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x22, 0xd0, 0x01, 0x0a, + 0x0c, 0x4e, 0x6f, 0x64, 0x65, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x12, 0x2c, 0x0a, + 0x08, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x10, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, + 0x72, 0x52, 0x08, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x12, 0x4b, 0x0a, 0x14, 0x6b, + 0x61, 0x72, 0x70, 0x65, 0x6e, 0x74, 0x65, 0x72, 0x5f, 0x64, 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x6d, + 0x65, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x61, 0x70, 0x69, 0x2e, + 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x4d, 0x65, 0x74, + 0x72, 0x69, 0x63, 0x52, 0x13, 0x6b, 0x61, 0x72, 0x70, 0x65, 0x6e, 0x74, 0x65, 0x72, 0x44, 0x65, + 0x70, 0x6c, 0x6f, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x45, 0x0a, 0x11, 0x6e, 0x6f, 0x64, 0x65, + 0x5f, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x69, 0x6e, 0x67, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6e, - 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x52, 0x13, 0x6b, - 0x61, 0x72, 0x70, 0x65, 0x6e, 0x74, 0x65, 0x72, 0x44, 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x6d, 0x65, - 0x6e, 0x74, 0x12, 0x45, 0x0a, 0x11, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x70, 0x72, 0x6f, 0x76, 0x69, - 0x73, 0x69, 0x6f, 0x6e, 0x69, 0x6e, 0x67, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, - 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, - 0x6e, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x52, 0x10, 0x6e, 0x6f, 0x64, 0x65, 0x50, 0x72, 0x6f, - 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x69, 0x6e, 0x67, 0x22, 0x85, 0x01, 0x0a, 0x10, 0x53, 0x65, - 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x12, 0x2c, - 0x0a, 0x08, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x10, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, - 0x6f, 0x72, 0x52, 0x08, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x12, 0x43, 0x0a, 0x10, - 0x73, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, - 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, - 0x52, 0x0f, 0x73, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, - 0x73, 0x22, 0x82, 0x01, 0x0a, 0x0f, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x4f, 0x70, 0x65, - 0x72, 0x61, 0x74, 0x6f, 0x72, 0x12, 0x2c, 0x0a, 0x08, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, - 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, - 0x2e, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x52, 0x08, 0x6f, 0x70, 0x65, 0x72, 0x61, - 0x74, 0x6f, 0x72, 0x12, 0x41, 0x0a, 0x0f, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x5f, 0x6d, + 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x52, 0x10, 0x6e, + 0x6f, 0x64, 0x65, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x69, 0x6e, 0x67, 0x22, + 0x85, 0x01, 0x0a, 0x10, 0x53, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x4f, 0x70, 0x65, 0x72, + 0x61, 0x74, 0x6f, 0x72, 0x12, 0x2c, 0x0a, 0x08, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, + 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x52, 0x08, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, + 0x6f, 0x72, 0x12, 0x43, 0x0a, 0x10, 0x73, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, - 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x52, 0x0e, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x4d, - 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x22, 0xd8, 0x01, 0x0a, 0x0c, 0x43, 0x6f, 0x73, 0x74, 0x4f, - 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x12, 0x2c, 0x0a, 0x08, 0x6f, 0x70, 0x65, 0x72, 0x61, - 0x74, 0x6f, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x61, 0x70, 0x69, 0x2e, - 0x76, 0x31, 0x2e, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x52, 0x08, 0x6f, 0x70, 0x65, - 0x72, 0x61, 0x74, 0x6f, 0x72, 0x12, 0x49, 0x0a, 0x13, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, - 0x65, 0x5f, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, + 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x52, 0x0f, 0x73, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, + 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x22, 0x82, 0x01, 0x0a, 0x0f, 0x4e, 0x65, 0x74, 0x77, + 0x6f, 0x72, 0x6b, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x12, 0x2c, 0x0a, 0x08, 0x6f, + 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, + 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x52, + 0x08, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x12, 0x41, 0x0a, 0x0f, 0x6e, 0x65, 0x74, + 0x77, 0x6f, 0x72, 0x6b, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x6e, - 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x52, 0x12, 0x69, 0x6e, - 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x43, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, - 0x12, 0x4f, 0x0a, 0x16, 0x72, 0x65, 0x73, 0x65, 0x72, 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, - 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x18, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, - 0x74, 0x69, 0x6f, 0x6e, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x52, 0x15, 0x72, 0x65, 0x73, 0x65, - 0x72, 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x43, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, - 0x6e, 0x22, 0xaf, 0x03, 0x0a, 0x1b, 0x47, 0x65, 0x74, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, - 0x72, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x12, 0x3b, 0x0a, 0x0d, 0x72, 0x65, 0x61, 0x64, 0x5f, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, - 0x6f, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, - 0x31, 0x2e, 0x52, 0x65, 0x61, 0x64, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x48, 0x00, - 0x52, 0x0c, 0x72, 0x65, 0x61, 0x64, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x12, 0x3e, - 0x0a, 0x0e, 0x77, 0x72, 0x69, 0x74, 0x65, 0x5f, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, - 0x57, 0x72, 0x69, 0x74, 0x65, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x48, 0x00, 0x52, - 0x0d, 0x77, 0x72, 0x69, 0x74, 0x65, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x12, 0x3b, - 0x0a, 0x0d, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x18, - 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4e, - 0x6f, 0x64, 0x65, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x48, 0x00, 0x52, 0x0c, 0x6e, - 0x6f, 0x64, 0x65, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x12, 0x47, 0x0a, 0x11, 0x73, - 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x5f, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, - 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, - 0x53, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, - 0x48, 0x00, 0x52, 0x10, 0x73, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x4f, 0x70, 0x65, 0x72, - 0x61, 0x74, 0x6f, 0x72, 0x12, 0x44, 0x0a, 0x10, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x5f, - 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, - 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x4f, - 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x48, 0x00, 0x52, 0x0f, 0x6e, 0x65, 0x74, 0x77, 0x6f, - 0x72, 0x6b, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x12, 0x3b, 0x0a, 0x0d, 0x63, 0x6f, - 0x73, 0x74, 0x5f, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x18, 0x06, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x14, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x73, 0x74, 0x4f, - 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x48, 0x00, 0x52, 0x0c, 0x63, 0x6f, 0x73, 0x74, 0x4f, - 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x42, 0x0a, 0x0a, 0x08, 0x6f, 0x70, 0x65, 0x72, 0x61, - 0x74, 0x6f, 0x72, 0x22, 0x51, 0x0a, 0x17, 0x47, 0x65, 0x74, 0x4f, 0x43, 0x49, 0x52, 0x65, 0x67, - 0x69, 0x73, 0x74, 0x72, 0x69, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x17, - 0x0a, 0x07, 0x74, 0x65, 0x61, 0x6d, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x06, 0x74, 0x65, 0x61, 0x6d, 0x49, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x6c, 0x75, 0x73, 0x74, - 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x6c, 0x75, - 0x73, 0x74, 0x65, 0x72, 0x49, 0x64, 0x22, 0x91, 0x01, 0x0a, 0x14, 0x52, 0x65, 0x67, 0x69, 0x73, - 0x74, 0x72, 0x79, 0x4d, 0x69, 0x72, 0x72, 0x6f, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, - 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, - 0x61, 0x6d, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x1a, 0x0a, - 0x08, 0x75, 0x70, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x08, 0x75, 0x70, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x12, 0x1d, 0x0a, 0x0a, 0x6d, 0x69, 0x72, - 0x72, 0x6f, 0x72, 0x5f, 0x66, 0x6f, 0x72, 0x18, 0x04, 0x20, 0x03, 0x28, 0x09, 0x52, 0x09, 0x6d, - 0x69, 0x72, 0x72, 0x6f, 0x72, 0x46, 0x6f, 0x72, 0x12, 0x10, 0x0a, 0x03, 0x74, 0x74, 0x6c, 0x18, - 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x74, 0x74, 0x6c, 0x22, 0x63, 0x0a, 0x18, 0x47, 0x65, + 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x52, 0x0e, 0x6e, 0x65, + 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x22, 0xd8, 0x01, 0x0a, + 0x0c, 0x43, 0x6f, 0x73, 0x74, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x12, 0x2c, 0x0a, + 0x08, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x10, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, + 0x72, 0x52, 0x08, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x12, 0x49, 0x0a, 0x13, 0x69, + 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x5f, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, + 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, + 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x4d, 0x65, 0x74, 0x72, + 0x69, 0x63, 0x52, 0x12, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x43, 0x6f, 0x6c, 0x6c, + 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x4f, 0x0a, 0x16, 0x72, 0x65, 0x73, 0x65, 0x72, 0x76, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, + 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, + 0x52, 0x15, 0x72, 0x65, 0x73, 0x65, 0x72, 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x43, 0x6f, 0x6c, + 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0xaf, 0x03, 0x0a, 0x1b, 0x47, 0x65, 0x74, 0x4f, + 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3b, 0x0a, 0x0d, 0x72, 0x65, 0x61, 0x64, 0x5f, + 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, + 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x61, 0x64, 0x4f, 0x70, 0x65, 0x72, + 0x61, 0x74, 0x6f, 0x72, 0x48, 0x00, 0x52, 0x0c, 0x72, 0x65, 0x61, 0x64, 0x4f, 0x70, 0x65, 0x72, + 0x61, 0x74, 0x6f, 0x72, 0x12, 0x3e, 0x0a, 0x0e, 0x77, 0x72, 0x69, 0x74, 0x65, 0x5f, 0x6f, 0x70, + 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x61, + 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x57, 0x72, 0x69, 0x74, 0x65, 0x4f, 0x70, 0x65, 0x72, 0x61, + 0x74, 0x6f, 0x72, 0x48, 0x00, 0x52, 0x0d, 0x77, 0x72, 0x69, 0x74, 0x65, 0x4f, 0x70, 0x65, 0x72, + 0x61, 0x74, 0x6f, 0x72, 0x12, 0x3b, 0x0a, 0x0d, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x6f, 0x70, 0x65, + 0x72, 0x61, 0x74, 0x6f, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x61, 0x70, + 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, + 0x72, 0x48, 0x00, 0x52, 0x0c, 0x6e, 0x6f, 0x64, 0x65, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, + 0x72, 0x12, 0x47, 0x0a, 0x11, 0x73, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x5f, 0x6f, 0x70, + 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x61, + 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x4f, 0x70, + 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x48, 0x00, 0x52, 0x10, 0x73, 0x65, 0x63, 0x75, 0x72, 0x69, + 0x74, 0x79, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x12, 0x44, 0x0a, 0x10, 0x6e, 0x65, + 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x5f, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x18, 0x05, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4e, 0x65, + 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x48, 0x00, 0x52, + 0x0f, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, + 0x12, 0x3b, 0x0a, 0x0d, 0x63, 0x6f, 0x73, 0x74, 0x5f, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, + 0x72, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, + 0x2e, 0x43, 0x6f, 0x73, 0x74, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x48, 0x00, 0x52, + 0x0c, 0x63, 0x6f, 0x73, 0x74, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x42, 0x0a, 0x0a, + 0x08, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x22, 0x51, 0x0a, 0x17, 0x47, 0x65, 0x74, + 0x4f, 0x43, 0x49, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x69, 0x65, 0x73, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x12, 0x17, 0x0a, 0x07, 0x74, 0x65, 0x61, 0x6d, 0x5f, 0x69, 0x64, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x74, 0x65, 0x61, 0x6d, 0x49, 0x64, 0x12, 0x1d, 0x0a, + 0x0a, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x09, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, 0x64, 0x22, 0x91, 0x01, 0x0a, + 0x14, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x4d, 0x69, 0x72, 0x72, 0x6f, 0x72, 0x43, + 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x65, 0x6e, 0x61, + 0x62, 0x6c, 0x65, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x65, 0x6e, 0x61, 0x62, + 0x6c, 0x65, 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x75, 0x70, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x75, 0x70, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x12, + 0x1d, 0x0a, 0x0a, 0x6d, 0x69, 0x72, 0x72, 0x6f, 0x72, 0x5f, 0x66, 0x6f, 0x72, 0x18, 0x04, 0x20, + 0x03, 0x28, 0x09, 0x52, 0x09, 0x6d, 0x69, 0x72, 0x72, 0x6f, 0x72, 0x46, 0x6f, 0x72, 0x12, 0x10, + 0x0a, 0x03, 0x74, 0x74, 0x6c, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x74, 0x74, 0x6c, + 0x22, 0x63, 0x0a, 0x18, 0x47, 0x65, 0x74, 0x4f, 0x43, 0x49, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, + 0x72, 0x69, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x47, 0x0a, 0x10, + 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x73, + 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, + 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x4d, 0x69, 0x72, 0x72, 0x6f, 0x72, 0x43, 0x6f, + 0x6e, 0x66, 0x69, 0x67, 0x52, 0x0f, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x43, 0x6f, + 0x6e, 0x66, 0x69, 0x67, 0x73, 0x2a, 0xe1, 0x01, 0x0a, 0x0c, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, + 0x6f, 0x72, 0x54, 0x79, 0x70, 0x65, 0x12, 0x1d, 0x0a, 0x19, 0x4f, 0x50, 0x45, 0x52, 0x41, 0x54, + 0x4f, 0x52, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, + 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x16, 0x0a, 0x12, 0x4f, 0x50, 0x45, 0x52, 0x41, 0x54, 0x4f, + 0x52, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x52, 0x45, 0x41, 0x44, 0x10, 0x01, 0x12, 0x17, 0x0a, + 0x13, 0x4f, 0x50, 0x45, 0x52, 0x41, 0x54, 0x4f, 0x52, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x57, + 0x52, 0x49, 0x54, 0x45, 0x10, 0x02, 0x12, 0x16, 0x0a, 0x12, 0x4f, 0x50, 0x45, 0x52, 0x41, 0x54, + 0x4f, 0x52, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x4e, 0x4f, 0x44, 0x45, 0x10, 0x03, 0x12, 0x1a, + 0x0a, 0x16, 0x4f, 0x50, 0x45, 0x52, 0x41, 0x54, 0x4f, 0x52, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, + 0x53, 0x45, 0x43, 0x55, 0x52, 0x49, 0x54, 0x59, 0x10, 0x04, 0x12, 0x19, 0x0a, 0x15, 0x4f, 0x50, + 0x45, 0x52, 0x41, 0x54, 0x4f, 0x52, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x4e, 0x45, 0x54, 0x57, + 0x4f, 0x52, 0x4b, 0x10, 0x05, 0x12, 0x16, 0x0a, 0x12, 0x4f, 0x50, 0x45, 0x52, 0x41, 0x54, 0x4f, + 0x52, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x43, 0x4f, 0x53, 0x54, 0x10, 0x06, 0x12, 0x1a, 0x0a, + 0x16, 0x4f, 0x50, 0x45, 0x52, 0x41, 0x54, 0x4f, 0x52, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x53, + 0x4e, 0x41, 0x50, 0x53, 0x48, 0x4f, 0x54, 0x10, 0x07, 0x2a, 0xa3, 0x01, 0x0a, 0x0d, 0x4f, 0x70, + 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x1e, 0x0a, 0x1a, 0x4f, + 0x50, 0x45, 0x52, 0x41, 0x54, 0x4f, 0x52, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x45, 0x5f, 0x55, 0x4e, + 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x1b, 0x0a, 0x17, 0x4f, + 0x50, 0x45, 0x52, 0x41, 0x54, 0x4f, 0x52, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x45, 0x5f, 0x44, 0x49, + 0x53, 0x41, 0x42, 0x4c, 0x45, 0x44, 0x10, 0x01, 0x12, 0x1e, 0x0a, 0x1a, 0x4f, 0x50, 0x45, 0x52, + 0x41, 0x54, 0x4f, 0x52, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x45, 0x5f, 0x4f, 0x50, 0x45, 0x52, 0x41, + 0x54, 0x49, 0x4f, 0x4e, 0x41, 0x4c, 0x10, 0x02, 0x12, 0x1b, 0x0a, 0x17, 0x4f, 0x50, 0x45, 0x52, + 0x41, 0x54, 0x4f, 0x52, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x45, 0x5f, 0x44, 0x45, 0x47, 0x52, 0x41, + 0x44, 0x45, 0x44, 0x10, 0x03, 0x12, 0x18, 0x0a, 0x14, 0x4f, 0x50, 0x45, 0x52, 0x41, 0x54, 0x4f, + 0x52, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x45, 0x5f, 0x45, 0x52, 0x52, 0x4f, 0x52, 0x10, 0x04, 0x2a, + 0x88, 0x01, 0x0a, 0x15, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x49, 0x6e, 0x73, 0x74, + 0x61, 0x6c, 0x6c, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x12, 0x27, 0x0a, 0x23, 0x4f, 0x50, 0x45, + 0x52, 0x41, 0x54, 0x4f, 0x52, 0x5f, 0x49, 0x4e, 0x53, 0x54, 0x41, 0x4c, 0x4c, 0x5f, 0x4d, 0x45, + 0x54, 0x48, 0x4f, 0x44, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, + 0x10, 0x00, 0x12, 0x24, 0x0a, 0x20, 0x4f, 0x50, 0x45, 0x52, 0x41, 0x54, 0x4f, 0x52, 0x5f, 0x49, + 0x4e, 0x53, 0x54, 0x41, 0x4c, 0x4c, 0x5f, 0x4d, 0x45, 0x54, 0x48, 0x4f, 0x44, 0x5f, 0x4d, 0x41, + 0x4e, 0x49, 0x46, 0x45, 0x53, 0x54, 0x10, 0x01, 0x12, 0x20, 0x0a, 0x1c, 0x4f, 0x50, 0x45, 0x52, + 0x41, 0x54, 0x4f, 0x52, 0x5f, 0x49, 0x4e, 0x53, 0x54, 0x41, 0x4c, 0x4c, 0x5f, 0x4d, 0x45, 0x54, + 0x48, 0x4f, 0x44, 0x5f, 0x48, 0x45, 0x4c, 0x4d, 0x10, 0x02, 0x2a, 0xb4, 0x01, 0x0a, 0x15, 0x4f, + 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x53, 0x74, + 0x61, 0x74, 0x75, 0x73, 0x12, 0x27, 0x0a, 0x23, 0x4f, 0x50, 0x45, 0x52, 0x41, 0x54, 0x4f, 0x52, + 0x5f, 0x56, 0x45, 0x52, 0x53, 0x49, 0x4f, 0x4e, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, + 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x23, 0x0a, + 0x1f, 0x4f, 0x50, 0x45, 0x52, 0x41, 0x54, 0x4f, 0x52, 0x5f, 0x56, 0x45, 0x52, 0x53, 0x49, 0x4f, + 0x4e, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x43, 0x55, 0x52, 0x52, 0x45, 0x4e, 0x54, + 0x10, 0x01, 0x12, 0x24, 0x0a, 0x20, 0x4f, 0x50, 0x45, 0x52, 0x41, 0x54, 0x4f, 0x52, 0x5f, 0x56, + 0x45, 0x52, 0x53, 0x49, 0x4f, 0x4e, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x4f, 0x55, + 0x54, 0x44, 0x41, 0x54, 0x45, 0x44, 0x10, 0x02, 0x12, 0x27, 0x0a, 0x23, 0x4f, 0x50, 0x45, 0x52, + 0x41, 0x54, 0x4f, 0x52, 0x5f, 0x56, 0x45, 0x52, 0x53, 0x49, 0x4f, 0x4e, 0x5f, 0x53, 0x54, 0x41, + 0x54, 0x55, 0x53, 0x5f, 0x55, 0x4e, 0x53, 0x55, 0x50, 0x50, 0x4f, 0x52, 0x54, 0x45, 0x44, 0x10, + 0x03, 0x32, 0x93, 0x02, 0x0a, 0x0f, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x53, 0x65, + 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x49, 0x0a, 0x0c, 0x47, 0x65, 0x74, 0x4f, 0x70, 0x65, 0x72, + 0x61, 0x74, 0x6f, 0x72, 0x73, 0x12, 0x1b, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, + 0x65, 0x74, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x4f, + 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x12, 0x5e, 0x0a, 0x13, 0x47, 0x65, 0x74, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x4d, + 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x22, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, + 0x2e, 0x47, 0x65, 0x74, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x4d, 0x65, 0x74, 0x61, + 0x64, 0x61, 0x74, 0x61, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x61, 0x70, + 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, + 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x12, 0x55, 0x0a, 0x10, 0x47, 0x65, 0x74, 0x4f, 0x43, 0x49, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, + 0x72, 0x69, 0x65, 0x73, 0x12, 0x1f, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x4f, 0x43, 0x49, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x69, 0x65, 0x73, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x47, 0x0a, 0x10, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, - 0x72, 0x79, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, - 0x32, 0x1c, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, - 0x72, 0x79, 0x4d, 0x69, 0x72, 0x72, 0x6f, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x0f, - 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x73, 0x2a, - 0xe1, 0x01, 0x0a, 0x0c, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x54, 0x79, 0x70, 0x65, - 0x12, 0x1d, 0x0a, 0x19, 0x4f, 0x50, 0x45, 0x52, 0x41, 0x54, 0x4f, 0x52, 0x5f, 0x54, 0x59, 0x50, - 0x45, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, - 0x16, 0x0a, 0x12, 0x4f, 0x50, 0x45, 0x52, 0x41, 0x54, 0x4f, 0x52, 0x5f, 0x54, 0x59, 0x50, 0x45, - 0x5f, 0x52, 0x45, 0x41, 0x44, 0x10, 0x01, 0x12, 0x17, 0x0a, 0x13, 0x4f, 0x50, 0x45, 0x52, 0x41, - 0x54, 0x4f, 0x52, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x57, 0x52, 0x49, 0x54, 0x45, 0x10, 0x02, - 0x12, 0x16, 0x0a, 0x12, 0x4f, 0x50, 0x45, 0x52, 0x41, 0x54, 0x4f, 0x52, 0x5f, 0x54, 0x59, 0x50, - 0x45, 0x5f, 0x4e, 0x4f, 0x44, 0x45, 0x10, 0x03, 0x12, 0x1a, 0x0a, 0x16, 0x4f, 0x50, 0x45, 0x52, - 0x41, 0x54, 0x4f, 0x52, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x53, 0x45, 0x43, 0x55, 0x52, 0x49, - 0x54, 0x59, 0x10, 0x04, 0x12, 0x19, 0x0a, 0x15, 0x4f, 0x50, 0x45, 0x52, 0x41, 0x54, 0x4f, 0x52, - 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x4e, 0x45, 0x54, 0x57, 0x4f, 0x52, 0x4b, 0x10, 0x05, 0x12, - 0x16, 0x0a, 0x12, 0x4f, 0x50, 0x45, 0x52, 0x41, 0x54, 0x4f, 0x52, 0x5f, 0x54, 0x59, 0x50, 0x45, - 0x5f, 0x43, 0x4f, 0x53, 0x54, 0x10, 0x06, 0x12, 0x1a, 0x0a, 0x16, 0x4f, 0x50, 0x45, 0x52, 0x41, - 0x54, 0x4f, 0x52, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x53, 0x4e, 0x41, 0x50, 0x53, 0x48, 0x4f, - 0x54, 0x10, 0x07, 0x2a, 0xa3, 0x01, 0x0a, 0x0d, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, - 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x1e, 0x0a, 0x1a, 0x4f, 0x50, 0x45, 0x52, 0x41, 0x54, 0x4f, - 0x52, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x45, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, - 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x1b, 0x0a, 0x17, 0x4f, 0x50, 0x45, 0x52, 0x41, 0x54, 0x4f, - 0x52, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x45, 0x5f, 0x44, 0x49, 0x53, 0x41, 0x42, 0x4c, 0x45, 0x44, - 0x10, 0x01, 0x12, 0x1e, 0x0a, 0x1a, 0x4f, 0x50, 0x45, 0x52, 0x41, 0x54, 0x4f, 0x52, 0x5f, 0x53, - 0x54, 0x41, 0x54, 0x45, 0x5f, 0x4f, 0x50, 0x45, 0x52, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x41, 0x4c, - 0x10, 0x02, 0x12, 0x1b, 0x0a, 0x17, 0x4f, 0x50, 0x45, 0x52, 0x41, 0x54, 0x4f, 0x52, 0x5f, 0x53, - 0x54, 0x41, 0x54, 0x45, 0x5f, 0x44, 0x45, 0x47, 0x52, 0x41, 0x44, 0x45, 0x44, 0x10, 0x03, 0x12, - 0x18, 0x0a, 0x14, 0x4f, 0x50, 0x45, 0x52, 0x41, 0x54, 0x4f, 0x52, 0x5f, 0x53, 0x54, 0x41, 0x54, - 0x45, 0x5f, 0x45, 0x52, 0x52, 0x4f, 0x52, 0x10, 0x04, 0x2a, 0x88, 0x01, 0x0a, 0x15, 0x4f, 0x70, - 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x4d, 0x65, 0x74, - 0x68, 0x6f, 0x64, 0x12, 0x27, 0x0a, 0x23, 0x4f, 0x50, 0x45, 0x52, 0x41, 0x54, 0x4f, 0x52, 0x5f, - 0x49, 0x4e, 0x53, 0x54, 0x41, 0x4c, 0x4c, 0x5f, 0x4d, 0x45, 0x54, 0x48, 0x4f, 0x44, 0x5f, 0x55, - 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x24, 0x0a, 0x20, - 0x4f, 0x50, 0x45, 0x52, 0x41, 0x54, 0x4f, 0x52, 0x5f, 0x49, 0x4e, 0x53, 0x54, 0x41, 0x4c, 0x4c, - 0x5f, 0x4d, 0x45, 0x54, 0x48, 0x4f, 0x44, 0x5f, 0x4d, 0x41, 0x4e, 0x49, 0x46, 0x45, 0x53, 0x54, - 0x10, 0x01, 0x12, 0x20, 0x0a, 0x1c, 0x4f, 0x50, 0x45, 0x52, 0x41, 0x54, 0x4f, 0x52, 0x5f, 0x49, - 0x4e, 0x53, 0x54, 0x41, 0x4c, 0x4c, 0x5f, 0x4d, 0x45, 0x54, 0x48, 0x4f, 0x44, 0x5f, 0x48, 0x45, - 0x4c, 0x4d, 0x10, 0x02, 0x32, 0x93, 0x02, 0x0a, 0x0f, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, - 0x72, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x49, 0x0a, 0x0c, 0x47, 0x65, 0x74, 0x4f, - 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x73, 0x12, 0x1b, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, - 0x31, 0x2e, 0x47, 0x65, 0x74, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x73, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, - 0x65, 0x74, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x12, 0x5e, 0x0a, 0x13, 0x47, 0x65, 0x74, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, - 0x6f, 0x72, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x22, 0x2e, 0x61, 0x70, 0x69, - 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x4d, - 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, - 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x4f, 0x70, 0x65, 0x72, 0x61, - 0x74, 0x6f, 0x72, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x12, 0x55, 0x0a, 0x10, 0x47, 0x65, 0x74, 0x4f, 0x43, 0x49, 0x52, 0x65, 0x67, - 0x69, 0x73, 0x74, 0x72, 0x69, 0x65, 0x73, 0x12, 0x1f, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, - 0x2e, 0x47, 0x65, 0x74, 0x4f, 0x43, 0x49, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x69, 0x65, - 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, - 0x31, 0x2e, 0x47, 0x65, 0x74, 0x4f, 0x43, 0x49, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x69, - 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x86, 0x01, 0x0a, 0x0a, 0x63, - 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x42, 0x0d, 0x4f, 0x70, 0x65, 0x72, 0x61, - 0x74, 0x6f, 0x72, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x30, 0x67, 0x69, 0x74, 0x68, - 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x64, 0x65, 0x76, 0x7a, 0x65, 0x72, 0x6f, 0x2d, 0x69, - 0x6e, 0x63, 0x2f, 0x7a, 0x78, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x72, 0x2f, 0x67, 0x65, 0x6e, 0x2f, - 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x3b, 0x61, 0x70, 0x69, 0x76, 0x31, 0xa2, 0x02, 0x03, 0x41, - 0x58, 0x58, 0xaa, 0x02, 0x06, 0x41, 0x70, 0x69, 0x2e, 0x56, 0x31, 0xca, 0x02, 0x06, 0x41, 0x70, - 0x69, 0x5c, 0x56, 0x31, 0xe2, 0x02, 0x12, 0x41, 0x70, 0x69, 0x5c, 0x56, 0x31, 0x5c, 0x47, 0x50, - 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x07, 0x41, 0x70, 0x69, 0x3a, - 0x3a, 0x56, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, + 0x65, 0x74, 0x4f, 0x43, 0x49, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x69, 0x65, 0x73, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x8b, 0x01, 0x0a, 0x0a, 0x63, 0x6f, 0x6d, 0x2e, + 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x42, 0x0d, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, + 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x35, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, + 0x63, 0x6f, 0x6d, 0x2f, 0x64, 0x65, 0x76, 0x7a, 0x65, 0x72, 0x6f, 0x2d, 0x69, 0x6e, 0x63, 0x2f, + 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2f, 0x64, 0x61, 0x6b, 0x72, 0x2f, 0x67, 0x65, + 0x6e, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x3b, 0x61, 0x70, 0x69, 0x76, 0x31, 0xa2, 0x02, + 0x03, 0x41, 0x58, 0x58, 0xaa, 0x02, 0x06, 0x41, 0x70, 0x69, 0x2e, 0x56, 0x31, 0xca, 0x02, 0x06, + 0x41, 0x70, 0x69, 0x5c, 0x56, 0x31, 0xe2, 0x02, 0x12, 0x41, 0x70, 0x69, 0x5c, 0x56, 0x31, 0x5c, + 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x07, 0x41, 0x70, + 0x69, 0x3a, 0x3a, 0x56, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -1482,72 +1596,74 @@ func file_api_v1_operator_proto_rawDescGZIP() []byte { return file_api_v1_operator_proto_rawDescData } -var file_api_v1_operator_proto_enumTypes = make([]protoimpl.EnumInfo, 3) +var file_api_v1_operator_proto_enumTypes = make([]protoimpl.EnumInfo, 4) var file_api_v1_operator_proto_msgTypes = make([]protoimpl.MessageInfo, 15) var file_api_v1_operator_proto_goTypes = []interface{}{ (OperatorType)(0), // 0: api.v1.OperatorType (OperatorState)(0), // 1: api.v1.OperatorState (OperatorInstallMethod)(0), // 2: api.v1.OperatorInstallMethod - (*Operator)(nil), // 3: api.v1.Operator - (*GetOperatorsRequest)(nil), // 4: api.v1.GetOperatorsRequest - (*GetOperatorsResponse)(nil), // 5: api.v1.GetOperatorsResponse - (*ConnectionMetric)(nil), // 6: api.v1.ConnectionMetric - (*GetOperatorMetadataRequest)(nil), // 7: api.v1.GetOperatorMetadataRequest - (*ReadOperator)(nil), // 8: api.v1.ReadOperator - (*WriteOperator)(nil), // 9: api.v1.WriteOperator - (*NodeOperator)(nil), // 10: api.v1.NodeOperator - (*SecurityOperator)(nil), // 11: api.v1.SecurityOperator - (*NetworkOperator)(nil), // 12: api.v1.NetworkOperator - (*CostOperator)(nil), // 13: api.v1.CostOperator - (*GetOperatorMetadataResponse)(nil), // 14: api.v1.GetOperatorMetadataResponse - (*GetOCIRegistriesRequest)(nil), // 15: api.v1.GetOCIRegistriesRequest - (*RegistryMirrorConfig)(nil), // 16: api.v1.RegistryMirrorConfig - (*GetOCIRegistriesResponse)(nil), // 17: api.v1.GetOCIRegistriesResponse - (*timestamppb.Timestamp)(nil), // 18: google.protobuf.Timestamp + (OperatorVersionStatus)(0), // 3: api.v1.OperatorVersionStatus + (*Operator)(nil), // 4: api.v1.Operator + (*GetOperatorsRequest)(nil), // 5: api.v1.GetOperatorsRequest + (*GetOperatorsResponse)(nil), // 6: api.v1.GetOperatorsResponse + (*ConnectionMetric)(nil), // 7: api.v1.ConnectionMetric + (*GetOperatorMetadataRequest)(nil), // 8: api.v1.GetOperatorMetadataRequest + (*ReadOperator)(nil), // 9: api.v1.ReadOperator + (*WriteOperator)(nil), // 10: api.v1.WriteOperator + (*NodeOperator)(nil), // 11: api.v1.NodeOperator + (*SecurityOperator)(nil), // 12: api.v1.SecurityOperator + (*NetworkOperator)(nil), // 13: api.v1.NetworkOperator + (*CostOperator)(nil), // 14: api.v1.CostOperator + (*GetOperatorMetadataResponse)(nil), // 15: api.v1.GetOperatorMetadataResponse + (*GetOCIRegistriesRequest)(nil), // 16: api.v1.GetOCIRegistriesRequest + (*RegistryMirrorConfig)(nil), // 17: api.v1.RegistryMirrorConfig + (*GetOCIRegistriesResponse)(nil), // 18: api.v1.GetOCIRegistriesResponse + (*timestamppb.Timestamp)(nil), // 19: google.protobuf.Timestamp } var file_api_v1_operator_proto_depIdxs = []int32{ 0, // 0: api.v1.Operator.type:type_name -> api.v1.OperatorType 2, // 1: api.v1.Operator.install_method:type_name -> api.v1.OperatorInstallMethod 1, // 2: api.v1.Operator.state:type_name -> api.v1.OperatorState - 18, // 3: api.v1.Operator.connected_at:type_name -> google.protobuf.Timestamp - 3, // 4: api.v1.GetOperatorsResponse.operators:type_name -> api.v1.Operator - 18, // 5: api.v1.ConnectionMetric.last_seen:type_name -> google.protobuf.Timestamp - 0, // 6: api.v1.GetOperatorMetadataRequest.type:type_name -> api.v1.OperatorType - 3, // 7: api.v1.ReadOperator.operator:type_name -> api.v1.Operator - 6, // 8: api.v1.ReadOperator.container_metrics:type_name -> api.v1.ConnectionMetric - 6, // 9: api.v1.ReadOperator.node_metrics:type_name -> api.v1.ConnectionMetric - 6, // 10: api.v1.ReadOperator.snapshot_metrics:type_name -> api.v1.ConnectionMetric - 6, // 11: api.v1.ReadOperator.storage_metrics:type_name -> api.v1.ConnectionMetric - 3, // 12: api.v1.WriteOperator.operator:type_name -> api.v1.Operator - 6, // 13: api.v1.WriteOperator.recommendation_metrics:type_name -> api.v1.ConnectionMetric - 3, // 14: api.v1.NodeOperator.operator:type_name -> api.v1.Operator - 6, // 15: api.v1.NodeOperator.karpenter_deployment:type_name -> api.v1.ConnectionMetric - 6, // 16: api.v1.NodeOperator.node_provisioning:type_name -> api.v1.ConnectionMetric - 3, // 17: api.v1.SecurityOperator.operator:type_name -> api.v1.Operator - 6, // 18: api.v1.SecurityOperator.security_metrics:type_name -> api.v1.ConnectionMetric - 3, // 19: api.v1.NetworkOperator.operator:type_name -> api.v1.Operator - 6, // 20: api.v1.NetworkOperator.network_metrics:type_name -> api.v1.ConnectionMetric - 3, // 21: api.v1.CostOperator.operator:type_name -> api.v1.Operator - 6, // 22: api.v1.CostOperator.instance_collection:type_name -> api.v1.ConnectionMetric - 6, // 23: api.v1.CostOperator.reservation_collection:type_name -> api.v1.ConnectionMetric - 8, // 24: api.v1.GetOperatorMetadataResponse.read_operator:type_name -> api.v1.ReadOperator - 9, // 25: api.v1.GetOperatorMetadataResponse.write_operator:type_name -> api.v1.WriteOperator - 10, // 26: api.v1.GetOperatorMetadataResponse.node_operator:type_name -> api.v1.NodeOperator - 11, // 27: api.v1.GetOperatorMetadataResponse.security_operator:type_name -> api.v1.SecurityOperator - 12, // 28: api.v1.GetOperatorMetadataResponse.network_operator:type_name -> api.v1.NetworkOperator - 13, // 29: api.v1.GetOperatorMetadataResponse.cost_operator:type_name -> api.v1.CostOperator - 16, // 30: api.v1.GetOCIRegistriesResponse.registry_configs:type_name -> api.v1.RegistryMirrorConfig - 4, // 31: api.v1.OperatorService.GetOperators:input_type -> api.v1.GetOperatorsRequest - 7, // 32: api.v1.OperatorService.GetOperatorMetadata:input_type -> api.v1.GetOperatorMetadataRequest - 15, // 33: api.v1.OperatorService.GetOCIRegistries:input_type -> api.v1.GetOCIRegistriesRequest - 5, // 34: api.v1.OperatorService.GetOperators:output_type -> api.v1.GetOperatorsResponse - 14, // 35: api.v1.OperatorService.GetOperatorMetadata:output_type -> api.v1.GetOperatorMetadataResponse - 17, // 36: api.v1.OperatorService.GetOCIRegistries:output_type -> api.v1.GetOCIRegistriesResponse - 34, // [34:37] is the sub-list for method output_type - 31, // [31:34] is the sub-list for method input_type - 31, // [31:31] is the sub-list for extension type_name - 31, // [31:31] is the sub-list for extension extendee - 0, // [0:31] is the sub-list for field type_name + 19, // 3: api.v1.Operator.connected_at:type_name -> google.protobuf.Timestamp + 3, // 4: api.v1.Operator.version_status:type_name -> api.v1.OperatorVersionStatus + 4, // 5: api.v1.GetOperatorsResponse.operators:type_name -> api.v1.Operator + 19, // 6: api.v1.ConnectionMetric.last_seen:type_name -> google.protobuf.Timestamp + 0, // 7: api.v1.GetOperatorMetadataRequest.type:type_name -> api.v1.OperatorType + 4, // 8: api.v1.ReadOperator.operator:type_name -> api.v1.Operator + 7, // 9: api.v1.ReadOperator.container_metrics:type_name -> api.v1.ConnectionMetric + 7, // 10: api.v1.ReadOperator.node_metrics:type_name -> api.v1.ConnectionMetric + 7, // 11: api.v1.ReadOperator.snapshot_metrics:type_name -> api.v1.ConnectionMetric + 7, // 12: api.v1.ReadOperator.storage_metrics:type_name -> api.v1.ConnectionMetric + 4, // 13: api.v1.WriteOperator.operator:type_name -> api.v1.Operator + 7, // 14: api.v1.WriteOperator.recommendation_metrics:type_name -> api.v1.ConnectionMetric + 4, // 15: api.v1.NodeOperator.operator:type_name -> api.v1.Operator + 7, // 16: api.v1.NodeOperator.karpenter_deployment:type_name -> api.v1.ConnectionMetric + 7, // 17: api.v1.NodeOperator.node_provisioning:type_name -> api.v1.ConnectionMetric + 4, // 18: api.v1.SecurityOperator.operator:type_name -> api.v1.Operator + 7, // 19: api.v1.SecurityOperator.security_metrics:type_name -> api.v1.ConnectionMetric + 4, // 20: api.v1.NetworkOperator.operator:type_name -> api.v1.Operator + 7, // 21: api.v1.NetworkOperator.network_metrics:type_name -> api.v1.ConnectionMetric + 4, // 22: api.v1.CostOperator.operator:type_name -> api.v1.Operator + 7, // 23: api.v1.CostOperator.instance_collection:type_name -> api.v1.ConnectionMetric + 7, // 24: api.v1.CostOperator.reservation_collection:type_name -> api.v1.ConnectionMetric + 9, // 25: api.v1.GetOperatorMetadataResponse.read_operator:type_name -> api.v1.ReadOperator + 10, // 26: api.v1.GetOperatorMetadataResponse.write_operator:type_name -> api.v1.WriteOperator + 11, // 27: api.v1.GetOperatorMetadataResponse.node_operator:type_name -> api.v1.NodeOperator + 12, // 28: api.v1.GetOperatorMetadataResponse.security_operator:type_name -> api.v1.SecurityOperator + 13, // 29: api.v1.GetOperatorMetadataResponse.network_operator:type_name -> api.v1.NetworkOperator + 14, // 30: api.v1.GetOperatorMetadataResponse.cost_operator:type_name -> api.v1.CostOperator + 17, // 31: api.v1.GetOCIRegistriesResponse.registry_configs:type_name -> api.v1.RegistryMirrorConfig + 5, // 32: api.v1.OperatorService.GetOperators:input_type -> api.v1.GetOperatorsRequest + 8, // 33: api.v1.OperatorService.GetOperatorMetadata:input_type -> api.v1.GetOperatorMetadataRequest + 16, // 34: api.v1.OperatorService.GetOCIRegistries:input_type -> api.v1.GetOCIRegistriesRequest + 6, // 35: api.v1.OperatorService.GetOperators:output_type -> api.v1.GetOperatorsResponse + 15, // 36: api.v1.OperatorService.GetOperatorMetadata:output_type -> api.v1.GetOperatorMetadataResponse + 18, // 37: api.v1.OperatorService.GetOCIRegistries:output_type -> api.v1.GetOCIRegistriesResponse + 35, // [35:38] is the sub-list for method output_type + 32, // [32:35] is the sub-list for method input_type + 32, // [32:32] is the sub-list for extension type_name + 32, // [32:32] is the sub-list for extension extendee + 0, // [0:32] is the sub-list for field type_name } func init() { file_api_v1_operator_proto_init() } @@ -1750,7 +1866,7 @@ func file_api_v1_operator_proto_init() { File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_api_v1_operator_proto_rawDesc, - NumEnums: 3, + NumEnums: 4, NumMessages: 15, NumExtensions: 0, NumServices: 1, diff --git a/gen/api/v1/operator_health.pb.go b/gen/api/v1/operator_health.pb.go index cd4af62e..09569cf6 100644 --- a/gen/api/v1/operator_health.pb.go +++ b/gen/api/v1/operator_health.pb.go @@ -144,22 +144,40 @@ func (x *ComponentHealth) GetMetadata() map[string]string { return nil } -type ReportHealthRequest struct { +// OperatorConfig carries the runtime configuration an operator can determine +// about itself: which feature flags it actually booted with, where its agent +// runs, and the batch sizes it pulls with. Support needs these to reason about a +// cluster without asking the customer to run kubectl. +// +// Equivalent fields already existed on the deprecated OperatorHealth message +// (api/v1/recommendation.proto), which travels the legacy Balance-stream path +// the control plane deliberately does not persist. Nothing populated them and +// nothing read them, so every cluster reported the zero value: no namespace and +// every feature flag false. That is how a cluster running with argocd patching +// enabled came to be diagnosed as having it disabled. +// +// The operator_health table also has checkpoint_image_registry, +// checkpoint_image_repo_name and checkpoint_image_registry_auth_provider +// columns. The workload operator has no such configuration, so they are +// deliberately absent here rather than shipped as fields nothing can populate; +// those columns stay empty until whichever component owns them reports them. +type OperatorConfig struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - ClusterId string `protobuf:"bytes,1,opt,name=cluster_id,json=clusterId,proto3" json:"cluster_id,omitempty"` - OperatorType OperatorType `protobuf:"varint,2,opt,name=operator_type,json=operatorType,proto3,enum=api.v1.OperatorType" json:"operator_type,omitempty"` - Version string `protobuf:"bytes,3,opt,name=version,proto3" json:"version,omitempty"` - OverallStatus HealthStatus `protobuf:"varint,4,opt,name=overall_status,json=overallStatus,proto3,enum=api.v1.HealthStatus" json:"overall_status,omitempty"` - Components []*ComponentHealth `protobuf:"bytes,5,rep,name=components,proto3" json:"components,omitempty"` - UptimeSince *timestamppb.Timestamp `protobuf:"bytes,6,opt,name=uptime_since,json=uptimeSince,proto3" json:"uptime_since,omitempty"` - Commit string `protobuf:"bytes,7,opt,name=commit,proto3" json:"commit,omitempty"` + AgentNamespace string `protobuf:"bytes,1,opt,name=agent_namespace,json=agentNamespace,proto3" json:"agent_namespace,omitempty"` + AgentAppName string `protobuf:"bytes,2,opt,name=agent_app_name,json=agentAppName,proto3" json:"agent_app_name,omitempty"` + NoCloudCreds bool `protobuf:"varint,3,opt,name=no_cloud_creds,json=noCloudCreds,proto3" json:"no_cloud_creds,omitempty"` + CustomScheduler bool `protobuf:"varint,4,opt,name=custom_scheduler,json=customScheduler,proto3" json:"custom_scheduler,omitempty"` + ArgocdPatching bool `protobuf:"varint,5,opt,name=argocd_patching,json=argocdPatching,proto3" json:"argocd_patching,omitempty"` + WorkloadRecBatchSize int32 `protobuf:"varint,6,opt,name=workload_rec_batch_size,json=workloadRecBatchSize,proto3" json:"workload_rec_batch_size,omitempty"` + NodeGroupRecBatchSize int32 `protobuf:"varint,7,opt,name=node_group_rec_batch_size,json=nodeGroupRecBatchSize,proto3" json:"node_group_rec_batch_size,omitempty"` + SvcAccountName string `protobuf:"bytes,8,opt,name=svc_account_name,json=svcAccountName,proto3" json:"svc_account_name,omitempty"` } -func (x *ReportHealthRequest) Reset() { - *x = ReportHealthRequest{} +func (x *OperatorConfig) Reset() { + *x = OperatorConfig{} if protoimpl.UnsafeEnabled { mi := &file_api_v1_operator_health_proto_msgTypes[1] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -167,13 +185,13 @@ func (x *ReportHealthRequest) Reset() { } } -func (x *ReportHealthRequest) String() string { +func (x *OperatorConfig) String() string { return protoimpl.X.MessageStringOf(x) } -func (*ReportHealthRequest) ProtoMessage() {} +func (*OperatorConfig) ProtoMessage() {} -func (x *ReportHealthRequest) ProtoReflect() protoreflect.Message { +func (x *OperatorConfig) ProtoReflect() protoreflect.Message { mi := &file_api_v1_operator_health_proto_msgTypes[1] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -185,71 +203,87 @@ func (x *ReportHealthRequest) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use ReportHealthRequest.ProtoReflect.Descriptor instead. -func (*ReportHealthRequest) Descriptor() ([]byte, []int) { +// Deprecated: Use OperatorConfig.ProtoReflect.Descriptor instead. +func (*OperatorConfig) Descriptor() ([]byte, []int) { return file_api_v1_operator_health_proto_rawDescGZIP(), []int{1} } -func (x *ReportHealthRequest) GetClusterId() string { +func (x *OperatorConfig) GetAgentNamespace() string { if x != nil { - return x.ClusterId + return x.AgentNamespace } return "" } -func (x *ReportHealthRequest) GetOperatorType() OperatorType { +func (x *OperatorConfig) GetAgentAppName() string { if x != nil { - return x.OperatorType + return x.AgentAppName } - return OperatorType_OPERATOR_TYPE_UNSPECIFIED + return "" } -func (x *ReportHealthRequest) GetVersion() string { +func (x *OperatorConfig) GetNoCloudCreds() bool { if x != nil { - return x.Version + return x.NoCloudCreds } - return "" + return false } -func (x *ReportHealthRequest) GetOverallStatus() HealthStatus { +func (x *OperatorConfig) GetCustomScheduler() bool { if x != nil { - return x.OverallStatus + return x.CustomScheduler } - return HealthStatus_HEALTH_STATUS_UNSPECIFIED + return false } -func (x *ReportHealthRequest) GetComponents() []*ComponentHealth { +func (x *OperatorConfig) GetArgocdPatching() bool { if x != nil { - return x.Components + return x.ArgocdPatching } - return nil + return false } -func (x *ReportHealthRequest) GetUptimeSince() *timestamppb.Timestamp { +func (x *OperatorConfig) GetWorkloadRecBatchSize() int32 { if x != nil { - return x.UptimeSince + return x.WorkloadRecBatchSize } - return nil + return 0 } -func (x *ReportHealthRequest) GetCommit() string { +func (x *OperatorConfig) GetNodeGroupRecBatchSize() int32 { if x != nil { - return x.Commit + return x.NodeGroupRecBatchSize + } + return 0 +} + +func (x *OperatorConfig) GetSvcAccountName() string { + if x != nil { + return x.SvcAccountName } return "" } -type ReportHealthResponse struct { +type ReportHealthRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - // When true, trezr should run a full collection pass immediately. - CollectNow bool `protobuf:"varint,1,opt,name=collect_now,json=collectNow,proto3" json:"collect_now,omitempty"` + ClusterId string `protobuf:"bytes,1,opt,name=cluster_id,json=clusterId,proto3" json:"cluster_id,omitempty"` + OperatorType OperatorType `protobuf:"varint,2,opt,name=operator_type,json=operatorType,proto3,enum=api.v1.OperatorType" json:"operator_type,omitempty"` + Version string `protobuf:"bytes,3,opt,name=version,proto3" json:"version,omitempty"` + OverallStatus HealthStatus `protobuf:"varint,4,opt,name=overall_status,json=overallStatus,proto3,enum=api.v1.HealthStatus" json:"overall_status,omitempty"` + Components []*ComponentHealth `protobuf:"bytes,5,rep,name=components,proto3" json:"components,omitempty"` + UptimeSince *timestamppb.Timestamp `protobuf:"bytes,6,opt,name=uptime_since,json=uptimeSince,proto3" json:"uptime_since,omitempty"` + Commit string `protobuf:"bytes,7,opt,name=commit,proto3" json:"commit,omitempty"` + // Optional so that an operator which does not report configuration is + // distinguishable from one reporting all-false. Absent means "leave the stored + // configuration alone"; present overwrites it. + Config *OperatorConfig `protobuf:"bytes,8,opt,name=config,proto3,oneof" json:"config,omitempty"` } -func (x *ReportHealthResponse) Reset() { - *x = ReportHealthResponse{} +func (x *ReportHealthRequest) Reset() { + *x = ReportHealthRequest{} if protoimpl.UnsafeEnabled { mi := &file_api_v1_operator_health_proto_msgTypes[2] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -257,13 +291,13 @@ func (x *ReportHealthResponse) Reset() { } } -func (x *ReportHealthResponse) String() string { +func (x *ReportHealthRequest) String() string { return protoimpl.X.MessageStringOf(x) } -func (*ReportHealthResponse) ProtoMessage() {} +func (*ReportHealthRequest) ProtoMessage() {} -func (x *ReportHealthResponse) ProtoReflect() protoreflect.Message { +func (x *ReportHealthRequest) ProtoReflect() protoreflect.Message { mi := &file_api_v1_operator_health_proto_msgTypes[2] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -275,167 +309,93 @@ func (x *ReportHealthResponse) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use ReportHealthResponse.ProtoReflect.Descriptor instead. -func (*ReportHealthResponse) Descriptor() ([]byte, []int) { +// Deprecated: Use ReportHealthRequest.ProtoReflect.Descriptor instead. +func (*ReportHealthRequest) Descriptor() ([]byte, []int) { return file_api_v1_operator_health_proto_rawDescGZIP(), []int{2} } -func (x *ReportHealthResponse) GetCollectNow() bool { +func (x *ReportHealthRequest) GetClusterId() string { if x != nil { - return x.CollectNow - } - return false -} - -type ConnectorHealthStatus struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - ConnectorId string `protobuf:"bytes,1,opt,name=connector_id,json=connectorId,proto3" json:"connector_id,omitempty"` - Status string `protobuf:"bytes,2,opt,name=status,proto3" json:"status,omitempty"` - Error *string `protobuf:"bytes,3,opt,name=error,proto3,oneof" json:"error,omitempty"` - CheckedAt *timestamppb.Timestamp `protobuf:"bytes,4,opt,name=checked_at,json=checkedAt,proto3" json:"checked_at,omitempty"` -} - -func (x *ConnectorHealthStatus) Reset() { - *x = ConnectorHealthStatus{} - if protoimpl.UnsafeEnabled { - mi := &file_api_v1_operator_health_proto_msgTypes[3] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *ConnectorHealthStatus) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ConnectorHealthStatus) ProtoMessage() {} - -func (x *ConnectorHealthStatus) ProtoReflect() protoreflect.Message { - mi := &file_api_v1_operator_health_proto_msgTypes[3] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms + return x.ClusterId } - return mi.MessageOf(x) -} - -// Deprecated: Use ConnectorHealthStatus.ProtoReflect.Descriptor instead. -func (*ConnectorHealthStatus) Descriptor() ([]byte, []int) { - return file_api_v1_operator_health_proto_rawDescGZIP(), []int{3} + return "" } -func (x *ConnectorHealthStatus) GetConnectorId() string { +func (x *ReportHealthRequest) GetOperatorType() OperatorType { if x != nil { - return x.ConnectorId + return x.OperatorType } - return "" + return OperatorType_OPERATOR_TYPE_UNSPECIFIED } -func (x *ConnectorHealthStatus) GetStatus() string { +func (x *ReportHealthRequest) GetVersion() string { if x != nil { - return x.Status + return x.Version } return "" } -func (x *ConnectorHealthStatus) GetError() string { - if x != nil && x.Error != nil { - return *x.Error +func (x *ReportHealthRequest) GetOverallStatus() HealthStatus { + if x != nil { + return x.OverallStatus } - return "" + return HealthStatus_HEALTH_STATUS_UNSPECIFIED } -func (x *ConnectorHealthStatus) GetCheckedAt() *timestamppb.Timestamp { +func (x *ReportHealthRequest) GetComponents() []*ComponentHealth { if x != nil { - return x.CheckedAt + return x.Components } return nil } -type ReportConnectorHealthRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - ClusterId string `protobuf:"bytes,1,opt,name=cluster_id,json=clusterId,proto3" json:"cluster_id,omitempty"` - Statuses []*ConnectorHealthStatus `protobuf:"bytes,2,rep,name=statuses,proto3" json:"statuses,omitempty"` -} - -func (x *ReportConnectorHealthRequest) Reset() { - *x = ReportConnectorHealthRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_api_v1_operator_health_proto_msgTypes[4] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *ReportConnectorHealthRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ReportConnectorHealthRequest) ProtoMessage() {} - -func (x *ReportConnectorHealthRequest) ProtoReflect() protoreflect.Message { - mi := &file_api_v1_operator_health_proto_msgTypes[4] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms +func (x *ReportHealthRequest) GetUptimeSince() *timestamppb.Timestamp { + if x != nil { + return x.UptimeSince } - return mi.MessageOf(x) -} - -// Deprecated: Use ReportConnectorHealthRequest.ProtoReflect.Descriptor instead. -func (*ReportConnectorHealthRequest) Descriptor() ([]byte, []int) { - return file_api_v1_operator_health_proto_rawDescGZIP(), []int{4} + return nil } -func (x *ReportConnectorHealthRequest) GetClusterId() string { +func (x *ReportHealthRequest) GetCommit() string { if x != nil { - return x.ClusterId + return x.Commit } return "" } -func (x *ReportConnectorHealthRequest) GetStatuses() []*ConnectorHealthStatus { +func (x *ReportHealthRequest) GetConfig() *OperatorConfig { if x != nil { - return x.Statuses + return x.Config } return nil } -type ReportConnectorHealthResponse struct { +type ReportHealthResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields + + // When true, trezr should run a full collection pass immediately. + CollectNow bool `protobuf:"varint,1,opt,name=collect_now,json=collectNow,proto3" json:"collect_now,omitempty"` } -func (x *ReportConnectorHealthResponse) Reset() { - *x = ReportConnectorHealthResponse{} +func (x *ReportHealthResponse) Reset() { + *x = ReportHealthResponse{} if protoimpl.UnsafeEnabled { - mi := &file_api_v1_operator_health_proto_msgTypes[5] + mi := &file_api_v1_operator_health_proto_msgTypes[3] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } -func (x *ReportConnectorHealthResponse) String() string { +func (x *ReportHealthResponse) String() string { return protoimpl.X.MessageStringOf(x) } -func (*ReportConnectorHealthResponse) ProtoMessage() {} +func (*ReportHealthResponse) ProtoMessage() {} -func (x *ReportConnectorHealthResponse) ProtoReflect() protoreflect.Message { - mi := &file_api_v1_operator_health_proto_msgTypes[5] +func (x *ReportHealthResponse) ProtoReflect() protoreflect.Message { + mi := &file_api_v1_operator_health_proto_msgTypes[3] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -446,9 +406,16 @@ func (x *ReportConnectorHealthResponse) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use ReportConnectorHealthResponse.ProtoReflect.Descriptor instead. -func (*ReportConnectorHealthResponse) Descriptor() ([]byte, []int) { - return file_api_v1_operator_health_proto_rawDescGZIP(), []int{5} +// Deprecated: Use ReportHealthResponse.ProtoReflect.Descriptor instead. +func (*ReportHealthResponse) Descriptor() ([]byte, []int) { + return file_api_v1_operator_health_proto_rawDescGZIP(), []int{3} +} + +func (x *ReportHealthResponse) GetCollectNow() bool { + if x != nil { + return x.CollectNow + } + return false } var File_api_v1_operator_health_proto protoreflect.FileDescriptor @@ -474,84 +441,84 @@ var file_api_v1_operator_health_proto_rawDesc = []byte{ 0x61, 0x1a, 0x3b, 0x0a, 0x0d, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0xd6, - 0x02, 0x0a, 0x13, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, - 0x72, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x6c, 0x75, 0x73, - 0x74, 0x65, 0x72, 0x49, 0x64, 0x12, 0x39, 0x0a, 0x0d, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, - 0x72, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x14, 0x2e, 0x61, - 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x54, 0x79, - 0x70, 0x65, 0x52, 0x0c, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x54, 0x79, 0x70, 0x65, - 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x3b, 0x0a, 0x0e, 0x6f, 0x76, - 0x65, 0x72, 0x61, 0x6c, 0x6c, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x04, 0x20, 0x01, - 0x28, 0x0e, 0x32, 0x14, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x48, 0x65, 0x61, 0x6c, - 0x74, 0x68, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x0d, 0x6f, 0x76, 0x65, 0x72, 0x61, 0x6c, - 0x6c, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x37, 0x0a, 0x0a, 0x63, 0x6f, 0x6d, 0x70, 0x6f, - 0x6e, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x61, 0x70, - 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x48, 0x65, - 0x61, 0x6c, 0x74, 0x68, 0x52, 0x0a, 0x63, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x73, - 0x12, 0x3d, 0x0a, 0x0c, 0x75, 0x70, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x73, 0x69, 0x6e, 0x63, 0x65, - 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, - 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, - 0x6d, 0x70, 0x52, 0x0b, 0x75, 0x70, 0x74, 0x69, 0x6d, 0x65, 0x53, 0x69, 0x6e, 0x63, 0x65, 0x12, - 0x16, 0x0a, 0x06, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x06, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x22, 0x37, 0x0a, 0x14, 0x52, 0x65, 0x70, 0x6f, 0x72, - 0x74, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, - 0x1f, 0x0a, 0x0b, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x5f, 0x6e, 0x6f, 0x77, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x4e, 0x6f, 0x77, - 0x22, 0xb2, 0x01, 0x0a, 0x15, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x48, 0x65, - 0x61, 0x6c, 0x74, 0x68, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x21, 0x0a, 0x0c, 0x63, 0x6f, - 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x0b, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x49, 0x64, 0x12, 0x16, 0x0a, - 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, - 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x19, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x03, - 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x88, 0x01, 0x01, - 0x12, 0x39, 0x0a, 0x0a, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x04, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, - 0x52, 0x09, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x65, 0x64, 0x41, 0x74, 0x42, 0x08, 0x0a, 0x06, 0x5f, - 0x65, 0x72, 0x72, 0x6f, 0x72, 0x22, 0x78, 0x0a, 0x1c, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x43, - 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, - 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x6c, 0x75, 0x73, 0x74, - 0x65, 0x72, 0x49, 0x64, 0x12, 0x39, 0x0a, 0x08, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x65, 0x73, - 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, - 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x53, - 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x08, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x65, 0x73, 0x22, - 0x1f, 0x0a, 0x1d, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, - 0x6f, 0x72, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x2a, 0x81, 0x01, 0x0a, 0x0c, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x53, 0x74, 0x61, 0x74, 0x75, - 0x73, 0x12, 0x1d, 0x0a, 0x19, 0x48, 0x45, 0x41, 0x4c, 0x54, 0x48, 0x5f, 0x53, 0x54, 0x41, 0x54, - 0x55, 0x53, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, - 0x12, 0x19, 0x0a, 0x15, 0x48, 0x45, 0x41, 0x4c, 0x54, 0x48, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x55, - 0x53, 0x5f, 0x48, 0x45, 0x41, 0x4c, 0x54, 0x48, 0x59, 0x10, 0x01, 0x12, 0x1a, 0x0a, 0x16, 0x48, - 0x45, 0x41, 0x4c, 0x54, 0x48, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x44, 0x45, 0x47, - 0x52, 0x41, 0x44, 0x45, 0x44, 0x10, 0x02, 0x12, 0x1b, 0x0a, 0x17, 0x48, 0x45, 0x41, 0x4c, 0x54, - 0x48, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x55, 0x4e, 0x48, 0x45, 0x41, 0x4c, 0x54, - 0x48, 0x59, 0x10, 0x03, 0x32, 0xc8, 0x01, 0x0a, 0x15, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, - 0x72, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x49, - 0x0a, 0x0c, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x12, 0x1b, - 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x48, 0x65, - 0x61, 0x6c, 0x74, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x61, 0x70, - 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x48, 0x65, 0x61, 0x6c, 0x74, - 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x64, 0x0a, 0x15, 0x52, 0x65, 0x70, - 0x6f, 0x72, 0x74, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x48, 0x65, 0x61, 0x6c, - 0x74, 0x68, 0x12, 0x24, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x70, 0x6f, - 0x72, 0x74, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x48, 0x65, 0x61, 0x6c, 0x74, - 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x25, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, - 0x31, 0x2e, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x6f, - 0x72, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, - 0x8c, 0x01, 0x0a, 0x0a, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x42, 0x13, + 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0xf4, + 0x02, 0x0a, 0x0e, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, + 0x67, 0x12, 0x27, 0x0a, 0x0f, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x73, + 0x70, 0x61, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x61, 0x67, 0x65, 0x6e, + 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x24, 0x0a, 0x0e, 0x61, 0x67, + 0x65, 0x6e, 0x74, 0x5f, 0x61, 0x70, 0x70, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x0c, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x41, 0x70, 0x70, 0x4e, 0x61, 0x6d, 0x65, + 0x12, 0x24, 0x0a, 0x0e, 0x6e, 0x6f, 0x5f, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x5f, 0x63, 0x72, 0x65, + 0x64, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x6e, 0x6f, 0x43, 0x6c, 0x6f, 0x75, + 0x64, 0x43, 0x72, 0x65, 0x64, 0x73, 0x12, 0x29, 0x0a, 0x10, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, + 0x5f, 0x73, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, + 0x52, 0x0f, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x53, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, + 0x72, 0x12, 0x27, 0x0a, 0x0f, 0x61, 0x72, 0x67, 0x6f, 0x63, 0x64, 0x5f, 0x70, 0x61, 0x74, 0x63, + 0x68, 0x69, 0x6e, 0x67, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0e, 0x61, 0x72, 0x67, 0x6f, + 0x63, 0x64, 0x50, 0x61, 0x74, 0x63, 0x68, 0x69, 0x6e, 0x67, 0x12, 0x35, 0x0a, 0x17, 0x77, 0x6f, + 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x5f, 0x72, 0x65, 0x63, 0x5f, 0x62, 0x61, 0x74, 0x63, 0x68, + 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x05, 0x52, 0x14, 0x77, 0x6f, 0x72, + 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x65, 0x63, 0x42, 0x61, 0x74, 0x63, 0x68, 0x53, 0x69, 0x7a, + 0x65, 0x12, 0x38, 0x0a, 0x19, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, + 0x72, 0x65, 0x63, 0x5f, 0x62, 0x61, 0x74, 0x63, 0x68, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x07, + 0x20, 0x01, 0x28, 0x05, 0x52, 0x15, 0x6e, 0x6f, 0x64, 0x65, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x52, + 0x65, 0x63, 0x42, 0x61, 0x74, 0x63, 0x68, 0x53, 0x69, 0x7a, 0x65, 0x12, 0x28, 0x0a, 0x10, 0x73, + 0x76, 0x63, 0x5f, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, + 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x73, 0x76, 0x63, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, + 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x22, 0x96, 0x03, 0x0a, 0x13, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, + 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, + 0x0a, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x09, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, 0x64, 0x12, 0x39, 0x0a, 0x0d, + 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x0e, 0x32, 0x14, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x70, 0x65, + 0x72, 0x61, 0x74, 0x6f, 0x72, 0x54, 0x79, 0x70, 0x65, 0x52, 0x0c, 0x6f, 0x70, 0x65, 0x72, 0x61, + 0x74, 0x6f, 0x72, 0x54, 0x79, 0x70, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, + 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, + 0x6e, 0x12, 0x3b, 0x0a, 0x0e, 0x6f, 0x76, 0x65, 0x72, 0x61, 0x6c, 0x6c, 0x5f, 0x73, 0x74, 0x61, + 0x74, 0x75, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x14, 0x2e, 0x61, 0x70, 0x69, 0x2e, + 0x76, 0x31, 0x2e, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, + 0x0d, 0x6f, 0x76, 0x65, 0x72, 0x61, 0x6c, 0x6c, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x37, + 0x0a, 0x0a, 0x63, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x05, 0x20, 0x03, + 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6d, 0x70, + 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x52, 0x0a, 0x63, 0x6f, 0x6d, + 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x3d, 0x0a, 0x0c, 0x75, 0x70, 0x74, 0x69, 0x6d, + 0x65, 0x5f, 0x73, 0x69, 0x6e, 0x63, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, + 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, + 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0b, 0x75, 0x70, 0x74, 0x69, 0x6d, + 0x65, 0x53, 0x69, 0x6e, 0x63, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, + 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x12, 0x33, + 0x0a, 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, + 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, + 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x48, 0x00, 0x52, 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, + 0x88, 0x01, 0x01, 0x42, 0x09, 0x0a, 0x07, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x22, 0x37, + 0x0a, 0x14, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, + 0x74, 0x5f, 0x6e, 0x6f, 0x77, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x63, 0x6f, 0x6c, + 0x6c, 0x65, 0x63, 0x74, 0x4e, 0x6f, 0x77, 0x2a, 0x81, 0x01, 0x0a, 0x0c, 0x48, 0x65, 0x61, 0x6c, + 0x74, 0x68, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x1d, 0x0a, 0x19, 0x48, 0x45, 0x41, 0x4c, + 0x54, 0x48, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, + 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x19, 0x0a, 0x15, 0x48, 0x45, 0x41, 0x4c, 0x54, + 0x48, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x48, 0x45, 0x41, 0x4c, 0x54, 0x48, 0x59, + 0x10, 0x01, 0x12, 0x1a, 0x0a, 0x16, 0x48, 0x45, 0x41, 0x4c, 0x54, 0x48, 0x5f, 0x53, 0x54, 0x41, + 0x54, 0x55, 0x53, 0x5f, 0x44, 0x45, 0x47, 0x52, 0x41, 0x44, 0x45, 0x44, 0x10, 0x02, 0x12, 0x1b, + 0x0a, 0x17, 0x48, 0x45, 0x41, 0x4c, 0x54, 0x48, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, + 0x55, 0x4e, 0x48, 0x45, 0x41, 0x4c, 0x54, 0x48, 0x59, 0x10, 0x03, 0x32, 0x62, 0x0a, 0x15, 0x4f, + 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x53, 0x65, 0x72, + 0x76, 0x69, 0x63, 0x65, 0x12, 0x49, 0x0a, 0x0c, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x48, 0x65, + 0x61, 0x6c, 0x74, 0x68, 0x12, 0x1b, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, + 0x70, 0x6f, 0x72, 0x74, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x1c, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x70, 0x6f, 0x72, + 0x74, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, + 0x91, 0x01, 0x0a, 0x0a, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x42, 0x13, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x50, 0x72, - 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x30, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, - 0x6d, 0x2f, 0x64, 0x65, 0x76, 0x7a, 0x65, 0x72, 0x6f, 0x2d, 0x69, 0x6e, 0x63, 0x2f, 0x7a, 0x78, - 0x70, 0x6f, 0x72, 0x74, 0x65, 0x72, 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, - 0x31, 0x3b, 0x61, 0x70, 0x69, 0x76, 0x31, 0xa2, 0x02, 0x03, 0x41, 0x58, 0x58, 0xaa, 0x02, 0x06, - 0x41, 0x70, 0x69, 0x2e, 0x56, 0x31, 0xca, 0x02, 0x06, 0x41, 0x70, 0x69, 0x5c, 0x56, 0x31, 0xe2, - 0x02, 0x12, 0x41, 0x70, 0x69, 0x5c, 0x56, 0x31, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, - 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x07, 0x41, 0x70, 0x69, 0x3a, 0x3a, 0x56, 0x31, 0x62, 0x06, - 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x35, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, + 0x6d, 0x2f, 0x64, 0x65, 0x76, 0x7a, 0x65, 0x72, 0x6f, 0x2d, 0x69, 0x6e, 0x63, 0x2f, 0x73, 0x65, + 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2f, 0x64, 0x61, 0x6b, 0x72, 0x2f, 0x67, 0x65, 0x6e, 0x2f, + 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x3b, 0x61, 0x70, 0x69, 0x76, 0x31, 0xa2, 0x02, 0x03, 0x41, + 0x58, 0x58, 0xaa, 0x02, 0x06, 0x41, 0x70, 0x69, 0x2e, 0x56, 0x31, 0xca, 0x02, 0x06, 0x41, 0x70, + 0x69, 0x5c, 0x56, 0x31, 0xe2, 0x02, 0x12, 0x41, 0x70, 0x69, 0x5c, 0x56, 0x31, 0x5c, 0x47, 0x50, + 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x07, 0x41, 0x70, 0x69, 0x3a, + 0x3a, 0x56, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -567,37 +534,32 @@ func file_api_v1_operator_health_proto_rawDescGZIP() []byte { } var file_api_v1_operator_health_proto_enumTypes = make([]protoimpl.EnumInfo, 1) -var file_api_v1_operator_health_proto_msgTypes = make([]protoimpl.MessageInfo, 7) +var file_api_v1_operator_health_proto_msgTypes = make([]protoimpl.MessageInfo, 5) var file_api_v1_operator_health_proto_goTypes = []interface{}{ - (HealthStatus)(0), // 0: api.v1.HealthStatus - (*ComponentHealth)(nil), // 1: api.v1.ComponentHealth - (*ReportHealthRequest)(nil), // 2: api.v1.ReportHealthRequest - (*ReportHealthResponse)(nil), // 3: api.v1.ReportHealthResponse - (*ConnectorHealthStatus)(nil), // 4: api.v1.ConnectorHealthStatus - (*ReportConnectorHealthRequest)(nil), // 5: api.v1.ReportConnectorHealthRequest - (*ReportConnectorHealthResponse)(nil), // 6: api.v1.ReportConnectorHealthResponse - nil, // 7: api.v1.ComponentHealth.MetadataEntry - (OperatorType)(0), // 8: api.v1.OperatorType - (*timestamppb.Timestamp)(nil), // 9: google.protobuf.Timestamp + (HealthStatus)(0), // 0: api.v1.HealthStatus + (*ComponentHealth)(nil), // 1: api.v1.ComponentHealth + (*OperatorConfig)(nil), // 2: api.v1.OperatorConfig + (*ReportHealthRequest)(nil), // 3: api.v1.ReportHealthRequest + (*ReportHealthResponse)(nil), // 4: api.v1.ReportHealthResponse + nil, // 5: api.v1.ComponentHealth.MetadataEntry + (OperatorType)(0), // 6: api.v1.OperatorType + (*timestamppb.Timestamp)(nil), // 7: google.protobuf.Timestamp } var file_api_v1_operator_health_proto_depIdxs = []int32{ - 0, // 0: api.v1.ComponentHealth.status:type_name -> api.v1.HealthStatus - 7, // 1: api.v1.ComponentHealth.metadata:type_name -> api.v1.ComponentHealth.MetadataEntry - 8, // 2: api.v1.ReportHealthRequest.operator_type:type_name -> api.v1.OperatorType - 0, // 3: api.v1.ReportHealthRequest.overall_status:type_name -> api.v1.HealthStatus - 1, // 4: api.v1.ReportHealthRequest.components:type_name -> api.v1.ComponentHealth - 9, // 5: api.v1.ReportHealthRequest.uptime_since:type_name -> google.protobuf.Timestamp - 9, // 6: api.v1.ConnectorHealthStatus.checked_at:type_name -> google.protobuf.Timestamp - 4, // 7: api.v1.ReportConnectorHealthRequest.statuses:type_name -> api.v1.ConnectorHealthStatus - 2, // 8: api.v1.OperatorHealthService.ReportHealth:input_type -> api.v1.ReportHealthRequest - 5, // 9: api.v1.OperatorHealthService.ReportConnectorHealth:input_type -> api.v1.ReportConnectorHealthRequest - 3, // 10: api.v1.OperatorHealthService.ReportHealth:output_type -> api.v1.ReportHealthResponse - 6, // 11: api.v1.OperatorHealthService.ReportConnectorHealth:output_type -> api.v1.ReportConnectorHealthResponse - 10, // [10:12] is the sub-list for method output_type - 8, // [8:10] is the sub-list for method input_type - 8, // [8:8] is the sub-list for extension type_name - 8, // [8:8] is the sub-list for extension extendee - 0, // [0:8] is the sub-list for field type_name + 0, // 0: api.v1.ComponentHealth.status:type_name -> api.v1.HealthStatus + 5, // 1: api.v1.ComponentHealth.metadata:type_name -> api.v1.ComponentHealth.MetadataEntry + 6, // 2: api.v1.ReportHealthRequest.operator_type:type_name -> api.v1.OperatorType + 0, // 3: api.v1.ReportHealthRequest.overall_status:type_name -> api.v1.HealthStatus + 1, // 4: api.v1.ReportHealthRequest.components:type_name -> api.v1.ComponentHealth + 7, // 5: api.v1.ReportHealthRequest.uptime_since:type_name -> google.protobuf.Timestamp + 2, // 6: api.v1.ReportHealthRequest.config:type_name -> api.v1.OperatorConfig + 3, // 7: api.v1.OperatorHealthService.ReportHealth:input_type -> api.v1.ReportHealthRequest + 4, // 8: api.v1.OperatorHealthService.ReportHealth:output_type -> api.v1.ReportHealthResponse + 8, // [8:9] is the sub-list for method output_type + 7, // [7:8] is the sub-list for method input_type + 7, // [7:7] is the sub-list for extension type_name + 7, // [7:7] is the sub-list for extension extendee + 0, // [0:7] is the sub-list for field type_name } func init() { file_api_v1_operator_health_proto_init() } @@ -620,7 +582,7 @@ func file_api_v1_operator_health_proto_init() { } } file_api_v1_operator_health_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ReportHealthRequest); i { + switch v := v.(*OperatorConfig); i { case 0: return &v.state case 1: @@ -632,7 +594,7 @@ func file_api_v1_operator_health_proto_init() { } } file_api_v1_operator_health_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ReportHealthResponse); i { + switch v := v.(*ReportHealthRequest); i { case 0: return &v.state case 1: @@ -644,31 +606,7 @@ func file_api_v1_operator_health_proto_init() { } } file_api_v1_operator_health_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ConnectorHealthStatus); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_api_v1_operator_health_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ReportConnectorHealthRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_api_v1_operator_health_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ReportConnectorHealthResponse); i { + switch v := v.(*ReportHealthResponse); i { case 0: return &v.state case 1: @@ -680,14 +618,14 @@ func file_api_v1_operator_health_proto_init() { } } } - file_api_v1_operator_health_proto_msgTypes[3].OneofWrappers = []interface{}{} + file_api_v1_operator_health_proto_msgTypes[2].OneofWrappers = []interface{}{} type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_api_v1_operator_health_proto_rawDesc, NumEnums: 1, - NumMessages: 7, + NumMessages: 5, NumExtensions: 0, NumServices: 1, }, diff --git a/gen/api/v1/operator_health_grpc.pb.go b/gen/api/v1/operator_health_grpc.pb.go index c9ba59e4..a43f3064 100644 --- a/gen/api/v1/operator_health_grpc.pb.go +++ b/gen/api/v1/operator_health_grpc.pb.go @@ -19,8 +19,7 @@ import ( const _ = grpc.SupportPackageIsVersion7 const ( - OperatorHealthService_ReportHealth_FullMethodName = "/api.v1.OperatorHealthService/ReportHealth" - OperatorHealthService_ReportConnectorHealth_FullMethodName = "/api.v1.OperatorHealthService/ReportConnectorHealth" + OperatorHealthService_ReportHealth_FullMethodName = "/api.v1.OperatorHealthService/ReportHealth" ) // OperatorHealthServiceClient is the client API for OperatorHealthService service. @@ -28,7 +27,6 @@ const ( // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. type OperatorHealthServiceClient interface { ReportHealth(ctx context.Context, in *ReportHealthRequest, opts ...grpc.CallOption) (*ReportHealthResponse, error) - ReportConnectorHealth(ctx context.Context, in *ReportConnectorHealthRequest, opts ...grpc.CallOption) (*ReportConnectorHealthResponse, error) } type operatorHealthServiceClient struct { @@ -48,21 +46,11 @@ func (c *operatorHealthServiceClient) ReportHealth(ctx context.Context, in *Repo return out, nil } -func (c *operatorHealthServiceClient) ReportConnectorHealth(ctx context.Context, in *ReportConnectorHealthRequest, opts ...grpc.CallOption) (*ReportConnectorHealthResponse, error) { - out := new(ReportConnectorHealthResponse) - err := c.cc.Invoke(ctx, OperatorHealthService_ReportConnectorHealth_FullMethodName, in, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - // OperatorHealthServiceServer is the server API for OperatorHealthService service. // All implementations must embed UnimplementedOperatorHealthServiceServer // for forward compatibility type OperatorHealthServiceServer interface { ReportHealth(context.Context, *ReportHealthRequest) (*ReportHealthResponse, error) - ReportConnectorHealth(context.Context, *ReportConnectorHealthRequest) (*ReportConnectorHealthResponse, error) mustEmbedUnimplementedOperatorHealthServiceServer() } @@ -73,9 +61,6 @@ type UnimplementedOperatorHealthServiceServer struct { func (UnimplementedOperatorHealthServiceServer) ReportHealth(context.Context, *ReportHealthRequest) (*ReportHealthResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method ReportHealth not implemented") } -func (UnimplementedOperatorHealthServiceServer) ReportConnectorHealth(context.Context, *ReportConnectorHealthRequest) (*ReportConnectorHealthResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method ReportConnectorHealth not implemented") -} func (UnimplementedOperatorHealthServiceServer) mustEmbedUnimplementedOperatorHealthServiceServer() {} // UnsafeOperatorHealthServiceServer may be embedded to opt out of forward compatibility for this service. @@ -107,24 +92,6 @@ func _OperatorHealthService_ReportHealth_Handler(srv interface{}, ctx context.Co return interceptor(ctx, in, info, handler) } -func _OperatorHealthService_ReportConnectorHealth_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(ReportConnectorHealthRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(OperatorHealthServiceServer).ReportConnectorHealth(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: OperatorHealthService_ReportConnectorHealth_FullMethodName, - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(OperatorHealthServiceServer).ReportConnectorHealth(ctx, req.(*ReportConnectorHealthRequest)) - } - return interceptor(ctx, in, info, handler) -} - // OperatorHealthService_ServiceDesc is the grpc.ServiceDesc for OperatorHealthService service. // It's only intended for direct use with grpc.RegisterService, // and not to be introspected or modified (even as a copy) @@ -136,10 +103,6 @@ var OperatorHealthService_ServiceDesc = grpc.ServiceDesc{ MethodName: "ReportHealth", Handler: _OperatorHealthService_ReportHealth_Handler, }, - { - MethodName: "ReportConnectorHealth", - Handler: _OperatorHealthService_ReportConnectorHealth_Handler, - }, }, Streams: []grpc.StreamDesc{}, Metadata: "api/v1/operator_health.proto", diff --git a/gen/google/type/money.pb.go b/gen/google/type/money.pb.go deleted file mode 100644 index 0d41d5ee..00000000 --- a/gen/google/type/money.pb.go +++ /dev/null @@ -1,195 +0,0 @@ -// Copyright 2025 Google LLC -// -// 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. - -// Code generated by protoc-gen-go. DO NOT EDIT. -// versions: -// protoc-gen-go v1.31.0 -// protoc (unknown) -// source: google/type/money.proto - -package money - -import ( - protoreflect "google.golang.org/protobuf/reflect/protoreflect" - protoimpl "google.golang.org/protobuf/runtime/protoimpl" - reflect "reflect" - sync "sync" -) - -const ( - // Verify that this generated code is sufficiently up-to-date. - _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) - // Verify that runtime/protoimpl is sufficiently up-to-date. - _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) -) - -// Represents an amount of money with its currency type. -type Money struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - // The three-letter currency code defined in ISO 4217. - CurrencyCode string `protobuf:"bytes,1,opt,name=currency_code,json=currencyCode,proto3" json:"currency_code,omitempty"` - // The whole units of the amount. - // For example if `currencyCode` is `"USD"`, then 1 unit is one US dollar. - Units int64 `protobuf:"varint,2,opt,name=units,proto3" json:"units,omitempty"` - // Number of nano (10^-9) units of the amount. - // The value must be between -999,999,999 and +999,999,999 inclusive. - // If `units` is positive, `nanos` must be positive or zero. - // If `units` is zero, `nanos` can be positive, zero, or negative. - // If `units` is negative, `nanos` must be negative or zero. - // For example $-1.75 is represented as `units`=-1 and `nanos`=-750,000,000. - Nanos int32 `protobuf:"varint,3,opt,name=nanos,proto3" json:"nanos,omitempty"` -} - -func (x *Money) Reset() { - *x = Money{} - if protoimpl.UnsafeEnabled { - mi := &file_google_type_money_proto_msgTypes[0] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *Money) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*Money) ProtoMessage() {} - -func (x *Money) ProtoReflect() protoreflect.Message { - mi := &file_google_type_money_proto_msgTypes[0] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use Money.ProtoReflect.Descriptor instead. -func (*Money) Descriptor() ([]byte, []int) { - return file_google_type_money_proto_rawDescGZIP(), []int{0} -} - -func (x *Money) GetCurrencyCode() string { - if x != nil { - return x.CurrencyCode - } - return "" -} - -func (x *Money) GetUnits() int64 { - if x != nil { - return x.Units - } - return 0 -} - -func (x *Money) GetNanos() int32 { - if x != nil { - return x.Nanos - } - return 0 -} - -var File_google_type_money_proto protoreflect.FileDescriptor - -var file_google_type_money_proto_rawDesc = []byte{ - 0x0a, 0x17, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x74, 0x79, 0x70, 0x65, 0x2f, 0x6d, 0x6f, - 0x6e, 0x65, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0b, 0x67, 0x6f, 0x6f, 0x67, 0x6c, - 0x65, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x22, 0x58, 0x0a, 0x05, 0x4d, 0x6f, 0x6e, 0x65, 0x79, 0x12, - 0x23, 0x0a, 0x0d, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x5f, 0x63, 0x6f, 0x64, 0x65, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, - 0x43, 0x6f, 0x64, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x75, 0x6e, 0x69, 0x74, 0x73, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x03, 0x52, 0x05, 0x75, 0x6e, 0x69, 0x74, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x6e, 0x61, - 0x6e, 0x6f, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x6e, 0x61, 0x6e, 0x6f, 0x73, - 0x42, 0xa5, 0x01, 0x0a, 0x0f, 0x63, 0x6f, 0x6d, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, - 0x74, 0x79, 0x70, 0x65, 0x42, 0x0a, 0x4d, 0x6f, 0x6e, 0x65, 0x79, 0x50, 0x72, 0x6f, 0x74, 0x6f, - 0x50, 0x01, 0x5a, 0x36, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x67, 0x6f, 0x6c, 0x61, 0x6e, - 0x67, 0x2e, 0x6f, 0x72, 0x67, 0x2f, 0x67, 0x65, 0x6e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x67, - 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x61, 0x70, 0x69, 0x73, 0x2f, 0x74, 0x79, 0x70, 0x65, 0x2f, 0x6d, - 0x6f, 0x6e, 0x65, 0x79, 0x3b, 0x6d, 0x6f, 0x6e, 0x65, 0x79, 0xf8, 0x01, 0x01, 0xa2, 0x02, 0x03, - 0x47, 0x54, 0x58, 0xaa, 0x02, 0x0b, 0x47, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x54, 0x79, 0x70, - 0x65, 0xca, 0x02, 0x0b, 0x47, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x5c, 0x54, 0x79, 0x70, 0x65, 0xe2, - 0x02, 0x17, 0x47, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x5c, 0x54, 0x79, 0x70, 0x65, 0x5c, 0x47, 0x50, - 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x0c, 0x47, 0x6f, 0x6f, 0x67, - 0x6c, 0x65, 0x3a, 0x3a, 0x54, 0x79, 0x70, 0x65, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, -} - -var ( - file_google_type_money_proto_rawDescOnce sync.Once - file_google_type_money_proto_rawDescData = file_google_type_money_proto_rawDesc -) - -func file_google_type_money_proto_rawDescGZIP() []byte { - file_google_type_money_proto_rawDescOnce.Do(func() { - file_google_type_money_proto_rawDescData = protoimpl.X.CompressGZIP(file_google_type_money_proto_rawDescData) - }) - return file_google_type_money_proto_rawDescData -} - -var file_google_type_money_proto_msgTypes = make([]protoimpl.MessageInfo, 1) -var file_google_type_money_proto_goTypes = []interface{}{ - (*Money)(nil), // 0: google.type.Money -} -var file_google_type_money_proto_depIdxs = []int32{ - 0, // [0:0] is the sub-list for method output_type - 0, // [0:0] is the sub-list for method input_type - 0, // [0:0] is the sub-list for extension type_name - 0, // [0:0] is the sub-list for extension extendee - 0, // [0:0] is the sub-list for field type_name -} - -func init() { file_google_type_money_proto_init() } -func file_google_type_money_proto_init() { - if File_google_type_money_proto != nil { - return - } - if !protoimpl.UnsafeEnabled { - file_google_type_money_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Money); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - } - type x struct{} - out := protoimpl.TypeBuilder{ - File: protoimpl.DescBuilder{ - GoPackagePath: reflect.TypeOf(x{}).PkgPath(), - RawDescriptor: file_google_type_money_proto_rawDesc, - NumEnums: 0, - NumMessages: 1, - NumExtensions: 0, - NumServices: 0, - }, - GoTypes: file_google_type_money_proto_goTypes, - DependencyIndexes: file_google_type_money_proto_depIdxs, - MessageInfos: file_google_type_money_proto_msgTypes, - }.Build() - File_google_type_money_proto = out.File - file_google_type_money_proto_rawDesc = nil - file_google_type_money_proto_goTypes = nil - file_google_type_money_proto_depIdxs = nil -} diff --git a/gen/io/prometheus/client/metrics.pb.go b/gen/io/prometheus/client/metrics.pb.go deleted file mode 100644 index 5faee871..00000000 --- a/gen/io/prometheus/client/metrics.pb.go +++ /dev/null @@ -1,1407 +0,0 @@ -// Copyright 2013 Prometheus Team -// 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. - -// Code generated by protoc-gen-go. DO NOT EDIT. -// versions: -// protoc-gen-go v1.31.0 -// protoc (unknown) -// source: io/prometheus/client/metrics.proto - -package io_prometheus_client - -import ( - protoreflect "google.golang.org/protobuf/reflect/protoreflect" - protoimpl "google.golang.org/protobuf/runtime/protoimpl" - timestamppb "google.golang.org/protobuf/types/known/timestamppb" - reflect "reflect" - sync "sync" -) - -const ( - // Verify that this generated code is sufficiently up-to-date. - _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) - // Verify that runtime/protoimpl is sufficiently up-to-date. - _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) -) - -type MetricType int32 - -const ( - // COUNTER must use the Metric field "counter". - MetricType_COUNTER MetricType = 0 - // GAUGE must use the Metric field "gauge". - MetricType_GAUGE MetricType = 1 - // SUMMARY must use the Metric field "summary". - MetricType_SUMMARY MetricType = 2 - // UNTYPED must use the Metric field "untyped". - MetricType_UNTYPED MetricType = 3 - // HISTOGRAM must use the Metric field "histogram". - MetricType_HISTOGRAM MetricType = 4 - // GAUGE_HISTOGRAM must use the Metric field "histogram". - MetricType_GAUGE_HISTOGRAM MetricType = 5 -) - -// Enum value maps for MetricType. -var ( - MetricType_name = map[int32]string{ - 0: "COUNTER", - 1: "GAUGE", - 2: "SUMMARY", - 3: "UNTYPED", - 4: "HISTOGRAM", - 5: "GAUGE_HISTOGRAM", - } - MetricType_value = map[string]int32{ - "COUNTER": 0, - "GAUGE": 1, - "SUMMARY": 2, - "UNTYPED": 3, - "HISTOGRAM": 4, - "GAUGE_HISTOGRAM": 5, - } -) - -func (x MetricType) Enum() *MetricType { - p := new(MetricType) - *p = x - return p -} - -func (x MetricType) String() string { - return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) -} - -func (MetricType) Descriptor() protoreflect.EnumDescriptor { - return file_io_prometheus_client_metrics_proto_enumTypes[0].Descriptor() -} - -func (MetricType) Type() protoreflect.EnumType { - return &file_io_prometheus_client_metrics_proto_enumTypes[0] -} - -func (x MetricType) Number() protoreflect.EnumNumber { - return protoreflect.EnumNumber(x) -} - -// Deprecated: Do not use. -func (x *MetricType) UnmarshalJSON(b []byte) error { - num, err := protoimpl.X.UnmarshalJSONEnum(x.Descriptor(), b) - if err != nil { - return err - } - *x = MetricType(num) - return nil -} - -// Deprecated: Use MetricType.Descriptor instead. -func (MetricType) EnumDescriptor() ([]byte, []int) { - return file_io_prometheus_client_metrics_proto_rawDescGZIP(), []int{0} -} - -type LabelPair struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Name *string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"` - Value *string `protobuf:"bytes,2,opt,name=value" json:"value,omitempty"` -} - -func (x *LabelPair) Reset() { - *x = LabelPair{} - if protoimpl.UnsafeEnabled { - mi := &file_io_prometheus_client_metrics_proto_msgTypes[0] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *LabelPair) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*LabelPair) ProtoMessage() {} - -func (x *LabelPair) ProtoReflect() protoreflect.Message { - mi := &file_io_prometheus_client_metrics_proto_msgTypes[0] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use LabelPair.ProtoReflect.Descriptor instead. -func (*LabelPair) Descriptor() ([]byte, []int) { - return file_io_prometheus_client_metrics_proto_rawDescGZIP(), []int{0} -} - -func (x *LabelPair) GetName() string { - if x != nil && x.Name != nil { - return *x.Name - } - return "" -} - -func (x *LabelPair) GetValue() string { - if x != nil && x.Value != nil { - return *x.Value - } - return "" -} - -type Gauge struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Value *float64 `protobuf:"fixed64,1,opt,name=value" json:"value,omitempty"` -} - -func (x *Gauge) Reset() { - *x = Gauge{} - if protoimpl.UnsafeEnabled { - mi := &file_io_prometheus_client_metrics_proto_msgTypes[1] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *Gauge) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*Gauge) ProtoMessage() {} - -func (x *Gauge) ProtoReflect() protoreflect.Message { - mi := &file_io_prometheus_client_metrics_proto_msgTypes[1] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use Gauge.ProtoReflect.Descriptor instead. -func (*Gauge) Descriptor() ([]byte, []int) { - return file_io_prometheus_client_metrics_proto_rawDescGZIP(), []int{1} -} - -func (x *Gauge) GetValue() float64 { - if x != nil && x.Value != nil { - return *x.Value - } - return 0 -} - -type Counter struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Value *float64 `protobuf:"fixed64,1,opt,name=value" json:"value,omitempty"` - Exemplar *Exemplar `protobuf:"bytes,2,opt,name=exemplar" json:"exemplar,omitempty"` - CreatedTimestamp *timestamppb.Timestamp `protobuf:"bytes,3,opt,name=created_timestamp,json=createdTimestamp" json:"created_timestamp,omitempty"` -} - -func (x *Counter) Reset() { - *x = Counter{} - if protoimpl.UnsafeEnabled { - mi := &file_io_prometheus_client_metrics_proto_msgTypes[2] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *Counter) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*Counter) ProtoMessage() {} - -func (x *Counter) ProtoReflect() protoreflect.Message { - mi := &file_io_prometheus_client_metrics_proto_msgTypes[2] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use Counter.ProtoReflect.Descriptor instead. -func (*Counter) Descriptor() ([]byte, []int) { - return file_io_prometheus_client_metrics_proto_rawDescGZIP(), []int{2} -} - -func (x *Counter) GetValue() float64 { - if x != nil && x.Value != nil { - return *x.Value - } - return 0 -} - -func (x *Counter) GetExemplar() *Exemplar { - if x != nil { - return x.Exemplar - } - return nil -} - -func (x *Counter) GetCreatedTimestamp() *timestamppb.Timestamp { - if x != nil { - return x.CreatedTimestamp - } - return nil -} - -type Quantile struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Quantile *float64 `protobuf:"fixed64,1,opt,name=quantile" json:"quantile,omitempty"` - Value *float64 `protobuf:"fixed64,2,opt,name=value" json:"value,omitempty"` -} - -func (x *Quantile) Reset() { - *x = Quantile{} - if protoimpl.UnsafeEnabled { - mi := &file_io_prometheus_client_metrics_proto_msgTypes[3] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *Quantile) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*Quantile) ProtoMessage() {} - -func (x *Quantile) ProtoReflect() protoreflect.Message { - mi := &file_io_prometheus_client_metrics_proto_msgTypes[3] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use Quantile.ProtoReflect.Descriptor instead. -func (*Quantile) Descriptor() ([]byte, []int) { - return file_io_prometheus_client_metrics_proto_rawDescGZIP(), []int{3} -} - -func (x *Quantile) GetQuantile() float64 { - if x != nil && x.Quantile != nil { - return *x.Quantile - } - return 0 -} - -func (x *Quantile) GetValue() float64 { - if x != nil && x.Value != nil { - return *x.Value - } - return 0 -} - -type Summary struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - SampleCount *uint64 `protobuf:"varint,1,opt,name=sample_count,json=sampleCount" json:"sample_count,omitempty"` - SampleSum *float64 `protobuf:"fixed64,2,opt,name=sample_sum,json=sampleSum" json:"sample_sum,omitempty"` - Quantile []*Quantile `protobuf:"bytes,3,rep,name=quantile" json:"quantile,omitempty"` - CreatedTimestamp *timestamppb.Timestamp `protobuf:"bytes,4,opt,name=created_timestamp,json=createdTimestamp" json:"created_timestamp,omitempty"` -} - -func (x *Summary) Reset() { - *x = Summary{} - if protoimpl.UnsafeEnabled { - mi := &file_io_prometheus_client_metrics_proto_msgTypes[4] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *Summary) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*Summary) ProtoMessage() {} - -func (x *Summary) ProtoReflect() protoreflect.Message { - mi := &file_io_prometheus_client_metrics_proto_msgTypes[4] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use Summary.ProtoReflect.Descriptor instead. -func (*Summary) Descriptor() ([]byte, []int) { - return file_io_prometheus_client_metrics_proto_rawDescGZIP(), []int{4} -} - -func (x *Summary) GetSampleCount() uint64 { - if x != nil && x.SampleCount != nil { - return *x.SampleCount - } - return 0 -} - -func (x *Summary) GetSampleSum() float64 { - if x != nil && x.SampleSum != nil { - return *x.SampleSum - } - return 0 -} - -func (x *Summary) GetQuantile() []*Quantile { - if x != nil { - return x.Quantile - } - return nil -} - -func (x *Summary) GetCreatedTimestamp() *timestamppb.Timestamp { - if x != nil { - return x.CreatedTimestamp - } - return nil -} - -type Untyped struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Value *float64 `protobuf:"fixed64,1,opt,name=value" json:"value,omitempty"` -} - -func (x *Untyped) Reset() { - *x = Untyped{} - if protoimpl.UnsafeEnabled { - mi := &file_io_prometheus_client_metrics_proto_msgTypes[5] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *Untyped) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*Untyped) ProtoMessage() {} - -func (x *Untyped) ProtoReflect() protoreflect.Message { - mi := &file_io_prometheus_client_metrics_proto_msgTypes[5] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use Untyped.ProtoReflect.Descriptor instead. -func (*Untyped) Descriptor() ([]byte, []int) { - return file_io_prometheus_client_metrics_proto_rawDescGZIP(), []int{5} -} - -func (x *Untyped) GetValue() float64 { - if x != nil && x.Value != nil { - return *x.Value - } - return 0 -} - -type Histogram struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - SampleCount *uint64 `protobuf:"varint,1,opt,name=sample_count,json=sampleCount" json:"sample_count,omitempty"` - SampleCountFloat *float64 `protobuf:"fixed64,4,opt,name=sample_count_float,json=sampleCountFloat" json:"sample_count_float,omitempty"` // Overrides sample_count if > 0. - SampleSum *float64 `protobuf:"fixed64,2,opt,name=sample_sum,json=sampleSum" json:"sample_sum,omitempty"` - // Buckets for the conventional histogram. - Bucket []*Bucket `protobuf:"bytes,3,rep,name=bucket" json:"bucket,omitempty"` // Ordered in increasing order of upper_bound, +Inf bucket is optional. - CreatedTimestamp *timestamppb.Timestamp `protobuf:"bytes,15,opt,name=created_timestamp,json=createdTimestamp" json:"created_timestamp,omitempty"` - // schema defines the bucket schema. Currently, valid numbers are -4 <= n <= 8. - // They are all for base-2 bucket schemas, where 1 is a bucket boundary in each case, and - // then each power of two is divided into 2^n logarithmic buckets. - // Or in other words, each bucket boundary is the previous boundary times 2^(2^-n). - // In the future, more bucket schemas may be added using numbers < -4 or > 8. - Schema *int32 `protobuf:"zigzag32,5,opt,name=schema" json:"schema,omitempty"` - ZeroThreshold *float64 `protobuf:"fixed64,6,opt,name=zero_threshold,json=zeroThreshold" json:"zero_threshold,omitempty"` // Breadth of the zero bucket. - ZeroCount *uint64 `protobuf:"varint,7,opt,name=zero_count,json=zeroCount" json:"zero_count,omitempty"` // Count in zero bucket. - ZeroCountFloat *float64 `protobuf:"fixed64,8,opt,name=zero_count_float,json=zeroCountFloat" json:"zero_count_float,omitempty"` // Overrides sb_zero_count if > 0. - // Negative buckets for the native histogram. - NegativeSpan []*BucketSpan `protobuf:"bytes,9,rep,name=negative_span,json=negativeSpan" json:"negative_span,omitempty"` - // Use either "negative_delta" or "negative_count", the former for - // regular histograms with integer counts, the latter for float - // histograms. - NegativeDelta []int64 `protobuf:"zigzag64,10,rep,name=negative_delta,json=negativeDelta" json:"negative_delta,omitempty"` // Count delta of each bucket compared to previous one (or to zero for 1st bucket). - NegativeCount []float64 `protobuf:"fixed64,11,rep,name=negative_count,json=negativeCount" json:"negative_count,omitempty"` // Absolute count of each bucket. - // Positive buckets for the native histogram. - // Use a no-op span (offset 0, length 0) for a native histogram without any - // observations yet and with a zero_threshold of 0. Otherwise, it would be - // indistinguishable from a classic histogram. - PositiveSpan []*BucketSpan `protobuf:"bytes,12,rep,name=positive_span,json=positiveSpan" json:"positive_span,omitempty"` - // Use either "positive_delta" or "positive_count", the former for - // regular histograms with integer counts, the latter for float - // histograms. - PositiveDelta []int64 `protobuf:"zigzag64,13,rep,name=positive_delta,json=positiveDelta" json:"positive_delta,omitempty"` // Count delta of each bucket compared to previous one (or to zero for 1st bucket). - PositiveCount []float64 `protobuf:"fixed64,14,rep,name=positive_count,json=positiveCount" json:"positive_count,omitempty"` // Absolute count of each bucket. - // Only used for native histograms. These exemplars MUST have a timestamp. - Exemplars []*Exemplar `protobuf:"bytes,16,rep,name=exemplars" json:"exemplars,omitempty"` -} - -func (x *Histogram) Reset() { - *x = Histogram{} - if protoimpl.UnsafeEnabled { - mi := &file_io_prometheus_client_metrics_proto_msgTypes[6] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *Histogram) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*Histogram) ProtoMessage() {} - -func (x *Histogram) ProtoReflect() protoreflect.Message { - mi := &file_io_prometheus_client_metrics_proto_msgTypes[6] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use Histogram.ProtoReflect.Descriptor instead. -func (*Histogram) Descriptor() ([]byte, []int) { - return file_io_prometheus_client_metrics_proto_rawDescGZIP(), []int{6} -} - -func (x *Histogram) GetSampleCount() uint64 { - if x != nil && x.SampleCount != nil { - return *x.SampleCount - } - return 0 -} - -func (x *Histogram) GetSampleCountFloat() float64 { - if x != nil && x.SampleCountFloat != nil { - return *x.SampleCountFloat - } - return 0 -} - -func (x *Histogram) GetSampleSum() float64 { - if x != nil && x.SampleSum != nil { - return *x.SampleSum - } - return 0 -} - -func (x *Histogram) GetBucket() []*Bucket { - if x != nil { - return x.Bucket - } - return nil -} - -func (x *Histogram) GetCreatedTimestamp() *timestamppb.Timestamp { - if x != nil { - return x.CreatedTimestamp - } - return nil -} - -func (x *Histogram) GetSchema() int32 { - if x != nil && x.Schema != nil { - return *x.Schema - } - return 0 -} - -func (x *Histogram) GetZeroThreshold() float64 { - if x != nil && x.ZeroThreshold != nil { - return *x.ZeroThreshold - } - return 0 -} - -func (x *Histogram) GetZeroCount() uint64 { - if x != nil && x.ZeroCount != nil { - return *x.ZeroCount - } - return 0 -} - -func (x *Histogram) GetZeroCountFloat() float64 { - if x != nil && x.ZeroCountFloat != nil { - return *x.ZeroCountFloat - } - return 0 -} - -func (x *Histogram) GetNegativeSpan() []*BucketSpan { - if x != nil { - return x.NegativeSpan - } - return nil -} - -func (x *Histogram) GetNegativeDelta() []int64 { - if x != nil { - return x.NegativeDelta - } - return nil -} - -func (x *Histogram) GetNegativeCount() []float64 { - if x != nil { - return x.NegativeCount - } - return nil -} - -func (x *Histogram) GetPositiveSpan() []*BucketSpan { - if x != nil { - return x.PositiveSpan - } - return nil -} - -func (x *Histogram) GetPositiveDelta() []int64 { - if x != nil { - return x.PositiveDelta - } - return nil -} - -func (x *Histogram) GetPositiveCount() []float64 { - if x != nil { - return x.PositiveCount - } - return nil -} - -func (x *Histogram) GetExemplars() []*Exemplar { - if x != nil { - return x.Exemplars - } - return nil -} - -// A Bucket of a conventional histogram, each of which is treated as -// an individual counter-like time series by Prometheus. -type Bucket struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - CumulativeCount *uint64 `protobuf:"varint,1,opt,name=cumulative_count,json=cumulativeCount" json:"cumulative_count,omitempty"` // Cumulative in increasing order. - CumulativeCountFloat *float64 `protobuf:"fixed64,4,opt,name=cumulative_count_float,json=cumulativeCountFloat" json:"cumulative_count_float,omitempty"` // Overrides cumulative_count if > 0. - UpperBound *float64 `protobuf:"fixed64,2,opt,name=upper_bound,json=upperBound" json:"upper_bound,omitempty"` // Inclusive. - Exemplar *Exemplar `protobuf:"bytes,3,opt,name=exemplar" json:"exemplar,omitempty"` -} - -func (x *Bucket) Reset() { - *x = Bucket{} - if protoimpl.UnsafeEnabled { - mi := &file_io_prometheus_client_metrics_proto_msgTypes[7] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *Bucket) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*Bucket) ProtoMessage() {} - -func (x *Bucket) ProtoReflect() protoreflect.Message { - mi := &file_io_prometheus_client_metrics_proto_msgTypes[7] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use Bucket.ProtoReflect.Descriptor instead. -func (*Bucket) Descriptor() ([]byte, []int) { - return file_io_prometheus_client_metrics_proto_rawDescGZIP(), []int{7} -} - -func (x *Bucket) GetCumulativeCount() uint64 { - if x != nil && x.CumulativeCount != nil { - return *x.CumulativeCount - } - return 0 -} - -func (x *Bucket) GetCumulativeCountFloat() float64 { - if x != nil && x.CumulativeCountFloat != nil { - return *x.CumulativeCountFloat - } - return 0 -} - -func (x *Bucket) GetUpperBound() float64 { - if x != nil && x.UpperBound != nil { - return *x.UpperBound - } - return 0 -} - -func (x *Bucket) GetExemplar() *Exemplar { - if x != nil { - return x.Exemplar - } - return nil -} - -// A BucketSpan defines a number of consecutive buckets in a native -// histogram with their offset. Logically, it would be more -// straightforward to include the bucket counts in the Span. However, -// the protobuf representation is more compact in the way the data is -// structured here (with all the buckets in a single array separate -// from the Spans). -type BucketSpan struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Offset *int32 `protobuf:"zigzag32,1,opt,name=offset" json:"offset,omitempty"` // Gap to previous span, or starting point for 1st span (which can be negative). - Length *uint32 `protobuf:"varint,2,opt,name=length" json:"length,omitempty"` // Length of consecutive buckets. -} - -func (x *BucketSpan) Reset() { - *x = BucketSpan{} - if protoimpl.UnsafeEnabled { - mi := &file_io_prometheus_client_metrics_proto_msgTypes[8] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *BucketSpan) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*BucketSpan) ProtoMessage() {} - -func (x *BucketSpan) ProtoReflect() protoreflect.Message { - mi := &file_io_prometheus_client_metrics_proto_msgTypes[8] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use BucketSpan.ProtoReflect.Descriptor instead. -func (*BucketSpan) Descriptor() ([]byte, []int) { - return file_io_prometheus_client_metrics_proto_rawDescGZIP(), []int{8} -} - -func (x *BucketSpan) GetOffset() int32 { - if x != nil && x.Offset != nil { - return *x.Offset - } - return 0 -} - -func (x *BucketSpan) GetLength() uint32 { - if x != nil && x.Length != nil { - return *x.Length - } - return 0 -} - -type Exemplar struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Label []*LabelPair `protobuf:"bytes,1,rep,name=label" json:"label,omitempty"` - Value *float64 `protobuf:"fixed64,2,opt,name=value" json:"value,omitempty"` - Timestamp *timestamppb.Timestamp `protobuf:"bytes,3,opt,name=timestamp" json:"timestamp,omitempty"` // OpenMetrics-style. -} - -func (x *Exemplar) Reset() { - *x = Exemplar{} - if protoimpl.UnsafeEnabled { - mi := &file_io_prometheus_client_metrics_proto_msgTypes[9] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *Exemplar) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*Exemplar) ProtoMessage() {} - -func (x *Exemplar) ProtoReflect() protoreflect.Message { - mi := &file_io_prometheus_client_metrics_proto_msgTypes[9] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use Exemplar.ProtoReflect.Descriptor instead. -func (*Exemplar) Descriptor() ([]byte, []int) { - return file_io_prometheus_client_metrics_proto_rawDescGZIP(), []int{9} -} - -func (x *Exemplar) GetLabel() []*LabelPair { - if x != nil { - return x.Label - } - return nil -} - -func (x *Exemplar) GetValue() float64 { - if x != nil && x.Value != nil { - return *x.Value - } - return 0 -} - -func (x *Exemplar) GetTimestamp() *timestamppb.Timestamp { - if x != nil { - return x.Timestamp - } - return nil -} - -type Metric struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Label []*LabelPair `protobuf:"bytes,1,rep,name=label" json:"label,omitempty"` - Gauge *Gauge `protobuf:"bytes,2,opt,name=gauge" json:"gauge,omitempty"` - Counter *Counter `protobuf:"bytes,3,opt,name=counter" json:"counter,omitempty"` - Summary *Summary `protobuf:"bytes,4,opt,name=summary" json:"summary,omitempty"` - Untyped *Untyped `protobuf:"bytes,5,opt,name=untyped" json:"untyped,omitempty"` - Histogram *Histogram `protobuf:"bytes,7,opt,name=histogram" json:"histogram,omitempty"` - TimestampMs *int64 `protobuf:"varint,6,opt,name=timestamp_ms,json=timestampMs" json:"timestamp_ms,omitempty"` -} - -func (x *Metric) Reset() { - *x = Metric{} - if protoimpl.UnsafeEnabled { - mi := &file_io_prometheus_client_metrics_proto_msgTypes[10] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *Metric) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*Metric) ProtoMessage() {} - -func (x *Metric) ProtoReflect() protoreflect.Message { - mi := &file_io_prometheus_client_metrics_proto_msgTypes[10] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use Metric.ProtoReflect.Descriptor instead. -func (*Metric) Descriptor() ([]byte, []int) { - return file_io_prometheus_client_metrics_proto_rawDescGZIP(), []int{10} -} - -func (x *Metric) GetLabel() []*LabelPair { - if x != nil { - return x.Label - } - return nil -} - -func (x *Metric) GetGauge() *Gauge { - if x != nil { - return x.Gauge - } - return nil -} - -func (x *Metric) GetCounter() *Counter { - if x != nil { - return x.Counter - } - return nil -} - -func (x *Metric) GetSummary() *Summary { - if x != nil { - return x.Summary - } - return nil -} - -func (x *Metric) GetUntyped() *Untyped { - if x != nil { - return x.Untyped - } - return nil -} - -func (x *Metric) GetHistogram() *Histogram { - if x != nil { - return x.Histogram - } - return nil -} - -func (x *Metric) GetTimestampMs() int64 { - if x != nil && x.TimestampMs != nil { - return *x.TimestampMs - } - return 0 -} - -type MetricFamily struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Name *string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"` - Help *string `protobuf:"bytes,2,opt,name=help" json:"help,omitempty"` - Type *MetricType `protobuf:"varint,3,opt,name=type,enum=io.prometheus.client.MetricType" json:"type,omitempty"` - Metric []*Metric `protobuf:"bytes,4,rep,name=metric" json:"metric,omitempty"` - Unit *string `protobuf:"bytes,5,opt,name=unit" json:"unit,omitempty"` -} - -func (x *MetricFamily) Reset() { - *x = MetricFamily{} - if protoimpl.UnsafeEnabled { - mi := &file_io_prometheus_client_metrics_proto_msgTypes[11] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *MetricFamily) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*MetricFamily) ProtoMessage() {} - -func (x *MetricFamily) ProtoReflect() protoreflect.Message { - mi := &file_io_prometheus_client_metrics_proto_msgTypes[11] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use MetricFamily.ProtoReflect.Descriptor instead. -func (*MetricFamily) Descriptor() ([]byte, []int) { - return file_io_prometheus_client_metrics_proto_rawDescGZIP(), []int{11} -} - -func (x *MetricFamily) GetName() string { - if x != nil && x.Name != nil { - return *x.Name - } - return "" -} - -func (x *MetricFamily) GetHelp() string { - if x != nil && x.Help != nil { - return *x.Help - } - return "" -} - -func (x *MetricFamily) GetType() MetricType { - if x != nil && x.Type != nil { - return *x.Type - } - return MetricType_COUNTER -} - -func (x *MetricFamily) GetMetric() []*Metric { - if x != nil { - return x.Metric - } - return nil -} - -func (x *MetricFamily) GetUnit() string { - if x != nil && x.Unit != nil { - return *x.Unit - } - return "" -} - -var File_io_prometheus_client_metrics_proto protoreflect.FileDescriptor - -var file_io_prometheus_client_metrics_proto_rawDesc = []byte{ - 0x0a, 0x22, 0x69, 0x6f, 0x2f, 0x70, 0x72, 0x6f, 0x6d, 0x65, 0x74, 0x68, 0x65, 0x75, 0x73, 0x2f, - 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x2f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2e, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x14, 0x69, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x6d, 0x65, 0x74, 0x68, - 0x65, 0x75, 0x73, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x1a, 0x1f, 0x67, 0x6f, 0x6f, 0x67, - 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x74, 0x69, 0x6d, 0x65, - 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x35, 0x0a, 0x09, 0x4c, - 0x61, 0x62, 0x65, 0x6c, 0x50, 0x61, 0x69, 0x72, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x14, 0x0a, 0x05, - 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, - 0x75, 0x65, 0x22, 0x1d, 0x0a, 0x05, 0x47, 0x61, 0x75, 0x67, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x76, - 0x61, 0x6c, 0x75, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x01, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, - 0x65, 0x22, 0xa4, 0x01, 0x0a, 0x07, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x12, 0x14, 0x0a, - 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x01, 0x52, 0x05, 0x76, 0x61, - 0x6c, 0x75, 0x65, 0x12, 0x3a, 0x0a, 0x08, 0x65, 0x78, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x72, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x69, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x6d, 0x65, - 0x74, 0x68, 0x65, 0x75, 0x73, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x2e, 0x45, 0x78, 0x65, - 0x6d, 0x70, 0x6c, 0x61, 0x72, 0x52, 0x08, 0x65, 0x78, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x72, 0x12, - 0x47, 0x0a, 0x11, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x73, - 0x74, 0x61, 0x6d, 0x70, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, - 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, - 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x10, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x54, - 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x22, 0x3c, 0x0a, 0x08, 0x51, 0x75, 0x61, 0x6e, - 0x74, 0x69, 0x6c, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x71, 0x75, 0x61, 0x6e, 0x74, 0x69, 0x6c, 0x65, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x01, 0x52, 0x08, 0x71, 0x75, 0x61, 0x6e, 0x74, 0x69, 0x6c, 0x65, - 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x01, 0x52, - 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0xd0, 0x01, 0x0a, 0x07, 0x53, 0x75, 0x6d, 0x6d, 0x61, - 0x72, 0x79, 0x12, 0x21, 0x0a, 0x0c, 0x73, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x5f, 0x63, 0x6f, 0x75, - 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0b, 0x73, 0x61, 0x6d, 0x70, 0x6c, 0x65, - 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x5f, - 0x73, 0x75, 0x6d, 0x18, 0x02, 0x20, 0x01, 0x28, 0x01, 0x52, 0x09, 0x73, 0x61, 0x6d, 0x70, 0x6c, - 0x65, 0x53, 0x75, 0x6d, 0x12, 0x3a, 0x0a, 0x08, 0x71, 0x75, 0x61, 0x6e, 0x74, 0x69, 0x6c, 0x65, - 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x69, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x6d, - 0x65, 0x74, 0x68, 0x65, 0x75, 0x73, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x2e, 0x51, 0x75, - 0x61, 0x6e, 0x74, 0x69, 0x6c, 0x65, 0x52, 0x08, 0x71, 0x75, 0x61, 0x6e, 0x74, 0x69, 0x6c, 0x65, - 0x12, 0x47, 0x0a, 0x11, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, - 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, - 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, - 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x10, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, - 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x22, 0x1f, 0x0a, 0x07, 0x55, 0x6e, 0x74, - 0x79, 0x70, 0x65, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x01, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0xea, 0x05, 0x0a, 0x09, 0x48, - 0x69, 0x73, 0x74, 0x6f, 0x67, 0x72, 0x61, 0x6d, 0x12, 0x21, 0x0a, 0x0c, 0x73, 0x61, 0x6d, 0x70, - 0x6c, 0x65, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0b, - 0x73, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x2c, 0x0a, 0x12, 0x73, - 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x66, 0x6c, 0x6f, 0x61, - 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x01, 0x52, 0x10, 0x73, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x43, - 0x6f, 0x75, 0x6e, 0x74, 0x46, 0x6c, 0x6f, 0x61, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x61, 0x6d, - 0x70, 0x6c, 0x65, 0x5f, 0x73, 0x75, 0x6d, 0x18, 0x02, 0x20, 0x01, 0x28, 0x01, 0x52, 0x09, 0x73, - 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x53, 0x75, 0x6d, 0x12, 0x34, 0x0a, 0x06, 0x62, 0x75, 0x63, 0x6b, - 0x65, 0x74, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x69, 0x6f, 0x2e, 0x70, 0x72, - 0x6f, 0x6d, 0x65, 0x74, 0x68, 0x65, 0x75, 0x73, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x2e, - 0x42, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x52, 0x06, 0x62, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x12, 0x47, - 0x0a, 0x11, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, - 0x61, 0x6d, 0x70, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, - 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, - 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x10, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x54, 0x69, - 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x63, 0x68, 0x65, 0x6d, - 0x61, 0x18, 0x05, 0x20, 0x01, 0x28, 0x11, 0x52, 0x06, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x12, - 0x25, 0x0a, 0x0e, 0x7a, 0x65, 0x72, 0x6f, 0x5f, 0x74, 0x68, 0x72, 0x65, 0x73, 0x68, 0x6f, 0x6c, - 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x01, 0x52, 0x0d, 0x7a, 0x65, 0x72, 0x6f, 0x54, 0x68, 0x72, - 0x65, 0x73, 0x68, 0x6f, 0x6c, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x7a, 0x65, 0x72, 0x6f, 0x5f, 0x63, - 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x07, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x7a, 0x65, 0x72, 0x6f, - 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x28, 0x0a, 0x10, 0x7a, 0x65, 0x72, 0x6f, 0x5f, 0x63, 0x6f, - 0x75, 0x6e, 0x74, 0x5f, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x18, 0x08, 0x20, 0x01, 0x28, 0x01, 0x52, - 0x0e, 0x7a, 0x65, 0x72, 0x6f, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x46, 0x6c, 0x6f, 0x61, 0x74, 0x12, - 0x45, 0x0a, 0x0d, 0x6e, 0x65, 0x67, 0x61, 0x74, 0x69, 0x76, 0x65, 0x5f, 0x73, 0x70, 0x61, 0x6e, - 0x18, 0x09, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x69, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x6d, - 0x65, 0x74, 0x68, 0x65, 0x75, 0x73, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x2e, 0x42, 0x75, - 0x63, 0x6b, 0x65, 0x74, 0x53, 0x70, 0x61, 0x6e, 0x52, 0x0c, 0x6e, 0x65, 0x67, 0x61, 0x74, 0x69, - 0x76, 0x65, 0x53, 0x70, 0x61, 0x6e, 0x12, 0x25, 0x0a, 0x0e, 0x6e, 0x65, 0x67, 0x61, 0x74, 0x69, - 0x76, 0x65, 0x5f, 0x64, 0x65, 0x6c, 0x74, 0x61, 0x18, 0x0a, 0x20, 0x03, 0x28, 0x12, 0x52, 0x0d, - 0x6e, 0x65, 0x67, 0x61, 0x74, 0x69, 0x76, 0x65, 0x44, 0x65, 0x6c, 0x74, 0x61, 0x12, 0x25, 0x0a, - 0x0e, 0x6e, 0x65, 0x67, 0x61, 0x74, 0x69, 0x76, 0x65, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, - 0x0b, 0x20, 0x03, 0x28, 0x01, 0x52, 0x0d, 0x6e, 0x65, 0x67, 0x61, 0x74, 0x69, 0x76, 0x65, 0x43, - 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x45, 0x0a, 0x0d, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x76, 0x65, - 0x5f, 0x73, 0x70, 0x61, 0x6e, 0x18, 0x0c, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x69, 0x6f, - 0x2e, 0x70, 0x72, 0x6f, 0x6d, 0x65, 0x74, 0x68, 0x65, 0x75, 0x73, 0x2e, 0x63, 0x6c, 0x69, 0x65, - 0x6e, 0x74, 0x2e, 0x42, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x53, 0x70, 0x61, 0x6e, 0x52, 0x0c, 0x70, - 0x6f, 0x73, 0x69, 0x74, 0x69, 0x76, 0x65, 0x53, 0x70, 0x61, 0x6e, 0x12, 0x25, 0x0a, 0x0e, 0x70, - 0x6f, 0x73, 0x69, 0x74, 0x69, 0x76, 0x65, 0x5f, 0x64, 0x65, 0x6c, 0x74, 0x61, 0x18, 0x0d, 0x20, - 0x03, 0x28, 0x12, 0x52, 0x0d, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x76, 0x65, 0x44, 0x65, 0x6c, - 0x74, 0x61, 0x12, 0x25, 0x0a, 0x0e, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x76, 0x65, 0x5f, 0x63, - 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x0e, 0x20, 0x03, 0x28, 0x01, 0x52, 0x0d, 0x70, 0x6f, 0x73, 0x69, - 0x74, 0x69, 0x76, 0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x3c, 0x0a, 0x09, 0x65, 0x78, 0x65, - 0x6d, 0x70, 0x6c, 0x61, 0x72, 0x73, 0x18, 0x10, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x69, - 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x6d, 0x65, 0x74, 0x68, 0x65, 0x75, 0x73, 0x2e, 0x63, 0x6c, 0x69, - 0x65, 0x6e, 0x74, 0x2e, 0x45, 0x78, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x72, 0x52, 0x09, 0x65, 0x78, - 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x72, 0x73, 0x22, 0xc6, 0x01, 0x0a, 0x06, 0x42, 0x75, 0x63, 0x6b, - 0x65, 0x74, 0x12, 0x29, 0x0a, 0x10, 0x63, 0x75, 0x6d, 0x75, 0x6c, 0x61, 0x74, 0x69, 0x76, 0x65, - 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0f, 0x63, 0x75, - 0x6d, 0x75, 0x6c, 0x61, 0x74, 0x69, 0x76, 0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x34, 0x0a, - 0x16, 0x63, 0x75, 0x6d, 0x75, 0x6c, 0x61, 0x74, 0x69, 0x76, 0x65, 0x5f, 0x63, 0x6f, 0x75, 0x6e, - 0x74, 0x5f, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x01, 0x52, 0x14, 0x63, - 0x75, 0x6d, 0x75, 0x6c, 0x61, 0x74, 0x69, 0x76, 0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x46, 0x6c, - 0x6f, 0x61, 0x74, 0x12, 0x1f, 0x0a, 0x0b, 0x75, 0x70, 0x70, 0x65, 0x72, 0x5f, 0x62, 0x6f, 0x75, - 0x6e, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x01, 0x52, 0x0a, 0x75, 0x70, 0x70, 0x65, 0x72, 0x42, - 0x6f, 0x75, 0x6e, 0x64, 0x12, 0x3a, 0x0a, 0x08, 0x65, 0x78, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x72, - 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x69, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x6d, - 0x65, 0x74, 0x68, 0x65, 0x75, 0x73, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x2e, 0x45, 0x78, - 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x72, 0x52, 0x08, 0x65, 0x78, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x72, - 0x22, 0x3c, 0x0a, 0x0a, 0x42, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x53, 0x70, 0x61, 0x6e, 0x12, 0x16, - 0x0a, 0x06, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x11, 0x52, 0x06, - 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x06, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x22, 0x91, - 0x01, 0x0a, 0x08, 0x45, 0x78, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x72, 0x12, 0x35, 0x0a, 0x05, 0x6c, - 0x61, 0x62, 0x65, 0x6c, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x69, 0x6f, 0x2e, - 0x70, 0x72, 0x6f, 0x6d, 0x65, 0x74, 0x68, 0x65, 0x75, 0x73, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, - 0x74, 0x2e, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x50, 0x61, 0x69, 0x72, 0x52, 0x05, 0x6c, 0x61, 0x62, - 0x65, 0x6c, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x01, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x38, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, - 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, - 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, - 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, - 0x6d, 0x70, 0x22, 0xff, 0x02, 0x0a, 0x06, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x12, 0x35, 0x0a, - 0x05, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x69, - 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x6d, 0x65, 0x74, 0x68, 0x65, 0x75, 0x73, 0x2e, 0x63, 0x6c, 0x69, - 0x65, 0x6e, 0x74, 0x2e, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x50, 0x61, 0x69, 0x72, 0x52, 0x05, 0x6c, - 0x61, 0x62, 0x65, 0x6c, 0x12, 0x31, 0x0a, 0x05, 0x67, 0x61, 0x75, 0x67, 0x65, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x69, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x6d, 0x65, 0x74, 0x68, - 0x65, 0x75, 0x73, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x2e, 0x47, 0x61, 0x75, 0x67, 0x65, - 0x52, 0x05, 0x67, 0x61, 0x75, 0x67, 0x65, 0x12, 0x37, 0x0a, 0x07, 0x63, 0x6f, 0x75, 0x6e, 0x74, - 0x65, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x69, 0x6f, 0x2e, 0x70, 0x72, - 0x6f, 0x6d, 0x65, 0x74, 0x68, 0x65, 0x75, 0x73, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x2e, - 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x52, 0x07, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, - 0x12, 0x37, 0x0a, 0x07, 0x73, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x1d, 0x2e, 0x69, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x6d, 0x65, 0x74, 0x68, 0x65, 0x75, - 0x73, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x2e, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, - 0x52, 0x07, 0x73, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x12, 0x37, 0x0a, 0x07, 0x75, 0x6e, 0x74, - 0x79, 0x70, 0x65, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x69, 0x6f, 0x2e, - 0x70, 0x72, 0x6f, 0x6d, 0x65, 0x74, 0x68, 0x65, 0x75, 0x73, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, - 0x74, 0x2e, 0x55, 0x6e, 0x74, 0x79, 0x70, 0x65, 0x64, 0x52, 0x07, 0x75, 0x6e, 0x74, 0x79, 0x70, - 0x65, 0x64, 0x12, 0x3d, 0x0a, 0x09, 0x68, 0x69, 0x73, 0x74, 0x6f, 0x67, 0x72, 0x61, 0x6d, 0x18, - 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x69, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x6d, 0x65, - 0x74, 0x68, 0x65, 0x75, 0x73, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x2e, 0x48, 0x69, 0x73, - 0x74, 0x6f, 0x67, 0x72, 0x61, 0x6d, 0x52, 0x09, 0x68, 0x69, 0x73, 0x74, 0x6f, 0x67, 0x72, 0x61, - 0x6d, 0x12, 0x21, 0x0a, 0x0c, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x5f, 0x6d, - 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0b, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, - 0x6d, 0x70, 0x4d, 0x73, 0x22, 0xb6, 0x01, 0x0a, 0x0c, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x46, - 0x61, 0x6d, 0x69, 0x6c, 0x79, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x68, 0x65, 0x6c, - 0x70, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x68, 0x65, 0x6c, 0x70, 0x12, 0x34, 0x0a, - 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x20, 0x2e, 0x69, 0x6f, - 0x2e, 0x70, 0x72, 0x6f, 0x6d, 0x65, 0x74, 0x68, 0x65, 0x75, 0x73, 0x2e, 0x63, 0x6c, 0x69, 0x65, - 0x6e, 0x74, 0x2e, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x54, 0x79, 0x70, 0x65, 0x52, 0x04, 0x74, - 0x79, 0x70, 0x65, 0x12, 0x34, 0x0a, 0x06, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x18, 0x04, 0x20, - 0x03, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x69, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x6d, 0x65, 0x74, 0x68, - 0x65, 0x75, 0x73, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x2e, 0x4d, 0x65, 0x74, 0x72, 0x69, - 0x63, 0x52, 0x06, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x12, 0x12, 0x0a, 0x04, 0x75, 0x6e, 0x69, - 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x75, 0x6e, 0x69, 0x74, 0x2a, 0x62, 0x0a, - 0x0a, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x54, 0x79, 0x70, 0x65, 0x12, 0x0b, 0x0a, 0x07, 0x43, - 0x4f, 0x55, 0x4e, 0x54, 0x45, 0x52, 0x10, 0x00, 0x12, 0x09, 0x0a, 0x05, 0x47, 0x41, 0x55, 0x47, - 0x45, 0x10, 0x01, 0x12, 0x0b, 0x0a, 0x07, 0x53, 0x55, 0x4d, 0x4d, 0x41, 0x52, 0x59, 0x10, 0x02, - 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x4e, 0x54, 0x59, 0x50, 0x45, 0x44, 0x10, 0x03, 0x12, 0x0d, 0x0a, - 0x09, 0x48, 0x49, 0x53, 0x54, 0x4f, 0x47, 0x52, 0x41, 0x4d, 0x10, 0x04, 0x12, 0x13, 0x0a, 0x0f, - 0x47, 0x41, 0x55, 0x47, 0x45, 0x5f, 0x48, 0x49, 0x53, 0x54, 0x4f, 0x47, 0x52, 0x41, 0x4d, 0x10, - 0x05, 0x42, 0xd6, 0x01, 0x0a, 0x18, 0x63, 0x6f, 0x6d, 0x2e, 0x69, 0x6f, 0x2e, 0x70, 0x72, 0x6f, - 0x6d, 0x65, 0x74, 0x68, 0x65, 0x75, 0x73, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x42, 0x0c, - 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x3a, - 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x70, 0x72, 0x6f, 0x6d, 0x65, - 0x74, 0x68, 0x65, 0x75, 0x73, 0x2f, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x5f, 0x6d, 0x6f, 0x64, - 0x65, 0x6c, 0x2f, 0x67, 0x6f, 0x3b, 0x69, 0x6f, 0x5f, 0x70, 0x72, 0x6f, 0x6d, 0x65, 0x74, 0x68, - 0x65, 0x75, 0x73, 0x5f, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0xa2, 0x02, 0x03, 0x49, 0x50, 0x43, - 0xaa, 0x02, 0x14, 0x49, 0x6f, 0x2e, 0x50, 0x72, 0x6f, 0x6d, 0x65, 0x74, 0x68, 0x65, 0x75, 0x73, - 0x2e, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0xca, 0x02, 0x14, 0x49, 0x6f, 0x5c, 0x50, 0x72, 0x6f, - 0x6d, 0x65, 0x74, 0x68, 0x65, 0x75, 0x73, 0x5c, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0xe2, 0x02, - 0x20, 0x49, 0x6f, 0x5c, 0x50, 0x72, 0x6f, 0x6d, 0x65, 0x74, 0x68, 0x65, 0x75, 0x73, 0x5c, 0x43, - 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, - 0x61, 0xea, 0x02, 0x16, 0x49, 0x6f, 0x3a, 0x3a, 0x50, 0x72, 0x6f, 0x6d, 0x65, 0x74, 0x68, 0x65, - 0x75, 0x73, 0x3a, 0x3a, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, -} - -var ( - file_io_prometheus_client_metrics_proto_rawDescOnce sync.Once - file_io_prometheus_client_metrics_proto_rawDescData = file_io_prometheus_client_metrics_proto_rawDesc -) - -func file_io_prometheus_client_metrics_proto_rawDescGZIP() []byte { - file_io_prometheus_client_metrics_proto_rawDescOnce.Do(func() { - file_io_prometheus_client_metrics_proto_rawDescData = protoimpl.X.CompressGZIP(file_io_prometheus_client_metrics_proto_rawDescData) - }) - return file_io_prometheus_client_metrics_proto_rawDescData -} - -var file_io_prometheus_client_metrics_proto_enumTypes = make([]protoimpl.EnumInfo, 1) -var file_io_prometheus_client_metrics_proto_msgTypes = make([]protoimpl.MessageInfo, 12) -var file_io_prometheus_client_metrics_proto_goTypes = []interface{}{ - (MetricType)(0), // 0: io.prometheus.client.MetricType - (*LabelPair)(nil), // 1: io.prometheus.client.LabelPair - (*Gauge)(nil), // 2: io.prometheus.client.Gauge - (*Counter)(nil), // 3: io.prometheus.client.Counter - (*Quantile)(nil), // 4: io.prometheus.client.Quantile - (*Summary)(nil), // 5: io.prometheus.client.Summary - (*Untyped)(nil), // 6: io.prometheus.client.Untyped - (*Histogram)(nil), // 7: io.prometheus.client.Histogram - (*Bucket)(nil), // 8: io.prometheus.client.Bucket - (*BucketSpan)(nil), // 9: io.prometheus.client.BucketSpan - (*Exemplar)(nil), // 10: io.prometheus.client.Exemplar - (*Metric)(nil), // 11: io.prometheus.client.Metric - (*MetricFamily)(nil), // 12: io.prometheus.client.MetricFamily - (*timestamppb.Timestamp)(nil), // 13: google.protobuf.Timestamp -} -var file_io_prometheus_client_metrics_proto_depIdxs = []int32{ - 10, // 0: io.prometheus.client.Counter.exemplar:type_name -> io.prometheus.client.Exemplar - 13, // 1: io.prometheus.client.Counter.created_timestamp:type_name -> google.protobuf.Timestamp - 4, // 2: io.prometheus.client.Summary.quantile:type_name -> io.prometheus.client.Quantile - 13, // 3: io.prometheus.client.Summary.created_timestamp:type_name -> google.protobuf.Timestamp - 8, // 4: io.prometheus.client.Histogram.bucket:type_name -> io.prometheus.client.Bucket - 13, // 5: io.prometheus.client.Histogram.created_timestamp:type_name -> google.protobuf.Timestamp - 9, // 6: io.prometheus.client.Histogram.negative_span:type_name -> io.prometheus.client.BucketSpan - 9, // 7: io.prometheus.client.Histogram.positive_span:type_name -> io.prometheus.client.BucketSpan - 10, // 8: io.prometheus.client.Histogram.exemplars:type_name -> io.prometheus.client.Exemplar - 10, // 9: io.prometheus.client.Bucket.exemplar:type_name -> io.prometheus.client.Exemplar - 1, // 10: io.prometheus.client.Exemplar.label:type_name -> io.prometheus.client.LabelPair - 13, // 11: io.prometheus.client.Exemplar.timestamp:type_name -> google.protobuf.Timestamp - 1, // 12: io.prometheus.client.Metric.label:type_name -> io.prometheus.client.LabelPair - 2, // 13: io.prometheus.client.Metric.gauge:type_name -> io.prometheus.client.Gauge - 3, // 14: io.prometheus.client.Metric.counter:type_name -> io.prometheus.client.Counter - 5, // 15: io.prometheus.client.Metric.summary:type_name -> io.prometheus.client.Summary - 6, // 16: io.prometheus.client.Metric.untyped:type_name -> io.prometheus.client.Untyped - 7, // 17: io.prometheus.client.Metric.histogram:type_name -> io.prometheus.client.Histogram - 0, // 18: io.prometheus.client.MetricFamily.type:type_name -> io.prometheus.client.MetricType - 11, // 19: io.prometheus.client.MetricFamily.metric:type_name -> io.prometheus.client.Metric - 20, // [20:20] is the sub-list for method output_type - 20, // [20:20] is the sub-list for method input_type - 20, // [20:20] is the sub-list for extension type_name - 20, // [20:20] is the sub-list for extension extendee - 0, // [0:20] is the sub-list for field type_name -} - -func init() { file_io_prometheus_client_metrics_proto_init() } -func file_io_prometheus_client_metrics_proto_init() { - if File_io_prometheus_client_metrics_proto != nil { - return - } - if !protoimpl.UnsafeEnabled { - file_io_prometheus_client_metrics_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*LabelPair); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_io_prometheus_client_metrics_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Gauge); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_io_prometheus_client_metrics_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Counter); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_io_prometheus_client_metrics_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Quantile); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_io_prometheus_client_metrics_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Summary); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_io_prometheus_client_metrics_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Untyped); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_io_prometheus_client_metrics_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Histogram); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_io_prometheus_client_metrics_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Bucket); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_io_prometheus_client_metrics_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*BucketSpan); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_io_prometheus_client_metrics_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Exemplar); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_io_prometheus_client_metrics_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Metric); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_io_prometheus_client_metrics_proto_msgTypes[11].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*MetricFamily); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - } - type x struct{} - out := protoimpl.TypeBuilder{ - File: protoimpl.DescBuilder{ - GoPackagePath: reflect.TypeOf(x{}).PkgPath(), - RawDescriptor: file_io_prometheus_client_metrics_proto_rawDesc, - NumEnums: 1, - NumMessages: 12, - NumExtensions: 0, - NumServices: 0, - }, - GoTypes: file_io_prometheus_client_metrics_proto_goTypes, - DependencyIndexes: file_io_prometheus_client_metrics_proto_depIdxs, - EnumInfos: file_io_prometheus_client_metrics_proto_enumTypes, - MessageInfos: file_io_prometheus_client_metrics_proto_msgTypes, - }.Build() - File_io_prometheus_client_metrics_proto = out.File - file_io_prometheus_client_metrics_proto_rawDesc = nil - file_io_prometheus_client_metrics_proto_goTypes = nil - file_io_prometheus_client_metrics_proto_depIdxs = nil -} diff --git a/go.mod b/go.mod index d2911277..de3e249a 100644 --- a/go.mod +++ b/go.mod @@ -3,99 +3,100 @@ module github.com/devzero-inc/zxporter go 1.26.0 require ( - connectrpc.com/connect v1.20.0 + connectrpc.com/connect v1.21.0 github.com/NVIDIA/KAI-scheduler v0.4.6 - github.com/onsi/ginkgo/v2 v2.32.0 - github.com/onsi/gomega v1.42.1 - github.com/stretchr/testify v1.11.1 - k8s.io/apimachinery v0.36.3 - k8s.io/client-go v0.36.3 - sigs.k8s.io/controller-runtime v0.23.3 + github.com/onsi/ginkgo/v2 v2.33.0 + github.com/onsi/gomega v1.44.0 + github.com/stretchr/testify v1.12.1 + k8s.io/apimachinery v0.36.5 + k8s.io/client-go v0.36.5 + sigs.k8s.io/controller-runtime v0.24.1 ) require ( github.com/Code-Hex/go-generics-cache v1.5.1 - github.com/KimMachineGun/automemlimit v0.7.5 - github.com/cilium/cilium v1.17.16 - github.com/cilium/ebpf v0.17.1 + github.com/KimMachineGun/automemlimit v1.0.0 + github.com/cilium/cilium v1.19.8 + github.com/cilium/ebpf v0.22.0 github.com/florianl/go-conntrack v0.7.0 github.com/google/gopacket v1.1.19 github.com/hashicorp/golang-lru/v2 v2.0.7 - google.golang.org/genproto v0.0.0-20260319201613-d00831a3d3e7 + google.golang.org/genproto v0.0.0-20260904194346-d0f1323225a4 inet.af/netaddr v0.0.0-20230525184311-b8eac61e914a ) require ( - github.com/Masterminds/semver/v3 v3.4.0 // indirect + github.com/Azure/azure-sdk-for-go/sdk/azcore v1.23.1 // indirect + github.com/Azure/azure-sdk-for-go/sdk/internal v1.12.0 // indirect + github.com/Masterminds/semver/v3 v3.5.0 // indirect github.com/cenkalti/backoff/v5 v5.0.3 // indirect - github.com/cilium/hive v0.0.0-20250522145610-0734675df148 // indirect - github.com/cilium/statedb v0.4.5 // indirect - github.com/cilium/stream v0.0.0-20241203114243-53c3e5d79744 // indirect - github.com/go-openapi/analysis v0.23.0 // indirect - github.com/go-openapi/errors v0.22.0 // indirect - github.com/go-openapi/loads v0.22.0 // indirect - github.com/go-openapi/runtime v0.28.0 // indirect - github.com/go-openapi/spec v0.21.0 // indirect - github.com/go-openapi/strfmt v0.23.0 // indirect - github.com/go-openapi/swag/cmdutils v0.26.0 // indirect - github.com/go-openapi/swag/conv v0.26.0 // indirect - github.com/go-openapi/swag/fileutils v0.26.0 // indirect - github.com/go-openapi/swag/jsonname v0.26.0 // indirect - github.com/go-openapi/swag/jsonutils v0.26.0 // indirect - github.com/go-openapi/swag/loading v0.26.0 // indirect - github.com/go-openapi/swag/mangling v0.26.0 // indirect - github.com/go-openapi/swag/netutils v0.26.0 // indirect - github.com/go-openapi/swag/stringutils v0.26.0 // indirect - github.com/go-openapi/swag/typeutils v0.26.0 // indirect - github.com/go-openapi/swag/yamlutils v0.26.0 // indirect - github.com/go-openapi/validate v0.24.0 // indirect + github.com/cilium/hive v0.0.0-20260108104938-97756f6ff54c // indirect + github.com/cilium/proxy v0.0.0-20250623105955-2136f59a4ea1 // indirect + github.com/cilium/statedb v0.5.10 // indirect + github.com/cilium/stream v0.0.1 // indirect + github.com/cilium/workerpool v1.3.0 // indirect + github.com/cloudflare/cfssl v1.6.5 // indirect + github.com/go-openapi/analysis v0.25.5 // indirect + github.com/go-openapi/errors v0.22.8 // indirect + github.com/go-openapi/loads v0.25.0 // indirect + github.com/go-openapi/runtime v0.33.0 // indirect + github.com/go-openapi/runtime/server-middleware v0.30.0 // indirect + github.com/go-openapi/spec v0.22.9 // indirect + github.com/go-openapi/strfmt v0.27.0 // indirect + github.com/go-openapi/swag/cmdutils v0.29.1 // indirect + github.com/go-openapi/swag/conv v0.29.1 // indirect + github.com/go-openapi/swag/fileutils v0.29.1 // indirect + github.com/go-openapi/swag/jsonutils v0.29.1 // indirect + github.com/go-openapi/swag/loading v0.29.1 // indirect + github.com/go-openapi/swag/mangling v0.29.1 // indirect + github.com/go-openapi/swag/netutils v0.29.1 // indirect + github.com/go-openapi/swag/pools v0.29.1 // indirect + github.com/go-openapi/swag/stringutils v0.29.1 // indirect + github.com/go-openapi/swag/typeutils v0.29.1 // indirect + github.com/go-openapi/swag/yamlutils v0.29.1 // indirect + github.com/go-openapi/validate v0.26.1 // indirect + github.com/go-viper/mapstructure/v2 v2.5.0 // indirect github.com/gopacket/gopacket v1.6.1 // indirect github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674 // indirect - github.com/hashicorp/hcl v1.0.1-vault-7 // indirect github.com/josharian/native v1.1.0 // indirect github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de // indirect - github.com/mackerelio/go-osstat v0.2.5 // indirect - github.com/magiconair/properties v1.8.7 // indirect + github.com/mackerelio/go-osstat v0.2.6 // indirect github.com/mdlayher/netlink v1.7.2 // indirect - github.com/mdlayher/socket v0.4.1 // indirect + github.com/mdlayher/socket v0.5.1 // indirect github.com/mitchellh/go-wordwrap v1.0.1 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/moby/spdystream v0.5.1 // indirect - github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f // indirect - github.com/oklog/ulid v1.3.1 // indirect - github.com/opentracing/opentracing-go v1.2.1-0.20220228012449-10b1cf09e00b // indirect + github.com/oklog/ulid/v2 v2.1.1 // indirect github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 // indirect - github.com/pelletier/go-toml/v2 v2.2.3 // indirect - github.com/petermattis/goid v0.0.0-20240813172612-4fcff4a6cae7 // indirect - github.com/sagikazarmark/locafero v0.4.0 // indirect - github.com/sagikazarmark/slog-shim v0.1.0 // indirect - github.com/sasha-s/go-deadlock v0.3.5 // indirect - github.com/sirupsen/logrus v1.9.4 // indirect - github.com/sourcegraph/conc v0.3.0 // indirect + github.com/pelletier/go-toml/v2 v2.2.4 // indirect + github.com/petermattis/goid v0.0.0-20250813065127-a731cc31b4fe // indirect + github.com/sagikazarmark/locafero v0.11.0 // indirect + github.com/sasha-s/go-deadlock v0.3.6 // indirect + github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 // indirect github.com/spf13/afero v1.15.0 // indirect github.com/spf13/cast v1.10.0 // indirect - github.com/spf13/viper v1.19.0 // indirect + github.com/spf13/viper v1.21.0 // indirect github.com/subosito/gotenv v1.6.0 // indirect - github.com/vishvananda/netlink v1.3.1-0.20250303224720-0e7078ed04c8 // indirect + github.com/vishvananda/netlink v1.3.2-0.20260109214200-c6faf428e8f8 // indirect github.com/vishvananda/netns v0.0.5 // indirect - go.mongodb.org/mongo-driver v1.17.9 // indirect go.uber.org/dig v1.17.1 // indirect go.yaml.in/yaml/v2 v2.4.4 // indirect - go.yaml.in/yaml/v3 v3.0.4 // indirect + go.yaml.in/yaml/v3 v3.0.5 // indirect go4.org/intern v0.0.0-20211027215823-ae77deb06f29 // indirect go4.org/netipx v0.0.0-20231129151722-fdeea329fbba // indirect go4.org/unsafe/assume-no-moving-gc v0.0.0-20230525183740-e7c30c78aeb2 // indirect - golang.org/x/mod v0.37.0 // indirect - gopkg.in/ini.v1 v1.67.0 // indirect + golang.org/x/mod v0.41.0 // indirect + k8s.io/streaming v0.36.5 // indirect + sigs.k8s.io/gateway-api v1.4.0-rc.2 // indirect + sigs.k8s.io/mcs-api v0.3.1-0.20260224125735-0f775a3eff97 // indirect sigs.k8s.io/randfill v1.0.0 // indirect - sigs.k8s.io/structured-merge-diff/v6 v6.4.0 // indirect + sigs.k8s.io/structured-merge-diff/v6 v6.4.2 // indirect ) require ( - cel.dev/expr v0.25.2 // indirect + cel.dev/expr v0.25.3 // indirect github.com/expr-lang/expr v1.17.8 // indirect - github.com/google/btree v1.1.3 // indirect - github.com/kedacore/keda/v2 v2.20.2 + github.com/kedacore/keda/v2 v2.21.0 github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect go.opentelemetry.io/auto/sdk v1.2.1 // indirect gopkg.in/evanphx/json-patch.v4 v4.13.0 // indirect @@ -103,99 +104,75 @@ require ( require ( github.com/antlr4-go/antlr/v4 v4.13.1 // indirect - github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/blang/semver/v4 v4.0.0 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/emicklei/go-restful/v3 v3.13.0 // indirect github.com/evanphx/json-patch/v5 v5.9.11 // indirect - github.com/felixge/httpsnoop v1.0.4 // indirect - github.com/fsnotify/fsnotify v1.10.0 // indirect - github.com/fxamacker/cbor/v2 v2.9.1 // indirect + github.com/felixge/httpsnoop v1.1.0 // indirect + github.com/fsnotify/fsnotify v1.10.1 // indirect + github.com/fxamacker/cbor/v2 v2.9.3 // indirect github.com/go-logr/logr v1.4.4 github.com/go-logr/stdr v1.2.2 // indirect github.com/go-logr/zapr v1.3.0 - github.com/go-openapi/jsonpointer v0.23.1 // indirect - github.com/go-openapi/jsonreference v0.21.5 // indirect - github.com/go-openapi/swag v0.26.0 // indirect + github.com/go-openapi/jsonpointer v1.0.0 // indirect + github.com/go-openapi/jsonreference v1.0.1 // indirect + github.com/go-openapi/swag v0.29.1 // indirect github.com/go-task/slim-sprig/v3 v3.0.0 // indirect - github.com/google/cel-go v0.29.0 // indirect + github.com/google/cel-go v0.31.0 // indirect github.com/google/gnostic-models v0.7.1 // indirect github.com/google/go-cmp v0.7.0 // indirect - github.com/google/pprof v0.0.0-20260402051712-545e8a4df936 // indirect + github.com/google/pprof v0.0.0-20260903180319-d6c3cb2f37ec // indirect github.com/google/uuid v1.6.0 - github.com/grpc-ecosystem/grpc-gateway/v2 v2.28.0 // indirect + github.com/grpc-ecosystem/grpc-gateway/v2 v2.30.0 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/prometheus/client_golang v1.24.1 - github.com/prometheus/client_model v0.6.2 - github.com/prometheus/common v0.70.1 - github.com/prometheus/procfs v0.21.1 // indirect + github.com/prometheus/client_model v0.6.3 + github.com/prometheus/common v0.72.0 + github.com/prometheus/procfs v0.22.0 // indirect github.com/spf13/cobra v1.10.2 // indirect github.com/spf13/pflag v1.0.10 // indirect github.com/x448/float16 v0.8.4 // indirect - go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.67.0 // indirect - go.opentelemetry.io/otel v1.44.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.43.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.43.0 // indirect - go.opentelemetry.io/otel/metric v1.44.0 // indirect - go.opentelemetry.io/otel/sdk v1.44.0 // indirect - go.opentelemetry.io/otel/trace v1.44.0 // indirect - go.opentelemetry.io/proto/otlp v1.10.0 // indirect + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.71.0 // indirect + go.opentelemetry.io/otel v1.46.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.46.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.46.0 // indirect + go.opentelemetry.io/otel/metric v1.46.0 // indirect + go.opentelemetry.io/otel/sdk v1.46.0 // indirect + go.opentelemetry.io/otel/trace v1.46.0 // indirect + go.opentelemetry.io/proto/otlp v1.11.0 // indirect go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.28.0 - golang.org/x/exp v0.0.0-20260508232706-74f9aab9d74a // indirect - golang.org/x/net v0.57.0 - golang.org/x/oauth2 v0.36.0 // indirect - golang.org/x/sync v0.22.0 - golang.org/x/sys v0.47.0 - golang.org/x/term v0.45.0 // indirect - golang.org/x/text v0.40.0 // indirect + golang.org/x/exp v0.0.0-20260824195058-e88cd73687aa // indirect + golang.org/x/net v0.59.0 + golang.org/x/oauth2 v0.37.0 // indirect + golang.org/x/sync v0.23.0 + golang.org/x/sys v0.48.0 + golang.org/x/term v0.46.0 // indirect + golang.org/x/text v0.42.0 // indirect golang.org/x/time v0.15.0 // indirect - golang.org/x/tools v0.47.0 // indirect + golang.org/x/tools v0.49.0 // indirect gomodules.xyz/jsonpatch/v2 v2.5.0 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20260526163538-3dc84a4a5aaa // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20260526163538-3dc84a4a5aaa // indirect - google.golang.org/grpc v1.83.0 - google.golang.org/protobuf v1.36.12-0.20260120151049-f2248ac996af + google.golang.org/genproto/googleapis/api v0.0.0-20260904194346-d0f1323225a4 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20260904194346-d0f1323225a4 // indirect + google.golang.org/grpc v1.84.0 + google.golang.org/protobuf v1.36.12 gopkg.in/inf.v0 v0.9.1 // indirect - gopkg.in/yaml.v3 v3.0.1 // indirect - k8s.io/api v0.36.3 - k8s.io/apiextensions-apiserver v0.36.0 - k8s.io/apiserver v0.36.0 // indirect - k8s.io/component-base v0.36.0 // indirect + gopkg.in/yaml.v3 v3.0.1 + k8s.io/api v0.36.5 + k8s.io/apiextensions-apiserver v0.36.5 + k8s.io/apiserver v0.36.5 // indirect + k8s.io/component-base v0.36.5 // indirect k8s.io/klog/v2 v2.140.0 // indirect - k8s.io/kube-openapi v0.0.0-20260427204847-8949caaa1199 // indirect - k8s.io/metrics v0.36.3 - k8s.io/utils v0.0.0-20260319190234-28399d86e0b5 - sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.34.0 // indirect + k8s.io/kube-openapi v0.0.0-20260821135717-be32def86098 // indirect + k8s.io/metrics v0.36.5 + k8s.io/utils v0.0.0-20260707023825-cf1189d6abe3 + sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.36.0 // indirect sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730 // indirect sigs.k8s.io/yaml v1.6.0 ) - -// KEDA v2.20.1's own go.mod uses these replace directives to pin -// k8s.io/client-go and controller-runtime to versions its code -// actually builds against. `replace` in a dependency's go.mod is -// ignored by consumers, so we duplicate the pins here — otherwise -// KEDA's require lines drag in k8s.io/client-go v0.36.0 whose -// ResourceEventHandlerRegistration interface controller-runtime -// v0.23.3 doesn't implement (missing HasSyncedChecker). -replace k8s.io/client-go => k8s.io/client-go v0.35.5 - -replace sigs.k8s.io/controller-runtime => sigs.k8s.io/controller-runtime v0.23.3 - -replace k8s.io/api => k8s.io/api v0.35.5 - -replace k8s.io/apimachinery => k8s.io/apimachinery v0.35.5 - -replace k8s.io/apiextensions-apiserver => k8s.io/apiextensions-apiserver v0.35.5 - -replace k8s.io/apiserver => k8s.io/apiserver v0.35.5 - -replace k8s.io/component-base => k8s.io/component-base v0.35.5 - -replace k8s.io/kms => k8s.io/kms v0.35.5 diff --git a/go.sum b/go.sum index e7ab9eca..5ebc439f 100644 --- a/go.sum +++ b/go.sum @@ -1,21 +1,25 @@ -cel.dev/expr v0.25.2 h1:K6j46C81hXtZQfuX60cVWQFBJahKSE2gfRbNuvr5bFs= -cel.dev/expr v0.25.2/go.mod h1:hrXvqGP6G6gyx8UAHSHJ5RGk//1Oj5nXQ2NI02Nrsg4= -connectrpc.com/connect v1.20.0 h1:6TNDAB+WeNd2uolWNlYczB5E0KNNaVMNUEx8JEUsPmQ= -connectrpc.com/connect v1.20.0/go.mod h1:A2ygJrukXwWy32vkCAAHNVguZrqZ+jeZ9rGRnGR4dN4= +cel.dev/expr v0.25.3 h1:A2jO8jwOugrrovveCWfj0KEZOfqiLgAcwjpHPhzIGw0= +cel.dev/expr v0.25.3/go.mod h1:hrXvqGP6G6gyx8UAHSHJ5RGk//1Oj5nXQ2NI02Nrsg4= +connectrpc.com/connect v1.21.0 h1:LhqSJt7jHf5NJBo9Jq/t/9FjcYAideif0mg+qe2jCUs= +connectrpc.com/connect v1.21.0/go.mod h1:A2ygJrukXwWy32vkCAAHNVguZrqZ+jeZ9rGRnGR4dN4= +github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6 h1:He8afgbRMd7mFxO99hRNu+6tazq8nFF9lIwo9JFroBk= +github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6/go.mod h1:8o94RPi1/7XTJvwPpRSzSUedZrtlirdB3r9Z20bi2f8= +github.com/Azure/azure-sdk-for-go/sdk/azcore v1.23.1 h1:zvXfGJCWvywnCA814d8ZiVyt+fm9nnTE8xSb99zRyfo= +github.com/Azure/azure-sdk-for-go/sdk/azcore v1.23.1/go.mod h1:iptorS+VYKFL2N6PnebpS91dubG35eAOEERnT4PJbQU= +github.com/Azure/azure-sdk-for-go/sdk/internal v1.12.0 h1:fhqpLE3UEXi9lPaBRpQ6XuRW0nU7hgg4zlmZZa+a9q4= +github.com/Azure/azure-sdk-for-go/sdk/internal v1.12.0/go.mod h1:7dCRMLwisfRH3dBupKeNCioWYUZ4SS09Z14H+7i8ZoY= github.com/Code-Hex/go-generics-cache v1.5.1 h1:6vhZGc5M7Y/YD8cIUcY8kcuQLB4cHR7U+0KMqAA0KcU= github.com/Code-Hex/go-generics-cache v1.5.1/go.mod h1:qxcC9kRVrct9rHeiYpFWSoW1vxyillCVzX13KZG8dl4= -github.com/KimMachineGun/automemlimit v0.7.5 h1:RkbaC0MwhjL1ZuBKunGDjE/ggwAX43DwZrJqVwyveTk= -github.com/KimMachineGun/automemlimit v0.7.5/go.mod h1:QZxpHaGOQoYvFhv/r4u3U0JTC2ZcOwbSr11UZF46UBM= -github.com/Masterminds/semver/v3 v3.4.0 h1:Zog+i5UMtVoCU8oKka5P7i9q9HgrJeGzI9SA1Xbatp0= -github.com/Masterminds/semver/v3 v3.4.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM= +github.com/KimMachineGun/automemlimit v1.0.0 h1:+MqlvDE/pkJNjk1rU+O14QsH8k10nJAD0frB0lsxyvw= +github.com/KimMachineGun/automemlimit v1.0.0/go.mod h1:n+BSXxQWDFS1DKh67Rqo0lgTsowsg6x65ak5uyngML0= +github.com/Masterminds/semver/v3 v3.5.0 h1:kQceYJfbupGfZOKZQg0kou0DgAKhzDg2NZPAwZ/2OOE= +github.com/Masterminds/semver/v3 v3.5.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM= github.com/NVIDIA/KAI-scheduler v0.4.6 h1:EX42/5zFLt8kyy0I5kLz0IhCwvtvgTAhAaCXWphv78U= github.com/NVIDIA/KAI-scheduler v0.4.6/go.mod h1:TVLGmb0vZ5wQNez8pRnn1tnUyhArpOkQj3lIHk0dpME= github.com/antlr4-go/antlr/v4 v4.13.1 h1:SqQKkuVZ+zWkMMNkjy5FZe5mr5WURWnlpmOuzYWrPrQ= github.com/antlr4-go/antlr/v4 v4.13.1/go.mod h1:GKmUxMtwp6ZgGwZSva4eWPC5mS6vUAmOABFgjdkM7Nw= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= -github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 h1:DklsrG3dyBCFEj5IhUbnKptjxatkF07cF2ak3yi77so= -github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= 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/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM= @@ -24,18 +28,26 @@ github.com/cenkalti/backoff/v5 v5.0.3 h1:ZN+IMa753KfX5hd8vVaMixjnqRZ3y8CuJKRKj1x github.com/cenkalti/backoff/v5 v5.0.3/go.mod h1:rkhZdG3JZukswDf7f0cwqPNk4K0sa+F97BxZthm/crw= github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/cilium/cilium v1.17.16 h1:Q7P7zQ2Y9RVmc4oTklyJya3Xgc4aUEa448rb/snKvlI= -github.com/cilium/cilium v1.17.16/go.mod h1:Eo7JFpb7zvlezx4tdT8T6G0FVzE7IClv+DFzovHbi/o= -github.com/cilium/ebpf v0.17.1 h1:G8mzU81R2JA1nE5/8SRubzqvBMmAmri2VL8BIZPWvV0= -github.com/cilium/ebpf v0.17.1/go.mod h1:vay2FaYSmIlv3r8dNACd4mW/OCaZLJKJOo+IHBvCIO8= -github.com/cilium/fake v0.6.1 h1:cLkNx1nkF0b0pPW79JaQxaI5oG2/rBzRKpp0YUg1fTA= -github.com/cilium/fake v0.6.1/go.mod h1:V9lCbbcsnSf3vB6sdOP7Q0bsUUJ/jyHPZxnFAw5nPUc= -github.com/cilium/hive v0.0.0-20250522145610-0734675df148 h1:0zljF9fNEJ36/OyifWahfcizUcs+X9OMSvEv58+wo5g= -github.com/cilium/hive v0.0.0-20250522145610-0734675df148/go.mod h1:pI2GJ1n3SLKIQVFrKF7W6A6gb6BQkZ+3Hp4PAEo5SuI= -github.com/cilium/statedb v0.4.5 h1:WP1xbkAdDup0wOkhRZUJG/Wh4+ZLAOgl7yIIxJ3vIEo= -github.com/cilium/statedb v0.4.5/go.mod h1:DlxX9OQi/nM8oumUuz8VjxXUtVRiEfbfo8Ri1YWNCGI= -github.com/cilium/stream v0.0.0-20241203114243-53c3e5d79744 h1:f+CgYUy2YyZ2EX31QSqf3vwFiJJQSAMIQLn4d3QQYno= -github.com/cilium/stream v0.0.0-20241203114243-53c3e5d79744/go.mod h1:/e83AwqvNKpyg4n3C41qmnmj1x2G9DwzI+jb7GkF4lI= +github.com/cilium/cilium v1.19.8 h1:DmIU6MdIL/4LKWhzMjmFntax8JBVYs3VIWVKiTxvvBE= +github.com/cilium/cilium v1.19.8/go.mod h1:OdM6zKmqKYDd/GfUXIj4w1IosBFZHWyv3Aeq78FOeig= +github.com/cilium/ebpf v0.22.0 h1:v2ktp0roffpMOj2MMf3idtCQZOsAoC4BJbAJN+ke2bY= +github.com/cilium/ebpf v0.22.0/go.mod h1:CDzZbe2hC5JjlDC+CY3KFCzlYwN4gbxppYM+Z10bQt4= +github.com/cilium/fake v0.7.0 h1:4EKBtTweQrJoD4q45qDGu8udulmYMo48Y0BhEbrB1jc= +github.com/cilium/fake v0.7.0/go.mod h1:hA1YsEjgIs5Gdeq/DVrDWGuhLCoVok7THTvQaGDO5bc= +github.com/cilium/hive v0.0.0-20260108104938-97756f6ff54c h1:mP/Z+oVplgbg3oV1lwsAC86NPLWioN/TqlmZ6+BI2I0= +github.com/cilium/hive v0.0.0-20260108104938-97756f6ff54c/go.mod h1:4/8FBMcTjVdkrNNWaB7t3QqaU4kZDJLJ1leKVP9GjEI= +github.com/cilium/proxy v0.0.0-20250623105955-2136f59a4ea1 h1:SOOtIfQmW/pF1iW1I4hVUx1pvgX7Xh2E8jHv+itBXQ0= +github.com/cilium/proxy v0.0.0-20250623105955-2136f59a4ea1/go.mod h1:Kwyyx+cC2H67Aj1sDuqBLvPn6TEmEJRPvULIrJ/kBRo= +github.com/cilium/statedb v0.5.10 h1:0NC5eGJySTkU+niaYUfCTh9ocCxM5txF99L1Y6DBZ8s= +github.com/cilium/statedb v0.5.10/go.mod h1:utZbqAU8l3X/2zmbBwoYC2KuRTstuSqo+c4cw4jXsCM= +github.com/cilium/stream v0.0.1 h1:82zuM/WwkLiac2Jg5FrzPxZHvIBbxXTi4VY7M+EYLs0= +github.com/cilium/stream v0.0.1/go.mod h1:/e83AwqvNKpyg4n3C41qmnmj1x2G9DwzI+jb7GkF4lI= +github.com/cilium/workerpool v1.3.0 h1:7BhHxoqNtpqtmce6MxZdgWODze4lYHbWkEUQ+3xEu8M= +github.com/cilium/workerpool v1.3.0/go.mod h1:0evs6P39nORTphjRtTtHLXTyCPQUwelXCK4wBJmVP7g= +github.com/cloudflare/cfssl v1.6.5 h1:46zpNkm6dlNkMZH/wMW22ejih6gIaJbzL2du6vD7ZeI= +github.com/cloudflare/cfssl v1.6.5/go.mod h1:Bk1si7sq8h2+yVEDrFJiz3d7Aw+pfjjJSZVaD+Taky4= +github.com/cncf/xds/go v0.0.0-20260202195803-dba9d589def2 h1:aBangftG7EVZoUb69Os8IaYg++6uMOdKK83QtkkvJik= +github.com/cncf/xds/go v0.0.0-20260202195803-dba9d589def2/go.mod h1:qwXFYgsP6T7XnJtbKlf1HP8AjxZZyzxMmc+Lq5GjlU4= github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -44,22 +56,27 @@ github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8Yc github.com/dvyukov/go-fuzz v0.0.0-20210103155950-6a8e9d1f2415/go.mod h1:11Gm+ccJnvAhCNLlf5+cS9KjtbaD5I5zaZpFMsTHWTw= github.com/emicklei/go-restful/v3 v3.13.0 h1:C4Bl2xDndpU6nJ4bc1jXd+uTmYPVUwkD6bFY/oTyCes= github.com/emicklei/go-restful/v3 v3.13.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= -github.com/evanphx/json-patch v5.9.0+incompatible h1:fBXyNpNMuTTDdquAq/uisOr2lShz4oaXpDTX2bLe7ls= -github.com/evanphx/json-patch v5.9.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= +github.com/envoyproxy/go-control-plane v0.14.0 h1:hbG2kr4RuFj222B6+7T83thSPqLjwBIfQawTkC++2HA= +github.com/envoyproxy/go-control-plane/envoy v1.39.0 h1:1uwRDYPYG8BIBU9Mj1sUAebNmlM6beu/ZKKweSLDxk8= +github.com/envoyproxy/go-control-plane/envoy v1.39.0/go.mod h1:5e4ylfTZO723MEEFsCpSW4ZEBWR8mwkEyXfwJBTCZ9c= +github.com/envoyproxy/protoc-gen-validate v1.3.3 h1:MVQghNeW+LZcmXe7SY1V36Z+WFMDjpqGAGacLe2T0ds= +github.com/envoyproxy/protoc-gen-validate v1.3.3/go.mod h1:TsndJ/ngyIdQRhMcVVGDDHINPLWB7C82oDArY51KfB0= +github.com/evanphx/json-patch v5.9.11+incompatible h1:ixHHqfcGvxhWkniF1tWxBHA0yb4Z+d1UQi45df52xW8= +github.com/evanphx/json-patch v5.9.11+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/expr-lang/expr v1.17.8 h1:W1loDTT+0PQf5YteHSTpju2qfUfNoBt4yw9+wOEU9VM= github.com/expr-lang/expr v1.17.8/go.mod h1:8/vRC7+7HBzESEqt5kKpYXxrxkr31SaO8r40VO/1IT4= -github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= -github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= +github.com/felixge/httpsnoop v1.1.0 h1:3YtUj32ZZkqZtt3sZZsClsymw/QDuVfpNhoA31zeORc= +github.com/felixge/httpsnoop v1.1.0/go.mod h1:Zqxgdd+1Rkcz8euOqdr7lqgCRJztwr5hp9vDSi5UZCE= github.com/florianl/go-conntrack v0.7.0 h1:kWbRhLRUqBlmi2ncASmygN5yI281wlpnTUJs69tcs7g= github.com/florianl/go-conntrack v0.7.0/go.mod h1:7kzfTjtQCRsAVkuornpI8KiwFfa79QmcZXZQex1IXhU= 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.10.0 h1:Xx/5Ydg9CeBDX/wi4VJqStNtohYjitZhhlHt4h3St1M= -github.com/fsnotify/fsnotify v1.10.0/go.mod h1:TLheqan6HD6GBK6PrDWyDPBaEV8LspOxvPSjC+bVfgo= -github.com/fxamacker/cbor/v2 v2.9.1 h1:2rWm8B193Ll4VdjsJY28jxs70IdDsHRWgQYAI80+rMQ= -github.com/fxamacker/cbor/v2 v2.9.1/go.mod h1:vM4b+DJCtHn+zz7h3FFp/hDAI9WNWCsZj23V5ytsSxQ= +github.com/fsnotify/fsnotify v1.10.1 h1:b0/UzAf9yR5rhf3RPm9gf3ehBPpf0oZKIjtpKrx59Ho= +github.com/fsnotify/fsnotify v1.10.1/go.mod h1:TLheqan6HD6GBK6PrDWyDPBaEV8LspOxvPSjC+bVfgo= +github.com/fxamacker/cbor/v2 v2.9.3 h1:oQBnFATpNdY8gJHTndDDv5Xl4QqNaz51G5LLEPhng3Q= +github.com/fxamacker/cbor/v2 v2.9.3/go.mod h1:vM4b+DJCtHn+zz7h3FFp/hDAI9WNWCsZj23V5ytsSxQ= 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= @@ -73,66 +90,68 @@ github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/go-logr/zapr v1.3.0 h1:XGdV8XW8zdwFiwOA2Dryh1gj2KRQyOOoNmBy4EplIcQ= github.com/go-logr/zapr v1.3.0/go.mod h1:YKepepNBd1u/oyhd/yQmtjVXmm9uML4IXUgMOwR8/Gg= -github.com/go-openapi/analysis v0.23.0 h1:aGday7OWupfMs+LbmLZG4k0MYXIANxcuBTYUC03zFCU= -github.com/go-openapi/analysis v0.23.0/go.mod h1:9mz9ZWaSlV8TvjQHLl2mUW2PbZtemkE8yA5v22ohupo= -github.com/go-openapi/errors v0.22.0 h1:c4xY/OLxUBSTiepAg3j/MHuAv5mJhnf53LLMWFB+u/w= -github.com/go-openapi/errors v0.22.0/go.mod h1:J3DmZScxCDufmIMsdOuDHxJbdOGC0xtUynjIx092vXE= -github.com/go-openapi/jsonpointer v0.23.1 h1:1HBACs7XIwR2RcmItfdSFlALhGbe6S92p0ry4d1GWg4= -github.com/go-openapi/jsonpointer v0.23.1/go.mod h1:iWRmZTrGn7XwYhtPt/fvdSFj1OfNBngqRT2UG3BxSqY= -github.com/go-openapi/jsonreference v0.21.5 h1:6uCGVXU/aNF13AQNggxfysJ+5ZcU4nEAe+pJyVWRdiE= -github.com/go-openapi/jsonreference v0.21.5/go.mod h1:u25Bw85sX4E2jzFodh1FOKMTZLcfifd1Q+iKKOUxExw= -github.com/go-openapi/loads v0.22.0 h1:ECPGd4jX1U6NApCGG1We+uEozOAvXvJSF4nnwHZ8Aco= -github.com/go-openapi/loads v0.22.0/go.mod h1:yLsaTCS92mnSAZX5WWoxszLj0u+Ojl+Zs5Stn1oF+rs= -github.com/go-openapi/runtime v0.28.0 h1:gpPPmWSNGo214l6n8hzdXYhPuJcGtziTOgUpvsFWGIQ= -github.com/go-openapi/runtime v0.28.0/go.mod h1:QN7OzcS+XuYmkQLw05akXk0jRH/eZ3kb18+1KwW9gyc= -github.com/go-openapi/spec v0.21.0 h1:LTVzPc3p/RzRnkQqLRndbAzjY0d0BCL72A6j3CdL9ZY= -github.com/go-openapi/spec v0.21.0/go.mod h1:78u6VdPw81XU44qEWGhtr982gJ5BWg2c0I5XwVMotYk= -github.com/go-openapi/strfmt v0.23.0 h1:nlUS6BCqcnAk0pyhi9Y+kdDVZdZMHfEKQiS4HaMgO/c= -github.com/go-openapi/strfmt v0.23.0/go.mod h1:NrtIpfKtWIygRkKVsxh7XQMDQW5HKQl6S5ik2elW+K4= -github.com/go-openapi/swag v0.26.0 h1:GVDXCmfvhfu1BxiHo8/FA+BbKmhecHnG3varjON5/RI= -github.com/go-openapi/swag v0.26.0/go.mod h1:82g3193sZJRbocs7bNCqGfIgq8pkuwVwCfhKIRlEQF0= -github.com/go-openapi/swag/cmdutils v0.26.0 h1:iowihOcvq7y4egO8cOq0dmfohz6wfeQ63U1EnuhO2TU= -github.com/go-openapi/swag/cmdutils v0.26.0/go.mod h1:Sm1MVFMkF6guJJ+pQqHnQA3N0j9qALV3NxzDSv6bETM= -github.com/go-openapi/swag/conv v0.26.0 h1:5yGGsPYI1ZCva93U0AoKi/iZrNhaJEjr324YVsiD89I= -github.com/go-openapi/swag/conv v0.26.0/go.mod h1:tpAmIL7X58VPnHHiSO4uE3jBeRamGsFsfdDeDtb5ECE= -github.com/go-openapi/swag/fileutils v0.26.0 h1:WJoPRvsA7QRiiWluowkLJa9jaYR7FCuxmDvnCgaRRxU= -github.com/go-openapi/swag/fileutils v0.26.0/go.mod h1:0WDJ7lp67eNjPMO50wAWYlKvhOb6CQ37rzR7wrgI8Tc= -github.com/go-openapi/swag/jsonname v0.26.0 h1:gV1NFX9M8avo0YSpmWogqfQISigCmpaiNci8cGECU5w= -github.com/go-openapi/swag/jsonname v0.26.0/go.mod h1:urBBR8bZNoDYGr653ynhIx+gTeIz0ARZxHkAPktJK2M= -github.com/go-openapi/swag/jsonutils v0.26.0 h1:FawFML2iAXsPqmERscuMPIHmFsoP1tOqWkxBaKNMsnA= -github.com/go-openapi/swag/jsonutils v0.26.0/go.mod h1:2VmA0CJlyFqgawOaPI9psnjFDqzyivIqLYN34t9p91E= -github.com/go-openapi/swag/jsonutils/fixtures_test v0.26.0 h1:apqeINu/ICHouqiRZbyFvuDge5jCmmLTqGQ9V95EaOM= -github.com/go-openapi/swag/jsonutils/fixtures_test v0.26.0/go.mod h1:AyM6QT8uz5IdKxk5akv0y6u4QvcL9GWERt0Jx/F/R8Y= -github.com/go-openapi/swag/loading v0.26.0 h1:Apg6zaKhCJurpJer0DCxq99qwmhFddBhaMX7kilDcko= -github.com/go-openapi/swag/loading v0.26.0/go.mod h1:dBxQ/6V2uBaAQdevN18VELE6xSpJWZxLX4txe12JwDg= -github.com/go-openapi/swag/mangling v0.26.0 h1:Du2YC4YLA/Y5m/YKQd7AnY5qq0wRKSFZTTt8ktFaXcQ= -github.com/go-openapi/swag/mangling v0.26.0/go.mod h1:jifS7W9vbg+pw63bT+GI53otluMQL3CeemuyCHKwVx0= -github.com/go-openapi/swag/netutils v0.26.0 h1:CmZp+ZT7HrmFwrC3GdGsXBq2+42T1bjKBapcqVpIs3c= -github.com/go-openapi/swag/netutils v0.26.0/go.mod h1:5iK+Ok3ZohWWex1C50BFTPexi03UaPwjW4Oj8kgrpwo= -github.com/go-openapi/swag/stringutils v0.26.0 h1:qZQngLxs5s7SLijc3N2ZO+fUq2o8LjuWAASSrJuh+xg= -github.com/go-openapi/swag/stringutils v0.26.0/go.mod h1:sWn5uY+QIIspwPhvgnqJsH8xqFT2ZbYcvbcFanRyhFE= -github.com/go-openapi/swag/typeutils v0.26.0 h1:2kdEwdiNWy+JJdOvu5MA2IIg2SylWAFuuyQIKYybfq4= -github.com/go-openapi/swag/typeutils v0.26.0/go.mod h1:oovDuIUvTrEHVMqWilQzKzV4YlSKgyZmFh7AlfABNVE= -github.com/go-openapi/swag/yamlutils v0.26.0 h1:H7O8l/8NJJQ/oiReEN+oMpnGMyt8G0hl460nRZxhLMQ= -github.com/go-openapi/swag/yamlutils v0.26.0/go.mod h1:1evKEGAtP37Pkwcc7EWMF0hedX0/x3Rkvei2wtG/TbU= -github.com/go-openapi/testify/enable/yaml/v2 v2.4.2 h1:5zRca5jw7lzVREKCZVNBpysDNBjj74rBh0N2BGQbSR0= -github.com/go-openapi/testify/enable/yaml/v2 v2.4.2/go.mod h1:XVevPw5hUXuV+5AkI1u1PeAm27EQVrhXTTCPAF85LmE= -github.com/go-openapi/testify/v2 v2.4.2 h1:tiByHpvE9uHrrKjOszax7ZvKB7QOgizBWGBLuq0ePx4= -github.com/go-openapi/testify/v2 v2.4.2/go.mod h1:SgsVHtfooshd0tublTtJ50FPKhujf47YRqauXXOUxfw= -github.com/go-openapi/validate v0.24.0 h1:LdfDKwNbpB6Vn40xhTdNZAnfLECL81w+VX3BumrGD58= -github.com/go-openapi/validate v0.24.0/go.mod h1:iyeX1sEufmv3nPbBdX3ieNviWnOZaJ1+zquzJEf2BAQ= -github.com/go-quicktest/qt v1.101.0 h1:O1K29Txy5P2OK0dGo59b7b0LR6wKfIhttaAhHUyn7eI= -github.com/go-quicktest/qt v1.101.0/go.mod h1:14Bz/f7NwaXPtdYEgzsx46kqSxVwTbzVZsDC26tQJow= +github.com/go-openapi/analysis v0.25.5 h1:xPYEvTb90o1y0epuiOPAoG4QqahjP3cdp5xNlHeKJRI= +github.com/go-openapi/analysis v0.25.5/go.mod h1:d3UGtQC5uq5Kqqqis2VH09Km/v3vwsWrYkbp4gdm+Rc= +github.com/go-openapi/errors v0.22.8 h1:oP7sW7TWc3wFFjrzzj0nI83H2qMBkNjNfSd+XRejk/I= +github.com/go-openapi/errors v0.22.8/go.mod h1:BuUoHcYrU6E7V9gfj1I5wLQqgtIHnup/alXZ8KdgQ0w= +github.com/go-openapi/jsonpointer v1.0.0 h1:kR9tHqY0CtZaOPVFm622dPVNhrvYpwr4uCxgL3h1H8s= +github.com/go-openapi/jsonpointer v1.0.0/go.mod h1:Z3rw7dWu1p9IgitXCFamSlA5lmDiklEB6vkaxcNZW5Y= +github.com/go-openapi/jsonreference v1.0.1 h1:4zJ7AmYDKNmD3aSpfPnFNCFA5E80/xMHUNKgydaLh38= +github.com/go-openapi/jsonreference v1.0.1/go.mod h1:dYplQXa6p5lXprLcJ8LE2iU7vNpXsAHDQ5ZAgL+Qx3A= +github.com/go-openapi/loads v0.25.0 h1:74Bc2snfaVlsHzwdQj/3gsA9XJz3daXTJVs+4ZaK7jI= +github.com/go-openapi/loads v0.25.0/go.mod h1:JFBw4SIB9+PTIFHDfcXuSSy5h6aWzjtUCrPYyx3qWU8= +github.com/go-openapi/runtime v0.33.0 h1:Dd3Oj2ig+WH8ckK95l0Wn2V8a4bH/UqWPRZVT0vc8yU= +github.com/go-openapi/runtime v0.33.0/go.mod h1:+rsupH3+TFKqmFysqkmgBOTxpVJV8eV+j9myvvea2Xw= +github.com/go-openapi/runtime/server-middleware v0.30.0 h1:8rPoJ/xv7JL8BsovaqboKETlpWBArVh8n+0L/GyePog= +github.com/go-openapi/runtime/server-middleware v0.30.0/go.mod h1:OYNT/TxNvB/VK5oe4htM2jDTwlEXuejVJmu0DVZfAMs= +github.com/go-openapi/spec v0.22.9 h1:/vKIFDcGKp0ktZWGbym/tJEWbk6/XOEmAVU0kqKMH+w= +github.com/go-openapi/spec v0.22.9/go.mod h1:b/mNUYIOQOyIiUzUzXEE8xzyZqf93KvM9hQGP91yfl0= +github.com/go-openapi/strfmt v0.27.0 h1:kbcTeaD9TXuXD0hhMXzuYa1sdTo6+dWGvwjW93E80IM= +github.com/go-openapi/strfmt v0.27.0/go.mod h1:s/qhDqfY72irigXUGJmtgid2Rm+3tnz3k8hZaRmvWYc= +github.com/go-openapi/swag v0.29.1 h1:C6EeWzUwQtcWEhE9eqBdUubGXxhWY4PlzHMLD7kLaiQ= +github.com/go-openapi/swag v0.29.1/go.mod h1:BzxEXKiPlSXRsRTv1KSBF/BpGKHxA/YciCnr4tv9bvA= +github.com/go-openapi/swag/cmdutils v0.29.1 h1:3DorPGfUdE80BogKY22EzoHBcHMrkVomZMoV7kS4ANY= +github.com/go-openapi/swag/cmdutils v0.29.1/go.mod h1:Sm1MVFMkF6guJJ+pQqHnQA3N0j9qALV3NxzDSv6bETM= +github.com/go-openapi/swag/conv v0.29.1 h1:AC4Eh/5c/eUDOUCzzsRC9ghmFgOSBHeRMGIngY0ZUGA= +github.com/go-openapi/swag/conv v0.29.1/go.mod h1:S1X7/ZrBEZOC0Wc8AGxjbcGS92l3WEjA7aPtpl+RaqM= +github.com/go-openapi/swag/fileutils v0.29.1 h1:ZcPzMceVhU1WPbK6N1G6sNQKdd1CWJlf3cA08UHuoM0= +github.com/go-openapi/swag/fileutils v0.29.1/go.mod h1:/wofKYckbtRl2p3+EwQsosie5CT1B38+dQ+PS579BzI= +github.com/go-openapi/swag/jsonutils v0.29.1 h1:AFCxs0eQZ24/QyfhVHM2t49rMz7Vv3XCsZQI6yrNy+c= +github.com/go-openapi/swag/jsonutils v0.29.1/go.mod h1:u3+sCfJpttDpcmS5kpm0yxL6GK0eWgODsx8Yw8fcqNM= +github.com/go-openapi/swag/jsonutils/fixtures_test v0.29.1 h1:BiiXE31Bx9SfpsMmOQj5KYpUhTZBpLVriVhJDuLuY2o= +github.com/go-openapi/swag/jsonutils/fixtures_test v0.29.1/go.mod h1:julgTUKZ9/D0j6O7GKajmRs+812FWxQg/mMpGunWSjg= +github.com/go-openapi/swag/loading v0.29.1 h1:FCv5fG8UhTdDJa2R7w+5O9Ekpcbw7tt0nFWvmDKGBjc= +github.com/go-openapi/swag/loading v0.29.1/go.mod h1:N0ESuem4p2oedKal8EJhciqnJ9Q9Wmt83L1CRB3Fouw= +github.com/go-openapi/swag/mangling v0.29.1 h1:lHALtvYCdxVnRl4GrHmFPwfBTZYIObqdGNSKyu/8D6I= +github.com/go-openapi/swag/mangling v0.29.1/go.mod h1:SAop9pB7PUjQ/CGCNf/JmCKTRK+GDO+RqE9UHqC/N6s= +github.com/go-openapi/swag/netutils v0.29.1 h1:IjIvdEP5duKcghFqJEPSUraRnkKYHoM65kTluTu+Jb4= +github.com/go-openapi/swag/netutils v0.29.1/go.mod h1:DUde7x4Bx00k5jYl2AdRpNAO0m7atUvD2x6X+bWkbno= +github.com/go-openapi/swag/pools v0.29.1 h1:NRogYxdEW9SjRM4mkAOji9iefO4MRXq3p/ZJcoQbUKg= +github.com/go-openapi/swag/pools v0.29.1/go.mod h1:leDcaghjkRAhCuCRv9NfJU5f0mjoU3cT/XZObhMk3pc= +github.com/go-openapi/swag/stringutils v0.29.1 h1:1ykunK7iJQk1uOO7+oUH1ukbsK85fFCOiCFMOVSY+F0= +github.com/go-openapi/swag/stringutils v0.29.1/go.mod h1:7fSqZ+z8Qc0tOfAAK0jVa5qFGrnIlRi6n7NeGGrr1vc= +github.com/go-openapi/swag/typeutils v0.29.1 h1:Nzv9nhnlLCRBPQqfOX+7lB6Guju370or8StT+lIOf6M= +github.com/go-openapi/swag/typeutils v0.29.1/go.mod h1:hxpgDZJVBkBsi/d3MIUosafoFdE5exaQRmVp0zwu3YE= +github.com/go-openapi/swag/yamlutils v0.29.1 h1:69w3tsBajm7MR/fejLy7HD/3J68Ys1SeeZMEzZ3w2sk= +github.com/go-openapi/swag/yamlutils v0.29.1/go.mod h1:rgsp3vT/QdWzKwn43CigDwjOGIenPyTZMKnxEM8jZOA= +github.com/go-openapi/testify/enable/yaml/v2 v2.6.1 h1:Jm+/ze2rMtbD98yen92AhATGLGREDYXG56Xr4gMjEtE= +github.com/go-openapi/testify/enable/yaml/v2 v2.6.1/go.mod h1:YDPnwCRDu38/oJBVMBVXOUDiJ9cIeBHWvfImHaXqnv4= +github.com/go-openapi/testify/v2 v2.6.1 h1:6CNJhTjMzgaeaH8WhshcsZNPIvRemiOcFpU7seO/y7Q= +github.com/go-openapi/testify/v2 v2.6.1/go.mod h1:SgsVHtfooshd0tublTtJ50FPKhujf47YRqauXXOUxfw= +github.com/go-openapi/validate v0.26.1 h1:pZSbvtRO8G2R2FpWTYRn3w8LrsNwbtaVhP2dWiBa0Us= +github.com/go-openapi/validate v0.26.1/go.mod h1:B8UMgXiQiwwQWIbmuROlwJZDPGlikPuh7iHV1vPX9Oo= +github.com/go-quicktest/qt v1.101.1-0.20240301121107-c6c8733fa1e6 h1:teYtXy9B7y5lHTp8V9KPxpYRAVA7dozigQcMiBust1s= +github.com/go-quicktest/qt v1.101.1-0.20240301121107-c6c8733fa1e6/go.mod h1:p4lGIVX+8Wa6ZPNDvqcxq36XpUDLh42FLetFU7odllI= 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.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/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= -github.com/google/btree v1.1.3 h1:CVpQJjYgC4VbzxeGVHfvZrv1ctoYCAI8vbl07Fcxlyg= -github.com/google/btree v1.1.3/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4= -github.com/google/cel-go v0.29.0 h1:fEG+Ja3YRwNOqnQxTyJwoByAUAvTuxUGiro/jhrm4F4= -github.com/google/cel-go v0.29.0/go.mod h1:X0bD6iVNR8pkROSOoHVdgTkzmRcosof7WQqCD6wcMc8= +github.com/google/cel-go v0.31.0 h1:H0bhpFTqOvmHrBGrWKp7ZlhBm5Hh8PYUEXnwxT1LL7A= +github.com/google/cel-go v0.31.0/go.mod h1:X0bD6iVNR8pkROSOoHVdgTkzmRcosof7WQqCD6wcMc8= github.com/google/gnostic-models v0.7.1 h1:SisTfuFKJSKM5CPZkffwi6coztzzeYUhc3v4yxLWH8c= github.com/google/gnostic-models v0.7.1/go.mod h1:whL5G0m6dmc5cPxKc5bdKdEN3UjI7OUGxBlw57miDrQ= github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= @@ -144,20 +163,18 @@ github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gopacket v1.1.19 h1:ves8RnFZPGiFnTS0uPQStjwru6uO6h+nlr9j6fL7kF8= github.com/google/gopacket v1.1.19/go.mod h1:iJ8V8n6KS+z2U1A8pUwu8bW5SyEMkXJB8Yo/Vo+TKTo= -github.com/google/pprof v0.0.0-20260402051712-545e8a4df936 h1:EwtI+Al+DeppwYX2oXJCETMO23COyaKGP6fHVpkpWpg= -github.com/google/pprof v0.0.0-20260402051712-545e8a4df936/go.mod h1:MxpfABSjhmINe3F1It9d+8exIHFvUqtLIRCdOGNXqiI= +github.com/google/pprof v0.0.0-20260903180319-d6c3cb2f37ec h1:ZhR4jlKRUmzPEPd2RLiJRUi25rUrdKBrhQNVIbqpTNE= +github.com/google/pprof v0.0.0-20260903180319-d6c3cb2f37ec/go.mod h1:jl5iWTm0/hd5PjEYEOuwAJ57L/CibdZfrqZ5XA5GrCk= 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/gopacket/gopacket v1.6.1 h1:S19Ok/KVGDFNHVW2uCva5U0vZ+uHqiZQdxteL50v6Ak= github.com/gopacket/gopacket v1.6.1/go.mod h1:i3NaGaqfoWKAr1+g7qxEdWsmfT+MXuWkAe9+THv8LME= github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674 h1:JeSE6pjso5THxAzdVpqr6/geYxZytqFMBCOtn/ujyeo= github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674/go.mod h1:r4w70xmWCQKmi1ONH4KIaBptdivuRPyosB9RmPlGEwA= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.28.0 h1:HWRh5R2+9EifMyIHV7ZV+MIZqgz+PMpZ14Jynv3O2Zs= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.28.0/go.mod h1:JfhWUomR1baixubs02l85lZYYOm7LV6om4ceouMv45c= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.30.0 h1:/Tnpcb2E0Pz/tN9s3bfEY2Q8ePCEX9iuS+cneUwncnw= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.30.0/go.mod h1:zOBXOsUaBSjKgmH4OGzV1esUpR3oUSCPYVd2cUBjKYY= github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k= github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM= -github.com/hashicorp/hcl v1.0.1-vault-7 h1:ag5OxFVy3QYTFTJODRzTKVZ6xvdfLLCA1cy/Y6xGI0I= -github.com/hashicorp/hcl v1.0.1-vault-7/go.mod h1:XYhtn6ijBSAj6n4YqAaf7RBPS4I06AItNorpy+MoQNM= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/josharian/native v1.0.0/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w= @@ -169,10 +186,10 @@ github.com/jsimonetti/rtnetlink/v2 v2.0.1 h1:xda7qaHDSVOsADNouv7ukSuicKZO7GgVUCX github.com/jsimonetti/rtnetlink/v2 v2.0.1/go.mod h1:7MoNYNbb3UaDHtF8udiJo/RH6VsTKP1pqKLUTVCvToE= 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/kedacore/keda/v2 v2.20.2 h1:SR60v8uIcelbveS8n/1jcl8tLdBBIbHMxmdOHw8tKqA= -github.com/kedacore/keda/v2 v2.20.2/go.mod h1:wrV1MP6buZQ1S5/J/R7UyBeJr857x92E43LADVoSajs= -github.com/klauspost/compress v1.19.1 h1:VsB4HPswih7mmZ8WleSFQ75c/Ui1M4trX5oAsJnhSlk= -github.com/klauspost/compress v1.19.1/go.mod h1:cwPg85FWrGar70rWktvGQj8/hthj3wpl0PGDogxkrSQ= +github.com/kedacore/keda/v2 v2.21.0 h1:R6z2HZvstK4RLUMxroNACzBj74xGxt3kG59b5Xc+Xps= +github.com/kedacore/keda/v2 v2.21.0/go.mod h1:djq9CnH0OT9Wis6jwtc02oiUstN081M81Amk8JtkbVo= +github.com/klauspost/compress v1.20.0 h1:a3C1ke2ohxFymNlb2HWAHjDeKCI90scRskErZkR0ezA= +github.com/klauspost/compress v1.20.0/go.mod h1:LUdAzn7YLVvxLpc7y3V1m40wESHTgc1422pwwBSKYuI= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= @@ -181,18 +198,16 @@ github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0 github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de h1:9TO3cAIGXtEhnIaL+V+BEER86oLrvS+kWobKpbJuye0= github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de/go.mod h1:zAbeS9B/r2mtpb6U+EI2rYA5OAXxsYw6wTamcNW+zcE= -github.com/mackerelio/go-osstat v0.2.5 h1:+MqTbZUhoIt4m8qzkVoXUJg1EuifwlAJSk4Yl2GXh+o= -github.com/mackerelio/go-osstat v0.2.5/go.mod h1:atxwWF+POUZcdtR1wnsUcQxTytoHG4uhl2AKKzrOajY= -github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= -github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= +github.com/mackerelio/go-osstat v0.2.6 h1:gs4U8BZeS1tjrL08tt5VUliVvSWP26Ai2Ob8Lr7f2i0= +github.com/mackerelio/go-osstat v0.2.6/go.mod h1:lRy8V9ZuHpuRVZh+vyTkODeDPl3/d5MgXHtLSaqG8bA= 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/mdlayher/netlink v1.6.2/go.mod h1:O1HXX2sIWSMJ3Qn1BYZk1yZM+7iMki/uYGGiwGyq/iU= github.com/mdlayher/netlink v1.7.2 h1:/UtM3ofJap7Vl4QWCPDGXY8d3GIY2UGSDbK+QWmY8/g= github.com/mdlayher/netlink v1.7.2/go.mod h1:xraEF7uJbxLhc5fpHL4cPe221LI2bdttWlU+ZGLfQSw= github.com/mdlayher/socket v0.2.3/go.mod h1:bz12/FozYNH/VbvC3q7TRIK/Y6dH1kCKsXaUeXi/FmY= -github.com/mdlayher/socket v0.4.1 h1:eM9y2/jlbs1M615oshPQOHZzj6R6wMT7bX5NPiQvn2U= -github.com/mdlayher/socket v0.4.1/go.mod h1:cAqeGjoufqdxWkD7DkpyS+wcefOtmu5OQ8KuoJGIReA= +github.com/mdlayher/socket v0.5.1 h1:VZaqt6RkGkt2OE9l3GcC6nZkqD3xKeQLyfleW/uBcos= +github.com/mdlayher/socket v0.5.1/go.mod h1:TjPLHI1UgwEv5J1B5q0zTZq12A/6H7nKmtTanQE37IQ= github.com/mfridman/tparse v0.18.0 h1:wh6dzOKaIwkUGyKgOntDW4liXSo37qg5AXbIhkMV3vE= github.com/mfridman/tparse v0.18.0/go.mod h1:gEvqZTuCgEhPbYk/2lS3Kcxg1GmTxxU7kTC8DvP0i/A= github.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0= @@ -209,48 +224,41 @@ github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee h1:W5t00kpgFd github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= -github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f h1:y5//uYreIhSUg3J1GEMiLbxo1LJaP8RfCpH6pymGZus= -github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw= -github.com/oklog/ulid v1.3.1 h1:EGfNDEx6MqHz8B3uNV6QAib1UR2Lm97sHi3ocA6ESJ4= -github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= -github.com/onsi/ginkgo/v2 v2.32.0 h1:Hw7s2pVrQo/8Yz5N77qdnpHaoc+c6cC9WIV1Jce+J6E= -github.com/onsi/ginkgo/v2 v2.32.0/go.mod h1:+aXOY+vzZ5mu2iI2HpTZUPmM//oQfsNFX6gU9kNcA44= -github.com/onsi/gomega v1.42.1 h1:iN1rCUX+44NZ1Dc97MPoeFYbFR0vh8zxoxMFwKdyZ6I= -github.com/onsi/gomega v1.42.1/go.mod h1:REff/hsDsodHoKlWsP2mAPhu1+5/6hVYNf9rIEBpeSg= -github.com/opentracing/opentracing-go v1.2.1-0.20220228012449-10b1cf09e00b h1:FfH+VrHHk6Lxt9HdVS0PXzSXFyS2NbZKXv33FYPol0A= -github.com/opentracing/opentracing-go v1.2.1-0.20220228012449-10b1cf09e00b/go.mod h1:AC62GU6hc0BrNm+9RK9VSiwa/EUe1bkIeFORAMcHvJU= +github.com/oklog/ulid/v2 v2.1.1 h1:suPZ4ARWLOJLegGFiZZ1dFAkqzhMjL3J1TzI+5wHz8s= +github.com/oklog/ulid/v2 v2.1.1/go.mod h1:rcEKHmBBKfef9DhnvX7y1HZBYxjXb0cP5ExxNsTT1QQ= +github.com/onsi/ginkgo/v2 v2.33.0 h1:C8gBA6Uc2ZEubiV+SXiu5tZnMTwEmXHgkJwGozKtZf8= +github.com/onsi/ginkgo/v2 v2.33.0/go.mod h1:+aXOY+vzZ5mu2iI2HpTZUPmM//oQfsNFX6gU9kNcA44= +github.com/onsi/gomega v1.44.0 h1:eAiGl3Pw5jz5GQdDff0BcxYpAX1JxW8xD7mFUuwNfZQ= +github.com/onsi/gomega v1.44.0/go.mod h1:e/C2HwaZ1DhvjzXXuFhcR7hY7Sh9pl7MmoWKEjzwcdA= github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 h1:onHthvaw9LFnH4t2DcNVpwGmV9E1BkGknEliJkfwQj0= github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58/go.mod h1:DXv8WO4yhMYhSNPKjeNKa5WY9YCIEBRbNzFFPJbWO6Y= -github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M= -github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc= -github.com/petermattis/goid v0.0.0-20240813172612-4fcff4a6cae7 h1:Dx7Ovyv/SFnMFw3fD4oEoeorXc6saIiQ23LrGLth0Gw= -github.com/petermattis/goid v0.0.0-20240813172612-4fcff4a6cae7/go.mod h1:pxMtw7cyUw6B2bRH0ZBANSPg+AoSud1I1iyJHI69jH4= -github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= -github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pborman/getopt v0.0.0-20170112200414-7148bc3a4c30/go.mod h1:85jBQOZwpVEaDAr341tbn15RS4fCAsIst0qp7i8ex1o= +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/petermattis/goid v0.0.0-20250813065127-a731cc31b4fe h1:vHpqOnPlnkba8iSxU4j/CvDSS9J4+F4473esQsYLGoE= +github.com/petermattis/goid v0.0.0-20250813065127-a731cc31b4fe/go.mod h1:pxMtw7cyUw6B2bRH0ZBANSPg+AoSud1I1iyJHI69jH4= +github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 h1:GFCKgmp0tecUJ0sJuv4pzYCqS9+RGSn52M3FUwPs+uo= +github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10/go.mod h1:t/avpk3KcrXxUnYOhZhMXJlSEyie6gQbtLq5NM3loB8= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prometheus/client_golang v1.24.1 h1:JnJkREXzWxUdCuPFpIWZiPispT9xVV59uiuyR2bPlnU= github.com/prometheus/client_golang v1.24.1/go.mod h1:F+oSRECHg4sse5ucfYpYDeIv/hu68Zo0uoHKetWnzcE= -github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk= -github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE= -github.com/prometheus/common v0.70.1 h1:1HvjP4D5oL3t8RsPlwxA9onvvStjtIHYE5XuuwOi/PY= -github.com/prometheus/common v0.70.1/go.mod h1:VdFUQDMZK3VLkurFUVhia6uys/0suUp86TJz5qbJRhc= -github.com/prometheus/procfs v0.21.1 h1:GljZCt+zSTS+NZq88cyQ1LjZ+RCHp3uVuabBWA5+OJI= -github.com/prometheus/procfs v0.21.1/go.mod h1:aB55Cww9pdSJVHk0hUf0inxWyyjPogFIjmHKYgMKmtY= +github.com/prometheus/client_model v0.6.3 h1:O0jaTVAYNxTHYInEPFJt5I3+sN8zqBtVMPTB1qyxiEo= +github.com/prometheus/client_model v0.6.3/go.mod h1:gpN5P9S7Rr6Yr92PiQ+Ixvhf6JZEkF1dnxsYL2aPBEM= +github.com/prometheus/common v0.72.0 h1:tAYsE+sPJxIncDAobm4H5aQjmox9ZxEIIqPbiffa8G4= +github.com/prometheus/common v0.72.0/go.mod h1:77NWqAQ2tXT7BIK40qjJdw5Acrsrg1TlHAnsQi3i6mk= +github.com/prometheus/procfs v0.22.0 h1:6q9+/JL9IKAPbCmBrv9n5O5Ty3NKnciV5X7YGw0oics= +github.com/prometheus/procfs v0.22.0/go.mod h1:CvmFr/GVhIjIvWJZW3tgkODBQMRIf0EyWMQLHCHab58= 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.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/sagikazarmark/locafero v0.4.0 h1:HApY1R9zGo4DBgr7dqsTH/JJxLTTsOt7u6keLGt6kNQ= -github.com/sagikazarmark/locafero v0.4.0/go.mod h1:Pe1W6UlPYUk/+wc/6KFhbORCfqzgYEpgQ3O5fPuL3H4= -github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE= -github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ= -github.com/sasha-s/go-deadlock v0.3.5 h1:tNCOEEDG6tBqrNDOX35j/7hL5FcFViG6awUGROb2NsU= -github.com/sasha-s/go-deadlock v0.3.5/go.mod h1:bugP6EGbdGYObIlx7pUZtWqlvo8k9H6vCBBsiChJQ5U= -github.com/sirupsen/logrus v1.9.4 h1:TsZE7l11zFCLZnZ+teH4Umoq5BhEIfIzfRDZ1Uzql2w= -github.com/sirupsen/logrus v1.9.4/go.mod h1:ftWc9WdOfJ0a92nsE2jF5u5ZwH8Bv2zdeOC42RjbV2g= -github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo= -github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0= +github.com/sagikazarmark/locafero v0.11.0 h1:1iurJgmM9G3PA/I+wWYIOw/5SyBtxapeHDcg+AAIFXc= +github.com/sagikazarmark/locafero v0.11.0/go.mod h1:nVIGvgyzw595SUSUE6tvCp3YYTeHs15MvlmU87WwIik= +github.com/sasha-s/go-deadlock v0.3.6 h1:TR7sfOnZ7x00tWPfD397Peodt57KzMDo+9Ae9rMiUmw= +github.com/sasha-s/go-deadlock v0.3.6/go.mod h1:CUqNyyvMxTyjFqDT7MRg9mb4Dv/btmGTqSR+rky/UXo= +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= @@ -260,15 +268,14 @@ github.com/spf13/cobra v1.10.2/go.mod h1:7C1pvHqHw5A4vrJfjNwvOdzYu0Gml16OCs2GRiT 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= -github.com/spf13/viper v1.19.0 h1:RWq5SEjt8o25SROyN3z2OrDB9l7RPd3lwTWU8EcEdcI= -github.com/spf13/viper v1.19.0/go.mod h1:GQUN9bilAbhU/jgc1bKs99f/suXKeUMct8Adx5+Ntkg= +github.com/spf13/viper v1.21.0 h1:x5S+0EU27Lbphp4UKm1C+1oQO+rKx36vfCoaVebLFSU= +github.com/spf13/viper v1.21.0/go.mod h1:P0lhsswPGWD/1lZJ9ny3fYnVqxiegrlNrEmgLjbTCAY= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.5.3 h1:jmXUvGomnU1o3W/V5h2VEradbpJDwGrzugQQvL0POH4= github.com/stretchr/objx v0.5.3/go.mod h1:rDQraq+vQZU7Fde9LOZLr8Tax6zZvy4kuNKF+QYS+U0= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= -github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/stretchr/testify v1.12.1 h1:EuwCh5fleGS7H32xRwO3wRGT7DxrDhLAT6FF8MpWDWE= +github.com/stretchr/testify v1.12.1/go.mod h1:MDEgiDPPsNp5cuIrHPPCyornHKgEVbtFUmoNlxoYthg= github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= github.com/tidwall/gjson v1.19.0 h1:xwxm7n691Uf3u5OFjzngavjGTh55KX5q/9w9xHW88JU= @@ -279,36 +286,33 @@ github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4= github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= github.com/tidwall/sjson v1.2.5 h1:kLy8mja+1c9jlljvWTlSazM7cKDRfJuR/bOJhcY5NcY= github.com/tidwall/sjson v1.2.5/go.mod h1:Fvgq9kS/6ociJEDnK0Fk1cpYF4FIW6ZF7LAe+6jwd28= -github.com/vishvananda/netlink v1.3.1-0.20250303224720-0e7078ed04c8 h1:Y4egeTrP7sccowz2GWTJVtHlwkZippgBTpUmMteFUWQ= -github.com/vishvananda/netlink v1.3.1-0.20250303224720-0e7078ed04c8/go.mod h1:i6NetklAujEcC6fK0JPjT8qSwWyO0HLn4UKG+hGqeJs= -github.com/vishvananda/netns v0.0.4/go.mod h1:SpkAiCQRtJ6TvvxPnOSyH3BMl6unz3xZlaprSwhNNJM= +github.com/vishvananda/netlink v1.3.2-0.20260109214200-c6faf428e8f8 h1:/EaCkwYyCH9rDgccb78ZTaGwo7UGjjdh0iyCa3+miRs= +github.com/vishvananda/netlink v1.3.2-0.20260109214200-c6faf428e8f8/go.mod h1:lEui7SPMd9fgxzHVGRAvTxsBGCF6PRH81o2kLWLWHgw= github.com/vishvananda/netns v0.0.5 h1:DfiHV+j8bA32MFM7bfEunvT8IAqQ/NzSJHtcmW5zdEY= github.com/vishvananda/netns v0.0.5/go.mod h1:SpkAiCQRtJ6TvvxPnOSyH3BMl6unz3xZlaprSwhNNJM= github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -go.mongodb.org/mongo-driver v1.17.9 h1:IexDdCuuNJ3BHrELgBlyaH9p60JXAvdzWR128q+U5tU= -go.mongodb.org/mongo-driver v1.17.9/go.mod h1:LlOhpH5NUEfhxcAwG0UEkMqwYcc4JU18gtCdGudk/tQ= 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.67.0 h1:OyrsyzuttWTSur2qN/Lm0m2a8yqyIjUVBZcxFPuXq2o= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.67.0/go.mod h1:C2NGBr+kAB4bk3xtMXfZ94gqFDtg/GkI7e9zqGh5Beg= -go.opentelemetry.io/otel v1.44.0 h1:JjwHmHpA4iZ3wBxluu2fbbE7j4kqlE8jXyAyPXH7HqU= -go.opentelemetry.io/otel v1.44.0/go.mod h1:BMgjTHL9WPRlRjL2oZCBTL4whCGtXch2H4BhOPIAyYc= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.43.0 h1:88Y4s2C8oTui1LGM6bTWkw0ICGcOLCAI5l6zsD1j20k= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.43.0/go.mod h1:Vl1/iaggsuRlrHf/hfPJPvVag77kKyvrLeD10kpMl+A= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.43.0 h1:RAE+JPfvEmvy+0LzyUA25/SGawPwIUbZ6u0Wug54sLc= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.43.0/go.mod h1:AGmbycVGEsRx9mXMZ75CsOyhSP6MFIcj/6dnG+vhVjk= -go.opentelemetry.io/otel/metric v1.44.0 h1:1w0gILTcHdr3YI+ixLyjemwrVnsMURbTZFrSYCdDdmc= -go.opentelemetry.io/otel/metric v1.44.0/go.mod h1:8O7hanEPBNgEMmybD3s2VBKcgWOCsA6tzHBPODAiquo= -go.opentelemetry.io/otel/sdk v1.44.0 h1:nHYwb9lK+fJPU/dnT6s7W7Z8itMWyqrnVfbheVYrZ58= -go.opentelemetry.io/otel/sdk v1.44.0/go.mod h1:Osuydd3Se74nqjAKxid74N5eC+jfEqfTegHRnq58oK0= -go.opentelemetry.io/otel/sdk/metric v1.44.0 h1:3LlKgI+VjbVsjNRFZJZAJ30WjXC5VkNRks6si09iEfI= -go.opentelemetry.io/otel/sdk/metric v1.44.0/go.mod h1:5B5pMARnXxKhltooO4xUuCBorl65a4EpnTalObqOigA= -go.opentelemetry.io/otel/trace v1.44.0 h1:jxF5CsGYCe74MCRx2X4g7WsY/VBKRqqpNvXlX/6gtIk= -go.opentelemetry.io/otel/trace v1.44.0/go.mod h1:oLl1jrMQAVo6v3GAggN+1VH9VIz9iUSvW53sW1Q8PIE= -go.opentelemetry.io/proto/otlp v1.10.0 h1:IQRWgT5srOCYfiWnpqUYz9CVmbO8bFmKcwYxpuCSL2g= -go.opentelemetry.io/proto/otlp v1.10.0/go.mod h1:/CV4QoCR/S9yaPj8utp3lvQPoqMtxXdzn7ozvvozVqk= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.71.0 h1:3g7B90UzBltIDKq1/5mrTGxTnOFDV0ICOhLoxiZ8jlg= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.71.0/go.mod h1:Ef8SuTh59BT7+ofpDxN9z+yOlc4t2GjLmKDgYNJL/NU= +go.opentelemetry.io/otel v1.46.0 h1:FHt5/CDyVxi/8IM1CH7VE/rRgq3kLHa2mSTVMO8AWyc= +go.opentelemetry.io/otel v1.46.0/go.mod h1:Gj3SEScelsNC45tp4nSxRYlS+f5iez7W8XPMCt905kE= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.46.0 h1:OFnwLJr+pF3iHrlGSzbxyuo6/6HyBlnlN1CWEJmBVcw= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.46.0/go.mod h1:716wFneO0ov19A2beH5hjfh9AK5z/VWNAtDijp1Y0/g= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.46.0 h1:w53CDeOA/Kurp7yRsegSr6pbbr759dOvJ+yNmWM6Hxs= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.46.0/go.mod h1:BOmGMCbAtvcJiSJ+hLuhgPLdDbimnraSl8irz3iY8sY= +go.opentelemetry.io/otel/metric v1.46.0 h1:yBnkXvgV7AXFILZc5K6IZe/CBFF3OS7BJ8ov6/lj0K8= +go.opentelemetry.io/otel/metric v1.46.0/go.mod h1:iPmdWqifKUdzziPkvvzIJXITl56fQx2mGM/DHLB3/2o= +go.opentelemetry.io/otel/sdk v1.46.0 h1:h5CNQQjEbuQXY/JfZtgt3i7HVFV3aHPO2OAwO2eTYPI= +go.opentelemetry.io/otel/sdk v1.46.0/go.mod h1:GAERFXFt5SYCEB+YiKUbMBeza6UaDH7GmGOZEfh2gSM= +go.opentelemetry.io/otel/sdk/metric v1.46.0 h1:0piZ26EG4RBfebb2jhDH6ERCYHoVWduc3kLgPCwSnSE= +go.opentelemetry.io/otel/sdk/metric v1.46.0/go.mod h1:I1PbKrdVc8Qu8HYVDNtqVIwLwjNrhsV/uFuxfwg8mO4= +go.opentelemetry.io/otel/trace v1.46.0 h1:OULy7ccdJnZtJ0UDYFOIGaCmiWzJ8Vi2G/Rsu60qs1c= +go.opentelemetry.io/otel/trace v1.46.0/go.mod h1:J7GAXweO77XSFkB/rmAqk9D6ihszhFjLU+d9WuUxDLI= +go.opentelemetry.io/proto/otlp v1.11.0 h1:5rrYs0Ykyj50sdU/JU0x8etU+LubXWb+gED6TbEdMIk= +go.opentelemetry.io/proto/otlp v1.11.0/go.mod h1:SmVizdCOAm3XBtG1g1NnOdhW6jtddT72hLMhv8VwA8E= go.uber.org/dig v1.17.1 h1:Tga8Lz8PcYNsWsyHMZ1Vm0OQOUaJNDyvPImgbAu9YSc= go.uber.org/dig v1.17.1/go.mod h1:Us0rSJiThwCv2GteUN0Q7OKvU7n5J4dxZ9JKUXozFdE= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= @@ -319,8 +323,9 @@ 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.4 h1:tuyd0P+2Ont/d6e2rl3be67goVK4R6deVxCUX5vyPaQ= go.yaml.in/yaml/v2 v2.4.4/go.mod h1:gMZqIpDtDqOfM0uNfy0SkpRhvUryYH0Z6wdMYcacYXQ= -go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= +go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw= +go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg= go4.org/intern v0.0.0-20211027215823-ae77deb06f29 h1:UXLjNohABv4S58tHmeuIZDO6e3mHpW2Dx33gaNt03LE= go4.org/intern v0.0.0-20211027215823-ae77deb06f29/go.mod h1:cS2ma+47FKrLPdXFpr7CuxiTW3eyJbWew4qx0qtQWDA= go4.org/netipx v0.0.0-20231129151722-fdeea329fbba h1:0b9z3AuHCjxk0x/opv64kcgZLBseWJUpBw5I82+2U4M= @@ -331,28 +336,28 @@ go4.org/unsafe/assume-no-moving-gc v0.0.0-20230525183740-e7c30c78aeb2/go.mod h1: golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/exp v0.0.0-20260508232706-74f9aab9d74a h1:+3jdDGGB8NGb1Zktc737jlt3/A5f6UlwSzmvqUuufxw= -golang.org/x/exp v0.0.0-20260508232706-74f9aab9d74a/go.mod h1:d2fgXJLVs4dYDHUk5lwMIfzRzSrWCfGZb0ZqeLa/Vcw= +golang.org/x/exp v0.0.0-20260824195058-e88cd73687aa h1:QSyA8ishJCyT21kER9KwNt0b7BM3iRK4x9QXhjN5Fdk= +golang.org/x/exp v0.0.0-20260824195058-e88cd73687aa/go.mod h1:zeBbvyFKDaLwa7CH/zI8KXt7gTl14SF7sO08Pl5jBCM= golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.37.0 h1:vF1DjpVEshcIqoEaauuHebaLk1O1forxjxBaVn884JQ= -golang.org/x/mod v0.37.0/go.mod h1:m8S8VeM9r4dzDwjrKO0a1sZP3YjeMamRRlD+fmR2Q/0= +golang.org/x/mod v0.41.0 h1:qJmnOUb4YB+FsEuM3HcWucdZASCPGhsX6uljO6pog0c= +golang.org/x/mod v0.41.0/go.mod h1:Ek9pY8RKWXwsWvd3rQiHYtMqkjSUV+s1Rj7j4H5Ur6o= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20220923203811-8be639271d50/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= -golang.org/x/net v0.57.0 h1:K5+3DljvIuDG9/Jv9rvyMywYNFCQ9RSUY6OOTTkT+tE= -golang.org/x/net v0.57.0/go.mod h1:KpXc8iv+r3XplLAG/f7Jsf9RPszJzdR0f58q9vGOuEU= -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/net v0.59.0 h1:5zfYln+w5XCxwrnMMJPufRgNoXEaGxl0wo5GqPXyues= +golang.org/x/net v0.59.0/go.mod h1:2DA/G1UfVbCpQPeWTmMPGY7Cs2PkBkwu743bVX5PIVg= +golang.org/x/oauth2 v0.37.0 h1:JUlcxA8oAtauLfiH8FX2/FkAWHAdi0QtGCGc+hofE98= +golang.org/x/oauth2 v0.37.0/go.mod h1:IxwZNxUULJmpBFf9K/9NTMSIfZZuvuTy1gGxhigP/58= golang.org/x/sync v0.0.0-20190423024810-112230192c58/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.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220923202941-7f9b1623fab7/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.22.0 h1:SZjpbeLmrCk4xhRSZFNZW5gFUeCeFgjekvI/+gfScek= -golang.org/x/sync v0.22.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= +golang.org/x/sync v0.23.0 h1:KameEIfc1IkluZyXWLn39Wd4tURc6GbCiISGiZm2bQk= +golang.org/x/sync v0.23.0/go.mod h1:sUUOizhqBxiL6pEWpqNLUiaJn1ShEbZ6BBqskPbjZm0= 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-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -360,26 +365,24 @@ golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220319134239-a9b59b0215f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.47.0 h1:o7XGOvZQCADBQQ4Y7VNq2dRWQR7JmOUW8Kxx4ZsNgWs= -golang.org/x/sys v0.47.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= +golang.org/x/sys v0.48.0 h1:bbX/i/6MgT9BVLM9RT1thmxL04yeTAhbEz4SyadbXoo= +golang.org/x/sys v0.48.0/go.mod h1:hNLxWAXmnKAxqDtdwIYC4bM9oQPEecfsnNMuSxOs3og= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.45.0 h1:NwWyBmoJCbfTHpxrWoZ9C6/VxOf7ic219I8xZZFdrf0= -golang.org/x/term v0.45.0/go.mod h1:9aqxs0blBcrm/n0L9QW0aRVD+ktan8ssZromtqJC43w= +golang.org/x/term v0.46.0 h1:3+OXuTbaKDgwk8jTi3aSLHRlmWqHEUDUtxnbFigO4YE= +golang.org/x/term v0.46.0/go.mod h1:+K02xbkittuwc0Am4abfA3Fc+XRGXkvBXNO88NCXPoc= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.40.0 h1:Ub2Z6/xjgF1WrYQz2nuITOEegKFtiIy+rieRJ5lHZKs= -golang.org/x/text v0.40.0/go.mod h1:hpnzDAfGV753zIKo+wk3u1bVKCGPbrnF7+7LBF/UHVY= +golang.org/x/text v0.42.0 h1:JbOZXgfeCPU9gacVtYliJqOhD+zhrEqK4LfdpmlUZqI= +golang.org/x/text v0.42.0/go.mod h1:ojzP1Z+2QtioaF8DTtO8K5q7JWVVYwZKenzujK0Zd0E= golang.org/x/time v0.15.0 h1:bbrp8t3bGUeFOx08pvsMYRTCVSMk89u4tKbNOZbp88U= golang.org/x/time v0.15.0/go.mod h1:Y4YMaQmXwGQZoFaVFk4YpCt4FLQMYKZe9oeV/f4MSno= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= -golang.org/x/tools v0.47.0 h1:7Kn5x/d1svx/PzryTsqeoZN4TZwqeH5pGWjefhLi/1Q= -golang.org/x/tools v0.47.0/go.mod h1:dFHnyTvFWY212G+h7ZY4Vsp/K3U4/7W9TyVaAul8uCA= +golang.org/x/tools v0.49.0 h1:3NI7VXzL9+1WZD52Dx2ttoPwD5DWrFGpl9mFZDlmisI= +golang.org/x/tools v0.49.0/go.mod h1:SJNXV9DBKT0UbdttsQjbfJlAE/q+y36++zo3uL3N0Oo= 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= @@ -388,16 +391,16 @@ gomodules.xyz/jsonpatch/v2 v2.5.0 h1:JELs8RLM12qJGXU4u/TO3V25KW8GreMKl9pdkk14RM0 gomodules.xyz/jsonpatch/v2 v2.5.0/go.mod h1:AH3dM2RI6uoBZxn3LVrfvJ3E0/9dG4cSrbuBJT4moAY= gonum.org/v1/gonum v0.17.0 h1:VbpOemQlsSMrYmn7T2OUvQ4dqxQXU+ouZFQsZOx50z4= gonum.org/v1/gonum v0.17.0/go.mod h1:El3tOrEuMpv2UdMrbNlKEh9vd86bmQ6vqIcDwxEOc1E= -google.golang.org/genproto v0.0.0-20260319201613-d00831a3d3e7 h1:XzmzkmB14QhVhgnawEVsOn6OFsnpyxNPRY9QV01dNB0= -google.golang.org/genproto v0.0.0-20260319201613-d00831a3d3e7/go.mod h1:L43LFes82YgSonw6iTXTxXUX1OlULt4AQtkik4ULL/I= -google.golang.org/genproto/googleapis/api v0.0.0-20260526163538-3dc84a4a5aaa h1:Kjn0N0tCrDgiAFW+lGO4JZ3ck44CehvJQMAwj9QF0G8= -google.golang.org/genproto/googleapis/api v0.0.0-20260526163538-3dc84a4a5aaa/go.mod h1:q4lMZS6kskjT5HvCPrnnypcDPVJqT/f4nfxmkE7gryY= -google.golang.org/genproto/googleapis/rpc v0.0.0-20260526163538-3dc84a4a5aaa h1:mZHHdPZl0dbGHCflZgAq/Q468DWVFcU2whhB2KAo8fk= -google.golang.org/genproto/googleapis/rpc v0.0.0-20260526163538-3dc84a4a5aaa/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8= -google.golang.org/grpc v1.83.0 h1:JeNZEKJFbQxArAMl+hiytHauacDNqJUllNfmIMmpqnQ= -google.golang.org/grpc v1.83.0/go.mod h1:kDyl6SKsiHKt0uylY5gtn5cEjkrIOhQOGDgIc4JGwzQ= -google.golang.org/protobuf v1.36.12-0.20260120151049-f2248ac996af h1:+5/Sw3GsDNlEmu7TfklWKPdQ0Ykja5VEmq2i817+jbI= -google.golang.org/protobuf v1.36.12-0.20260120151049-f2248ac996af/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= +google.golang.org/genproto v0.0.0-20260904194346-d0f1323225a4 h1:6QRNo34FxJcE1LG6XfnmaQ17m6w21oXaKlKoBaF+UqE= +google.golang.org/genproto v0.0.0-20260904194346-d0f1323225a4/go.mod h1:eifJ91nefXnfP/bMkZsACVGm14ZtEL4eUXRLTryfbY4= +google.golang.org/genproto/googleapis/api v0.0.0-20260904194346-d0f1323225a4 h1:NCe/UiklGd/9xjT+ROBVhJ1kf6TRQaFedsR+z7u1gvo= +google.golang.org/genproto/googleapis/api v0.0.0-20260904194346-d0f1323225a4/go.mod h1:fJ2lYaWjqNknJyQBOCd0fA3HnEElJqGplH71a2txi+g= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260904194346-d0f1323225a4 h1:5t+ZydAFj5kGVLrgCvLmpmCf9ylGRd64hpEronfRaws= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260904194346-d0f1323225a4/go.mod h1:DjtHYE8FKJLivXcBEjGwndXfIC23G0VpXiXKqG179uA= +google.golang.org/grpc v1.84.0 h1:soMyaPJ8pAak5PIQ0DGBUir0XRo2fRoMqhNWMLlLxO0= +google.golang.org/grpc v1.84.0/go.mod h1:ljCht0DrxQrXBDRTZp52Qxh3Ffk8CdYm2sj4O2QN2C0= +google.golang.org/protobuf v1.36.12 h1:pJOKDDOyeXErUroCihFAd5LQuwXBSpVnKGrj5o/fwxc= +google.golang.org/protobuf v1.36.12/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= @@ -405,42 +408,45 @@ gopkg.in/evanphx/json-patch.v4 v4.13.0 h1:czT3CmqEaQ1aanPc5SdlgQrrEIb8w/wwCvWWnf gopkg.in/evanphx/json-patch.v4 v4.13.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWMG4EsWvDvM72M= gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= -gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= -gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= inet.af/netaddr v0.0.0-20230525184311-b8eac61e914a h1:1XCVEdxrvL6c0TGOhecLuB7U9zYNdxZEjvOqJreKZiM= inet.af/netaddr v0.0.0-20230525184311-b8eac61e914a/go.mod h1:e83i32mAQOW1LAqEIweALsuK2Uw4mhQadA5r7b0Wobo= -k8s.io/api v0.35.5 h1:BrFeUDGY/LBtlA1R5RoxhlYRHs76RnQBc6xbm/y7hsQ= -k8s.io/api v0.35.5/go.mod h1:xWkFhMnoPZdTAQh95Rlw3zZpUUNVlFHcuESUYd06BWM= -k8s.io/apiextensions-apiserver v0.35.5 h1:HttlJjgsx3ddLsASCqklkKvfBlwUoXma8VLpeMG5YL8= -k8s.io/apiextensions-apiserver v0.35.5/go.mod h1:4xbAgP/jbt8sVHE3H4DfE1gSPLUoSzXrNqhZz1lTHKc= -k8s.io/apimachinery v0.35.5 h1:lbjjjUfVeVqFbiOpyhqZHc8DhiYkWOxSNij7lHx2U8Y= -k8s.io/apimachinery v0.35.5/go.mod h1:NNi1taPOpep0jOj+oRha3mBJPqvi0hGdaV8TCqGQ+cc= -k8s.io/apiserver v0.35.5 h1:ZtFpSEmxf/VmOdbL3bo7hLxyNRorRegqOLmYSW0mxEo= -k8s.io/apiserver v0.35.5/go.mod h1:6NNWFTq/UosCwUmqhQDC+3ApzSx5ekeYMIwzSG+49VU= -k8s.io/client-go v0.35.5 h1:wUrgqVSmFRw75bgSHY7X0G/hZM/QYpV0Hg7SYYOYpFk= -k8s.io/client-go v0.35.5/go.mod h1:Z0mDcAJsX1Y7RQfuQlJipiRtqf8Mhk2VDu1/JvRqdGo= -k8s.io/component-base v0.35.5 h1:1y1xxfpFNkNi4RMi6bvPNN4aDr9VhOijtEfrqnhPijs= -k8s.io/component-base v0.35.5/go.mod h1:n/+aL98XYINubqIu/Okh6mS/kZT2nMeN4IQkQR4VXRg= +k8s.io/api v0.36.5 h1:vtL/ByHmw7suLt+SGVMPZnuj8QdfZ0kN1vvMvHRCY9c= +k8s.io/api v0.36.5/go.mod h1:erfc3/3d6z30KO2Kf+BmOewE6CajqZTz1Hcd+FQqg60= +k8s.io/apiextensions-apiserver v0.36.5 h1:pZcOcB7ur1ou+VeQSGChj3cvhrL2nA3gERMUaCHVJNQ= +k8s.io/apiextensions-apiserver v0.36.5/go.mod h1:l1uFNtfzcEBqgrYEiOIYwu+6R1RCkuZIiEvfPE6RpNs= +k8s.io/apimachinery v0.36.5 h1:X5Xbg6G4om0ma6LOcGbmUmo+B5Nq7mmIpAWhk+tgCh8= +k8s.io/apimachinery v0.36.5/go.mod h1:oPTSicSaDHDdoGwOs5DFPo6YS2wdTnzFHIYMEglfJ7I= +k8s.io/apiserver v0.36.5 h1:KX599gn2a5fQXeoCrDNdpND+AJ9ymq3u4XXvAslwnlo= +k8s.io/apiserver v0.36.5/go.mod h1:wxPt+sgzaPkk+WOgsrwsGB5eyRFXlRuPd2EoZSvh5k8= +k8s.io/client-go v0.36.5 h1:D79FgevPon0NIBcjUHMy+QnALtihLGTBqOvIGCgoZmw= +k8s.io/client-go v0.36.5/go.mod h1:JAGQ3N4Z8E3H0W0Nm6NBg1EvB9+/wtEoBPDe8XGkHsI= +k8s.io/component-base v0.36.5 h1:4lvIJ/QZDOchuIRqIna6JqjqO2MoDsH7JdaQ8FJtLVw= +k8s.io/component-base v0.36.5/go.mod h1:ZCmi+txXm31F2uYY5p9FwQsww0g5BM0pI/gNsH9uAMY= k8s.io/klog/v2 v2.140.0 h1:Tf+J3AH7xnUzZyVVXhTgGhEKnFqye14aadWv7bzXdzc= k8s.io/klog/v2 v2.140.0/go.mod h1:o+/RWfJ6PwpnFn7OyAG3QnO47BFsymfEfrz6XyYSSp0= -k8s.io/kube-openapi v0.0.0-20260427204847-8949caaa1199 h1:sWu4Td5mgJlwunsUydnhKEAfNUHM7hm1wfKEQmD7G5c= -k8s.io/kube-openapi v0.0.0-20260427204847-8949caaa1199/go.mod h1:uGBT7iTA6c6MvqUvSXIaYZo9ukscABYi2btjhvgKGZ0= -k8s.io/metrics v0.36.3 h1:NDKceAgWS8CJCdDtM5kFACkBOa9Lxia1jUiibJfvUgQ= -k8s.io/metrics v0.36.3/go.mod h1:NTLS8ybwn+zYGwKqYublWPvmnNp8N4pV3etjtx7XWaM= -k8s.io/utils v0.0.0-20260319190234-28399d86e0b5 h1:kBawHLSnx/mYHmRnNUf9d4CpjREbeZuxoSGOX/J+aYM= -k8s.io/utils v0.0.0-20260319190234-28399d86e0b5/go.mod h1:xDxuJ0whA3d0I4mf/C4ppKHxXynQ+fxnkmQH0vTHnuk= -sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.34.0 h1:hSfpvjjTQXQY2Fol2CS0QHMNs/WI1MOSGzCm1KhM5ec= -sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.34.0/go.mod h1:Ve9uj1L+deCXFrPOk1LpFXqTg7LCFzFso6PA48q/XZw= -sigs.k8s.io/controller-runtime v0.23.3 h1:VjB/vhoPoA9l1kEKZHBMnQF33tdCLQKJtydy4iqwZ80= -sigs.k8s.io/controller-runtime v0.23.3/go.mod h1:B6COOxKptp+YaUT5q4l6LqUJTRpizbgf9KSRNdQGns0= +k8s.io/kube-openapi v0.0.0-20260821135717-be32def86098 h1:z5+pcu1jTyKK5mNTe2/+x+U6Uuv9jRVOJQLaBJJMpeI= +k8s.io/kube-openapi v0.0.0-20260821135717-be32def86098/go.mod h1:0/mqHCVhlumdJ3BhCfnjSZQE037nAhNodh1/hK0T8/I= +k8s.io/metrics v0.36.5 h1:UE0VhdGrgpLvK6gmyyr5fX0o2zW/Z5OL7CPBLnoJr88= +k8s.io/metrics v0.36.5/go.mod h1:eCjFrV/1gjxIvD8XVyKAIE8erzXZZPapOYAzUqWft7g= +k8s.io/streaming v0.36.5 h1:gcY9VwgT4J/idqwS9ZrzJtH8ouHvZuFEyDWofcaL+F4= +k8s.io/streaming v0.36.5/go.mod h1:tJ6S2bZa2HxIBauguBbCWSCYyd93Grfz1+z3tcOvlDE= +k8s.io/utils v0.0.0-20260707023825-cf1189d6abe3 h1:jVkFFVfXdXP74B/zbO3hM3hpSFD0xvhQ5U686DPurkE= +k8s.io/utils v0.0.0-20260707023825-cf1189d6abe3/go.mod h1:M2s5JB1lIYP3jzZdorPLHXIPJzt9vv2muW5a6L9DtNM= +sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.36.0 h1:/YpDJ4vReG7ZmzSpBGxduXgywWkJU9zHubgJG03MT+Y= +sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.36.0/go.mod h1:tJo1aepTXyR+8Xs3sUsGBDk4Ub2AM5dPAPKJx0mpm5c= +sigs.k8s.io/controller-runtime v0.24.1 h1:miPEwrmirImAvgME1L9qebGHrOnGJoVmVdtOU9fRfo4= +sigs.k8s.io/controller-runtime v0.24.1/go.mod h1:vFkfY5fGt5xAC/sKb8IBFKgWPNKG9OUG29dR8Y2wImw= +sigs.k8s.io/gateway-api v1.4.0-rc.2 h1:1JVw4/b7ug+3AgWDQDSPAnovePYBmSiZ1H1muzgQv8s= +sigs.k8s.io/gateway-api v1.4.0-rc.2/go.mod h1:AR5RSqciWP98OPckEjOjh2XJhAe2Na4LHyXD2FUY7Qk= sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730 h1:IpInykpT6ceI+QxKBbEflcR5EXP7sU1kvOlxwZh5txg= sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730/go.mod h1:mdzfpAEoE6DHQEN0uh9ZbOCuHbLK5wOm7dK4ctXE9Tg= +sigs.k8s.io/mcs-api v0.3.1-0.20260224125735-0f775a3eff97 h1:SCB1P+SmtFETI9jkWt7bRVYXPeFPtT1zFtskvfXYetU= +sigs.k8s.io/mcs-api v0.3.1-0.20260224125735-0f775a3eff97/go.mod h1:zZ5CK8uS6HaLkxY4HqsmcBHfzHuNMrY2uJy8T7jffK4= sigs.k8s.io/randfill v1.0.0 h1:JfjMILfT8A6RbawdsK2JXGBR5AQVfd+9TbzrlneTyrU= sigs.k8s.io/randfill v1.0.0/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY= -sigs.k8s.io/structured-merge-diff/v6 v6.4.0 h1:qmp2e3ZfFi1/jJbDGpD4mt3wyp6PE1NfKHCYLqgNQJo= -sigs.k8s.io/structured-merge-diff/v6 v6.4.0/go.mod h1:M3W8sfWvn2HhQDIbGWj3S099YozAsymCo/wrT5ohRUE= +sigs.k8s.io/structured-merge-diff/v6 v6.4.2 h1:qdOxHwrl2Kaag1aQEarlYcOA9vSyGCp3CIki3aW8c4Q= +sigs.k8s.io/structured-merge-diff/v6 v6.4.2/go.mod h1:M3W8sfWvn2HhQDIbGWj3S099YozAsymCo/wrT5ohRUE= sigs.k8s.io/yaml v1.6.0 h1:G8fkbMSAFqgEFgh4b1wmtzDnioxFCUgTZhlbj5P9QYs= sigs.k8s.io/yaml v1.6.0/go.mod h1:796bPqUfzR/0jLAl6XjHl3Ck7MiyVv8dbTdyT3/pMf4= diff --git a/hack/install-new-zxporter.sh b/hack/install-new-zxporter.sh index c6c9f1bd..c850ef5e 100755 --- a/hack/install-new-zxporter.sh +++ b/hack/install-new-zxporter.sh @@ -35,6 +35,11 @@ done echo "" echo "=== Installing zxporter (nodemon mode, no Prometheus) ===" +# The charts vendor devzero-common into their gitignored charts/ directories. +for chart in zxporter-nodemon zxporter; do + helm dependency build "./helm-chart/$chart" >/dev/null +done + helm install "$RELEASE_NAME" ./helm-chart/zxporter \ --namespace "$NAMESPACE" --create-namespace \ --set image.repository="$IMAGE_REPO" \ diff --git a/helm-chart/devzero-common/Chart.yaml b/helm-chart/devzero-common/Chart.yaml new file mode 100644 index 00000000..86a502e3 --- /dev/null +++ b/helm-chart/devzero-common/Chart.yaml @@ -0,0 +1,13 @@ +apiVersion: v2 +name: devzero-common +description: >- + DevZero platform library chart (D3). Exports the shared named templates every + DevZero subchart imports — labels, image resolution, resources, telemetry, + TLS, secrets, and service account — so cross-chart conventions live in one + place instead of ten diverged _helpers.tpl copies. Not installable on its own. +type: library +version: 0.1.0 +# appVersion intentionally omitted: a library chart ships no application. +maintainers: + - name: DevZero + url: https://github.com/devzero-inc/services diff --git a/helm-chart/devzero-common/README.md b/helm-chart/devzero-common/README.md new file mode 100644 index 00000000..d0fdeb46 --- /dev/null +++ b/helm-chart/devzero-common/README.md @@ -0,0 +1,67 @@ +# devzero-common + +DevZero platform **Helm library chart** (D3 of the release-flow standardization — +see [`docs/plans/release-flow-standardization.md`](https://github.com/devzero-inc/services/blob/main/docs/plans/release-flow-standardization.md) §5). +It exports the shared named templates every DevZero subchart imports, so +cross-chart conventions (labels, image resolution, resources, telemetry, TLS, +secrets, service account) live in one place instead of ten independently-diverged +`_helpers.tpl` copies. + +It is `type: library` — not installable on its own; it only ships `define`d +templates. + +## Use it from a chart + +Add the dependency in the consumer's `Chart.yaml`: + +```yaml +dependencies: + - name: devzero-common + version: 0.1.0 + repository: file://../devzero-common # once both live under charts/ +``` + +Re-export the helpers as one-liners in the consumer's `templates/_helpers.tpl` +so local template names stay conventional: + +``` +{{- define "mychart.labels" -}}{{ include "devzero-common.labels" . }}{{- end -}} +{{- define "mychart.fullname" -}}{{ include "devzero-common.fullname" . }}{{- end -}} +``` + +## Exported templates + +| Template | Emits | Typical usage | +|---|---|---| +| `devzero-common.name` / `.fullname` / `.chart` | name strings | metadata.name, chart label | +| `devzero-common.labels` | full label set (`app.kubernetes.io/*`, `part-of: dz-platform`, `devzero.io/component`, + `global.commonLabels`). `commonLabels` may **add** keys but never override a standard one — the standard keys carry the selector labels, so letting them be reassigned could desync a workload's labels from its immutable selector. | `labels: {{- include ... \| nindent 4 }}` | +| `devzero-common.selectorLabels` | immutable subset (name + instance) | selector `matchLabels` | +| `devzero-common.image` | `/:` (registry ← `global.imageRegistry`→`image.registry`; tag ← `image.tag`→`.Chart.AppVersion`) | `image: {{ include ... }}` | +| `devzero-common.imageRef` | a full image reference with its registry host replaced by `global.imageRegistry`, keeping the path, tag and digest (`registry.k8s.io/pause:3.9` → `/pause:3.9`, `alpine:3.19` → `/library/alpine:3.19`); the input unchanged when the global is empty. For charts that build references from many keys rather than one `image` object. Tests: `tools/chartlibtest` | `image: {{ include ... (dict "ref" $ref "global" .Values.global) \| quote }}` | +| `devzero-common.imagePullSecrets` | merged `global.imagePullSecrets` + `image.pullSecrets` block, deduped by name (accepts `["name"]` and `[{name: ...}]`, and dedupes across the two shapes) | `{{- include ... \| nindent 6 }}` | +| `devzero-common.clusterName` / `.dakrUrl` / `.k8sProvider` | `global.clusterName` / `global.dakrUrl` / `global.k8sProvider` when set, else the chart's own value (the umbrella sets each once for every component; a standalone install keeps its own key) | `{{ include "devzero-common.dakrUrl" (list . .Values.dakrUrl) \| quote }}` | +| `devzero-common.resources` | `.Values.resources`, else a conservative default | `resources: {{- include ... \| nindent 12 }}` | +| `devzero-common.telemetry` | OTLP env vars from `global.telemetry.otlp` (empty when disabled) | container `env:` | +| `devzero-common.serviceAccount` / `.serviceAccount.name` | ServiceAccount object / resolved name | `templates/serviceaccount.yaml`, `serviceAccountName:` | +| `devzero-common.tls.certificate` | cert-manager `Certificate` when `global.tls.certManagerIssuerRef` is set | `templates/certificate.yaml` | +| `devzero-common.secretName` / `.createSecret` | resolved secret name / whether to create inline | `secretKeyRef.name`, `{{- if eq (include "devzero-common.createSecret" .) "true" }}` | + +`global.*` keys are the umbrella pass-through surface (D5): the platform umbrella +fans `global.imageRegistry`, `global.imagePullSecrets`, `global.telemetry`, +`global.tls`, `global.commonLabels`, and the cluster identity +(`global.clusterName`, `global.dakrUrl`, `global.k8sProvider`) into every +subchart automatically. A component honours a key only once its chart calls +the matching helper. + +## Values contract (enforced by `just chart-lint`) + +Consumer charts must follow the shared schema: `image` is always an **object** +(`registry`/`repository`/`tag`/`pullPolicy`/`pullSecrets`), never a bare string; +`serviceAccount.{create,name,annotations,automount}` (`automount: false` is honoured — it is not treated as unset); feature flags under +`features..enabled`. See §5 of the plan for the full schema. + +## Lint + +```sh +just chart-lint # helm lint + layout conventions on every chart under charts/ +``` diff --git a/helm-chart/devzero-common/templates/_globals.tpl b/helm-chart/devzero-common/templates/_globals.tpl new file mode 100644 index 00000000..e375a480 --- /dev/null +++ b/helm-chart/devzero-common/templates/_globals.tpl @@ -0,0 +1,42 @@ +{{/* +Cluster identity the platform umbrella sets once for every component: + + global.clusterName the name dakr shows for the cluster + global.dakrUrl the dakr endpoint the component reports to + global.k8sProvider the cloud provider (aws, gcp, azure, oci, other) + +Each helper takes (list $ ) and returns the global when +it is set, else the chart's own value, so the umbrella overrides every +component in one place (as global.imageRegistry does) and a standalone install +keeps using the key it always had. The result is a plain string; quote it at +the call site. + +Usage: KUBE_CONTEXT_NAME: {{ include "devzero-common.clusterName" (list . .Values.zxporter.kubeContextName) | quote }} +*/}} +{{- define "devzero-common.clusterName" -}} +{{- include "devzero-common.globalOr" (list (index . 0) "clusterName" (index . 1)) -}} +{{- end -}} + +{{- define "devzero-common.dakrUrl" -}} +{{- include "devzero-common.globalOr" (list (index . 0) "dakrUrl" (index . 1)) -}} +{{- end -}} + +{{- define "devzero-common.k8sProvider" -}} +{{- include "devzero-common.globalOr" (list (index . 0) "k8sProvider" (index . 1)) -}} +{{- end -}} + +{{/* +global. unless it is null or "", else the given value, else "". Only null +and "" count as unset: Helm's default would also skip 0, and a cluster named 0 +must not silently report as the chart's own name. Not for direct use: call the +named helpers above, which fix the set of keys. +*/}} +{{- define "devzero-common.globalOr" -}} +{{- $ctx := index . 0 -}} +{{- $g := $ctx.Values.global | default dict -}} +{{- $v := index $g (index . 1) -}} +{{- if or (kindIs "invalid" $v) (eq (toString $v) "") -}} +{{- $v = index . 2 -}} +{{- end -}} +{{- if kindIs "invalid" $v }}{{ else }}{{ $v }}{{ end -}} +{{- end -}} diff --git a/helm-chart/devzero-common/templates/_helpers.tpl b/helm-chart/devzero-common/templates/_helpers.tpl new file mode 100644 index 00000000..d1674476 --- /dev/null +++ b/helm-chart/devzero-common/templates/_helpers.tpl @@ -0,0 +1,60 @@ +{{/* +Name / fullname / chart helpers. Consumers re-export these from their own +templates/_helpers.tpl as one-liners, e.g. + {{- define "trezr.fullname" -}}{{ include "devzero-common.fullname" . }}{{- end -}} +*/}} + +{{- define "devzero-common.name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}} +{{- end -}} + +{{- define "devzero-common.fullname" -}} +{{- if .Values.fullnameOverride -}} +{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}} +{{- else -}} +{{- $name := default .Chart.Name .Values.nameOverride -}} +{{- if contains $name .Release.Name -}} +{{- .Release.Name | trunc 63 | trimSuffix "-" -}} +{{- else -}} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}} +{{- end -}} +{{- end -}} +{{- end -}} + +{{- define "devzero-common.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}} +{{- end -}} + +{{/* +Full label set. app.kubernetes.io/version falls back to the chart version when +a chart ships no appVersion (e.g. placeholder charts). part-of is fixed to the +platform; component is the chart name. global.commonLabels (umbrella pass-through) +can ADD keys but never override a standard one — see below. +*/}} +{{- define "devzero-common.labels" -}} +{{- $standard := dict + "helm.sh/chart" (include "devzero-common.chart" .) + "app.kubernetes.io/version" (.Chart.AppVersion | default .Chart.Version) + "app.kubernetes.io/managed-by" .Release.Service + "app.kubernetes.io/part-of" "dz-platform" + "devzero.io/component" .Chart.Name +-}} +{{- /* selectorLabels stays the single source of truth for name/instance. */ -}} +{{- $standard = merge $standard (include "devzero-common.selectorLabels" . | fromYaml) -}} +{{- /* One merged dict emitted once, rather than appending commonLabels as extra + lines. Appending would emit DUPLICATE YAML keys on collision, where the last + value silently wins — and since this set carries the selector labels, a + stray app.kubernetes.io/name in commonLabels could desync a workload's + metadata labels from its immutable selector. sprig's `merge` gives + precedence to the destination, so the standard keys win. */ -}} +{{- toYaml (merge $standard ((.Values.global | default dict).commonLabels | default dict)) | trim -}} +{{- end -}} + +{{/* +Selector labels — the immutable subset. Never add to this: Deployment/StatefulSet +selectors are immutable, so anything here is locked for the life of the workload. +*/}} +{{- define "devzero-common.selectorLabels" -}} +app.kubernetes.io/name: {{ include "devzero-common.name" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +{{- end -}} diff --git a/helm-chart/devzero-common/templates/_image.tpl b/helm-chart/devzero-common/templates/_image.tpl new file mode 100644 index 00000000..dd02ac18 --- /dev/null +++ b/helm-chart/devzero-common/templates/_image.tpl @@ -0,0 +1,115 @@ +{{/* +Image reference: "/:". registry falls back +global.imageRegistry -> image.registry (umbrella override wins); tag falls back +image.tag -> .Chart.AppVersion -> .Chart.Version. repository is required — the +schema forbids `image` as a bare string, so image.repository always exists. +Usage: image: {{ include "devzero-common.image" . }} +*/}} +{{- define "devzero-common.image" -}} +{{- $g := .Values.global | default dict -}} +{{- $img := .Values.image | default dict -}} +{{- $registry := $g.imageRegistry | default $img.registry -}} +{{- $repository := required "image.repository is required (image must be an object, not a string)" $img.repository -}} +{{- $tag := $img.tag | default .Chart.AppVersion | default .Chart.Version -}} +{{- if $registry -}} +{{- printf "%s/%s:%s" $registry $repository $tag -}} +{{- else -}} +{{- printf "%s:%s" $repository $tag -}} +{{- end -}} +{{- end -}} + +{{/* +Merged image pull secrets (global.imagePullSecrets + image.pullSecrets), deduped. +Emits the whole `imagePullSecrets:` block, or nothing when empty. Accepts both +["name"] and [{name: "..."}] element shapes. +Usage: {{- include "devzero-common.imagePullSecrets" . | nindent 6 }} +*/}} +{{- define "devzero-common.imagePullSecrets" -}} +{{- $g := .Values.global | default dict -}} +{{- $img := .Values.image | default dict -}} +{{- /* Normalize both accepted element shapes down to plain names BEFORE + deduping. Deduping the raw list would miss the common case where the same + secret arrives as "foo" from one source and {name: foo} from the other — + uniq compares whole elements, so those two never match. */ -}} +{{- $names := list -}} +{{- range concat ($g.imagePullSecrets | default list) ($img.pullSecrets | default list) -}} +{{- if kindIs "map" . -}} +{{- /* Not `default`: it would drop a name that is 0 or false. */ -}} +{{- if not (kindIs "invalid" .name) -}} +{{- $names = append $names (toString .name) -}} +{{- end -}} +{{- else if not (kindIs "invalid" .) -}} +{{- $names = append $names (toString .) -}} +{{- end -}} +{{- end -}} +{{- /* Names are strings before deduping (--set x[0].name=123 arrives as an + int), and are emitted through toYaml, which quotes a name YAML would + otherwise read as another type (yes, on, null, true, 123) and leaves + every other name bare. */ -}} +{{- with ($names | compact | uniq) -}} +imagePullSecrets: +{{- range . }} + - name: {{ toYaml . }} +{{- end }} +{{- end -}} +{{- end -}} + +{{/* +A full image reference with its registry host swapped for global.imageRegistry, +for charts that build references from many keys instead of one `image` object. +With global.imageRegistry empty the reference is returned unchanged, so an +install that does not set it renders exactly as before. + +When set, the result is "/[:tag][@digest]", where + is the reference without its registry host. That is the layout +`crane copy`, `skopeo sync` and `oras copy` produce when copying a list of +images into one registry, so a mirror needs no rename table. Parsing follows +the Docker reference grammar (distribution/reference): + + 1. Split off the digest (@...), then the tag: the last ":" after the last "/". + 2. Only a name containing "/" can carry a host, and its first component is + the host when it contains "." or ":", is "localhost", or has an + uppercase letter (path components must be lowercase). So the ":" in + alpine:3.19 is a tag, and localhost:5000/x has the host localhost:5000. + 3. A Docker Hub name with a single component gets "library/" (alpine:3.19 + and docker.io/alpine:3.19 become /library/alpine:3.19), as + Docker Hub stores it. + +Tags and digests pass through unchanged, so digest pins stay verifiable. +global.imageRegistry may carry a path prefix (harbor.corp/devzero); a trailing +"/" is ignored. An empty reference stays empty. + +Usage: image: {{ include "devzero-common.imageRef" (dict "ref" $ref "global" .Values.global) | quote }} +*/}} +{{- define "devzero-common.imageRef" -}} +{{- $ref := .ref | default "" | toString -}} +{{- $g := .global | default dict -}} +{{- $registry := $g.imageRegistry | default "" | toString | trimSuffix "/" -}} +{{- if or (not $registry) (not $ref) -}} +{{- $ref -}} +{{- else -}} +{{- $name := $ref -}} +{{- $digest := "" -}} +{{- if contains "@" $name -}} +{{- $parts := splitn "@" 2 $name -}} +{{- $name = $parts._0 -}} +{{- $digest = printf "@%s" $parts._1 -}} +{{- end -}} +{{- $tag := regexFind ":[^:/]+$" $name -}} +{{- $name = trimSuffix $tag $name -}} +{{- $path := $name -}} +{{- $dockerHub := true -}} +{{- if contains "/" $name -}} +{{- $parts := splitn "/" 2 $name -}} +{{- $first := $parts._0 -}} +{{- if or (contains "." $first) (contains ":" $first) (eq $first "localhost") (ne (lower $first) $first) -}} +{{- $path = $parts._1 -}} +{{- $dockerHub = has $first (list "docker.io" "index.docker.io" "registry-1.docker.io") -}} +{{- end -}} +{{- end -}} +{{- if and $dockerHub (not (contains "/" $path)) -}} +{{- $path = printf "library/%s" $path -}} +{{- end -}} +{{- printf "%s/%s%s%s" $registry $path $tag $digest -}} +{{- end -}} +{{- end -}} diff --git a/helm-chart/devzero-common/templates/_security.tpl b/helm-chart/devzero-common/templates/_security.tpl new file mode 100644 index 00000000..9f434005 --- /dev/null +++ b/helm-chart/devzero-common/templates/_security.tpl @@ -0,0 +1,44 @@ +{{/* +cert-manager Certificate, emitted only when the umbrella sets +global.tls.certManagerIssuerRef. The issued secret is named +-tls and can be mounted by the consumer. Put in the consumer's +templates/certificate.yaml as the whole body: {{ include "devzero-common.tls.certificate" . }} +*/}} +{{- define "devzero-common.tls.certificate" -}} +{{- $tls := (.Values.global | default dict).tls | default dict -}} +{{- with $tls.certManagerIssuerRef }} +apiVersion: cert-manager.io/v1 +kind: Certificate +metadata: + name: {{ include "devzero-common.fullname" $ }}-tls + labels: + {{- include "devzero-common.labels" $ | nindent 4 }} +spec: + secretName: {{ include "devzero-common.fullname" $ }}-tls + issuerRef: + {{- toYaml . | nindent 4 }} + dnsNames: + - {{ include "devzero-common.fullname" $ }} + - {{ include "devzero-common.fullname" $ }}.{{ $.Release.Namespace }}.svc +{{- end }} +{{- end -}} + +{{/* +Secret name resolver: the caller's existing secret when secret.existingSecret is +set, otherwise the chart's own . Lets a consumer BYO-secret or create +one inline without duplicating the name logic. +Usage: secretKeyRef: { name: {{ include "devzero-common.secretName" . }}, key: ... } +*/}} +{{- define "devzero-common.secretName" -}} +{{- $s := .Values.secret | default dict -}} +{{- $s.existingSecret | default (include "devzero-common.fullname" .) -}} +{{- end -}} + +{{/* +True (string "true") when this chart should create the inline Secret itself — +i.e. no existingSecret was provided. Usage: {{- if eq (include "devzero-common.createSecret" .) "true" }} +*/}} +{{- define "devzero-common.createSecret" -}} +{{- $s := .Values.secret | default dict -}} +{{- if $s.existingSecret -}}false{{- else -}}true{{- end -}} +{{- end -}} diff --git a/helm-chart/devzero-common/templates/_serviceaccount.tpl b/helm-chart/devzero-common/templates/_serviceaccount.tpl new file mode 100644 index 00000000..2a461baa --- /dev/null +++ b/helm-chart/devzero-common/templates/_serviceaccount.tpl @@ -0,0 +1,42 @@ +{{/* +Service account name: the created SA's name when serviceAccount.create, else the +referenced name (default "default"). Usage: + serviceAccountName: {{ include "devzero-common.serviceAccount.name" . }} +*/}} +{{- define "devzero-common.serviceAccount.name" -}} +{{- $sa := .Values.serviceAccount | default dict -}} +{{- if $sa.create -}} +{{- $sa.name | default (include "devzero-common.fullname" .) -}} +{{- else -}} +{{- $sa.name | default "default" -}} +{{- end -}} +{{- end -}} + +{{/* +Full ServiceAccount object, emitted only when serviceAccount.create is true. +Put this in the consumer's templates/serviceaccount.yaml as the whole body: + {{ include "devzero-common.serviceAccount" . }} +*/}} +{{- define "devzero-common.serviceAccount" -}} +{{- $sa := .Values.serviceAccount | default dict -}} +{{- if $sa.create -}} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ include "devzero-common.serviceAccount.name" . }} + labels: + {{- include "devzero-common.labels" . | nindent 4 }} + {{- with $sa.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +{{- /* hasKey, not `default true`: sprig's default treats boolean false as empty, + so `serviceAccount.automount: false` — the security-hardening case — would + be silently flipped back to true and be impossible to turn off. */}} +{{- if hasKey $sa "automount" }} +automountServiceAccountToken: {{ $sa.automount }} +{{- else }} +automountServiceAccountToken: true +{{- end }} +{{- end -}} +{{- end -}} diff --git a/helm-chart/devzero-common/templates/_workload.tpl b/helm-chart/devzero-common/templates/_workload.tpl new file mode 100644 index 00000000..7bddb860 --- /dev/null +++ b/helm-chart/devzero-common/templates/_workload.tpl @@ -0,0 +1,30 @@ +{{/* +Container resources: .Values.resources verbatim, or a conservative default when +unset. Usage: resources: {{- include "devzero-common.resources" . | nindent 12 }} +*/}} +{{- define "devzero-common.resources" -}} +{{- if .Values.resources -}} +{{- toYaml .Values.resources -}} +{{- else -}} +requests: + cpu: 100m + memory: 128Mi +limits: + memory: 256Mi +{{- end -}} +{{- end -}} + +{{/* +OTLP telemetry env vars, injected from the umbrella's global.telemetry.otlp +({enabled, endpoint}). Emits nothing when disabled/unset. Placed under a +container's env:. Usage: env: {{- include "devzero-common.telemetry" . | nindent 12 }} +*/}} +{{- define "devzero-common.telemetry" -}} +{{- $otlp := ((.Values.global | default dict).telemetry | default dict).otlp | default dict -}} +{{- if $otlp.enabled -}} +- name: OTEL_EXPORTER_OTLP_ENDPOINT + value: {{ required "global.telemetry.otlp.endpoint is required when otlp.enabled" $otlp.endpoint | quote }} +- name: OTEL_SERVICE_NAME + value: {{ include "devzero-common.name" . | quote }} +{{- end -}} +{{- end -}} diff --git a/helm-chart/internal/charttest/charttest.go b/helm-chart/internal/charttest/charttest.go new file mode 100644 index 00000000..fc322dfc --- /dev/null +++ b/helm-chart/internal/charttest/charttest.go @@ -0,0 +1,661 @@ +// Package charttest holds the shared assertions for the site-metadata contract +// (commonLabels, commonAnnotations, podLabels, podAnnotations) that every chart +// in this directory implements. +// +// Clusters running an admission policy engine (OPA Gatekeeper, Kyverno) commonly +// require site-specific labels on every object before they will admit it. A +// chart with no way to set them cannot be installed there at all, so these +// assertions are what keep the knobs working as templates change. +// +// The assertions sweep every rendered object rather than a hand-listed sample. +// That matters more than it sounds: the equivalent contract was first written +// with a hand-maintained list of chart-owned keys, and the list was wrong twice. +// Anything derived from a render cannot drift from the templates. +package charttest + +import ( + "bytes" + "encoding/json" + "errors" + "fmt" + "io" + "os" + "os/exec" + "path/filepath" + "sort" + "strings" + "sync" + "testing" + "time" + + yamlv3 "gopkg.in/yaml.v3" + "sigs.k8s.io/yaml" +) + +// Spec describes one chart under test. +type Spec struct { + // ChartDir is the chart directory, relative to the test file. + ChartDir string + // BaseArgs are the --set flags this chart needs in order to render at all. + BaseArgs []string + // SubchartPrefix, when set, is a rendered-source prefix that must appear in + // the output, proving the parent's global values reached that subchart. + SubchartPrefix string + // Variants are additional configurations the whole contract is re-run + // against, named for the subtest. + // + // A chart that renders a metadata block only under some condition satisfies + // the contract in the default render and breaks it elsewhere — the nodemon + // DaemonSet put its annotations inside `if $v.withDcgm`, so turning DCGM off + // dropped commonAnnotations entirely while the default render stayed green. + Variants map[string][]string +} + +// withArgs returns a copy of the spec carrying extra base arguments. +func (s Spec) withArgs(extra []string) Spec { + out := s + out.BaseArgs = append(append([]string{}, s.BaseArgs...), extra...) + out.Variants = nil + return out +} + +// dependencyBuild runs once per chart directory. A chart with a file:// subchart +// dependency cannot render until charts/ is populated, and charts/*.tgz is +// gitignored, so a clean checkout has none. Doing it here keeps `go test +// ./helm-chart/...` working without a separate setup step. +var dependencyBuild sync.Map + +func ensureDependencies(t *testing.T, chartDir string) { + t.Helper() + + if _, loaded := dependencyBuild.LoadOrStore(chartDir, true); loaded { + return + } + if _, err := os.Stat(filepath.Join(chartDir, "Chart.lock")); err != nil { + return // no dependencies to build + } + + // go test runs each chart's package in its own process, and building + // zxporter packages ../zxporter-nodemon, charts/ included, while the + // nodemon package may be rewriting that directory. One build at a time. + defer lockDependencyBuilds(t)() + + cmd := exec.Command("helm", "dependency", "build", chartDir) + if out, err := cmd.CombinedOutput(); err != nil { + t.Fatalf("helm dependency build failed for %s: %v\n%s", chartDir, err, out) + } +} + +// lockDependencyBuilds takes a lock shared by every test process on the host +// and returns its release. A directory is the lock because creating one is +// atomic on every platform; one older than a build could take is left over +// from a killed run and is cleared. +func lockDependencyBuilds(t *testing.T) func() { + t.Helper() + + lock := filepath.Join(os.TempDir(), "zxporter-charttest-deps.lock") + deadline := time.Now().Add(5 * time.Minute) + for { + if err := os.Mkdir(lock, 0o700); err == nil { + return func() { _ = os.Remove(lock) } + } else if !errors.Is(err, os.ErrExist) { + t.Fatalf("creating %s: %v", lock, err) + } + if info, err := os.Stat(lock); err == nil && time.Since(info.ModTime()) > 2*time.Minute { + _ = os.Remove(lock) + continue + } + if time.Now().After(deadline) { + t.Fatalf("timed out waiting for %s; remove it if no test is running", lock) + } + time.Sleep(50 * time.Millisecond) + } +} + +// Set turns key=value pairs into helm --set flags. +func Set(pairs ...string) []string { + args := make([]string, 0, 2*len(pairs)) + for _, p := range pairs { + args = append(args, "--set", p) + } + return args +} + +// Render runs `helm template` and returns the rendered manifests. +func Render(t *testing.T, spec Spec, extraArgs ...string) string { + t.Helper() + + out, err := TryRender(t, spec, extraArgs...) + if err != nil { + t.Fatalf("helm template failed: %v", err) + } + return out +} + +// TryRender runs `helm template` and returns its error, carrying helm's stderr, +// instead of failing the test, for cases where the render is expected to fail. +func TryRender(t *testing.T, spec Spec, extraArgs ...string) (string, error) { + t.Helper() + + if _, err := exec.LookPath("helm"); err != nil { + t.Skip("helm binary not available, skipping chart render test") + } + + chartDir, err := filepath.Abs(spec.ChartDir) + if err != nil { + t.Fatalf("resolving chart dir: %v", err) + } + ensureDependencies(t, chartDir) + + args := append([]string{"template", "release", chartDir}, spec.BaseArgs...) + args = append(args, extraArgs...) + + var stdout, stderr bytes.Buffer + cmd := exec.Command("helm", args...) + cmd.Stdout = &stdout + cmd.Stderr = &stderr + if err := cmd.Run(); err != nil { + return "", fmt.Errorf("%w\n%s", err, stderr.String()) + } + return stdout.String(), nil +} + +// Object is one rendered document plus the chart path Helm attributed it to. +type Object struct { + // Source is the `# Source:` path Helm emits above each document. + Source string + Doc map[string]any +} + +// FromSubchart reports whether Helm rendered this object from a subchart. +// +// Helm values are chart-scoped: a chart-local commonLabels applies to the chart +// it is set on and does not cross into a subchart, which is what global.* is +// for. Assertions about a chart-local value therefore have to skip these, while +// the global assertion covers every object. +func (o Object) FromSubchart() bool { + return strings.Contains(o.Source, "/charts/") +} + +// Objects decodes every document in a rendered manifest stream. +func Objects(t *testing.T, manifests string) []Object { + t.Helper() + + docs := strings.Split(manifests, "\n---\n") + objects := make([]Object, 0, len(docs)) + for _, doc := range docs { + if strings.TrimSpace(doc) == "" { + continue + } + + var obj map[string]any + if err := yaml.Unmarshal([]byte(doc), &obj); err != nil { + t.Fatalf("unmarshalling rendered manifest: %v\n%s", err, doc) + } + if obj == nil || obj["kind"] == nil { + continue + } + + source := "" + for _, line := range strings.Split(doc, "\n") { + if rest, found := strings.CutPrefix(strings.TrimSpace(line), "# Source:"); found { + source = strings.TrimSpace(rest) + break + } + } + objects = append(objects, Object{Source: source, Doc: obj}) + } + + if len(objects) == 0 { + t.Fatal("rendered no objects") + } + return objects +} + +// Describe identifies an object in assertion failures. +func Describe(obj map[string]any) string { + kind, _ := obj["kind"].(string) + meta, _ := obj["metadata"].(map[string]any) + name, _ := meta["name"].(string) + return kind + "/" + name +} + +// StringMap reads a nested map[string]string, e.g. metadata.labels. +func StringMap(obj map[string]any, path ...string) map[string]string { + cur := obj + for _, key := range path[:len(path)-1] { + next, ok := cur[key].(map[string]any) + if !ok { + return nil + } + cur = next + } + + raw, ok := cur[path[len(path)-1]].(map[string]any) + if !ok { + return nil + } + out := map[string]string{} + for k, v := range raw { + s, _ := v.(string) + out[k] = s + } + return out +} + +// PodTemplates returns the spec.template of every workload object. When +// ownChartOnly is set, subchart objects are skipped. +func PodTemplates(objects []Object, ownChartOnly bool) map[string]map[string]any { + templates := map[string]map[string]any{} + for _, obj := range objects { + if ownChartOnly && obj.FromSubchart() { + continue + } + spec, ok := obj.Doc["spec"].(map[string]any) + if !ok { + continue + } + template, ok := spec["template"].(map[string]any) + if !ok { + continue + } + if _, ok := template["spec"].(map[string]any); !ok { + continue + } + templates[Describe(obj.Doc)] = template + } + return templates +} + +// SetJSON builds a --set-json argument pair. +func SetJSON(key, jsonValue string) []string { + return []string{"--set-json", key + "=" + jsonValue} +} + +// DuplicateKeys reports every mapping in the stream that defines the same key +// twice. sigs.k8s.io/yaml routes through JSON and silently keeps the last value, +// so a duplicate is invisible to the other assertions here — but the API server +// rejects it under strict decoding, which is how a user would find out. +func DuplicateKeys(t *testing.T, manifests string) []string { + t.Helper() + + var found []string + var walk func(node *yamlv3.Node, path string) + walk = func(node *yamlv3.Node, path string) { + if node.Kind == yamlv3.MappingNode { + seen := map[string]bool{} + for i := 0; i+1 < len(node.Content); i += 2 { + key := node.Content[i].Value + if seen[key] { + found = append(found, fmt.Sprintf("%s.%s (line %d)", path, key, node.Content[i].Line)) + } + seen[key] = true + walk(node.Content[i+1], path+"."+key) + } + return + } + for _, child := range node.Content { + walk(child, path) + } + } + + decoder := yamlv3.NewDecoder(strings.NewReader(manifests)) + for { + var doc yamlv3.Node + err := decoder.Decode(&doc) + if errors.Is(err, io.EOF) { + break + } + if err != nil { + t.Fatalf("decoding rendered manifests: %v", err) + } + walk(&doc, "") + } + return found +} + +// emittedKeys harvests every metadata key the chart emits on its own, from a +// render with the knobs unset. +func emittedKeys(t *testing.T, spec Spec) (labels, annotations []string) { + t.Helper() + + labelSet := map[string]bool{} + annotationSet := map[string]bool{} + collect := func(meta map[string]any) { + for key := range StringMap(meta, "metadata", "labels") { + labelSet[key] = true + } + for key := range StringMap(meta, "metadata", "annotations") { + annotationSet[key] = true + } + } + + objects := Objects(t, Render(t, spec)) + for _, obj := range objects { + collect(obj.Doc) + } + for _, template := range PodTemplates(objects, false) { + collect(template) + } + + for key := range labelSet { + labels = append(labels, key) + } + for key := range annotationSet { + annotations = append(annotations, key) + } + sort.Strings(labels) + sort.Strings(annotations) + return labels, annotations +} + +func hostileMap(keys []string) string { + pairs := make([]string, 0, len(keys)) + for _, key := range keys { + pairs = append(pairs, fmt.Sprintf("%q:%q", key, "hijacked")) + } + return "{" + strings.Join(pairs, ",") + "}" +} + +// selectors returns every selector in the render, keyed by object. Selectors on +// Deployments, DaemonSets and StatefulSets are immutable after creation, so +// changing one turns `helm upgrade` into a manual delete-and-recreate for every +// existing installation. +func selectors(t *testing.T, objects []Object) map[string]string { + t.Helper() + + found := map[string]string{} + for _, obj := range objects { + spec, ok := obj.Doc["spec"].(map[string]any) + if !ok { + continue + } + selector, ok := spec["selector"] + if !ok { + continue + } + encoded, err := json.Marshal(selector) + if err != nil { + t.Fatalf("marshalling selector for %s: %v", Describe(obj.Doc), err) + } + found[Describe(obj.Doc)] = string(encoded) + } + return found +} + +// RunContract asserts the full site-metadata contract for one chart, and for +// each of its Variants. +func RunContract(t *testing.T, spec Spec) { + t.Helper() + + runContract(t, spec) + + for name, args := range spec.Variants { + t.Run(name, func(t *testing.T) { runContract(t, spec.withArgs(args)) }) + } +} + +func runContract(t *testing.T, spec Spec) { + t.Helper() + + for _, assertion := range []struct { + name string + run func(*testing.T, Spec) + }{ + {"common labels reach every object", assertCommonLabelsReachEveryObject}, + {"common annotations reach every object", assertCommonAnnotationsReachEveryObject}, + {"common metadata reaches every pod template", assertCommonMetadataReachesEveryPodTemplate}, + {"pod metadata reaches every pod template", assertPodMetadataReachesEveryPodTemplate}, + {"global common labels reach every object", assertGlobalCommonLabelsReachEveryObject}, + {"no chart-emitted key can produce a duplicate", assertNoChartemittedKeyCanProduceADuplicate}, + {"selectors are unchanged", assertSelectorsAreUnchanged}, + {"no empty metadata blocks when unset", assertNoEmptyMetadataBlocksWhenUnset}, + } { + t.Run(assertion.name, func(t *testing.T) { assertion.run(t, spec) }) + } +} + +func assertCommonLabelsReachEveryObject(t *testing.T, spec Spec) { + t.Helper() + + objects := Objects(t, Render(t, spec, + SetJSON("commonLabels", `{"example.com/cost-center":"platform","example.com/tier":"infra"}`)...)) + + for _, obj := range objects { + labels := StringMap(obj.Doc, "metadata", "labels") + for key, want := range map[string]string{ + "example.com/cost-center": "platform", + "example.com/tier": "infra", + } { + // A chart-local value must reach this chart's own objects and must + // NOT cross into a subchart — asserting only the first half would + // let a regression that forwards it everywhere pass unnoticed. + if obj.FromSubchart() { + if got, found := labels[key]; found { + t.Errorf("%s: chart-local commonLabels[%q] = %q leaked into a subchart", Describe(obj.Doc), key, got) + } + continue + } + if got := labels[key]; got != want { + t.Errorf("%s: metadata.labels[%q] = %q, want %q", Describe(obj.Doc), key, got, want) + } + } + } +} + +func assertCommonAnnotationsReachEveryObject(t *testing.T, spec Spec) { + t.Helper() + + objects := Objects(t, Render(t, spec, + SetJSON("commonAnnotations", `{"example.com/owner":"platform-team"}`)...)) + + for _, obj := range objects { + if obj.FromSubchart() { + continue // chart-local values do not cross into a subchart + } + if got := StringMap(obj.Doc, "metadata", "annotations")["example.com/owner"]; got != "platform-team" { + t.Errorf("%s: metadata.annotations[example.com/owner] = %q, want platform-team", Describe(obj.Doc), got) + } + } +} + +// Gatekeeper's expand-workloads evaluates constraints against the Pod a +// workload would create, so an object whose own metadata satisfies a policy +// can still be denied on its pod template. +func assertCommonMetadataReachesEveryPodTemplate(t *testing.T, spec Spec) { + t.Helper() + + args := append( + SetJSON("commonLabels", `{"example.com/tier":"infra"}`), + SetJSON("commonAnnotations", `{"example.com/owner":"platform-team"}`)..., + ) + templates := PodTemplates(Objects(t, Render(t, spec, args...)), true) + if len(templates) == 0 { + t.Fatal("rendered no pod templates") + } + + for name, template := range templates { + if got := StringMap(template, "metadata", "labels")["example.com/tier"]; got != "infra" { + t.Errorf("%s: pod template labels[example.com/tier] = %q, want infra", name, got) + } + if got := StringMap(template, "metadata", "annotations")["example.com/owner"]; got != "platform-team" { + t.Errorf("%s: pod template annotations[example.com/owner] = %q, want platform-team", name, got) + } + } +} + +func assertPodMetadataReachesEveryPodTemplate(t *testing.T, spec Spec) { + t.Helper() + + args := append( + SetJSON("podLabels", `{"example.com/workload":"zxporter"}`), + SetJSON("podAnnotations", `{"example.com/scrape":"false"}`)..., + ) + objects := Objects(t, Render(t, spec, args...)) + + templates := PodTemplates(objects, true) + if len(templates) == 0 { + t.Fatal("rendered no pod templates") + } + for name, template := range templates { + if got := StringMap(template, "metadata", "labels")["example.com/workload"]; got != "zxporter" { + t.Errorf("%s: pod template labels[example.com/workload] = %q, want zxporter", name, got) + } + if got := StringMap(template, "metadata", "annotations")["example.com/scrape"]; got != "false" { + t.Errorf("%s: pod template annotations[example.com/scrape] = %q, want false", name, got) + } + } + + // podLabels is pod-scoped; leaking it onto object metadata would be + // wrong for the knob's contract. + for _, obj := range objects { + if _, found := StringMap(obj.Doc, "metadata", "labels")["example.com/workload"]; found { + t.Errorf("%s: podLabels leaked onto metadata.labels", Describe(obj.Doc)) + } + } +} + +// global.* is the knob that reaches a subchart; a chart-local commonLabels +// on the parent does not. +func assertGlobalCommonLabelsReachEveryObject(t *testing.T, spec Spec) { + t.Helper() + + objects := Objects(t, Render(t, spec, + SetJSON("global.commonLabels", `{"example.com/tier":"infra"}`)...)) + + // Match the named prefix, not merely "some subchart rendered": a typo in + // SubchartPrefix would otherwise still satisfy the guard below and leave the + // propagation claim unproven for the subchart it names. + sawNamedSubchart := false + for _, obj := range objects { + if spec.SubchartPrefix != "" && strings.HasPrefix(obj.Source, spec.SubchartPrefix) { + sawNamedSubchart = true + } + if got := StringMap(obj.Doc, "metadata", "labels")["example.com/tier"]; got != "infra" { + t.Errorf("%s: metadata.labels[example.com/tier] = %q, want infra", Describe(obj.Doc), got) + } + } + if spec.SubchartPrefix != "" && !sawNamedSubchart { + t.Errorf("no rendered object came from %q; without one this proves nothing about propagation", + spec.SubchartPrefix) + } +} + +// The reserved key lists in the chart helpers are hand-maintained, so this +// derives the real set from a render instead of restating it: a template that +// starts emitting a new label fails here until that key is dropped, rather +// than shipping a chart that renders invalid YAML for anyone who sets it. +func assertNoChartemittedKeyCanProduceADuplicate(t *testing.T, spec Spec) { + t.Helper() + + labels, annotations := emittedKeys(t, spec) + if len(labels) == 0 { + t.Fatal("harvested no chart-emitted label keys") + } + + var args []string + args = append(args, SetJSON("commonLabels", hostileMap(labels))...) + args = append(args, SetJSON("podLabels", hostileMap(labels))...) + args = append(args, SetJSON("global.commonLabels", hostileMap(labels))...) + if len(annotations) > 0 { + args = append(args, SetJSON("commonAnnotations", hostileMap(annotations))...) + args = append(args, SetJSON("podAnnotations", hostileMap(annotations))...) + } + + for _, dup := range DuplicateKeys(t, Render(t, spec, args...)) { + t.Errorf("duplicate YAML key in rendered output: %s", dup) + } +} + +func assertSelectorsAreUnchanged(t *testing.T, spec Spec) { + t.Helper() + + baseline := selectors(t, Objects(t, Render(t, spec))) + if len(baseline) == 0 { + t.Fatal("rendered no selectors") + } + + args := append( + SetJSON("commonLabels", `{"example.com/tier":"infra"}`), + SetJSON("podLabels", `{"example.com/workload":"zxporter"}`)..., + ) + withLabels := selectors(t, Objects(t, Render(t, spec, args...))) + + for name, want := range baseline { + got, ok := withLabels[name] + if !ok { + t.Errorf("%s: selector disappeared when commonLabels was set", name) + continue + } + if got != want { + t.Errorf("%s: selector changed when commonLabels was set:\n got %s\nwant %s", name, got, want) + } + } +} + +// With the knobs unset the chart must render exactly as it did before they +// existed, so adopting them is a no-op upgrade for every existing release. +func assertNoEmptyMetadataBlocksWhenUnset(t *testing.T, spec Spec) { + t.Helper() + + for _, obj := range Objects(t, Render(t, spec)) { + meta, _ := obj.Doc["metadata"].(map[string]any) + if labels, ok := meta["labels"].(map[string]any); ok && len(labels) == 0 { + t.Errorf("%s: rendered an empty metadata.labels block", Describe(obj.Doc)) + } + if annotations, ok := meta["annotations"].(map[string]any); ok && len(annotations) == 0 { + t.Errorf("%s: rendered an empty metadata.annotations block", Describe(obj.Doc)) + } + } +} + +// Platform labels every DevZero chart takes from devzero-common. +const ( + PartOfLabel = "app.kubernetes.io/part-of" + ComponentLabel = "devzero.io/component" + PartOf = "dz-platform" +) + +// component is the chart an object was rendered from: the directory before +// templates/ in its Source, so a subchart object is attributed to the subchart. +func (o Object) component() string { + before, _, found := strings.Cut(o.Source, "/templates/") + if !found { + return "" + } + return filepath.Base(before) +} + +// AssertPlatformLabels checks that every rendered object carries +// devzero-common's platform labels, naming the chart it was rendered from, and +// that no pod template does: pod labels come from podLabels and the selector +// labels only, so adopting the library restarts no pods by itself. +func AssertPlatformLabels(t *testing.T, spec Spec, extraArgs ...string) { + t.Helper() + + objects := Objects(t, Render(t, spec, extraArgs...)) + for _, obj := range objects { + labels := StringMap(obj.Doc, "metadata", "labels") + if got := labels[PartOfLabel]; got != PartOf { + t.Errorf("%s: %s = %q, want %q", Describe(obj.Doc), PartOfLabel, got, PartOf) + } + if got, want := labels[ComponentLabel], obj.component(); got != want { + t.Errorf("%s: %s = %q, want %q", Describe(obj.Doc), ComponentLabel, got, want) + } + } + for name, template := range PodTemplates(objects, false) { + labels := StringMap(template, "metadata", "labels") + for _, key := range []string{PartOfLabel, ComponentLabel} { + if _, ok := labels[key]; ok { + t.Errorf("%s pod template carries %s", name, key) + } + } + } +} + +// Selectors returns every object's spec.selector as JSON, keyed by kind/name. +// Workload selectors are immutable, so tests pin them exactly. +func Selectors(t *testing.T, objects []Object) map[string]string { + t.Helper() + return selectors(t, objects) +} diff --git a/helm-chart/internal/charttest/images.go b/helm-chart/internal/charttest/images.go new file mode 100644 index 00000000..477d40ac --- /dev/null +++ b/helm-chart/internal/charttest/images.go @@ -0,0 +1,161 @@ +package charttest + +import ( + "reflect" + "slices" + "strings" + "testing" +) + +// The image-registry contract the dz-platform umbrella relies on: with +// global.imageRegistry set, every image a chart renders comes from that +// registry, and with global.imagePullSecrets set, every pod carries those +// secrets. Like the metadata contract it sweeps every rendered pod template +// rather than a hand-listed one, so a new workload that skips the helpers fails +// here instead of silently pulling from a public registry. + +// ImageGlobals describes how a chart takes part in the contract. +type ImageGlobals struct { + // PullSecretsKey is the chart's own pull-secrets value key, merged with + // global.imagePullSecrets (for example "imagePullSecrets"). + PullSecretsKey string + // OwnChartOnly skips subchart objects, for a parent whose bundled + // subchart is covered by its own package. + OwnChartOnly bool + // Variants are extra configurations, each rendering pod templates or + // containers the default does not. + Variants map[string][]string +} + +// mirror is the registry the contract renders with. The path prefix proves +// the helper appends to the value rather than treating it as a bare host. +const mirror = "mirror.example/devzero" + +// AssertImageGlobals checks the contract for the chart's default render and +// each variant. +func AssertImageGlobals(t *testing.T, spec Spec, g ImageGlobals) { + t.Helper() + variants := map[string][]string{"default": nil} + for name, args := range g.Variants { + variants[name] = args + } + for name, args := range variants { + t.Run(name, func(t *testing.T) { + assertImageGlobals(t, spec, g, args) + }) + } +} + +func assertImageGlobals(t *testing.T, spec Spec, g ImageGlobals, args []string) { + t.Helper() + render := func(extra ...string) map[string]map[string]any { + all := append(append([]string{}, args...), extra...) + return PodTemplates(Objects(t, Render(t, spec, all...)), g.OwnChartOnly) + } + + defaults := render() + if len(defaults) == 0 { + t.Fatal("rendered no pod templates") + } + for name, tmpl := range defaults { + if got := PullSecretNames(tmpl); len(got) != 0 { + t.Errorf("%s: imagePullSecrets %v with no pull secrets set", name, got) + } + } + + mirrored := render("--set", "global.imageRegistry="+mirror) + for name, tmpl := range defaults { + want := Images(tmpl) + for i := range want { + want[i] = RewriteImage(mirror, want[i]) + } + if got := Images(mirrored[name]); !reflect.DeepEqual(got, want) { + t.Errorf("%s: images with global.imageRegistry=%s\n got %v\nwant %v", name, mirror, got, want) + } + } + + withSecrets := render( + "--set", "global.imagePullSecrets[0]=from-global", + "--set", "global.imagePullSecrets[1].name=both", + "--set", g.PullSecretsKey+"[0]=both", + "--set", g.PullSecretsKey+"[1].name=own", + ) + want := []string{"from-global", "both", "own"} + for name := range defaults { + if got := PullSecretNames(withSecrets[name]); !reflect.DeepEqual(got, want) { + t.Errorf("%s: imagePullSecrets = %v, want %v (global first, then %s, deduplicated)", name, got, want, g.PullSecretsKey) + } + } + + // Valid Secret names that YAML reads as a number or a bool must still + // render as strings, or the API server rejects the pod. + scalarLooking := render( + "--set", "global.imagePullSecrets[0]=123", + "--set-string", g.PullSecretsKey+"[0].name=true", + ) + want = []string{"123", "true"} + for name := range defaults { + if got := PullSecretNames(scalarLooking[name]); !reflect.DeepEqual(got, want) { + t.Errorf("%s: imagePullSecrets = %v, want %v (scalar-looking names kept as strings)", name, got, want) + } + } +} + +// Images lists a pod template's init container and container images, in order. +func Images(template map[string]any) []string { + spec, _ := template["spec"].(map[string]any) + var out []string + for _, key := range []string{"initContainers", "containers"} { + containers, _ := spec[key].([]any) + for _, c := range containers { + container, _ := c.(map[string]any) + image, _ := container["image"].(string) + out = append(out, image) + } + } + return out +} + +// PullSecretNames lists the names in a pod template's imagePullSecrets. An +// entry without a name (the bare-string shape the API rejects) is reported +// as "". +func PullSecretNames(template map[string]any) []string { + spec, _ := template["spec"].(map[string]any) + secrets, _ := spec["imagePullSecrets"].([]any) + out := make([]string, 0, len(secrets)) + for _, s := range secrets { + entry, _ := s.(map[string]any) + name, _ := entry["name"].(string) + if name == "" { + name = "" + } + out = append(out, name) + } + return out +} + +// RewriteImage is the reference rule devzero-common.imageRef implements, +// restated independently so the test does not trust the template it checks: +// keep the path, tag and digest, replace the registry host with registry, and +// spell out Docker Hub's implicit library/ namespace. A first path component +// is a host when it has a ".", a ":" or an uppercase letter, or is localhost. +func RewriteImage(registry, ref string) string { + name, digest, _ := strings.Cut(ref, "@") + if digest != "" { + digest = "@" + digest + } + tag := "" + if i := strings.LastIndex(name, ":"); i > strings.LastIndex(name, "/") { + name, tag = name[:i], name[i:] + } + path, dockerHub := name, true + if first, rest, found := strings.Cut(name, "/"); found && + (strings.ContainsAny(first, ".:") || first == "localhost" || strings.ToLower(first) != first) { + path = rest + dockerHub = slices.Contains([]string{"docker.io", "index.docker.io", "registry-1.docker.io"}, first) + } + if dockerHub && !strings.Contains(path, "/") { + path = "library/" + path + } + return strings.TrimSuffix(registry, "/") + "/" + path + tag + digest +} diff --git a/helm-chart/internal/charttest/images_test.go b/helm-chart/internal/charttest/images_test.go new file mode 100644 index 00000000..1ac34816 --- /dev/null +++ b/helm-chart/internal/charttest/images_test.go @@ -0,0 +1,21 @@ +package charttest + +import "testing" + +func TestRewriteImage(t *testing.T) { + for _, tc := range []struct{ ref, want string }{ + {"public.ecr.aws/devzeroinc/zxporter:v1", "m.example/p/devzeroinc/zxporter:v1"}, + {"devzeroinc/zxporter-netmon:v1", "m.example/p/devzeroinc/zxporter-netmon:v1"}, + {"docker.io/devzeroinc/criu:4.2.1", "m.example/p/devzeroinc/criu:4.2.1"}, + {"alpine:3.19", "m.example/p/library/alpine:3.19"}, + {"docker.io/alpine", "m.example/p/library/alpine"}, + {"localhost:5000/x", "m.example/p/x"}, + {"MYREGISTRY/team/app", "m.example/p/team/app"}, + {"ghcr.io/a/b@sha256:abc", "m.example/p/a/b@sha256:abc"}, + {"ghcr.io/a/b:v1@sha256:abc", "m.example/p/a/b:v1@sha256:abc"}, + } { + if got := RewriteImage("m.example/p/", tc.ref); got != tc.want { + t.Errorf("RewriteImage(%q) = %q, want %q", tc.ref, got, tc.want) + } + } +} diff --git a/helm-chart/zxporter-netmon/.helmignore b/helm-chart/zxporter-netmon/.helmignore new file mode 100644 index 00000000..e372ec71 --- /dev/null +++ b/helm-chart/zxporter-netmon/.helmignore @@ -0,0 +1,14 @@ +# Patterns to ignore when building packages. +.DS_Store +.git/ +.gitignore +*.swp +*.bak +*.tmp +*.orig +*~ +.idea/ +.vscode/ +# Chart tests run with go test; they are not part of the package. +*_test.go +README.md.gotmpl diff --git a/helm-chart/zxporter-netmon/Chart.lock b/helm-chart/zxporter-netmon/Chart.lock new file mode 100644 index 00000000..fa7af3ab --- /dev/null +++ b/helm-chart/zxporter-netmon/Chart.lock @@ -0,0 +1,6 @@ +dependencies: +- name: devzero-common + repository: file://../devzero-common + version: 0.1.0 +digest: sha256:95ad29cb5f46a1e2219d1280d118b1c6a51d3c7dad9c60cd87cc7bdc28228892 +generated: "2026-10-07T11:42:30.632495-03:00" diff --git a/helm-chart/zxporter-netmon/Chart.yaml b/helm-chart/zxporter-netmon/Chart.yaml index 0cb36c54..5fc3e1d9 100644 --- a/helm-chart/zxporter-netmon/Chart.yaml +++ b/helm-chart/zxporter-netmon/Chart.yaml @@ -2,5 +2,12 @@ apiVersion: v2 name: zxporter-netmon description: A Helm chart for zxporter network monitor daemonset type: application -version: 0.1.6 -appVersion: "0.1.6" +version: 0.1.25 +appVersion: "0.1.25" +dependencies: + # Shared DevZero chart conventions (labels). A byte-identical copy of the + # monorepo's charts/devzero-common, kept here so the public zxporter repo + # carries it; scripts/chart_lint.py fails CI if the copy drifts. + - name: devzero-common + version: 0.1.0 + repository: "file://../devzero-common" diff --git a/helm-chart/zxporter-netmon/README.md b/helm-chart/zxporter-netmon/README.md new file mode 100644 index 00000000..24e31799 --- /dev/null +++ b/helm-chart/zxporter-netmon/README.md @@ -0,0 +1,84 @@ +# zxporter-netmon + +![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) + +A Helm chart for zxporter network monitor daemonset + +zxporter-netmon is the per-node DaemonSet that collects network flow metrics +for zxporter, using conntrack (netfilter) or eBPF. + +Every object carries `app.kubernetes.io/part-of: dz-platform` and a +`devzero.io/component` label naming its chart, from the shared +`devzero-common` library. Pod templates and selectors do not carry them. + +To pull from a private registry, set `global.imageRegistry` (the `dz-platform` +umbrella sets it for every component). It replaces the registry host of the +netmon image and keeps the path, tag and digest. The default repository is a +Docker Hub path, so `devzeroinc/zxporter-netmon:` becomes +`/devzeroinc/zxporter-netmon:`. The value may carry +a path prefix (`harbor.example/devzero`). `global.imagePullSecrets` is merged +with `imagePullSecrets` on every netmon pod; both take names or +`{name: ...}` objects. + +The chart vendors its dependencies into `charts/`, which is not committed. +From a source checkout, run `make helm-deps` (or `helm dependency build` on +the chart) before installing or rendering it. + +This README is generated by [helm-docs](https://github.com/norwoodj/helm-docs) +from `values.yaml` and `README.md.gotmpl`; edit those and run +`just chart-docs` in the DevZero services monorepo instead of editing it. + +## Requirements + +| Repository | Name | Version | +|------------|------|---------| +| file://../devzero-common | devzero-common | 0.1.0 | + +## Values + +| Key | Type | Default | Description | +|-----|------|---------|-------------| +| affinity | object | `{}` | | +| commonAnnotations | object | `{}` | | +| commonLabels | object | `{}` | | +| config.clusterToken | string | `""` | | +| config.collectorMode | string | `"netfilter"` | | +| config.containerPort | int | `9101` | | +| config.containerRedMetrics | bool | `false` | | +| config.dakrUrl | string | `""` | | +| config.exportInterval | string | `"60s"` | | +| config.logLevel | string | `"info"` | | +| config.mainConfigMapName | string | `"devzero-zxporter-env-config"` | | +| config.pprofBindAddress | string | `""` | | +| config.pprofBlockRate | int | `0` | | +| config.pprofMutexFraction | int | `0` | | +| config.tokenSecretName | string | `"devzero-zxporter-token"` | | +| containerRedMetricsResources.limits.cpu | string | `"300m"` | | +| containerRedMetricsResources.limits.memory | string | `"384Mi"` | | +| containerRedMetricsResources.requests.cpu | string | `"100m"` | | +| containerRedMetricsResources.requests.memory | string | `"128Mi"` | | +| dnsPolicy | string | `"ClusterFirstWithHostNet"` | | +| fullnameOverride | string | `""` | | +| hostNetwork | bool | `true` | | +| image.pullPolicy | string | `"IfNotPresent"` | | +| image.repository | string | `"devzeroinc/zxporter-netmon"` | | +| image.tag | string | the released version, v-prefixed | Image tag. The release sets it to the version being released. | +| imagePullSecrets | list | `[]` | Pull secrets for the netmon pods, merged with `global.imagePullSecrets` and deduplicated. Entries are secret names or `{name: ...}` objects. | +| nameOverride | string | `""` | | +| nodeSelector | object | `{}` | | +| podAnnotations."container.apparmor.security.beta.kubernetes.io/zxporter-netmon" | string | `"unconfined"` | | +| podLabels | object | `{}` | | +| podSecurityContext | object | `{}` | | +| resources.limits.cpu | string | `"100m"` | | +| resources.limits.memory | string | `"128Mi"` | | +| resources.requests.cpu | string | `"50m"` | | +| resources.requests.memory | string | `"64Mi"` | | +| securityContext.appArmorProfile.type | string | `"Unconfined"` | | +| securityContext.capabilities.add[0] | string | `"NET_ADMIN"` | | +| securityContext.capabilities.add[1] | string | `"SYS_RESOURCE"` | | +| securityContext.privileged | bool | `true` | | +| serviceAccount.annotations | object | `{}` | | +| serviceAccount.create | bool | `true` | | +| serviceAccount.name | string | `"zxporter-netmon"` | | +| tolerations[0].effect | string | `"NoSchedule"` | | +| tolerations[0].operator | string | `"Exists"` | | diff --git a/helm-chart/zxporter-netmon/README.md.gotmpl b/helm-chart/zxporter-netmon/README.md.gotmpl new file mode 100644 index 00000000..274098a3 --- /dev/null +++ b/helm-chart/zxporter-netmon/README.md.gotmpl @@ -0,0 +1,35 @@ +{{ template "chart.header" . }} +{{ template "chart.deprecationWarning" . }} + +{{/* No version badges: the release bumps Chart.yaml without regenerating this README. */ -}} +{{ template "chart.typeBadge" . }} + +{{ template "chart.description" . }} + +zxporter-netmon is the per-node DaemonSet that collects network flow metrics +for zxporter, using conntrack (netfilter) or eBPF. + +Every object carries `app.kubernetes.io/part-of: dz-platform` and a +`devzero.io/component` label naming its chart, from the shared +`devzero-common` library. Pod templates and selectors do not carry them. + +To pull from a private registry, set `global.imageRegistry` (the `dz-platform` +umbrella sets it for every component). It replaces the registry host of the +netmon image and keeps the path, tag and digest. The default repository is a +Docker Hub path, so `devzeroinc/zxporter-netmon:` becomes +`/devzeroinc/zxporter-netmon:`. The value may carry +a path prefix (`harbor.example/devzero`). `global.imagePullSecrets` is merged +with `imagePullSecrets` on every netmon pod; both take names or +`{name: ...}` objects. + +The chart vendors its dependencies into `charts/`, which is not committed. +From a source checkout, run `make helm-deps` (or `helm dependency build` on +the chart) before installing or rendering it. + +This README is generated by [helm-docs](https://github.com/norwoodj/helm-docs) +from `values.yaml` and `README.md.gotmpl`; edit those and run +`just chart-docs` in the DevZero services monorepo instead of editing it. + +{{ template "chart.requirementsSection" . }} + +{{ template "chart.valuesSection" . }} diff --git a/helm-chart/zxporter-netmon/chart_common_metadata_test.go b/helm-chart/zxporter-netmon/chart_common_metadata_test.go new file mode 100644 index 00000000..58037624 --- /dev/null +++ b/helm-chart/zxporter-netmon/chart_common_metadata_test.go @@ -0,0 +1,11 @@ +package zxporternetmon_test + +import ( + "testing" + + "github.com/devzero-inc/zxporter/helm-chart/internal/charttest" +) + +func TestCommonMetadataContract(t *testing.T) { + charttest.RunContract(t, charttest.Spec{ChartDir: "."}) +} diff --git a/helm-chart/zxporter-netmon/chart_globals_test.go b/helm-chart/zxporter-netmon/chart_globals_test.go new file mode 100644 index 00000000..1334110f --- /dev/null +++ b/helm-chart/zxporter-netmon/chart_globals_test.go @@ -0,0 +1,68 @@ +package zxporternetmon_test + +import ( + "testing" + + "github.com/devzero-inc/zxporter/helm-chart/internal/charttest" +) + +// netmonDakrURL returns the DAKR_URL env of the netmon container, and whether +// it is set at all (without it netmon reads zxporter's ConfigMap). +func netmonDakrURL(t *testing.T, args ...string) (string, bool) { + t.Helper() + for _, template := range charttest.PodTemplates(charttest.Objects(t, charttest.Render(t, platformSpec, args...)), true) { + containers, _ := template["spec"].(map[string]any)["containers"].([]any) + for _, c := range containers { + env, _ := c.(map[string]any)["env"].([]any) + for _, e := range env { + if e := e.(map[string]any); e["name"] == "DAKR_URL" { + v, _ := e["value"].(string) + return v, true + } + } + } + } + return "", false +} + +// global.dakrUrl from the dz-platform umbrella wins over config.dakrUrl; with +// neither set, netmon keeps reading DAKR_URL from zxporter's ConfigMap. +func TestGlobalDakrURL(t *testing.T) { + for _, tc := range []struct { + name string + args []string + want string + set bool + }{ + {"neither set", nil, "", false}, + {"chart value", charttest.Set("config.dakrUrl=https://own.example"), "https://own.example", true}, + {"global wins", charttest.Set("config.dakrUrl=https://own.example", "global.dakrUrl=https://dakr.example"), "https://dakr.example", true}, + {"global alone", charttest.Set("global.dakrUrl=https://dakr.example"), "https://dakr.example", true}, + {"empty global keeps the chart value", charttest.Set("config.dakrUrl=https://own.example", "global.dakrUrl="), "https://own.example", true}, + } { + t.Run(tc.name, func(t *testing.T) { + got, set := netmonDakrURL(t, tc.args...) + if got != tc.want || set != tc.set { + t.Errorf("DAKR_URL=%q (set %v), want %q (set %v)", got, set, tc.want, tc.set) + } + }) + } +} + +// The umbrella's global.imageRegistry and global.imagePullSecrets reach every +// netmon pod. +func TestImageGlobals(t *testing.T) { + charttest.AssertImageGlobals(t, platformSpec, charttest.ImageGlobals{PullSecretsKey: "imagePullSecrets"}) +} + +// The repository renders verbatim, as it always did, even a scalar Helm's +// default would treat as empty. The tag is pinned so the release bump, which +// rewrites the default tag, does not break the test. +func TestImageRepositoryScalar(t *testing.T) { + manifests := charttest.Render(t, platformSpec, charttest.Set("image.repository=0", "image.tag=v9.9.9")...) + for name, tmpl := range charttest.PodTemplates(charttest.Objects(t, manifests), true) { + if got := charttest.Images(tmpl); len(got) != 1 || got[0] != "0:v9.9.9" { + t.Errorf("%s: images %v, want [0:v9.9.9]", name, got) + } + } +} diff --git a/helm-chart/zxporter-netmon/chart_platform_test.go b/helm-chart/zxporter-netmon/chart_platform_test.go new file mode 100644 index 00000000..aa6e1f5d --- /dev/null +++ b/helm-chart/zxporter-netmon/chart_platform_test.go @@ -0,0 +1,79 @@ +package zxporternetmon_test + +import ( + "reflect" + "strings" + "testing" + + "github.com/devzero-inc/zxporter/helm-chart/internal/charttest" +) + +// schemaError prefixes every values.schema.json violation, on helm 3 and 4. +const schemaError = "values don't meet the specifications of the schema" + +var platformSpec = charttest.Spec{ChartDir: "."} + +func TestPlatformLabels(t *testing.T) { + charttest.AssertPlatformLabels(t, platformSpec) + charttest.AssertPlatformLabels(t, platformSpec, "--set", "serviceAccount.create=false") +} + +func TestUserCommonLabelsKeepTheirPlatformKeys(t *testing.T) { + objects := charttest.Objects(t, charttest.Render(t, platformSpec, + "--set", `commonLabels.devzero\.io/component=custom`, + )) + for _, obj := range objects { + if got := charttest.StringMap(obj.Doc, "metadata", "labels")[charttest.ComponentLabel]; got != "custom" { + t.Errorf("%s: %s = %q, want %q", charttest.Describe(obj.Doc), charttest.ComponentLabel, got, "custom") + } + } +} + +func TestValuesSchema(t *testing.T) { + rejected := []struct { + name string + args []string + path string + }{ + {"image removed", []string{"--set", "image=null"}, "image"}, + {"service account removed", []string{"--set", "serviceAccount=null"}, "serviceAccount"}, + {"config removed", []string{"--set", "config=null"}, "config"}, + {"config as a string", []string{"--set", "config=x"}, "config"}, + } + for _, tc := range rejected { + t.Run("rejects "+tc.name, func(t *testing.T) { + _, err := charttest.TryRender(t, platformSpec, tc.args...) + if err == nil || !strings.Contains(err.Error(), schemaError) || !strings.Contains(err.Error(), tc.path) { + t.Fatalf("want a schema error naming %q, got %v", tc.path, err) + } + }) + } + + accepted := []struct { + name string + args []string + }{ + {"numeric image tag", []string{"--set", "image.tag=123"}}, + {"tolerations as a string, which rendered before the schema", []string{"--set", "tolerations=x"}}, + {"unknown key", []string{"--set", "somethingNew=1"}}, + } + for _, tc := range accepted { + t.Run("accepts "+tc.name, func(t *testing.T) { + if _, err := charttest.TryRender(t, platformSpec, tc.args...); err != nil { + t.Fatal(err) + } + }) + } +} + +// The DaemonSet selector is immutable, so it is pinned exactly. +func TestSelectorsArePinned(t *testing.T) { + want := map[string]string{ + "DaemonSet/release-zxporter-netmon": `{"matchLabels":{"app":"zxporter-netmon",` + + `"app.kubernetes.io/instance":"release","app.kubernetes.io/name":"zxporter-netmon"}}`, + } + got := charttest.Selectors(t, charttest.Objects(t, charttest.Render(t, platformSpec))) + if !reflect.DeepEqual(got, want) { + t.Fatalf("selectors changed:\n got %v\nwant %v", got, want) + } +} diff --git a/helm-chart/zxporter-netmon/templates/_helpers.tpl b/helm-chart/zxporter-netmon/templates/_helpers.tpl index fadb6597..1ecc457d 100644 --- a/helm-chart/zxporter-netmon/templates/_helpers.tpl +++ b/helm-chart/zxporter-netmon/templates/_helpers.tpl @@ -5,6 +5,33 @@ Expand the name of the chart. {{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }} {{- end }} +{{/* +The netmon image: image.repository:image.tag (the appVersion when the tag is +empty), with its registry host swapped for global.imageRegistry when the +dz-platform umbrella sets it. The default repository has no host, so it is a +Docker Hub path: devzeroinc/zxporter-netmon becomes +/devzeroinc/zxporter-netmon. With the global unset this +is exactly the reference the chart always rendered. +*/}} +{{- define "zxporter-netmon.image" -}} +{{- /* Each key renders as the chart always printed it: the repository + verbatim (null as empty, 0 or false as its string), the tag falling back + to the appVersion when empty (0 and false included, as before). */ -}} +{{- $repository := .Values.image.repository -}} +{{- $tag := .Values.image.tag | default .Chart.AppVersion -}} +{{- $ref := printf "%s:%s" (ternary "" (toString $repository) (kindIs "invalid" $repository)) (ternary "" (toString $tag) (kindIs "invalid" $tag)) -}} +{{- include "devzero-common.imageRef" (dict "ref" $ref "global" .Values.global) -}} +{{- end }} + +{{/* +The pod's imagePullSecrets block: global.imagePullSecrets merged with the +chart's own imagePullSecrets, names or {name: ...} objects, deduplicated. +Empty, and so rendering nothing, when neither is set. +*/}} +{{- define "zxporter-netmon.imagePullSecrets" -}} +{{- include "devzero-common.imagePullSecrets" (dict "Values" (dict "global" .Values.global "image" (dict "pullSecrets" .Values.imagePullSecrets))) -}} +{{- end }} + {{/* Create a default fully qualified app name. We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). @@ -40,6 +67,9 @@ helm.sh/chart: {{ include "zxporter-netmon.chart" . }} app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} {{- end }} app.kubernetes.io/managed-by: {{ .Release.Service }} +{{- with (include "zxporter-netmon.commonLabels" .) }} +{{ . }} +{{- end }} {{- end }} {{/* @@ -61,3 +91,93 @@ Create the name of the service account to use {{- default "default" .Values.serviceAccount.name }} {{- end }} {{- end }} + +{{/* +Site-wide labels for object metadata. + +Clusters running an admission policy engine (OPA Gatekeeper, Kyverno) often +require site-specific metadata on every object before they will admit it. Every +key the chart owns is dropped from the user-supplied values, so a collision can +neither rewrite chart state nor emit a duplicate YAML key. + +`global.commonLabels` is the knob that reaches subcharts; a chart-local +`commonLabels` applies only to the chart it is set on and wins on a collision. + +Every object also carries devzero-common's platform labels, +app.kubernetes.io/part-of and devzero.io/component, so all DevZero charts can +be selected the same way. A user who already set either key through +commonLabels keeps their value. Pod templates do not get them: those labels +come from podExtraLabels, and selectors never change. +*/}} +{{- define "zxporter-netmon.commonLabels" -}} +{{- $extra := deepCopy (.Values.commonLabels | default dict) -}} +{{- if .Values.global }}{{- with .Values.global.commonLabels }}{{- $extra = merge $extra (deepCopy .) }}{{- end }}{{- end }} +{{- /* Completeness of this list is enforced by the "no chart-emitted key can + produce a duplicate" assertion in helm-chart/internal/charttest, which + derives the real set from a render rather than restating it here. */}} +{{- range $key := list "helm.sh/chart" "app.kubernetes.io/name" "app.kubernetes.io/instance" "app.kubernetes.io/version" "app.kubernetes.io/managed-by" "app.kubernetes.io/component" "app" "control-plane" }} +{{- $_ := unset $extra $key }} +{{- end }} +{{- toYaml (merge $extra (include "zxporter-netmon.platformLabels" . | fromYaml)) }} +{{- end }} + +{{/* +devzero-common's platform labels: part-of and component only. The chart keeps +its own name, instance, version, managed-by and helm.sh/chart labels, and its +own selectors, so nothing that selects on them changes. +*/}} +{{- define "zxporter-netmon.platformLabels" -}} +{{- toYaml (pick (include "devzero-common.labels" . | fromYaml) "app.kubernetes.io/part-of" "devzero.io/component") }} +{{- end }} + +{{/* +Site-wide annotations for object metadata. +*/}} +{{- define "zxporter-netmon.commonAnnotations" -}} +{{- $extra := deepCopy (.Values.commonAnnotations | default dict) -}} +{{- if .Values.global }}{{- with .Values.global.commonAnnotations }}{{- $extra = merge $extra (deepCopy .) }}{{- end }}{{- end }} +{{- range $key := list "helm.sh/hook" "helm.sh/hook-weight" "helm.sh/hook-delete-policy" "kubectl.kubernetes.io/default-container" "devzero.io/gpu-runtime-mode" "devzero.io/gpu-runtime-class" "ignore-check.kube-linter.io/privileged-container" "ignore-check.kube-linter.io/run-as-non-root" "ignore-check.kube-linter.io/privilege-escalation-container" "ignore-check.kube-linter.io/no-read-only-root-fs" }} +{{- $_ := unset $extra $key }} +{{- end }} +{{- with $extra }} +{{- toYaml . }} +{{- end }} +{{- end }} + +{{/* +Pod-template labels: commonLabels plus podLabels, podLabels winning. + +Gatekeeper's expand-workloads (and Kyverno's equivalent) evaluates constraints +against the Pod a workload would create, so site labels have to reach the pod +template and not just the owning object. Keys the pod templates set themselves +are dropped: several of them are selector labels, which are immutable after +creation. +*/}} +{{- define "zxporter-netmon.podExtraLabels" -}} +{{- $extra := deepCopy (.Values.podLabels | default dict) -}} +{{- if .Values.global }}{{- with .Values.global.podLabels }}{{- $extra = merge $extra (deepCopy .) }}{{- end }}{{- end }} +{{- $extra = merge $extra (deepCopy (.Values.commonLabels | default dict)) -}} +{{- if .Values.global }}{{- with .Values.global.commonLabels }}{{- $extra = merge $extra (deepCopy .) }}{{- end }}{{- end }} +{{- range $key := list "helm.sh/chart" "app.kubernetes.io/name" "app.kubernetes.io/instance" "app.kubernetes.io/version" "app.kubernetes.io/managed-by" "app.kubernetes.io/component" "app" "control-plane" }} +{{- $_ := unset $extra $key }} +{{- end }} +{{- with $extra }} +{{- toYaml . }} +{{- end }} +{{- end }} + +{{/* +Pod-template annotations: commonAnnotations plus podAnnotations. +*/}} +{{- define "zxporter-netmon.podExtraAnnotations" -}} +{{- $extra := deepCopy (.Values.podAnnotations | default dict) -}} +{{- if .Values.global }}{{- with .Values.global.podAnnotations }}{{- $extra = merge $extra (deepCopy .) }}{{- end }}{{- end }} +{{- $extra = merge $extra (deepCopy (.Values.commonAnnotations | default dict)) -}} +{{- if .Values.global }}{{- with .Values.global.commonAnnotations }}{{- $extra = merge $extra (deepCopy .) }}{{- end }}{{- end }} +{{- range $key := list "helm.sh/hook" "helm.sh/hook-weight" "helm.sh/hook-delete-policy" "kubectl.kubernetes.io/default-container" "devzero.io/gpu-runtime-mode" "devzero.io/gpu-runtime-class" "ignore-check.kube-linter.io/privileged-container" "ignore-check.kube-linter.io/run-as-non-root" "ignore-check.kube-linter.io/privilege-escalation-container" "ignore-check.kube-linter.io/no-read-only-root-fs" }} +{{- $_ := unset $extra $key }} +{{- end }} +{{- with $extra }} +{{- toYaml . }} +{{- end }} +{{- end }} diff --git a/helm-chart/zxporter-netmon/templates/clusterrole.yaml b/helm-chart/zxporter-netmon/templates/clusterrole.yaml index 784da39c..4700ad58 100644 --- a/helm-chart/zxporter-netmon/templates/clusterrole.yaml +++ b/helm-chart/zxporter-netmon/templates/clusterrole.yaml @@ -1,6 +1,10 @@ apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: + {{- with (include "zxporter-netmon.commonAnnotations" $) }} + annotations: + {{- . | nindent 4 }} + {{- end }} name: "{{ include "zxporter-netmon.fullname" . }}" labels: {{- include "zxporter-netmon.labels" . | nindent 4 }} rules: diff --git a/helm-chart/zxporter-netmon/templates/clusterrolebinding.yaml b/helm-chart/zxporter-netmon/templates/clusterrolebinding.yaml index ac51cbe4..1a421aed 100644 --- a/helm-chart/zxporter-netmon/templates/clusterrolebinding.yaml +++ b/helm-chart/zxporter-netmon/templates/clusterrolebinding.yaml @@ -1,6 +1,10 @@ apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: + {{- with (include "zxporter-netmon.commonAnnotations" $) }} + annotations: + {{- . | nindent 4 }} + {{- end }} name: "{{ include "zxporter-netmon.fullname" . }}" labels: {{- include "zxporter-netmon.labels" . | nindent 4 }} roleRef: diff --git a/helm-chart/zxporter-netmon/templates/daemonset.yaml b/helm-chart/zxporter-netmon/templates/daemonset.yaml index a62591ee..b730e845 100644 --- a/helm-chart/zxporter-netmon/templates/daemonset.yaml +++ b/helm-chart/zxporter-netmon/templates/daemonset.yaml @@ -1,6 +1,10 @@ apiVersion: apps/v1 kind: DaemonSet metadata: + {{- with (include "zxporter-netmon.commonAnnotations" $) }} + annotations: + {{- . | nindent 4 }} + {{- end }} name: {{ include "zxporter-netmon.fullname" . }} namespace: {{ .Release.Namespace }} labels: @@ -11,16 +15,22 @@ spec: {{- include "zxporter-netmon.selectorLabels" . | nindent 6 }} template: metadata: - {{- with .Values.podAnnotations }} + {{- /* podExtraAnnotations already merges .Values.podAnnotations with the + common and global values, so this emits the merged map once. A + second `toYaml .Values.podAnnotations` here would duplicate every + key it holds. */}} + {{- with (include "zxporter-netmon.podExtraAnnotations" $) }} annotations: - {{- toYaml . | nindent 8 }} + {{- . | nindent 8 }} {{- end }} labels: + {{- with (include "zxporter-netmon.podExtraLabels" $) }} + {{- . | nindent 8 }} + {{- end }} {{- include "zxporter-netmon.selectorLabels" . | nindent 8 }} spec: - {{- with .Values.imagePullSecrets }} - imagePullSecrets: - {{- toYaml . | nindent 8 }} + {{- with include "zxporter-netmon.imagePullSecrets" . }} + {{- . | nindent 6 }} {{- end }} serviceAccountName: {{ include "zxporter-netmon.serviceAccountName" . }} securityContext: @@ -31,7 +41,7 @@ spec: - name: {{ .Chart.Name }} securityContext: {{- toYaml .Values.securityContext | nindent 12 }} - image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}" + image: {{ include "zxporter-netmon.image" . | quote }} imagePullPolicy: {{ .Values.image.pullPolicy }} ports: - name: http @@ -51,9 +61,9 @@ spec: valueFrom: fieldRef: fieldPath: spec.nodeName - {{- if .Values.config.dakrUrl }} + {{- with include "devzero-common.dakrUrl" (list . .Values.config.dakrUrl) }} - name: DAKR_URL - value: {{ .Values.config.dakrUrl | quote }} + value: {{ . | quote }} {{- end }} {{- if .Values.config.clusterToken }} - name: CLUSTER_TOKEN @@ -63,9 +73,26 @@ spec: value: {{ .Values.config.exportInterval | quote }} args: - "-collector-mode={{ .Values.config.collectorMode }}" + - "-metrics-bind-address=:{{ .Values.config.containerPort }}" + {{- if .Values.config.containerRedMetrics }} + - "-container-red-metrics" + {{- end }} + {{- if .Values.config.pprofBindAddress }} + - "-pprof-bind-address={{ .Values.config.pprofBindAddress }}" + {{- if .Values.config.pprofMutexFraction }} + - "-pprof-mutex-fraction={{ .Values.config.pprofMutexFraction }}" + {{- end }} + {{- if .Values.config.pprofBlockRate }} + - "-pprof-block-rate={{ .Values.config.pprofBlockRate }}" + {{- end }} + {{- end }} # - "-log-level={{ .Values.config.logLevel }}" resources: + {{- if .Values.config.containerRedMetrics }} + {{- toYaml .Values.containerRedMetricsResources | nindent 12 }} + {{- else }} {{- toYaml .Values.resources | nindent 12 }} + {{- end }} volumeMounts: - name: zxporter-config mountPath: /etc/zxporter diff --git a/helm-chart/zxporter-netmon/templates/serviceaccount.yaml b/helm-chart/zxporter-netmon/templates/serviceaccount.yaml index 375dbffb..53900f7b 100644 --- a/helm-chart/zxporter-netmon/templates/serviceaccount.yaml +++ b/helm-chart/zxporter-netmon/templates/serviceaccount.yaml @@ -6,7 +6,12 @@ metadata: namespace: {{ .Release.Namespace }} labels: {{- include "zxporter-netmon.labels" . | nindent 4 }} - {{- with .Values.serviceAccount.annotations }} + {{- /* serviceAccount.annotations wins over commonAnnotations on a key + collision. Merged here rather than emitted as two blocks because + `with` rebinds the dot, which would hand the helper the annotation + map instead of the root context. */}} + {{- $saAnnotations := merge (deepCopy (.Values.serviceAccount.annotations | default dict)) (fromYaml (include "zxporter-netmon.commonAnnotations" $)) }} + {{- with $saAnnotations }} annotations: {{- toYaml . | nindent 4 }} {{- end }} diff --git a/helm-chart/zxporter-netmon/values.schema.json b/helm-chart/zxporter-netmon/values.schema.json new file mode 100644 index 00000000..de80c791 --- /dev/null +++ b/helm-chart/zxporter-netmon/values.schema.json @@ -0,0 +1,133 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "zxporter-netmon chart values", + "description": "Permissive by design: unknown keys are allowed and scalars stay untyped, because installers pass --set strings and releases upgrade with reused values, so every value a release already carries must keep validating. A map or list is typed only where every other value already failed to render. required lists only the keys whose absence (--set key=null) failed to render in every configuration before this schema existed.", + "type": "object", + "properties": { + "commonLabels": { + "properties": {} + }, + "commonAnnotations": { + "properties": {} + }, + "podLabels": { + "properties": {} + }, + "image": { + "type": [ + "object", + "null" + ], + "properties": { + "repository": {}, + "pullPolicy": {}, + "tag": {} + } + }, + "imagePullSecrets": {}, + "nameOverride": {}, + "fullnameOverride": {}, + "serviceAccount": { + "type": [ + "object", + "null" + ], + "properties": { + "create": {}, + "annotations": { + "properties": {} + }, + "name": {} + } + }, + "podAnnotations": { + "properties": { + "container.apparmor.security.beta.kubernetes.io/zxporter-netmon": {} + } + }, + "podSecurityContext": { + "properties": {} + }, + "securityContext": { + "properties": { + "privileged": {}, + "appArmorProfile": { + "properties": { + "type": {} + } + }, + "capabilities": { + "properties": { + "add": {} + } + } + } + }, + "resources": { + "properties": { + "limits": { + "properties": { + "cpu": {}, + "memory": {} + } + }, + "requests": { + "properties": { + "cpu": {}, + "memory": {} + } + } + } + }, + "containerRedMetricsResources": { + "properties": { + "limits": { + "properties": { + "cpu": {}, + "memory": {} + } + }, + "requests": { + "properties": { + "cpu": {}, + "memory": {} + } + } + } + }, + "nodeSelector": { + "properties": {} + }, + "tolerations": {}, + "affinity": { + "properties": {} + }, + "hostNetwork": {}, + "dnsPolicy": {}, + "config": { + "type": [ + "object", + "null" + ], + "properties": { + "containerPort": {}, + "logLevel": {}, + "mainConfigMapName": {}, + "exportInterval": {}, + "collectorMode": {}, + "dakrUrl": {}, + "clusterToken": {}, + "tokenSecretName": {}, + "containerRedMetrics": {}, + "pprofBindAddress": {}, + "pprofMutexFraction": {}, + "pprofBlockRate": {} + } + } + }, + "required": [ + "image", + "serviceAccount", + "config" + ] +} diff --git a/helm-chart/zxporter-netmon/values.yaml b/helm-chart/zxporter-netmon/values.yaml index 2ff88a81..6fbb5ed4 100644 --- a/helm-chart/zxporter-netmon/values.yaml +++ b/helm-chart/zxporter-netmon/values.yaml @@ -1,8 +1,38 @@ +# Labels and annotations added to every object this chart creates, for clusters +# whose admission policy (OPA Gatekeeper, Kyverno) requires site-specific +# metadata before it will admit a resource. Without these, such a cluster +# rejects the chart's objects and the install fails. +# +# commonLabels: +# example.com/cost-center: platform +# example.com/tier: infra +# +# commonLabels/commonAnnotations land on both the object and its pod template -- +# Gatekeeper evaluates constraints against the Pod a workload would create, so +# metadata on the DaemonSet alone is not enough. podLabels/podAnnotations add +# pod-only metadata on top. +# +# Set these under `global:` to cover subcharts as well; a chart-local +# value applies only to this chart and wins on a collision. +# +# Keys this chart owns win over these and are dropped from the values: +# app.kubernetes.io/*, helm.sh/*, selector labels, and config checksums. +# Selector labels are never modified, so setting these on an existing release is +# a safe upgrade. +commonLabels: {} +commonAnnotations: {} +podLabels: {} +# podAnnotations is defined below with this chart's apparmor default; the +# site-metadata helpers merge it with commonAnnotations rather than replacing it. + image: repository: devzeroinc/zxporter-netmon pullPolicy: IfNotPresent - # Overrides the image tag whose default is the chart appVersion. - tag: "v0.1.6" + # -- Image tag. The release sets it to the version being released. + # @default -- the released version, v-prefixed + tag: "v0.1.25" +# -- Pull secrets for the netmon pods, merged with `global.imagePullSecrets` +# and deduplicated. Entries are secret names or `{name: ...}` objects. imagePullSecrets: [] nameOverride: "" fullnameOverride: "" @@ -34,6 +64,36 @@ resources: requests: cpu: 50m memory: 64Mi +# Used instead of the block above when config.containerRedMetrics is true. The +# socket probes add an 8Mi ring buffer, a connection table and a decode loop on +# every socket call, so the defaults are too tight to run them under: with the +# feature on, netmon idles at ~98MiB working set / ~66MiB anon RSS before serving +# any traffic, and the first decodable load on the node OOM-killed it within 26s +# of a 128Mi limit. +# +# The binary sets GOMEMLIMIT to 0.9 x the memory limit at startup (automemlimit, +# see cmd/zxporter-netmon/main.go), so the limit is the GC's target and not only +# the kernel's kill threshold -- which is why raising it was not optional. At +# 128Mi that target is 115MiB against a ~66MiB idle heap, putting the GC in a +# permanent collect cycle; at cpu: 100m that throttles the ring-buffer reader +# into silent kernel-side event drops rather than an OOM kill. +# +# 384Mi/300m is headroom over the measured idle footprint plus the ~9MiB of BPF +# maps (which sit outside GOMEMLIMIT), not a measured working set under load. +# Tighten it once there is load data from a node running the feature. +# +# The memory request moves too, unlike in earlier revisions of this chart. A +# request of 64Mi against a ~98MiB steady-state working set makes netmon the +# kubelet's first eviction candidate under node memory pressure, since ranking is +# by usage above request -- so the feature would be reserving nothing and paying +# for it in restarts. +containerRedMetricsResources: + limits: + cpu: 300m + memory: 384Mi + requests: + cpu: 100m + memory: 128Mi nodeSelector: {} tolerations: - operator: Exists @@ -43,15 +103,46 @@ hostNetwork: true dnsPolicy: ClusterFirstWithHostNet # Config for collection config: - containerPort: 8081 + # Serves /metrics, /healthz and /readyz. hostNetwork is true, so Kubernetes defaults + # hostPort to this containerPort and the value becomes a reservation on the node's + # real port. Do NOT move it back to 8081: dakr-operator is also hostNetwork and binds + # :8081 for its health probe, so the two pods cannot be scheduled onto the same node. + # Wired to -metrics-bind-address in the daemonset, so changing it here moves both the + # reservation and the listener together. + containerPort: 9101 logLevel: "info" mainConfigMapName: "devzero-zxporter-env-config" # Default scrape interval 5s exportInterval: "60s" # Collector mode: 'netfilter' or 'ebpf' collectorMode: "netfilter" + # Sets DAKR_URL; empty reads it from mainConfigMapName. global.dakrUrl, when + # set (the dz-platform umbrella sets it), takes precedence. dakrUrl: "" clusterToken: "" # Name of the Secret containing CLUSTER_TOKEN (created by the zxporter installer). # Takes precedence over the token in mainConfigMapName when present. tokenSecretName: "devzero-zxporter-token" + # Per-container inbound RED metrics from socket probes. + # + # Off unless switched on deliberately: it attaches kprobes and holds + # per-connection state on a DaemonSet that shares every node with the workloads + # it measures. Nothing about the chart's default behaviour changes while it is + # false -- the agent does not attach the probe and sends no such metrics. + containerRedMetrics: false + # pprof, for diagnosing CPU or memory behaviour on a node that is misbehaving. + # + # Empty disables it and that is the default. Keep the address on loopback: this + # DaemonSet runs with hostNetwork, so anything else publishes profiles on the node's + # own network, and a heap profile of this agent contains buffered request bytes from + # the traffic it is decoding. Reach it with: + # + # kubectl port-forward -n 6060:6060 + # go tool pprof http://127.0.0.1:6060/debug/pprof/profile?seconds=30 + # + # pprofMutexFraction and pprofBlockRate turn on contention profiling, which is off by + # default because it adds work to every contended lock. Switch them on for an + # investigation and back off afterwards. + pprofBindAddress: "" + pprofMutexFraction: 0 + pprofBlockRate: 0 diff --git a/helm-chart/zxporter-nodemon/.helmignore b/helm-chart/zxporter-nodemon/.helmignore new file mode 100644 index 00000000..e372ec71 --- /dev/null +++ b/helm-chart/zxporter-nodemon/.helmignore @@ -0,0 +1,14 @@ +# Patterns to ignore when building packages. +.DS_Store +.git/ +.gitignore +*.swp +*.bak +*.tmp +*.orig +*~ +.idea/ +.vscode/ +# Chart tests run with go test; they are not part of the package. +*_test.go +README.md.gotmpl diff --git a/helm-chart/zxporter-nodemon/Chart.lock b/helm-chart/zxporter-nodemon/Chart.lock new file mode 100644 index 00000000..7522477d --- /dev/null +++ b/helm-chart/zxporter-nodemon/Chart.lock @@ -0,0 +1,6 @@ +dependencies: +- name: devzero-common + repository: file://../devzero-common + version: 0.1.0 +digest: sha256:95ad29cb5f46a1e2219d1280d118b1c6a51d3c7dad9c60cd87cc7bdc28228892 +generated: "2026-10-07T11:42:25.91521-03:00" diff --git a/helm-chart/zxporter-nodemon/Chart.yaml b/helm-chart/zxporter-nodemon/Chart.yaml index 0757fbe3..262fe51d 100644 --- a/helm-chart/zxporter-nodemon/Chart.yaml +++ b/helm-chart/zxporter-nodemon/Chart.yaml @@ -2,5 +2,12 @@ apiVersion: v2 name: zxporter-nodemon description: A Helm chart for Kubernetes type: application -version: 0.1.6 -appVersion: "0.1.6" +version: 0.1.25 +appVersion: "0.1.25" +dependencies: + # Shared DevZero chart conventions (labels). A byte-identical copy of the + # monorepo's charts/devzero-common, kept here so the public zxporter repo + # carries it; scripts/chart_lint.py fails CI if the copy drifts. + - name: devzero-common + version: 0.1.0 + repository: "file://../devzero-common" diff --git a/helm-chart/zxporter-nodemon/README.md b/helm-chart/zxporter-nodemon/README.md new file mode 100644 index 00000000..bb792fbb --- /dev/null +++ b/helm-chart/zxporter-nodemon/README.md @@ -0,0 +1,93 @@ +# zxporter-nodemon + +![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) + +A Helm chart for Kubernetes + +zxporter-nodemon is the per-node DaemonSet that collects container, GPU (DCGM) +and runtime metrics for zxporter. It is normally installed as a subchart of +`zxporter`; installing it on its own is for testing. + +Every object carries `app.kubernetes.io/part-of: dz-platform` and a +`devzero.io/component` label naming its chart, from the shared +`devzero-common` library. Pod templates and selectors do not carry them. + +To pull from a private registry, set `global.imageRegistry` (the `dz-platform` +umbrella sets it for every component, and `zxporter` passes its globals to +this subchart). It replaces the registry host of the nodemon and DCGM exporter +images and keeps the path, tag and digest: +`public.ecr.aws/devzeroinc/zxporter-nodemon:` becomes +`/devzeroinc/zxporter-nodemon:`. The value may +carry a path prefix (`harbor.example/devzero`). `global.imagePullSecrets` is +merged with `imagePullSecrets` on every nodemon pod; both take names or +`{name: ...}` objects. + +The chart vendors its dependencies into `charts/`, which is not committed. +From a source checkout, run `make helm-deps` (or `helm dependency build` on +the chart) before installing or rendering it. + +This README is generated by [helm-docs](https://github.com/norwoodj/helm-docs) +from `values.yaml` and `README.md.gotmpl`; edit those and run +`just chart-docs` in the DevZero services monorepo instead of editing it. + +## Requirements + +| Repository | Name | Version | +|------------|------|---------| +| file://../devzero-common | devzero-common | 0.1.0 | + +## Values + +| Key | Type | Default | Description | +|-----|------|---------|-------------| +| cgroupMetrics.enabled | bool | `true` | Collect runtime-aware cgroup signals (Plane 0) by reading /sys/fs/cgroup directly: real CFS throttle counters, memory.events:max (the Guaranteed-memory ceiling counter), and PSI (cpu/memory pressure). Mounts /sys/fs/cgroup read-only. Unlike runtimeMetrics this needs NO hostPID / root / SYS_PTRACE — container identities are resolved from the k8s API, not /proc. | +| commonAnnotations | object | `{}` | | +| commonLabels | object | `{}` | | +| dcgmExporter.enabled | bool | `true` | | +| dcgmExporter.extraEnv | list | `[]` | | +| dcgmExporter.gpuNodeSelector.key | string | `"nvidia.com/gpu.present"` | | +| dcgmExporter.gpuNodeSelector.value | string | `"true"` | | +| dcgmExporter.image.pullPolicy | string | `"IfNotPresent"` | | +| dcgmExporter.image.repository | string | `"public.ecr.aws/devzeroinc/dcgm-exporter"` | | +| dcgmExporter.image.tag | string | `"4.5.2-4.8.1-ubuntu22.04"` | | +| dcgmExporter.resources.requests.cpu | string | `"50m"` | | +| dcgmExporter.resources.requests.memory | string | `"128Mi"` | | +| dcgmExporter.runtimeClassName | string | `"nvidia"` | | +| dcgmExporter.useExternalHostEngine | bool | `false` | | +| fullnameOverride | string | `"zxporter-nodemon"` | | +| global.gpuRuntime.mode | string | `"explicit"` | | +| global.gpuRuntime.runtimeClassName | string | `"nvidia"` | | +| global.k8sProvider | string | `""` | | +| global.priorityClass.enabled | bool | `true` | | +| image.pullPolicy | string | `"IfNotPresent"` | | +| image.repository | string | `"public.ecr.aws/devzeroinc/zxporter-nodemon"` | | +| image.tag | string | the released version, v-prefixed | Image tag. The release sets it to the version being released. | +| imagePullSecrets | list | `[]` | Pull secrets for the nodemon pods, merged with `global.imagePullSecrets` and deduplicated. Entries are secret names or `{name: ...}` objects. | +| jvmMetrics.enabled | bool | `true` | Deprecated: renamed to runtimeMetrics.enabled now that this toggle also gates Node.js detection, not just JVM. Kept for backward compatibility with existing overrides; set runtimeMetrics.enabled instead going forward. | +| nameOverride | string | `""` | | +| nodemon.affinity | string | `nil` | optional pod affinity/anti-affinity. Leave empty so nodemon schedules on EVERY node (it is the sole source of node/container metrics). Only set this if you intentionally want to restrict where nodemon runs. NOTE: this is no longer GPU-gated on GCP — DCGM/GPU metrics are handled by the dcgm-exporter sidecar, which idles gracefully on non-GPU nodes. | +| nodemon.config | object | `{}` | | +| nodemon.goMaxProcs | int | `2` | | +| nodemon.port | int | `6061` | | +| nodemon.rbac.clusterWide | bool | `true` | | +| nodemon.resources.limits.memory | string | `"256Mi"` | | +| nodemon.resources.requests.cpu | string | `"60m"` | | +| nodemon.resources.requests.memory | string | `"256Mi"` | | +| nodemon.securityContext.readOnlyRootFilesystem | bool | `true` | | +| nodemon.securityContext.runAsNonRoot | bool | `true` | | +| nodemon.tolerations[0].effect | string | `"NoExecute"` | | +| nodemon.tolerations[0].operator | string | `"Exists"` | | +| nodemon.tolerations[1].effect | string | `"NoSchedule"` | | +| nodemon.tolerations[1].operator | string | `"Exists"` | | +| nodemon.tolerations[2].effect | string | `"PreferNoSchedule"` | | +| nodemon.tolerations[2].operator | string | `"Exists"` | | +| podAnnotations | object | `{}` | | +| podLabels | object | `{}` | | +| priorityClass.create | bool | `true` | | +| priorityClass.description | string | `"This priority class is for critical zxporter infrastructure components."` | | +| priorityClass.preemptionPolicy | string | `"PreemptLowerPriority"` | | +| priorityClass.value | int | `1000000000` | | +| runtimeMetrics | object | `{}` | Enable process-introspection metrics (JVM heap via hsperfdata, Node.js detection/version). NOTE: requires hostPID + running nodemon as UID 0 + SYS_PTRACE to read other containers' /proc//root overlay. It also sets seLinuxOptions.level "s0:c0.c1023" so SELinux-enforcing nodes (Bottlerocket), which isolate containers by MCS category, do not block that read; the type stays container_t, and the level is ignored where SELinux is not enforcing. Leave unset to fall back to the deprecated jvmMetrics.enabled below. scanHandlerTimeoutMs -- bounds how long the scan-heavy /container/jvm-metrics and /container/runtime-metrics handlers may take per request (a slow /proc walk or binary version-sniff otherwise stalls the HTTP server / probes). Leave unset to use the built-in default (2500ms). | +| serviceAccount.annotations | object | `{}` | | +| serviceAccount.create | bool | `true` | | +| updateStrategy | object | `{}` | Controls `daemonset.spec.updateStrategy` field. updateStrategy: type: RollingUpdate rollingUpdate: maxUnavailable: 1 | diff --git a/helm-chart/zxporter-nodemon/README.md.gotmpl b/helm-chart/zxporter-nodemon/README.md.gotmpl new file mode 100644 index 00000000..1af10bb7 --- /dev/null +++ b/helm-chart/zxporter-nodemon/README.md.gotmpl @@ -0,0 +1,37 @@ +{{ template "chart.header" . }} +{{ template "chart.deprecationWarning" . }} + +{{/* No version badges: the release bumps Chart.yaml without regenerating this README. */ -}} +{{ template "chart.typeBadge" . }} + +{{ template "chart.description" . }} + +zxporter-nodemon is the per-node DaemonSet that collects container, GPU (DCGM) +and runtime metrics for zxporter. It is normally installed as a subchart of +`zxporter`; installing it on its own is for testing. + +Every object carries `app.kubernetes.io/part-of: dz-platform` and a +`devzero.io/component` label naming its chart, from the shared +`devzero-common` library. Pod templates and selectors do not carry them. + +To pull from a private registry, set `global.imageRegistry` (the `dz-platform` +umbrella sets it for every component, and `zxporter` passes its globals to +this subchart). It replaces the registry host of the nodemon and DCGM exporter +images and keeps the path, tag and digest: +`public.ecr.aws/devzeroinc/zxporter-nodemon:` becomes +`/devzeroinc/zxporter-nodemon:`. The value may +carry a path prefix (`harbor.example/devzero`). `global.imagePullSecrets` is +merged with `imagePullSecrets` on every nodemon pod; both take names or +`{name: ...}` objects. + +The chart vendors its dependencies into `charts/`, which is not committed. +From a source checkout, run `make helm-deps` (or `helm dependency build` on +the chart) before installing or rendering it. + +This README is generated by [helm-docs](https://github.com/norwoodj/helm-docs) +from `values.yaml` and `README.md.gotmpl`; edit those and run +`just chart-docs` in the DevZero services monorepo instead of editing it. + +{{ template "chart.requirementsSection" . }} + +{{ template "chart.valuesSection" . }} diff --git a/helm-chart/zxporter-nodemon/chart_common_metadata_test.go b/helm-chart/zxporter-nodemon/chart_common_metadata_test.go new file mode 100644 index 00000000..731d545d --- /dev/null +++ b/helm-chart/zxporter-nodemon/chart_common_metadata_test.go @@ -0,0 +1,23 @@ +package zxporternodemon_test + +import ( + "testing" + + "github.com/devzero-inc/zxporter/helm-chart/internal/charttest" +) + +// zxporter-nodemon is published standalone as well as being a subchart of +// zxporter, so it carries the knobs in its own right rather than only inheriting +// global values from the parent. +func TestCommonMetadataContract(t *testing.T) { + charttest.RunContract(t, charttest.Spec{ + ChartDir: ".", + BaseArgs: []string{"--set", "global.k8sProvider=aws"}, + Variants: map[string][]string{ + // The DaemonSet's annotations used to be emitted only under + // `if $v.withDcgm`, so a non-GPU install silently dropped + // commonAnnotations while the default render stayed green. + "dcgm disabled": {"--set", "dcgmExporter.enabled=false"}, + }, + }) +} diff --git a/helm-chart/zxporter-nodemon/chart_globals_test.go b/helm-chart/zxporter-nodemon/chart_globals_test.go new file mode 100644 index 00000000..aaf2fd5b --- /dev/null +++ b/helm-chart/zxporter-nodemon/chart_globals_test.go @@ -0,0 +1,47 @@ +package zxporternodemon_test + +import ( + "reflect" + "testing" + + "github.com/devzero-inc/zxporter/helm-chart/internal/charttest" +) + +// The umbrella's global.imageRegistry and global.imagePullSecrets reach every +// nodemon pod, both DaemonSets and both containers (nodemon and the DCGM +// exporter), in each configuration the dist installers ship. +func TestImageGlobals(t *testing.T) { + charttest.AssertImageGlobals(t, platformSpec, charttest.ImageGlobals{ + PullSecretsKey: "imagePullSecrets", + Variants: map[string][]string{ + "low privilege": charttest.Set("runtimeMetrics.enabled=false", "priorityClass.create=false"), + "without dcgm": charttest.Set("dcgmExporter.enabled=false"), + }, + }) +} + +// The DCGM tag renders verbatim, as it always did, 0 and false included. The +// nodemon tag is pinned so the release bump, which rewrites the default tag, +// does not break the test. +func TestImageTagScalars(t *testing.T) { + for _, tc := range []struct { + name string + args []string + want []string + }{ + {"dcgm tag 0", charttest.Set("image.tag=v9.9.9", "dcgmExporter.image.tag=0"), []string{ + "public.ecr.aws/devzeroinc/zxporter-nodemon:v9.9.9", "public.ecr.aws/devzeroinc/dcgm-exporter:0", + }}, + {"dcgm tag false", charttest.Set("image.tag=v9.9.9", "dcgmExporter.image.tag=false"), []string{ + "public.ecr.aws/devzeroinc/zxporter-nodemon:v9.9.9", "public.ecr.aws/devzeroinc/dcgm-exporter:false", + }}, + } { + t.Run(tc.name, func(t *testing.T) { + for name, tmpl := range charttest.PodTemplates(charttest.Objects(t, charttest.Render(t, platformSpec, tc.args...)), true) { + if got := charttest.Images(tmpl); !reflect.DeepEqual(got, tc.want) { + t.Errorf("%s: images %v, want %v", name, got, tc.want) + } + } + }) + } +} diff --git a/helm-chart/zxporter-nodemon/chart_platform_test.go b/helm-chart/zxporter-nodemon/chart_platform_test.go new file mode 100644 index 00000000..7f5d91e7 --- /dev/null +++ b/helm-chart/zxporter-nodemon/chart_platform_test.go @@ -0,0 +1,94 @@ +package zxporternodemon_test + +import ( + "reflect" + "strings" + "testing" + + "github.com/devzero-inc/zxporter/helm-chart/internal/charttest" +) + +// schemaError prefixes every values.schema.json violation, on helm 3 and 4. +const schemaError = "values don't meet the specifications of the schema" + +var platformSpec = charttest.Spec{ + ChartDir: ".", + BaseArgs: []string{"--set", "global.k8sProvider=aws"}, +} + +func TestPlatformLabels(t *testing.T) { + charttest.AssertPlatformLabels(t, platformSpec) + // The low-privilege bundle the dist installers ship. + lowPriv := charttest.Set("runtimeMetrics.enabled=false", "priorityClass.create=false") + charttest.AssertPlatformLabels(t, platformSpec, lowPriv...) +} + +func TestUserCommonLabelsKeepTheirPlatformKeys(t *testing.T) { + objects := charttest.Objects(t, charttest.Render(t, platformSpec, + "--set", `commonLabels.app\.kubernetes\.io/part-of=mine`, + )) + for _, obj := range objects { + if got := charttest.StringMap(obj.Doc, "metadata", "labels")[charttest.PartOfLabel]; got != "mine" { + t.Errorf("%s: %s = %q, want %q", charttest.Describe(obj.Doc), charttest.PartOfLabel, got, "mine") + } + } +} + +func TestValuesSchema(t *testing.T) { + rejected := []struct { + name string + args []string + path string + }{ + {"image removed", []string{"--set", "image=null"}, "image"}, + {"nodemon removed", []string{"--set", "nodemon=null"}, "nodemon"}, + {"rbac removed", []string{"--set", "nodemon.rbac=null"}, "rbac"}, + {"jvm metrics removed", []string{"--set", "jvmMetrics=null"}, "jvmMetrics"}, + {"provider removed", []string{"--set", "global.k8sProvider=null"}, "k8sProvider"}, + {"dcgm exporter removed", []string{"--set", "dcgmExporter=null"}, "dcgmExporter"}, + {"gpu node selector removed", []string{"--set", "dcgmExporter.gpuNodeSelector=null"}, "gpuNodeSelector"}, + {"service account as a string", []string{"--set", "serviceAccount=x"}, "serviceAccount"}, + {"extra env as a string", []string{"--set", "dcgmExporter.extraEnv=x"}, "extraEnv"}, + } + for _, tc := range rejected { + t.Run("rejects "+tc.name, func(t *testing.T) { + _, err := charttest.TryRender(t, platformSpec, tc.args...) + if err == nil || !strings.Contains(err.Error(), schemaError) || !strings.Contains(err.Error(), tc.path) { + t.Fatalf("want a schema error naming %q, got %v", tc.path, err) + } + }) + } + + accepted := []struct { + name string + args []string + }{ + {"numeric image tag", []string{"--set", "image.tag=123"}}, + {"dcgm image removed while dcgm is off", charttest.Set("dcgmExporter.enabled=false", "dcgmExporter.image=null")}, + {"runtime class removed in default gpu mode", charttest.Set( + "global.gpuRuntime.mode=default", "global.gpuRuntime.runtimeClassName=null")}, + {"tolerations as a string, which rendered before the schema", []string{"--set", "nodemon.tolerations=x"}}, + } + for _, tc := range accepted { + t.Run("accepts "+tc.name, func(t *testing.T) { + if _, err := charttest.TryRender(t, platformSpec, tc.args...); err != nil { + t.Fatal(err) + } + }) + } +} + +// DaemonSet selectors are immutable; the instance label is the fullname, not +// the release name, which keeps any release byte-identical to the dist bundles. +func TestSelectorsArePinned(t *testing.T) { + want := map[string]string{ + "DaemonSet/zxporter-nodemon": `{"matchLabels":{"app.kubernetes.io/instance":"zxporter-nodemon",` + + `"app.kubernetes.io/name":"zxporter-nodemon"}}`, + "DaemonSet/zxporter-nodemon-gpu": `{"matchLabels":{"app.kubernetes.io/instance":"zxporter-nodemon-gpu",` + + `"app.kubernetes.io/name":"zxporter-nodemon"}}`, + } + got := charttest.Selectors(t, charttest.Objects(t, charttest.Render(t, platformSpec))) + if !reflect.DeepEqual(got, want) { + t.Fatalf("selectors changed:\n got %v\nwant %v", got, want) + } +} diff --git a/helm-chart/zxporter-nodemon/chart_rbac_test.go b/helm-chart/zxporter-nodemon/chart_rbac_test.go new file mode 100644 index 00000000..d1b6d63a --- /dev/null +++ b/helm-chart/zxporter-nodemon/chart_rbac_test.go @@ -0,0 +1,75 @@ +package zxporternodemon_test + +import ( + "slices" + "strings" + "testing" + + "sigs.k8s.io/yaml" +) + +// TestPodsRuleGrantsListAndWatchForEveryFeatureCombination guards the RBAC the +// node-scoped pod informer (PodContainerIndex) needs. nodemon starts that +// informer when cgroupMetrics OR runtimeMetrics is enabled, and an informer +// needs both list and watch. Gating watch on runtimeMetrics alone left every +// runtimeMetrics.enabled=false install (including the -lowpriv dist bundles) +// failing with "cannot watch resource pods" and silently dropping all cgroup +// signals. +func TestPodsRuleGrantsListAndWatchForEveryFeatureCombination(t *testing.T) { + for _, tc := range []struct { + runtime, cgroup string + }{ + {"true", "true"}, + {"false", "true"}, + {"true", "false"}, + {"false", "false"}, + } { + t.Run("runtime="+tc.runtime+",cgroup="+tc.cgroup, func(t *testing.T) { + manifest := renderChart(t, + "--set", "global.k8sProvider=other", + "--set", "runtimeMetrics.enabled="+tc.runtime, + "--set", "cgroupMetrics.enabled="+tc.cgroup, + ) + + verbs := podsRuleVerbs(t, manifest) + for _, want := range []string{"get", "list", "watch"} { + if !slices.Contains(verbs, want) { + t.Errorf("pods rule verbs = %v, missing %q", verbs, want) + } + } + }) + } +} + +// podsRuleVerbs returns the verbs of the core-group pods rule in the chart's +// ClusterRole or Role, failing the test if there is not exactly one. +func podsRuleVerbs(t *testing.T, manifest string) []string { + t.Helper() + + var found [][]string + for doc := range strings.SplitSeq(manifest, "\n---\n") { + var obj struct { + Kind string `json:"kind"` + Rules []struct { + APIGroups []string `json:"apiGroups"` + Resources []string `json:"resources"` + Verbs []string `json:"verbs"` + } `json:"rules"` + } + if err := yaml.Unmarshal([]byte(doc), &obj); err != nil { + t.Fatalf("unmarshalling rendered manifest: %v\n%s", err, doc) + } + if obj.Kind != "ClusterRole" && obj.Kind != "Role" { + continue + } + for _, r := range obj.Rules { + if slices.Contains(r.APIGroups, "") && slices.Contains(r.Resources, "pods") { + found = append(found, r.Verbs) + } + } + } + if len(found) != 1 { + t.Fatalf("expected exactly one core-group pods rule, found %d", len(found)) + } + return found[0] +} diff --git a/helm-chart/zxporter-nodemon/chart_resources_test.go b/helm-chart/zxporter-nodemon/chart_resources_test.go new file mode 100644 index 00000000..940f8ab0 --- /dev/null +++ b/helm-chart/zxporter-nodemon/chart_resources_test.go @@ -0,0 +1,219 @@ +package zxporternodemon_test + +import ( + "bytes" + "fmt" + "os/exec" + "path/filepath" + "sort" + "strings" + "testing" + + "sigs.k8s.io/yaml" +) + +// resourceKnobs maps every container this chart installs to the values key +// that sets its requests/limits, and to a sentinel CPU request unique to that +// key. A container with no way to override its resources is a deployment +// blocker on any cluster whose namespaces carry a ResourceQuota or a +// LimitRange with no default request: the admission plugin rejects the whole +// pod, so one unsized sidecar takes the entire DaemonSet down with it. +// +// The chart renders the same two containers into both DaemonSet variants (the +// base one and the -gpu one produced by the GPU-node split), so both variants +// are checked. +var resourceKnobs = []struct { + valuesKey string + container string + // cpu is the sentinel request this knob is set to for the render. Each is + // distinct, so a template that hardcodes resources or reads the wrong key + // surfaces as a mismatch rather than a silent pass. + cpu string +}{ + {"nodemon.resources", "zxporter-nodemon", "111m"}, + {"dcgmExporter.resources", "dcgm-exporter", "222m"}, +} + +type renderedContainer struct { + workload string + container string + requests map[string]any + limits map[string]any +} + +func (c renderedContainer) key() string { return c.workload + "/" + c.container } + +func renderChart(t *testing.T, extraArgs ...string) string { + t.Helper() + + if _, err := exec.LookPath("helm"); err != nil { + t.Skip("helm binary not available, skipping chart render test") + } + + chartDir, err := filepath.Abs(".") + if err != nil { + t.Fatalf("resolving chart dir: %v", err) + } + + args := append([]string{"template", "release", chartDir}, extraArgs...) + + var stdout, stderr bytes.Buffer + cmd := exec.Command("helm", args...) + cmd.Stdout = &stdout + cmd.Stderr = &stderr + if err := cmd.Run(); err != nil { + t.Fatalf("helm template failed: %v\n%s", err, stderr.String()) + } + return stdout.String() +} + +// collectContainers decodes a rendered manifest and returns every container +// and initContainer of every pod-bearing workload in it, keyed by +// "/". +func collectContainers(t *testing.T, manifest string) map[string]renderedContainer { + t.Helper() + + found := map[string]renderedContainer{} + for doc := range strings.SplitSeq(manifest, "\n---\n") { + if strings.TrimSpace(doc) == "" { + continue + } + + var obj map[string]any + if err := yaml.Unmarshal([]byte(doc), &obj); err != nil { + t.Fatalf("unmarshalling rendered manifest: %v\n%s", err, doc) + } + + spec, _ := obj["spec"].(map[string]any) + if spec == nil { + continue + } + template, _ := spec["template"].(map[string]any) + podSpec, _ := template["spec"].(map[string]any) + if podSpec == nil { + continue + } + + meta, _ := obj["metadata"].(map[string]any) + name, _ := meta["name"].(string) + + for _, field := range []string{"initContainers", "containers"} { + list, _ := podSpec[field].([]any) + for _, entry := range list { + container, _ := entry.(map[string]any) + containerName, _ := container["name"].(string) + resources, _ := container["resources"].(map[string]any) + requests, _ := resources["requests"].(map[string]any) + limits, _ := resources["limits"].(map[string]any) + + rc := renderedContainer{ + workload: name, + container: containerName, + requests: requests, + limits: limits, + } + found[rc.key()] = rc + } + } + } + return found +} + +// TestEveryInstalledContainerHasAnOverridableResourcesKnob is the guard that +// keeps requests/limits overridable for everything this chart installs. It +// asserts both directions: +// +// - every knob in resourceKnobs reaches its container (no template may +// hardcode resources or read the wrong values key), and +// - every container the chart renders appears in resourceKnobs (adding a +// container without a values key fails here, rather than in a customer's +// quota-constrained namespace). +func TestEveryInstalledContainerHasAnOverridableResourcesKnob(t *testing.T) { + args := []string{ + "--namespace", "devzero-system", + "--set", "global.k8sProvider=aws", + } + for _, knob := range resourceKnobs { + args = append(args, "--set-json", fmt.Sprintf( + `%s={"requests":{"cpu":%q,"memory":"64Mi"},"limits":{"cpu":%q,"memory":"128Mi"}}`, + knob.valuesKey, knob.cpu, knob.cpu, + )) + } + + rendered := collectContainers(t, renderChart(t, args...)) + if len(rendered) == 0 { + t.Fatal("chart rendered no containers; the render arguments are stale") + } + + byContainer := map[string]string{} + for _, knob := range resourceKnobs { + byContainer[knob.container] = knob.cpu + } + + seen := map[string]bool{} + var unknown []string + for key, container := range rendered { + wantCPU, ok := byContainer[container.container] + if !ok { + unknown = append(unknown, key) + continue + } + seen[container.container] = true + + if got := container.requests["cpu"]; got != wantCPU { + t.Errorf("%s: cpu request is %v, want %q -- the template is not reading its values key", + key, got, wantCPU) + } + if got := container.limits["cpu"]; got != wantCPU { + t.Errorf("%s: cpu limit is %v, want %q -- the template drops limits", + key, got, wantCPU) + } + } + + for _, knob := range resourceKnobs { + if !seen[knob.container] { + t.Errorf("container %q did not render; resourceKnobs is stale", knob.container) + } + } + + sort.Strings(unknown) + if len(unknown) > 0 { + t.Errorf("containers render with no entry in resourceKnobs: %v\n"+ + "Give each one a values key for its resources and add it to the table. "+ + "A container with hardcoded resources cannot be sized by an operator, "+ + "and one with none at all is rejected outright by a namespace "+ + "ResourceQuota that requires requests.", unknown) + } +} + +// TestChartDefaultsGiveEveryContainerResourceRequests guards the default +// render: a fresh install with no overrides must still request CPU and memory +// for every container, so `helm install` succeeds in a namespace whose +// ResourceQuota requires requests. Both the split (GPU-node) and non-split +// layouts are covered, on a provider that takes the host-driver path and one +// that does not. +func TestChartDefaultsGiveEveryContainerResourceRequests(t *testing.T) { + for _, args := range [][]string{ + // Split layout: base DaemonSet plus the -gpu one. + {"--namespace", "devzero-system", "--set", "global.k8sProvider=aws"}, + // Non-split layout, host-mounted driver path. + { + "--namespace", "devzero-system", + "--set", "global.k8sProvider=gcp", + "--set", "dcgmExporter.runtimeClassName=", + }, + } { + rendered := collectContainers(t, renderChart(t, args...)) + if len(rendered) == 0 { + t.Fatalf("chart rendered no containers for %v", args) + } + for key, container := range rendered { + if container.requests["cpu"] == nil { + t.Errorf("%s has no default cpu request (rendered with %v)", key, args) + } + if container.requests["memory"] == nil { + t.Errorf("%s has no default memory request (rendered with %v)", key, args) + } + } + } +} diff --git a/helm-chart/zxporter-nodemon/templates/_helpers.tpl b/helm-chart/zxporter-nodemon/templates/_helpers.tpl index f7d3253a..88795cb7 100644 --- a/helm-chart/zxporter-nodemon/templates/_helpers.tpl +++ b/helm-chart/zxporter-nodemon/templates/_helpers.tpl @@ -5,6 +5,36 @@ Expand the name of the chart. {{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }} {{- end }} +{{/* +An image reference built from an image values block, +(dict "image" "defaultTag" "global" .Values.global), +with its registry host swapped for global.imageRegistry when the dz-platform +umbrella sets it. With the global unset this is exactly the reference the +chart always rendered. +*/}} +{{- define "zxporter-nodemon.image" -}} +{{- /* Each key renders as the chart always printed it: null as empty, any + other value (0 or false included) as its string. Only a block that had a + fallback tag (the nodemon image, defaulting to the appVersion) keeps + `default`, which also replaces 0 and false, as it always did. */ -}} +{{- $repository := .image.repository -}} +{{- $tag := .image.tag -}} +{{- if hasKey . "defaultTag" -}} +{{- $tag = $tag | default .defaultTag -}} +{{- end -}} +{{- $ref := printf "%s:%s" (ternary "" (toString $repository) (kindIs "invalid" $repository)) (ternary "" (toString $tag) (kindIs "invalid" $tag)) -}} +{{- include "devzero-common.imageRef" (dict "ref" $ref "global" .global) -}} +{{- end }} + +{{/* +The pod's imagePullSecrets block: global.imagePullSecrets merged with the +chart's own imagePullSecrets, names or {name: ...} objects, deduplicated. +Empty, and so rendering nothing, when neither is set. +*/}} +{{- define "zxporter-nodemon.imagePullSecrets" -}} +{{- include "devzero-common.imagePullSecrets" (dict "Values" (dict "global" .Values.global "image" (dict "pullSecrets" .Values.imagePullSecrets))) -}} +{{- end }} + {{/* Create chart name and version as used by the chart label. */}} @@ -61,6 +91,9 @@ helm.sh/chart: {{ include "zxporter-nodemon.chart" . }} app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} {{- end }} app.kubernetes.io/managed-by: {{ .Release.Service }} +{{- with (include "zxporter-nodemon.commonLabels" .) }} +{{ . }} +{{- end }} {{- end }} {{/* @@ -92,3 +125,93 @@ Validate required configuration. {{- fail (printf "ERROR: global.gpuRuntime.runtimeClassName is required when global.gpuRuntime.mode is %s." $gpuRuntimeMode) -}} {{- end -}} {{- end }} + +{{/* +Site-wide labels for object metadata. + +Clusters running an admission policy engine (OPA Gatekeeper, Kyverno) often +require site-specific metadata on every object before they will admit it. Every +key the chart owns is dropped from the user-supplied values, so a collision can +neither rewrite chart state nor emit a duplicate YAML key. + +`global.commonLabels` is the knob that reaches subcharts; a chart-local +`commonLabels` applies only to the chart it is set on and wins on a collision. + +Every object also carries devzero-common's platform labels, +app.kubernetes.io/part-of and devzero.io/component, so all DevZero charts can +be selected the same way. A user who already set either key through +commonLabels keeps their value. Pod templates do not get them: those labels +come from podExtraLabels, and selectors never change. +*/}} +{{- define "zxporter-nodemon.commonLabels" -}} +{{- $extra := deepCopy (.Values.commonLabels | default dict) -}} +{{- if .Values.global }}{{- with .Values.global.commonLabels }}{{- $extra = merge $extra (deepCopy .) }}{{- end }}{{- end }} +{{- /* Completeness of this list is enforced by the "no chart-emitted key can + produce a duplicate" assertion in helm-chart/internal/charttest, which + derives the real set from a render rather than restating it here. */}} +{{- range $key := list "helm.sh/chart" "app.kubernetes.io/name" "app.kubernetes.io/instance" "app.kubernetes.io/version" "app.kubernetes.io/managed-by" "app.kubernetes.io/component" "app" "control-plane" }} +{{- $_ := unset $extra $key }} +{{- end }} +{{- toYaml (merge $extra (include "zxporter-nodemon.platformLabels" . | fromYaml)) }} +{{- end }} + +{{/* +devzero-common's platform labels: part-of and component only. The chart keeps +its own name, instance, version, managed-by and helm.sh/chart labels, and its +own selectors, so nothing that selects on them changes. +*/}} +{{- define "zxporter-nodemon.platformLabels" -}} +{{- toYaml (pick (include "devzero-common.labels" . | fromYaml) "app.kubernetes.io/part-of" "devzero.io/component") }} +{{- end }} + +{{/* +Site-wide annotations for object metadata. +*/}} +{{- define "zxporter-nodemon.commonAnnotations" -}} +{{- $extra := deepCopy (.Values.commonAnnotations | default dict) -}} +{{- if .Values.global }}{{- with .Values.global.commonAnnotations }}{{- $extra = merge $extra (deepCopy .) }}{{- end }}{{- end }} +{{- range $key := list "helm.sh/hook" "helm.sh/hook-weight" "helm.sh/hook-delete-policy" "kubectl.kubernetes.io/default-container" "devzero.io/gpu-runtime-mode" "devzero.io/gpu-runtime-class" "ignore-check.kube-linter.io/privileged-container" "ignore-check.kube-linter.io/run-as-non-root" "ignore-check.kube-linter.io/privilege-escalation-container" "ignore-check.kube-linter.io/no-read-only-root-fs" }} +{{- $_ := unset $extra $key }} +{{- end }} +{{- with $extra }} +{{- toYaml . }} +{{- end }} +{{- end }} + +{{/* +Pod-template labels: commonLabels plus podLabels, podLabels winning. + +Gatekeeper's expand-workloads (and Kyverno's equivalent) evaluates constraints +against the Pod a workload would create, so site labels have to reach the pod +template and not just the owning object. Keys the pod templates set themselves +are dropped: several of them are selector labels, which are immutable after +creation. +*/}} +{{- define "zxporter-nodemon.podExtraLabels" -}} +{{- $extra := deepCopy (.Values.podLabels | default dict) -}} +{{- if .Values.global }}{{- with .Values.global.podLabels }}{{- $extra = merge $extra (deepCopy .) }}{{- end }}{{- end }} +{{- $extra = merge $extra (deepCopy (.Values.commonLabels | default dict)) -}} +{{- if .Values.global }}{{- with .Values.global.commonLabels }}{{- $extra = merge $extra (deepCopy .) }}{{- end }}{{- end }} +{{- range $key := list "helm.sh/chart" "app.kubernetes.io/name" "app.kubernetes.io/instance" "app.kubernetes.io/version" "app.kubernetes.io/managed-by" "app.kubernetes.io/component" "app" "control-plane" }} +{{- $_ := unset $extra $key }} +{{- end }} +{{- with $extra }} +{{- toYaml . }} +{{- end }} +{{- end }} + +{{/* +Pod-template annotations: commonAnnotations plus podAnnotations. +*/}} +{{- define "zxporter-nodemon.podExtraAnnotations" -}} +{{- $extra := deepCopy (.Values.podAnnotations | default dict) -}} +{{- if .Values.global }}{{- with .Values.global.podAnnotations }}{{- $extra = merge $extra (deepCopy .) }}{{- end }}{{- end }} +{{- $extra = merge $extra (deepCopy (.Values.commonAnnotations | default dict)) -}} +{{- if .Values.global }}{{- with .Values.global.commonAnnotations }}{{- $extra = merge $extra (deepCopy .) }}{{- end }}{{- end }} +{{- range $key := list "helm.sh/hook" "helm.sh/hook-weight" "helm.sh/hook-delete-policy" "kubectl.kubernetes.io/default-container" "devzero.io/gpu-runtime-mode" "devzero.io/gpu-runtime-class" "ignore-check.kube-linter.io/privileged-container" "ignore-check.kube-linter.io/run-as-non-root" "ignore-check.kube-linter.io/privilege-escalation-container" "ignore-check.kube-linter.io/no-read-only-root-fs" }} +{{- $_ := unset $extra $key }} +{{- end }} +{{- with $extra }} +{{- toYaml . }} +{{- end }} +{{- end }} diff --git a/helm-chart/zxporter-nodemon/templates/daemonset.yaml b/helm-chart/zxporter-nodemon/templates/daemonset.yaml index ebd24d79..db5a3681 100644 --- a/helm-chart/zxporter-nodemon/templates/daemonset.yaml +++ b/helm-chart/zxporter-nodemon/templates/daemonset.yaml @@ -41,6 +41,9 @@ metadata: name: {{ $v.name }} namespace: {{ $.Release.Namespace }} labels: + {{- with (include "zxporter-nodemon.commonLabels" $) }} + {{- . | nindent 4 }} + {{- end }} helm.sh/chart: {{ include "zxporter-nodemon.chart" $ }} app.kubernetes.io/name: {{ include "zxporter-nodemon.name" $ }} app.kubernetes.io/instance: {{ $v.instance }} @@ -48,8 +51,16 @@ metadata: app.kubernetes.io/version: {{ $.Chart.AppVersion | quote }} {{- end }} app.kubernetes.io/managed-by: {{ $.Release.Service }} - {{- if $v.withDcgm }} + {{- /* The kube-linter and gpu-runtime annotations belong to the DCGM variant, + but commonAnnotations must reach this object either way: gating the + whole block on withDcgm dropped them from every non-GPU install. */}} + {{- $commonAnnotations := include "zxporter-nodemon.commonAnnotations" $ }} + {{- if or $v.withDcgm $commonAnnotations }} annotations: + {{- with $commonAnnotations }} + {{- . | nindent 4 }} + {{- end }} + {{- if $v.withDcgm }} ignore-check.kube-linter.io/privileged-container: "This daemon set needs to run DCGM Exporter as privileged to access the GPU metrics." ignore-check.kube-linter.io/run-as-non-root: "This daemon set needs to run DCGM Exporter as root to access the GPU metrics." ignore-check.kube-linter.io/privilege-escalation-container: "This daemon set needs escalate privileges for DCGM Exporter." @@ -58,6 +69,7 @@ metadata: devzero.io/gpu-runtime-mode: {{ $gpuMode | quote }} devzero.io/gpu-runtime-class: {{ $gpuRuntime | quote }} {{- end }} + {{- end }} {{- end }} spec: {{- with $.Values.updateStrategy }} @@ -70,7 +82,14 @@ spec: app.kubernetes.io/instance: {{ $v.instance }} template: metadata: + {{- with (include "zxporter-nodemon.podExtraAnnotations" $) }} + annotations: + {{- . | nindent 8 }} + {{- end }} labels: + {{- with (include "zxporter-nodemon.podExtraLabels" $) }} + {{- . | nindent 8 }} + {{- end }} app.kubernetes.io/name: {{ include "zxporter-nodemon.name" $ }} app.kubernetes.io/instance: {{ $v.instance }} spec: @@ -85,9 +104,8 @@ spec: {{- if $runtimeMetricsEnabled }} hostPID: true {{- end }} - {{- with $.Values.imagePullSecrets }} - imagePullSecrets: - {{- toYaml . | nindent 8 }} + {{- with include "zxporter-nodemon.imagePullSecrets" $ }} + {{- . | nindent 6 }} {{- end }} {{- if eq $v.gpuAff "include" }} {{- if ne $gpuRuntime "" }} @@ -169,10 +187,17 @@ spec: capabilities: add: - SYS_PTRACE + # Bottlerocket (SELinux enforcing) gives each container a unique MCS + # category pair, which denies reading other containers' files via + # /proc//root (JVM hsperfdata, runtime binaries). Granting the + # full category range keeps the container_t type; it is a no-op where + # SELinux is not enforcing. + seLinuxOptions: + level: "s0:c0.c1023" {{- else }} {{- toYaml $.Values.nodemon.securityContext | nindent 12 }} {{- end }} - image: "{{ $.Values.image.repository }}:{{ $.Values.image.tag | default $.Chart.AppVersion }}" + image: {{ include "zxporter-nodemon.image" (dict "image" $.Values.image "defaultTag" $.Chart.AppVersion "global" $.Values.global) | quote }} imagePullPolicy: {{ $.Values.image.pullPolicy }} ports: - name: http @@ -231,6 +256,14 @@ spec: {{- if $v.withDcgm }} - name: dcgm-exporter securityContext: + # SYS_ADMIN alone is not enough: enumerating MIG Compute Instance + # profiles via NVML needs broader host access than that one + # capability grants (confirmed live -- SYS_ADMIN-only reproduces + # "CacheManager Init Failed. Error: -17" the instant any MIG + # instance exists on the node; switching to privileged fixes it + # with zero other changes). This is a generic NVML/driver + # requirement, not provider-specific, so unlike the hostPath/ + # LD_LIBRARY_PATH blocks below it must not be gated on gcp only. capabilities: add: - SYS_ADMIN @@ -238,10 +271,8 @@ spec: - NET_RAW runAsNonRoot: false runAsUser: 0 - {{- if eq $.Values.global.k8sProvider "gcp"}} privileged: true - {{- end }} - image: "{{ $.Values.dcgmExporter.image.repository }}:{{ $.Values.dcgmExporter.image.tag }}" + image: {{ include "zxporter-nodemon.image" (dict "image" $.Values.dcgmExporter.image "global" $.Values.global) | quote }} imagePullPolicy: {{ $.Values.dcgmExporter.image.pullPolicy }} command: [ "/bin/bash", "-c" ] args: @@ -270,6 +301,24 @@ spec: fieldRef: fieldPath: status.hostIP {{- end }} + {{- $hasCustomVisibleDevices := false }} + {{- range $.Values.dcgmExporter.extraEnv }} + {{- if eq .name "NVIDIA_VISIBLE_DEVICES" }} + {{- $hasCustomVisibleDevices = true }} + {{- end }} + {{- end }} + {{- if not $hasCustomVisibleDevices }} + # Without this, nvidia-container-runtime's "auto" mode only exposes + # whole/non-MIG GPUs to a non-privileged container with no explicit + # device-plugin resource request (which this sidecar deliberately + # doesn't make, since it must see every GPU on the node, not just + # one). MIG-partitioned GPUs stay invisible without it -- confirmed + # by direct comparison against GPU Operator's own dcgm-exporter, + # which gets the same visibility via privileged:true instead; this + # is the minimal fix that doesn't need the broader grant. + - name: "NVIDIA_VISIBLE_DEVICES" + value: "all" + {{- end }} {{- $hasCustomLDLibraryPath := false }} {{- range $.Values.dcgmExporter.extraEnv }} {{- if eq .name "LD_LIBRARY_PATH" }} @@ -297,5 +346,9 @@ spec: {{- end }} - name: {{- include "dcgm-exporter.config-map" $ | indent 1 }} mountPath: "/etc/dcgm-exporter" + {{- with $.Values.dcgmExporter.resources }} + resources: + {{- toYaml . | nindent 12 }} + {{- end }} {{- end }} {{- end }} diff --git a/helm-chart/zxporter-nodemon/templates/dcgm-exporter-configmap.yaml b/helm-chart/zxporter-nodemon/templates/dcgm-exporter-configmap.yaml index 4f74bec7..88486682 100644 --- a/helm-chart/zxporter-nodemon/templates/dcgm-exporter-configmap.yaml +++ b/helm-chart/zxporter-nodemon/templates/dcgm-exporter-configmap.yaml @@ -2,6 +2,14 @@ apiVersion: v1 kind: ConfigMap metadata: + {{- with (include "zxporter-nodemon.commonLabels" $) }} + labels: + {{- . | nindent 4 }} + {{- end }} + {{- with (include "zxporter-nodemon.commonAnnotations" $) }} + annotations: + {{- . | nindent 4 }} + {{- end }} name: {{- include "dcgm-exporter.config-map" . | indent 1 }} namespace: {{ .Release.Namespace }} data: @@ -17,8 +25,12 @@ data: DCGM_FI_DEV_GPU_UTIL, gauge, GPU utilization (in %). # DCGM_FI_PROF_SM_ACTIVE, gauge, The ratio of cycles an SM has at least 1 warp assigned # DCGM_FI_PROF_SM_OCCUPANCY, gauge, The fraction of resident warps on a multiprocessor - # DCGM_FI_PROF_GR_ENGINE_ACTIVE, gauge, Ratio of time the graphics engine is active (in %). - # DCGM_FI_PROF_DRAM_ACTIVE, gauge, The ratio of cycles the device memory interface is active sending or receiving data. + # Enabled: required for per-MIG-instance profiling data (GPU_I_PROFILE/GPU_I_ID + # labels only attach to DCGM_FI_PROF_* fields, never to plain DCGM_FI_DEV_* + # ones) and directly consumed by nodemon's GPUMigInstance parsing + # (GraphicsEngineActive/DRAMActive/TensorActive below). + DCGM_FI_PROF_GR_ENGINE_ACTIVE, gauge, Ratio of time the graphics engine is active (in %). + DCGM_FI_PROF_DRAM_ACTIVE, gauge, The ratio of cycles the device memory interface is active sending or receiving data. # Memory usage,, DCGM_FI_DEV_FB_FREE, gauge, Framebuffer memory free (in MiB). @@ -37,7 +49,9 @@ data: # DCGM_FI_PROF_PIPE_FP16_ACTIVE, gauge, Ratio of cycles the fp16 pipe is active. # DCGM_FI_PROF_PIPE_FP32_ACTIVE, gauge, Ratio of cycles the fp32 pipe is active. # DCGM_FI_PROF_PIPE_FP64_ACTIVE, gauge, Ratio of cycles the fp64 pipe is active. - # DCGM_FI_PROF_PIPE_TENSOR_ACTIVE, gauge, The ratio of cycles the tensor (HMMA) pipe is active (off the peak sustained elapsed cycles) + # Enabled: see the GR_ENGINE_ACTIVE/DRAM_ACTIVE note above -- this is + # GPUMigInstance.TensorActive. + DCGM_FI_PROF_PIPE_TENSOR_ACTIVE, gauge, The ratio of cycles the tensor (HMMA) pipe is active (off the peak sustained elapsed cycles) # Health,, # DCGM_FI_DEV_CLOCKS_EVENT_REASONS is not supported by DCGM 3.3.7 diff --git a/helm-chart/zxporter-nodemon/templates/nodemon-configmap.yaml b/helm-chart/zxporter-nodemon/templates/nodemon-configmap.yaml index e676b956..b86c99fd 100644 --- a/helm-chart/zxporter-nodemon/templates/nodemon-configmap.yaml +++ b/helm-chart/zxporter-nodemon/templates/nodemon-configmap.yaml @@ -1,6 +1,14 @@ apiVersion: v1 kind: ConfigMap metadata: + {{- with (include "zxporter-nodemon.commonLabels" $) }} + labels: + {{- . | nindent 4 }} + {{- end }} + {{- with (include "zxporter-nodemon.commonAnnotations" $) }} + annotations: + {{- . | nindent 4 }} + {{- end }} name: {{- include "zxporter-nodemon.config-map" . | indent 1}} namespace: {{ .Release.Namespace }} data: diff --git a/helm-chart/zxporter-nodemon/templates/priorityclass.yaml b/helm-chart/zxporter-nodemon/templates/priorityclass.yaml index 552778cc..8a789038 100644 --- a/helm-chart/zxporter-nodemon/templates/priorityclass.yaml +++ b/helm-chart/zxporter-nodemon/templates/priorityclass.yaml @@ -2,6 +2,10 @@ apiVersion: scheduling.k8s.io/v1 kind: PriorityClass metadata: + {{- with (include "zxporter-nodemon.commonAnnotations" $) }} + annotations: + {{- . | nindent 4 }} + {{- end }} {{- /* Hardcoded (not release-derived) to match the same-named PriorityClass the parent zxporter chart and the kustomize dist/ bundles create, so a standalone nodemon install (CI, helm-release-nodemon) targets the SAME class the parent would. When installed via diff --git a/helm-chart/zxporter-nodemon/templates/rbac.yaml b/helm-chart/zxporter-nodemon/templates/rbac.yaml index 2bc642ec..4c5744d0 100644 --- a/helm-chart/zxporter-nodemon/templates/rbac.yaml +++ b/helm-chart/zxporter-nodemon/templates/rbac.yaml @@ -1,7 +1,3 @@ -{{- $runtimeMetricsEnabled := .Values.jvmMetrics.enabled }} -{{- if hasKey (.Values.runtimeMetrics | default dict) "enabled" }} -{{- $runtimeMetricsEnabled = .Values.runtimeMetrics.enabled }} -{{- end }} {{- if .Values.serviceAccount.create -}} apiVersion: v1 kind: ServiceAccount @@ -10,7 +6,12 @@ metadata: namespace: {{ .Release.Namespace }} labels: {{- include "zxporter-nodemon.labels" . | nindent 4 }} - {{- with .Values.serviceAccount.annotations }} + {{- /* serviceAccount.annotations wins over commonAnnotations on a key + collision. Merged here rather than emitted as two blocks because + `with` rebinds the dot, which would hand the helper the annotation + map instead of the root context. */}} + {{- $saAnnotations := merge (deepCopy (.Values.serviceAccount.annotations | default dict)) (fromYaml (include "zxporter-nodemon.commonAnnotations" $)) }} + {{- with $saAnnotations }} annotations: {{- toYaml . | nindent 4 }} {{- end }} @@ -19,6 +20,10 @@ metadata: apiVersion: rbac.authorization.k8s.io/v1 kind: {{ if .Values.nodemon.rbac.clusterWide }}ClusterRole{{ else }}Role{{ end }} metadata: + {{- with (include "zxporter-nodemon.commonAnnotations" $) }} + annotations: + {{- . | nindent 4 }} + {{- end }} name: {{ include "zxporter-nodemon.fullname" . }} labels: {{- include "zxporter-nodemon.labels" . | nindent 4 }} @@ -31,11 +36,12 @@ rules: resources: - pods verbs: + {{- /* watch is unconditional: the node-scoped pod informer (PodContainerIndex) + needs list+watch whenever cgroupMetrics OR runtimeMetrics is on, and + watch exposes nothing list does not. */}} - get - list - {{- if $runtimeMetricsEnabled }} - watch - {{- end }} - apiGroups: - apps resources: @@ -70,6 +76,10 @@ rules: apiVersion: rbac.authorization.k8s.io/v1 kind: {{ if .Values.nodemon.rbac.clusterWide }}ClusterRoleBinding{{ else }}RoleBinding{{ end }} metadata: + {{- with (include "zxporter-nodemon.commonAnnotations" $) }} + annotations: + {{- . | nindent 4 }} + {{- end }} name: {{ include "zxporter-nodemon.fullname" . }} labels: {{- include "zxporter-nodemon.labels" . | nindent 4 }} diff --git a/helm-chart/zxporter-nodemon/values.schema.json b/helm-chart/zxporter-nodemon/values.schema.json new file mode 100644 index 00000000..56e2b55f --- /dev/null +++ b/helm-chart/zxporter-nodemon/values.schema.json @@ -0,0 +1,219 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "zxporter-nodemon chart values", + "description": "Permissive by design: unknown keys are allowed and scalars stay untyped, because installers pass --set strings and releases upgrade with reused values, so every value a release already carries must keep validating. A map or list is typed only where every other value already failed to render. required lists only the keys whose absence (--set key=null) failed to render in every configuration before this schema existed.", + "type": "object", + "properties": { + "commonLabels": { + "properties": {} + }, + "commonAnnotations": { + "properties": {} + }, + "podLabels": { + "properties": {} + }, + "podAnnotations": { + "properties": {} + }, + "nameOverride": {}, + "fullnameOverride": {}, + "imagePullSecrets": {}, + "serviceAccount": { + "type": [ + "object", + "null" + ], + "properties": { + "create": {}, + "annotations": { + "properties": {} + } + } + }, + "image": { + "type": [ + "object", + "null" + ], + "properties": { + "repository": {}, + "pullPolicy": {}, + "tag": {} + } + }, + "nodemon": { + "type": [ + "object", + "null" + ], + "properties": { + "affinity": {}, + "config": { + "properties": {} + }, + "goMaxProcs": {}, + "resources": { + "properties": { + "limits": { + "properties": { + "memory": {} + } + }, + "requests": { + "properties": { + "cpu": {}, + "memory": {} + } + } + } + }, + "securityContext": { + "properties": { + "readOnlyRootFilesystem": {}, + "runAsNonRoot": {} + } + }, + "port": {}, + "tolerations": {}, + "rbac": { + "type": [ + "object", + "null" + ], + "properties": { + "clusterWide": {} + } + } + }, + "required": [ + "rbac" + ] + }, + "runtimeMetrics": { + "properties": {} + }, + "jvmMetrics": { + "type": [ + "object", + "null" + ], + "properties": { + "enabled": {} + } + }, + "cgroupMetrics": { + "properties": { + "enabled": {} + } + }, + "global": { + "type": [ + "object", + "null" + ], + "properties": { + "k8sProvider": {}, + "gpuRuntime": { + "type": [ + "object", + "null" + ], + "properties": { + "mode": {}, + "runtimeClassName": {} + } + }, + "priorityClass": { + "type": [ + "object", + "null" + ], + "properties": { + "enabled": {} + } + } + }, + "required": [ + "k8sProvider", + "gpuRuntime", + "priorityClass" + ] + }, + "priorityClass": { + "type": [ + "object", + "null" + ], + "properties": { + "create": {}, + "value": {}, + "description": {}, + "preemptionPolicy": {} + } + }, + "dcgmExporter": { + "type": [ + "object", + "null" + ], + "properties": { + "enabled": {}, + "runtimeClassName": {}, + "gpuNodeSelector": { + "type": [ + "object", + "null" + ], + "properties": { + "key": {}, + "value": {} + } + }, + "image": { + "type": [ + "object", + "null" + ], + "properties": { + "repository": {}, + "pullPolicy": {}, + "tag": {} + } + }, + "resources": { + "properties": { + "requests": { + "properties": { + "cpu": {}, + "memory": {} + } + } + } + }, + "useExternalHostEngine": {}, + "extraEnv": { + "type": [ + "array", + "null" + ] + } + }, + "required": [ + "gpuNodeSelector" + ] + }, + "updateStrategy": { + "properties": {} + } + }, + "required": [ + "serviceAccount", + "image", + "nodemon", + "jvmMetrics", + "global", + "priorityClass", + "dcgmExporter" + ] +} diff --git a/helm-chart/zxporter-nodemon/values.yaml b/helm-chart/zxporter-nodemon/values.yaml index 745c7637..6f5254e5 100644 --- a/helm-chart/zxporter-nodemon/values.yaml +++ b/helm-chart/zxporter-nodemon/values.yaml @@ -1,3 +1,28 @@ +# Labels and annotations added to every object this chart creates, for clusters +# whose admission policy (OPA Gatekeeper, Kyverno) requires site-specific +# metadata before it will admit a resource. Without these, such a cluster +# rejects the chart's objects and the install fails. +# +# commonLabels: +# example.com/cost-center: platform +# example.com/tier: infra +# +# commonLabels/commonAnnotations land on both the object and its pod template -- +# Gatekeeper evaluates constraints against the Pod a workload would create, so +# metadata on the DaemonSet alone is not enough. podLabels/podAnnotations add +# pod-only metadata on top. +# +# Set these under `global:` to cover subcharts as well; a chart-local +# value applies only to this chart and wins on a collision. +# +# Keys this chart owns win over these and are dropped from the values: +# app.kubernetes.io/*, helm.sh/*, selector labels, and config checksums. +# Selector labels are never modified, so setting these on an existing release is +# a safe upgrade. +commonLabels: {} +commonAnnotations: {} +podLabels: {} +podAnnotations: {} # fullnameOverride pins all nodemon resource names to a stable value, independent of the Helm # release name. It MUST stay "zxporter-nodemon" so that: # 1. the zxporter controller can discover nodemon pods via the fixed label selector @@ -6,6 +31,8 @@ # after a Helm install updates the existing resources instead of creating duplicates. nameOverride: "" fullnameOverride: "zxporter-nodemon" +# -- Pull secrets for the nodemon pods, merged with `global.imagePullSecrets` +# and deduplicated. Entries are secret names or `{name: ...}` objects. imagePullSecrets: [] serviceAccount: create: true @@ -13,7 +40,9 @@ serviceAccount: image: repository: public.ecr.aws/devzeroinc/zxporter-nodemon pullPolicy: IfNotPresent - tag: "v0.1.6" + # -- Image tag. The release sets it to the version being released. + # @default -- the released version, v-prefixed + tag: "v0.1.25" nodemon: # nodemon.affinity -- optional pod affinity/anti-affinity. Leave empty so nodemon schedules on # EVERY node (it is the sole source of node/container metrics). Only set this if you intentionally @@ -53,7 +82,10 @@ nodemon: clusterWide: true # runtimeMetrics -- Enable process-introspection metrics (JVM heap via hsperfdata, # Node.js detection/version). NOTE: requires hostPID + running nodemon as UID 0 + -# SYS_PTRACE to read other containers' /proc//root overlay. +# SYS_PTRACE to read other containers' /proc//root overlay. It also sets +# seLinuxOptions.level "s0:c0.c1023" so SELinux-enforcing nodes (Bottlerocket), +# which isolate containers by MCS category, do not block that read; the type +# stays container_t, and the level is ignored where SELinux is not enforcing. # Leave unset to fall back to the deprecated jvmMetrics.enabled below. # # scanHandlerTimeoutMs -- bounds how long the scan-heavy /container/jvm-metrics @@ -126,6 +158,23 @@ dcgmExporter: repository: public.ecr.aws/devzeroinc/dcgm-exporter pullPolicy: IfNotPresent tag: 4.5.2-4.8.1-ubuntu22.04 + # resources -- requests/limits for the dcgm-exporter sidecar. This block + # replaces the container's whole `resources` field, so set requests and + # limits together; set it to null to render no resources block at all. + # + # Requests are set by default because a namespace ResourceQuota, or a + # LimitRange with no default, rejects the entire pod when any one container + # omits them -- one unsized sidecar would take the whole DaemonSet down. + # + # No memory limit by default, unlike nodemon.resources above: this sidecar's + # footprint grows with the number of GPUs and MIG compute instances on the + # node, so a fleet-wide limit picked here would OOMKill exactly the dense + # nodes it matters most on. Set one explicitly if your namespace quota + # requires limits, sizing it against your densest GPU node. + resources: + requests: + cpu: 50m + memory: 128Mi useExternalHostEngine: false # extraEnv -- extra env vars for the dcgm-exporter sidecar, e.g. LD_LIBRARY_PATH # when the driver lives outside the provider=gcp default (/usr/local/nvidia/lib64). diff --git a/helm-chart/zxporter/.helmignore b/helm-chart/zxporter/.helmignore new file mode 100644 index 00000000..e372ec71 --- /dev/null +++ b/helm-chart/zxporter/.helmignore @@ -0,0 +1,14 @@ +# Patterns to ignore when building packages. +.DS_Store +.git/ +.gitignore +*.swp +*.bak +*.tmp +*.orig +*~ +.idea/ +.vscode/ +# Chart tests run with go test; they are not part of the package. +*_test.go +README.md.gotmpl diff --git a/helm-chart/zxporter/Chart.lock b/helm-chart/zxporter/Chart.lock index b085bfc7..883d551b 100644 --- a/helm-chart/zxporter/Chart.lock +++ b/helm-chart/zxporter/Chart.lock @@ -1,6 +1,9 @@ dependencies: +- name: devzero-common + repository: file://../devzero-common + version: 0.1.0 - name: zxporter-nodemon repository: file://../zxporter-nodemon - version: 0.1.6 -digest: sha256:6d5a88673e5b4997a8f2e75733aa6fce062b81bff4110d2e0042a343e1e12f27 -generated: "2026-08-04T08:42:16.511514311Z" + version: 0.1.25 +digest: sha256:6e10c82910aa35cf6952bdc794a79a6dc5b06edaf3a35bb4a0a4a73701fa919d +generated: "2026-10-15T21:47:36.007601808Z" diff --git a/helm-chart/zxporter/Chart.yaml b/helm-chart/zxporter/Chart.yaml index 9fd79ae6..6ee0215c 100644 --- a/helm-chart/zxporter/Chart.yaml +++ b/helm-chart/zxporter/Chart.yaml @@ -1,8 +1,9 @@ apiVersion: v2 name: zxporter description: A Helm chart for DevZero ZXPorter - Kubernetes resource monitoring and optimization -version: 0.1.6 -appVersion: "0.1.6" +type: application +version: 0.1.25 +appVersion: "0.1.25" home: https://github.com/devzero-inc/zxporter sources: - https://github.com/devzero-inc/zxporter @@ -16,6 +17,12 @@ keywords: - resource-optimization - devzero dependencies: + # Shared DevZero chart conventions (labels). A byte-identical copy of the + # monorepo's charts/devzero-common, kept here so the public zxporter repo + # carries it; scripts/chart_lint.py fails CI if the copy drifts. + - name: devzero-common + version: 0.1.0 + repository: "file://../devzero-common" - name: zxporter-nodemon - version: "0.1.6" + version: "0.1.25" repository: "file://../zxporter-nodemon" diff --git a/helm-chart/zxporter/README.md b/helm-chart/zxporter/README.md new file mode 100644 index 00000000..73b2e77c --- /dev/null +++ b/helm-chart/zxporter/README.md @@ -0,0 +1,91 @@ +# zxporter + +![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) + +A Helm chart for DevZero ZXPorter - Kubernetes resource monitoring and optimization + +zxporter is the DevZero cluster agent. It watches the cluster's Kubernetes +resources and reports them to the DevZero control plane (dakr). It bundles the +`zxporter-nodemon` DaemonSet as a subchart; configure it under the +`zxporter-nodemon` key, or through `global.*` for values both charts share. + +Every object carries `app.kubernetes.io/part-of: dz-platform` and a +`devzero.io/component` label naming its chart, from the shared +`devzero-common` library. Pod templates and selectors do not carry them. + +To pull from a private registry, set `global.imageRegistry` (the `dz-platform` +umbrella sets it for every component). It replaces the registry host of the +controller image and keeps the path, tag and digest: +`public.ecr.aws/devzeroinc/zxporter:` becomes +`/devzeroinc/zxporter:`. The value may carry a +path prefix (`harbor.example/devzero`). `global.imagePullSecrets` is merged +with `imagePullSecrets` on the controller pod; both take names or +`{name: ...}` objects. + +The chart vendors its dependencies into `charts/`, which is not committed. +From a source checkout, run `make helm-deps` (or `helm dependency build` on +the chart) before installing or rendering it. + +## GitOps and reinstalls + +For GitOps or IaC installs, pass `zxporter.clusterToken` or `zxporter.existingSecret` so no Helm run can drop the token. With a PAT, zxporter re-attaches to its cluster by the kube-system namespace UID after a reinstall. Set `zxporter.clusterId` only to pin an install to a specific cluster. + +zxporter keeps its cluster token in the Secret `devzero-zxporter-token` (or the ConfigMap, with `useSecretForToken=false`). If that token is lost, for example after `helm uninstall`, zxporter registers again with the PAT and DevZero re-attaches the same cluster by its kube-system namespace UID. The token is rotated by that re-registration, and the other DevZero operators reload it from the Secret. `zxporter.clusterId` applies only at registration, when no token is stored; to move an existing install to another cluster, delete `devzero-zxporter-token` before restarting zxporter. + +This README is generated by [helm-docs](https://github.com/norwoodj/helm-docs) +from `values.yaml` and `README.md.gotmpl`; edit those and run +`just chart-docs` in the DevZero services monorepo instead of editing it. + +## Values + +| Key | Type | Default | Description | +|-----|------|---------|-------------| +| affinity | object | `{}` | | +| commonAnnotations | object | `{}` | | +| commonLabels | object | `{}` | | +| global.gpuRuntime.mode | string | `"auto"` | | +| global.gpuRuntime.runtimeClassName | string | `"nvidia"` | | +| global.k8sProvider | string | `""` | | +| global.priorityClass.enabled | bool | `true` | | +| highAvailability.enabled | bool | `true` | | +| highAvailability.podDisruptionBudget.enabled | bool | `true` | | +| highAvailability.podDisruptionBudget.minAvailable | int | `1` | | +| image.pullPolicy | string | `"IfNotPresent"` | | +| image.repository | string | `"public.ecr.aws/devzeroinc/zxporter"` | | +| image.tag | string | the released version, v-prefixed | Image tag. The release sets it to the version being released. | +| imagePullSecrets | list | `[]` | Pull secrets for the controller pod, merged with `global.imagePullSecrets` and deduplicated. Entries are secret names or `{name: ...}` objects. | +| mpaServer.port | int | `50051` | | +| nodeSelector | object | `{}` | | +| podAnnotations | object | `{}` | | +| podLabels | object | `{}` | | +| priorityClass.description | string | `"This priority class is for critical zxporter infrastructure components."` | | +| priorityClass.preemptionPolicy | string | `"PreemptLowerPriority"` | | +| priorityClass.value | int | `1000000000` | | +| resources.limits | object | `{}` | | +| resources.requests.cpu | string | `"200m"` | | +| resources.requests.memory | string | `"2Gi"` | | +| tolerations | list | `[]` | | +| topologySpreadConstraints | list | `[]` | | +| zxporter-nodemon.priorityClass.create | bool | `false` | | +| zxporter.clusterId | string | `""` | | +| zxporter.clusterToken | string | `""` | | +| zxporter.dakrUrl | string | `"https://dakr.devzero.io"` | | +| zxporter.disableGPUMetrics | string | `""` | | +| zxporter.existingSecret.clusterTokenKey | string | `"CLUSTER_TOKEN"` | | +| zxporter.existingSecret.name | string | `""` | | +| zxporter.existingSecret.patTokenKey | string | `"PAT_TOKEN"` | | +| zxporter.kedaMetricsEndpoint | string | `""` | | +| zxporter.kubeContextName | string | `""` | | +| zxporter.kubeletFallbackTimeout | string | `"15s"` | | +| zxporter.logLevel | string | `"error"` | | +| zxporter.migPartedConfigMapName | string | `""` | | +| zxporter.migPartedConfigMapNamespace | string | `""` | | +| zxporter.nodeMetricsConcurrency | int | `20` | | +| zxporter.nodemonRequestTimeout | string | `"15s"` | | +| zxporter.patToken | string | `""` | | +| zxporter.schedulerMetricsScrapeInterval | string | `""` | | +| zxporter.targetNamespaces | string | `""` | | +| zxporter.tokenConfigMapName | string | `"devzero-zxporter-env-config"` | | +| zxporter.tokenCredentialsSecretName | string | `"devzero-zxporter-credentials"` | | +| zxporter.tokenSecretName | string | `"devzero-zxporter-token"` | | +| zxporter.useSecretForToken | bool | `true` | | diff --git a/helm-chart/zxporter/README.md.gotmpl b/helm-chart/zxporter/README.md.gotmpl new file mode 100644 index 00000000..8852dde8 --- /dev/null +++ b/helm-chart/zxporter/README.md.gotmpl @@ -0,0 +1,42 @@ +{{ template "chart.header" . }} +{{ template "chart.deprecationWarning" . }} + +{{/* No version badges: the release bumps Chart.yaml without regenerating this README. */ -}} +{{ template "chart.typeBadge" . }} + +{{ template "chart.description" . }} + +zxporter is the DevZero cluster agent. It watches the cluster's Kubernetes +resources and reports them to the DevZero control plane (dakr). It bundles the +`zxporter-nodemon` DaemonSet as a subchart; configure it under the +`zxporter-nodemon` key, or through `global.*` for values both charts share. + +Every object carries `app.kubernetes.io/part-of: dz-platform` and a +`devzero.io/component` label naming its chart, from the shared +`devzero-common` library. Pod templates and selectors do not carry them. + +To pull from a private registry, set `global.imageRegistry` (the `dz-platform` +umbrella sets it for every component). It replaces the registry host of the +controller image and keeps the path, tag and digest: +`public.ecr.aws/devzeroinc/zxporter:` becomes +`/devzeroinc/zxporter:`. The value may carry a +path prefix (`harbor.example/devzero`). `global.imagePullSecrets` is merged +with `imagePullSecrets` on the controller pod; both take names or +`{name: ...}` objects. + +The chart vendors its dependencies into `charts/`, which is not committed. +From a source checkout, run `make helm-deps` (or `helm dependency build` on +the chart) before installing or rendering it. + +## GitOps and reinstalls + +For GitOps or IaC installs, pass `zxporter.clusterToken` or `zxporter.existingSecret` so no Helm run can drop the token. With a PAT, zxporter re-attaches to its cluster by the kube-system namespace UID after a reinstall. Set `zxporter.clusterId` only to pin an install to a specific cluster. + +zxporter keeps its cluster token in the Secret `devzero-zxporter-token` (or the ConfigMap, with `useSecretForToken=false`). If that token is lost, for example after `helm uninstall`, zxporter registers again with the PAT and DevZero re-attaches the same cluster by its kube-system namespace UID. The token is rotated by that re-registration, and the other DevZero operators reload it from the Secret. `zxporter.clusterId` applies only at registration, when no token is stored; to move an existing install to another cluster, delete `devzero-zxporter-token` before restarting zxporter. + +This README is generated by [helm-docs](https://github.com/norwoodj/helm-docs) +from `values.yaml` and `README.md.gotmpl`; edit those and run +`just chart-docs` in the DevZero services monorepo instead of editing it. + +{{/* No requirements table: the release bumps the zxporter-nodemon pin without regenerating this README. */ -}} +{{ template "chart.valuesSection" . }} diff --git a/helm-chart/zxporter/chart_common_metadata_test.go b/helm-chart/zxporter/chart_common_metadata_test.go new file mode 100644 index 00000000..b4d42f6f --- /dev/null +++ b/helm-chart/zxporter/chart_common_metadata_test.go @@ -0,0 +1,23 @@ +package zxporter_test + +import ( + "testing" + + "github.com/devzero-inc/zxporter/helm-chart/internal/charttest" +) + +// The zxporter chart pulls zxporter-nodemon in as a local subchart, so this +// render covers both. A chart-local commonLabels reaches only the chart it is +// set on; global.commonLabels is what covers the whole release, which the +// contract asserts separately. +func TestCommonMetadataContract(t *testing.T) { + charttest.RunContract(t, charttest.Spec{ + ChartDir: ".", + BaseArgs: []string{ + "--set", "global.k8sProvider=aws", + "--set", "zxporter.clusterToken=test-token", + "--set", "zxporter.kubeContextName=test-cluster", + }, + SubchartPrefix: "zxporter/charts/zxporter-nodemon/", + }) +} diff --git a/helm-chart/zxporter/chart_globals_test.go b/helm-chart/zxporter/chart_globals_test.go new file mode 100644 index 00000000..3664e66b --- /dev/null +++ b/helm-chart/zxporter/chart_globals_test.go @@ -0,0 +1,75 @@ +package zxporter_test + +import ( + "testing" + + "github.com/devzero-inc/zxporter/helm-chart/internal/charttest" +) + +// envConfig returns the data of the ConfigMap that carries KUBE_CONTEXT_NAME. +func envConfig(t *testing.T, args ...string) map[string]string { + t.Helper() + for _, obj := range charttest.Objects(t, charttest.Render(t, platformSpec, args...)) { + if obj.Doc["kind"] != "ConfigMap" { + continue + } + if data := charttest.StringMap(obj.Doc, "data"); data["KUBE_CONTEXT_NAME"] != "" { + return data + } + } + t.Fatal("no ConfigMap carries KUBE_CONTEXT_NAME") + return nil +} + +// The dz-platform umbrella sets the cluster's identity once, under global.*, +// and it wins over zxporter's own keys (whose dakrUrl defaults to production, +// so a chart-first rule would ignore the umbrella). A release that sets no +// global, or an empty one, renders exactly as before. +func TestGlobalClusterIdentity(t *testing.T) { + for _, tc := range []struct { + name string + args []string + context, dakr string + }{ + {"chart values without globals", nil, "test-cluster", "https://dakr.devzero.io"}, + {"globals win", charttest.Set("global.clusterName=umbrella", "global.dakrUrl=https://dakr.example"), "umbrella", "https://dakr.example"}, + {"empty globals keep the chart values", charttest.Set("global.clusterName=", "global.dakrUrl="), "test-cluster", "https://dakr.devzero.io"}, + {"global.clusterName stands in for kubeContextName", charttest.Set("zxporter.kubeContextName=", "global.clusterName=umbrella"), "umbrella", "https://dakr.devzero.io"}, + } { + t.Run(tc.name, func(t *testing.T) { + data := envConfig(t, tc.args...) + if data["KUBE_CONTEXT_NAME"] != tc.context || data["DAKR_URL"] != tc.dakr { + t.Errorf("KUBE_CONTEXT_NAME=%q DAKR_URL=%q, want %q %q", data["KUBE_CONTEXT_NAME"], data["DAKR_URL"], tc.context, tc.dakr) + } + }) + } + + _, err := charttest.TryRender(t, platformSpec, charttest.Set("zxporter.kubeContextName=", "global.clusterName=")...) + if err == nil { + t.Error("rendered with neither zxporter.kubeContextName nor global.clusterName") + } +} + +// A tag the chart always printed verbatim keeps rendering verbatim, including +// the scalars Helm's default would treat as empty. +func TestImageTagScalars(t *testing.T) { + for _, tc := range []struct{ tag, want string }{ + {"0", "public.ecr.aws/devzeroinc/zxporter:0"}, + {"false", "public.ecr.aws/devzeroinc/zxporter:false"}, + } { + for name, templates := range charttest.PodTemplates(charttest.Objects(t, charttest.Render(t, platformSpec, "--set", "image.tag="+tc.tag)), true) { + if got := charttest.Images(templates); len(got) != 1 || got[0] != tc.want { + t.Errorf("%s with image.tag=%s: images %v, want [%s]", name, tc.tag, got, tc.want) + } + } + } +} + +// The umbrella's global.imageRegistry and global.imagePullSecrets reach every +// pod this chart renders. The bundled nodemon is covered in its own package. +func TestImageGlobals(t *testing.T) { + charttest.AssertImageGlobals(t, platformSpec, charttest.ImageGlobals{ + PullSecretsKey: "imagePullSecrets", + OwnChartOnly: true, + }) +} diff --git a/helm-chart/zxporter/chart_platform_test.go b/helm-chart/zxporter/chart_platform_test.go new file mode 100644 index 00000000..1005b394 --- /dev/null +++ b/helm-chart/zxporter/chart_platform_test.go @@ -0,0 +1,125 @@ +package zxporter_test + +import ( + "reflect" + "strings" + "testing" + + "github.com/devzero-inc/zxporter/helm-chart/internal/charttest" +) + +// schemaError prefixes every values.schema.json violation, on helm 3 and 4. +const schemaError = "values don't meet the specifications of the schema" + +var platformSpec = charttest.Spec{ + ChartDir: ".", + BaseArgs: []string{ + "--set", "global.k8sProvider=aws", + "--set", "zxporter.clusterToken=test-token", + "--set", "zxporter.kubeContextName=test-cluster", + }, +} + +// The nodemon subchart's objects are attributed to zxporter-nodemon. +func TestPlatformLabels(t *testing.T) { + charttest.AssertPlatformLabels(t, platformSpec) + gcp := charttest.Set("priorityClass.create=true", "global.k8sProvider=gcp") + charttest.AssertPlatformLabels(t, platformSpec, gcp...) +} + +// Releases that already set either platform key through commonLabels keep +// their value: adopting the library adds labels and never rewrites one. +func TestUserCommonLabelsKeepTheirPlatformKeys(t *testing.T) { + objects := charttest.Objects(t, charttest.Render(t, platformSpec, + "--set", `commonLabels.app\.kubernetes\.io/part-of=mine`, + "--set", `global.commonLabels.devzero\.io/component=site`, + )) + for _, obj := range objects { + labels := charttest.StringMap(obj.Doc, "metadata", "labels") + desc := charttest.Describe(obj.Doc) + if want := "site"; labels[charttest.ComponentLabel] != want { + t.Errorf("%s: %s = %q, want %q", desc, charttest.ComponentLabel, labels[charttest.ComponentLabel], want) + } + want := "mine" + if obj.FromSubchart() { + want = charttest.PartOf // a chart-local commonLabels stays in its chart + } + if labels[charttest.PartOfLabel] != want { + t.Errorf("%s: %s = %q, want %q", desc, charttest.PartOfLabel, labels[charttest.PartOfLabel], want) + } + } +} + +// A null under zxporter-nodemon is not a rejection case here: helm 3 drops +// it and keeps the subchart default, helm 4 deletes the key. The subchart's +// own schema test covers its required keys. +func TestValuesSchema(t *testing.T) { + rejected := []struct { + name string + args []string + path string + }{ + {"image removed", []string{"--set", "image=null"}, "image"}, + {"image tag removed", []string{"--set", "image.tag=null"}, "tag"}, + {"cluster context removed", []string{"--set", "zxporter.kubeContextName=null"}, "kubeContextName"}, + {"provider removed", []string{"--set", "global.k8sProvider=null"}, "k8sProvider"}, + {"gpu runtime removed", []string{"--set", "global.gpuRuntime=null"}, "gpuRuntime"}, + {"priority class removed", []string{"--set", "priorityClass=null"}, "priorityClass"}, + {"mpa server removed", []string{"--set", "mpaServer=null"}, "mpaServer"}, + {"high availability removed", []string{"--set", "highAvailability=null"}, "highAvailability"}, + {"zxporter block as a string", []string{"--set", "zxporter=x"}, "zxporter"}, + } + for _, tc := range rejected { + t.Run("rejects "+tc.name, func(t *testing.T) { + _, err := charttest.TryRender(t, platformSpec, tc.args...) + if err == nil || !strings.Contains(err.Error(), schemaError) || + !strings.Contains(err.Error(), tc.path) { + t.Fatalf("want a schema error naming %q, got %v", tc.path, err) + } + }) + } + + accepted := []struct { + name string + args []string + }{ + {"numeric image tag", []string{"--set", "image.tag=123"}}, + {"pdb removed while high availability is off", charttest.Set( + "highAvailability.enabled=false", "highAvailability.podDisruptionBudget=null")}, + {"cluster token removed with an existing secret", charttest.Set( + "zxporter.clusterToken=null", "zxporter.existingSecret.name=s")}, + {"runtime class removed in default gpu mode", charttest.Set( + "global.gpuRuntime.mode=default", "global.gpuRuntime.runtimeClassName=null", + "zxporter-nodemon.global.gpuRuntime.mode=default")}, + {"unknown key", []string{"--set", "somethingNew=1"}}, + } + for _, tc := range accepted { + t.Run("accepts "+tc.name, func(t *testing.T) { + if _, err := charttest.TryRender(t, platformSpec, tc.args...); err != nil { + t.Fatal(err) + } + }) + } +} + +// Workload selectors are immutable and the Services route by theirs, so every +// one is pinned. The nodemon DaemonSets' instance label is their fullname, not +// the release name, which keeps them byte-identical to the dist bundles. +func TestSelectorsArePinned(t *testing.T) { + controller := `{"matchLabels":{"control-plane":"controller-manager"}}` + want := map[string]string{ + "PodDisruptionBudget/devzero-zxporter-devzero-zxporter-pdb": controller, + "Service/devzero-zxporter-controller-manager-mpa": `{"app.kubernetes.io/instance":"release",` + + `"app.kubernetes.io/name":"zxporter"}`, + "Service/devzero-zxporter-controller-manager-metrics-service": `{"control-plane":"controller-manager"}`, + "DaemonSet/zxporter-nodemon": `{"matchLabels":{"app.kubernetes.io/instance":"zxporter-nodemon",` + + `"app.kubernetes.io/name":"zxporter-nodemon"}}`, + "DaemonSet/zxporter-nodemon-gpu": `{"matchLabels":{"app.kubernetes.io/instance":"zxporter-nodemon-gpu",` + + `"app.kubernetes.io/name":"zxporter-nodemon"}}`, + "Deployment/devzero-zxporter-controller-manager": controller, + } + got := charttest.Selectors(t, charttest.Objects(t, charttest.Render(t, platformSpec))) + if !reflect.DeepEqual(got, want) { + t.Fatalf("selectors changed:\n got %v\nwant %v", got, want) + } +} diff --git a/helm-chart/zxporter/chart_token_identity_test.go b/helm-chart/zxporter/chart_token_identity_test.go new file mode 100644 index 00000000..92c7f7bc --- /dev/null +++ b/helm-chart/zxporter/chart_token_identity_test.go @@ -0,0 +1,124 @@ +package zxporter_test + +import ( + "strings" + "testing" + + "github.com/devzero-inc/zxporter/helm-chart/internal/charttest" +) + +var patSpec = charttest.Spec{ + ChartDir: ".", + BaseArgs: []string{ + "--set", "global.k8sProvider=aws", + "--set", "zxporter.patToken=dzu-test", + "--set", "zxporter.kubeContextName=test-cluster", + "--set", "zxporter.useSecretForToken=true", + }, +} + +// A bare data: renders as data: null, and Helm's three-way merge then wipes +// the token zxporter stored in this Secret (KUB-158). +func TestTokenSecretNeverRendersNullData(t *testing.T) { + for _, obj := range charttest.Objects(t, charttest.Render(t, patSpec)) { + if obj.Doc["kind"] != "Secret" { + continue + } + if v, ok := obj.Doc["data"]; ok && v == nil { + t.Errorf("%s renders data: null", charttest.Describe(obj.Doc)) + } + } +} + +func TestClusterIDReachesTheConfig(t *testing.T) { + if got := envConfig(t, "--set", "zxporter.clusterId=c-7")["CLUSTER_ID"]; got != "c-7" { + t.Fatalf("CLUSTER_ID=%q", got) + } +} + +func controllerConfigChecksum(t *testing.T, args ...string) string { + t.Helper() + for name, template := range charttest.PodTemplates(charttest.Objects(t, charttest.Render(t, patSpec, args...)), true) { + if !strings.Contains(name, "devzero-zxporter-controller-manager") { + continue + } + return charttest.StringMap(template, "metadata", "annotations")["checksum/config"] + } + t.Fatal("no controller-manager pod template rendered") + return "" +} + +// zxporter reads CLUSTER_ID and the rest of its config once at start, so a +// changed ConfigMap must roll the controller (KUB-158). +func TestControllerRollsWhenItsConfigMapChanges(t *testing.T) { + before := controllerConfigChecksum(t) + if before == "" { + t.Fatal("controller pod template has no checksum/config annotation") + } + if again := controllerConfigChecksum(t); again != before { + t.Fatalf("checksum is not deterministic: %q then %q", before, again) + } + if after := controllerConfigChecksum(t, "--set", "zxporter.clusterId=c-7"); after == before { + t.Fatal("changing zxporter.clusterId left checksum/config unchanged") + } + hijacked := controllerConfigChecksum(t, "--set-json", `podAnnotations={"checksum/config":"hijacked"}`) + if hijacked != before { + t.Fatalf("podAnnotations overrode the chart-owned checksum/config: %q", hijacked) + } +} + +// controllerEnv is the manager container env of the controller-manager pod +// template, by name. +func controllerEnv(t *testing.T, args ...string) map[string]map[string]any { + t.Helper() + for name, template := range charttest.PodTemplates(charttest.Objects(t, charttest.Render(t, patSpec, args...)), true) { + if !strings.Contains(name, "devzero-zxporter-controller-manager") { + continue + } + spec, _ := template["spec"].(map[string]any) + containers, _ := spec["containers"].([]any) + for _, c := range containers { + container, _ := c.(map[string]any) + if container["name"] != "manager" { + continue + } + out := map[string]map[string]any{} + env, _ := container["env"].([]any) + for _, e := range env { + entry, _ := e.(map[string]any) + name, _ := entry["name"].(string) + out[name] = entry + } + return out + } + } + t.Fatal("no controller-manager manager container rendered") + return nil +} + +// In the default Secret mode CLUSTER_TOKEN is a pod-start copy of the mirror +// Secret zxporter writes itself, and the chart marks it so, so that a promoted +// standby reads the mirror live. An existingSecret, or ConfigMap mode, is an +// explicit credential and is left unmarked (KUB-158). +func TestClusterTokenSourceMarksOnlyTheMirror(t *testing.T) { + cases := []struct { + name string + args []string + want any // nil means CLUSTER_TOKEN_SOURCE is absent + }{ + {name: "default Secret mode", want: "mirror"}, + {name: "existingSecret", args: []string{"--set", "zxporter.existingSecret.name=s"}}, + {name: "ConfigMap mode", args: []string{"--set", "zxporter.useSecretForToken=false"}}, + } + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + entry, ok := controllerEnv(t, tc.args...)["CLUSTER_TOKEN_SOURCE"] + switch { + case tc.want == nil && ok: + t.Fatalf("CLUSTER_TOKEN_SOURCE=%v, want it absent", entry["value"]) + case tc.want != nil && (!ok || entry["value"] != tc.want): + t.Fatalf("CLUSTER_TOKEN_SOURCE=%v, want %v", entry["value"], tc.want) + } + }) + } +} diff --git a/helm-chart/zxporter/chart_topology_spread_test.go b/helm-chart/zxporter/chart_topology_spread_test.go new file mode 100644 index 00000000..2d154a7c --- /dev/null +++ b/helm-chart/zxporter/chart_topology_spread_test.go @@ -0,0 +1,103 @@ +package zxporter_test + +import ( + "reflect" + "testing" + + "github.com/devzero-inc/zxporter/helm-chart/internal/charttest" +) + +const ( + zoneSpread = `{"maxSkew":1,"topologyKey":"topology.kubernetes.io/zone","whenUnsatisfiable":"ScheduleAnyway"}` + hostnameSpread = `{"maxSkew":1,"topologyKey":"kubernetes.io/hostname","whenUnsatisfiable":"DoNotSchedule","labelSelector":{"matchLabels":{"custom":"selector"}}}` +) + +var topologySpreadSpec = charttest.Spec{ + ChartDir: ".", + BaseArgs: []string{ + "--set", "global.k8sProvider=aws", + "--set", "zxporter.clusterToken=test-token", + "--set", "zxporter.kubeContextName=test-cluster", + }, +} + +func TestTopologySpreadConstraints(t *testing.T) { + tests := []struct { + name string + args []string + // want maps each constraint's topologyKey to the labelSelector.matchLabels + // it must carry; nil means the pod's own labels. + want map[string]map[string]string + }{ + {name: "unset renders none"}, + { + name: "entry without labelSelector selects the controller-manager pods", + args: charttest.SetJSON("topologySpreadConstraints", "["+zoneSpread+"]"), + want: map[string]map[string]string{"topology.kubernetes.io/zone": nil}, + }, + { + name: "explicit labelSelector is kept", + args: charttest.SetJSON("topologySpreadConstraints", "["+zoneSpread+","+hostnameSpread+"]"), + want: map[string]map[string]string{ + "topology.kubernetes.io/zone": nil, + "kubernetes.io/hostname": {"custom": "selector"}, + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + manifests := charttest.Render(t, topologySpreadSpec, tt.args...) + if dups := charttest.DuplicateKeys(t, manifests); len(dups) > 0 { + t.Fatalf("duplicate keys: %v", dups) + } + + sawDeployment := false + for name, template := range charttest.PodTemplates(charttest.Objects(t, manifests), false) { + constraints, _ := template["spec"].(map[string]any)["topologySpreadConstraints"].([]any) + if name != "Deployment/devzero-zxporter-controller-manager" { + if len(constraints) > 0 { + t.Errorf("%s: got topologySpreadConstraints %v, want none", name, constraints) + } + continue + } + sawDeployment = true + + podLabels := charttest.StringMap(template, "metadata", "labels") + got := map[string]map[string]string{} + for _, c := range constraints { + constraint, _ := c.(map[string]any) + key, _ := constraint["topologyKey"].(string) + got[key] = charttest.StringMap(constraint, "labelSelector", "matchLabels") + } + if len(got) != len(tt.want) { + t.Fatalf("%s: got constraints %v, want keys %v", name, got, tt.want) + } + for key, wantSelector := range tt.want { + gotSelector, ok := got[key] + if !ok { + t.Errorf("%s: missing constraint on %q", name, key) + continue + } + if wantSelector != nil { + if !reflect.DeepEqual(gotSelector, wantSelector) { + t.Errorf("%s: %q selects %v, want %v", name, key, gotSelector, wantSelector) + } + continue + } + if len(gotSelector) == 0 { + t.Errorf("%s: %q has no default labelSelector", name, key) + } + for k, v := range gotSelector { + if podLabels[k] != v { + t.Errorf("%s: %q selects %s=%s, which the pod does not carry", name, key, k, v) + } + } + } + } + if !sawDeployment { + t.Fatal("controller-manager Deployment not rendered") + } + }) + } +} diff --git a/helm-chart/zxporter/crds/devzero.io_collectionpolicies.yaml b/helm-chart/zxporter/crds/devzero.io_collectionpolicies.yaml index acf8e66b..75b29012 100644 --- a/helm-chart/zxporter/crds/devzero.io_collectionpolicies.yaml +++ b/helm-chart/zxporter/crds/devzero.io_collectionpolicies.yaml @@ -52,6 +52,39 @@ spec: exclusions: description: Exclusions specifies resources to exclude from collection properties: + excludedArgoCDApplicationSets: + items: + description: ExcludedArgoCDApplicationSet identifies an Argo + CD ApplicationSet to exclude from collection + properties: + name: + description: Name is the Argo CD ApplicationSet's name + type: string + namespace: + description: Namespace is the Argo CD ApplicationSet's namespace + type: string + required: + - name + - namespace + type: object + type: array + excludedArgoCDApplications: + description: Argo CD Exclusions + items: + description: ExcludedArgoCDApplication identifies an Argo CD + Application to exclude from collection + properties: + name: + description: Name is the Argo CD Application's name + type: string + namespace: + description: Namespace is the Argo CD Application's namespace + type: string + required: + - name + - namespace + type: object + type: array excludedCNPGClusters: description: CloudNativePG Exclusions items: @@ -227,6 +260,303 @@ spec: - namespace type: object type: array + excludedFlaggerAlertProviders: + items: + description: ExcludedFlaggerAlertProvider identifies a Flagger + AlertProvider to exclude from collection + properties: + name: + type: string + namespace: + type: string + required: + - name + - namespace + type: object + type: array + excludedFlaggerCanaries: + description: Flagger Exclusions + items: + description: ExcludedFlaggerCanary identifies a Flagger Canary + to exclude from collection + properties: + name: + type: string + namespace: + type: string + required: + - name + - namespace + type: object + type: array + excludedFlaggerMetricTemplates: + items: + description: ExcludedFlaggerMetricTemplate identifies a Flagger + MetricTemplate to exclude from collection + properties: + name: + type: string + namespace: + type: string + required: + - name + - namespace + type: object + type: array + excludedFluxAlerts: + items: + description: ExcludedFluxAlert identifies a FluxCD Alert to + exclude from collection + properties: + name: + type: string + namespace: + type: string + required: + - name + - namespace + type: object + type: array + excludedFluxBuckets: + items: + description: ExcludedFluxBucket identifies a FluxCD Bucket to + exclude from collection + properties: + name: + type: string + namespace: + type: string + required: + - name + - namespace + type: object + type: array + excludedFluxExternalArtifacts: + description: FluxCD Exclusions -- collector-only kinds + items: + description: ExcludedFluxExternalArtifact identifies a FluxCD + ExternalArtifact to exclude from collection + properties: + name: + type: string + namespace: + type: string + required: + - name + - namespace + type: object + type: array + excludedFluxGitRepositories: + description: FluxCD Exclusions -- full-explorer kinds + items: + description: ExcludedFluxGitRepository identifies a FluxCD GitRepository + to exclude from collection + properties: + name: + type: string + namespace: + type: string + required: + - name + - namespace + type: object + type: array + excludedFluxHelmCharts: + items: + description: ExcludedFluxHelmChart identifies a FluxCD HelmChart + to exclude from collection + properties: + name: + type: string + namespace: + type: string + required: + - name + - namespace + type: object + type: array + excludedFluxHelmReleases: + items: + description: ExcludedFluxHelmRelease identifies a FluxCD HelmRelease + to exclude from collection + properties: + name: + type: string + namespace: + type: string + required: + - name + - namespace + type: object + type: array + excludedFluxHelmRepositories: + items: + description: ExcludedFluxHelmRepository identifies a FluxCD + HelmRepository to exclude from collection + properties: + name: + type: string + namespace: + type: string + required: + - name + - namespace + type: object + type: array + excludedFluxImagePolicies: + items: + description: ExcludedFluxImagePolicy identifies a FluxCD ImagePolicy + to exclude from collection + properties: + name: + type: string + namespace: + type: string + required: + - name + - namespace + type: object + type: array + excludedFluxImageRepositories: + items: + description: ExcludedFluxImageRepository identifies a FluxCD + ImageRepository to exclude from collection + properties: + name: + type: string + namespace: + type: string + required: + - name + - namespace + type: object + type: array + excludedFluxImageUpdateAutomations: + items: + description: ExcludedFluxImageUpdateAutomation identifies a + FluxCD ImageUpdateAutomation to exclude from collection + properties: + name: + type: string + namespace: + type: string + required: + - name + - namespace + type: object + type: array + excludedFluxInstances: + items: + description: ExcludedFluxInstance identifies a Flux Operator + FluxInstance to exclude from collection + properties: + name: + type: string + namespace: + type: string + required: + - name + - namespace + type: object + type: array + excludedFluxKustomizations: + items: + description: ExcludedFluxKustomization identifies a FluxCD Kustomization + to exclude from collection + properties: + name: + type: string + namespace: + type: string + required: + - name + - namespace + type: object + type: array + excludedFluxOCIRepositories: + items: + description: ExcludedFluxOCIRepository identifies a FluxCD OCIRepository + to exclude from collection + properties: + name: + type: string + namespace: + type: string + required: + - name + - namespace + type: object + type: array + excludedFluxProviders: + items: + description: ExcludedFluxProvider identifies a FluxCD Provider + to exclude from collection + properties: + name: + type: string + namespace: + type: string + required: + - name + - namespace + type: object + type: array + excludedFluxReceivers: + items: + description: ExcludedFluxReceiver identifies a FluxCD Receiver + to exclude from collection + properties: + name: + type: string + namespace: + type: string + required: + - name + - namespace + type: object + type: array + excludedFluxReports: + items: + description: ExcludedFluxReport identifies a Flux Operator FluxReport + to exclude from collection + properties: + name: + type: string + namespace: + type: string + required: + - name + - namespace + type: object + type: array + excludedFluxResourceSetInputProviders: + items: + description: ExcludedFluxResourceSetInputProvider identifies + a Flux Operator ResourceSetInputProvider to exclude from collection + properties: + name: + type: string + namespace: + type: string + required: + - name + - namespace + type: object + type: array + excludedFluxResourceSets: + items: + description: ExcludedFluxResourceSet identifies a Flux Operator + ResourceSet to exclude from collection + properties: + name: + type: string + namespace: + type: string + required: + - name + - namespace + type: object + type: array excludedHPAs: items: description: ExcludedHPA identifies a horizontal pod autoscaler @@ -664,9 +994,13 @@ spec: type: boolean disabledCollectors: description: |- - DisabledCollectors is a list of collector types to completely disable - Valid values include: "pod", "deployment", "statefulset", "daemonset", "service", - "configmap", "secret", "node", "event", etc. + DisabledCollectors is a list of collector types to completely disable, by their + canonical names, e.g. "pod", "deployment", "stateful_set", "daemon_set", "service", + "node", "event", "persistent_volume", "persistent_volume_claim", + "custom_resource_definition", "keda_scaled_job", "keda_scaled_object". + The legacy spellings "statefulset", "daemonset", "persistentvolume", + "persistentvolumeclaim", "crd", "scaledjob" and "scaledobject" are still accepted + and mean the same collectors. items: type: string type: array @@ -674,6 +1008,13 @@ spec: description: Frequency is how often to collect resource usage metrics type: string + kedaMetricsEndpoint: + description: |- + KEDAMetricsEndpoint is the full URL of the KEDA operator /metrics endpoint + that the KedaScalerMetrics collector scrapes. Defaults to + collector.DefaultKedaMetricsEndpoint (KEDA in the devzero-system namespace). + Override only when KEDA runs in a different namespace or on a different port. + type: string kubeContextName: description: KubeContextName is the name of the current context being used to apply the installation yaml @@ -738,6 +1079,13 @@ spec: PATToken is the Personal Access Token used for automatic cluster token exchange If ClusterToken is not provided but PATToken is, the system will exchange it for a cluster token type: string + schedulerMetricsScrapeInterval: + description: |- + SchedulerMetricsScrapeInterval bounds how often the SchedulerMetrics + collector scrapes dz-scheduler's /metrics endpoint (defaults to 60s -- + see collector.DefaultSchedulerMetricsScrapeInterval's doc comment for + why). A Go duration string (e.g. "60s", "30s"). + type: string watchedCRDs: description: WatchedCRDs is a list of custom resource definitions to explicitly watch diff --git a/helm-chart/zxporter/templates/NOTES.txt b/helm-chart/zxporter/templates/NOTES.txt index 24a27c01..01052120 100644 --- a/helm-chart/zxporter/templates/NOTES.txt +++ b/helm-chart/zxporter/templates/NOTES.txt @@ -27,10 +27,10 @@ Ensure it exists in namespace {{ .Release.Namespace }} and contains at least one {{- end }} -{{- if not .Values.zxporter.kubeContextName }} +{{- if not (include "devzero-common.clusterName" (list . .Values.zxporter.kubeContextName)) }} NOTE: No cluster context name configured. -Please set zxporter.kubeContextName to a unique identifier for your cluster. +Please set zxporter.kubeContextName (or global.clusterName) to a unique identifier for your cluster. {{- end }} diff --git a/helm-chart/zxporter/templates/_helpers.tpl b/helm-chart/zxporter/templates/_helpers.tpl index 7443a42f..e1daa816 100644 --- a/helm-chart/zxporter/templates/_helpers.tpl +++ b/helm-chart/zxporter/templates/_helpers.tpl @@ -38,6 +38,9 @@ helm.sh/chart: {{ include "zxporter.chart" . }} app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} {{- end }} app.kubernetes.io/managed-by: {{ .Release.Service }} +{{- with (include "zxporter.commonLabels" .) }} +{{ . }} +{{- end }} {{- end }} {{/* @@ -48,6 +51,48 @@ app.kubernetes.io/name: {{ include "zxporter.name" . }} app.kubernetes.io/instance: {{ .Release.Name }} {{- end }} +{{/* +Render a pod's topologySpreadConstraints list. Called with +(dict "constraints" "selector" ): an entry with no labelSelector +gets one matching the pod's own labels, so a user can add the spread rule an +admission policy requires without knowing the release-derived labels. +*/}} +{{- define "zxporter.topologySpreadConstraints" -}} +{{- $out := list }} +{{- range .constraints }} +{{- $c := deepCopy . }} +{{- if not (hasKey $c "labelSelector") }} +{{- $_ := set $c "labelSelector" (dict "matchLabels" $.selector) }} +{{- end }} +{{- $out = append $out $c }} +{{- end }} +{{- toYaml $out }} +{{- end }} + +{{/* +The controller image: image.repository:image.tag, with its registry host +swapped for global.imageRegistry when the dz-platform umbrella sets it. With +the global unset this is exactly the reference the chart always rendered. +*/}} +{{- define "zxporter.image" -}} +{{- /* Each key renders as the chart always printed it: null as empty, any + other value (a tag of 0 or false included) as its string. `default` + would turn 0 and false into empty. */ -}} +{{- $repository := .Values.image.repository -}} +{{- $tag := .Values.image.tag -}} +{{- $ref := printf "%s:%s" (ternary "" (toString $repository) (kindIs "invalid" $repository)) (ternary "" (toString $tag) (kindIs "invalid" $tag)) -}} +{{- include "devzero-common.imageRef" (dict "ref" $ref "global" .Values.global) -}} +{{- end }} + +{{/* +The pod's imagePullSecrets block: global.imagePullSecrets merged with the +chart's own imagePullSecrets, names or {name: ...} objects, deduplicated. +Empty, and so rendering nothing, when neither is set. +*/}} +{{- define "zxporter.imagePullSecrets" -}} +{{- include "devzero-common.imagePullSecrets" (dict "Values" (dict "global" .Values.global "image" (dict "pullSecrets" .Values.imagePullSecrets))) -}} +{{- end }} + {{/* Validate required configuration */}} @@ -61,8 +106,8 @@ Validate required configuration {{- fail (printf "ERROR: zxporter.existingSecret.name (%q) must not equal zxporter.tokenSecretName. The chart creates and writes to that runtime Secret itself, so reusing the name would collide on install and let the controller overwrite your managed Secret." .Values.zxporter.tokenSecretName) -}} {{- end -}} - {{- if empty .Values.zxporter.kubeContextName -}} - {{- fail "ERROR: zxporter.kubeContextName is required. Please provide a unique identifier for your cluster." -}} + {{- if empty (include "devzero-common.clusterName" (list . .Values.zxporter.kubeContextName)) -}} + {{- fail "ERROR: zxporter.kubeContextName (or global.clusterName) is required. Please provide a unique identifier for your cluster." -}} {{- end -}} {{- if empty .Values.global.k8sProvider -}} @@ -85,3 +130,93 @@ Validate required configuration {{- fail (printf "ERROR: global.gpuRuntime.runtimeClassName is required when global.gpuRuntime.mode is %s." $gpuRuntimeMode) -}} {{- end -}} {{- end }} + +{{/* +Site-wide labels for object metadata. + +Clusters running an admission policy engine (OPA Gatekeeper, Kyverno) often +require site-specific metadata on every object before they will admit it. Every +key the chart owns is dropped from the user-supplied values, so a collision can +neither rewrite chart state nor emit a duplicate YAML key. + +`global.commonLabels` is the knob that reaches subcharts; a chart-local +`commonLabels` applies only to the chart it is set on and wins on a collision. + +Every object also carries devzero-common's platform labels, +app.kubernetes.io/part-of and devzero.io/component, so all DevZero charts can +be selected the same way. A user who already set either key through +commonLabels keeps their value. Pod templates do not get them: those labels +come from podExtraLabels, and selectors never change. +*/}} +{{- define "zxporter.commonLabels" -}} +{{- $extra := deepCopy (.Values.commonLabels | default dict) -}} +{{- if .Values.global }}{{- with .Values.global.commonLabels }}{{- $extra = merge $extra (deepCopy .) }}{{- end }}{{- end }} +{{- /* Completeness of this list is enforced by the "no chart-emitted key can + produce a duplicate" assertion in helm-chart/internal/charttest, which + derives the real set from a render rather than restating it here. */}} +{{- range $key := list "helm.sh/chart" "app.kubernetes.io/name" "app.kubernetes.io/instance" "app.kubernetes.io/version" "app.kubernetes.io/managed-by" "app.kubernetes.io/component" "app" "control-plane" }} +{{- $_ := unset $extra $key }} +{{- end }} +{{- toYaml (merge $extra (include "zxporter.platformLabels" . | fromYaml)) }} +{{- end }} + +{{/* +devzero-common's platform labels: part-of and component only. The chart keeps +its own name, instance, version, managed-by and helm.sh/chart labels, and its +own selectors, so nothing that selects on them changes. +*/}} +{{- define "zxporter.platformLabels" -}} +{{- toYaml (pick (include "devzero-common.labels" . | fromYaml) "app.kubernetes.io/part-of" "devzero.io/component") }} +{{- end }} + +{{/* +Site-wide annotations for object metadata. +*/}} +{{- define "zxporter.commonAnnotations" -}} +{{- $extra := deepCopy (.Values.commonAnnotations | default dict) -}} +{{- if .Values.global }}{{- with .Values.global.commonAnnotations }}{{- $extra = merge $extra (deepCopy .) }}{{- end }}{{- end }} +{{- range $key := list "helm.sh/hook" "helm.sh/hook-weight" "helm.sh/hook-delete-policy" "kubectl.kubernetes.io/default-container" "checksum/config" "devzero.io/gpu-runtime-mode" "devzero.io/gpu-runtime-class" "ignore-check.kube-linter.io/privileged-container" "ignore-check.kube-linter.io/run-as-non-root" "ignore-check.kube-linter.io/privilege-escalation-container" "ignore-check.kube-linter.io/no-read-only-root-fs" }} +{{- $_ := unset $extra $key }} +{{- end }} +{{- with $extra }} +{{- toYaml . }} +{{- end }} +{{- end }} + +{{/* +Pod-template labels: commonLabels plus podLabels, podLabels winning. + +Gatekeeper's expand-workloads (and Kyverno's equivalent) evaluates constraints +against the Pod a workload would create, so site labels have to reach the pod +template and not just the owning object. Keys the pod templates set themselves +are dropped: several of them are selector labels, which are immutable after +creation. +*/}} +{{- define "zxporter.podExtraLabels" -}} +{{- $extra := deepCopy (.Values.podLabels | default dict) -}} +{{- if .Values.global }}{{- with .Values.global.podLabels }}{{- $extra = merge $extra (deepCopy .) }}{{- end }}{{- end }} +{{- $extra = merge $extra (deepCopy (.Values.commonLabels | default dict)) -}} +{{- if .Values.global }}{{- with .Values.global.commonLabels }}{{- $extra = merge $extra (deepCopy .) }}{{- end }}{{- end }} +{{- range $key := list "helm.sh/chart" "app.kubernetes.io/name" "app.kubernetes.io/instance" "app.kubernetes.io/version" "app.kubernetes.io/managed-by" "app.kubernetes.io/component" "app" "control-plane" }} +{{- $_ := unset $extra $key }} +{{- end }} +{{- with $extra }} +{{- toYaml . }} +{{- end }} +{{- end }} + +{{/* +Pod-template annotations: commonAnnotations plus podAnnotations. +*/}} +{{- define "zxporter.podExtraAnnotations" -}} +{{- $extra := deepCopy (.Values.podAnnotations | default dict) -}} +{{- if .Values.global }}{{- with .Values.global.podAnnotations }}{{- $extra = merge $extra (deepCopy .) }}{{- end }}{{- end }} +{{- $extra = merge $extra (deepCopy (.Values.commonAnnotations | default dict)) -}} +{{- if .Values.global }}{{- with .Values.global.commonAnnotations }}{{- $extra = merge $extra (deepCopy .) }}{{- end }}{{- end }} +{{- range $key := list "helm.sh/hook" "helm.sh/hook-weight" "helm.sh/hook-delete-policy" "kubectl.kubernetes.io/default-container" "checksum/config" "devzero.io/gpu-runtime-mode" "devzero.io/gpu-runtime-class" "ignore-check.kube-linter.io/privileged-container" "ignore-check.kube-linter.io/run-as-non-root" "ignore-check.kube-linter.io/privilege-escalation-container" "ignore-check.kube-linter.io/no-read-only-root-fs" }} +{{- $_ := unset $extra $key }} +{{- end }} +{{- with $extra }} +{{- toYaml . }} +{{- end }} +{{- end }} diff --git a/helm-chart/zxporter/templates/configmap.yaml b/helm-chart/zxporter/templates/configmap.yaml index 777dc768..a49e54f1 100644 --- a/helm-chart/zxporter/templates/configmap.yaml +++ b/helm-chart/zxporter/templates/configmap.yaml @@ -42,22 +42,33 @@ data: EXCLUDED_STORAGECLASSES: "" EXCLUDED_VPAS: "" K8S_PROVIDER: "{{ .Values.global.k8sProvider }}" - KUBE_CONTEXT_NAME: "{{ .Values.zxporter.kubeContextName }}" + KUBE_CONTEXT_NAME: "{{ include "devzero-common.clusterName" (list . .Values.zxporter.kubeContextName) }}" LOG_LEVEL: "{{ .Values.zxporter.logLevel }}" MASK_SECRET_DATA: "" NODE_METRICS_INTERVAL: "" NODE_METRICS_CONCURRENCY: "{{ .Values.zxporter.nodeMetricsConcurrency }}" NODEMON_REQUEST_TIMEOUT: "{{ .Values.zxporter.nodemonRequestTimeout }}" KUBELET_FALLBACK_TIMEOUT: "{{ .Values.zxporter.kubeletFallbackTimeout }}" - DAKR_URL: "{{ .Values.zxporter.dakrUrl }}" + SCHEDULER_METRICS_SCRAPE_INTERVAL: "{{ .Values.zxporter.schedulerMetricsScrapeInterval }}" + KEDA_METRICS_ENDPOINT: "{{ .Values.zxporter.kedaMetricsEndpoint }}" + DAKR_URL: "{{ include "devzero-common.dakrUrl" (list . .Values.zxporter.dakrUrl) }}" TARGET_NAMESPACES: "{{ .Values.zxporter.targetNamespaces }}" TOKEN_CONFIGMAP_NAME: "{{ .Values.zxporter.tokenConfigMapName }}" TOKEN_CREDENTIALS_SECRET_NAME: "{{ .Values.zxporter.tokenCredentialsSecretName }}" TOKEN_RUNTIME_SECRET_NAME: "{{ .Values.zxporter.tokenSecretName }}" TOKEN_SECRET_NAME: "{{ .Values.zxporter.tokenSecretName }}" + CLUSTER_ID: "{{ .Values.zxporter.clusterId }}" USE_SECRET_FOR_TOKEN: "{{ .Values.zxporter.useSecretForToken }}" WATCHED_CRDS: "" kind: ConfigMap metadata: + {{- with (include "zxporter.commonLabels" $) }} + labels: + {{- . | nindent 4 }} + {{- end }} + {{- with (include "zxporter.commonAnnotations" $) }} + annotations: + {{- . | nindent 4 }} + {{- end }} name: {{ .Values.zxporter.tokenConfigMapName }} namespace: {{ .Release.Namespace }} diff --git a/helm-chart/zxporter/templates/deployment.yaml b/helm-chart/zxporter/templates/deployment.yaml index 5b591698..aa008a30 100644 --- a/helm-chart/zxporter/templates/deployment.yaml +++ b/helm-chart/zxporter/templates/deployment.yaml @@ -2,7 +2,14 @@ apiVersion: apps/v1 kind: Deployment metadata: + {{- with (include "zxporter.commonAnnotations" $) }} + annotations: + {{- . | nindent 4 }} + {{- end }} labels: + {{- with (include "zxporter.commonLabels" $) }} + {{- . | nindent 4 }} + {{- end }} app.kubernetes.io/managed-by: kustomize app.kubernetes.io/name: devzero-zxporter control-plane: controller-manager @@ -16,11 +23,22 @@ spec: template: metadata: annotations: + {{- with (include "zxporter.podExtraAnnotations" $) }} + {{- . | nindent 8 }} + {{- end }} kubectl.kubernetes.io/default-container: manager + {{- /* zxporter reads its config once at start: roll it when the ConfigMap changes. */}} + checksum/config: {{ include (print $.Template.BasePath "/configmap.yaml") . | sha256sum }} labels: + {{- with (include "zxporter.podExtraLabels" $) }} + {{- . | nindent 8 }} + {{- end }} control-plane: controller-manager {{- include "zxporter.selectorLabels" . | nindent 8 }} spec: + {{- with include "zxporter.imagePullSecrets" . }} + {{- . | nindent 6 }} + {{- end }} {{- if .Values.global.priorityClass.enabled }} {{- /* Must equal the PriorityClass name in templates/priorityclass.yaml, which is hardcoded to the (double-prefixed) dist/ bundle name so helm + installer_updater stay in sync. */}} @@ -38,6 +56,12 @@ spec: affinity: {{- toYaml . | nindent 8 }} {{- end }} + {{- with .Values.topologySpreadConstraints }} + {{- $selector := include "zxporter.selectorLabels" $ | fromYaml }} + {{- $_ := set $selector "control-plane" "controller-manager" }} + topologySpreadConstraints: + {{- include "zxporter.topologySpreadConstraints" (dict "constraints" . "selector" $selector) | nindent 8 }} + {{- end }} containers: - args: - --metrics-bind-address=:8443 @@ -45,7 +69,7 @@ spec: - --mpa-server-port={{ .Values.mpaServer.port }} - --health-probe-bind-address=:8081 - --zap-log-level={{ .Values.zxporter.logLevel }} - image: {{ .Values.image.repository }}:{{ .Values.image.tag }} + image: {{ include "zxporter.image" . }} imagePullPolicy: {{ .Values.image.pullPolicy }} ports: - name: http-metrics @@ -90,6 +114,12 @@ spec: name: {{ .Values.zxporter.tokenSecretName }} key: CLUSTER_TOKEN optional: true + # Marks CLUSTER_TOKEN as a pod-start copy of the mirror Secret + # zxporter writes, so zxporter reads that Secret live instead: a + # standby promoted after another replica re-registered must not + # use its older copy. Without this marker env wins. + - name: CLUSTER_TOKEN_SOURCE + value: mirror {{- if .Values.zxporter.patToken }} - name: PAT_TOKEN valueFrom: diff --git a/helm-chart/zxporter/templates/gpu-runtime-rbac.yaml b/helm-chart/zxporter/templates/gpu-runtime-rbac.yaml index 8111270e..b3f9b95f 100644 --- a/helm-chart/zxporter/templates/gpu-runtime-rbac.yaml +++ b/helm-chart/zxporter/templates/gpu-runtime-rbac.yaml @@ -4,6 +4,14 @@ apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: + {{- with (include "zxporter.commonLabels" $) }} + labels: + {{- . | nindent 4 }} + {{- end }} + {{- with (include "zxporter.commonAnnotations" $) }} + annotations: + {{- . | nindent 4 }} + {{- end }} name: devzero-zxporter-gpu-runtime-reader rules: - apiGroups: @@ -18,6 +26,14 @@ rules: apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: + {{- with (include "zxporter.commonLabels" $) }} + labels: + {{- . | nindent 4 }} + {{- end }} + {{- with (include "zxporter.commonAnnotations" $) }} + annotations: + {{- . | nindent 4 }} + {{- end }} name: devzero-zxporter-gpu-runtime-reader-rolebinding subjects: - kind: ServiceAccount @@ -31,6 +47,14 @@ roleRef: apiVersion: rbac.authorization.k8s.io/v1 kind: Role metadata: + {{- with (include "zxporter.commonLabels" $) }} + labels: + {{- . | nindent 4 }} + {{- end }} + {{- with (include "zxporter.commonAnnotations" $) }} + annotations: + {{- . | nindent 4 }} + {{- end }} name: devzero-zxporter-gpu-runtime-patcher namespace: {{ .Release.Namespace }} rules: @@ -47,6 +71,14 @@ rules: apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding metadata: + {{- with (include "zxporter.commonLabels" $) }} + labels: + {{- . | nindent 4 }} + {{- end }} + {{- with (include "zxporter.commonAnnotations" $) }} + annotations: + {{- . | nindent 4 }} + {{- end }} name: devzero-zxporter-gpu-runtime-patcher-rolebinding namespace: {{ .Release.Namespace }} subjects: diff --git a/helm-chart/zxporter/templates/poddisruptionbudget.yaml b/helm-chart/zxporter/templates/poddisruptionbudget.yaml index 0890047c..8fddb9ea 100644 --- a/helm-chart/zxporter/templates/poddisruptionbudget.yaml +++ b/helm-chart/zxporter/templates/poddisruptionbudget.yaml @@ -2,6 +2,10 @@ apiVersion: policy/v1 kind: PodDisruptionBudget metadata: + {{- with (include "zxporter.commonAnnotations" $) }} + annotations: + {{- . | nindent 4 }} + {{- end }} {{- /* Hardcoded to match the kustomize-generated dist/ bundles, which double-apply the "devzero-zxporter-" namePrefix. Keep identical to config/manager/poddisruptionbudget.yaml's rendered name so `helm install` and dist/installer_updater.yaml target the SAME PDB. */}} diff --git a/helm-chart/zxporter/templates/priorityclass.yaml b/helm-chart/zxporter/templates/priorityclass.yaml index a3fb0620..d08e3b41 100644 --- a/helm-chart/zxporter/templates/priorityclass.yaml +++ b/helm-chart/zxporter/templates/priorityclass.yaml @@ -2,6 +2,10 @@ apiVersion: scheduling.k8s.io/v1 kind: PriorityClass metadata: + {{- with (include "zxporter.commonAnnotations" $) }} + annotations: + {{- . | nindent 4 }} + {{- end }} {{- /* Hardcoded (not release-derived) to match the kustomize-generated dist/ bundles, which double-apply the "devzero-zxporter-" namePrefix (config/manager/priorityclass.yaml is already named devzero-zxporter-critical, then namePrefix adds it again). Keeping the exact diff --git a/helm-chart/zxporter/templates/secret-credentials.yaml b/helm-chart/zxporter/templates/secret-credentials.yaml index 16d781a0..4b2f1e4c 100644 --- a/helm-chart/zxporter/templates/secret-credentials.yaml +++ b/helm-chart/zxporter/templates/secret-credentials.yaml @@ -6,9 +6,16 @@ apiVersion: v1 kind: Secret metadata: + {{- with (include "zxporter.commonAnnotations" $) }} + annotations: + {{- . | nindent 4 }} + {{- end }} name: {{ .Values.zxporter.tokenCredentialsSecretName }} namespace: {{ .Release.Namespace }} labels: + {{- with (include "zxporter.commonLabels" $) }} + {{- . | nindent 4 }} + {{- end }} app.kubernetes.io/name: zxporter app.kubernetes.io/component: credentials type: Opaque diff --git a/helm-chart/zxporter/templates/secret.yaml b/helm-chart/zxporter/templates/secret.yaml index a1af05c4..c81d4748 100644 --- a/helm-chart/zxporter/templates/secret.yaml +++ b/helm-chart/zxporter/templates/secret.yaml @@ -7,9 +7,16 @@ apiVersion: v1 kind: Secret metadata: + {{- with (include "zxporter.commonAnnotations" $) }} + annotations: + {{- . | nindent 4 }} + {{- end }} name: {{ .Values.zxporter.tokenSecretName }} namespace: {{ .Release.Namespace }} labels: + {{- with (include "zxporter.commonLabels" $) }} + {{- . | nindent 4 }} + {{- end }} app.kubernetes.io/name: zxporter app.kubernetes.io/component: runtime-tokens type: Opaque diff --git a/helm-chart/zxporter/templates/service-mpa.yaml b/helm-chart/zxporter/templates/service-mpa.yaml index 9792d764..7f92ff37 100644 --- a/helm-chart/zxporter/templates/service-mpa.yaml +++ b/helm-chart/zxporter/templates/service-mpa.yaml @@ -1,6 +1,10 @@ apiVersion: v1 kind: Service metadata: + {{- with (include "zxporter.commonAnnotations" $) }} + annotations: + {{- . | nindent 4 }} + {{- end }} name: devzero-zxporter-controller-manager-mpa namespace: {{ .Release.Namespace }} labels: diff --git a/helm-chart/zxporter/templates/service.yaml b/helm-chart/zxporter/templates/service.yaml index ece5c4da..f1bd0e08 100644 --- a/helm-chart/zxporter/templates/service.yaml +++ b/helm-chart/zxporter/templates/service.yaml @@ -1,7 +1,14 @@ apiVersion: v1 kind: Service metadata: + {{- with (include "zxporter.commonAnnotations" $) }} + annotations: + {{- . | nindent 4 }} + {{- end }} labels: + {{- with (include "zxporter.commonLabels" $) }} + {{- . | nindent 4 }} + {{- end }} app.kubernetes.io/managed-by: kustomize app.kubernetes.io/name: devzero-zxporter control-plane: controller-manager diff --git a/helm-chart/zxporter/templates/serviceaccount.yaml b/helm-chart/zxporter/templates/serviceaccount.yaml index 877c025e..7e02f095 100644 --- a/helm-chart/zxporter/templates/serviceaccount.yaml +++ b/helm-chart/zxporter/templates/serviceaccount.yaml @@ -1,7 +1,14 @@ apiVersion: v1 kind: ServiceAccount metadata: + {{- with (include "zxporter.commonAnnotations" $) }} + annotations: + {{- . | nindent 4 }} + {{- end }} labels: + {{- with (include "zxporter.commonLabels" $) }} + {{- . | nindent 4 }} + {{- end }} app.kubernetes.io/managed-by: kustomize app.kubernetes.io/name: devzero-zxporter name: devzero-zxporter-controller-manager diff --git a/helm-chart/zxporter/templates/zxporter-rbac.yaml b/helm-chart/zxporter/templates/zxporter-rbac.yaml index a1b53975..d71f9dd3 100644 --- a/helm-chart/zxporter/templates/zxporter-rbac.yaml +++ b/helm-chart/zxporter/templates/zxporter-rbac.yaml @@ -7,7 +7,14 @@ apiVersion: rbac.authorization.k8s.io/v1 kind: Role metadata: + {{- with (include "zxporter.commonAnnotations" $) }} + annotations: + {{- . | nindent 4 }} + {{- end }} labels: + {{- with (include "zxporter.commonLabels" $) }} + {{- . | nindent 4 }} + {{- end }} app.kubernetes.io/managed-by: kustomize app.kubernetes.io/name: devzero-zxporter name: devzero-zxporter-leader-election-role @@ -84,7 +91,14 @@ rules: apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: + {{- with (include "zxporter.commonAnnotations" $) }} + annotations: + {{- . | nindent 4 }} + {{- end }} labels: + {{- with (include "zxporter.commonLabels" $) }} + {{- . | nindent 4 }} + {{- end }} app.kubernetes.io/managed-by: kustomize app.kubernetes.io/name: devzero-zxporter name: devzero-zxporter-collectionpolicy-editor-role @@ -111,7 +125,14 @@ rules: apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: + {{- with (include "zxporter.commonAnnotations" $) }} + annotations: + {{- . | nindent 4 }} + {{- end }} labels: + {{- with (include "zxporter.commonLabels" $) }} + {{- . | nindent 4 }} + {{- end }} app.kubernetes.io/managed-by: kustomize app.kubernetes.io/name: devzero-zxporter name: devzero-zxporter-collectionpolicy-viewer-role @@ -141,6 +162,14 @@ rules: apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: + {{- with (include "zxporter.commonLabels" $) }} + labels: + {{- . | nindent 4 }} + {{- end }} + {{- with (include "zxporter.commonAnnotations" $) }} + annotations: + {{- . | nindent 4 }} + {{- end }} name: devzero-zxporter-manager-role rules: # ========================================== @@ -248,6 +277,19 @@ rules: - pods/status verbs: - get +# SchedulerMetricsCollector (issue #10055): read-only access to the single named +# dz-scheduler Lease, so this collector can resolve the current leader pod directly +# instead of scraping the dz-scheduler Service VIP, which could otherwise get stuck +# scraping a standby replica and reporting all-zero placement metrics. Restricted to +# this one Lease -- this collector never needs any other Lease in the cluster. +- apiGroups: + - coordination.k8s.io + resources: + - leases + resourceNames: + - dz-scheduler + verbs: + - get - apiGroups: - apiextensions.k8s.io resources: @@ -280,6 +322,8 @@ rules: - apiGroups: - argoproj.io resources: + - applications + - applicationsets - rollouts verbs: - get @@ -294,6 +338,76 @@ rules: - get - list - watch +- apiGroups: + - source.toolkit.fluxcd.io + resources: + - buckets + - externalartifacts + - gitrepositories + - helmcharts + - helmrepositories + - ocirepositories + verbs: + - get + - list + - watch +- apiGroups: + - kustomize.toolkit.fluxcd.io + resources: + - kustomizations + verbs: + - get + - list + - watch +- apiGroups: + - helm.toolkit.fluxcd.io + resources: + - helmreleases + verbs: + - get + - list + - watch +- apiGroups: + - notification.toolkit.fluxcd.io + resources: + - alerts + - providers + - receivers + verbs: + - get + - list + - watch +- apiGroups: + - fluxcd.controlplane.io + resources: + - fluxinstances + - fluxreports + - resourcesetinputproviders + - resourcesets + verbs: + - get + - list + - watch +- apiGroups: + - image.toolkit.fluxcd.io + resources: + - imagepolicies + - imagerepositories + - imageupdateautomations + verbs: + - get + - list + - watch +- apiGroups: + - flagger.app + resources: + - alertproviders + - canaries + - metrictemplates + verbs: + - get + - list + - watch - apiGroups: - wgpolicyk8s.io resources: @@ -558,6 +672,14 @@ rules: apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: + {{- with (include "zxporter.commonLabels" $) }} + labels: + {{- . | nindent 4 }} + {{- end }} + {{- with (include "zxporter.commonAnnotations" $) }} + annotations: + {{- . | nindent 4 }} + {{- end }} name: devzero-zxporter-metrics-auth-role rules: - apiGroups: @@ -576,6 +698,14 @@ rules: apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: + {{- with (include "zxporter.commonLabels" $) }} + labels: + {{- . | nindent 4 }} + {{- end }} + {{- with (include "zxporter.commonAnnotations" $) }} + annotations: + {{- . | nindent 4 }} + {{- end }} name: devzero-zxporter-metrics-reader rules: - nonResourceURLs: @@ -586,7 +716,14 @@ rules: apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding metadata: + {{- with (include "zxporter.commonAnnotations" $) }} + annotations: + {{- . | nindent 4 }} + {{- end }} labels: + {{- with (include "zxporter.commonLabels" $) }} + {{- . | nindent 4 }} + {{- end }} app.kubernetes.io/managed-by: kustomize app.kubernetes.io/name: devzero-zxporter name: devzero-zxporter-leader-election-rolebinding @@ -603,7 +740,14 @@ subjects: apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: + {{- with (include "zxporter.commonAnnotations" $) }} + annotations: + {{- . | nindent 4 }} + {{- end }} labels: + {{- with (include "zxporter.commonLabels" $) }} + {{- . | nindent 4 }} + {{- end }} app.kubernetes.io/managed-by: kustomize app.kubernetes.io/name: devzero-zxporter name: devzero-zxporter-manager-rolebinding @@ -619,6 +763,14 @@ subjects: apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: + {{- with (include "zxporter.commonLabels" $) }} + labels: + {{- . | nindent 4 }} + {{- end }} + {{- with (include "zxporter.commonAnnotations" $) }} + annotations: + {{- . | nindent 4 }} + {{- end }} name: devzero-zxporter-metrics-auth-rolebinding roleRef: apiGroup: rbac.authorization.k8s.io @@ -628,3 +780,31 @@ subjects: - kind: ServiceAccount name: devzero-zxporter-controller-manager namespace: {{ .Release.Namespace }} +--- +# Binds devzero-zxporter-metrics-reader (nonResourceURLs: ["/metrics"], verb: get) +# to zxporter's own ServiceAccount so the SchedulerMetrics collector can scrape +# dz-scheduler's /metrics endpoint. dz-scheduler runs kube-scheduler with +# delegated authorization to the API server (SubjectAccessReview), and +# "/metrics" is not in kube-scheduler's default AlwaysAllowPaths -- unlike +# /healthz and /livez, an authenticated caller still needs an explicit RBAC +# grant for this nonResourceURL, or every scrape 403s. +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + {{- with (include "zxporter.commonLabels" $) }} + labels: + {{- . | nindent 4 }} + {{- end }} + {{- with (include "zxporter.commonAnnotations" $) }} + annotations: + {{- . | nindent 4 }} + {{- end }} + name: devzero-zxporter-metrics-reader-rolebinding +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: devzero-zxporter-metrics-reader +subjects: + - kind: ServiceAccount + name: devzero-zxporter-controller-manager + namespace: {{ .Release.Namespace }} diff --git a/helm-chart/zxporter/values.schema.json b/helm-chart/zxporter/values.schema.json new file mode 100644 index 00000000..52e9a40e --- /dev/null +++ b/helm-chart/zxporter/values.schema.json @@ -0,0 +1,197 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "zxporter chart values", + "description": "Permissive by design: unknown keys are allowed and scalars stay untyped, because installers pass --set strings and releases upgrade with reused values, so every value a release already carries must keep validating. A map or list is typed only where every other value already failed to render. required lists only the keys whose absence (--set key=null) failed to render in every configuration before this schema existed.", + "type": "object", + "properties": { + "commonLabels": { + "properties": {} + }, + "commonAnnotations": { + "properties": {} + }, + "podLabels": { + "properties": {} + }, + "podAnnotations": { + "properties": {} + }, + "image": { + "type": [ + "object", + "null" + ], + "properties": { + "repository": {}, + "pullPolicy": {}, + "tag": {} + }, + "required": [ + "tag" + ] + }, + "imagePullSecrets": {}, + "zxporter": { + "type": [ + "object", + "null" + ], + "properties": { + "dakrUrl": {}, + "targetNamespaces": {}, + "clusterToken": {}, + "patToken": {}, + "kubeContextName": {}, + "clusterId": {}, + "useSecretForToken": {}, + "logLevel": {}, + "disableGPUMetrics": {}, + "migPartedConfigMapName": {}, + "migPartedConfigMapNamespace": {}, + "nodeMetricsConcurrency": {}, + "nodemonRequestTimeout": {}, + "kubeletFallbackTimeout": {}, + "schedulerMetricsScrapeInterval": {}, + "kedaMetricsEndpoint": {}, + "existingSecret": { + "type": [ + "object", + "null" + ], + "properties": { + "name": {}, + "clusterTokenKey": {}, + "patTokenKey": {} + } + }, + "tokenCredentialsSecretName": {}, + "tokenSecretName": {}, + "tokenConfigMapName": {} + }, + "required": [ + "kubeContextName" + ] + }, + "resources": { + "properties": { + "requests": { + "properties": { + "cpu": {}, + "memory": {} + } + }, + "limits": { + "properties": {} + } + } + }, + "nodeSelector": { + "properties": {} + }, + "tolerations": {}, + "affinity": { + "properties": {} + }, + "topologySpreadConstraints": {}, + "highAvailability": { + "type": [ + "object", + "null" + ], + "properties": { + "enabled": {}, + "podDisruptionBudget": { + "type": [ + "object", + "null" + ], + "properties": { + "enabled": {}, + "minAvailable": {} + } + } + } + }, + "mpaServer": { + "type": [ + "object", + "null" + ], + "properties": { + "port": {} + } + }, + "global": { + "type": [ + "object", + "null" + ], + "properties": { + "k8sProvider": {}, + "gpuRuntime": { + "type": [ + "object", + "null" + ], + "properties": { + "mode": {}, + "runtimeClassName": {} + } + }, + "priorityClass": { + "type": [ + "object", + "null" + ], + "properties": { + "enabled": {} + } + } + }, + "required": [ + "k8sProvider", + "gpuRuntime", + "priorityClass" + ] + }, + "priorityClass": { + "type": [ + "object", + "null" + ], + "properties": { + "value": {}, + "description": {}, + "preemptionPolicy": {} + } + }, + "zxporter-nodemon": { + "type": [ + "object", + "null" + ], + "properties": { + "priorityClass": { + "type": [ + "object", + "null" + ], + "properties": { + "create": {} + } + } + }, + "required": [ + "priorityClass" + ] + } + }, + "required": [ + "image", + "zxporter", + "highAvailability", + "mpaServer", + "global", + "priorityClass" + ] +} diff --git a/helm-chart/zxporter/values.yaml b/helm-chart/zxporter/values.yaml index 9b4fede7..dfa5971a 100644 --- a/helm-chart/zxporter/values.yaml +++ b/helm-chart/zxporter/values.yaml @@ -1,19 +1,50 @@ +# Labels and annotations added to every object this chart creates, for clusters +# whose admission policy (OPA Gatekeeper, Kyverno) requires site-specific +# metadata before it will admit a resource. Without these, such a cluster +# rejects the chart's objects and the install fails. +# +# commonLabels: +# example.com/cost-center: platform +# example.com/tier: infra +# +# commonLabels/commonAnnotations land on both the object and its pod template -- +# Gatekeeper evaluates constraints against the Pod a workload would create, so +# metadata on the DaemonSet alone is not enough. podLabels/podAnnotations add +# pod-only metadata on top. +# +# Set these under `global:` to cover subcharts as well (zxporter-nodemon ships as one); a chart-local +# value applies only to this chart and wins on a collision. +# +# Keys this chart owns win over these and are dropped from the values: +# app.kubernetes.io/*, helm.sh/*, selector labels, and config checksums. +# Selector labels are never modified, so setting these on an existing release is +# a safe upgrade. +commonLabels: {} +commonAnnotations: {} +podLabels: {} +podAnnotations: {} # Default values for zxporter. # # REQUIRED FIELDS: # The following fields MUST be configured before deployment: # 1. zxporter.clusterToken OR zxporter.patToken (at least one required) -# 2. zxporter.kubeContextName - unique identifier for your cluster +# 2. zxporter.kubeContextName - unique identifier for your cluster (or +# global.clusterName, which takes precedence when set) # 3. global.k8sProvider - one of: aws, gcp, azure, oci, other # # For production deployments, we recommend using patToken for automatic token exchange. - image: repository: public.ecr.aws/devzeroinc/zxporter pullPolicy: IfNotPresent - tag: "v0.1.6" + # -- Image tag. The release sets it to the version being released. + # @default -- the released version, v-prefixed + tag: "v0.1.25" +# -- Pull secrets for the controller pod, merged with `global.imagePullSecrets` +# and deduplicated. Entries are secret names or `{name: ...}` objects. +imagePullSecrets: [] # ZXPorter configuration zxporter: + # global.dakrUrl, when set (the dz-platform umbrella sets it), takes precedence. dakrUrl: "https://dakr.devzero.io" targetNamespaces: "" # Cluster token for authentication with the DevZero platform @@ -33,7 +64,17 @@ zxporter: # - AKS: "my-aks-cluster" # - OCI: "ocid1.cluster.oc1.iad.aaaaaaaa..." # - Other: "production-cluster" or "staging-cluster" + # global.clusterName, when set (the dz-platform umbrella sets it), takes + # precedence and satisfies this requirement. kubeContextName: "" + # Existing DevZero cluster ID to attach this install to. Leave empty: a + # re-install finds its cluster by the kube-system namespace UID. Set it only to + # pin an install to a specific cluster; an ID that is not a live cluster of + # the token's team makes zxporter refuse to start. It is honoured when + # zxporter registers, that is when no token is stored (a fresh install, or + # after the token Secret was wiped). To move an existing install to another + # cluster, delete the devzero-zxporter-token Secret before restarting. + clusterId: "" # Set to true to store CLUSTER_TOKEN and PAT_TOKEN in Kubernetes Secrets instead of ConfigMap # Default is false for backward compatibility useSecretForToken: true @@ -62,6 +103,17 @@ zxporter: # API (the metrics fallback for nodes with no nodemon pod) before giving # up. Go duration string, e.g. "15s". kubeletFallbackTimeout: "15s" + # How often the SchedulerMetrics collector scrapes dz-scheduler's /metrics + # endpoint. Go duration string, e.g. "60s". Empty uses the collector's + # built-in default (60s) -- see DefaultSchedulerMetricsScrapeInterval in + # internal/collector/scheduler_metrics_collector.go for the reasoning. + schedulerMetricsScrapeInterval: "" + # Full URL of the KEDA operator /metrics endpoint scraped by the KedaScalerMetrics + # collector. Empty uses the collector's built-in default, which targets KEDA in the + # devzero-system namespace (DefaultKedaMetricsEndpoint in + # internal/collector/keda_scaler_metrics_collector.go). Override only when KEDA runs + # in a different namespace or on a different port. + kedaMetricsEndpoint: "" # Point to a pre-existing Secret that already holds the auth token(s), instead of # passing clusterToken/patToken through this values file. This is intended for # GitOps/SOPS workflows where you want to encrypt only the Secret, not the whole @@ -101,6 +153,18 @@ resources: nodeSelector: {} tolerations: [] affinity: {} +# Topology spread for the controller-manager Deployment. None by default. Needed +# for clusters whose admission policy requires a zone spread on Deployments +# (for example a Gatekeeper K8sRequiredTopologySpreadByZone constraint). An +# entry without labelSelector is scoped to the controller-manager's own pods: +# +# topologySpreadConstraints: +# - maxSkew: 1 +# topologyKey: topology.kubernetes.io/zone +# whenUnsatisfiable: ScheduleAnyway +# +# The nodemon DaemonSets are unaffected: they already run on every node. +topologySpreadConstraints: [] # High Availability Configuration highAvailability: enabled: true diff --git a/internal/collector/argo_cd_collector.go b/internal/collector/argo_cd_collector.go new file mode 100644 index 00000000..6345699a --- /dev/null +++ b/internal/collector/argo_cd_collector.go @@ -0,0 +1,455 @@ +// internal/collector/argo_cd_collector.go +package collector + +import ( + "context" + "fmt" + "sync" + "time" + + gen "github.com/devzero-inc/zxporter/gen/api/v1" + telemetry_logger "github.com/devzero-inc/zxporter/internal/logger" + "github.com/go-logr/logr" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/apimachinery/pkg/types" + "k8s.io/client-go/dynamic" + "k8s.io/client-go/dynamic/dynamicinformer" + "k8s.io/client-go/tools/cache" +) + +// Argo CD GVRs. These are Argo CD resources, NOT Argo Rollouts: both projects +// publish under the argoproj.io group, but Rollouts is a separate controller +// and ArgoRolloutsCollector continues to own it. +var ( + argoCDApplicationGVR = schema.GroupVersionResource{ + Group: "argoproj.io", + Version: "v1alpha1", + Resource: "applications", + } + + argoCDApplicationSetGVR = schema.GroupVersionResource{ + Group: "argoproj.io", + Version: "v1alpha1", + Resource: "applicationsets", + } +) + +// ArgoCDCollector watches one Argo CD kind through a dynamic informer. +// +// Applications and ApplicationSets share this implementation but NOT an +// instance: each collector emits exactly one ResourceType, because the send +// path labels a whole batch with the resource type of its first element +// (see CollectionManager.processCollectorChannel and +// CollectionPolicyReconciler.processCollectedResources). A single collector +// feeding both kinds into one batcher would ship ApplicationSets to DAKR +// labelled as Applications. Construct with NewArgoCDApplicationCollector or +// NewArgoCDApplicationSetCollector. +type ArgoCDCollector struct { + dynamicClient dynamic.Interface + gvr schema.GroupVersionResource + resourceType ResourceType + informerKey string + batchChan chan CollectedResource // Channel for individual resources -> input to batcher + resourceChan chan []CollectedResource // Channel for batched resources -> output from batcher + batcher *ResourcesBatcher + stopCh chan struct{} + informers map[string]cache.SharedIndexInformer + informerStopChs map[string]chan struct{} + namespaces []string + excludedApps map[types.NamespacedName]bool + logger logr.Logger + telemetryLogger telemetry_logger.Logger + + // mu guards the informer maps and stopped and, with it, the lifetime of + // batchChan: senders hold the read lock, Stop takes the write lock before + // closing the channel. + mu sync.RWMutex + stopped bool + + // stopOnce makes Stop idempotent. StopAll cancels the collector context -- + // waking the watcher goroutine in Start, which calls Stop -- and then calls + // Stop itself, so two Stops genuinely race. + stopOnce sync.Once +} + +// NewArgoCDApplicationCollector creates a collector for Argo CD Applications. +func NewArgoCDApplicationCollector( + dynamicClient dynamic.Interface, + namespaces []string, + excludedApplications []ExcludedArgoCDApplication, + maxBatchSize int, + maxBatchTime time.Duration, + logger logr.Logger, + telemetryLogger telemetry_logger.Logger, +) *ArgoCDCollector { + excluded := make(map[types.NamespacedName]bool, len(excludedApplications)) + for _, a := range excludedApplications { + excluded[types.NamespacedName{Namespace: a.Namespace, Name: a.Name}] = true + } + + return newArgoCDCollector( + dynamicClient, + argoCDApplicationGVR, + ArgoCDApplication, + "argo-cd-applications", + namespaces, + excluded, + maxBatchSize, + maxBatchTime, + logger.WithName("argo-cd-application-collector"), + telemetryLogger, + ) +} + +// NewArgoCDApplicationSetCollector creates a collector for Argo CD +// ApplicationSets. +func NewArgoCDApplicationSetCollector( + dynamicClient dynamic.Interface, + namespaces []string, + excludedApplicationSets []ExcludedArgoCDApplicationSet, + maxBatchSize int, + maxBatchTime time.Duration, + logger logr.Logger, + telemetryLogger telemetry_logger.Logger, +) *ArgoCDCollector { + excluded := make(map[types.NamespacedName]bool, len(excludedApplicationSets)) + for _, a := range excludedApplicationSets { + excluded[types.NamespacedName{Namespace: a.Namespace, Name: a.Name}] = true + } + + return newArgoCDCollector( + dynamicClient, + argoCDApplicationSetGVR, + ArgoCDApplicationSet, + "argo-cd-applicationsets", + namespaces, + excluded, + maxBatchSize, + maxBatchTime, + logger.WithName("argo-cd-applicationset-collector"), + telemetryLogger, + ) +} + +func newArgoCDCollector( + dynamicClient dynamic.Interface, + gvr schema.GroupVersionResource, + resourceType ResourceType, + informerKey string, + namespaces []string, + excludedApps map[types.NamespacedName]bool, + maxBatchSize int, + maxBatchTime time.Duration, + logger logr.Logger, + telemetryLogger telemetry_logger.Logger, +) *ArgoCDCollector { + batchChan := make(chan CollectedResource, 100) + resourceChan := make(chan []CollectedResource, 100) + batcher := NewResourcesBatcher(maxBatchSize, maxBatchTime, batchChan, resourceChan, logger) + + return &ArgoCDCollector{ + dynamicClient: dynamicClient, + gvr: gvr, + resourceType: resourceType, + informerKey: informerKey, + batchChan: batchChan, + resourceChan: resourceChan, + batcher: batcher, + stopCh: make(chan struct{}), + informers: make(map[string]cache.SharedIndexInformer), + informerStopChs: make(map[string]chan struct{}), + namespaces: namespaces, + excludedApps: excludedApps, + logger: logger, + telemetryLogger: telemetryLogger, + } +} + +// Start begins watching the collector's Argo CD kind. +func (c *ArgoCDCollector) Start(ctx context.Context) error { + c.logger.Info("Starting Argo CD collector", + "resource", c.gvr.Resource, + "namespaces", c.namespaces) + + // A stopped collector's channels are already closed, so restarting this + // instance would double-close them. Callers build a fresh collector instead. + c.mu.RLock() + stopped := c.stopped + c.mu.RUnlock() + if stopped { + return fmt.Errorf("Argo CD collector for %s has been stopped and cannot be restarted", c.gvr.Resource) + } + + var factory dynamicinformer.DynamicSharedInformerFactory + if len(c.namespaces) == 1 && c.namespaces[0] != "" { + factory = dynamicinformer.NewFilteredDynamicSharedInformerFactory( + c.dynamicClient, 0, c.namespaces[0], nil, + ) + } else { + factory = dynamicinformer.NewFilteredDynamicSharedInformerFactory( + c.dynamicClient, 0, "", nil, + ) + } + + informer := factory.ForResource(c.gvr).Informer() + + // Strip managedFields + last-applied-configuration from cached objects. + // Argo CD writes a large last-applied-configuration onto Applications it + // manages, so this is a meaningful memory saving here. + if err := informer.SetTransform(StripMetadataTransform); err != nil { + return fmt.Errorf("failed to set %s informer transform: %w", c.gvr.Resource, err) + } + + _, err := informer.AddEventHandler(cache.ResourceEventHandlerFuncs{ + AddFunc: func(obj interface{}) { + u, ok := obj.(*unstructured.Unstructured) + if !ok { + c.logger.Error(nil, "Failed to convert object to unstructured") + c.telemetryLogger.Report(gen.LogLevel_LOG_LEVEL_ERROR, "ArgoCDCollector_AddFunc", + "Failed to convert object to unstructured", fmt.Errorf("type assertion failed"), + map[string]string{"object_type": fmt.Sprintf("%T", obj), "resource": c.gvr.Resource}) + return + } + c.handleArgoCDEvent(u, EventTypeAdd) + }, + UpdateFunc: func(oldObj, newObj interface{}) { + newU, ok := newObj.(*unstructured.Unstructured) + if !ok { + c.logger.Error(nil, "Failed to convert new object to unstructured") + c.telemetryLogger.Report(gen.LogLevel_LOG_LEVEL_ERROR, "ArgoCDCollector_UpdateFunc", + "Failed to convert new object to unstructured", fmt.Errorf("type assertion failed"), + map[string]string{"object_type": fmt.Sprintf("%T", newObj), "resource": c.gvr.Resource}) + return + } + c.handleArgoCDEvent(newU, EventTypeUpdate) + }, + DeleteFunc: func(obj interface{}) { + u, ok := obj.(*unstructured.Unstructured) + if !ok { + if tombstone, ok := obj.(cache.DeletedFinalStateUnknown); ok { + if u, ok = tombstone.Obj.(*unstructured.Unstructured); ok { + c.handleArgoCDEvent(u, EventTypeDelete) + return + } + } + c.logger.Error(nil, "Failed to convert deleted object") + c.telemetryLogger.Report(gen.LogLevel_LOG_LEVEL_ERROR, "ArgoCDCollector_DeleteFunc", + "Failed to convert deleted object", fmt.Errorf("type assertion failed"), + map[string]string{"object_type": fmt.Sprintf("%T", obj), "resource": c.gvr.Resource}) + return + } + c.handleArgoCDEvent(u, EventTypeDelete) + }, + }) + if err != nil { + c.telemetryLogger.Report(gen.LogLevel_LOG_LEVEL_ERROR, "ArgoCDCollector_Start", + "Failed to add event handler to informer", err, + map[string]string{"resource": c.gvr.Resource}) + return fmt.Errorf("failed to add event handler to informer for %s: %w", c.gvr.Resource, err) + } + + c.mu.Lock() + c.informers[c.informerKey] = informer + c.informerStopChs[c.informerKey] = make(chan struct{}) + informerStopCh := c.informerStopChs[c.informerKey] + c.mu.Unlock() + + go informer.Run(informerStopCh) + + syncCtx, cancel := context.WithTimeout(ctx, 30*time.Second) + defer cancel() + + if !cache.WaitForCacheSync(syncCtx.Done(), informer.HasSynced) { + c.telemetryLogger.Report(gen.LogLevel_LOG_LEVEL_ERROR, "ArgoCDCollector_Start", + "Timeout waiting for cache to sync", fmt.Errorf("cache sync timeout"), + map[string]string{"resource": c.gvr.Resource, "timeout": "30s"}) + + c.mu.Lock() + if stopCh, ok := c.informerStopChs[c.informerKey]; ok { + close(stopCh) + delete(c.informerStopChs, c.informerKey) + delete(c.informers, c.informerKey) + } + c.mu.Unlock() + + return fmt.Errorf("timeout waiting for %s cache to sync", c.gvr.Resource) + } + + c.logger.Info("Successfully started informer", "resource", c.gvr.Resource) + c.batcher.start() + + stopCh := c.stopCh + go func() { + select { + case <-ctx.Done(): + if err := c.Stop(); err != nil { + c.logger.Error(err, "Error stopping Argo CD collector") + } + case <-stopCh: + // Channel was closed by Stop() + } + }() + + return nil +} + +// handleArgoCDEvent processes an Argo CD Application or ApplicationSet event. +func (c *ArgoCDCollector) handleArgoCDEvent(obj *unstructured.Unstructured, eventType EventType) { + name := obj.GetName() + namespace := obj.GetNamespace() + + if c.isExcluded(namespace, name) { + return + } + + key := fmt.Sprintf("%s/%s", namespace, name) + + resource := CollectedResource{ + ResourceType: c.resourceType, + Object: c.processArgoCDObject(obj), + Timestamp: time.Now(), + EventType: eventType, + Key: key, + } + + // The read lock keeps Stop from closing batchChan underneath an in-flight + // send; the stopCh arm releases a send that is blocked on a full buffer. + c.mu.RLock() + defer c.mu.RUnlock() + if c.stopped { + return + } + + select { + case c.batchChan <- resource: + case <-c.stopCh: + } +} + +// processArgoCDObject builds the payload DAKR reads. +// +// DAKR unmarshals this into a ProcessedArgoApplication or +// ProcessedArgoApplicationSet, which read only name, namespace and raw. The +// whole object goes out under raw because every derived column DAKR stores -- +// sync status, health, the managed-resource list -- is computed server-side +// from the typed Argo CD API rather than re-extracted here, which keeps one +// definition of those fields instead of two that can drift. +func (c *ArgoCDCollector) processArgoCDObject(obj *unstructured.Unstructured) map[string]interface{} { + return map[string]interface{}{ + "name": obj.GetName(), + "namespace": obj.GetNamespace(), + "uid": string(obj.GetUID()), + "resourceVersion": obj.GetResourceVersion(), + "raw": obj, + } +} + +// isExcluded reports whether a resource should be skipped. +func (c *ArgoCDCollector) isExcluded(namespace, name string) bool { + if len(c.namespaces) > 0 && c.namespaces[0] != "" { + found := false + for _, ns := range c.namespaces { + if ns == namespace { + found = true + break + } + } + if !found { + return true + } + } + + c.mu.RLock() + defer c.mu.RUnlock() + return c.excludedApps[types.NamespacedName{Namespace: namespace, Name: name}] +} + +// Stop gracefully shuts down the collector. +func (c *ArgoCDCollector) Stop() error { + c.logger.Info("Stopping Argo CD collector", "resource", c.gvr.Resource) + + // Concurrent callers block here until the first Stop has fully finished, so + // every close below happens exactly once. + c.stopOnce.Do(func() { + // 1. Release any handler goroutine parked on a full batchChan first. + // This has to happen before the write lock below: a parked sender + // holds the read lock, so taking the write lock while it is still + // blocked would deadlock. + close(c.stopCh) + c.logger.Info("Closed Argo CD collector stop channel", "resource", c.gvr.Resource) + + // 2. Taking the write lock waits for every in-flight handleArgoCDEvent + // to leave its send, so closing batchChan cannot panic a live + // sender. Later events see stopped and return without sending. + c.mu.Lock() + c.stopped = true + for key, stopCh := range c.informerStopChs { + c.logger.Info("Stopping informer", "resource", key) + close(stopCh) + } + c.informers = make(map[string]cache.SharedIndexInformer) + c.informerStopChs = make(map[string]chan struct{}) + close(c.batchChan) + c.mu.Unlock() + c.logger.Info("Closed Argo CD collector batch input channel", "resource", c.gvr.Resource) + + // 3. The batcher drains the closed batchChan and closes resourceChan. + if c.batcher != nil { + c.batcher.stop() + c.logger.Info("Argo CD collector batcher stopped", "resource", c.gvr.Resource) + } + }) + + return nil +} + +// GetResourceChannel returns the channel of batched resources. +func (c *ArgoCDCollector) GetResourceChannel() <-chan []CollectedResource { + return c.resourceChan +} + +// GetType returns the collector's type key. It must equal the ResourceType +// string, because the manager keys collectors by this value and the +// disabled-collectors policy is matched against ResourceType.String(). +func (c *ArgoCDCollector) GetType() string { + return c.resourceType.String() +} + +// IsAvailable reports whether this collector's Argo CD CRD is present. +// +// Each kind gates on its own CRD: a very old Argo CD install can predate the +// bundled ApplicationSet controller and serve applications but not +// applicationsets, in which case only the ApplicationSet collector is skipped +// (and retried later through the pending-collector factory). +func (c *ArgoCDCollector) IsAvailable(ctx context.Context) bool { + _, err := c.dynamicClient.Resource(c.gvr).List(ctx, metav1.ListOptions{Limit: 1}) + if err != nil { + c.logger.V(1).Info("Argo CD resources not available in the cluster", + "resource", c.gvr.Resource, "error", err.Error()) + c.telemetryLogger.Report(gen.LogLevel_LOG_LEVEL_WARN, "ArgoCDCollector_IsAvailable", + "Argo CD resources not available in the cluster", err, + map[string]string{"resource": c.gvr.Resource}) + return false + } + return true +} + +// AddResource manually adds an Argo CD resource to be processed. +func (c *ArgoCDCollector) AddResource(resource interface{}) error { + u, ok := resource.(*unstructured.Unstructured) + if !ok { + err := fmt.Errorf("expected *unstructured.Unstructured, got %T", resource) + c.telemetryLogger.Report(gen.LogLevel_LOG_LEVEL_ERROR, "ArgoCDCollector_AddResource", + "Invalid resource type", err, + map[string]string{ + "expected_type": "*unstructured.Unstructured", + "actual_type": fmt.Sprintf("%T", resource), + "resource": c.gvr.Resource, + }) + return err + } + c.handleArgoCDEvent(u, EventTypeAdd) + return nil +} diff --git a/internal/collector/argo_cd_collector_test.go b/internal/collector/argo_cd_collector_test.go new file mode 100644 index 00000000..9ab734f7 --- /dev/null +++ b/internal/collector/argo_cd_collector_test.go @@ -0,0 +1,194 @@ +package collector + +import ( + "testing" + "time" + + "github.com/go-logr/logr" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" +) + +func argoCDApplicationFixture(namespace, name string) *unstructured.Unstructured { + return &unstructured.Unstructured{ + Object: map[string]interface{}{ + "apiVersion": "argoproj.io/v1alpha1", + "kind": "Application", + "metadata": map[string]interface{}{ + "name": name, + "namespace": namespace, + "uid": "app-uid-1", + "resourceVersion": "4211", + }, + "spec": map[string]interface{}{ + "project": "default", + "destination": map[string]interface{}{ + "server": "https://kubernetes.default.svc", + "namespace": "guestbook", + }, + }, + "status": map[string]interface{}{ + "sync": map[string]interface{}{"status": "Synced"}, + "health": map[string]interface{}{"status": "Healthy"}, + }, + }, + } +} + +func argoCDApplicationSetFixture(namespace, name string) *unstructured.Unstructured { + return &unstructured.Unstructured{ + Object: map[string]interface{}{ + "apiVersion": "argoproj.io/v1alpha1", + "kind": "ApplicationSet", + "metadata": map[string]interface{}{ + "name": name, + "namespace": namespace, + "uid": "appset-uid-1", + "resourceVersion": "88", + }, + }, + } +} + +// Applications and ApplicationSets must never share a collector instance: the +// send path labels a whole batch with the resource type of its first element, +// so a collector emitting both kinds would ship ApplicationSets to DAKR +// labelled as Applications. +func TestArgoCDCollector_EachKindHasItsOwnResourceType(t *testing.T) { + apps := NewArgoCDApplicationCollector(nil, nil, nil, 10, time.Second, logr.Discard(), nil) + appSets := NewArgoCDApplicationSetCollector(nil, nil, nil, 10, time.Second, logr.Discard(), nil) + + assert.Equal(t, ArgoCDApplication, apps.resourceType) + assert.Equal(t, "applications", apps.gvr.Resource) + + assert.Equal(t, ArgoCDApplicationSet, appSets.resourceType) + assert.Equal(t, "applicationsets", appSets.gvr.Resource) + + assert.NotEqual(t, apps.resourceType, appSets.resourceType) + assert.NotEqual(t, apps.informerKey, appSets.informerKey) +} + +// GetType is the key the manager stores collectors under and the value the +// disabled-collectors policy is matched against, so it must equal the +// ResourceType string rather than any other spelling. +func TestArgoCDCollector_GetTypeMatchesResourceTypeString(t *testing.T) { + apps := NewArgoCDApplicationCollector(nil, nil, nil, 10, time.Second, logr.Discard(), nil) + appSets := NewArgoCDApplicationSetCollector(nil, nil, nil, 10, time.Second, logr.Discard(), nil) + + assert.Equal(t, "argo_cd_application", apps.GetType()) + assert.Equal(t, ArgoCDApplication.String(), apps.GetType()) + + assert.Equal(t, "argo_cd_application_set", appSets.GetType()) + assert.Equal(t, ArgoCDApplicationSet.String(), appSets.GetType()) +} + +func TestArgoCDCollector_HandleEventEmitsCollectorResourceType(t *testing.T) { + tests := []struct { + name string + collector *ArgoCDCollector + obj *unstructured.Unstructured + wantType ResourceType + wantKey string + wantUID string + wantRevision string + }{ + { + name: "application", + collector: NewArgoCDApplicationCollector(nil, nil, nil, 10, time.Second, logr.Discard(), nil), + obj: argoCDApplicationFixture("argocd", "guestbook"), + wantType: ArgoCDApplication, + wantKey: "argocd/guestbook", + wantUID: "app-uid-1", + wantRevision: "4211", + }, + { + name: "applicationset", + collector: NewArgoCDApplicationSetCollector(nil, nil, nil, 10, time.Second, logr.Discard(), nil), + obj: argoCDApplicationSetFixture("argocd", "guestbook-set"), + wantType: ArgoCDApplicationSet, + wantKey: "argocd/guestbook-set", + wantUID: "appset-uid-1", + wantRevision: "88", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + tt.collector.handleArgoCDEvent(tt.obj, EventTypeUpdate) + + select { + case res := <-tt.collector.batchChan: + assert.Equal(t, tt.wantType, res.ResourceType) + assert.Equal(t, EventTypeUpdate, res.EventType) + assert.Equal(t, tt.wantKey, res.Key) + + payload, ok := res.Object.(map[string]interface{}) + require.True(t, ok, "payload should be a map, got %T", res.Object) + assert.Equal(t, tt.obj.GetName(), payload["name"]) + assert.Equal(t, "argocd", payload["namespace"]) + assert.Equal(t, tt.wantUID, payload["uid"]) + assert.Equal(t, tt.wantRevision, payload["resourceVersion"]) + // DAKR derives every stored column from raw, so the whole + // object has to survive the trip. + assert.Equal(t, tt.obj, payload["raw"]) + case <-time.After(time.Second): + t.Fatal("expected a collected resource on the batch channel") + } + }) + } +} + +func TestArgoCDCollector_DropsExcludedApplication(t *testing.T) { + c := NewArgoCDApplicationCollector( + nil, nil, + []ExcludedArgoCDApplication{{Namespace: "argocd", Name: "noisy-app"}}, + 10, time.Second, logr.Discard(), nil, + ) + + c.handleArgoCDEvent(argoCDApplicationFixture("argocd", "noisy-app"), EventTypeAdd) + assert.Empty(t, c.batchChan, "an excluded application must not be collected") + + c.handleArgoCDEvent(argoCDApplicationFixture("argocd", "guestbook"), EventTypeAdd) + assert.Len(t, c.batchChan, 1, "a non-excluded application must still be collected") +} + +// Each constructor builds the exclusion map from its own exclusion type; the +// ApplicationSet one is easy to leave empty, and doing so silently disables +// ApplicationSet exclusions. +func TestArgoCDCollector_DropsExcludedApplicationSet(t *testing.T) { + c := NewArgoCDApplicationSetCollector( + nil, nil, + []ExcludedArgoCDApplicationSet{{Namespace: "argocd", Name: "noisy-set"}}, + 10, time.Second, logr.Discard(), nil, + ) + + c.handleArgoCDEvent(argoCDApplicationSetFixture("argocd", "noisy-set"), EventTypeAdd) + assert.Empty(t, c.batchChan, "an excluded applicationset must not be collected") + + c.handleArgoCDEvent(argoCDApplicationSetFixture("argocd", "wanted-set"), EventTypeAdd) + assert.Len(t, c.batchChan, 1, "a non-excluded applicationset must still be collected") +} + +func TestArgoCDCollector_DropsResourceOutsideTargetNamespaces(t *testing.T) { + c := NewArgoCDApplicationCollector( + nil, []string{"argocd"}, nil, 10, time.Second, logr.Discard(), nil, + ) + + c.handleArgoCDEvent(argoCDApplicationFixture("other-ns", "guestbook"), EventTypeAdd) + assert.Empty(t, c.batchChan, "an application outside the target namespaces must not be collected") + + c.handleArgoCDEvent(argoCDApplicationFixture("argocd", "guestbook"), EventTypeAdd) + assert.Len(t, c.batchChan, 1, "an application inside the target namespaces must be collected") +} + +// An empty target-namespace entry means cluster-wide collection, which is how +// the controller expresses "no namespace restriction". +func TestArgoCDCollector_EmptyNamespaceMeansClusterWide(t *testing.T) { + c := NewArgoCDApplicationCollector( + nil, []string{""}, nil, 10, time.Second, logr.Discard(), nil, + ) + + c.handleArgoCDEvent(argoCDApplicationFixture("any-namespace", "guestbook"), EventTypeAdd) + assert.Len(t, c.batchChan, 1) +} diff --git a/internal/collector/change_detector.go b/internal/collector/change_detector.go index 4eac60a3..a98eb876 100644 --- a/internal/collector/change_detector.go +++ b/internal/collector/change_detector.go @@ -8,8 +8,26 @@ import ( type ChangeDetectionStatus string const ( - IgnoreChanges ChangeDetectionStatus = "IgnoreChanges" - PushChanges ChangeDetectionStatus = "PushChanges" + // IgnoreChanges means the object did not move at all (same resourceVersion). + IgnoreChanges ChangeDetectionStatus = "IgnoreChanges" + + // PushChanges means the ObjectMeta itself changed and the update must be sent. + PushChanges ChangeDetectionStatus = "PushChanges" + + // UnknownChanges means the object moved but the ObjectMeta alone cannot say + // whether it moved in a way we care about. Callers MUST fall through to + // their own spec and status comparisons on this value: + // + // if changed == PushChanges { + // return true + // } + // // spec/status comparisons follow + // + // Returning "changed == PushChanges" for every non-IgnoreChanges value + // instead silently drops every status-only update — a rollout advancing, + // ResourceQuota usage moving, a Job finishing — because those bump + // resourceVersion without touching labels or annotations, which left the + // spec and status comparisons below unreachable. That was a real bug. UnknownChanges ChangeDetectionStatus = "UnknownChanges" ) diff --git a/internal/collector/clusterrole_collector.go b/internal/collector/clusterrole_collector.go index c4ecd921..332b531a 100644 --- a/internal/collector/clusterrole_collector.go +++ b/internal/collector/clusterrole_collector.go @@ -163,8 +163,8 @@ func (c *ClusterRoleCollector) clusterRoleChanged(oldRole, newRole *rbacv1.Clust oldRole.ObjectMeta, newRole.ObjectMeta, ) - if changed != IgnoreChanges { - return changed == PushChanges + if changed == PushChanges { + return true } // Check for rules changes diff --git a/internal/collector/clusterrolebinding_collector.go b/internal/collector/clusterrolebinding_collector.go index 60952f3b..5be7a369 100644 --- a/internal/collector/clusterrolebinding_collector.go +++ b/internal/collector/clusterrolebinding_collector.go @@ -165,8 +165,8 @@ func (c *ClusterRoleBindingCollector) clusterRoleBindingChanged( oldCRB.ObjectMeta, newCRB.ObjectMeta, ) - if changed != IgnoreChanges { - return changed == PushChanges + if changed == PushChanges { + return true } // Check for role reference changes if oldCRB.RoleRef.Kind != newCRB.RoleRef.Kind || diff --git a/internal/collector/collector_type_aliases.go b/internal/collector/collector_type_aliases.go new file mode 100644 index 00000000..cfcd7c09 --- /dev/null +++ b/internal/collector/collector_type_aliases.go @@ -0,0 +1,47 @@ +package collector + +// legacyCollectorTypeAliases maps collector type names that older zxporter versions used +// as registry keys (and, for "statefulset" and "daemonset", documented as +// DisabledCollectors values) to the ResourceType a collector is registered under today. +// +// Those collectors' GetType() used to return these spellings, so a value written against +// them in a CollectionPolicy's disabledCollectors or in DISABLED_COLLECTORS must keep +// working. CanonicalCollectorType is the single place they are translated. +var legacyCollectorTypeAliases = map[string]ResourceType{ + "statefulset": StatefulSet, + "daemonset": DaemonSet, + "persistentvolume": PersistentVolume, + "persistentvolumeclaim": PersistentVolumeClaim, + "crd": CustomResourceDefinition, + "scaledjob": KedaScaledJob, + "scaledobject": KedaScaledObject, +} + +// CanonicalCollectorType returns the registry key for a user-supplied collector type, +// translating a legacy spelling; any other value is returned unchanged. +func CanonicalCollectorType(name string) string { + if resourceType, ok := legacyCollectorTypeAliases[name]; ok { + return resourceType.String() + } + return name +} + +// CanonicalCollectorTypes applies CanonicalCollectorType to every entry, dropping +// duplicates that normalization creates (e.g. both "daemonset" and "daemon_set"). +// It returns nil for an empty input so an unset list stays unset. +func CanonicalCollectorTypes(names []string) []string { + if len(names) == 0 { + return nil + } + seen := make(map[string]bool, len(names)) + out := make([]string, 0, len(names)) + for _, name := range names { + canonical := CanonicalCollectorType(name) + if seen[canonical] { + continue + } + seen[canonical] = true + out = append(out, canonical) + } + return out +} diff --git a/internal/collector/composite_request_count_test.go b/internal/collector/composite_request_count_test.go index 2e798995..193bae94 100644 --- a/internal/collector/composite_request_count_test.go +++ b/internal/collector/composite_request_count_test.go @@ -138,7 +138,7 @@ func TestCollectors_CompositeAvoids4NLegacyCalls(t *testing.T) { nc := &NodeCollector{ metricsClient: &metricsv1.Clientset{}, nodemonClient: newClient(), - kubeletClient: NewKubeletSummaryClient(nil, logr.Discard(), 0), + kubeletClient: newInertFakeKubeletClient(t), nodeInformer: nodeInformer, batchChan: nodeBatch, config: NodeCollectorConfig{DisableGPUMetrics: false}, diff --git a/internal/collector/container_resource_collector.go b/internal/collector/container_resource_collector.go index 2a528017..ab38f3a8 100644 --- a/internal/collector/container_resource_collector.go +++ b/internal/collector/container_resource_collector.go @@ -658,15 +658,9 @@ func (c *ContainerResourceCollector) processContainerMetrics( } } - // For OOM-killed containers, the post-restart memory usage from metrics-server - // reflects the NEW container (e.g. 12MB), not the pre-OOM usage (~limit). - // This would cause the operator's fast-path to multiply the wrong base value, - // potentially recommending LESS memory than before. Use the memory limit as the - // usage proxy for OOM events, consistent with BuildOOMSnapshot. - effectiveMemoryUsage := memoryUsage - if lastTerminationReason == ReasonOOMKilled && memoryLimitBytes > 0 && memoryUsage < memoryLimitBytes { - effectiveMemoryUsage = memoryLimitBytes - } + effectiveMemoryUsage := oomAdjustedMemoryUsage( + memoryUsage, memoryLimitBytes, lastTerminationReason, containerStatus, time.Now(), + ) // Create resource data with both metrics and pod info containerKey := fmt.Sprintf("%s/%s/%s", pod.Namespace, pod.Name, containerMetrics.Name) @@ -1191,6 +1185,63 @@ func (c *ContainerResourceCollector) AddResource(resource interface{}) error { return nil } +// oomUsageProxyWindow bounds how long after an OOM kill (or the restart that +// followed it) the memory limit stands in for the container's measured usage. +// It spans many collection cycles, so the kill is still reported, but is far +// shorter than any recommendation cycle. +const oomUsageProxyWindow = 5 * time.Minute + +// oomUsageProxyClockSkew is how far in the future a kubelet-reported timestamp +// may be and still count. The timestamps come from another node's clock; a +// larger skew would otherwise keep the proxy alive for as long as that clock +// runs ahead, which is the stuck signal the window exists to prevent. +const oomUsageProxyClockSkew = time.Minute + +// oomAdjustedMemoryUsage returns the memory usage to report for a container. +// +// Right after an OOM kill, metrics-server measures the NEW container (e.g. +// 12MB), not the pre-OOM usage (~limit). Reporting that would let a consumer +// multiply the wrong base and recommend LESS memory than before, so the memory +// limit stands in for usage, consistent with BuildOOMSnapshot. +// +// That stand-in applies only while the kill is recent. LastTerminationState +// keeps saying OOMKilled until the container terminates again, which with +// in-place resize can be days, and a container that has run for that long is +// measured correctly. Substituting the limit for the whole time made usage +// track every new limit exactly, which control-plane OOM detection ("usage at +// >= 95% of the limit") read as a fresh OOM on every cycle: one prod pod was +// resized 164 MiB -> 31 GiB in 1.5x steps while its RSS stayed under 80 MiB. +func oomAdjustedMemoryUsage( + usage, limit int64, + lastTerminationReason string, + status *corev1.ContainerStatus, + now time.Time, +) int64 { + if lastTerminationReason != ReasonOOMKilled || limit <= 0 || usage >= limit { + return usage + } + // A StartError normalized to OOMKilled also lands here; either way the kill + // time is FinishedAt. Without one there is no evidence the kill is recent. + if status == nil || status.LastTerminationState.Terminated == nil { + return usage + } + // Measure from the later of the kill and the current run's start. Under + // CrashLoopBackOff the restart can come up to five minutes after the kill, + // and the low post-restart reading the proxy hides only exists from then on. + // An in-place resize does not restart the container, so neither moves. + ref := status.LastTerminationState.Terminated.FinishedAt.Time + if running := status.State.Running; running != nil && running.StartedAt.After(ref) { + ref = running.StartedAt.Time + } + if ref.IsZero() { + return usage + } + if age := now.Sub(ref); age > oomUsageProxyWindow || age < -oomUsageProxyClockSkew { + return usage + } + return limit +} + // containerMemoryBreakdown holds the raw nodemon-sourced memory values needed to // derive the active/inactive cache split, plus the RSS value. workingSet, usage, // and rss come from the kubelet stats/summary API; cache and swap come from the diff --git a/internal/collector/container_resource_collector_test.go b/internal/collector/container_resource_collector_test.go index 500bf67b..f0977f24 100644 --- a/internal/collector/container_resource_collector_test.go +++ b/internal/collector/container_resource_collector_test.go @@ -498,3 +498,56 @@ func TestProcessContainerMetrics_UnknownContainerSkipped(t *testing.T) { // expected: nothing emitted } } + +func TestOOMAdjustedMemoryUsage(t *testing.T) { + now := time.Date(2026, 10, 1, 12, 0, 0, 0, time.UTC) + const usage, limit = int64(65 << 20), int64(1 << 30) + + terminatedAt := func(at time.Time) *corev1.ContainerStatus { + return &corev1.ContainerStatus{ + LastTerminationState: corev1.ContainerState{ + Terminated: &corev1.ContainerStateTerminated{ + Reason: ReasonOOMKilled, + FinishedAt: metav1.NewTime(at), + }, + }, + } + } + + restartedAt := func(killed, started time.Time) *corev1.ContainerStatus { + s := terminatedAt(killed) + s.State.Running = &corev1.ContainerStateRunning{StartedAt: metav1.NewTime(started)} + return s + } + + tests := []struct { + name string + reason string + limit int64 + status *corev1.ContainerStatus + want int64 + }{ + {"reports the limit when the OOM kill just happened", ReasonOOMKilled, limit, terminatedAt(now.Add(-time.Minute)), limit}, + // CrashLoopBackOff at its 5m cap: the restart, not the kill, opens the window. + {"reports the limit right after a backed-off restart", ReasonOOMKilled, limit, restartedAt(now.Add(-6*time.Minute), now.Add(-time.Minute)), limit}, + {"reports measured usage once the restarted run is past the window", ReasonOOMKilled, limit, restartedAt(now.Add(-12*time.Minute), now.Add(-6*time.Minute)), usage}, + // In-place resize keeps the same run, so a days-old start does not reopen it. + {"reports measured usage when the run started days ago", ReasonOOMKilled, limit, restartedAt(now.Add(-48*time.Hour), now.Add(-48*time.Hour+10*time.Second)), usage}, + {"reports the limit when the kill time is slightly ahead of this clock", ReasonOOMKilled, limit, terminatedAt(now.Add(30 * time.Second)), limit}, + {"reports measured usage when the kill time is far ahead of this clock", ReasonOOMKilled, limit, terminatedAt(now.Add(time.Hour)), usage}, + {"reports the limit at the edge of the window", ReasonOOMKilled, limit, terminatedAt(now.Add(-oomUsageProxyWindow)), limit}, + // The prod incident: the container kept running for days after one OOM + // while in-place resizes raised its limit, and status still said OOMKilled. + {"reports measured usage when the OOM kill is days old", ReasonOOMKilled, limit, terminatedAt(now.Add(-48 * time.Hour)), usage}, + {"reports measured usage just past the window", ReasonOOMKilled, limit, terminatedAt(now.Add(-oomUsageProxyWindow - time.Second)), usage}, + {"reports measured usage when the kill time is unknown", ReasonOOMKilled, limit, terminatedAt(time.Time{}), usage}, + {"reports measured usage without a container status", ReasonOOMKilled, limit, nil, usage}, + {"reports measured usage when the last termination was not an OOM", "Error", limit, terminatedAt(now), usage}, + {"reports measured usage when the container has no limit", ReasonOOMKilled, 0, terminatedAt(now), usage}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + require.Equal(t, tt.want, oomAdjustedMemoryUsage(usage, tt.limit, tt.reason, tt.status, now)) + }) + } +} diff --git a/internal/collector/crd_collector.go b/internal/collector/crd_collector.go index 246c2fb0..9cbfe450 100644 --- a/internal/collector/crd_collector.go +++ b/internal/collector/crd_collector.go @@ -200,7 +200,7 @@ func (c *CRDCollector) GetResourceChannel() <-chan []CollectedResource { } func (c *CRDCollector) GetType() string { - return "crd" + return CustomResourceDefinition.String() } func (c *CRDCollector) IsAvailable(ctx context.Context) bool { diff --git a/internal/collector/cronjob_collector.go b/internal/collector/cronjob_collector.go index 560427e3..69aa564e 100644 --- a/internal/collector/cronjob_collector.go +++ b/internal/collector/cronjob_collector.go @@ -169,8 +169,8 @@ func (c *CronJobCollector) cronJobChanged(oldCronJob, newCronJob *batchv1.CronJo oldCronJob.ObjectMeta, newCronJob.ObjectMeta, ) - if changed != IgnoreChanges { - return changed == PushChanges + if changed == PushChanges { + return true } // Check for schedule changes diff --git a/internal/collector/csidriver_collector.go b/internal/collector/csidriver_collector.go index 27c72b5a..90f8902c 100644 --- a/internal/collector/csidriver_collector.go +++ b/internal/collector/csidriver_collector.go @@ -162,8 +162,8 @@ func (c *CSIDriverCollector) csiDriverChanged(oldDriver, newDriver *storagev1.CS oldDriver.ObjectMeta, newDriver.ObjectMeta, ) - if changed != IgnoreChanges { - return changed == PushChanges + if changed == PushChanges { + return true } // Check for spec changes diff --git a/internal/collector/csistoragecapacity_collector.go b/internal/collector/csistoragecapacity_collector.go index be786676..f33a84be 100644 --- a/internal/collector/csistoragecapacity_collector.go +++ b/internal/collector/csistoragecapacity_collector.go @@ -174,8 +174,8 @@ func (c *CSIStorageCapacityCollector) csiStorageCapacityChanged( oldCSC.ObjectMeta, newCSC.ObjectMeta, ) - if changed != IgnoreChanges { - return changed == PushChanges + if changed == PushChanges { + return true } // Check for capacity changes diff --git a/internal/collector/daemonset_collector.go b/internal/collector/daemonset_collector.go index d6655e7b..5bae5c8f 100644 --- a/internal/collector/daemonset_collector.go +++ b/internal/collector/daemonset_collector.go @@ -170,8 +170,8 @@ func (c *DaemonSetCollector) daemonSetChanged(oldDaemonSet, newDaemonSet *appsv1 oldDaemonSet.ObjectMeta, newDaemonSet.ObjectMeta, ) - if changed != IgnoreChanges { - return changed == PushChanges + if changed == PushChanges { + return true } // Check for status changes @@ -290,7 +290,7 @@ func (c *DaemonSetCollector) GetResourceChannel() <-chan []CollectedResource { // GetType returns the type of resource this collector handles func (c *DaemonSetCollector) GetType() string { - return "daemonset" + return DaemonSet.String() } // IsAvailable checks if DaemonSet resources can be accessed in the cluster diff --git a/internal/collector/deployment_collector.go b/internal/collector/deployment_collector.go index 544c3545..3858b8d2 100644 --- a/internal/collector/deployment_collector.go +++ b/internal/collector/deployment_collector.go @@ -172,8 +172,8 @@ func (c *DeploymentCollector) deploymentChanged( oldDeployment.ObjectMeta, newDeployment.ObjectMeta, ) - if changed != IgnoreChanges { - return changed == PushChanges + if changed == PushChanges { + return true } // Check for spec changes diff --git a/internal/collector/deployment_collector_test.go b/internal/collector/deployment_collector_test.go new file mode 100644 index 00000000..138ba7b7 --- /dev/null +++ b/internal/collector/deployment_collector_test.go @@ -0,0 +1,68 @@ +package collector + +import ( + "testing" + + appsv1 "k8s.io/api/apps/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// deploymentFixture builds a Deployment whose only interesting fields are the +// ones the change detector inspects. +func deploymentFixture(resourceVersion string, updatedReplicas int32) *appsv1.Deployment { + replicas := int32(3) + return &appsv1.Deployment{ + ObjectMeta: metav1.ObjectMeta{ + Name: "api", + Namespace: "team-a", + ResourceVersion: resourceVersion, + Generation: 1, + Labels: map[string]string{"app": "api"}, + }, + Spec: appsv1.DeploymentSpec{ + Replicas: &replicas, + }, + Status: appsv1.DeploymentStatus{ + Replicas: 3, + ReadyReplicas: 3, + AvailableReplicas: 3, + UpdatedReplicas: updatedReplicas, + ObservedGeneration: 1, + }, + } +} + +func TestDeploymentChanged(t *testing.T) { + c := &DeploymentCollector{} + + tests := []struct { + name string + old *appsv1.Deployment + new *appsv1.Deployment + want bool + }{ + { + // Rollout progress: only status moves, so the ObjectMeta check + // returns UnknownChanges and the status comparison below it must + // still run. + name: "status.updatedReplicas changed with a bumped resourceVersion", + old: deploymentFixture("100", 1), + new: deploymentFixture("101", 3), + want: true, + }, + { + name: "only resourceVersion changed", + old: deploymentFixture("100", 3), + new: deploymentFixture("101", 3), + want: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := c.deploymentChanged(tt.old, tt.new); got != tt.want { + t.Errorf("deploymentChanged() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/internal/collector/disruption_block.go b/internal/collector/disruption_block.go index c7cfbfcf..0e864ae2 100644 --- a/internal/collector/disruption_block.go +++ b/internal/collector/disruption_block.go @@ -68,6 +68,13 @@ const ( // dakr REJECTS an event carrying any other value — k8sconverters gates the field against // models.AllDisruptionBlockReasons and drops the whole item on an unknown one — so these // strings are exact, case-sensitive contract, not labels. +// +// This set is a strict SUBSET of models.AllDisruptionBlockReasons, and that is not a bug. +// Three of dakr's buckets have no producer here because they cannot be derived from live +// cluster state at all: DisruptionBudget and PodsWouldNotSchedule have always been +// read-side-only, and PodNominated joined them when its at-source derivation was removed as +// unsound (see classifyDisruptionBlocked). The gate is one-directional — dakr accepts the +// union, so a value this file never emits is still ingested from an older agent that does. const ( disruptionBlockReasonPDBViolation = "PDBViolation" disruptionBlockReasonDoNotDisruptAnnotation = "DoNotDisruptAnnotation" @@ -113,8 +120,10 @@ type collectorRegistry interface { // That indirection is the point: the reconciler replaces individual collectors in place // when a CollectionPolicy changes (see restartCollectors), so a pointer captured at wiring // time would silently go stale and start reading a stopped informer's frozen cache. A -// GetCollector lookup is a mutex-guarded map read, and the only caller is a -// DisruptionBlocked event, so resolving per event costs nothing worth optimising. +// GetCollector lookup is a lock-free map read, so resolving per event costs nothing worth +// optimising. It must stay lock-free: these lookups run inside informer event handlers +// (DisruptionBlocked events here, every Node event via NodeCollector.nodeManagedByKarpenter), +// and a handler blocked on the manager mutex stalls that informer's whole listener. // // A nil *disruptionSources, or any individual collector being absent or disabled, degrades // to "that input is unknown" rather than failing — see classifyDisruptionBlocked. @@ -148,12 +157,23 @@ func (s *disruptionSources) nodeClaims() nodeClaimSource { // disruptionBlockClassification is the outcome of classifying one DisruptionBlocked event. type disruptionBlockClassification struct { - // Reason is always one of the four disruptionBlockReason* values. + // Reason is always one of the disruptionBlockReason* values. Reason string // BlockingPDBName is "namespace/name" of the PDB that blocked eviction, set only // alongside PDBViolation. Nil rather than "" so dakr can write a real NULL: "no PDB was // involved" is not the same fact as "a PDB with an empty name". BlockingPDBName *string + // RelatedPodName is "namespace/name" of the pod behind the classification. Only + // DoNotDisruptAnnotation sets it today, naming the pod that carries the annotation. + // Nil, not "", for the same reason as BlockingPDBName — and because that reason IS + // reachable without a specific pod identity in hand (nodeClaimBlocked's node-level + // annotation path has no pod at all), so nil must mean "not applicable here", not + // "lookup failed". + // + // The column it feeds (k8s_events.related_pod_name) is documented for PodNominated too, + // which this file no longer produces — see classifyDisruptionBlocked. That is a + // forward-compatible column, not a contract this classifier owes. + RelatedPodName *string } // isKarpenterDisruptionBlocked reports whether an event is the Karpenter DisruptionBlocked @@ -189,10 +209,39 @@ func isKarpenterDisruptionBlocked(event *corev1.Event) bool { // pod's annotation and returns on the first hit, and only then consults PDBs), so a // node with both produces the annotation event upstream too. // 4. Other — the node-level blocks that are neither: uninitialized, already deleting, -// nominated for a pending pod, missing its NodePool label. Also where an -// unresolvable node, an unavailable collector, or a stale cache lands. It is a real -// classification, not a failure: the verbatim message survives in k8s_events.message, -// which is the only place the detail exists. +// missing its NodePool label. Also where an unresolvable node, an unavailable +// collector, or a stale cache lands. It is a real classification, not a failure: the +// verbatim message survives in k8s_events.message, which is the only place that detail +// exists. +// +// PodNominated is deliberately NOT derived here, and must not be re-added. The obvious +// signal for it is the wrong one, which is why the check that used to sit at precedence 4 +// was deleted rather than reordered: +// +// - The field it read, pod.Status.NominatedNodeName, is kube-scheduler PREEMPTION state. +// Karpenter's own helper for that field is named IsPreempting +// (pkg/utils/pod/scheduling.go) and Karpenter never consults it when deciding whether a +// node is nominated. +// - Karpenter's "node is nominated for a pending pod" block comes from +// StateNode.Nominated(clk), which reads nominatedUntil: an unexported, in-memory, +// providerID-keyed timestamp the provisioning scheduler stamps for +// max(2 x BatchMaxDuration, 10s) — about 20 seconds with the defaults +// (scheduling/scheduler.go calls Cluster.NominateNodeForPod, which calls +// StateNode.Nominate). Verified identical in sigs.k8s.io/karpenter v1.7.1 and v1.12.1. +// - That state is therefore NOT observable through the Kubernetes API at all: no CRD +// field, no annotation, no status condition, no event other than the DisruptionBlocked +// message itself. No amount of extra cluster state gets this bucket right at the source. +// +// So the read-side message parse owns it — disruptionBlockMessageRules in dakr's +// repositories/event_clickhouse.go — and for this one bucket that parse is exact rather +// than heuristic: "node is nominated for a pending pod" is a fixed string with no +// interpolation, so there is nothing version-fragile to hedge against. +// +// The deleted derivation had never once fired: across 1,156 preprod rows this classifier +// actually produced, PodNominated appeared zero times, while roughly 36% of production +// blocking events (folded per event) carry that message. The nodes that were BOTH +// preempting and blocked landed in DoNotDisruptAnnotation or PDBViolation, which is also +// the correct answer, so nothing regressed by removing it. // // Never returns an empty Reason. func classifyDisruptionBlocked( @@ -229,7 +278,11 @@ func classifyDisruptionBlocked( for _, pod := range pods { if podOptedOutOfDisruption(pod) { - return disruptionBlockClassification{Reason: disruptionBlockReasonDoNotDisruptAnnotation} + podName := pod.Namespace + "/" + pod.Name + return disruptionBlockClassification{ + Reason: disruptionBlockReasonDoNotDisruptAnnotation, + RelatedPodName: &podName, + } } } if nodeClaimBlocked { diff --git a/internal/collector/disruption_block_test.go b/internal/collector/disruption_block_test.go index 22ea6e48..3bc1b440 100644 --- a/internal/collector/disruption_block_test.go +++ b/internal/collector/disruption_block_test.go @@ -155,6 +155,15 @@ func blockedPod(namespace, name string, podLabels, annotations map[string]string } } +// preemptingPod is a pod whose Status.NominatedNodeName is set — kube-scheduler preemption +// state, which is exactly the signal a deleted PodNominated derivation used to read. Only +// TestClassifyDisruptionBlocked_NeverDerivesPodNominated needs it. +func preemptingPod(namespace, name, nominatedNodeName string, podLabels, annotations map[string]string) *corev1.Pod { + pod := blockedPod(namespace, name, podLabels, annotations) + pod.Status.NominatedNodeName = nominatedNodeName + return pod +} + func blockingPDB(namespace, name string, matchLabels map[string]string, disruptionsAllowed int32) *policyv1.PodDisruptionBudget { return &policyv1.PodDisruptionBudget{ ObjectMeta: metav1.ObjectMeta{Namespace: namespace, Name: name}, @@ -331,6 +340,8 @@ func TestClassifyDisruptionBlocked(t *testing.T) { assert.Equal(t, disruptionBlockReasonDoNotDisruptAnnotation, got.Reason) assert.Nil(t, got.BlockingPDBName) + require.NotNil(t, got.RelatedPodName) + assert.Equal(t, "payments/ledger-0", *got.RelatedPodName) }) // Only the literal "true" opts out, matching Karpenter's own check — "false" or a @@ -532,8 +543,11 @@ func TestClassifyDisruptionBlocked(t *testing.T) { }) // The node-level blocks Karpenter rejects on before it ever looks at pods — - // uninitialized, deleting, nominated for a pending pod. Nothing in the caches explains - // them, and Other is the honest answer. + // uninitialized, deleting, missing its NodePool label. Nothing in the caches explains + // them, and Other is the honest answer. Note the message says "nominated for a pending + // pod": Other is correct HERE even so, because this classifier never reads the message, + // and the nominated bucket that message maps to is a read-side classification (see + // TestClassifyDisruptionBlocked_NeverDerivesPodNominated). t.Run("nothing on the node explains the block", func(t *testing.T) { state := clusterState{ podsByNode: map[string][]*corev1.Pod{ @@ -552,6 +566,80 @@ func TestClassifyDisruptionBlocked(t *testing.T) { }) } +// TestClassifyDisruptionBlocked_NeverDerivesPodNominated is the regression guard for #9897. +// +// A pod with Status.NominatedNodeName set is being PREEMPTED by kube-scheduler — Karpenter's +// own helper for that field is IsPreempting — and says nothing about Karpenter's nomination +// window, which lives only in the Karpenter controller's memory for ~20s and is not +// observable through the API. A previous version of this classifier derived PodNominated from +// it and consequently emitted that bucket zero times in production while ~36% of blocking +// events carried the corresponding message. The bucket now belongs to the read-side message +// parse in dakr, so this classifier must never produce it from cluster state again. +// +// Each case below puts a preempting pod on the blocked node and asserts the verdict is +// whatever the node's REAL state says — never PodNominated. +func TestClassifyDisruptionBlocked_NeverDerivesPodNominated(t *testing.T) { + now := time.Date(2026, 8, 4, 12, 0, 0, 0, time.UTC) + const nominatedMessage = "Cannot disrupt Node: state node is nominated for a pending pod" + + cases := []struct { + name string + state clusterState + want string + }{ + { + // The exact shape the deleted derivation used to claim as PodNominated: a + // preempting pod on the node and nothing else blocking. Other is the honest + // answer, because live state genuinely does not explain this block. + name: "a preempting pod alone does not explain the block", + state: clusterState{ + podsByNode: map[string][]*corev1.Pod{ + "ip-10-0-1-5": {preemptingPod("payments", "checkout-9f8b7d", "ip-10-0-1-5", + map[string]string{"app": "checkout"}, nil)}, + }, + }, + want: disruptionBlockReasonOther, + }, + { + name: "a preempting pod does not mask a blocking pdb", + state: clusterState{ + podsByNode: map[string][]*corev1.Pod{ + "ip-10-0-1-5": {preemptingPod("payments", "checkout-7d9f8b", "ip-10-0-1-5", + map[string]string{"app": "checkout"}, nil)}, + }, + pdbs: map[string][]*policyv1.PodDisruptionBudget{ + "payments": {blockingPDB("payments", "checkout-pdb", map[string]string{"app": "checkout"}, 0)}, + }, + }, + want: disruptionBlockReasonPDBViolation, + }, + { + name: "a preempting pod does not mask a do-not-disrupt annotation", + state: clusterState{ + podsByNode: map[string][]*corev1.Pod{ + "ip-10-0-1-5": {preemptingPod("payments", "ledger-0", "ip-10-0-1-5", nil, map[string]string{ + "karpenter.sh/do-not-disrupt": "true", + })}, + }, + }, + want: disruptionBlockReasonDoNotDisruptAnnotation, + }, + } + + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + got := classifyDisruptionBlocked( + disruptionBlockedEvent("ip-10-0-1-5", nominatedMessage), + tc.state.sources(), now, + ) + + assert.Equal(t, tc.want, got.Reason) + assert.NotEqual(t, "PodNominated", got.Reason, + "PodNominated is not derivable from cluster state — see #9897") + }) + } +} + // TestClassifyDisruptionBlocked_NeverUnclassified is the contract the empty ClickHouse // column depends on: "" means the event predates this classification, so a live collector // must never emit it. Degraded inputs have to land in Other, not in nothing. diff --git a/internal/collector/endpoint_collector.go b/internal/collector/endpoint_collector.go index 89929d8b..ee74080d 100644 --- a/internal/collector/endpoint_collector.go +++ b/internal/collector/endpoint_collector.go @@ -167,8 +167,8 @@ func (c *EndpointCollector) endpointsChanged(oldEndpoints, newEndpoints *corev1. oldEndpoints.ObjectMeta, newEndpoints.ObjectMeta, ) - if changed != IgnoreChanges { - return changed == PushChanges + if changed == PushChanges { + return true } // Check for changes in subsets diff --git a/internal/collector/event_collector.go b/internal/collector/event_collector.go index ea8c4ad0..cdbad3ee 100644 --- a/internal/collector/event_collector.go +++ b/internal/collector/event_collector.go @@ -237,6 +237,7 @@ type enrichedEvent struct { DisruptionBlockReason string `json:"disruptionBlockReason,omitempty"` BlockingPDBName *string `json:"blockingPdbName,omitempty"` + RelatedPodName *string `json:"relatedPodName,omitempty"` } // eventPayload returns what to put on the wire for an event. @@ -258,6 +259,7 @@ func (c *EventCollector) eventPayload(event *corev1.Event) interface{} { Event: event, DisruptionBlockReason: classification.Reason, BlockingPDBName: classification.BlockingPDBName, + RelatedPodName: classification.RelatedPodName, } } @@ -608,8 +610,8 @@ func (c *EventCollector) eventChanged(oldEvent, newEvent *corev1.Event) bool { oldEvent.ObjectMeta, newEvent.ObjectMeta, ) - if changed != IgnoreChanges { - return changed == PushChanges + if changed == PushChanges { + return true } // Check for count changes diff --git a/internal/collector/flagger_resource_collector.go b/internal/collector/flagger_resource_collector.go new file mode 100644 index 00000000..67472a85 --- /dev/null +++ b/internal/collector/flagger_resource_collector.go @@ -0,0 +1,272 @@ +// internal/collector/flagger_resource_collector.go +package collector + +import ( + "context" + "fmt" + "sync" + "time" + + telemetry_logger "github.com/devzero-inc/zxporter/internal/logger" + "github.com/go-logr/logr" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/apimachinery/pkg/types" + "k8s.io/client-go/dynamic" + "k8s.io/client-go/dynamic/dynamicinformer" + "k8s.io/client-go/tools/cache" +) + +// GVRs for the three Flagger kinds this package collects. All three live in +// one API group and one version, and all three are namespaced. +var ( + // flagger.app/v1beta1 + FlaggerCanaryGVR = schema.GroupVersionResource{Group: "flagger.app", Version: "v1beta1", Resource: "canaries"} + FlaggerMetricTemplateGVR = schema.GroupVersionResource{Group: "flagger.app", Version: "v1beta1", Resource: "metrictemplates"} + FlaggerAlertProviderGVR = schema.GroupVersionResource{Group: "flagger.app", Version: "v1beta1", Resource: "alertproviders"} +) + +// FlaggerResourceCollector watches one Flagger GVR through a dynamic informer +// and emits an identity envelope around the whole object. All three Flagger +// kinds watch, convert and send the same shape, so one parameterized collector +// replaces three near-identical files -- the same consolidation +// FluxResourceCollector makes for the eighteen FluxCD kinds. +// +// If a Flagger kind ever needs zxporter-side field extraction (the way +// KyvernoPolicyCollector.processPolicy lifts ruleCount/background), it gets its +// own collector file at that point rather than a switch in here. +type FlaggerResourceCollector struct { + dynamicClient dynamic.Interface + gvr schema.GroupVersionResource + resourceType ResourceType + batchChan chan CollectedResource + resourceChan chan []CollectedResource + batcher *ResourcesBatcher + stopCh chan struct{} + namespaces []string + excluded map[types.NamespacedName]bool + logger logr.Logger + telemetryLogger telemetry_logger.Logger + mu sync.Mutex + // chanMu guards batchChan against the send-after-close shutdown race: + // informer callbacks RLock around sends, Stop write-locks before closing. + chanMu sync.RWMutex + stopped bool +} + +// NewFlaggerResourceCollector creates a collector for one Flagger kind, +// identified by its GVR and the ResourceType it emits as. +func NewFlaggerResourceCollector( + dynamicClient dynamic.Interface, + gvr schema.GroupVersionResource, + resourceType ResourceType, + namespaces []string, + excludedResources []ExcludedFlaggerResource, + maxBatchSize int, + maxBatchTime time.Duration, + logger logr.Logger, + telemetryLogger telemetry_logger.Logger, +) *FlaggerResourceCollector { + batchChan := make(chan CollectedResource, 100) + resourceChan := make(chan []CollectedResource, 100) + batcher := NewResourcesBatcher(maxBatchSize, maxBatchTime, batchChan, resourceChan, logger) + + excluded := make(map[types.NamespacedName]bool, len(excludedResources)) + for _, e := range excludedResources { + excluded[types.NamespacedName{Namespace: e.Namespace, Name: e.Name}] = true + } + + return &FlaggerResourceCollector{ + dynamicClient: dynamicClient, + gvr: gvr, + resourceType: resourceType, + batchChan: batchChan, + resourceChan: resourceChan, + batcher: batcher, + stopCh: make(chan struct{}), + namespaces: namespaces, + excluded: excluded, + logger: logger.WithName(fmt.Sprintf("%s-collector", resourceType.String())), + telemetryLogger: telemetryLogger, + } +} + +// Start begins watching this collector's GVR. +func (c *FlaggerResourceCollector) Start(ctx context.Context) error { + c.logger.Info("Starting Flagger resource collector", "gvr", c.gvr.String(), "namespaces", c.namespaces) + + // Flagger is an optional add-on, so an absent CRD is a live cluster state + // rather than a failure: skip silently instead of erroring, the same + // posture FluxResourceCollector and KyvernoPolicyCollector take. + if !c.IsAvailable(ctx) { + c.logger.V(4).Info("Flagger CRD not available, skipping", "gvr", c.gvr.String()) + return nil + } + + targetNamespace := "" + if len(c.namespaces) == 1 && c.namespaces[0] != "" { + targetNamespace = c.namespaces[0] + } + factory := dynamicinformer.NewFilteredDynamicSharedInformerFactory( + c.dynamicClient, 0, targetNamespace, nil, + ) + informer := factory.ForResource(c.gvr).Informer() + + if err := informer.SetTransform(StripMetadataTransform); err != nil { + return fmt.Errorf("failed to set informer transform for %s: %w", c.gvr.String(), err) + } + if _, err := informer.AddEventHandler(c.eventHandler()); err != nil { + return fmt.Errorf("failed to add event handler for %s: %w", c.gvr.String(), err) + } + + stopCh := c.stopCh + go informer.Run(stopCh) + + syncCtx, cancel := context.WithTimeout(ctx, 30*time.Second) + defer cancel() + if !cache.WaitForCacheSync(syncCtx.Done(), informer.HasSynced) { + return fmt.Errorf("timeout waiting for %s cache to sync", c.gvr.String()) + } + + c.logger.Info("Flagger resource informer started and synced", "gvr", c.gvr.String()) + c.batcher.start() + + go func() { + select { + case <-ctx.Done(): + c.Stop() //nolint:errcheck + case <-stopCh: + } + }() + + return nil +} + +// eventHandler builds the shared informer callbacks. +func (c *FlaggerResourceCollector) eventHandler() cache.ResourceEventHandlerFuncs { + return cache.ResourceEventHandlerFuncs{ + AddFunc: func(obj interface{}) { + if u, ok := obj.(*unstructured.Unstructured); ok { + c.handleEvent(u, EventTypeAdd) + } + }, + UpdateFunc: func(_, newObj interface{}) { + if u, ok := newObj.(*unstructured.Unstructured); ok { + c.handleEvent(u, EventTypeUpdate) + } + }, + DeleteFunc: func(obj interface{}) { + u, ok := obj.(*unstructured.Unstructured) + if !ok { + if tombstone, ok2 := obj.(cache.DeletedFinalStateUnknown); ok2 { + u, ok = tombstone.Obj.(*unstructured.Unstructured) + } + } + if ok { + c.handleEvent(u, EventTypeDelete) + } + }, + } +} + +// handleEvent processes a Flagger object event. +func (c *FlaggerResourceCollector) handleEvent(obj *unstructured.Unstructured, eventType EventType) { + if c.excluded[types.NamespacedName{Namespace: obj.GetNamespace(), Name: obj.GetName()}] { + return + } + + key := obj.GetName() + if ns := obj.GetNamespace(); ns != "" { + key = fmt.Sprintf("%s/%s", ns, obj.GetName()) + } + + c.chanMu.RLock() + defer c.chanMu.RUnlock() + if c.stopped { + return + } + + select { + case c.batchChan <- CollectedResource{ + ResourceType: c.resourceType, + Object: processFlaggerObject(obj), + Timestamp: time.Now(), + EventType: eventType, + Key: key, + }: + case <-c.stopCh: + // Stop is closing us down and the batcher has stopped draining; + // parking on a full channel here would hold the read lock Stop is + // waiting on. + } +} + +// processFlaggerObject extracts the identity fields dakr's Flagger converters +// expect: every Flagger kind is a thin identity envelope around the full +// object, with all field extraction happening in dakr, not here. +func processFlaggerObject(obj *unstructured.Unstructured) map[string]interface{} { + return map[string]interface{}{ + "kind": obj.GetKind(), + "name": obj.GetName(), + "namespace": obj.GetNamespace(), + "uid": string(obj.GetUID()), + "labels": obj.GetLabels(), + "annotations": obj.GetAnnotations(), + "creationTimestamp": obj.GetCreationTimestamp().Format(time.RFC3339), + "raw": obj.Object, + } +} + +// Stop shuts down the collector. +func (c *FlaggerResourceCollector) Stop() error { + c.logger.Info("Stopping Flagger resource collector", "gvr", c.gvr.String()) + c.mu.Lock() + defer c.mu.Unlock() + + // stopCh closes before the write lock is taken: a sender parked on a full + // batchChan holds the read lock, and only the stopCh arm of its select can + // release it. Taking the write lock first would deadlock against it. + select { + case <-c.stopCh: + default: + close(c.stopCh) + } + c.chanMu.Lock() + if !c.stopped { + c.stopped = true + close(c.batchChan) + } + c.chanMu.Unlock() + if c.batcher != nil { + c.batcher.stop() + } + return nil +} + +// GetResourceChannel returns the channel of batched resources. +func (c *FlaggerResourceCollector) GetResourceChannel() <-chan []CollectedResource { + return c.resourceChan +} + +// GetType returns the string key used to identify this collector. +func (c *FlaggerResourceCollector) GetType() string { + return c.resourceType.String() +} + +// IsAvailable reports whether this collector's GVR is served by the cluster's +// API server. +func (c *FlaggerResourceCollector) IsAvailable(ctx context.Context) bool { + _, err := c.dynamicClient.Resource(c.gvr).List(ctx, metav1.ListOptions{Limit: 1}) + return err == nil +} + +// AddResource implements ResourceCollector for manual injection (used in tests). +func (c *FlaggerResourceCollector) AddResource(resource interface{}) error { + obj, ok := resource.(*unstructured.Unstructured) + if !ok { + return fmt.Errorf("expected *unstructured.Unstructured for %s, got %T", c.gvr.String(), resource) + } + c.handleEvent(obj, EventTypeAdd) + return nil +} diff --git a/internal/collector/flagger_resource_collector_test.go b/internal/collector/flagger_resource_collector_test.go new file mode 100644 index 00000000..09ced899 --- /dev/null +++ b/internal/collector/flagger_resource_collector_test.go @@ -0,0 +1,281 @@ +package collector + +import ( + "context" + "strings" + "sync" + "testing" + "time" + + "github.com/go-logr/logr" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + apierrors "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" + dynamicfake "k8s.io/client-go/dynamic/fake" + clienttesting "k8s.io/client-go/testing" +) + +// flaggerKinds is the roster the controller registers one collector per, +// spelled out as literals. The registration site repeats each (GVR, +// ResourceType) pair four times per kind, which is where a copy-paste pairs one +// kind's GVR with another kind's ResourceType -- and because all three share +// one collector type and one wire shape, nothing downstream can detect the swap. +var flaggerKinds = []struct { + resourceType ResourceType + gvr schema.GroupVersionResource + wantResource string + listKind string +}{ + {FlaggerCanary, FlaggerCanaryGVR, "canaries", "CanaryList"}, + {FlaggerMetricTemplate, FlaggerMetricTemplateGVR, "metrictemplates", "MetricTemplateList"}, + {FlaggerAlertProvider, FlaggerAlertProviderGVR, "alertproviders", "AlertProviderList"}, +} + +// newFlaggerTestCollector builds a collector over a fake API server that serves +// every Flagger GVR except those in absent, which answer List with a NotFound +// the way a real API server does for an uninstalled CRD. The fake client panics +// rather than erroring on a GVR it has never heard of, so "absent" has to be +// expressed as a reactor, not as an unregistered list kind. +func newFlaggerTestCollector(t *testing.T, gvr schema.GroupVersionResource, resourceType ResourceType, absent []schema.GroupVersionResource, objects ...runtime.Object) *FlaggerResourceCollector { + t.Helper() + + listKinds := map[schema.GroupVersionResource]string{} + for _, kind := range flaggerKinds { + listKinds[kind.gvr] = kind.listKind + } + + client := dynamicfake.NewSimpleDynamicClientWithCustomListKinds(runtime.NewScheme(), listKinds, objects...) + for _, missing := range absent { + client.PrependReactor("list", missing.Resource, func(clienttesting.Action) (bool, runtime.Object, error) { + return true, nil, apierrors.NewNotFound(missing.GroupResource(), "") + }) + } + + return NewFlaggerResourceCollector(client, gvr, resourceType, nil, nil, 10, time.Second, logr.Discard(), nil) +} + +func flaggerObjectFixture(gvr schema.GroupVersionResource, kind, namespace, name string) *unstructured.Unstructured { + return &unstructured.Unstructured{Object: map[string]interface{}{ + "apiVersion": gvr.GroupVersion().String(), + "kind": kind, + "metadata": map[string]interface{}{ + "name": name, + "namespace": namespace, + "uid": "flagger-uid-1", + "resourceVersion": "77214", + "creationTimestamp": "2026-09-01T08:30:00Z", + "labels": map[string]interface{}{"app": "podinfo"}, + "annotations": map[string]interface{}{"flagger.app/primary": "podinfo-primary"}, + }, + "spec": map[string]interface{}{ + "provider": "istio", + "analysis": map[string]interface{}{"interval": "1m", "threshold": int64(5)}, + }, + "status": map[string]interface{}{ + "phase": "Progressing", + "canaryWeight": int64(10), + "failedChecks": int64(0), + }, + }} +} + +// One collector type serves all three kinds, so the GVR and the ResourceType +// are the only things that distinguish two instances. Pairing them wrongly +// ships one kind's objects to DAKR under another kind's label, which the send +// path cannot detect: both are the same wire shape. +func TestFlaggerResourceCollector_EachKindKeepsItsOwnGVRAndResourceType(t *testing.T) { + require.Len(t, flaggerKinds, 3, "three Flagger kinds are collected; this roster drifted") + + seenGVR := map[schema.GroupVersionResource]ResourceType{} + seenType := map[ResourceType]schema.GroupVersionResource{} + + for _, kind := range flaggerKinds { + t.Run(kind.resourceType.String(), func(t *testing.T) { + c := NewFlaggerResourceCollector(nil, kind.gvr, kind.resourceType, nil, nil, 10, time.Second, logr.Discard(), nil) + + assert.Equal(t, kind.gvr, c.gvr) + assert.Equal(t, kind.resourceType, c.resourceType) + assert.Equal(t, kind.resourceType.String(), c.GetType(), + "GetType is the key the manager stores the collector under and the disabled-collectors policy matches on") + assert.True(t, strings.HasPrefix(c.GetType(), "flagger_")) + + assert.Equal(t, "flagger.app", kind.gvr.Group) + assert.Equal(t, "v1beta1", kind.gvr.Version) + assert.Equal(t, kind.wantResource, kind.gvr.Resource) + }) + + if previous, ok := seenGVR[kind.gvr]; ok { + t.Fatalf("GVR %s is shared by %s and %s", kind.gvr, previous, kind.resourceType) + } + seenGVR[kind.gvr] = kind.resourceType + + if previous, ok := seenType[kind.resourceType]; ok { + t.Fatalf("ResourceType %s is shared by %s and %s", kind.resourceType, previous, kind.gvr) + } + seenType[kind.resourceType] = kind.gvr + } +} + +// Flagger is an optional add-on, so most clusters serve none of these GVRs. +// This is the live case behind "Collector not available, skipping registration", +// which is what puts the kind on the controller's pending-retry list rather +// than failing startup. +func TestFlaggerResourceCollector_IsAvailableFollowsTheServedGVRs(t *testing.T) { + t.Run("a served GVR is available", func(t *testing.T) { + c := newFlaggerTestCollector(t, FlaggerCanaryGVR, FlaggerCanary, nil) + assert.True(t, c.IsAvailable(context.Background())) + }) + + t.Run("a cluster without Flagger installed serves none of them", func(t *testing.T) { + allAbsent := []schema.GroupVersionResource{FlaggerCanaryGVR, FlaggerMetricTemplateGVR, FlaggerAlertProviderGVR} + for _, kind := range flaggerKinds { + c := newFlaggerTestCollector(t, kind.gvr, kind.resourceType, allAbsent) + assert.False(t, c.IsAvailable(context.Background()), "%s", kind.resourceType) + } + }) +} + +// A missing CRD must leave Start a no-op that returns nil: the manager treats a +// non-nil error as a startup failure, so erroring here would take zxporter down +// on every cluster that has not installed Flagger -- which is most of them. +func TestFlaggerResourceCollector_StartOnAnAbsentCRDIsANoOpNotAnError(t *testing.T) { + c := newFlaggerTestCollector(t, FlaggerCanaryGVR, FlaggerCanary, []schema.GroupVersionResource{FlaggerCanaryGVR}) + + require.NoError(t, c.Start(context.Background())) + assert.Empty(t, c.batchChan, "a skipped collector must not emit resources") + + select { + case batch := <-c.resourceChan: + t.Fatalf("a skipped collector emitted a batch: %v", batch) + case <-time.After(50 * time.Millisecond): + } +} + +func TestFlaggerResourceCollector_StartOnAServedCRDDeliversExistingObjects(t *testing.T) { + existing := flaggerObjectFixture(FlaggerCanaryGVR, "Canary", "test", "podinfo") + c := newFlaggerTestCollector(t, FlaggerCanaryGVR, FlaggerCanary, nil, existing) + + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + require.NoError(t, c.Start(ctx)) + defer c.Stop() //nolint:errcheck + + select { + case batch := <-c.GetResourceChannel(): + require.Len(t, batch, 1) + assert.Equal(t, FlaggerCanary, batch[0].ResourceType) + assert.Equal(t, EventTypeAdd, batch[0].EventType) + assert.Equal(t, "test/podinfo", batch[0].Key) + case <-time.After(10 * time.Second): + t.Fatal("expected the informer's initial list to reach the resource channel") + } +} + +// DAKR's Flagger converter unmarshals the raw object into the upstream typed +// struct, so the envelope has to carry the whole thing alongside the identity +// fields -- dropping raw would leave every typed column empty with no error +// anywhere, and DAKR rejects a nil raw with InvalidArgument. +func TestFlaggerResourceCollector_HandleEventEmitsTheIdentityEnvelope(t *testing.T) { + obj := flaggerObjectFixture(FlaggerCanaryGVR, "Canary", "test", "podinfo") + c := NewFlaggerResourceCollector(nil, FlaggerCanaryGVR, FlaggerCanary, nil, nil, 10, time.Second, logr.Discard(), nil) + + c.handleEvent(obj, EventTypeUpdate) + + select { + case res := <-c.batchChan: + assert.Equal(t, FlaggerCanary, res.ResourceType) + assert.Equal(t, EventTypeUpdate, res.EventType) + assert.Equal(t, "test/podinfo", res.Key) + + payload, ok := res.Object.(map[string]interface{}) + require.True(t, ok, "payload should be a map, got %T", res.Object) + assert.Equal(t, "Canary", payload["kind"]) + assert.Equal(t, "podinfo", payload["name"]) + assert.Equal(t, "test", payload["namespace"]) + assert.Equal(t, "flagger-uid-1", payload["uid"]) + assert.Equal(t, map[string]string{"app": "podinfo"}, payload["labels"]) + assert.Equal(t, map[string]string{"flagger.app/primary": "podinfo-primary"}, payload["annotations"]) + // metav1.Time decodes to the pod's local zone, so the envelope carries + // whatever offset that is. dakr re-parses it as RFC3339, so the instant + // is what has to survive -- not the spelling. + created, err := time.Parse(time.RFC3339, payload["creationTimestamp"].(string)) + require.NoError(t, err) + assert.Equal(t, time.Date(2026, 9, 1, 8, 30, 0, 0, time.UTC), created.UTC()) + assert.Equal(t, obj.Object, payload["raw"]) + case <-time.After(time.Second): + t.Fatal("expected a collected resource on the batch channel") + } +} + +func TestFlaggerResourceCollector_ExcludedObjectIsDropped(t *testing.T) { + c := NewFlaggerResourceCollector( + nil, FlaggerCanaryGVR, FlaggerCanary, nil, + []ExcludedFlaggerResource{{Namespace: "test", Name: "noisy"}}, + 10, time.Second, logr.Discard(), nil, + ) + + c.handleEvent(flaggerObjectFixture(FlaggerCanaryGVR, "Canary", "test", "noisy"), EventTypeAdd) + assert.Empty(t, c.batchChan, "an excluded object must not reach the batch channel") + + // Same name in another namespace is a different object. + c.handleEvent(flaggerObjectFixture(FlaggerCanaryGVR, "Canary", "prod", "noisy"), EventTypeAdd) + assert.Len(t, c.batchChan, 1) +} + +// Informer callbacks can still be in flight when the manager stops a collector, +// so an event arriving after Stop has to be dropped rather than sent on a +// closed channel. Stop itself is called from both the manager and the +// context-cancel goroutine in Start, so it has to tolerate being called twice. +func TestFlaggerResourceCollector_StopIsIdempotentAndDropsLaterEvents(t *testing.T) { + c := NewFlaggerResourceCollector(nil, FlaggerAlertProviderGVR, FlaggerAlertProvider, nil, nil, 10, time.Second, logr.Discard(), nil) + + require.NoError(t, c.Stop()) + require.NoError(t, c.Stop()) + + assert.NotPanics(t, func() { + c.handleEvent(flaggerObjectFixture(FlaggerAlertProviderGVR, "AlertProvider", "test", "on-call"), EventTypeAdd) + }) +} + +// Run with -race: senders that win the read lock just before Stop takes the +// write lock must not send on the closed channel, and a sender parked on a full +// batchChan must fall out through stopCh rather than hold the read lock Stop is +// waiting on. +func TestFlaggerResourceCollector_StopIsSafeAgainstInFlightEvents(t *testing.T) { + c := NewFlaggerResourceCollector(nil, FlaggerMetricTemplateGVR, FlaggerMetricTemplate, nil, nil, 10, time.Second, logr.Discard(), nil) + obj := flaggerObjectFixture(FlaggerMetricTemplateGVR, "MetricTemplate", "test", "latency") + + var wg sync.WaitGroup + start := make(chan struct{}) + for i := 0; i < 8; i++ { + wg.Add(1) + go func() { + defer wg.Done() + <-start + for j := 0; j < 50; j++ { + c.handleEvent(obj, EventTypeUpdate) + } + }() + } + wg.Add(1) + go func() { + defer wg.Done() + <-start + assert.NoError(t, c.Stop()) + }() + close(start) + wg.Wait() +} + +func TestFlaggerResourceCollector_AddResourceRejectsAForeignType(t *testing.T) { + c := NewFlaggerResourceCollector(nil, FlaggerCanaryGVR, FlaggerCanary, nil, nil, 10, time.Second, logr.Discard(), nil) + + require.Error(t, c.AddResource("not an unstructured object")) + assert.Empty(t, c.batchChan) + + require.NoError(t, c.AddResource(flaggerObjectFixture(FlaggerCanaryGVR, "Canary", "test", "podinfo"))) + assert.Len(t, c.batchChan, 1) +} diff --git a/internal/collector/flux_resource_collector.go b/internal/collector/flux_resource_collector.go new file mode 100644 index 00000000..8694d47d --- /dev/null +++ b/internal/collector/flux_resource_collector.go @@ -0,0 +1,282 @@ +// internal/collector/flux_resource_collector.go +package collector + +import ( + "context" + "fmt" + "sync" + "time" + + telemetry_logger "github.com/devzero-inc/zxporter/internal/logger" + "github.com/go-logr/logr" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/client-go/dynamic" + "k8s.io/client-go/dynamic/dynamicinformer" + "k8s.io/client-go/tools/cache" +) + +// GVRs for the eighteen FluxCD kinds this package collects, grouped by API +// group the way the ledger and common.proto group them. +var ( + // source.toolkit.fluxcd.io/v1 + FluxGitRepositoryGVR = schema.GroupVersionResource{Group: "source.toolkit.fluxcd.io", Version: "v1", Resource: "gitrepositories"} + FluxOCIRepositoryGVR = schema.GroupVersionResource{Group: "source.toolkit.fluxcd.io", Version: "v1", Resource: "ocirepositories"} + FluxHelmRepositoryGVR = schema.GroupVersionResource{Group: "source.toolkit.fluxcd.io", Version: "v1", Resource: "helmrepositories"} + FluxHelmChartGVR = schema.GroupVersionResource{Group: "source.toolkit.fluxcd.io", Version: "v1", Resource: "helmcharts"} + FluxBucketGVR = schema.GroupVersionResource{Group: "source.toolkit.fluxcd.io", Version: "v1", Resource: "buckets"} + FluxExternalArtifactGVR = schema.GroupVersionResource{Group: "source.toolkit.fluxcd.io", Version: "v1", Resource: "externalartifacts"} + + // kustomize.toolkit.fluxcd.io/v1 + FluxKustomizationGVR = schema.GroupVersionResource{Group: "kustomize.toolkit.fluxcd.io", Version: "v1", Resource: "kustomizations"} + + // helm.toolkit.fluxcd.io/v2 + FluxHelmReleaseGVR = schema.GroupVersionResource{Group: "helm.toolkit.fluxcd.io", Version: "v2", Resource: "helmreleases"} + + // notification.toolkit.fluxcd.io + FluxAlertGVR = schema.GroupVersionResource{Group: "notification.toolkit.fluxcd.io", Version: "v1beta3", Resource: "alerts"} + FluxProviderGVR = schema.GroupVersionResource{Group: "notification.toolkit.fluxcd.io", Version: "v1beta3", Resource: "providers"} + FluxReceiverGVR = schema.GroupVersionResource{Group: "notification.toolkit.fluxcd.io", Version: "v1", Resource: "receivers"} + + // fluxcd.controlplane.io/v1 (Flux Operator) + FluxInstanceGVR = schema.GroupVersionResource{Group: "fluxcd.controlplane.io", Version: "v1", Resource: "fluxinstances"} + FluxReportGVR = schema.GroupVersionResource{Group: "fluxcd.controlplane.io", Version: "v1", Resource: "fluxreports"} + FluxResourceSetInputProviderGVR = schema.GroupVersionResource{Group: "fluxcd.controlplane.io", Version: "v1", Resource: "resourcesetinputproviders"} + FluxResourceSetGVR = schema.GroupVersionResource{Group: "fluxcd.controlplane.io", Version: "v1", Resource: "resourcesets"} + + // image.toolkit.fluxcd.io/v1 (image-reflector-controller / image-automation-controller -- + // not deployed on every cluster, hence collector-only + the factory pattern) + FluxImageRepositoryGVR = schema.GroupVersionResource{Group: "image.toolkit.fluxcd.io", Version: "v1", Resource: "imagerepositories"} + FluxImagePolicyGVR = schema.GroupVersionResource{Group: "image.toolkit.fluxcd.io", Version: "v1", Resource: "imagepolicies"} + FluxImageUpdateAutomationGVR = schema.GroupVersionResource{Group: "image.toolkit.fluxcd.io", Version: "v1", Resource: "imageupdateautomations"} +) + +// FluxResourceCollector is a generic single-GVR collector shared by every +// FluxCD kind. All eighteen kinds emit the same wire shape -- identity +// fields plus the full unstructured object under "raw" -- because dakr's +// per-kind converters do the field extraction from raw, not zxporter (see +// dakr/k8s-gen/k8sconverters/flux_common.go). That leaves nothing per-kind +// for zxporter to do beyond naming the GVR and the ResourceType, so one +// parameterized collector replaces what would otherwise be eighteen +// near-identical ~250-line files (the shape KyvernoPolicyCollector and +// every other collector in this package hand-roll). +// +// If a Flux kind ever needs zxporter-side field extraction (the way +// KyvernoPolicyCollector.processPolicy lifts ruleCount/background), give it +// its own collector file at that point -- this type is for the common case, +// not a mandate to force every future kind through it. +type FluxResourceCollector struct { + dynamicClient dynamic.Interface + gvr schema.GroupVersionResource + resourceType ResourceType + batchChan chan CollectedResource + resourceChan chan []CollectedResource + batcher *ResourcesBatcher + stopCh chan struct{} + namespaces []string + logger logr.Logger + telemetryLogger telemetry_logger.Logger + mu sync.Mutex + // chanMu guards batchChan against the send-after-close shutdown race: + // informer callbacks RLock around sends, Stop write-locks before closing. + chanMu sync.RWMutex + stopped bool +} + +// NewFluxResourceCollector creates a collector for one FluxCD kind, identified +// by its GVR and the ResourceType it emits as. +func NewFluxResourceCollector( + dynamicClient dynamic.Interface, + gvr schema.GroupVersionResource, + resourceType ResourceType, + namespaces []string, + maxBatchSize int, + maxBatchTime time.Duration, + logger logr.Logger, + telemetryLogger telemetry_logger.Logger, +) *FluxResourceCollector { + batchChan := make(chan CollectedResource, 100) + resourceChan := make(chan []CollectedResource, 100) + batcher := NewResourcesBatcher(maxBatchSize, maxBatchTime, batchChan, resourceChan, logger) + + return &FluxResourceCollector{ + dynamicClient: dynamicClient, + gvr: gvr, + resourceType: resourceType, + batchChan: batchChan, + resourceChan: resourceChan, + batcher: batcher, + stopCh: make(chan struct{}), + namespaces: namespaces, + logger: logger.WithName(fmt.Sprintf("%s-collector", resourceType.String())), + telemetryLogger: telemetryLogger, + } +} + +// Start begins watching this collector's GVR. +func (c *FluxResourceCollector) Start(ctx context.Context) error { + c.logger.Info("Starting Flux resource collector", "gvr", c.gvr.String(), "namespaces", c.namespaces) + + // If the CRD is not installed (e.g. the image automation controllers are + // not deployed on this cluster), skip silently rather than error -- the + // same posture KyvernoPolicyCollector takes for the Kyverno CRDs. + if !c.IsAvailable(ctx) { + c.logger.V(4).Info("Flux CRD not available, skipping", "gvr", c.gvr.String()) + return nil + } + + targetNamespace := "" + if len(c.namespaces) == 1 && c.namespaces[0] != "" { + targetNamespace = c.namespaces[0] + } + factory := dynamicinformer.NewFilteredDynamicSharedInformerFactory( + c.dynamicClient, 0, targetNamespace, nil, + ) + informer := factory.ForResource(c.gvr).Informer() + + if err := informer.SetTransform(StripMetadataTransform); err != nil { + return fmt.Errorf("failed to set informer transform for %s: %w", c.gvr.String(), err) + } + if _, err := informer.AddEventHandler(c.eventHandler()); err != nil { + return fmt.Errorf("failed to add event handler for %s: %w", c.gvr.String(), err) + } + + stopCh := c.stopCh + go informer.Run(stopCh) + + syncCtx, cancel := context.WithTimeout(ctx, 30*time.Second) + defer cancel() + if !cache.WaitForCacheSync(syncCtx.Done(), informer.HasSynced) { + return fmt.Errorf("timeout waiting for %s cache to sync", c.gvr.String()) + } + + c.logger.Info("Flux resource informer started and synced", "gvr", c.gvr.String()) + c.batcher.start() + + go func() { + select { + case <-ctx.Done(): + c.Stop() //nolint:errcheck + case <-stopCh: + } + }() + + return nil +} + +// eventHandler builds the shared informer callbacks. +func (c *FluxResourceCollector) eventHandler() cache.ResourceEventHandlerFuncs { + return cache.ResourceEventHandlerFuncs{ + AddFunc: func(obj interface{}) { + if u, ok := obj.(*unstructured.Unstructured); ok { + c.handleEvent(u, EventTypeAdd) + } + }, + UpdateFunc: func(_, newObj interface{}) { + if u, ok := newObj.(*unstructured.Unstructured); ok { + c.handleEvent(u, EventTypeUpdate) + } + }, + DeleteFunc: func(obj interface{}) { + u, ok := obj.(*unstructured.Unstructured) + if !ok { + if tombstone, ok2 := obj.(cache.DeletedFinalStateUnknown); ok2 { + u, ok = tombstone.Obj.(*unstructured.Unstructured) + } + } + if ok { + c.handleEvent(u, EventTypeDelete) + } + }, + } +} + +// handleEvent processes a Flux object event. +func (c *FluxResourceCollector) handleEvent(obj *unstructured.Unstructured, eventType EventType) { + key := obj.GetName() + if ns := obj.GetNamespace(); ns != "" { + key = fmt.Sprintf("%s/%s", ns, obj.GetName()) + } + + c.chanMu.RLock() + defer c.chanMu.RUnlock() + if c.stopped { + return + } + + c.batchChan <- CollectedResource{ + ResourceType: c.resourceType, + Object: processFluxObject(obj), + Timestamp: time.Now(), + EventType: eventType, + Key: key, + } +} + +// processFluxObject extracts the identity fields dakr's Flux converters +// expect (see k8smodels.ProcessedFluxGitRepository et al.): every Flux kind +// is a thin identity envelope around the full object, with all field +// extraction happening in dakr, not here. +func processFluxObject(obj *unstructured.Unstructured) map[string]interface{} { + return map[string]interface{}{ + "kind": obj.GetKind(), + "name": obj.GetName(), + "namespace": obj.GetNamespace(), + "uid": string(obj.GetUID()), + "labels": obj.GetLabels(), + "annotations": obj.GetAnnotations(), + "creationTimestamp": obj.GetCreationTimestamp().Format(time.RFC3339), + "raw": obj.Object, + } +} + +// Stop shuts down the collector. +func (c *FluxResourceCollector) Stop() error { + c.logger.Info("Stopping Flux resource collector", "gvr", c.gvr.String()) + c.mu.Lock() + defer c.mu.Unlock() + + select { + case <-c.stopCh: + default: + close(c.stopCh) + } + c.chanMu.Lock() + if !c.stopped { + c.stopped = true + close(c.batchChan) + } + c.chanMu.Unlock() + if c.batcher != nil { + c.batcher.stop() + } + return nil +} + +// GetResourceChannel returns the output channel for batched resources. +func (c *FluxResourceCollector) GetResourceChannel() <-chan []CollectedResource { + return c.resourceChan +} + +// GetType returns the string key used to identify this collector. +func (c *FluxResourceCollector) GetType() string { + return c.resourceType.String() +} + +// IsAvailable returns true when this collector's GVR is served by the +// cluster's API server. +func (c *FluxResourceCollector) IsAvailable(ctx context.Context) bool { + _, err := c.dynamicClient.Resource(c.gvr).List(ctx, metav1.ListOptions{Limit: 1}) + return err == nil +} + +// AddResource implements ResourceCollector for manual injection (used in tests). +func (c *FluxResourceCollector) AddResource(resource interface{}) error { + obj, ok := resource.(*unstructured.Unstructured) + if !ok { + return fmt.Errorf("expected *unstructured.Unstructured, got %T", resource) + } + c.handleEvent(obj, EventTypeAdd) + return nil +} diff --git a/internal/collector/flux_resource_collector_test.go b/internal/collector/flux_resource_collector_test.go new file mode 100644 index 00000000..4830d38b --- /dev/null +++ b/internal/collector/flux_resource_collector_test.go @@ -0,0 +1,262 @@ +package collector + +import ( + "context" + "strings" + "testing" + "time" + + "github.com/go-logr/logr" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + apierrors "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" + dynamicfake "k8s.io/client-go/dynamic/fake" + clienttesting "k8s.io/client-go/testing" +) + +// fluxKinds is the roster the controller registers one collector per, spelled +// out as literals. The registration site repeats each (GVR, ResourceType) pair +// four times per kind, which is where a copy-paste pairs one kind's GVR with +// another kind's ResourceType — and because all eighteen share one collector +// type and one wire shape, nothing downstream can detect the swap. +var fluxKinds = []struct { + resourceType ResourceType + gvr schema.GroupVersionResource + wantGroup string + wantVersion string + wantResource string + listKind string +}{ + {FluxGitRepository, FluxGitRepositoryGVR, "source.toolkit.fluxcd.io", "v1", "gitrepositories", "GitRepositoryList"}, + {FluxOCIRepository, FluxOCIRepositoryGVR, "source.toolkit.fluxcd.io", "v1", "ocirepositories", "OCIRepositoryList"}, + {FluxHelmRepository, FluxHelmRepositoryGVR, "source.toolkit.fluxcd.io", "v1", "helmrepositories", "HelmRepositoryList"}, + {FluxHelmChart, FluxHelmChartGVR, "source.toolkit.fluxcd.io", "v1", "helmcharts", "HelmChartList"}, + {FluxBucket, FluxBucketGVR, "source.toolkit.fluxcd.io", "v1", "buckets", "BucketList"}, + {FluxKustomization, FluxKustomizationGVR, "kustomize.toolkit.fluxcd.io", "v1", "kustomizations", "KustomizationList"}, + {FluxHelmRelease, FluxHelmReleaseGVR, "helm.toolkit.fluxcd.io", "v2", "helmreleases", "HelmReleaseList"}, + {FluxExternalArtifact, FluxExternalArtifactGVR, "source.toolkit.fluxcd.io", "v1", "externalartifacts", "ExternalArtifactList"}, + {FluxAlert, FluxAlertGVR, "notification.toolkit.fluxcd.io", "v1beta3", "alerts", "AlertList"}, + {FluxProvider, FluxProviderGVR, "notification.toolkit.fluxcd.io", "v1beta3", "providers", "ProviderList"}, + {FluxReceiver, FluxReceiverGVR, "notification.toolkit.fluxcd.io", "v1", "receivers", "ReceiverList"}, + {FluxInstance, FluxInstanceGVR, "fluxcd.controlplane.io", "v1", "fluxinstances", "FluxInstanceList"}, + {FluxReport, FluxReportGVR, "fluxcd.controlplane.io", "v1", "fluxreports", "FluxReportList"}, + {FluxResourceSetInputProvider, FluxResourceSetInputProviderGVR, "fluxcd.controlplane.io", "v1", "resourcesetinputproviders", "ResourceSetInputProviderList"}, + {FluxResourceSet, FluxResourceSetGVR, "fluxcd.controlplane.io", "v1", "resourcesets", "ResourceSetList"}, + {FluxImageRepository, FluxImageRepositoryGVR, "image.toolkit.fluxcd.io", "v1", "imagerepositories", "ImageRepositoryList"}, + {FluxImagePolicy, FluxImagePolicyGVR, "image.toolkit.fluxcd.io", "v1", "imagepolicies", "ImagePolicyList"}, + {FluxImageUpdateAutomation, FluxImageUpdateAutomationGVR, "image.toolkit.fluxcd.io", "v1", "imageupdateautomations", "ImageUpdateAutomationList"}, +} + +// newFluxTestCollector builds a collector over a fake API server that serves +// every Flux GVR except those in absent, which answer List with a NotFound the +// way a real API server does for an uninstalled CRD. The fake client panics +// rather than erroring on a GVR it has never heard of, so "absent" has to be +// expressed as a reactor, not as an unregistered list kind. +func newFluxTestCollector(t *testing.T, gvr schema.GroupVersionResource, resourceType ResourceType, absent []schema.GroupVersionResource, objects ...runtime.Object) *FluxResourceCollector { + t.Helper() + + listKinds := map[schema.GroupVersionResource]string{} + for _, kind := range fluxKinds { + listKinds[kind.gvr] = kind.listKind + } + + client := dynamicfake.NewSimpleDynamicClientWithCustomListKinds(runtime.NewScheme(), listKinds, objects...) + for _, missing := range absent { + client.PrependReactor("list", missing.Resource, func(clienttesting.Action) (bool, runtime.Object, error) { + return true, nil, apierrors.NewNotFound(missing.GroupResource(), "") + }) + } + + return NewFluxResourceCollector(client, gvr, resourceType, nil, 10, time.Second, logr.Discard(), nil) +} + +func fluxObjectFixture(gvr schema.GroupVersionResource, kind, namespace, name string) *unstructured.Unstructured { + return &unstructured.Unstructured{Object: map[string]interface{}{ + "apiVersion": gvr.GroupVersion().String(), + "kind": kind, + "metadata": map[string]interface{}{ + "name": name, + "namespace": namespace, + "uid": "flux-uid-1", + "resourceVersion": "884213", + "creationTimestamp": "2026-09-01T08:30:00Z", + "labels": map[string]interface{}{"kustomize.toolkit.fluxcd.io/name": "flux-system"}, + "annotations": map[string]interface{}{"reconcile.fluxcd.io/requestedAt": "2026-09-15T10:00:00Z"}, + }, + "spec": map[string]interface{}{"interval": "10m0s"}, + "status": map[string]interface{}{ + "conditions": []interface{}{ + map[string]interface{}{"type": "Ready", "status": "True", "reason": "Succeeded"}, + }, + }, + }} +} + +// One collector type serves all eighteen kinds, so the GVR and the ResourceType +// are the only things that distinguish two instances. Pairing them wrongly +// ships one kind's objects to DAKR under another kind's label, which the send +// path cannot detect: both are the same wire shape. +func TestFluxResourceCollector_EachKindKeepsItsOwnGVRAndResourceType(t *testing.T) { + require.Len(t, fluxKinds, 18, "eighteen Flux kinds are collected; this roster drifted") + + seenGVR := map[schema.GroupVersionResource]ResourceType{} + seenType := map[ResourceType]schema.GroupVersionResource{} + + for _, kind := range fluxKinds { + t.Run(kind.resourceType.String(), func(t *testing.T) { + c := NewFluxResourceCollector(nil, kind.gvr, kind.resourceType, nil, 10, time.Second, logr.Discard(), nil) + + assert.Equal(t, kind.gvr, c.gvr) + assert.Equal(t, kind.resourceType, c.resourceType) + assert.Equal(t, kind.resourceType.String(), c.GetType(), + "GetType is the key the manager stores the collector under and the disabled-collectors policy matches on") + assert.True(t, strings.HasPrefix(c.GetType(), "flux_")) + + assert.Equal(t, kind.wantGroup, kind.gvr.Group) + assert.Equal(t, kind.wantVersion, kind.gvr.Version) + assert.Equal(t, kind.wantResource, kind.gvr.Resource) + }) + + if previous, ok := seenGVR[kind.gvr]; ok { + t.Fatalf("GVR %s is shared by %s and %s", kind.gvr, previous, kind.resourceType) + } + seenGVR[kind.gvr] = kind.resourceType + + if previous, ok := seenType[kind.resourceType]; ok { + t.Fatalf("ResourceType %s is shared by %s and %s", kind.resourceType, previous, kind.gvr) + } + seenType[kind.resourceType] = kind.gvr + } +} + +// The image-automation and Flux Operator CRDs are optional add-ons, so a +// cluster running core Flux serves some of these GVRs and not others. This is +// the live case behind "Collector not available, skipping registration". +func TestFluxResourceCollector_IsAvailableFollowsTheServedGVRs(t *testing.T) { + t.Run("a served GVR is available", func(t *testing.T) { + c := newFluxTestCollector(t, FluxKustomizationGVR, FluxKustomization, nil) + assert.True(t, c.IsAvailable(context.Background())) + }) + + t.Run("a GVR the cluster does not serve is unavailable", func(t *testing.T) { + // Core Flux without the image-reflector-controller: kustomizations + // resolve, imagepolicies do not. + c := newFluxTestCollector(t, FluxImagePolicyGVR, FluxImagePolicy, []schema.GroupVersionResource{FluxImagePolicyGVR}) + assert.False(t, c.IsAvailable(context.Background())) + }) +} + +// A missing CRD must leave Start a no-op that returns nil: the manager treats a +// non-nil error as a startup failure, so erroring here would take zxporter down +// on any cluster that has not installed the optional Flux controllers. +func TestFluxResourceCollector_StartOnAnAbsentCRDIsANoOpNotAnError(t *testing.T) { + c := newFluxTestCollector(t, FluxImageUpdateAutomationGVR, FluxImageUpdateAutomation, []schema.GroupVersionResource{FluxImageUpdateAutomationGVR}) + + require.NoError(t, c.Start(context.Background())) + assert.Empty(t, c.batchChan, "a skipped collector must not emit resources") + + select { + case batch := <-c.resourceChan: + t.Fatalf("a skipped collector emitted a batch: %v", batch) + case <-time.After(50 * time.Millisecond): + } +} + +func TestFluxResourceCollector_StartOnAServedCRDDeliversExistingObjects(t *testing.T) { + existing := fluxObjectFixture(FluxKustomizationGVR, "Kustomization", "flux-system", "apps") + c := newFluxTestCollector(t, FluxKustomizationGVR, FluxKustomization, nil, existing) + + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + require.NoError(t, c.Start(ctx)) + defer c.Stop() //nolint:errcheck + + select { + case batch := <-c.GetResourceChannel(): + require.Len(t, batch, 1) + assert.Equal(t, FluxKustomization, batch[0].ResourceType) + assert.Equal(t, EventTypeAdd, batch[0].EventType) + assert.Equal(t, "flux-system/apps", batch[0].Key) + case <-time.After(10 * time.Second): + t.Fatal("expected the informer's initial list to reach the resource channel") + } +} + +// DAKR's Flux converters read every stored column out of the raw object, so the +// envelope has to carry the whole thing alongside the identity fields -- +// dropping raw would leave every typed column empty with no error anywhere. +func TestFluxResourceCollector_HandleEventEmitsTheIdentityEnvelope(t *testing.T) { + obj := fluxObjectFixture(FluxGitRepositoryGVR, "GitRepository", "flux-system", "fleet-infra") + c := NewFluxResourceCollector(nil, FluxGitRepositoryGVR, FluxGitRepository, nil, 10, time.Second, logr.Discard(), nil) + + c.handleEvent(obj, EventTypeUpdate) + + select { + case res := <-c.batchChan: + assert.Equal(t, FluxGitRepository, res.ResourceType) + assert.Equal(t, EventTypeUpdate, res.EventType) + assert.Equal(t, "flux-system/fleet-infra", res.Key) + + payload, ok := res.Object.(map[string]interface{}) + require.True(t, ok, "payload should be a map, got %T", res.Object) + assert.Equal(t, "GitRepository", payload["kind"]) + assert.Equal(t, "fleet-infra", payload["name"]) + assert.Equal(t, "flux-system", payload["namespace"]) + assert.Equal(t, "flux-uid-1", payload["uid"]) + assert.Equal(t, map[string]string{"kustomize.toolkit.fluxcd.io/name": "flux-system"}, payload["labels"]) + assert.Equal(t, map[string]string{"reconcile.fluxcd.io/requestedAt": "2026-09-15T10:00:00Z"}, payload["annotations"]) + // metav1.Time decodes to the pod's local zone, so the envelope carries + // whatever offset that is. dakr re-parses it as RFC3339, so the instant + // is what has to survive -- not the spelling. + created, err := time.Parse(time.RFC3339, payload["creationTimestamp"].(string)) + require.NoError(t, err) + assert.Equal(t, time.Date(2026, 9, 1, 8, 30, 0, 0, time.UTC), created.UTC()) + assert.Equal(t, obj.Object, payload["raw"]) + case <-time.After(time.Second): + t.Fatal("expected a collected resource on the batch channel") + } +} + +// FluxInstance and FluxReport are cluster-scoped, so their key is the bare name. +// A "/name" key would not match the key any other event for the same object +// produces, breaking dedup downstream. +func TestFluxResourceCollector_ClusterScopedKindKeyIsTheBareName(t *testing.T) { + obj := fluxObjectFixture(FluxInstanceGVR, "FluxInstance", "", "flux") + unstructured.RemoveNestedField(obj.Object, "metadata", "namespace") + c := NewFluxResourceCollector(nil, FluxInstanceGVR, FluxInstance, nil, 10, time.Second, logr.Discard(), nil) + + c.handleEvent(obj, EventTypeAdd) + + res := <-c.batchChan + assert.Equal(t, "flux", res.Key) + payload := res.Object.(map[string]interface{}) + assert.Empty(t, payload["namespace"]) +} + +// Informer callbacks can still be in flight when the manager stops a collector, +// so an event arriving after Stop has to be dropped rather than sent on a +// closed channel. Stop itself is called from both the manager and the +// context-cancel goroutine in Start, so it has to tolerate being called twice. +func TestFluxResourceCollector_StopIsIdempotentAndDropsLaterEvents(t *testing.T) { + c := NewFluxResourceCollector(nil, FluxAlertGVR, FluxAlert, nil, 10, time.Second, logr.Discard(), nil) + + require.NoError(t, c.Stop()) + require.NoError(t, c.Stop()) + + assert.NotPanics(t, func() { + c.handleEvent(fluxObjectFixture(FluxAlertGVR, "Alert", "flux-system", "on-call"), EventTypeAdd) + }) +} + +func TestFluxResourceCollector_AddResourceRejectsAForeignType(t *testing.T) { + c := NewFluxResourceCollector(nil, FluxBucketGVR, FluxBucket, nil, 10, time.Second, logr.Discard(), nil) + + require.Error(t, c.AddResource("not an unstructured object")) + assert.Empty(t, c.batchChan) + + require.NoError(t, c.AddResource(fluxObjectFixture(FluxBucketGVR, "Bucket", "flux-system", "manifests"))) + assert.Len(t, c.batchChan, 1) +} diff --git a/internal/collector/horizontalpodautoscaler_collector.go b/internal/collector/horizontalpodautoscaler_collector.go index 45d7c044..ce3d33fb 100644 --- a/internal/collector/horizontalpodautoscaler_collector.go +++ b/internal/collector/horizontalpodautoscaler_collector.go @@ -177,8 +177,8 @@ func (c *HorizontalPodAutoscalerCollector) hpaChanged( oldHPA.ObjectMeta, newHPA.ObjectMeta, ) - if changed != IgnoreChanges { - return changed == PushChanges + if changed == PushChanges { + return true } // Check for min/max replicas changes diff --git a/internal/collector/ingress_collector.go b/internal/collector/ingress_collector.go index 68bd0c1a..46999a9b 100644 --- a/internal/collector/ingress_collector.go +++ b/internal/collector/ingress_collector.go @@ -169,8 +169,8 @@ func (c *IngressCollector) ingressChanged(oldIngress, newIngress *networkingv1.I oldIngress.ObjectMeta, newIngress.ObjectMeta, ) - if changed != IgnoreChanges { - return changed == PushChanges + if changed == PushChanges { + return true } // Check for ingress class changes diff --git a/internal/collector/ingressclass_collector.go b/internal/collector/ingressclass_collector.go index 0bf93772..6bcd16d2 100644 --- a/internal/collector/ingressclass_collector.go +++ b/internal/collector/ingressclass_collector.go @@ -167,8 +167,8 @@ func (c *IngressClassCollector) ingressClassChanged( oldIngressClass.ObjectMeta, newIngressClass.ObjectMeta, ) - if changed != IgnoreChanges { - return changed == PushChanges + if changed == PushChanges { + return true } // Check for controller changes diff --git a/internal/collector/interface.go b/internal/collector/interface.go index ca9efa8e..dce8ca6f 100644 --- a/internal/collector/interface.go +++ b/internal/collector/interface.go @@ -158,12 +158,54 @@ const ( MigPartedConfig NodeLifecycleTransition PodUnschedulableEvent + // SchedulerPlacementMetric identifies the collector that scrapes dz-scheduler's + // auth-gated /metrics endpoint and emits its allow-listed Prometheus families as + // typed rows (see scheduler_metrics_collector.go). + SchedulerPlacementMetric // ClusterAutoscalerStatus identifies the collector that watches the // kube-system/cluster-autoscaler-status ConfigMap. It is a COLLECTOR identity only: // that collector's output rides the ordinary Event resource type (see // cluster_autoscaler_status.go), so this value never reaches the wire and // deliberately has no ProtoType case. ClusterAutoscalerStatus + // KedaScalerMetrics identifies the collector that scrapes per-trigger scaler + // metric samples from the KEDA operator /metrics endpoint. It is a COLLECTOR + // identity only: data is forwarded directly via SendScalerMetrics and never + // travels through the combined resource channel, so this value has no + // ProtoType case. + KedaScalerMetrics + // ArgoCDApplication and ArgoCDApplicationSet are Argo CD (argoproj.io + // Application / ApplicationSet), NOT Argo Rollouts -- ArgoRollouts above is a + // different project that happens to share the argoproj.io group. + ArgoCDApplication + ArgoCDApplicationSet + // FluxCD GitOps kinds. The first seven get full explorer support; the + // remaining eleven (ExternalArtifact through ImageUpdateAutomation) are + // collector-only -- collected and stored so nothing is silently + // uncollected, but without a bespoke explorer detail panel yet. + FluxGitRepository + FluxOCIRepository + FluxHelmRepository + FluxHelmChart + FluxBucket + FluxKustomization + FluxHelmRelease + FluxExternalArtifact + FluxAlert + FluxProvider + FluxReceiver + FluxInstance + FluxReport + FluxResourceSetInputProvider + FluxResourceSet + FluxImageRepository + FluxImagePolicy + FluxImageUpdateAutomation + // Flagger progressive-delivery kinds (flagger.app), all namespaced and all + // served by one FlaggerResourceCollector. + FlaggerCanary + FlaggerMetricTemplate + FlaggerAlertProvider ) // String returns the string representation of the ResourceType @@ -236,7 +278,32 @@ func (r ResourceType) String() string { MigPartedConfig: "mig_parted_config", NodeLifecycleTransition: "node_lifecycle_transition", PodUnschedulableEvent: "pod_unschedulable_event", + SchedulerPlacementMetric: "scheduler_placement_metric", ClusterAutoscalerStatus: "cluster_autoscaler_status", + KedaScalerMetrics: "keda_scaler_metrics", + ArgoCDApplication: "argo_cd_application", + ArgoCDApplicationSet: "argo_cd_application_set", + FluxGitRepository: "flux_git_repository", + FluxOCIRepository: "flux_oci_repository", + FluxHelmRepository: "flux_helm_repository", + FluxHelmChart: "flux_helm_chart", + FluxBucket: "flux_bucket", + FluxKustomization: "flux_kustomization", + FluxHelmRelease: "flux_helm_release", + FluxExternalArtifact: "flux_external_artifact", + FluxAlert: "flux_alert", + FluxProvider: "flux_provider", + FluxReceiver: "flux_receiver", + FluxInstance: "flux_instance", + FluxReport: "flux_report", + FluxResourceSetInputProvider: "flux_resource_set_input_provider", + FluxResourceSet: "flux_resource_set", + FluxImageRepository: "flux_image_repository", + FluxImagePolicy: "flux_image_policy", + FluxImageUpdateAutomation: "flux_image_update_automation", + FlaggerCanary: "flagger_canary", + FlaggerMetricTemplate: "flagger_metric_template", + FlaggerAlertProvider: "flagger_alert_provider", } if name, ok := names[r]; ok { @@ -315,6 +382,30 @@ var resourceProtoTypes = map[ResourceType]gen.ResourceType{ MigPartedConfig: gen.ResourceType_RESOURCE_TYPE_MIG_PARTED_CONFIG, NodeLifecycleTransition: gen.ResourceType_RESOURCE_TYPE_NODE_LIFECYCLE_TRANSITION, PodUnschedulableEvent: gen.ResourceType_RESOURCE_TYPE_POD_UNSCHEDULABLE_EVENT, + SchedulerPlacementMetric: gen.ResourceType_RESOURCE_TYPE_SCHEDULER_PLACEMENT_METRIC, + ArgoCDApplication: gen.ResourceType_RESOURCE_TYPE_ARGO_APPLICATION, + ArgoCDApplicationSet: gen.ResourceType_RESOURCE_TYPE_ARGO_APPLICATION_SET, + FluxGitRepository: gen.ResourceType_RESOURCE_TYPE_FLUX_GIT_REPOSITORY, + FluxOCIRepository: gen.ResourceType_RESOURCE_TYPE_FLUX_OCI_REPOSITORY, + FluxHelmRepository: gen.ResourceType_RESOURCE_TYPE_FLUX_HELM_REPOSITORY, + FluxHelmChart: gen.ResourceType_RESOURCE_TYPE_FLUX_HELM_CHART, + FluxBucket: gen.ResourceType_RESOURCE_TYPE_FLUX_BUCKET, + FluxKustomization: gen.ResourceType_RESOURCE_TYPE_FLUX_KUSTOMIZATION, + FluxHelmRelease: gen.ResourceType_RESOURCE_TYPE_FLUX_HELM_RELEASE, + FluxExternalArtifact: gen.ResourceType_RESOURCE_TYPE_FLUX_EXTERNAL_ARTIFACT, + FluxAlert: gen.ResourceType_RESOURCE_TYPE_FLUX_ALERT, + FluxProvider: gen.ResourceType_RESOURCE_TYPE_FLUX_PROVIDER, + FluxReceiver: gen.ResourceType_RESOURCE_TYPE_FLUX_RECEIVER, + FluxInstance: gen.ResourceType_RESOURCE_TYPE_FLUX_INSTANCE, + FluxReport: gen.ResourceType_RESOURCE_TYPE_FLUX_REPORT, + FluxResourceSetInputProvider: gen.ResourceType_RESOURCE_TYPE_FLUX_RESOURCE_SET_INPUT_PROVIDER, + FluxResourceSet: gen.ResourceType_RESOURCE_TYPE_FLUX_RESOURCE_SET, + FluxImageRepository: gen.ResourceType_RESOURCE_TYPE_FLUX_IMAGE_REPOSITORY, + FluxImagePolicy: gen.ResourceType_RESOURCE_TYPE_FLUX_IMAGE_POLICY, + FluxImageUpdateAutomation: gen.ResourceType_RESOURCE_TYPE_FLUX_IMAGE_UPDATE_AUTOMATION, + FlaggerCanary: gen.ResourceType_RESOURCE_TYPE_FLAGGER_CANARY, + FlaggerMetricTemplate: gen.ResourceType_RESOURCE_TYPE_FLAGGER_METRIC_TEMPLATE, + FlaggerAlertProvider: gen.ResourceType_RESOURCE_TYPE_FLAGGER_ALERT_PROVIDER, } // ProtoType returns the protobuf wire enum for the ResourceType. diff --git a/internal/collector/job_collector.go b/internal/collector/job_collector.go index 19cceb27..cdcbecc4 100644 --- a/internal/collector/job_collector.go +++ b/internal/collector/job_collector.go @@ -167,8 +167,8 @@ func (c *JobCollector) jobChanged(oldJob, newJob *batchv1.Job) bool { oldJob.ObjectMeta, newJob.ObjectMeta, ) - if changed != IgnoreChanges { - return changed == PushChanges + if changed == PushChanges { + return true } // Check for status changes diff --git a/internal/collector/keda_scaled_job_collector.go b/internal/collector/keda_scaled_job_collector.go index 8b265d54..6a292986 100644 --- a/internal/collector/keda_scaled_job_collector.go +++ b/internal/collector/keda_scaled_job_collector.go @@ -194,8 +194,8 @@ func (c *ScaledJobCollector) scaledJobChanged( oldScaledJob.ObjectMeta, newScaledJob.ObjectMeta, ) - if changed != IgnoreChanges { - return changed == PushChanges + if changed == PushChanges { + return true } // Check for spec changes (most important for KEDA jobs) @@ -320,9 +320,11 @@ func (c *ScaledJobCollector) GetResourceChannel() <-chan []CollectedResource { return c.resourceChan } -// GetType returns the type of resource this collector handles +// GetType returns the type of resource this collector handles. It must be the wire name +// the controller registers, starts and disables this collector under: it used to be +// "scaledjob", so a deferred start after the KEDA CRDs appeared failed as "not registered". func (c *ScaledJobCollector) GetType() string { - return "scaledjob" + return KedaScaledJob.String() } // IsAvailable checks if ScaledJob resources can be accessed in the cluster diff --git a/internal/collector/keda_scaled_object_collector.go b/internal/collector/keda_scaled_object_collector.go index b9c25a52..b114471d 100644 --- a/internal/collector/keda_scaled_object_collector.go +++ b/internal/collector/keda_scaled_object_collector.go @@ -194,8 +194,8 @@ func (c *ScaledObjectCollector) scaledObjectChanged( oldScaledObject.ObjectMeta, newScaledObject.ObjectMeta, ) - if changed != IgnoreChanges { - return changed == PushChanges + if changed == PushChanges { + return true } // Check for spec changes (most important for KEDA) @@ -330,9 +330,12 @@ func (c *ScaledObjectCollector) GetResourceChannel() <-chan []CollectedResource return c.resourceChan } -// GetType returns the type of resource this collector handles +// GetType returns the type of resource this collector handles. It must be the wire name +// the controller registers, starts and disables this collector under: it used to be +// "scaledobject", so a deferred start after the KEDA CRDs appeared failed as "not +// registered". func (c *ScaledObjectCollector) GetType() string { - return "scaledobject" + return KedaScaledObject.String() } // IsAvailable checks if ScaledObject resources can be accessed in the cluster diff --git a/internal/collector/keda_scaler_metrics_collector.go b/internal/collector/keda_scaler_metrics_collector.go new file mode 100644 index 00000000..8d30aa4e --- /dev/null +++ b/internal/collector/keda_scaler_metrics_collector.go @@ -0,0 +1,568 @@ +// internal/collector/keda_scaler_metrics_collector.go +// +// KedaScalerMetricsCollector scrapes the KEDA operator's Prometheus /metrics +// endpoint on a configurable ticker interval, parses keda_scaler_metrics_value +// and keda_scaler_active, joins them by matching label sets, resolves the +// workload kind/name from the ScaledObject's scaleTargetRef, and forwards the +// resulting ScalerMetricSample batch to dakr via SendScalerMetrics. +// +// IsAvailable returns false when the configured endpoint is unreachable so the +// collector is silently skipped on non-KEDA clusters. +package collector + +import ( + "context" + "fmt" + "io" + "net/http" + "strconv" + "sync" + "time" + + gen "github.com/devzero-inc/zxporter/gen/api/v1" + telemetry_logger "github.com/devzero-inc/zxporter/internal/logger" + "github.com/go-logr/logr" + kedaclient "github.com/kedacore/keda/v2/pkg/generated/clientset/versioned" + dto "github.com/prometheus/client_model/go" + "github.com/prometheus/common/expfmt" + "github.com/prometheus/common/model" + "google.golang.org/protobuf/types/known/timestamppb" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +const ( + // DefaultKedaMetricsEndpoint is the in-cluster address for the KEDA operator + // metrics endpoint. DevZero installs KEDA into the devzero-system namespace and + // renames the operator Service to dzkeda-operator (dzkeda.operator.name in the + // dakr chart's values.yaml — a DevZero name-prefix footprint), so that is the + // primary default. Override via KedaScalerMetricsConfig.Endpoint (wired from + // the KEDA_METRICS_ENDPOINT env var / CollectionPolicy) when KEDA runs in a + // different namespace or on a different port. Keep the Service name here in + // sync with dzkeda.operator.name in operators/tunr/helm/dakr/values.yaml. + DefaultKedaMetricsEndpoint = "http://dzkeda-operator.devzero-system.svc.cluster.local:8080/metrics" + + // DefaultKedaScraperInterval is the default scrape cadence. + DefaultKedaScraperInterval = 30 * time.Second + + // kedaScalerMetricValueName is the Prometheus metric name for current scaler values. + kedaScalerMetricValueName = "keda_scaler_metrics_value" + + // kedaScalerActiveName is the Prometheus metric name for scaler active state. + kedaScalerActiveName = "keda_scaler_active" + + // defaultScaleTargetKind is the workload kind KEDA assumes when the + // ScaledObject's scaleTargetRef.kind field is omitted. + defaultScaleTargetKind = "Deployment" + + // maxMetricsResponseBytes caps how much of the KEDA /metrics response is read + // into the parser, bounding worst-case memory if the endpoint returns a + // pathologically large body (thousands of scalers, or a misbehaving + // operator). A realistic KEDA exposition is well under a few MB; this is a + // safety ceiling, not a normal-operation limit. Exceeding it truncates the + // stream — the already-parsed families still return. + maxMetricsResponseBytes = 16 << 20 // 16 MiB + + // emptyWithSOsLogEvery rate-limits the "reachable but exposing no scaler + // samples despite ScaledObjects existing" diagnostic (see classifyScrapeOutcome). + // Log the first tick of a streak, then once per this many scrapes (~10 min at + // the 30s default) while it persists — visible enough to notice, quiet enough + // not to spam. + emptyWithSOsLogEvery = 20 +) + +// scrapeOutcome classifies a completed scrape so the collector can log the +// actionable case loudly and the benign case quietly. It is derived purely from +// two counts the scrape already has, so it is unit-testable without HTTP or a +// Kubernetes client. +type scrapeOutcome int + +const ( + // outcomeSent: at least one scaler sample was produced and will be forwarded. + outcomeSent scrapeOutcome = iota + // outcomeIdle: no samples AND no ScaledObjects — an empty result is expected + // (idle or non-KEDA-workload cluster). Benign; logged at debug. + outcomeIdle + // outcomeMisconfigured: no samples BUT ScaledObjects exist — the scraped + // operator is reachable yet not exposing keda_scaler_metrics_value, so the + // scaler-metrics graph stays silently empty. This is the exact signature of a + // broken/duplicate operator or disabled Prometheus scaler metrics, so it is + // surfaced at a visible level instead of hidden at debug. + outcomeMisconfigured +) + +// classifyScrapeOutcome decides how a scrape result should be reported from the +// number of emitted samples and the number of ScaledObjects present. +func classifyScrapeOutcome(numSamples, numScaledObjects int) scrapeOutcome { + if numSamples > 0 { + return outcomeSent + } + if numScaledObjects > 0 { + return outcomeMisconfigured + } + return outcomeIdle +} + +// KedaScalerMetricsConfig holds the configurable parameters for the scraper. +type KedaScalerMetricsConfig struct { + // Endpoint is the full URL to the KEDA operator /metrics endpoint. + // Defaults to DefaultKedaMetricsEndpoint. + Endpoint string + + // Interval is the scrape cadence. Defaults to DefaultKedaScraperInterval. + Interval time.Duration +} + +// scalerSender is the subset of DakrClient that KedaScalerMetricsCollector needs. +// Using a narrow interface makes the collector easier to test. +type scalerSender interface { + SendScalerMetrics(ctx context.Context, clusterID, teamID string, samples []*gen.ScalerMetricSample) error +} + +// scaledObjectKey identifies a ScaledObject by namespace+name. +type scaledObjectKey struct { + Namespace string + Name string +} + +// workloadRef holds the resolved workload identity. +type workloadRef struct { + Kind string + Name string +} + +// KedaScalerMetricsCollector implements ResourceCollector by scraping the KEDA +// operator metrics endpoint and forwarding scaler sample batches to dakr. +type KedaScalerMetricsCollector struct { + config KedaScalerMetricsConfig + kedaClient kedaclient.Interface + sender scalerSender + resourceChan chan []CollectedResource + stopCh chan struct{} + mu sync.Mutex + logger logr.Logger + telemetryLogger telemetry_logger.Logger + httpClient *http.Client + + // emptyWithSOsStreak counts consecutive scrapes that returned zero scaler + // samples while ScaledObjects existed — the actionable misconfiguration + // signature (see classifyScrapeOutcome). Written only from the single scrape + // goroutine started by Start, so it needs no lock. Used to rate-limit the + // diagnostic log via emptyWithSOsLogEvery. + emptyWithSOsStreak int +} + +// NewKedaScalerMetricsCollector creates a new collector with the provided config. +func NewKedaScalerMetricsCollector( + cfg KedaScalerMetricsConfig, + kedaClient kedaclient.Interface, + sender scalerSender, + logger logr.Logger, + telemetryLogger telemetry_logger.Logger, +) *KedaScalerMetricsCollector { + if cfg.Endpoint == "" { + cfg.Endpoint = DefaultKedaMetricsEndpoint + } + if cfg.Interval <= 0 { + cfg.Interval = DefaultKedaScraperInterval + } + + return &KedaScalerMetricsCollector{ + config: cfg, + kedaClient: kedaClient, + sender: sender, + // This collector emits directly via sender.SendScalerMetrics and never + // writes to resourceChan; the channel exists only to satisfy the + // ResourceCollector interface, so it needs no real buffer. + resourceChan: make(chan []CollectedResource, 1), + stopCh: make(chan struct{}), + logger: logger.WithName("keda-scaler-metrics-collector"), + telemetryLogger: telemetryLogger, + httpClient: &http.Client{ + Timeout: 10 * time.Second, + }, + } +} + +// Start launches the scrape loop in a background goroutine and returns +// immediately. The CollectionManager calls Start synchronously and only starts +// the collector's channel processor after Start returns (see manager.go +// startCollectorInternal), so Start MUST NOT block — a blocking Start stalls the +// caller (e.g. StartCollector via retryPendingCollectors) indefinitely. This +// matches the convention of the other streaming collectors (e.g. +// ScaledObjectCollector). The loop runs until the context is cancelled or Stop +// closes stopCh. +func (c *KedaScalerMetricsCollector) Start(ctx context.Context) error { + c.logger.Info("Starting KEDA scaler metrics scraper", + "endpoint", c.config.Endpoint, + "interval", c.config.Interval.String()) + + go func() { + ticker := time.NewTicker(c.config.Interval) + defer ticker.Stop() + + // Run the first scrape immediately so we do not wait a full interval. + c.scrape(ctx) + + for { + select { + case <-ticker.C: + c.scrape(ctx) + case <-c.stopCh: + c.logger.Info("KEDA scaler metrics collector stopped") + return + case <-ctx.Done(): + c.logger.Info("KEDA scaler metrics collector context cancelled") + return + } + } + }() + + return nil +} + +// Stop signals the scrape loop to exit. +func (c *KedaScalerMetricsCollector) Stop() error { + c.mu.Lock() + defer c.mu.Unlock() + + select { + case <-c.stopCh: + // already closed + default: + close(c.stopCh) + } + return nil +} + +// GetResourceChannel returns the (unused) resource channel. +// This collector emits data directly via the sender rather than through the +// combined channel, but it must satisfy the ResourceCollector interface. +func (c *KedaScalerMetricsCollector) GetResourceChannel() <-chan []CollectedResource { + return c.resourceChan +} + +// GetType returns the string identifier for this collector type. +func (c *KedaScalerMetricsCollector) GetType() string { + return "keda_scaler_metrics" +} + +// IsAvailable reports whether the KEDA operator metrics endpoint is present. +// Availability is defined by reachability, NOT by HTTP status: any completed +// response — including a 405 to our HEAD probe — proves the operator's HTTP +// server is answering, so we return true. Only a transport-level error (DNS, +// connection refused, timeout) means the endpoint is absent, which is the +// non-KEDA-cluster case that silently skips registration. Do not "fix" this into +// checking resp.StatusCode: a 4xx/5xx still means the endpoint exists. +func (c *KedaScalerMetricsCollector) IsAvailable(ctx context.Context) bool { + req, err := http.NewRequestWithContext(ctx, http.MethodHead, c.config.Endpoint, nil) + if err != nil { + c.logger.V(1).Info("KEDA metrics endpoint HEAD request creation failed", "error", err.Error()) + return false + } + + resp, err := c.httpClient.Do(req) + if err != nil { + // Kept at debug on purpose: this collector is registered unconditionally + // and skipped on non-KEDA clusters, so a higher level would log on every + // pending-retry (~5m) across the fleet's non-KEDA clusters. The message + // names the usual cause so it is actionable once debug is enabled. + c.logger.V(1).Info("KEDA operator metrics endpoint unreachable — scaler metrics not collected "+ + "(expected when KEDA is not installed; if KEDA IS installed, enable its Prometheus metrics via "+ + "keda.prometheus.operator.enabled=true so the operator exposes :8080)", + "endpoint", c.config.Endpoint, "error", err.Error()) + return false + } + defer func() { _ = resp.Body.Close() }() + return true +} + +// AddResource is a no-op: this collector does not watch Kubernetes resources. +func (c *KedaScalerMetricsCollector) AddResource(resource interface{}) error { + return fmt.Errorf("KedaScalerMetricsCollector does not support AddResource") +} + +// ─── internal helpers ──────────────────────────────────────────────────────── + +// scrape performs one full scrape-parse-resolve-send cycle. +func (c *KedaScalerMetricsCollector) scrape(ctx context.Context) { + mfs, err := c.fetchMetrics(ctx) + if err != nil { + c.logger.Error(err, "Failed to fetch KEDA metrics") + return + } + + // Build the ScaledObject workload resolution map before parsing so we can + // annotate each sample with its workload identity. If resolution fails we + // skip the entire scrape cycle: emitting samples with blank + // workload_kind/workload_name would poison dakr's GetByWorkload lookup, so a + // missed cycle is strictly better than sending unattributed samples. + resolveMap, err := c.buildResolveMap(ctx) + if err != nil { + c.logger.Error(err, "Failed to list ScaledObjects for workload resolution; skipping scrape cycle") + return + } + + samples := parseScalerMetrics(mfs, resolveMap) + + // Classify the result so the actionable failure is not silent. resolveMap is + // the ScaledObject inventory we already listed above, so this costs nothing + // extra. "No samples but ScaledObjects exist" is the exact signature that made + // the scaler-metrics graph sit empty for hours: the scraped operator was + // reachable (IsAvailable passed) but never recorded keda_scaler_metrics_value + // because its ServiceAccount had no RBAC, and a duplicate KEDA operator was + // quietly winning the external.metrics APIService. Previously this branch was + // an unconditional debug line that hid exactly that state. + switch classifyScrapeOutcome(len(samples), len(resolveMap)) { + case outcomeMisconfigured: + c.emptyWithSOsStreak++ + if c.emptyWithSOsStreak == 1 || c.emptyWithSOsStreak%emptyWithSOsLogEvery == 0 { + c.logger.Info("KEDA operator is reachable and has ScaledObjects, but exposed no "+ + kedaScalerMetricValueName+" samples — the scaler-metrics graph will stay empty. Verify: "+ + "(1) the scraped operator is the ACTIVE KEDA operator and its ServiceAccount has RBAC to "+ + "query scalers (list/watch scaledobjects, read trigger-auth secrets) — a broken/absent RBAC "+ + "makes the operator's metric-serving path error before it records keda_scaler_metrics_value; "+ + "(2) exactly ONE KEDA operator is installed — a leftover/duplicate operator can win the "+ + "external.metrics APIService while the scraped one stays idle; "+ + "(3) Prometheus scaler metrics are enabled (dzkeda.prometheus.operator.enabled=true).", + "scaledObjects", len(resolveMap), + "endpoint", c.config.Endpoint, + "consecutiveEmptyScrapes", c.emptyWithSOsStreak) + } + return + case outcomeIdle: + c.emptyWithSOsStreak = 0 + c.logger.V(1).Info("No KEDA scaler metric samples after parsing, skipping send (no ScaledObjects present)") + return + } + + // outcomeSent: at least one attributable sample. Reset the streak so a later + // misconfiguration logs from its first tick again. + c.emptyWithSOsStreak = 0 + + // cluster_id and team_id are injected by the transport layer via context + // (the same mechanism used by SendClusterSnapshotStream). Pass empty strings + // here so the RealDakrClient can resolve them from the context values. + if err := c.sender.SendScalerMetrics(ctx, "", "", samples); err != nil { + c.logger.Error(err, "Failed to send KEDA scaler metrics", "samples", len(samples)) + } else { + c.logger.V(1).Info("Sent KEDA scaler metrics", "samples", len(samples)) + } +} + +// fetchMetrics GETs the /metrics endpoint and parses the Prometheus exposition. +func (c *KedaScalerMetricsCollector) fetchMetrics(ctx context.Context) (map[string]*dto.MetricFamily, error) { + req, err := http.NewRequestWithContext(ctx, http.MethodGet, c.config.Endpoint, nil) + if err != nil { + return nil, fmt.Errorf("creating request: %w", err) + } + + resp, err := c.httpClient.Do(req) + if err != nil { + return nil, fmt.Errorf("fetching KEDA metrics: %w", err) + } + defer func() { _ = resp.Body.Close() }() + + if resp.StatusCode != http.StatusOK { + return nil, fmt.Errorf("unexpected HTTP status from KEDA metrics endpoint: %d", resp.StatusCode) + } + + // Bound the bytes read into the parser so a pathologically large exposition + // cannot balloon the collector's memory. + return parsePrometheusExposition(io.LimitReader(resp.Body, maxMetricsResponseBytes), resp.Header.Get("Content-Type")) +} + +// parsePrometheusExposition reads a Prometheus text exposition from r. +// +// The contentType argument is currently ignored: KEDA serves the classic +// Prometheus text format (text/plain; version=0.0.4), which this text parser +// handles. TODO: if KEDA ever negotiates OpenMetrics +// (application/openmetrics-text), dispatch on contentType to an OpenMetrics +// parser here — the text parser does not accept that format. +func parsePrometheusExposition(r io.Reader, _ string) (map[string]*dto.MetricFamily, error) { + parser := expfmt.NewTextParser(model.UTF8Validation) + mfs, err := parser.TextToMetricFamilies(r) + if err != nil && len(mfs) == 0 { + return nil, fmt.Errorf("parsing Prometheus exposition: %w", err) + } + return mfs, nil +} + +// buildResolveMap lists all ScaledObjects across all namespaces and constructs +// a (namespace, name) -> workloadRef lookup table. +// +// Scale note (what to do on larger clusters): +// This issues a full ScaledObjects("").List() on every scrape (~30s). It +// retains nothing — the listed objects are GC'd once the small map is built — +// so steady-state memory is flat regardless of cluster size; there is no leak +// or unbounded growth. The only costs are a transient per-scrape allocation and +// a periodic LIST against the kube-apiserver. Both are negligible at typical +// scale (~4 KB for a handful of ScaledObjects) and stay small into the +// hundreds. If a deployment ever runs many *thousands* of ScaledObjects and the +// every-30s LIST becomes an apiserver concern (API load, not zxporter memory), +// two options in preference order: +// +// 1. Cache this resolve map with a short TTL (refresh every ~5m instead of +// every scrape). Retains only the tiny ns->workloadRef map, cuts the LIST +// and the transient allocation ~10x, and stays self-contained. Cost: a new +// ScaledObject's samples are unattributed (skipped) for up to the TTL. +// 2. Read from a shared ScaledObject informer cache. Zero per-scrape +// allocation and zero LIST — but zxporter has no shared informer factory +// (each collector owns its own), so this means either a *second* ScaledObject +// informer (a duplicate permanent cache — worse for memory) or wiring +// KedaScaledObjectCollector's informer through to here, which couples the two +// collectors' lifecycles and namespace scoping. Only worth it as a genuine +// shared-informer refactor, not a per-collector duplicate. +// +// Do NOT add a per-collector informer purely for consistency with the +// watch-based collectors: this is a pull-based scraper, so an always-on cache +// would raise steady-state memory to buy churn/API savings that only matter at +// extreme scale. +func (c *KedaScalerMetricsCollector) buildResolveMap(ctx context.Context) (map[scaledObjectKey]workloadRef, error) { + list, err := c.kedaClient.KedaV1alpha1().ScaledObjects("").List(ctx, metav1.ListOptions{}) + if err != nil { + return nil, err + } + + m := make(map[scaledObjectKey]workloadRef, len(list.Items)) + for i := range list.Items { + so := &list.Items[i] + if so.Spec.ScaleTargetRef == nil { + continue + } + kind := so.Spec.ScaleTargetRef.Kind + if kind == "" { + kind = defaultScaleTargetKind // KEDA defaults to Deployment when Kind is omitted + } + m[scaledObjectKey{Namespace: so.Namespace, Name: so.Name}] = workloadRef{ + Kind: kind, + Name: so.Spec.ScaleTargetRef.Name, + } + } + return m, nil +} + +// labelValue extracts a label's value from a Prometheus Metric, returning "" +// if the label is absent. +func labelValue(m *dto.Metric, name string) string { + for _, lp := range m.GetLabel() { + if lp.GetName() == name { + return lp.GetValue() + } + } + return "" +} + +// triggerIndex resolves the trigger index from either the modern "triggerIndex" +// label or the legacy "scalerIndex" label. Returns -1 when neither is present +// or when the value cannot be parsed. +func triggerIndex(m *dto.Metric) int32 { + // Prefer the modern label name first. + for _, candidate := range []string{"triggerIndex", "scalerIndex"} { + v := labelValue(m, candidate) + if v == "" { + continue + } + n, err := strconv.ParseInt(v, 10, 32) + if err == nil { + return int32(n) + } + } + return -1 +} + +// scalerMetricKey is a composite key that uniquely identifies one +// (scaledObject, namespace, scaler, triggerIndex, metric) tuple. +type scalerMetricKey struct { + scaledObject string + namespace string + scaler string + tidx int32 + metric string +} + +// parseScalerMetrics converts the raw MetricFamily map into a flat list of +// ScalerMetricSamples, joining keda_scaler_metrics_value with keda_scaler_active +// on a matching composite key. +// +// A sample is emitted ONLY when its ScaledObject resolves to a workload in +// resolveMap. Any unresolved ScaledObject — and every sample when resolveMap is +// nil or empty — is skipped, so a sample with blank workload_kind/workload_name +// is never produced (that would poison dakr's GetByWorkload lookup). +func parseScalerMetrics( + mfs map[string]*dto.MetricFamily, + resolveMap map[scaledObjectKey]workloadRef, +) []*gen.ScalerMetricSample { + ts := timestamppb.Now() + + // Collect gauge values indexed by composite key. The key already carries + // every identifying field (scaledObject, namespace, scaler, trigger index, + // metric), so the map stores only the value — no redundant per-series copy of + // those strings. + values := make(map[scalerMetricKey]float64) + + if vMF, ok := mfs[kedaScalerMetricValueName]; ok { + for _, m := range vMF.GetMetric() { + so := labelValue(m, "scaledObject") + ns := labelValue(m, "namespace") + scaler := labelValue(m, "scaler") + metricLabel := labelValue(m, "metric") + tidx := triggerIndex(m) + + if so == "" || ns == "" { + continue + } + + k := scalerMetricKey{scaledObject: so, namespace: ns, scaler: scaler, tidx: tidx, metric: metricLabel} + values[k] = m.GetGauge().GetValue() + } + } + + // Build an active-state index. + active := make(map[scalerMetricKey]bool) + if aMF, ok := mfs[kedaScalerActiveName]; ok { + for _, m := range aMF.GetMetric() { + so := labelValue(m, "scaledObject") + ns := labelValue(m, "namespace") + scaler := labelValue(m, "scaler") + metricLabel := labelValue(m, "metric") + tidx := triggerIndex(m) + + if so == "" || ns == "" { + continue + } + + k := scalerMetricKey{scaledObject: so, namespace: ns, scaler: scaler, tidx: tidx, metric: metricLabel} + active[k] = m.GetGauge().GetValue() == 1 + } + } + + samples := make([]*gen.ScalerMetricSample, 0, len(values)) + for k, v := range values { + soKey := scaledObjectKey{Namespace: k.namespace, Name: k.scaledObject} + // Require a resolvable workload identity. A nil/empty resolveMap, or a + // ScaledObject missing from it, means we cannot attribute the sample — + // skip it rather than emit blank workload_kind/workload_name to dakr. + ref, ok := resolveMap[soKey] + if !ok { + continue + } + + s := &gen.ScalerMetricSample{ + Scaledobject: k.scaledObject, + Namespace: k.namespace, + WorkloadKind: ref.Kind, + WorkloadName: ref.Name, + TriggerIndex: k.tidx, + MetricName: k.metric, + ScalerType: k.scaler, + Value: v, + // A value row with no matching keda_scaler_active row defaults to + // false: KEDA omits keda_scaler_active for a scaler it hasn't + // evaluated yet, which means the scaler is not active. + Active: active[k], + Ts: ts, + } + samples = append(samples, s) + } + return samples +} diff --git a/internal/collector/keda_scaler_metrics_collector_test.go b/internal/collector/keda_scaler_metrics_collector_test.go new file mode 100644 index 00000000..cfc42889 --- /dev/null +++ b/internal/collector/keda_scaler_metrics_collector_test.go @@ -0,0 +1,228 @@ +package collector + +import ( + "strings" + "testing" + + gen "github.com/devzero-inc/zxporter/gen/api/v1" + dto "github.com/prometheus/client_model/go" +) + +// sampleExpfmt is a synthetic Prometheus exposition text that exercises: +// - keda_scaler_metrics_value with "triggerIndex" label (modern) +// - keda_scaler_metrics_value with "scalerIndex" label (legacy) +// - keda_scaler_active for a subset of the metric series +const sampleExpfmt = `# HELP keda_scaler_metrics_value The current value for each scaler's metric that would be used by the HPA in computing the target replica count. +# TYPE keda_scaler_metrics_value gauge +keda_scaler_metrics_value{metric="queue-depth",namespace="prod",scaledObject="my-worker",scaler="rabbitmq",triggerIndex="0"} 42 +keda_scaler_metrics_value{metric="queue-depth",namespace="prod",scaledObject="my-worker",scaler="rabbitmq",triggerIndex="1"} 0 +keda_scaler_metrics_value{metric="queue-depth",namespace="staging",scaledObject="my-worker",scaler="rabbitmq",scalerIndex="0"} 7 +# HELP keda_scaler_active Indicates whether a scaler is active. +# TYPE keda_scaler_active gauge +keda_scaler_active{metric="queue-depth",namespace="prod",scaledObject="my-worker",scaler="rabbitmq",triggerIndex="0"} 1 +keda_scaler_active{metric="queue-depth",namespace="prod",scaledObject="my-worker",scaler="rabbitmq",triggerIndex="1"} 0 +keda_scaler_active{metric="queue-depth",namespace="staging",scaledObject="my-worker",scaler="rabbitmq",scalerIndex="0"} 1 +` + +// TestKedaScalerMetrics_classifyScrapeOutcome pins the diagnostic classification +// that decides whether an empty scrape is benign (idle cluster) or the actionable +// misconfiguration that leaves the scaler-metrics graph silently empty (operator +// reachable + ScaledObjects present, yet no keda_scaler_metrics_value samples). +func TestKedaScalerMetrics_classifyScrapeOutcome(t *testing.T) { + t.Parallel() + + cases := []struct { + name string + numSamples int + numScaledObjects int + want scrapeOutcome + }{ + {"samples produced -> sent", 3, 5, outcomeSent}, + {"samples with zero SOs still sent", 2, 0, outcomeSent}, + {"empty and no ScaledObjects -> idle", 0, 0, outcomeIdle}, + {"empty but ScaledObjects present -> misconfigured", 0, 4, outcomeMisconfigured}, + {"empty with a single ScaledObject -> misconfigured", 0, 1, outcomeMisconfigured}, + } + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + if got := classifyScrapeOutcome(tc.numSamples, tc.numScaledObjects); got != tc.want { + t.Fatalf("classifyScrapeOutcome(%d, %d) = %d, want %d", + tc.numSamples, tc.numScaledObjects, got, tc.want) + } + }) + } +} + +// TestKedaScalerMetrics_parseTriggerIndex validates the happy-path parse using +// the modern "triggerIndex" label. +func TestKedaScalerMetrics_parseTriggerIndex(t *testing.T) { + t.Parallel() + + mfs, err := parsePrometheusExposition(strings.NewReader(sampleExpfmt), "text/plain") + if err != nil { + t.Fatalf("parsePrometheusExposition: %v", err) + } + + // Provide a resolve map for the prod ScaledObject only. + resolveMap := map[scaledObjectKey]workloadRef{ + {Namespace: "prod", Name: "my-worker"}: {Kind: "Deployment", Name: "my-worker-deploy"}, + } + + samples := parseScalerMetrics(mfs, resolveMap) + + // Only the prod namespace entries are in the resolveMap; staging should be + // skipped. prod has two triggers → two samples. + if len(samples) != 2 { + t.Fatalf("expected 2 samples, got %d: %v", len(samples), samples) + } + + for _, s := range samples { + if s.Namespace != "prod" { + t.Errorf("unexpected namespace %q", s.Namespace) + } + if s.Scaledobject != "my-worker" { + t.Errorf("unexpected scaledobject %q", s.Scaledobject) + } + if s.WorkloadKind != "Deployment" { + t.Errorf("unexpected workload kind %q", s.WorkloadKind) + } + if s.WorkloadName != "my-worker-deploy" { + t.Errorf("unexpected workload name %q", s.WorkloadName) + } + if s.ScalerType != "rabbitmq" { + t.Errorf("unexpected scaler type %q", s.ScalerType) + } + if s.MetricName != "queue-depth" { + t.Errorf("unexpected metric name %q", s.MetricName) + } + } + + // Check individual trigger values. + byTrigger := make(map[int32]*gen.ScalerMetricSample) + for _, s := range samples { + byTrigger[s.TriggerIndex] = s + } + + s0 := byTrigger[0] + if s0 == nil { + t.Fatal("no sample for triggerIndex=0") + } + if s0.Value != 42 { + t.Errorf("triggerIndex=0 value: want 42, got %v", s0.Value) + } + if !s0.Active { + t.Error("triggerIndex=0 should be active") + } + + s1 := byTrigger[1] + if s1 == nil { + t.Fatal("no sample for triggerIndex=1") + } + if s1.Value != 0 { + t.Errorf("triggerIndex=1 value: want 0, got %v", s1.Value) + } + if s1.Active { + t.Error("triggerIndex=1 should be inactive") + } +} + +// TestKedaScalerMetrics_parseScalerIndex validates that the legacy "scalerIndex" +// label is accepted and its value used as the trigger index. +func TestKedaScalerMetrics_parseScalerIndex(t *testing.T) { + t.Parallel() + + mfs, err := parsePrometheusExposition(strings.NewReader(sampleExpfmt), "text/plain") + if err != nil { + t.Fatalf("parsePrometheusExposition: %v", err) + } + + resolveMap := map[scaledObjectKey]workloadRef{ + {Namespace: "staging", Name: "my-worker"}: {Kind: "StatefulSet", Name: "my-worker-sts"}, + } + + samples := parseScalerMetrics(mfs, resolveMap) + if len(samples) != 1 { + t.Fatalf("expected 1 sample, got %d", len(samples)) + } + s := samples[0] + if s.Namespace != "staging" { + t.Errorf("namespace: want staging, got %q", s.Namespace) + } + if s.TriggerIndex != 0 { + t.Errorf("trigger index: want 0, got %d", s.TriggerIndex) + } + if s.Value != 7 { + t.Errorf("value: want 7, got %v", s.Value) + } + if !s.Active { + t.Error("sample should be active (scalerIndex=0 has active=1)") + } + if s.WorkloadKind != "StatefulSet" { + t.Errorf("workload kind: want StatefulSet, got %q", s.WorkloadKind) + } +} + +// TestKedaScalerMetrics_parseNilResolveMap confirms that a nil resolveMap emits +// ZERO samples: without a resolvable workload identity we must never send a +// sample with blank workload_kind/workload_name (it would poison dakr's +// GetByWorkload lookup). +func TestKedaScalerMetrics_parseNilResolveMap(t *testing.T) { + t.Parallel() + + exposition := `# HELP keda_scaler_metrics_value metric +# TYPE keda_scaler_metrics_value gauge +keda_scaler_metrics_value{metric="q",namespace="ns",scaledObject="so",scaler="redis",triggerIndex="0"} 5 +` + mfs, err := parsePrometheusExposition(strings.NewReader(exposition), "text/plain") + if err != nil { + t.Fatalf("parsePrometheusExposition: %v", err) + } + + if samples := parseScalerMetrics(mfs, nil); len(samples) != 0 { + t.Fatalf("expected 0 samples for a nil resolveMap, got %d: %v", len(samples), samples) + } + + // An empty (non-nil) map is equally unresolvable and must also emit nothing. + if samples := parseScalerMetrics(mfs, map[scaledObjectKey]workloadRef{}); len(samples) != 0 { + t.Fatalf("expected 0 samples for an empty resolveMap, got %d: %v", len(samples), samples) + } + + // A resolveMap that resolves a DIFFERENT ScaledObject must skip the + // unresolved one, still emitting nothing. + other := map[scaledObjectKey]workloadRef{ + {Namespace: "ns", Name: "different-so"}: {Kind: "Deployment", Name: "x"}, + } + if samples := parseScalerMetrics(mfs, other); len(samples) != 0 { + t.Fatalf("expected 0 samples when the ScaledObject is unresolved, got %d: %v", len(samples), samples) + } +} + +// TestKedaScalerMetrics_triggerIndexFallback tests the triggerIndex helper for +// both label names. +func TestKedaScalerMetrics_triggerIndexFallback(t *testing.T) { + t.Parallel() + + makeLabel := func(name, value string) *dto.LabelPair { + n, v := name, value + return &dto.LabelPair{Name: &n, Value: &v} + } + + cases := []struct { + labels []*dto.LabelPair + want int32 + }{ + {[]*dto.LabelPair{makeLabel("triggerIndex", "3")}, 3}, + {[]*dto.LabelPair{makeLabel("scalerIndex", "7")}, 7}, + {[]*dto.LabelPair{makeLabel("triggerIndex", "1"), makeLabel("scalerIndex", "2")}, 1}, // triggerIndex preferred + {[]*dto.LabelPair{}, -1}, + {[]*dto.LabelPair{makeLabel("triggerIndex", "abc")}, -1}, + } + + for _, tc := range cases { + m := &dto.Metric{Label: tc.labels} + got := triggerIndex(m) + if got != tc.want { + t.Errorf("triggerIndex(%v) = %d, want %d", tc.labels, got, tc.want) + } + } +} diff --git a/internal/collector/kubelet_summary_client.go b/internal/collector/kubelet_summary_client.go index 920f2713..38734e55 100644 --- a/internal/collector/kubelet_summary_client.go +++ b/internal/collector/kubelet_summary_client.go @@ -27,12 +27,12 @@ const defaultKubeletFallbackTimeout = 15 * time.Second // guarantee that a DaemonSet alone cannot. // // The Summary API exposes the same kubelet stats nodemon itself is built on -// (usageNanoCores / workingSetBytes), so results are returned in the existing +// (usageNanoCores / workingSetBytes) plus node filesystem occupancy +// (node.fs and node.runtime.imageFs), so results are returned in the existing // UnifiedNodeMetric / UnifiedContainerMetric shapes and reuse all downstream -// conversion. Only CPU + memory are available here — network/disk rates, -// CPU-throttle, GPU and JVM metrics remain nodemon-only and stay zero for -// fallback nodes (a deliberate degraded mode, on par with what metrics-server -// would have provided). +// conversion. Network and disk-I/O rates, CPU-throttle, GPU and JVM metrics +// remain nodemon-only and stay zero for fallback nodes (a deliberate degraded +// mode, on par with what metrics-server would have provided). type KubeletSummaryClient struct { k8sClient kubernetes.Interface log logr.Logger @@ -62,9 +62,11 @@ type kubeletSummary struct { } type kubeletNodeStats struct { - NodeName string `json:"nodeName"` - CPU *kubeletCPUStats `json:"cpu"` - Memory *kubeletMemoryStats `json:"memory"` + NodeName string `json:"nodeName"` + CPU *kubeletCPUStats `json:"cpu"` + Memory *kubeletMemoryStats `json:"memory"` + Fs *kubeletFsStats `json:"fs"` + Runtime *kubeletRuntimeStats `json:"runtime"` } type kubeletPodStats struct { @@ -94,6 +96,19 @@ type kubeletMemoryStats struct { RSSBytes *uint64 `json:"rssBytes"` } +type kubeletFsStats struct { + AvailableBytes *uint64 `json:"availableBytes"` + CapacityBytes *uint64 `json:"capacityBytes"` + UsedBytes *uint64 `json:"usedBytes"` + Inodes *uint64 `json:"inodes"` + InodesFree *uint64 `json:"inodesFree"` + InodesUsed *uint64 `json:"inodesUsed"` +} + +type kubeletRuntimeStats struct { + ImageFs *kubeletFsStats `json:"imageFs"` +} + // fetchSummary retrieves and parses the kubelet Summary API for a single node // via GET /api/v1/nodes//proxy/stats/summary. func (c *KubeletSummaryClient) fetchSummary(ctx context.Context, nodeName string) (*kubeletSummary, error) { @@ -150,9 +165,72 @@ func nodeMetricFromSummary(nodeName string, summary *kubeletSummary) *UnifiedNod if mem := summary.Node.Memory; mem != nil && mem.WorkingSetBytes != nil { m.MemoryWorkingSet = *mem.WorkingSetBytes } + applyNodeStorage(m, summary.Node.Fs, summary.Node.Runtime) return m } +// applyNodeStorage copies filesystem occupancy from a kubelet node summary onto +// a UnifiedNodeMetric. Shared by the full node-metrics fallback and by the +// storage-only top-up FetchNodeStorageByNode uses. +func applyNodeStorage(m *UnifiedNodeMetric, fs *kubeletFsStats, runtime *kubeletRuntimeStats) { + if fs != nil { + m.NodefsUsedBytes = derefKubeletU64(fs.UsedBytes) + m.NodefsCapacityBytes = derefKubeletU64(fs.CapacityBytes) + m.NodefsAvailableBytes = derefKubeletU64(fs.AvailableBytes) + m.NodefsInodesUsed = derefKubeletU64(fs.InodesUsed) + m.NodefsInodesTotal = derefKubeletU64(fs.Inodes) + } + if runtime != nil && runtime.ImageFs != nil { + m.ImagefsUsedBytes = derefKubeletU64(runtime.ImageFs.UsedBytes) + m.ImagefsCapacityBytes = derefKubeletU64(runtime.ImageFs.CapacityBytes) + m.ImagefsAvailableBytes = derefKubeletU64(runtime.ImageFs.AvailableBytes) + m.ImagefsSharedDevice = kubeletImagefsShared(fs, runtime.ImageFs) + } +} + +func derefKubeletU64(v *uint64) uint64 { + if v == nil { + return 0 + } + return *v +} + +// kubeletImagefsShared mirrors nodemon's imagefsSharesDeviceWithNodefs so both +// collection paths classify a node identically. Shared iff total capacity and +// total inode count both match; zero capacity is not evidence of sharing. +func kubeletImagefsShared(nodefs, imagefs *kubeletFsStats) bool { + if nodefs == nil || imagefs == nil { + return false + } + capacity := derefKubeletU64(nodefs.CapacityBytes) + inodes := derefKubeletU64(nodefs.Inodes) + if capacity == 0 || inodes == 0 { + return false + } + return capacity == derefKubeletU64(imagefs.CapacityBytes) && inodes == derefKubeletU64(imagefs.Inodes) +} + +// FetchNodeStorageByNode returns ONLY the filesystem occupancy fields for a +// node, leaving CPU and memory zeroed. +// +// This exists for the rollout window: a nodemon pod that predates the storage +// fields returns a snapshot with no filesystem data, and the node collector +// tops it up from the kubelet rather than waiting for the DaemonSet to roll. +// It is deliberately NOT called unconditionally — that would reintroduce the +// per-node redundant round trip removed in issue 9410. +func (c *KubeletSummaryClient) FetchNodeStorageByNode( + ctx context.Context, + nodeName string, +) (*UnifiedNodeMetric, error) { + summary, err := c.fetchSummary(ctx, nodeName) + if err != nil { + return nil, err + } + m := &UnifiedNodeMetric{NodeName: nodeName} + applyNodeStorage(m, summary.Node.Fs, summary.Node.Runtime) + return m, nil +} + // FetchContainerMetricsByNode returns per-container CPU/memory usage for all pods // on the given node from the kubelet Summary API. func (c *KubeletSummaryClient) FetchContainerMetricsByNode( diff --git a/internal/collector/kubelet_summary_client_test.go b/internal/collector/kubelet_summary_client_test.go index b9bac8d0..ffe37396 100644 --- a/internal/collector/kubelet_summary_client_test.go +++ b/internal/collector/kubelet_summary_client_test.go @@ -8,7 +8,17 @@ const sampleSummaryJSON = `{ "node": { "nodeName": "node-a", "cpu": {"time": "2026-06-17T10:00:00Z", "usageNanoCores": 1500000000}, - "memory": {"workingSetBytes": 2147483648} + "memory": {"workingSetBytes": 2147483648}, + "fs": { + "availableBytes": 94501658624, "capacityBytes": 107294470144, "usedBytes": 12792811520, + "inodesFree": 52254168, "inodes": 52422640, "inodesUsed": 168472 + }, + "runtime": { + "imageFs": { + "availableBytes": 94501658624, "capacityBytes": 107294470144, "usedBytes": 6701690880, + "inodesFree": 52254168, "inodes": 52422640, "inodesUsed": 78769 + } + } }, "pods": [ { @@ -92,6 +102,45 @@ func TestParseKubeletSummary_ContainerMetrics(t *testing.T) { } } +func TestParseKubeletSummary_NodeStorage(t *testing.T) { + summary, err := parseKubeletSummary([]byte(sampleSummaryJSON), "node-a") + if err != nil { + t.Fatalf("parse failed: %v", err) + } + + nm := nodeMetricFromSummary("node-a", summary) + if nm.NodefsUsedBytes != 12792811520 { + t.Errorf("NodefsUsedBytes = %d, want 12792811520", nm.NodefsUsedBytes) + } + if nm.NodefsCapacityBytes != 107294470144 { + t.Errorf("NodefsCapacityBytes = %d, want 107294470144", nm.NodefsCapacityBytes) + } + if nm.NodefsInodesTotal != 52422640 { + t.Errorf("NodefsInodesTotal = %d, want 52422640", nm.NodefsInodesTotal) + } + if nm.ImagefsUsedBytes != 6701690880 { + t.Errorf("ImagefsUsedBytes = %d, want 6701690880", nm.ImagefsUsedBytes) + } + if !nm.ImagefsSharedDevice { + t.Error("ImagefsSharedDevice = false, want true") + } +} + +// The kubelet fallback path must be usable for a node whose summary omits the +// storage sections entirely — older kubelets, or a runtime that reports none. +func TestParseKubeletSummary_NodeStorageAbsent(t *testing.T) { + const noFS = `{"node": {"nodeName": "node-a", "cpu": {"usageNanoCores": 1}}, "pods": []}` + summary, err := parseKubeletSummary([]byte(noFS), "node-a") + if err != nil { + t.Fatalf("parse failed: %v", err) + } + + nm := nodeMetricFromSummary("node-a", summary) + if nm.NodefsCapacityBytes != 0 || nm.ImagefsSharedDevice { + t.Errorf("expected zeroed storage, got %+v", nm) + } +} + func TestParseKubeletSummary_MissingAndEmpty(t *testing.T) { // Node with no cpu/memory blocks and no pods must not panic and yield zeroed metrics. summary, err := parseKubeletSummary([]byte(`{"node":{"nodeName":"n"},"pods":[]}`), "n") diff --git a/internal/collector/limitrange_collector.go b/internal/collector/limitrange_collector.go index 0a0e2db1..ed6859d8 100644 --- a/internal/collector/limitrange_collector.go +++ b/internal/collector/limitrange_collector.go @@ -167,8 +167,8 @@ func (c *LimitRangeCollector) limitRangeChanged(oldLR, newLR *corev1.LimitRange) oldLR.ObjectMeta, newLR.ObjectMeta, ) - if changed != IgnoreChanges { - return changed == PushChanges + if changed == PushChanges { + return true } // Check for limit changes diff --git a/internal/collector/manager.go b/internal/collector/manager.go index fb06b83f..b976564c 100644 --- a/internal/collector/manager.go +++ b/internal/collector/manager.go @@ -7,6 +7,7 @@ import ( "fmt" "strings" "sync" + "sync/atomic" "time" gen "github.com/devzero-inc/zxporter/gen/api/v1" @@ -43,9 +44,17 @@ type CollectionConfig struct { // CollectionManager orchestrates multiple collectors type CollectionManager struct { - collectors map[string]ResourceCollector - collectorCtxs map[string]context.CancelFunc // Track context cancellation functions for each collector - processorWg map[string]*sync.WaitGroup // Track processor goroutines for each collector + collectors map[string]ResourceCollector + // collectorView is a copy-on-write snapshot of collectors, republished under mu by + // every mutation of that map. GetCollector reads it without taking mu, because its + // callers include informer event handlers (the node lifecycle fallback and the + // DisruptionBlocked classifier resolve other collectors' caches through it), and a + // handler that waits on mu stalls that informer's whole listener for as long as mu is + // held. mu is held for seconds at a time by stopCollectorInternal, and on 2026-09-25 a + // leaked mu stopped every Node event from reaching the control plane. + collectorView atomic.Pointer[map[string]ResourceCollector] + collectorCtxs map[string]*collectorRun // The live run of each running (or starting) collector + processorWg map[string]*sync.WaitGroup // Track processor goroutines for each collector combinedChannel chan []CollectedResource telemetryMetrics *TelemetryMetrics // Metrics for telemetry wg sync.WaitGroup @@ -71,9 +80,9 @@ func NewCollectionManager(config *CollectionConfig, bufferSize = config.BufferSize } - return &CollectionManager{ + m := &CollectionManager{ collectors: make(map[string]ResourceCollector), - collectorCtxs: make(map[string]context.CancelFunc), + collectorCtxs: make(map[string]*collectorRun), processorWg: make(map[string]*sync.WaitGroup), combinedChannel: make(chan []CollectedResource, bufferSize), telemetryMetrics: telemetryMetrics, @@ -84,6 +93,32 @@ func NewCollectionManager(config *CollectionConfig, telemetryLogger: telemetryLogger, healthManager: healthManager, } + m.publishCollectorViewLocked() + return m +} + +// collectorRun is one start of a collector, from reserveStart until it is stopped. It is +// compared by pointer: launchCollector re-checks, once Start returns, that its run is +// still the current entry in collectorCtxs, which is how a stop that landed during Start +// is detected even if a new run for the same type has been reserved since. +type collectorRun struct { + cancel context.CancelFunc + // starting is true from reserveStart until launchCollector's handshake. While it is + // set, the goroutine inside Start owns the collector: a stop cancels the run and + // unregisters it but leaves the Stop call to that goroutine, because stopping a + // collector concurrently with its own Start is not safe for the collectors here, and + // calling Stop from both sides would double-close their channels. + starting bool +} + +// publishCollectorViewLocked republishes the lock-free snapshot GetCollector reads. Must +// be called with mu held, after every write to m.collectors. +func (m *CollectionManager) publishCollectorViewLocked() { + view := make(map[string]ResourceCollector, len(m.collectors)) + for collectorType, collector := range m.collectors { + view[collectorType] = collector + } + m.collectorView.Store(&view) } // RegisterCollector adds a new collector @@ -98,6 +133,7 @@ func (m *CollectionManager) RegisterCollector(collector ResourceCollector) error m.logger.Info("Registering collector", "type", collectorType) m.collectors[collectorType] = collector + m.publishCollectorViewLocked() // Initialize wait group for this collector type if it doesn't exist if _, exists := m.processorWg[collectorType]; !exists { @@ -130,17 +166,35 @@ func (m *CollectionManager) stopCollectorInternal(collectorType string) error { return fmt.Errorf("collector for type %s not registered", collectorType) } - cancel, ctxExists := m.collectorCtxs[collectorType] - if !ctxExists { - return fmt.Errorf("collector %s is not running", collectorType) + run, running := m.collectorCtxs[collectorType] + + // Unpublish before Stop and the drain wait below, so lock-free readers stop being + // handed a collector that is shutting down. + delete(m.collectors, collectorType) + m.publishCollectorViewLocked() + + if !running { + // Registered but not running: its Start failed, or it was never started. It is + // still removed, or the replacement a restart registers next is refused as + // "already registered" and the type stays dark until the pod restarts. + m.logger.Info("Removed collector that was not running", "type", collectorType) + return nil } m.logger.Info("Stopping collector", "type", collectorType) // Cancel the context for this collector - cancel() + run.cancel() delete(m.collectorCtxs, collectorType) + if run.starting { + // Its Start is still in flight. Waiting for it here would deadlock (the starter + // needs mu to finish), so hand over instead: with the run gone, launchCollector's + // handshake stops the collector itself and never attaches a processor. + m.logger.Info("Collector stopped while starting; its starter will stop it", "type", collectorType) + return nil + } + // Stop the collector if err := collector.Stop(); err != nil { m.logger.Error(err, "Error stopping collector", "type", collectorType) @@ -167,9 +221,6 @@ func (m *CollectionManager) stopCollectorInternal(collectorType string) error { } } - // Remove the collector from the map - delete(m.collectors, collectorType) - return nil } @@ -287,19 +338,11 @@ func (m *CollectionManager) StartAll(ctx context.Context) error { // StartCollector starts a specific collector func (m *CollectionManager) StartCollector(ctx context.Context, collectorType string) error { - m.mu.Lock() - - collector, exists := m.collectors[collectorType] - if !exists { - return fmt.Errorf("collector for type %s not registered", collectorType) - } - - if _, ctxExists := m.collectorCtxs[collectorType]; ctxExists { - return fmt.Errorf("collector %s is already running", collectorType) + collector, collectorCtx, run, wg, err := m.reserveStart(collectorType, nil) + if err != nil { + return err } - m.mu.Unlock() - - return m.startCollectorInternal(collectorType, collector) + return m.launchCollector(collectorType, collector, collectorCtx, run, wg) } // startCollectorInternal is a helper function to start a collector with appropriate context management @@ -307,25 +350,81 @@ func (m *CollectionManager) startCollectorInternal( collectorType string, collector ResourceCollector, ) error { + _, collectorCtx, run, wg, err := m.reserveStart(collectorType, collector) + if err != nil { + return err + } + return m.launchCollector(collectorType, collector, collectorCtx, run, wg) +} + +// reserveStart claims collectorType for starting, in one critical section: it resolves +// the collector (when known is nil, or confirms known is still the registered instance), +// refuses one that is already running, and records the run that marks it running. The +// check and the claim are atomic so two concurrent starts cannot both pass the "already +// running" check. +// +// The mutex is released by defer on every path. The early returns here used to leave it +// held, which on 2026-09-25 wedged the manager, and every informer handler that looks a +// collector up through it, until the pod restarted. +func (m *CollectionManager) reserveStart( + collectorType string, + known ResourceCollector, +) (ResourceCollector, context.Context, *collectorRun, *sync.WaitGroup, error) { m.mu.Lock() + defer m.mu.Unlock() + + registered, exists := m.collectors[collectorType] + if !exists { + return nil, nil, nil, nil, fmt.Errorf("collector for type %s not registered", collectorType) + } + // StartAll hands its goroutines the instance it saw; if that was stopped or replaced + // before the goroutine got here, starting it would run an unregistered collector. + if known != nil && known != registered { + return nil, nil, nil, nil, fmt.Errorf("collector for type %s was replaced before it started", collectorType) + } + collector := registered + + if _, running := m.collectorCtxs[collectorType]; running { + return nil, nil, nil, nil, fmt.Errorf("collector %s is already running", collectorType) + } + m.logger.Info("Starting collector", "type", collectorType) // Create a new context for this collector that can be cancelled individually collectorCtx, cancel := context.WithCancel(context.Background()) - m.collectorCtxs[collectorType] = cancel + run := &collectorRun{cancel: cancel, starting: true} + m.collectorCtxs[collectorType] = run // Make sure the processor wait group exists for this collector - if _, exists := m.processorWg[collectorType]; !exists { - m.processorWg[collectorType] = &sync.WaitGroup{} + wg, exists := m.processorWg[collectorType] + if !exists || wg == nil { + wg = &sync.WaitGroup{} + m.processorWg[collectorType] = wg } - m.mu.Unlock() // we can unlock it here to unblock other go routines to progress, without depend on the start method of other collectors + return collector, collectorCtx, run, wg, nil +} - // Start this collector +// launchCollector runs the collector's Start outside the manager mutex (Start can block, +// and must not hold up other collectors), then attaches its channel processor. +// +// After Start returns it re-checks, under mu, that run is still the current entry. A stop +// that landed during Start has removed it and left the collector to this goroutine (see +// collectorRun.starting), so this goroutine stops the collector and does not attach a +// processor: its channel may never close, and the processor would leak. +func (m *CollectionManager) launchCollector( + collectorType string, + collector ResourceCollector, + collectorCtx context.Context, + run *collectorRun, + wg *sync.WaitGroup, +) error { if err := collector.Start(collectorCtx); err != nil { // this is the blocking call some time. - cancel() // Clean up the context + run.cancel() // Clean up the context m.mu.Lock() - delete(m.collectorCtxs, collectorType) + if m.collectorCtxs[collectorType] == run { + delete(m.collectorCtxs, collectorType) + } m.mu.Unlock() m.updateHealthStatus( health.ComponentCollectorManager, @@ -336,10 +435,24 @@ func (m *CollectionManager) startCollectorInternal( return fmt.Errorf("failed to start collector %s: %w", collectorType, err) } - // Start a goroutine to read from this collector's channel + m.mu.Lock() + if m.collectorCtxs[collectorType] != run { + m.mu.Unlock() + m.logger.Info("Collector was stopped while starting; stopping it", "type", collectorType) + if err := collector.Stop(); err != nil { + m.logger.Error(err, "Error stopping collector that was stopped while starting", "type", collectorType) + } + return fmt.Errorf("collector %s was stopped while it was starting", collectorType) + } + run.starting = false + // The Adds are taken under mu because StopAll and stopCollectorInternal Wait on + // these groups while holding it; without that edge an Add is unordered with a + // concurrent Wait (a WaitGroup misuse the race detector reports). m.wg.Add(1) - wg := m.processorWg[collectorType] wg.Add(1) + m.mu.Unlock() + + // Start a goroutine to read from this collector's channel go m.processCollectorChannel(collectorType, collector, wg) return nil @@ -359,13 +472,19 @@ func (m *CollectionManager) StopAll() error { m.logger.Info("Stopping all collectors", "count", len(m.collectors)) // Cancel the contexts for all collectors - for collectorType, cancel := range m.collectorCtxs { + for collectorType, run := range m.collectorCtxs { m.logger.Info("Cancelling context for collector", "type", collectorType) - cancel() + run.cancel() } // Stop each collector for collectorType, collector := range m.collectors { + if run, ok := m.collectorCtxs[collectorType]; ok && run.starting { + // Its Start is in flight; the reset of collectorCtxs below hands it to + // launchCollector's handshake, which stops it. See collectorRun.starting. + m.logger.Info("Collector is still starting; its starter will stop it", "type", collectorType) + continue + } m.logger.Info("Stopping collector", "type", collectorType) if err := collector.Stop(); err != nil { m.logger.Error(err, "Error stopping collector", "type", collectorType) @@ -400,7 +519,7 @@ func (m *CollectionManager) StopAll() error { m.combinedChannel = make(chan []CollectedResource, originalBufferSize) // Clear all tracking maps - m.collectorCtxs = make(map[string]context.CancelFunc) + m.collectorCtxs = make(map[string]*collectorRun) m.processorWg = make(map[string]*sync.WaitGroup) // Wait for all goroutines to finish (with timeout) @@ -517,12 +636,17 @@ func (m *CollectionManager) IsCollectorRunning(collectorType string) bool { return exists } -// GetCollector returns a specific collector by type, or nil if not found +// GetCollector returns a specific collector by type, or nil if not found. +// +// Lock-free by design: it reads the snapshot published by publishCollectorViewLocked and +// never waits on mu, so it is safe to call from an informer event handler. See the +// collectorView field. func (m *CollectionManager) GetCollector(collectorType string) ResourceCollector { - m.mu.RLock() - defer m.mu.RUnlock() - - collector, exists := m.collectors[collectorType] + view := m.collectorView.Load() + if view == nil { + return nil + } + collector, exists := (*view)[collectorType] if !exists { return nil } diff --git a/internal/collector/manager_lock_test.go b/internal/collector/manager_lock_test.go new file mode 100644 index 00000000..c3f32091 --- /dev/null +++ b/internal/collector/manager_lock_test.go @@ -0,0 +1,147 @@ +package collector + +import ( + "context" + "testing" + "time" + + "github.com/go-logr/logr" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// lockTestTimeout bounds every "does this call come back" check below. The calls under +// test are map reads and writes, so anything near this long is a deadlock, not slowness. +const lockTestTimeout = 2 * time.Second + +func newLockTestManager() *CollectionManager { + return NewCollectionManager( + &CollectionConfig{BufferSize: 10}, + nil, + getTestTelemetryMetrics(), + logr.Discard(), + &noopTelemetryLogger{}, + nil, + ) +} + +// completesWithin runs fn on its own goroutine and reports whether it returned within +// lockTestTimeout. On a timeout the goroutine is left blocked: that is the bug being detected, +// and there is no way to unblock a goroutine parked on a leaked mutex. +func completesWithin(fn func()) bool { + done := make(chan struct{}) + go func() { + fn() + close(done) + }() + select { + case <-done: + return true + case <-time.After(lockTestTimeout): + return false + } +} + +// assertManagerUsable fails the test if the manager's mutex is no longer obtainable, by +// exercising one reader and one writer that both take it. +func assertManagerUsable(t *testing.T, mgr *CollectionManager) { + t.Helper() + ok := completesWithin(func() { + _ = mgr.GetCollectorTypes() + _ = mgr.RegisterCollector(&mockCollector{ + collectorType: "after_failed_start", + resourceCh: make(chan []CollectedResource, 1), + }) + }) + require.True(t, ok, "the manager mutex was not released: a later lock holder never returned") +} + +// TestStartCollector_UnregisteredTypeReleasesLock is the preprod incident: a Start for a +// type that was never registered (the KEDA name/key mismatch) returned with the manager +// mutex still held, wedging every later caller — including the node informer's handler. +func TestStartCollector_UnregisteredTypeReleasesLock(t *testing.T) { + mgr := newLockTestManager() + + err := mgr.StartCollector(context.Background(), "keda_scaled_job") + require.Error(t, err) + assert.Contains(t, err.Error(), "not registered") + + assertManagerUsable(t, mgr) +} + +// TestStartCollector_AlreadyRunningReleasesLock covers the other early return. +func TestStartCollector_AlreadyRunningReleasesLock(t *testing.T) { + mgr := newLockTestManager() + mock := &mockCollector{collectorType: "test_resource", resourceCh: make(chan []CollectedResource, 1)} + require.NoError(t, mgr.RegisterCollector(mock)) + require.NoError(t, mgr.StartCollector(context.Background(), "test_resource")) + + err := mgr.StartCollector(context.Background(), "test_resource") + require.Error(t, err) + assert.Contains(t, err.Error(), "already running") + + assertManagerUsable(t, mgr) + assert.True(t, completesWithin(func() { _ = mgr.StopCollector("test_resource") })) +} + +// TestGetCollector_DoesNotWaitOnManagerMutex pins the hardening: GetCollector is called +// from informer event handlers (node lifecycle fallback, DisruptionBlocked +// classification), and a handler that blocks stalls that informer's whole listener. It +// must stay answerable while something else holds the manager mutex — for instance +// stopCollectorInternal, which holds it across a collector's Stop and a 5s drain wait. +func TestGetCollector_DoesNotWaitOnManagerMutex(t *testing.T) { + mgr := newLockTestManager() + mock := &mockCollector{collectorType: "karpenter", resourceCh: make(chan []CollectedResource, 1)} + require.NoError(t, mgr.RegisterCollector(mock)) + + mgr.mu.Lock() + defer mgr.mu.Unlock() + + var got ResourceCollector + require.True(t, completesWithin(func() { got = mgr.GetCollector("karpenter") }), + "GetCollector blocked on the manager mutex") + assert.Same(t, mock, got) + assert.True(t, completesWithin(func() { got = mgr.GetCollector("missing") })) + assert.Nil(t, got) +} + +// TestGetCollector_TracksRegistrationAndRemoval checks the lock-free view stays in step +// with the registry across register, stop and re-register. +func TestGetCollector_TracksRegistrationAndRemoval(t *testing.T) { + mgr := newLockTestManager() + first := &mockCollector{collectorType: "node", resourceCh: make(chan []CollectedResource, 1)} + require.NoError(t, mgr.RegisterCollector(first)) + assert.Same(t, first, mgr.GetCollector("node")) + + require.NoError(t, mgr.StartCollector(context.Background(), "node")) + require.NoError(t, mgr.StopCollector("node")) + assert.Nil(t, mgr.GetCollector("node"), "a stopped collector is removed from the registry") + + second := &mockCollector{collectorType: "node", resourceCh: make(chan []CollectedResource, 1)} + require.NoError(t, mgr.RegisterCollector(second)) + assert.Same(t, second, mgr.GetCollector("node")) + + require.NoError(t, mgr.DeregisterCollector("node")) + assert.Nil(t, mgr.GetCollector("node")) +} + +// TestNodeLifecycleFallback_DoesNotBlockOnManagerMutex is the amplifier from the incident, +// end to end: the node informer's handler consults the Karpenter NodeClaim cache through +// the manager. With the manager mutex held it must still return. +func TestNodeLifecycleFallback_DoesNotBlockOnManagerMutex(t *testing.T) { + mgr := newLockTestManager() + c, _ := newLifecycleTestNodeCollector() + c.SetNodeClaimSource(mgr) + + node := &corev1.Node{ObjectMeta: metav1.ObjectMeta{Name: "ip-10-0-9-9", Labels: map[string]string{}}} + + mgr.mu.Lock() + defer mgr.mu.Unlock() + + var managed bool + require.True(t, completesWithin(func() { managed = c.nodeManagedByKarpenter(node) }), + "the node informer handler blocked on the collection manager mutex") + assert.False(t, managed) +} diff --git a/internal/collector/manager_start_stop_test.go b/internal/collector/manager_start_stop_test.go new file mode 100644 index 00000000..ba885a5a --- /dev/null +++ b/internal/collector/manager_start_stop_test.go @@ -0,0 +1,110 @@ +package collector + +import ( + "context" + "sync/atomic" + "testing" + "time" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +// blockingStartCollector's Start parks until release is closed, so a test can stop it +// while its Start is still in flight. Its resource channel is never closed, so a +// processor attached to it would run forever; resourceChannelCalls counts processor +// launches (processCollectorChannel's first action is GetResourceChannel). +type blockingStartCollector struct { + collectorType string + entered chan struct{} + release chan struct{} + resourceCh chan []CollectedResource + stopCalls atomic.Int32 + resourceChannelCalls atomic.Int32 +} + +func newBlockingStartCollector(collectorType string) *blockingStartCollector { + return &blockingStartCollector{ + collectorType: collectorType, + entered: make(chan struct{}), + release: make(chan struct{}), + resourceCh: make(chan []CollectedResource), + } +} + +func (c *blockingStartCollector) Start(context.Context) error { + close(c.entered) + <-c.release + return nil +} +func (c *blockingStartCollector) Stop() error { c.stopCalls.Add(1); return nil } +func (c *blockingStartCollector) GetType() string { return c.collectorType } +func (c *blockingStartCollector) GetResourceChannel() <-chan []CollectedResource { + c.resourceChannelCalls.Add(1) + return c.resourceCh +} +func (c *blockingStartCollector) IsAvailable(context.Context) bool { return true } +func (c *blockingStartCollector) AddResource(resource interface{}) error { return nil } + +// TestStopWhileStarting_NoProcessorAndStoppedOnce covers a stop that lands while the +// collector's Start is still running. The stop must not wait for Start (the starter needs +// the mutex to finish), the collector must be Stopped exactly once (by whichever side +// owns it at that moment, never both), and no processor may be attached to a collector +// that was already stopped: its channel is never closed, so that goroutine would leak. +func TestStopWhileStarting_NoProcessorAndStoppedOnce(t *testing.T) { + stops := map[string]func(*CollectionManager, string){ + "StopCollector": func(m *CollectionManager, ct string) { _ = m.StopCollector(ct) }, + "DeregisterCollector": func(m *CollectionManager, ct string) { _ = m.DeregisterCollector(ct) }, + "StopAll": func(m *CollectionManager, _ string) { + m.mu.Lock() + m.started = true // StopAll is a no-op on a manager StartAll never started + m.mu.Unlock() + _ = m.StopAll() + }, + } + for name, stop := range stops { + t.Run(name, func(t *testing.T) { + mgr := newLockTestManager() + c := newBlockingStartCollector("slow") + require.NoError(t, mgr.RegisterCollector(c)) + + startErr := make(chan error, 1) + go func() { startErr <- mgr.StartCollector(context.Background(), "slow") }() + <-c.entered + + require.True(t, completesWithin(func() { stop(mgr, "slow") }), + "stopping a collector whose Start is in flight must not wait for that Start") + + close(c.release) + select { + case err := <-startErr: + assert.Error(t, err, "a start overtaken by a stop reports that it did not take effect") + case <-time.After(lockTestTimeout): + t.Fatal("StartCollector did not return after its Start was released") + } + + // A processor is spawned asynchronously; give one that was wrongly launched the + // chance to run before asserting that none was. + time.Sleep(50 * time.Millisecond) + assert.Equal(t, int32(1), c.stopCalls.Load(), "the collector is stopped exactly once") + assert.Equal(t, int32(0), c.resourceChannelCalls.Load(), + "no processor may be attached to a collector stopped mid-start") + assert.False(t, mgr.IsCollectorRunning("slow")) + }) + } +} + +// TestStopCollector_RemovesRegisteredButNotRunning covers a collector whose Start failed +// or never ran: stopping it must clear it, or the replacement a restart registers next is +// refused as "already registered". +func TestStopCollector_RemovesRegisteredButNotRunning(t *testing.T) { + mgr := newLockTestManager() + require.NoError(t, mgr.RegisterCollector( + &mockCollector{collectorType: "pod", resourceCh: make(chan []CollectedResource, 1)})) + + _ = mgr.StopCollector("pod") + + assert.Nil(t, mgr.GetCollector("pod")) + assert.NoError(t, mgr.RegisterCollector( + &mockCollector{collectorType: "pod", resourceCh: make(chan []CollectedResource, 1)})) +} diff --git a/internal/collector/namespace_collector.go b/internal/collector/namespace_collector.go index 3a5dd0d5..b0ca9fe7 100644 --- a/internal/collector/namespace_collector.go +++ b/internal/collector/namespace_collector.go @@ -163,8 +163,8 @@ func (c *NamespaceCollector) namespaceChanged(oldNamespace, newNamespace *corev1 oldNamespace.ObjectMeta, newNamespace.ObjectMeta, ) - if changed != IgnoreChanges { - return changed == PushChanges + if changed == PushChanges { + return true } // Check for status phase changes diff --git a/internal/collector/networkpolicy_collector.go b/internal/collector/networkpolicy_collector.go index ebbcb5b6..ef34b270 100644 --- a/internal/collector/networkpolicy_collector.go +++ b/internal/collector/networkpolicy_collector.go @@ -177,8 +177,8 @@ func (c *NetworkPolicyCollector) networkPolicyChanged( oldNetworkPolicy.ObjectMeta, newNetworkPolicy.ObjectMeta, ) - if changed != IgnoreChanges { - return changed == PushChanges + if changed == PushChanges { + return true } // Check for pod selector changes diff --git a/internal/collector/node_collector.go b/internal/collector/node_collector.go index 46679e28..d6f75298 100644 --- a/internal/collector/node_collector.go +++ b/internal/collector/node_collector.go @@ -328,21 +328,15 @@ func (c *NodeCollector) Start(ctx context.Context) error { // Add these new methods for pod event handling func (c *NodeCollector) handlePodEvent(pod *corev1.Pod, eventType EventType) { - // Skip pods not assigned to nodes yet - if pod.Spec.NodeName == "" { - return - } - - // Skip excluded nodes - if c.isExcluded(pod.Spec.NodeName) { - return - } - switch eventType { case EventTypeAdd, EventTypeUpdate: - c.addPodToNode(pod) + if pod.Spec.NodeName != "" && !c.isExcluded(pod.Spec.NodeName) { + c.addPodToNode(pod) + } case EventTypeDelete: - c.removePodFromNode(pod) + if pod.Spec.NodeName != "" { + c.removePodFromNode(pod) + } } } @@ -385,6 +379,14 @@ func (c *NodeCollector) removePodFromNode(pod *corev1.Pod) { // // Returns nil for an unknown node, which is indistinguishable from "node has no pods". // Both mean "no pod here can be blocking", so the distinction has no caller. +// +// Pods are tracked by pod.Spec.NodeName ONLY, so a pending pod appears here for no node. +// A second map keyed by pod.Status.NominatedNodeName used to be maintained alongside this +// one — on every pod add, update and delete cluster-wide — to serve a PodsNominatedFor +// lookup for the disruption classifier. Both are gone: that field is kube-scheduler +// preemption state, not Karpenter nomination, so the lookup had no correct consumer and the +// bookkeeping was pure per-event cost. Do not reintroduce it without reading the +// PodNominated note on classifyDisruptionBlocked first. func (c *NodeCollector) PodsOnNode(nodeName string) []*corev1.Pod { c.podMapMutex.RLock() defer c.podMapMutex.RUnlock() @@ -407,12 +409,14 @@ func (c *NodeCollector) calculateNodeWorkloadResources(nodeName string) map[stri defer c.podMapMutex.RUnlock() result := map[string]interface{}{ - "cpuRequestsMillis": int64(0), - "cpuLimitsMillis": int64(0), - "memoryRequestsBytes": int64(0), - "memoryLimitsBytes": int64(0), - "gpuRequestCount": int64(0), - "gpuLimitCount": int64(0), + "cpuRequestsMillis": int64(0), + "cpuLimitsMillis": int64(0), + "memoryRequestsBytes": int64(0), + "memoryLimitsBytes": int64(0), + "gpuRequestCount": int64(0), + "gpuLimitCount": int64(0), + "ephemeralStorageRequestsBytes": int64(0), + "ephemeralStorageLimitsBytes": int64(0), } // Check if we have pods for this node @@ -459,6 +463,19 @@ func (c *NodeCollector) calculateNodeWorkloadResources(nodeName string) map[stri if val, ok := container.Resources.Limits[gpuconst.GpuResource]; ok { result["gpuLimitCount"] = result["gpuLimitCount"].(int64) + val.Value() } + + // Ephemeral-storage requests. This is the quantity the scheduler + // admits pods against, so it is what makes a node's disk + // commitment comparable to its allocatable — unlike the kubelet's + // nodefs numbers, which measure actual consumption. + if val, ok := container.Resources.Requests[corev1.ResourceEphemeralStorage]; ok { + result["ephemeralStorageRequestsBytes"] = result["ephemeralStorageRequestsBytes"].(int64) + val.Value() + } + + // Ephemeral-storage limits. + if val, ok := container.Resources.Limits[corev1.ResourceEphemeralStorage]; ok { + result["ephemeralStorageLimitsBytes"] = result["ephemeralStorageLimitsBytes"].(int64) + val.Value() + } } } @@ -729,6 +746,11 @@ func (c *NodeCollector) nodeStatusChanged(oldNode, newNode *corev1.Node) bool { if !oldAllocGPU.Equal(newAllocGPU) { return true } + oldAllocEphemeral := oldNode.Status.Allocatable[corev1.ResourceEphemeralStorage] + newAllocEphemeral := newNode.Status.Allocatable[corev1.ResourceEphemeralStorage] + if !oldAllocEphemeral.Equal(newAllocEphemeral) { + return true + } // Check if capacity changed (CPU, memory, GPU) if !oldNode.Status.Capacity.Cpu().Equal(*newNode.Status.Capacity.Cpu()) || @@ -740,6 +762,11 @@ func (c *NodeCollector) nodeStatusChanged(oldNode, newNode *corev1.Node) bool { if !oldCapGPU.Equal(newCapGPU) { return true } + oldCapEphemeral := oldNode.Status.Capacity[corev1.ResourceEphemeralStorage] + newCapEphemeral := newNode.Status.Capacity[corev1.ResourceEphemeralStorage] + if !oldCapEphemeral.Equal(newCapEphemeral) { + return true + } if !reflect.DeepEqual(oldNode.Labels, newNode.Labels) { return true @@ -807,6 +834,17 @@ type nodeCollectionOutcome struct { // DCGM refresh is failing) and therefore dropped. Aggregated per sweep so the // DCGM problem is visible in DAKR telemetry, not only in nodemon's logs. gpuStale bool + // storageToppedUp is true when nodemon returned a node section with no + // filesystem data and the kubelet top-up recovered it. Aggregated per sweep + // so a nodemon rollout that never converges is visible in DAKR telemetry -- + // every one of these nodes is paying an extra API-server call per sweep. + storageToppedUp bool + // storageMissing is true when the node was shipped with no filesystem data + // at all: the top-up was needed and the kubelet could not supply it. This is + // the count that corresponds to an empty storage chart, so it must not + // include nodes the top-up rescued, nor excluded nodes (which are never + // enriched and never sent). + storageMissing bool } type nodeCoverage int @@ -881,6 +919,15 @@ func (c *NodeCollector) collectAllNodeResources(ctx context.Context) { // dropped — surfaced as a per-sweep signal so a DCGM problem is visible in // DAKR telemetry, not just nodemon logs. var gpuStaleNodes []string + // Nodes whose nodemon reported no filesystem data, forcing a kubelet + // top-up that succeeded. Expected transiently during a nodemon rolling + // upgrade; a persistent nonzero count means the fleet never converged and + // every one of these nodes is paying an extra API-server call per sweep. + var storageToppedUpNodes []string + // Nodes shipped with no filesystem data at all — the top-up was needed and + // the kubelet could not supply it. These are the nodes whose storage charts + // are actually empty. + var storageMissingNodes []string for _, o := range outcomes { switch o.coverage { case nodeCoverageNodemon: @@ -897,6 +944,12 @@ func (c *NodeCollector) collectAllNodeResources(ctx context.Context) { if o.gpuStale { gpuStaleNodes = append(gpuStaleNodes, o.nodeName) } + if o.storageToppedUp { + storageToppedUpNodes = append(storageToppedUpNodes, o.nodeName) + } + if o.storageMissing { + storageMissingNodes = append(storageMissingNodes, o.nodeName) + } } c.logger.V(1).Info("Built node metrics", "nodes", len(outcomes), @@ -985,6 +1038,59 @@ func (c *NodeCollector) collectAllNodeResources(ctx context.Context) { } } + // Two distinct signals, so the counts mean what their names say: a + // topped-up node has complete storage and cost an extra API-server call, + // while a missing one shipped without storage at all. Reporting both under + // node_storage_missing made a fully-recovered fleet look like a data gap. + const maxStorageSample = 20 + storageSample := func(nodes []string) string { + sample := nodes + suffix := "" + if len(sample) > maxStorageSample { + suffix = fmt.Sprintf(" (+%d more)", len(sample)-maxStorageSample) + sample = sample[:maxStorageSample] + } + return fmt.Sprintf("%v", sample) + suffix + } + + if len(storageToppedUpNodes) > 0 { + c.logger.Info("Node storage topped up from kubelet (nodemon reported no filesystem data)", + "count", len(storageToppedUpNodes)) + if c.telemetryLogger != nil { + c.telemetryLogger.Report( + gen.LogLevel_LOG_LEVEL_WARN, + "NodeCollector", + "Node storage unavailable from nodemon; topped up from kubelet", + nil, + map[string]string{ + "storage_topped_up": fmt.Sprintf("%d", len(storageToppedUpNodes)), + "sample_nodes": storageSample(storageToppedUpNodes), + "event_type": "node_storage_topped_up", + "zxporter_version": version.Get().String(), + }, + ) + } + } + + if len(storageMissingNodes) > 0 { + c.logger.Info("Node storage unavailable from both nodemon and the kubelet", + "count", len(storageMissingNodes)) + if c.telemetryLogger != nil { + c.telemetryLogger.Report( + gen.LogLevel_LOG_LEVEL_WARN, + "NodeCollector", + "Node storage unavailable from nodemon and the kubelet top-up failed", + nil, + map[string]string{ + "storage_missing": fmt.Sprintf("%d", len(storageMissingNodes)), + "sample_nodes": storageSample(storageMissingNodes), + "event_type": "node_storage_missing", + "zxporter_version": version.Get().String(), + }, + ) + } + } + // Surface coverage gaps: nodes where neither nodemon nor the kubelet fallback // produced metrics. This converts previously-silent blind spots into a signal. if len(uncoveredNodes) > 0 { @@ -1066,6 +1172,41 @@ func (c *NodeCollector) collectSingleNodeResources(ctx context.Context, node *co return outcome } + // Bridge the nodemon rollout window: an older nodemon reports no filesystem + // section, so fetch just that from the kubelet for this node. Conditional by + // design — an unconditional second fetch is exactly what issue 9410 removed. + // + // Below the isExcluded return because the top-up is enrichment: an excluded + // node's metrics are discarded, so fetching its filesystem would spend an + // API-server call on data nobody reads and would put a node that was never + // shipped into the storage counts. + if nodeStorageNeedsTopUp(fromNodemon, nodeMetric) { + if sm, serr := c.kubeletClient.FetchNodeStorageByNode(ctx, node.Name); serr != nil { + c.logger.V(1).Info("Kubelet node-storage top-up failed", "node", node.Name, "error", serr) + } else if sm != nil { + nodeMetric.NodefsUsedBytes = sm.NodefsUsedBytes + nodeMetric.NodefsCapacityBytes = sm.NodefsCapacityBytes + nodeMetric.NodefsAvailableBytes = sm.NodefsAvailableBytes + nodeMetric.NodefsInodesUsed = sm.NodefsInodesUsed + nodeMetric.NodefsInodesTotal = sm.NodefsInodesTotal + nodeMetric.ImagefsUsedBytes = sm.ImagefsUsedBytes + nodeMetric.ImagefsCapacityBytes = sm.ImagefsCapacityBytes + nodeMetric.ImagefsAvailableBytes = sm.ImagefsAvailableBytes + nodeMetric.ImagefsSharedDevice = sm.ImagefsSharedDevice + } + + // Classify on the result, not on whether the call returned an error: + // FetchNodeStorageByNode answers with a zero-valued metric when the + // kubelet's own summary has no filesystem section, so a "successful" + // top-up can still leave the node shipping without storage. Capacity is + // the same "> 0 means reported" contract the rest of this feature uses. + if nodeMetric.NodefsCapacityBytes > 0 { + outcome.storageToppedUp = true + } else { + outcome.storageMissing = true + } + } + usage := corev1.ResourceList{} if nodeMetric != nil { cpuMillis := int64(nodeMetric.CPUUsageNanoCores / 1_000_000) @@ -1133,6 +1274,11 @@ func (c *NodeCollector) collectSingleNodeResources(ctx context.Context, node *co "node": node, } + // Storage. Unlike networkMetrics below, this is NOT gated on fromNodemon: + // the kubelet fallback path produces real filesystem numbers, so a node + // with no nodemon pod still reports disk occupancy. + applyNodeStorageToResourceData(resourceData, node, nodeMetric) + // Add network metrics if available if len(networkMetrics) > 0 { resourceData["networkReceiveBytes"] = networkMetrics["NetworkReceiveBytes"] @@ -1224,6 +1370,59 @@ func networkIOMetricsFromNodeMetric(m *UnifiedNodeMetric) map[string]float64 { } } +// nodeStorageNeedsTopUp reports whether this node's storage must be fetched from +// the kubelet because nodemon did not supply it. +// +// This is the nodemon-rollout bridge. A nodemon pod that predates the +// filesystem fields returns a snapshot with them absent, which is +// indistinguishable from zero on the wire — but a real node always reports a +// nonzero nodefs capacity, so a zero there means "not reported", not "no disk". +// +// It is deliberately narrow. Only a nodemon-sourced metric can be missing +// storage: the kubelet fallback path fills the fields in itself, so a +// fallback-covered node never needs a second call. A nil metric means neither +// source reached the node at all, and a top-up would fail for the same reason. +func nodeStorageNeedsTopUp(fromNodemon bool, m *UnifiedNodeMetric) bool { + return fromNodemon && m != nil && m.NodefsCapacityBytes == 0 +} + +// applyNodeStorageToResourceData writes the node's storage keys into the +// payload dakr ingests. +// +// The filesystem values come from whichever metrics source covered the node. +// The ephemeral-storage values come from the Node object instead, and are +// therefore populated even when no metrics source reached it at all — they are +// what the scheduler admits pods against, which is a different question from +// how full the disk actually is. +func applyNodeStorageToResourceData(data map[string]interface{}, node *corev1.Node, m *UnifiedNodeMetric) { + if m != nil { + data["nodefsUsedBytes"] = int64(m.NodefsUsedBytes) + data["nodefsCapacityBytes"] = int64(m.NodefsCapacityBytes) + data["nodefsAvailableBytes"] = int64(m.NodefsAvailableBytes) + data["nodefsInodesUsed"] = int64(m.NodefsInodesUsed) + data["nodefsInodesTotal"] = int64(m.NodefsInodesTotal) + data["imagefsUsedBytes"] = int64(m.ImagefsUsedBytes) + data["imagefsCapacityBytes"] = int64(m.ImagefsCapacityBytes) + data["imagefsAvailableBytes"] = int64(m.ImagefsAvailableBytes) + data["imagefsSharedDevice"] = m.ImagefsSharedDevice + } else { + data["nodefsUsedBytes"] = int64(0) + data["nodefsCapacityBytes"] = int64(0) + data["nodefsAvailableBytes"] = int64(0) + data["nodefsInodesUsed"] = int64(0) + data["nodefsInodesTotal"] = int64(0) + data["imagefsUsedBytes"] = int64(0) + data["imagefsCapacityBytes"] = int64(0) + data["imagefsAvailableBytes"] = int64(0) + data["imagefsSharedDevice"] = false + } + + ephemeralCapacity := node.Status.Capacity[corev1.ResourceEphemeralStorage] + ephemeralAllocatable := node.Status.Allocatable[corev1.ResourceEphemeralStorage] + data["ephemeralStorageCapacityBytes"] = ephemeralCapacity.Value() + data["ephemeralStorageAllocatableBytes"] = ephemeralAllocatable.Value() +} + // isExcluded checks if a node should be excluded from collection func (c *NodeCollector) isExcluded(nodeName string) bool { c.mu.RLock() diff --git a/internal/collector/node_collector_perf_test.go b/internal/collector/node_collector_perf_test.go index 4c5c87d2..ed45d8cb 100644 --- a/internal/collector/node_collector_perf_test.go +++ b/internal/collector/node_collector_perf_test.go @@ -9,6 +9,7 @@ import ( "io" "net/http" "strings" + "sync" "testing" "time" @@ -16,7 +17,6 @@ import ( "github.com/stretchr/testify/require" corev1 "k8s.io/api/core/v1" "k8s.io/client-go/kubernetes" - k8sfake "k8s.io/client-go/kubernetes/fake" "k8s.io/client-go/rest" metricsv1 "k8s.io/metrics/pkg/client/clientset/versioned" ) @@ -113,6 +113,27 @@ func (rt *nodemonRoundTripper) RoundTrip(req *http.Request) (*http.Response, err // from the path since that's the only identifying information available. type kubeletProxyRoundTripper struct { delayByNode map[string]time.Duration + // nodefsByNode / imagefsByNode, when set for a node, are attached to that + // node's summary response, simulating a kubelet whose Summary API reports + // filesystem occupancy. A node present in delayByNode but absent from + // these maps gets a summary with only cpu/memory — the shape a real + // kubelet's Summary API never actually produces, but the one nodemon's + // composite snapshot produces when it predates the storage fields, + // exercised here for the node-storage top-up path. + nodefsByNode map[string]*kubeletFsStats + imagefsByNode map[string]*kubeletFsStats + + mu sync.Mutex + requestCounts map[string]int +} + +// count returns how many summary requests this round tripper has served for +// nodeName. Used to assert the node-storage top-up is conditional — exactly +// one call when needed, zero when not — rather than an unconditional refetch. +func (rt *kubeletProxyRoundTripper) count(nodeName string) int { + rt.mu.Lock() + defer rt.mu.Unlock() + return rt.requestCounts[nodeName] } func (rt *kubeletProxyRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) { @@ -133,6 +154,13 @@ func (rt *kubeletProxyRoundTripper) RoundTrip(req *http.Request) (*http.Response return nil, err } + rt.mu.Lock() + if rt.requestCounts == nil { + rt.requestCounts = map[string]int{} + } + rt.requestCounts[nodeName]++ + rt.mu.Unlock() + usage := uint64(300_000_000) working := uint64(512 * 1024 * 1024) summary := kubeletSummary{ @@ -142,6 +170,12 @@ func (rt *kubeletProxyRoundTripper) RoundTrip(req *http.Request) (*http.Response Memory: &kubeletMemoryStats{WorkingSetBytes: &working}, }, } + if fs, ok := rt.nodefsByNode[nodeName]; ok { + summary.Node.Fs = fs + } + if imagefs, ok := rt.imagefsByNode[nodeName]; ok { + summary.Node.Runtime = &kubeletRuntimeStats{ImageFs: imagefs} + } return jsonResponse(http.StatusOK, summary), nil } @@ -364,7 +398,7 @@ func TestStop_DoesNotRaceInFlightSweep(t *testing.T) { c := &NodeCollector{ metricsClient: &metricsv1.Clientset{}, nodemonClient: nmClient, - kubeletClient: NewKubeletSummaryClient(k8sfake.NewSimpleClientset(), logr.Discard(), 0), + kubeletClient: newInertFakeKubeletClient(t), nodeInformer: informer, batchChan: make(chan CollectedResource, numNodes), stopCh: make(chan struct{}), diff --git a/internal/collector/node_collector_storage_test.go b/internal/collector/node_collector_storage_test.go new file mode 100644 index 00000000..9a91a01e --- /dev/null +++ b/internal/collector/node_collector_storage_test.go @@ -0,0 +1,544 @@ +package collector + +import ( + "context" + "encoding/json" + "net/http" + "net/http/httptest" + "testing" + "time" + + "github.com/go-logr/logr" + "github.com/stretchr/testify/require" + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/resource" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/client-go/kubernetes" + "k8s.io/client-go/rest" + "k8s.io/client-go/tools/cache" + metricsv1 "k8s.io/metrics/pkg/client/clientset/versioned" +) + +func TestCalculateNodeWorkloadResources_EphemeralStorage(t *testing.T) { + c := &NodeCollector{ + nodeToPodsMap: map[string]map[string]*corev1.Pod{ + "node-a": { + "default/web-0": { + ObjectMeta: metav1.ObjectMeta{Name: "web-0", Namespace: "default"}, + Spec: corev1.PodSpec{ + NodeName: "node-a", + Containers: []corev1.Container{{ + Name: "app", + Resources: corev1.ResourceRequirements{ + Requests: corev1.ResourceList{ + corev1.ResourceEphemeralStorage: resource.MustParse("1Gi"), + }, + Limits: corev1.ResourceList{ + corev1.ResourceEphemeralStorage: resource.MustParse("2Gi"), + }, + }, + }}, + }, + Status: corev1.PodStatus{Phase: corev1.PodRunning}, + }, + }, + }, + } + + got := c.calculateNodeWorkloadResources("node-a") + + if got["ephemeralStorageRequestsBytes"].(int64) != 1073741824 { + t.Errorf("requests = %v, want 1073741824", got["ephemeralStorageRequestsBytes"]) + } + if got["ephemeralStorageLimitsBytes"].(int64) != 2147483648 { + t.Errorf("limits = %v, want 2147483648", got["ephemeralStorageLimitsBytes"]) + } +} + +// A node with no pods must still return the keys, zeroed — callers copy the +// whole map into resourceData and a missing key would drop the column. +func TestCalculateNodeWorkloadResources_EphemeralStorageEmptyNode(t *testing.T) { + c := &NodeCollector{nodeToPodsMap: map[string]map[string]*corev1.Pod{}} + + got := c.calculateNodeWorkloadResources("node-a") + + if got["ephemeralStorageRequestsBytes"].(int64) != 0 { + t.Errorf("requests = %v, want 0", got["ephemeralStorageRequestsBytes"]) + } + if got["ephemeralStorageLimitsBytes"].(int64) != 0 { + t.Errorf("limits = %v, want 0", got["ephemeralStorageLimitsBytes"]) + } +} + +func TestNodeStatusChanged_EphemeralStorage(t *testing.T) { + node := func(capacity, allocatable string) *corev1.Node { + return &corev1.Node{ + Status: corev1.NodeStatus{ + Capacity: corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("4"), + corev1.ResourceMemory: resource.MustParse("16Gi"), + corev1.ResourceEphemeralStorage: resource.MustParse(capacity), + }, + Allocatable: corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("4"), + corev1.ResourceMemory: resource.MustParse("16Gi"), + corev1.ResourceEphemeralStorage: resource.MustParse(allocatable), + }, + }, + } + } + + c := &NodeCollector{} + + if c.nodeStatusChanged(node("100Gi", "90Gi"), node("100Gi", "90Gi")) { + t.Error("identical nodes reported as changed") + } + if !c.nodeStatusChanged(node("100Gi", "90Gi"), node("200Gi", "90Gi")) { + t.Error("ephemeral-storage capacity change not detected") + } + if !c.nodeStatusChanged(node("100Gi", "90Gi"), node("100Gi", "80Gi")) { + t.Error("ephemeral-storage allocatable change not detected") + } +} + +func TestNodeStorageResourceData(t *testing.T) { + node := &corev1.Node{ + ObjectMeta: metav1.ObjectMeta{Name: "node-a"}, + Status: corev1.NodeStatus{ + Capacity: corev1.ResourceList{ + corev1.ResourceEphemeralStorage: resource.MustParse("100Gi"), + }, + Allocatable: corev1.ResourceList{ + corev1.ResourceEphemeralStorage: resource.MustParse("90Gi"), + }, + }, + } + metric := &UnifiedNodeMetric{ + NodeName: "node-a", + NodefsUsedBytes: 12792811520, + NodefsCapacityBytes: 107294470144, + NodefsAvailableBytes: 94501658624, + NodefsInodesUsed: 168472, + NodefsInodesTotal: 52422640, + ImagefsUsedBytes: 6701690880, + ImagefsCapacityBytes: 107294470144, + ImagefsAvailableBytes: 94501658624, + ImagefsSharedDevice: true, + } + + got := map[string]interface{}{} + applyNodeStorageToResourceData(got, node, metric) + + want := map[string]interface{}{ + "nodefsUsedBytes": int64(12792811520), + "nodefsCapacityBytes": int64(107294470144), + "nodefsAvailableBytes": int64(94501658624), + "nodefsInodesUsed": int64(168472), + "nodefsInodesTotal": int64(52422640), + "imagefsUsedBytes": int64(6701690880), + "imagefsCapacityBytes": int64(107294470144), + "imagefsAvailableBytes": int64(94501658624), + "imagefsSharedDevice": true, + "ephemeralStorageCapacityBytes": int64(107374182400), + "ephemeralStorageAllocatableBytes": int64(96636764160), + } + for k, v := range want { + if got[k] != v { + t.Errorf("%s = %v (%T), want %v (%T)", k, got[k], got[k], v, v) + } + } +} + +// A nil metric still yields the ephemeral-storage values, which come from the +// Node object and are available even when no metrics source reached the node. +func TestNodeStorageResourceData_NoMetric(t *testing.T) { + node := &corev1.Node{ + ObjectMeta: metav1.ObjectMeta{Name: "node-a"}, + Status: corev1.NodeStatus{ + Capacity: corev1.ResourceList{corev1.ResourceEphemeralStorage: resource.MustParse("100Gi")}, + Allocatable: corev1.ResourceList{corev1.ResourceEphemeralStorage: resource.MustParse("90Gi")}, + }, + } + + got := map[string]interface{}{} + applyNodeStorageToResourceData(got, node, nil) + + if got["ephemeralStorageCapacityBytes"].(int64) != 107374182400 { + t.Errorf("capacity = %v, want 107374182400", got["ephemeralStorageCapacityBytes"]) + } + if got["nodefsCapacityBytes"].(int64) != 0 { + t.Errorf("nodefs capacity = %v, want 0", got["nodefsCapacityBytes"]) + } +} + +func TestNodeStorageNeedsTopUp(t *testing.T) { + tests := []struct { + name string + fromNodemon bool + metric *UnifiedNodeMetric + want bool + }{ + {"nodemon with storage needs nothing", true, &UnifiedNodeMetric{NodefsCapacityBytes: 1}, false}, + {"nodemon without storage needs top-up", true, &UnifiedNodeMetric{}, true}, + {"kubelet fallback already has storage", false, &UnifiedNodeMetric{}, false}, + {"no metric at all", true, nil, false}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := nodeStorageNeedsTopUp(tt.fromNodemon, tt.metric); got != tt.want { + t.Errorf("got %v, want %v", got, tt.want) + } + }) + } +} + +// u64ptr is a small helper for populating kubeletFsStats literals, whose +// fields are *uint64 so the kubelet Summary API can distinguish "reported +// zero" from "not reported". +func u64ptr(v uint64) *uint64 { return &v } + +// TestNodeStorageTopUp_FiresExactlyOnceAndPopulatesPayload is the wired +// integration test for the top-up: it drives collectAllNodeResources end to +// end, rather than exercising nodeStorageNeedsTopUp and +// applyNodeStorageToResourceData in isolation as the tests above do. A +// regression that reorders the top-up relative to the fromNodemon capture, or +// past the isExcluded return, would still pass those two unit tests while +// silently breaking the feature — this test is what would catch it. +func TestNodeStorageTopUp_FiresExactlyOnceAndPopulatesPayload(t *testing.T) { + node := testNode("storage-node") + informer, stopCh := newSyncedNodeInformer(t, node) + defer close(stopCh) + + // nodemon reports CPU/memory but no filesystem section — the shape an + // un-upgraded nodemon produces, which is what the top-up bridges. + collectedAt := time.Now() + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if r.URL.Path != nodeSnapshotPath { + w.WriteHeader(http.StatusNotFound) + return + } + resp := nodeSnapshotResponse{ + SchemaVersion: snapshotSchemaVersion, + NodeMetrics: &UnifiedNodeMetric{ + NodeName: node.Name, + Timestamp: collectedAt, + CPUUsageNanoCores: 500_000_000, + MemoryWorkingSet: 1024 * 1024 * 1024, + }, + Sections: nodeSnapshotSections{ + Node: snapshotSectionStatus{State: snapshotStateReady, CollectedAt: &collectedAt}, + GPU: snapshotSectionStatus{State: snapshotStateDisabled}, + }, + } + w.Header().Set("Content-Type", "application/json") + _ = json.NewEncoder(w).Encode(resp) + })) + defer server.Close() + + nmClient := newNodemonClientForServer(t, server, node.Name) + + kubeletRT := &kubeletProxyRoundTripper{ + delayByNode: map[string]time.Duration{node.Name: 0}, + nodefsByNode: map[string]*kubeletFsStats{ + node.Name: { + UsedBytes: u64ptr(12792811520), + CapacityBytes: u64ptr(107294470144), + AvailableBytes: u64ptr(94501658624), + Inodes: u64ptr(52422640), + InodesUsed: u64ptr(168472), + }, + }, + imagefsByNode: map[string]*kubeletFsStats{ + node.Name: { + UsedBytes: u64ptr(6701690880), + CapacityBytes: u64ptr(107294470144), + AvailableBytes: u64ptr(94501658624), + Inodes: u64ptr(52422640), + }, + }, + } + kubeletK8sClient, err := kubernetes.NewForConfigAndClient( + &rest.Config{Host: "http://fake-apiserver", QPS: -1}, + &http.Client{Transport: kubeletRT}, + ) + require.NoError(t, err) + + fakeLogger := &fakeTelemetryLogger{} + c := &NodeCollector{ + metricsClient: &metricsv1.Clientset{}, + nodemonClient: nmClient, + kubeletClient: NewKubeletSummaryClient(kubeletK8sClient, logr.Discard(), 0), + nodeInformer: informer, + batchChan: make(chan CollectedResource, 10), + config: NodeCollectorConfig{DisableGPUMetrics: true}, + excludedNodes: map[string]bool{}, + logger: logr.Discard(), + telemetryLogger: fakeLogger, + nodeToPodsMap: make(map[string]map[string]*corev1.Pod), + } + + c.collectAllNodeResources(context.Background()) + + // The call count is the point: it proves the top-up is conditional, not + // an unconditional second fetch (exactly what issue 9410 removed). + if got := kubeletRT.count(node.Name); got != 1 { + t.Fatalf("kubelet storage top-up called %d times for %s, want exactly 1", got, node.Name) + } + + // A recovered node is reported as topped-up, not as missing: it shipped with + // complete storage and merely cost an extra API-server call. + events := fakeLogger.reportsWithEventType("node_storage_topped_up") + if len(events) != 1 { + t.Fatalf("expected one node_storage_topped_up telemetry event, got %d", len(events)) + } + if events[0].fields["storage_topped_up"] != "1" { + t.Errorf("storage_topped_up = %q, want \"1\"", events[0].fields["storage_topped_up"]) + } + if missing := fakeLogger.reportsWithEventType("node_storage_missing"); len(missing) != 0 { + t.Errorf("expected no node_storage_missing telemetry for a recovered node, got %d", len(missing)) + } + + select { + case res := <-c.batchChan: + data := res.Object.(map[string]interface{}) + want := map[string]interface{}{ + "nodefsUsedBytes": int64(12792811520), + "nodefsCapacityBytes": int64(107294470144), + "nodefsAvailableBytes": int64(94501658624), + "nodefsInodesUsed": int64(168472), + "nodefsInodesTotal": int64(52422640), + "imagefsUsedBytes": int64(6701690880), + "imagefsCapacityBytes": int64(107294470144), + "imagefsAvailableBytes": int64(94501658624), + "imagefsSharedDevice": true, + } + for k, v := range want { + if data[k] != v { + t.Errorf("%s = %v (%T), want %v (%T)", k, data[k], data[k], v, v) + } + } + case <-time.After(time.Second): + t.Fatal("expected a node resource to be emitted") + } +} + +// TestNodeStorageTopUp_SkippedWhenNodemonAlreadyHasStorage is the negative +// pairing for the test above: a nodemon-covered node whose snapshot already +// carries a nonzero NodefsCapacityBytes must make zero kubelet storage calls. +// Without this pairing, an unconditional top-up would still pass the positive +// test's assertions (a second write of the same values is invisible to it). +func TestNodeStorageTopUp_SkippedWhenNodemonAlreadyHasStorage(t *testing.T) { + node := testNode("storage-node-2") + informer, stopCh := newSyncedNodeInformer(t, node) + defer close(stopCh) + + collectedAt := time.Now() + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if r.URL.Path != nodeSnapshotPath { + w.WriteHeader(http.StatusNotFound) + return + } + resp := nodeSnapshotResponse{ + SchemaVersion: snapshotSchemaVersion, + NodeMetrics: &UnifiedNodeMetric{ + NodeName: node.Name, + Timestamp: collectedAt, + CPUUsageNanoCores: 500_000_000, + MemoryWorkingSet: 1024 * 1024 * 1024, + NodefsUsedBytes: 12792811520, + NodefsCapacityBytes: 107294470144, + NodefsAvailableBytes: 94501658624, + }, + Sections: nodeSnapshotSections{ + Node: snapshotSectionStatus{State: snapshotStateReady, CollectedAt: &collectedAt}, + GPU: snapshotSectionStatus{State: snapshotStateDisabled}, + }, + } + w.Header().Set("Content-Type", "application/json") + _ = json.NewEncoder(w).Encode(resp) + })) + defer server.Close() + + nmClient := newNodemonClientForServer(t, server, node.Name) + + // Kubelet would succeed if called — delayByNode includes the node — so a + // nonzero count here can only mean the top-up fired when it shouldn't have. + kubeletRT := &kubeletProxyRoundTripper{delayByNode: map[string]time.Duration{node.Name: 0}} + kubeletK8sClient, err := kubernetes.NewForConfigAndClient( + &rest.Config{Host: "http://fake-apiserver", QPS: -1}, + &http.Client{Transport: kubeletRT}, + ) + require.NoError(t, err) + + fakeLogger := &fakeTelemetryLogger{} + c := &NodeCollector{ + metricsClient: &metricsv1.Clientset{}, + nodemonClient: nmClient, + kubeletClient: NewKubeletSummaryClient(kubeletK8sClient, logr.Discard(), 0), + nodeInformer: informer, + batchChan: make(chan CollectedResource, 10), + config: NodeCollectorConfig{DisableGPUMetrics: true}, + excludedNodes: map[string]bool{}, + logger: logr.Discard(), + telemetryLogger: fakeLogger, + nodeToPodsMap: make(map[string]map[string]*corev1.Pod), + } + + c.collectAllNodeResources(context.Background()) + + if got := kubeletRT.count(node.Name); got != 0 { + t.Fatalf("kubelet storage top-up called %d times for %s, want 0 (nodemon already had storage)", got, node.Name) + } + if events := fakeLogger.reportsWithEventType("node_storage_missing"); len(events) != 0 { + t.Errorf("expected no node_storage_missing telemetry, got %d", len(events)) + } +} + +// newStorageTopUpCollector wires a collector whose nodemon reports CPU/memory +// but no filesystem section -- the shape that triggers the kubelet top-up -- +// against a kubelet fake the caller configures. Returns the collector, the +// kubelet round tripper (for call counts) and the telemetry sink. +func newStorageTopUpCollector( + t *testing.T, + node *corev1.Node, + informer cache.SharedIndexInformer, + kubeletRT *kubeletProxyRoundTripper, + excluded map[string]bool, +) (*NodeCollector, *fakeTelemetryLogger) { + t.Helper() + + collectedAt := time.Now() + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if r.URL.Path != nodeSnapshotPath { + w.WriteHeader(http.StatusNotFound) + return + } + resp := nodeSnapshotResponse{ + SchemaVersion: snapshotSchemaVersion, + NodeMetrics: &UnifiedNodeMetric{ + NodeName: node.Name, + Timestamp: collectedAt, + CPUUsageNanoCores: 500_000_000, + MemoryWorkingSet: 1024 * 1024 * 1024, + }, + Sections: nodeSnapshotSections{ + Node: snapshotSectionStatus{State: snapshotStateReady, CollectedAt: &collectedAt}, + GPU: snapshotSectionStatus{State: snapshotStateDisabled}, + }, + } + w.Header().Set("Content-Type", "application/json") + _ = json.NewEncoder(w).Encode(resp) + })) + t.Cleanup(server.Close) + + kubeletK8sClient, err := kubernetes.NewForConfigAndClient( + &rest.Config{Host: "http://fake-apiserver", QPS: -1}, + &http.Client{Transport: kubeletRT}, + ) + require.NoError(t, err) + + fakeLogger := &fakeTelemetryLogger{} + return &NodeCollector{ + metricsClient: &metricsv1.Clientset{}, + nodemonClient: newNodemonClientForServer(t, server, node.Name), + kubeletClient: NewKubeletSummaryClient(kubeletK8sClient, logr.Discard(), 0), + nodeInformer: informer, + batchChan: make(chan CollectedResource, 10), + config: NodeCollectorConfig{DisableGPUMetrics: true}, + excludedNodes: excluded, + logger: logr.Discard(), + telemetryLogger: fakeLogger, + nodeToPodsMap: make(map[string]map[string]*corev1.Pod), + }, fakeLogger +} + +// node_storage_missing has to mean what its name says: a node that shipped +// without filesystem data. A kubelet that answers the top-up but carries no +// filesystem section of its own is that case -- and it does NOT return an +// error, so classifying on the call's error would file it as a success. +func TestNodeStorageTopUp_MissingWhenKubeletHasNoFilesystemEither(t *testing.T) { + node := testNode("storage-node-no-fs") + informer, stopCh := newSyncedNodeInformer(t, node) + defer close(stopCh) + + // Node is present in delayByNode (so the request succeeds) but absent from + // nodefsByNode/imagefsByNode, so the summary carries cpu/memory only. + kubeletRT := &kubeletProxyRoundTripper{delayByNode: map[string]time.Duration{node.Name: 0}} + c, fakeLogger := newStorageTopUpCollector(t, node, informer, kubeletRT, map[string]bool{}) + + c.collectAllNodeResources(context.Background()) + + if got := kubeletRT.count(node.Name); got != 1 { + t.Fatalf("kubelet storage top-up called %d times, want exactly 1", got) + } + events := fakeLogger.reportsWithEventType("node_storage_missing") + if len(events) != 1 { + t.Fatalf("expected one node_storage_missing telemetry event, got %d", len(events)) + } + if events[0].fields["storage_missing"] != "1" { + t.Errorf("storage_missing = %q, want \"1\"", events[0].fields["storage_missing"]) + } + if toppedUp := fakeLogger.reportsWithEventType("node_storage_topped_up"); len(toppedUp) != 0 { + t.Errorf("expected no node_storage_topped_up telemetry, got %d", len(toppedUp)) + } +} + +// A failed top-up counts as missing, since the node ships without storage. +func TestNodeStorageTopUp_MissingWhenKubeletCallFails(t *testing.T) { + node := testNode("storage-node-kubelet-down") + informer, stopCh := newSyncedNodeInformer(t, node) + defer close(stopCh) + + // No entry in delayByNode at all: the round tripper answers 404, so + // FetchNodeStorageByNode returns an error. + kubeletRT := &kubeletProxyRoundTripper{delayByNode: map[string]time.Duration{}} + c, fakeLogger := newStorageTopUpCollector(t, node, informer, kubeletRT, map[string]bool{}) + + c.collectAllNodeResources(context.Background()) + + events := fakeLogger.reportsWithEventType("node_storage_missing") + if len(events) != 1 { + t.Fatalf("expected one node_storage_missing telemetry event, got %d", len(events)) + } + if toppedUp := fakeLogger.reportsWithEventType("node_storage_topped_up"); len(toppedUp) != 0 { + t.Errorf("expected no node_storage_topped_up telemetry, got %d", len(toppedUp)) + } +} + +// An excluded node is never enriched and never sent, so it belongs in neither +// storage count -- and must not spend an API-server call on data that is +// immediately discarded. +func TestNodeStorageTopUp_ExcludedNodeIsNeitherFetchedNorCounted(t *testing.T) { + node := testNode("storage-node-excluded") + informer, stopCh := newSyncedNodeInformer(t, node) + defer close(stopCh) + + // The kubelet would happily serve a full filesystem section if asked. + kubeletRT := &kubeletProxyRoundTripper{ + delayByNode: map[string]time.Duration{node.Name: 0}, + nodefsByNode: map[string]*kubeletFsStats{ + node.Name: { + UsedBytes: u64ptr(12792811520), + CapacityBytes: u64ptr(107294470144), + AvailableBytes: u64ptr(94501658624), + Inodes: u64ptr(52422640), + InodesUsed: u64ptr(168472), + }, + }, + } + c, fakeLogger := newStorageTopUpCollector(t, node, informer, kubeletRT, + map[string]bool{node.Name: true}) + + c.collectAllNodeResources(context.Background()) + + if got := kubeletRT.count(node.Name); got != 0 { + t.Errorf("kubelet storage top-up called %d times for an excluded node, want 0", got) + } + if events := fakeLogger.reportsWithEventType("node_storage_missing"); len(events) != 0 { + t.Errorf("expected no node_storage_missing telemetry for an excluded node, got %d", len(events)) + } + if events := fakeLogger.reportsWithEventType("node_storage_topped_up"); len(events) != 0 { + t.Errorf("expected no node_storage_topped_up telemetry for an excluded node, got %d", len(events)) + } +} diff --git a/internal/collector/node_collector_test.go b/internal/collector/node_collector_test.go index 5ebb2ae2..86bcd4bc 100644 --- a/internal/collector/node_collector_test.go +++ b/internal/collector/node_collector_test.go @@ -52,6 +52,28 @@ func newFakeKubeletClientForNode(t *testing.T, nodeName string, usageNanoCores, return NewKubeletSummaryClient(client, logr.Discard(), 0) } +// newInertFakeKubeletClient returns a KubeletSummaryClient backed by the same +// in-memory API-server proxy round tripper as newFakeKubeletClientForNode, but +// with no node simulated. Every request 404s, so fetchSummary returns a clean +// error instead of a nil-pointer panic. +// +// Tests that build a NodeCollector directly (rather than through +// newNodeCollectorForTest) need a kubeletClient wired the same way: the +// composite fixtures those tests use predate the storage fields, so +// nodeStorageNeedsTopUp now fires a real FetchNodeStorageByNode call for them. +// k8sfake.NewSimpleClientset()'s CoreV1().RESTClient() is nil, which panics +// the moment fetchSummary calls it — this fake exists so that call fails +// gracefully instead, leaving the rest of the test's assertions unaffected. +func newInertFakeKubeletClient(t *testing.T) *KubeletSummaryClient { + t.Helper() + client, err := kubernetes.NewForConfigAndClient( + &rest.Config{Host: "http://fake-apiserver", QPS: -1}, + &http.Client{Transport: &kubeletProxyRoundTripper{delayByNode: map[string]time.Duration{}}}, + ) + require.NoError(t, err) + return NewKubeletSummaryClient(client, logr.Discard(), 0) +} + // pathCountingNodemon is a fake nodemon that records how many requests hit each // path, so tests can assert the collector makes exactly one composite request // per node and never falls back to the legacy endpoints in steady state. @@ -92,11 +114,11 @@ func newNodemonClientForServer(t *testing.T, server *httptest.Server, nodeName s } } -func newNodeCollectorForTest(nmClient *NodemonClient, informer cache.SharedIndexInformer) *NodeCollector { +func newNodeCollectorForTest(t *testing.T, nmClient *NodemonClient, informer cache.SharedIndexInformer) *NodeCollector { return &NodeCollector{ metricsClient: &metricsv1.Clientset{}, nodemonClient: nmClient, - kubeletClient: NewKubeletSummaryClient(k8sfake.NewSimpleClientset(), logr.Discard(), 0), + kubeletClient: newInertFakeKubeletClient(t), nodeInformer: informer, batchChan: make(chan CollectedResource, 10), config: NodeCollectorConfig{DisableGPUMetrics: false}, @@ -149,7 +171,7 @@ func TestCollectAllNodeResources_UsesSingleCompositeRequest(t *testing.T) { defer server.Close() nmClient := newNodemonClientForServer(t, server, node.Name) - c := newNodeCollectorForTest(nmClient, informer) + c := newNodeCollectorForTest(t, nmClient, informer) c.collectAllNodeResources(context.Background()) @@ -200,7 +222,7 @@ func TestCollectAllNodeResources_NodeNotReadyFallsBackToKubelet(t *testing.T) { defer server.Close() nmClient := newNodemonClientForServer(t, server, node.Name) - c := newNodeCollectorForTest(nmClient, informer) + c := newNodeCollectorForTest(t, nmClient, informer) // Composite node section is not_ready, so node CPU/memory must come from the // kubelet Summary API fallback — while the usable GPU section is preserved. c.kubeletClient = newFakeKubeletClientForNode(t, node.Name, 300_000_000, 512*1024*1024) @@ -255,7 +277,7 @@ func TestCollectAllNodeResources_StaleGPUDropped(t *testing.T) { nmClient := newNodemonClientForServer(t, server, node.Name) fakeLogger := &fakeTelemetryLogger{} - c := newNodeCollectorForTest(nmClient, informer) + c := newNodeCollectorForTest(t, nmClient, informer) c.telemetryLogger = fakeLogger c.collectAllNodeResources(context.Background()) @@ -309,7 +331,7 @@ func TestCollectAllNodeResources_LegacyFallbackEmitsTelemetry(t *testing.T) { c := &NodeCollector{ metricsClient: &metricsv1.Clientset{}, nodemonClient: nmClient, - kubeletClient: NewKubeletSummaryClient(k8sfake.NewSimpleClientset(), logr.Discard(), 0), + kubeletClient: newInertFakeKubeletClient(t), nodeInformer: informer, batchChan: make(chan CollectedResource, 10), config: NodeCollectorConfig{DisableGPUMetrics: false}, @@ -478,7 +500,7 @@ func TestCollectAllNodeResources_SurvivesNodeDeletedDuringNodemonFetch(t *testin c := &NodeCollector{ metricsClient: &metricsv1.Clientset{}, nodemonClient: nmClient, - kubeletClient: NewKubeletSummaryClient(k8sfake.NewSimpleClientset(), logr.Discard(), 0), + kubeletClient: newInertFakeKubeletClient(t), nodeInformer: informer, batchChan: make(chan CollectedResource, 10), config: NodeCollectorConfig{DisableGPUMetrics: true}, @@ -508,3 +530,132 @@ func TestCollectAllNodeResources_SurvivesNodeDeletedDuringNodemonFetch(t *testin require.Empty(t, fakeLogger.reportsWithErrorType("node_cache_fail"), "collector should not report a cache miss when the node was captured before the race window") } + +// newBarePodTrackingCollector builds just enough of a NodeCollector to exercise +// handlePodEvent/PodsOnNode, without the nodemon/kubelet/metrics wiring the collection +// tests above need. +func newBarePodTrackingCollector() *NodeCollector { + return &NodeCollector{ + excludedNodes: map[string]bool{}, + logger: logr.Discard(), + nodeToPodsMap: make(map[string]map[string]*corev1.Pod), + } +} + +// boundPodNodeName is the node every fixture in the pod-tracking tests below binds to. Only +// the rescheduling case cares about a second node, and it names that one inline. +const boundPodNodeName = "ip-10-0-1-5" + +// boundPod is a pod the scheduler has already bound to a node, which is the only pod shape +// this collector tracks. +func boundPod() *corev1.Pod { + return &corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{Namespace: "payments", Name: "checkout-9f8b7d"}, + Spec: corev1.PodSpec{NodeName: boundPodNodeName}, + } +} + +// TestHandlePodEvent_TracksPodsOnNode pins the state machine PodsOnNode depends on, which is +// what classifyDisruptionBlocked reads to find the pod blocking a node. +// +// The last case is the regression guard for #9897 on this side of the seam: this collector +// used to keep a SECOND map keyed by pod.Status.NominatedNodeName, maintained on every pod +// event cluster-wide, to serve a PodsNominatedFor lookup. That field is kube-scheduler +// preemption state rather than Karpenter nomination, so the lookup was unsound and both it +// and the bookkeeping are gone. A pending pod is now tracked for no node at all. +func TestHandlePodEvent_TracksPodsOnNode(t *testing.T) { + t.Run("a bound pod is tracked for its node", func(t *testing.T) { + c := newBarePodTrackingCollector() + + c.handlePodEvent(boundPod(), EventTypeAdd) + + got := c.PodsOnNode("ip-10-0-1-5") + require.Len(t, got, 1) + require.Equal(t, "checkout-9f8b7d", got[0].Name) + }) + + t.Run("an excluded node is not tracked", func(t *testing.T) { + c := newBarePodTrackingCollector() + c.excludedNodes["ip-10-0-1-5"] = true + + c.handlePodEvent(boundPod(), EventTypeAdd) + + require.Empty(t, c.PodsOnNode("ip-10-0-1-5")) + }) + + // Reproduces the pod informer's UpdateFunc exactly: it calls handlePodEvent(newPod, ...) + // for the new state, THEN separately diffs oldPod/newPod to clear the stale node bucket + // handlePodEvent cannot know about on its own, since it only ever sees the new object. + t.Run("a rescheduled pod leaves its old node's bucket", func(t *testing.T) { + c := newBarePodTrackingCollector() + oldPod := boundPod() + c.handlePodEvent(oldPod, EventTypeAdd) + + newPod := oldPod.DeepCopy() + newPod.Spec.NodeName = "ip-10-0-1-9" + c.handlePodEvent(newPod, EventTypeUpdate) + if oldPod.Spec.NodeName != newPod.Spec.NodeName { + c.removePodFromNode(oldPod) + } + + require.Empty(t, c.PodsOnNode("ip-10-0-1-5"), "stale bucket under the old node must be cleared") + require.Len(t, c.PodsOnNode("ip-10-0-1-9"), 1) + }) + + t.Run("delete clears the pod", func(t *testing.T) { + c := newBarePodTrackingCollector() + pod := boundPod() + c.handlePodEvent(pod, EventTypeAdd) + + c.handlePodEvent(pod, EventTypeDelete) + + require.Empty(t, c.PodsOnNode("ip-10-0-1-5")) + }) + + t.Run("a pending pod nominated for a node is tracked for no node", func(t *testing.T) { + c := newBarePodTrackingCollector() + pending := &corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{Namespace: "payments", Name: "checkout-7d9f8b"}, + Status: corev1.PodStatus{NominatedNodeName: "ip-10-0-1-5"}, + } + + c.handlePodEvent(pending, EventTypeAdd) + + require.Empty(t, c.PodsOnNode("ip-10-0-1-5"), + "an unbound pod must not appear as ON the node, nominated or otherwise — see #9897") + }) +} + +// BenchmarkHandlePodEvent measures the per-pod-event bookkeeping the pod informer runs for +// EVERY pod in the cluster, on every add and every update — the highest-frequency path in +// this collector by a wide margin. A cluster of a few thousand pods re-delivers each of +// them on every status change, so a constant factor here is paid tens of thousands of times +// a minute, which is why unjustified work in this function is worth benchmarking at all. +// +// The bound case is the overwhelmingly common one (a pod with Spec.NodeName set); the +// pending case is a pod the scheduler has not bound, which reaches only the exclusion check. +func BenchmarkHandlePodEvent(b *testing.B) { + bound := &corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{Namespace: "payments", Name: "checkout-7d9f8b"}, + Spec: corev1.PodSpec{NodeName: "ip-10-0-1-5"}, + } + pending := &corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{Namespace: "payments", Name: "checkout-9f8b7d"}, + } + + for _, tc := range []struct { + name string + pod *corev1.Pod + }{ + {"bound", bound}, + {"pending", pending}, + } { + b.Run(tc.name, func(b *testing.B) { + c := newBarePodTrackingCollector() + b.ReportAllocs() + for i := 0; i < b.N; i++ { + c.handlePodEvent(tc.pod, EventTypeUpdate) + } + }) + } +} diff --git a/internal/collector/nodemon_client.go b/internal/collector/nodemon_client.go index 0ad80639..82578547 100644 --- a/internal/collector/nodemon_client.go +++ b/internal/collector/nodemon_client.go @@ -84,6 +84,17 @@ type UnifiedNodeMetric struct { DiskWriteBytesPerSec float64 `json:"disk_write_bytes_per_sec"` DiskReadOpsPerSec float64 `json:"disk_read_ops_per_sec"` DiskWriteOpsPerSec float64 `json:"disk_write_ops_per_sec"` + // Filesystem occupancy gauges. Mirrors nodemon.NodeMetricsResponse; see + // ImagefsSharedDevice there for why imagefs and nodefs must not be summed. + NodefsUsedBytes uint64 `json:"nodefs_used_bytes,omitempty"` + NodefsCapacityBytes uint64 `json:"nodefs_capacity_bytes,omitempty"` + NodefsAvailableBytes uint64 `json:"nodefs_available_bytes,omitempty"` + NodefsInodesUsed uint64 `json:"nodefs_inodes_used,omitempty"` + NodefsInodesTotal uint64 `json:"nodefs_inodes_total,omitempty"` + ImagefsUsedBytes uint64 `json:"imagefs_used_bytes,omitempty"` + ImagefsCapacityBytes uint64 `json:"imagefs_capacity_bytes,omitempty"` + ImagefsAvailableBytes uint64 `json:"imagefs_available_bytes,omitempty"` + ImagefsSharedDevice bool `json:"imagefs_shared_device,omitempty"` } // UnifiedPVCMetric mirrors nodemon's PVCMetricsResponse. diff --git a/internal/collector/persistentvolume_collector.go b/internal/collector/persistentvolume_collector.go index a2e2ee0b..5ef08ea3 100644 --- a/internal/collector/persistentvolume_collector.go +++ b/internal/collector/persistentvolume_collector.go @@ -163,8 +163,8 @@ func (c *PersistentVolumeCollector) pvChanged(oldPV, newPV *corev1.PersistentVol oldPV.ObjectMeta, newPV.ObjectMeta, ) - if changed != IgnoreChanges { - return changed == PushChanges + if changed == PushChanges { + return true } // Check for phase changes @@ -338,7 +338,7 @@ func (c *PersistentVolumeCollector) GetResourceChannel() <-chan []CollectedResou // GetType returns the type of resource this collector handles func (c *PersistentVolumeCollector) GetType() string { - return "persistentvolume" + return PersistentVolume.String() } // IsAvailable checks if PersistentVolume resources can be accessed in the cluster diff --git a/internal/collector/poddisruptionbudget_collector.go b/internal/collector/poddisruptionbudget_collector.go index 2eb8a66a..c441570b 100644 --- a/internal/collector/poddisruptionbudget_collector.go +++ b/internal/collector/poddisruptionbudget_collector.go @@ -205,8 +205,8 @@ func (c *PodDisruptionBudgetCollector) pdbChanged( oldPDB.ObjectMeta, newPDB.ObjectMeta, ) - if changed != IgnoreChanges { - return changed == PushChanges + if changed == PushChanges { + return true } // Check for spec changes diff --git a/internal/collector/pvc_collector.go b/internal/collector/pvc_collector.go index dadd9b2a..e3efa70d 100644 --- a/internal/collector/pvc_collector.go +++ b/internal/collector/pvc_collector.go @@ -172,8 +172,8 @@ func (c *PersistentVolumeClaimCollector) pvcChanged( oldPVC.ObjectMeta, newPVC.ObjectMeta, ) - if changed != IgnoreChanges { - return changed == PushChanges + if changed == PushChanges { + return true } // Check for status phase changes @@ -301,7 +301,7 @@ func (c *PersistentVolumeClaimCollector) GetResourceChannel() <-chan []Collected // GetType returns the type of resource this collector handles func (c *PersistentVolumeClaimCollector) GetType() string { - return "persistentvolumeclaim" + return PersistentVolumeClaim.String() } // IsAvailable checks if PersistentVolumeClaim resources can be accessed in the cluster diff --git a/internal/collector/replicaset_collector.go b/internal/collector/replicaset_collector.go index 6f977a25..4d00f529 100644 --- a/internal/collector/replicaset_collector.go +++ b/internal/collector/replicaset_collector.go @@ -172,8 +172,8 @@ func (c *ReplicaSetCollector) replicaSetChanged( oldReplicaSet.ObjectMeta, newReplicaSet.ObjectMeta, ) - if changed != IgnoreChanges { - return changed == PushChanges + if changed == PushChanges { + return true } // Check if replicas changed diff --git a/internal/collector/replicationcontroller_collector.go b/internal/collector/replicationcontroller_collector.go index 26995fc2..d7dcf62c 100644 --- a/internal/collector/replicationcontroller_collector.go +++ b/internal/collector/replicationcontroller_collector.go @@ -175,8 +175,8 @@ func (c *ReplicationControllerCollector) replicationControllerChanged( oldRC.ObjectMeta, newRC.ObjectMeta, ) - if changed != IgnoreChanges { - return changed == PushChanges + if changed == PushChanges { + return true } // Check if replicas changed diff --git a/internal/collector/resourcequota_collector.go b/internal/collector/resourcequota_collector.go index b6dd7a6c..401b1f2f 100644 --- a/internal/collector/resourcequota_collector.go +++ b/internal/collector/resourcequota_collector.go @@ -170,8 +170,8 @@ func (c *ResourceQuotaCollector) resourceQuotaChanged(oldRQ, newRQ *corev1.Resou oldRQ.ObjectMeta, newRQ.ObjectMeta, ) - if changed != IgnoreChanges { - return changed == PushChanges + if changed == PushChanges { + return true } // Check for spec resource changes diff --git a/internal/collector/resourcequota_collector_test.go b/internal/collector/resourcequota_collector_test.go new file mode 100644 index 00000000..57712fd8 --- /dev/null +++ b/internal/collector/resourcequota_collector_test.go @@ -0,0 +1,81 @@ +package collector + +import ( + "testing" + + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/resource" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// resourceQuotaFixture builds a ResourceQuota whose only interesting fields are +// the ones the change detector inspects. +func resourceQuotaFixture(resourceVersion string, used string) *corev1.ResourceQuota { + return &corev1.ResourceQuota{ + ObjectMeta: metav1.ObjectMeta{ + Name: "compute-quota", + Namespace: "team-a", + ResourceVersion: resourceVersion, + Labels: map[string]string{"app": "api"}, + }, + Spec: corev1.ResourceQuotaSpec{ + Hard: corev1.ResourceList{ + corev1.ResourceLimitsCPU: resource.MustParse("10"), + }, + }, + Status: corev1.ResourceQuotaStatus{ + Hard: corev1.ResourceList{ + corev1.ResourceLimitsCPU: resource.MustParse("10"), + }, + Used: corev1.ResourceList{ + corev1.ResourceLimitsCPU: resource.MustParse(used), + }, + }, + } +} + +func TestResourceQuotaChanged(t *testing.T) { + c := &ResourceQuotaCollector{} + + tests := []struct { + name string + old *corev1.ResourceQuota + new func() *corev1.ResourceQuota + want bool + }{ + { + // The bug this guards: a status-only update bumps resourceVersion + // but touches neither labels nor annotations, so the ObjectMeta + // check returns UnknownChanges and the status comparison below it + // must still run. + name: "status.used changed with a bumped resourceVersion", + old: resourceQuotaFixture("100", "4"), + new: func() *corev1.ResourceQuota { return resourceQuotaFixture("101", "7") }, + want: true, + }, + { + name: "only resourceVersion changed", + old: resourceQuotaFixture("100", "4"), + new: func() *corev1.ResourceQuota { return resourceQuotaFixture("101", "4") }, + want: false, + }, + { + name: "label changed", + old: resourceQuotaFixture("100", "4"), + new: func() *corev1.ResourceQuota { + rq := resourceQuotaFixture("101", "4") + rq.Labels = map[string]string{"app": "worker"} + return rq + }, + want: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := c.resourceQuotaChanged(tt.old, tt.new()); got != tt.want { + t.Errorf("resourceQuotaChanged() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/internal/collector/role_collector.go b/internal/collector/role_collector.go index e1d1e971..b10fe5b9 100644 --- a/internal/collector/role_collector.go +++ b/internal/collector/role_collector.go @@ -167,8 +167,8 @@ func (c *RoleCollector) roleChanged(oldRole, newRole *rbacv1.Role) bool { oldRole.ObjectMeta, newRole.ObjectMeta, ) - if changed != IgnoreChanges { - return changed == PushChanges + if changed == PushChanges { + return true } // Check for rules changes diff --git a/internal/collector/rolebinding_collector.go b/internal/collector/rolebinding_collector.go index 13e4ea92..c6140703 100644 --- a/internal/collector/rolebinding_collector.go +++ b/internal/collector/rolebinding_collector.go @@ -167,8 +167,8 @@ func (c *RoleBindingCollector) roleBindingChanged(oldRB, newRB *rbacv1.RoleBindi oldRB.ObjectMeta, newRB.ObjectMeta, ) - if changed != IgnoreChanges { - return changed == PushChanges + if changed == PushChanges { + return true } // Check for role reference changes diff --git a/internal/collector/scheduler_metrics_collector.go b/internal/collector/scheduler_metrics_collector.go new file mode 100644 index 00000000..911ea45d --- /dev/null +++ b/internal/collector/scheduler_metrics_collector.go @@ -0,0 +1,892 @@ +// internal/collector/scheduler_metrics_collector.go +package collector + +import ( + "context" + "crypto/tls" + "crypto/x509" + "fmt" + "math" + "net/http" + "os" + "strings" + "sync" + "time" + + gen "github.com/devzero-inc/zxporter/gen/api/v1" + telemetry_logger "github.com/devzero-inc/zxporter/internal/logger" + "github.com/go-logr/logr" + dto "github.com/prometheus/client_model/go" + "github.com/prometheus/common/expfmt" + "github.com/prometheus/common/model" + corev1 "k8s.io/api/core/v1" + apierrors "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/util/intstr" + "k8s.io/client-go/kubernetes" + "k8s.io/client-go/rest" + "k8s.io/client-go/transport" +) + +const ( + // schedulerMetricsNamespace and schedulerMetricsServiceName locate dz-scheduler's + // Service (operators/tunr/helm/dakr/templates/scheduler.yaml), which forwards to + // kube-scheduler's secure port (10259) -- the same port that serves /healthz and + // /metrics together. + schedulerMetricsNamespace = "devzero-system" + schedulerMetricsServiceName = "dz-scheduler" + + // schedulerMetricsLeaseName is the coordination.k8s.io/v1 Lease dz-scheduler's + // leader election maintains in schedulerMetricsNamespace. It is a distinct object + // from the Service above that happens to share its name -- not by leader-election + // convention (kube-scheduler's own default lock is named "kube-scheduler" in + // "kube-system"), but because dz-scheduler's own component config explicitly pins + // it there: operators/tunr/helm/dakr/templates/scheduler.yaml sets + // leaderElection.resourceName: dz-scheduler and .resourceNamespace: devzero-system. + // That is the coupling that must not drift -- if either side is ever renamed + // without the other, this constant and the chart fall out of sync silently (the + // Lease Get in resolveEndpoint would just start returning NotFound, which reads as + // the legitimate "no Lease" fallback case, not as a misconfiguration). See + // resolveEndpoint: the Lease's holderIdentity is how this collector finds the + // replica that is actually scheduling pods (issue #10055). + schedulerMetricsLeaseName = "dz-scheduler" + + schedulerMetricsPath = "/metrics" + + // schedulerMetricsPortName is the named port on the dz-scheduler Service + // (operators/tunr/helm/dakr/templates/scheduler.yaml) that forwards to the secure + // port. resolveEndpoint matches on this name first, falling back to the first port + // only if the name isn't found -- selecting Ports[0] unconditionally would silently + // redirect every scrape if a port were ever added ahead of it. The matched + // ServicePort is then read two different ways depending on which host + // resolveEndpoint is about to scrape: its Port (443) when addressing the Service + // VIP by DNS name (kube-proxy forwards that to the pod's TargetPort itself), or its + // TargetPort (10259) when dialing a specific pod's IP directly, bypassing the + // Service and kube-proxy entirely -- a pod has no listener on 443. + schedulerMetricsPortName = "https-healthz" + + // DefaultSchedulerMetricsScrapeInterval is design decision 5 in the pipeline plan: + // placement metrics feed a trend dashboard, not an alerting path, so 60s is fresh + // enough. + DefaultSchedulerMetricsScrapeInterval = 60 * time.Second + + schedulerMetricsScrapeTimeout = 10 * time.Second + + // schedulerMetricsCAFileEnv optionally names a CA bundle to verify dz-scheduler's + // serving certificate. Unset by default because dz-scheduler's kube-scheduler + // process currently runs with no --tls-cert-file/--tls-private-key-file (see + // scheduler.yaml), so it serves an ephemeral self-signed certificate that nothing + // in the pod's mounted trust bundle -- including the API server's own CA -- can + // validate. Wiring the API server's CA in as a default here would not make the + // connection more trustworthy; it would just make every scrape fail a handshake + // against a certificate that CA never signed. This env var exists so that if + // dz-scheduler is ever given a real certificate from a resolvable CA, trust can be + // turned on without a code change. + // + // Turning it on would need more than a cert, though, now that resolveEndpoint + // dials the leader pod's IP directly rather than the Service's stable DNS name + // (issue #10055): a certificate issued for the Service's DNS name would fail + // hostname verification against a pod IP, and pod IPs are not stable identities to + // issue a certificate for in the first place (they change every reschedule). A real + // fix would need per-pod SAN certificates or a SAN covering the Service DNS name + // verified separately from the dial target -- neither exists today. + schedulerMetricsCAFileEnv = "SCHEDULER_METRICS_CA_FILE" +) + +// The five wire values of dakr's SchedulerMetricType (dakr/models/scheduler_placement_metric.go), +// plus schedulerMetricBucketLEPositiveInf, which is NOT one of them -- it is BucketLE's +// own overflow-bucket encoding (see BucketLE's doc comment on the dakr side), not a +// metric type. Duplicated here as plain strings rather than imported: zxporter is a +// separate Go module and does not depend on dakr's model package. +// +// Both sides are pinned by test, not by import: TestSchedulerMetricsCollector_AllowedFamiliesMatchesDakrIngestAllowList +// (this package) reads these five constants and asserts them against dakr's vocabulary +// set-for-set, and dakr/server/scheduler_placement_metrics_wiring_test.go's +// TestSchedulerPlacementMetricCrossModuleRoundTrip (~:354-374) pins the same vocabulary +// from dakr's side; dakr also validates a decoded metric_type against that vocabulary at +// ingest (schedulerPlacementMetricDropReason). What remains unpinned: each test's copy of +// the five-value set is a frozen literal, not a read of the other side's source, so a +// rename applied consistently to both a production constant here and its mirrored +// literal in one of those tests would still leave both suites green -- the tests catch a +// one-sided drift, not a coordinated one. +const ( + schedulerMetricTypeCounter = "counter" + schedulerMetricTypeGauge = "gauge" + schedulerMetricTypeHistogramBucket = "histogram_bucket" + schedulerMetricTypeHistogramSum = "histogram_sum" + schedulerMetricTypeHistogramCount = "histogram_count" + schedulerMetricBucketLEPositiveInf = "+Inf" +) + +// schedulerMetricsAllowedFamilies is the CLOSED set of Prometheus metric families this +// collector forwards (pipeline plan design decision 2). dz-scheduler exposes hundreds of +// series; storing whatever arrives would make cardinality a property of the agent rather +// than the schema, and would let a buggy or compromised agent write unbounded series. +var schedulerMetricsAllowedFamilies = map[string]bool{ + "dz_scheduler_plugin_score": true, + "scheduler_schedule_attempts_total": true, + "scheduler_framework_extension_point_duration_seconds": true, + "scheduler_plugin_execution_duration_seconds": true, + "scheduler_plugin_evaluation_total": true, + "scheduler_pending_pods": true, +} + +// schedulerMetricsLabelColumns is the closed set of Prometheus label names this collector +// understands, matching dakr's SchedulerPlacementMetric six label columns (Plugin, +// ExtensionPoint, Result, Status, Profile, Queue) field-for-field. A label outside this +// set is not silently dropped -- see convertFamily -- because dropping it would collapse +// two logically distinct observations into one indistinguishable ClickHouse row with no +// error anywhere in the pipeline. +// +// This set is the union of every label the six allow-listed families (decision 2) are +// actually defined with, verified against the two source-of-truth metric definitions +// rather than assumed: +// - devzero-inc/dz-scheduler pkg/metrics/metrics.go: PluginScore is +// []string{"plugin"} only -- deliberately, per that file's own comment: Score runs +// once per candidate node per pod, so a node or pod label would make cardinality +// plugins x nodes x scheduling-rate. +// - kubernetes/kubernetes pkg/scheduler/metrics/metrics.go, at the version +// dz-scheduler VENDORS (its go.mod pins k8s.io/kubernetes v1.34.2), not the version +// of the cluster's own control plane -- dz-scheduler ships its own kube-scheduler +// binary, so these five families come from that vendored library and a customer +// running a newer Kubernetes does not change them: +// schedule_attempts_total {result, profile}; pending_pods {queue}; +// framework_extension_point_duration_seconds {extension_point, status, profile}; +// plugin_execution_duration_seconds {plugin, extension_point, status}; +// plugin_evaluation_total {plugin, extension_point, profile}. +// Re-verified unchanged at v1.36.3 and v1.37.0-rc.0, so the exposure is a bump of +// dz-scheduler's own dependency -- ours to make, in a PR -- not cluster drift. +// +// Union: {plugin, extension_point, result, status, profile, queue} -- exactly this set, +// no more and no fewer. A future family added to schedulerMetricsAllowedFamilies must +// have its label set re-derived from its own source before assuming it fits here, and +// so must a bump of dz-scheduler's vendored k8s.io/kubernetes. +// +// RE-DERIVE ON EITHER OF THOSE TWO EVENTS, because the failure is silent-by-design: a +// label ADDED to one of the five upstream families makes convertFamily reject every +// series in that family, so the metric vanishes from the dashboard with only a throttled +// telemetry report as the signal. Adding a NEW family upstream is harmless by contrast -- +// scrapeOnce filters against schedulerMetricsAllowedFamilies before it ever calls +// convertFamily, so an un-allow-listed family is skipped without its labels being parsed +// and never reaches this set (v1.37's queued_entities {queue, type} is exactly that case: +// it carries an unmodeled `type` label and is inert). convertFamily itself holds no +// allow-list check -- do not read its absence there as the gate having been removed. +// +// Adding or removing dz-scheduler PLUGINS does not touch this set. A plugin name is a +// label VALUE, not a label name: every plugin observes through the one shared definition +// (dzmetrics.PluginScore.WithLabelValues(Name)), and the upstream plugin_* families are +// likewise labelled by plugin name. Plugin churn therefore changes cardinality -- more or +// fewer series under the same six names -- and `plugin` is part of the ClickHouse ORDER BY +// identity, so distinct plugins get distinct rows rather than colliding. The plugin-shaped +// risk is a new plugin registering its own new metric FAMILY, which is then dropped as +// un-allow-listed until added to both this collector's allow-list and dakr's. +var schedulerMetricsLabelColumns = map[string]bool{ + "plugin": true, + "extension_point": true, + "result": true, + "status": true, + "profile": true, + "queue": true, +} + +// HTTPClient abstracts *http.Client for testing, mirroring nodemon.HTTPClient. +type HTTPClient interface { + Do(req *http.Request) (*http.Response, error) +} + +// SchedulerMetricsCollector scrapes dz-scheduler's auth-gated /metrics endpoint on a +// ticker, allow-lists the six contract Prometheus families, flattens them into rows +// matching dakr's SchedulerPlacementMetric, and emits them as +// RESOURCE_TYPE_SCHEDULER_PLACEMENT_METRIC. +// +// Availability (whether dz-scheduler's Service exists) is expressed only through +// IsAvailable, checked by the reconciler before Start is ever called -- a collector whose +// Start returns nil early is never retried (CollectionManager treats nil as success), so +// unlike karpenter_settings_collector.go's single Start-time probe, this collector also +// re-resolves the scrape target on every tick (Gatekeeper-style) so a leader failover, a +// scheduler that is later replaced, or a Service whose port changes is picked up without a +// zxporter restart. See resolveEndpoint: the target is normally the current leader pod +// (per the dz-scheduler Lease), not the Service VIP -- only the leader replica runs +// scheduleOne, so scraping the Service risks kube-proxy/keep-alive stickiness pinning the +// scrape to a standby that reports structurally-valid all-zero placement metrics forever +// (issue #10055). +type SchedulerMetricsCollector struct { + k8sClient kubernetes.Interface + httpClient HTTPClient + scrapeInterval time.Duration + + batchChan chan CollectedResource + resourceChan chan []CollectedResource + batcher *ResourcesBatcher + stopCh chan struct{} + logger logr.Logger + telemetryLogger telemetry_logger.Logger + + mu sync.Mutex + // chanMu guards batchChan against the send-after-close shutdown race: the scrape + // loop RLocks around sends, Stop write-locks before closing. + chanMu sync.RWMutex + stopped bool +} + +// NewSchedulerMetricsCollector constructs the collector. httpClient must already be +// configured with dz-scheduler's authentication and TLS trust -- see +// NewSchedulerMetricsHTTPClient. scrapeInterval <= 0 falls back to +// DefaultSchedulerMetricsScrapeInterval. +func NewSchedulerMetricsCollector( + k8sClient kubernetes.Interface, + httpClient HTTPClient, + scrapeInterval time.Duration, + maxBatchSize int, + maxBatchTime time.Duration, + logger logr.Logger, + telemetryLogger telemetry_logger.Logger, +) *SchedulerMetricsCollector { + if scrapeInterval <= 0 { + scrapeInterval = DefaultSchedulerMetricsScrapeInterval + } + + batchChan := make(chan CollectedResource, 100) + resourceChan := make(chan []CollectedResource, 100) + batcher := NewResourcesBatcher(maxBatchSize, maxBatchTime, batchChan, resourceChan, logger) + + return &SchedulerMetricsCollector{ + k8sClient: k8sClient, + httpClient: httpClient, + scrapeInterval: scrapeInterval, + batchChan: batchChan, + resourceChan: resourceChan, + batcher: batcher, + stopCh: make(chan struct{}), + logger: logger.WithName("scheduler-metrics-collector"), + telemetryLogger: telemetryLogger, + } +} + +// NewSchedulerMetricsHTTPClient builds the authenticated HTTPS client used to scrape +// dz-scheduler's /metrics endpoint. +// +// Auth: zxporter's own ServiceAccount bearer token, via client-go's transport package so +// a projected token that rotates during the process lifetime (the common case on modern +// clusters) is picked up from disk without restarting zxporter, rather than being read +// once and cached forever. +// +// TLS trust: see schedulerMetricsCAFileEnv's doc comment for why this defaults to +// InsecureSkipVerify against dz-scheduler specifically, rather than reusing the API +// server's CA. +// +// What that trade-off actually exposes: this sends inClusterConfig.BearerToken -- +// zxporter's full ServiceAccount token, default audience, not a token narrowed to this +// scrape. The devzero-zxporter-metrics-reader ClusterRole ADDS a permission to that +// identity; it does not mint a differently-scoped credential. The same token already +// authenticates zxporter's full manager-role grant (config/rbac/role.yaml): cluster-wide +// get/list/watch across pods, nodes, namespaces, endpoints, PV(C)s, and more, plus +// `secrets: create` cluster-wide and get/patch/update on the `token` secret. So a MITM +// on this connection captures a token usable for all of that against the real API +// server -- not merely "the same scrape data any legitimate client reads." +// +// Why InsecureSkipVerify is still the right call, and what actually reaches this token +// now (issue #10055 review): before that fix, resolveEndpoint always dialed the Service's +// stable DNS name, so this token could only ever land on one of dz-scheduler's own two +// replicas -- kube-proxy picks the backend, but only from the Service's own Endpoints. +// After that fix, resolveEndpoint dials the current Lease holder's pod IP directly, so +// the real exposure this client now carries is narrower than "intercept the pod network": +// it is write access to the dz-scheduler Lease's spec.holderIdentity field in +// devzero-system. Anything holding that -- confirmed to include the dakr/tunr operator's +// ClusterRole, which grants leases update/patch cluster-wide with no resourceNames +// restriction (operators/tunr/helm/dakr/templates/operator-rbac.yaml) -- can, in +// principle, point holderIdentity at a pod's name and have this client hand it zxporter's +// full ServiceAccount bearer token. schedulerVerifyPodMatchesServiceSelector (see +// resolveEndpoint) narrows this back down: it refuses to scrape a resolved pod unless the +// pod's own labels satisfy the dz-scheduler Service's selector, so exploiting this also +// requires running a correctly-labelled pod in devzero-system, not just Lease write +// access alone. That check is corroboration, not a cryptographic guarantee: a principal +// with BOTH Lease-write and pod-create/label access in devzero-system is not stopped by +// it. The actual fix, if dz-scheduler ever needs to defend against that combination +// specifically, is a projected ServiceAccount token scoped to a non-apiserver audience +// (so a token captured this way fails TokenReview if replayed against the real API +// server) -- not a TLS decision on an already self-signed endpoint. No such audience +// restriction exists today; this comment records the gap rather than papering over it. +func NewSchedulerMetricsHTTPClient() (HTTPClient, error) { + inClusterConfig, err := rest.InClusterConfig() + if err != nil { + return nil, fmt.Errorf("load in-cluster config: %w", err) + } + + tlsConfig := &tls.Config{MinVersion: tls.VersionTLS12} //nolint:gosec // InsecureSkipVerify set conditionally below, not unconditionally. + if caFile := os.Getenv(schedulerMetricsCAFileEnv); caFile != "" { + caData, err := os.ReadFile(caFile) //nolint:gosec // operator-provided path via env var, not user input. + pool := x509.NewCertPool() + if err == nil && pool.AppendCertsFromPEM(caData) { + tlsConfig.RootCAs = pool + } else { + tlsConfig.InsecureSkipVerify = true //nolint:gosec // see schedulerMetricsCAFileEnv doc comment. + } + } else { + tlsConfig.InsecureSkipVerify = true //nolint:gosec // see schedulerMetricsCAFileEnv doc comment. + } + + authTransport, err := transport.NewBearerAuthWithRefreshRoundTripper( + inClusterConfig.BearerToken, + inClusterConfig.BearerTokenFile, + &http.Transport{TLSClientConfig: tlsConfig}, + ) + if err != nil { + return nil, fmt.Errorf("build bearer auth round tripper: %w", err) + } + + return &http.Client{Transport: authTransport, Timeout: schedulerMetricsScrapeTimeout}, nil +} + +// NewSchedulerMetricsHTTPClientOrFailing returns a working client when the in-cluster +// bearer token is resolvable, otherwise a stub whose every request fails with the +// original error. This exists so registration call sites (registerResourceCollectors and +// the pending-collector 5-min retry path) never have to handle a failed HTTP client build +// by skipping registration: the pending-retry path in particular calls IsAvailable +// directly on whatever a factory returns, with no nil check, so a factory that could +// return nil would panic there. Returning a collector that is registered but whose scrapes +// fail (logged, ticker keeps running -- see Start's doc comment) is consistent with how +// every other scrape failure in this collector is already handled, and self-heals if +// zxporter is later restarted with working in-cluster credentials. +func NewSchedulerMetricsHTTPClientOrFailing(logger logr.Logger) HTTPClient { + httpClient, err := NewSchedulerMetricsHTTPClient() + if err != nil { + logger.Error(err, "Failed to build authenticated scheduler metrics HTTP client; scrapes will fail until this is resolved") + return failingHTTPClient{err: err} + } + return httpClient +} + +// failingHTTPClient is a HTTPClient whose every request fails with a fixed error. +type failingHTTPClient struct{ err error } + +func (f failingHTTPClient) Do(*http.Request) (*http.Response, error) { + return nil, f.err +} + +// IsAvailable reports whether dz-scheduler's Service exists in devzero-system. This is +// what makes install order work: the reconciler retries an unavailable collector with a +// factory every 5 minutes (see registerResourceCollectors), so zxporter can be installed +// before dz-scheduler and pick the collector up once the scheduler is deployed. +func (c *SchedulerMetricsCollector) IsAvailable(ctx context.Context) bool { + _, err := c.k8sClient.CoreV1().Services(schedulerMetricsNamespace).Get(ctx, schedulerMetricsServiceName, metav1.GetOptions{}) + return err == nil +} + +// resolveEndpoint re-fetches dz-scheduler's Service and Lease and builds the /metrics URL +// to scrape. Called on every tick rather than cached once, so a leader failover, a Service +// that is deleted/recreated, or a port change is reflected without a zxporter restart. +// +// Target selection (issue #10055 -- see the package doc comment above for why the Service +// VIP is unsafe to scrape unconditionally): +// - Lease absent: falls back to the Service VIP URL (schemeless DNS name, Service Port). +// Under dz-scheduler's own chart this is NOT the single-replica case -- leaderElect is +// hardcoded true regardless of replica count (scheduler.yaml), so a Lease exists even +// at replicas: 1. The branch is reachable in practice for a hand-rolled, non-chart +// dz-scheduler deployment with leader election disabled outright, and briefly on a +// freshly created chart install before the first election completes. Logged at V(1) by +// the caller either way. This is the ONLY case that uses the Service VIP. +// - Lease present, holder resolves to a pod with an IP that also matches the Service's +// own selector (schedulerVerifyPodMatchesServiceSelector): scrapes that pod's IP +// directly, on the Service's TargetPort (the pod has no listener on the Service's own +// Port; see schedulerMetricsPortName's doc comment). +// - Lease present but the holder cannot be resolved to a usable, Service-backing pod +// (holder empty, pod missing, pod has no IP yet, pod doesn't match the Service +// selector, or the target port can't be resolved): returns an error so the scrape is +// skipped for this tick. This deliberately does NOT fall back to the Service VIP -- +// doing so would silently reproduce the exact bug this function exists to fix, by +// scraping whichever replica kube-proxy happens to pick. +func (c *SchedulerMetricsCollector) resolveEndpoint(ctx context.Context) (string, error) { + svc, err := c.k8sClient.CoreV1().Services(schedulerMetricsNamespace).Get(ctx, schedulerMetricsServiceName, metav1.GetOptions{}) + if err != nil { + return "", fmt.Errorf("resolve dz-scheduler service %s/%s: %w", schedulerMetricsNamespace, schedulerMetricsServiceName, err) + } + if len(svc.Spec.Ports) == 0 { + return "", fmt.Errorf("dz-scheduler service %s/%s has no ports", schedulerMetricsNamespace, schedulerMetricsServiceName) + } + + svcPort := svc.Spec.Ports[0] + for _, p := range svc.Spec.Ports { + if p.Name == schedulerMetricsPortName { + svcPort = p + break + } + } + + lease, err := c.k8sClient.CoordinationV1().Leases(schedulerMetricsNamespace).Get(ctx, schedulerMetricsLeaseName, metav1.GetOptions{}) + if apierrors.IsNotFound(err) { + return fmt.Sprintf("https://%s.%s.svc:%d%s", svc.Name, svc.Namespace, svcPort.Port, schedulerMetricsPath), nil + } + if err != nil { + // Deliberately not a fallback to the Service VIP -- see doc comment above. + return "", fmt.Errorf("resolve dz-scheduler lease %s/%s: %w", schedulerMetricsNamespace, schedulerMetricsLeaseName, err) + } + + // HolderIdentity is a *string (unset means no leader has been elected yet); + // schedulerLeaseHolderPodName treats "" as its own error case, so a nil pointer and + // an explicit empty string are handled identically here. + holderIdentity := "" + if lease.Spec.HolderIdentity != nil { + holderIdentity = *lease.Spec.HolderIdentity + } + + podName, err := schedulerLeaseHolderPodName(holderIdentity) + if err != nil { + return "", fmt.Errorf("resolve leader pod from dz-scheduler lease %s/%s holder %q: %w", + schedulerMetricsNamespace, schedulerMetricsLeaseName, holderIdentity, err) + } + + pod, err := c.k8sClient.CoreV1().Pods(schedulerMetricsNamespace).Get(ctx, podName, metav1.GetOptions{}) + if err != nil { + return "", fmt.Errorf("resolve dz-scheduler leader pod %s/%s (lease holder %q): %w", + schedulerMetricsNamespace, podName, holderIdentity, err) + } + + // Verify the resolved pod is actually a backend of the dz-scheduler Service before + // trusting its IP enough to send it zxporter's full ServiceAccount bearer token (see + // NewSchedulerMetricsHTTPClient's doc comment for what that token exposes). Without + // this, the Lease's holderIdentity alone decides where the token goes -- and that + // field isn't only ever written by dz-scheduler's own two replicas (issue #10055 + // review). Reads the Service's own selector rather than hardcoding a label like + // `component: scheduler`, so this holds regardless of which labels a particular + // chart version uses, and needs no new RBAC. + if err := schedulerVerifyPodMatchesServiceSelector(svc, pod); err != nil { + return "", fmt.Errorf("dz-scheduler leader pod %s/%s (lease holder %q) is not a Service backend: %w", + schedulerMetricsNamespace, podName, holderIdentity, err) + } + + if pod.Status.PodIP == "" { + return "", fmt.Errorf("dz-scheduler leader pod %s/%s (lease holder %q) has no pod IP yet", + schedulerMetricsNamespace, podName, holderIdentity) + } + + targetPort, err := schedulerLeaderTargetPort(pod, svcPort.TargetPort) + if err != nil { + return "", fmt.Errorf("resolve scrape port on dz-scheduler leader pod %s/%s (lease holder %q): %w", + schedulerMetricsNamespace, podName, holderIdentity, err) + } + + return fmt.Sprintf("https://%s:%d%s", pod.Status.PodIP, targetPort, schedulerMetricsPath), nil +} + +// schedulerLeaseHolderPodName extracts the pod name from a Lease's holderIdentity. +// client-go's leaderelection package writes holderIdentity as +// "_" (LeaseSpec.HolderIdentity's own doc comment: this string +// only needs to be unique per election, but the resourcelock implementations all build it +// this way, and dz-scheduler's pod IS its own hostname). Everything before the LAST +// underscore is the pod name. First-vs-last is actually equivalent for any real pod: a +// Kubernetes pod name is an RFC 1123 DNS subdomain, which cannot contain "_" at all, so +// the hostname component here never contains one. Splitting on the LAST underscore is +// still the defensive choice, because it is the split that stays correct even for a +// holder that ISN'T a real pod name -- a hand-rolled resourcelock, or a value nobody +// should trust as well-formed in the first place, where the "pod names can't have +// underscores" assumption doesn't apply. A holder with no underscore at all is used +// as-is (a hand-rolled or future resourcelock implementation might not append a UUID). An +// empty holder, or a holder whose derived pod name comes out empty (e.g. a holder that IS +// just "_" or starts with "_"), is an error -- there is no leader pod name to resolve, and +// silently falling through to Pods().Get(ctx, "") would ask the API server a different, +// confusing question instead of failing here with a clear reason. +// +// If Kubernetes' CoordinatedLeaderElection feature is ever enabled for dz-scheduler, +// holderIdentity stops being client-go's "_" and instead becomes +// whatever the CoordinatedLeaderElection controller copies from the winning +// LeaseCandidate's name -- a different format this parsing rule has not been verified +// against. dz-scheduler's own ClusterRole already grants leasecandidates permissions +// (scheduler.yaml), so the binary supports the feature even though nothing enables it +// today; re-verify this function if that ever changes. +func schedulerLeaseHolderPodName(holderIdentity string) (string, error) { + if holderIdentity == "" { + return "", fmt.Errorf("lease has no holderIdentity (no leader elected yet)") + } + podName := holderIdentity + if idx := strings.LastIndex(holderIdentity, "_"); idx >= 0 { + podName = holderIdentity[:idx] + } + if podName == "" { + return "", fmt.Errorf("lease holderIdentity %q has no pod name before the last underscore", holderIdentity) + } + return podName, nil +} + +// schedulerVerifyPodMatchesServiceSelector confirms pod is a backend of svc by checking +// that every key/value pair in svc.Spec.Selector is present in pod.Labels, presence and +// value both -- the same has-the-key-with-this-value membership test the Kubernetes +// endpoints controller's own label selector (k8s.io/apimachinery/pkg/labels) uses. A +// pod's label being ABSENT and a pod's label being present with an empty value are +// distinct outcomes here on purpose: collapsing them (comparing pod.Labels[key] against +// wantValue without checking ok) would let a pod that was never labelled at all pass +// against any selector whose value happens to be the empty string, which Kubernetes +// permits. This is the mitigation for the exposure documented on +// NewSchedulerMetricsHTTPClient: without it, resolveEndpoint +// would trust the Lease's holderIdentity to name ANY pod in schedulerMetricsNamespace and +// send it zxporter's full ServiceAccount bearer token, rather than only a pod dz-scheduler's +// own Service would actually route traffic to. Deliberately reads the Service's selector +// instead of a hardcoded label set (`app: dz-scheduler` would look plausible but does not +// exist -- the real selector is `component: scheduler, tier: control-plane`), so this +// stays correct across chart versions with no code change. +// +// An empty or nil Service selector matches nothing here on purpose: a Service with no +// selector has no defined backends of its own (it is typically paired with manually +// managed Endpoints/EndpointSlices instead), so there is no basis to call any pod "the" +// backend by this test, and treating an empty selector as "matches everything" would +// defeat the point of the check entirely. +func schedulerVerifyPodMatchesServiceSelector(svc *corev1.Service, pod *corev1.Pod) error { + if len(svc.Spec.Selector) == 0 { + return fmt.Errorf("service %s/%s has no selector, so no pod can be verified as its backend", svc.Namespace, svc.Name) + } + for key, wantValue := range svc.Spec.Selector { + // Presence-checked, not just compared: a plain `pod.Labels[key] != wantValue` + // reads a missing key as Go's zero value "", so a selector whose value is + // itself the empty string (legal in Kubernetes) would let a pod that lacks the + // label ENTIRELY pass as a match. That would collapse "this pod carries the + // label with an empty value" and "this pod was never labelled at all" into the + // same accepted case -- exactly the absent-vs-empty distinction the real + // endpoints controller's Matches (k8s.io/apimachinery/pkg/labels) does not + // collapse either. + gotValue, ok := pod.Labels[key] + if !ok || gotValue != wantValue { + return fmt.Errorf("pod %s/%s label %q=%q (present=%t) does not match service %s/%s selector %q=%q", + pod.Namespace, pod.Name, key, gotValue, ok, svc.Namespace, svc.Name, key, wantValue) + } + } + return nil +} + +// schedulerLeaderTargetPort resolves the Service's targetPort to a concrete container port +// on the leader pod. An int targetPort (dz-scheduler's Service uses one -- see +// schedulerMetricsPortName's doc comment) is used as-is. A named targetPort is resolved +// against the pod's own container ports, exactly as the kubelet/kube-proxy would; if the +// name isn't found there, that is an error rather than a guess -- unlike the Service-VIP +// fallback path, there is no safe default to fall back to when scraping a pod directly. +// +// dz-scheduler's actual container (scheduler.yaml) declares no `ports:` at all today -- +// its livenessProbe references port 10259 by number, not by name -- so the named-port +// branch below is currently unreachable in production and only exercised by test. If a +// future chart revision ever switches the Service's targetPort to a name without also +// adding a matching named containerPort, every scrape would fail with this function's +// error on every tick, not silently degrade. +func schedulerLeaderTargetPort(pod *corev1.Pod, targetPort intstr.IntOrString) (int32, error) { + if targetPort.Type == intstr.Int { + return targetPort.IntVal, nil + } + + name := targetPort.StrVal + for _, container := range pod.Spec.Containers { + for _, p := range container.Ports { + if p.Name == name { + return p.ContainerPort, nil + } + } + } + return 0, fmt.Errorf("named target port %q not found on any container of pod %s/%s", name, pod.Namespace, pod.Name) +} + +// Start begins the scrape ticker. A scrape failure (endpoint resolution, HTTP, or parse) +// logs and the loop continues to the next tick -- never fatal, since a transient scheduler +// restart (routine under the #9848 settings design, which restarts dz-scheduler on every +// settings change) should not take this collector down. +func (c *SchedulerMetricsCollector) Start(ctx context.Context) error { + c.logger.Info("Starting SchedulerMetrics collector", "interval", c.scrapeInterval) + + c.batcher.start() + + stopCh := c.stopCh + go func() { + ticker := time.NewTicker(c.scrapeInterval) + defer ticker.Stop() + // Scrape once immediately rather than waiting a full interval for the first + // data point. + c.scrapeOnce(ctx) + for { + select { + case <-ticker.C: + c.scrapeOnce(ctx) + case <-ctx.Done(): + c.Stop() //nolint:errcheck + return + case <-stopCh: + return + } + } + }() + + return nil +} + +// scrapeOnce resolves the current endpoint, scrapes it, and emits every allow-listed row. +// Every failure path logs and returns rather than propagating -- see Start's doc comment. +func (c *SchedulerMetricsCollector) scrapeOnce(ctx context.Context) { + scrapeCtx, cancel := context.WithTimeout(ctx, schedulerMetricsScrapeTimeout) + defer cancel() + + endpoint, err := c.resolveEndpoint(scrapeCtx) + if err != nil { + // V(1), not V(4): V(4) is routinely suppressed at the configured log level, and + // this is the failure mode for "dz-scheduler's Service was deleted after this + // collector started" -- silent at V(4), that reads as a healthy collector that + // has actually gone permanently dark. + c.logger.V(1).Info("Skipping scheduler metrics scrape, endpoint not resolvable", "error", err.Error()) + return + } + + families, err := c.scrape(scrapeCtx, endpoint) + if err != nil { + c.logger.Error(err, "Scheduler metrics scrape failed") + return + } + + scrapedAt := time.Now().UTC() + for _, mf := range families { + if !schedulerMetricsAllowedFamilies[mf.GetName()] { + continue + } + c.convertFamily(mf, scrapedAt) + } +} + +// scrape performs one authenticated HTTP GET against endpoint and parses the response as +// Prometheus text-format metrics. +func (c *SchedulerMetricsCollector) scrape(ctx context.Context, endpoint string) (map[string]*dto.MetricFamily, error) { + req, err := http.NewRequestWithContext(ctx, http.MethodGet, endpoint, nil) + if err != nil { + return nil, fmt.Errorf("build scrape request: %w", err) + } + + resp, err := c.httpClient.Do(req) + if err != nil { + return nil, fmt.Errorf("scrape %s: %w", endpoint, err) + } + defer func() { _ = resp.Body.Close() }() + + if resp.StatusCode != http.StatusOK { + return nil, fmt.Errorf("scrape %s: unexpected status %d", endpoint, resp.StatusCode) + } + + parser := expfmt.NewTextParser(model.UTF8Validation) + families, err := parser.TextToMetricFamilies(resp.Body) + if err != nil { + return nil, fmt.Errorf("parse metrics from %s: %w", endpoint, err) + } + return families, nil +} + +// convertFamily flattens one allow-listed Prometheus metric family into per-series wire +// items and emits each on the batch channel. A series carrying a label outside the six +// modeled columns is rejected and logged with the offending label name (see +// schedulerMetricsLabelColumns' doc comment); rejecting one series does not affect its +// siblings in the same family or scrape. +func (c *SchedulerMetricsCollector) convertFamily(mf *dto.MetricFamily, scrapedAt time.Time) { + name := mf.GetName() + for _, m := range mf.GetMetric() { + labels, err := schedulerMetricLabels(name, m.GetLabel()) + if err != nil { + c.logger.Error(err, "Rejecting scheduler metric series with an unmodeled label", "metric", name) + // A contract violation, not a transient scrape hiccup: a series carrying a + // label this collector doesn't model means either dz-scheduler started + // exposing a new dimension or the allow-list/label-column set has drifted + // out of sync with it. Silent-in-pod-logs is not enough for that -- a + // cluster where every series in a family is rejected looks identical to a + // healthy one from outside the pod, so it goes through the same telemetry + // path the reconciler already uses for collector-health signals. + c.telemetryLogger.Report(gen.LogLevel_LOG_LEVEL_ERROR, "SchedulerMetricsCollector_convertFamily", + "Rejecting scheduler metric series with an unmodeled label", err, + map[string]string{"metric_family": name}) + continue + } + + switch mf.GetType() { + case dto.MetricType_COUNTER: + c.emit(schedulerMetricItem(scrapedAt, name, schedulerMetricTypeCounter, labels, nil, m.GetCounter().GetValue())) + case dto.MetricType_GAUGE: + c.emit(schedulerMetricItem(scrapedAt, name, schedulerMetricTypeGauge, labels, nil, m.GetGauge().GetValue())) + case dto.MetricType_HISTOGRAM: + c.emitHistogram(m.GetHistogram(), scrapedAt, name, labels) + default: + c.logger.Error(fmt.Errorf("unsupported metric type %s", mf.GetType()), "Rejecting scheduler metric family with an unsupported type", "metric", name) + return + } + } +} + +// emitHistogram flattens one histogram series into its bucket rows (one per `le`, +// including the +Inf overflow bucket) plus its sum and count rows -- never +// pre-aggregated, per pipeline plan design decision 3: quantiles are not averageable, so +// collapsing buckets at the agent would make later cross-cluster percentiles impossible. +func (c *SchedulerMetricsCollector) emitHistogram(hist *dto.Histogram, scrapedAt time.Time, name string, labels map[string]string) { + // Bucket.CumulativeCountFloat/Histogram.SampleCountFloat exist on dto for OpenMetrics + // native (exponential) histograms, whose float counters override the integer ones + // when set. expfmt's text-format parser (the only parser this collector uses -- see + // scrape) never populates them; kube-scheduler and dz-scheduler both expose classic + // bucketed histograms over the text format, not native ones. Reading them here would + // be dead code. + for _, bucket := range hist.GetBucket() { + le := bucket.GetUpperBound() + c.emit(schedulerMetricItem(scrapedAt, name, schedulerMetricTypeHistogramBucket, labels, &le, float64(bucket.GetCumulativeCount()))) + } + + c.emit(schedulerMetricItem(scrapedAt, name, schedulerMetricTypeHistogramSum, labels, nil, hist.GetSampleSum())) + c.emit(schedulerMetricItem(scrapedAt, name, schedulerMetricTypeHistogramCount, labels, nil, float64(hist.GetSampleCount()))) +} + +// emit sends one flattened row to the batch channel. +// +// The channel send is inside a select with c.stopCh rather than a bare blocking send: +// batchChan has a fixed 100-item buffer and a single scrape of all six families can +// exceed that, so if the batcher/downstream consumer ever stalls, a bare `c.batchChan <- +// resource` would block forever while still holding chanMu's read lock -- and Stop() +// would then deadlock waiting for chanMu's write lock to close batchChan, wedging this +// collector's goroutine permanently (the ticker never gets to observe ctx.Done(), and +// the pod eventually dies by SIGKILL rather than a clean shutdown). The select's +// <-c.stopCh case is always eventually ready, because Stop() closes stopCh before it +// ever tries to acquire chanMu -- so holding the read lock across this particular select +// cannot reproduce that deadlock: the moment Stop() runs, any in-flight emit +// unconditionally unblocks (abandoning at most the one item it was sending) and releases +// the lock, letting Stop() proceed to close batchChan. +func (c *SchedulerMetricsCollector) emit(item map[string]interface{}) { + // A NaN or +/-Inf value fails json.Marshal ("json: unsupported value") deep inside + // SendResourceBatch's structpb conversion (dakr_client.go's toStructpb), where the + // failure is caught and the one item is silently `continue`d past -- no error + // anywhere in the pipeline, the same failure mode bucket_le's string encoding exists + // to avoid. Rejecting here, loudly, keeps that contract: a bad row never reaches the + // wire path where it would vanish without a trace. + if value, ok := item["value"].(float64); !ok || math.IsNaN(value) || math.IsInf(value, 0) { + c.logger.Error(fmt.Errorf("non-finite value %v", item["value"]), "Rejecting scheduler metric row with a non-finite value", + "metric", item["metric_name"], "metric_type", item["metric_type"]) + return + } + + c.chanMu.RLock() + defer c.chanMu.RUnlock() + if c.stopped { + return + } + scrapedAt, _ := item["scraped_at"].(time.Time) + resource := CollectedResource{ + ResourceType: SchedulerPlacementMetric, + Object: item, + Timestamp: scrapedAt, + EventType: EventTypeMetrics, + Key: schedulerMetricKey(scrapedAt, item), + } + select { + case c.batchChan <- resource: + case <-c.stopCh: + } +} + +// schedulerMetricLabels extracts the six modeled label columns from a Prometheus series' +// label pairs. Returns an error naming the offending label if any label falls outside the +// closed set in schedulerMetricsLabelColumns -- silently dropping it would let two +// logically distinct series collapse into one indistinguishable ClickHouse row with no +// error anywhere in the pipeline. +func schedulerMetricLabels(metricFamily string, pairs []*dto.LabelPair) (map[string]string, error) { + labels := make(map[string]string, len(pairs)) + for _, pair := range pairs { + name := pair.GetName() + if !schedulerMetricsLabelColumns[name] { + return nil, fmt.Errorf("scheduler metric %q carries unmodeled label %q", metricFamily, name) + } + labels[name] = pair.GetValue() + } + return labels, nil +} + +// schedulerMetricItem builds one wire row matching dakr's SchedulerPlacementMetric +// field-for-field (dakr/models/scheduler_placement_metric.go). bucketLE is nil for every +// MetricType except histogram_bucket. The overflow bucket (bucketLE == +Inf) is encoded +// as the JSON string "+Inf" rather than a JSON number: Go's encoding/json cannot marshal +// math.Inf as a number (json: unsupported value: +Inf), and this is exactly the wire form +// dakr's model.UnmarshalJSON expects. +// +// Deliberately omits cluster_id and ingested_at: cluster_id is stamped by dakr from +// request metadata, never trusted from the payload, and ingested_at is stamped at insert +// -- see dakr/models/scheduler_placement_metric.go's doc comments. +func schedulerMetricItem( + scrapedAt time.Time, + metricName string, + metricType string, + labels map[string]string, + bucketLE *float64, + value float64, +) map[string]interface{} { + item := map[string]interface{}{ + "scraped_at": scrapedAt, + "metric_name": metricName, + "metric_type": metricType, + "plugin": labels["plugin"], + "extension_point": labels["extension_point"], + "result": labels["result"], + "status": labels["status"], + "profile": labels["profile"], + "queue": labels["queue"], + "value": value, + } + + switch { + case bucketLE == nil: + item["bucket_le"] = 0.0 + case math.IsInf(*bucketLE, 1): + item["bucket_le"] = schedulerMetricBucketLEPositiveInf + default: + item["bucket_le"] = *bucketLE + } + + return item +} + +// schedulerMetricKey builds a per-row identity for the CollectedResource envelope. +// bucket_le is included so the multiple histogram_bucket rows a single series produces +// (one per `le`) get distinct keys despite sharing every other field. +func schedulerMetricKey(scrapedAt time.Time, item map[string]interface{}) string { + return fmt.Sprintf("scheduler-metric/%v/%v/%v/%v/%v/%v/%v/%v/%v/%d", + item["metric_name"], item["metric_type"], + item["plugin"], item["extension_point"], item["result"], item["status"], item["profile"], item["queue"], + item["bucket_le"], + scrapedAt.UnixNano(), + ) +} + +// Stop shuts down the collector. +func (c *SchedulerMetricsCollector) Stop() error { + c.logger.Info("Stopping SchedulerMetrics collector") + c.mu.Lock() + defer c.mu.Unlock() + + select { + case <-c.stopCh: + default: + close(c.stopCh) + } + + c.chanMu.Lock() + if !c.stopped { + c.stopped = true + close(c.batchChan) + } + c.chanMu.Unlock() + + if c.batcher != nil { + c.batcher.stop() + } + return nil +} + +// GetResourceChannel returns the output channel for batched resources. +func (c *SchedulerMetricsCollector) GetResourceChannel() <-chan []CollectedResource { + return c.resourceChan +} + +// GetType returns the string key used to identify this collector. +func (c *SchedulerMetricsCollector) GetType() string { + return SchedulerPlacementMetric.String() +} + +// AddResource implements ResourceCollector for manual injection (used in tests). +func (c *SchedulerMetricsCollector) AddResource(resource interface{}) error { + item, ok := resource.(map[string]interface{}) + if !ok { + return fmt.Errorf("expected map[string]interface{}, got %T", resource) + } + c.emit(item) + return nil +} diff --git a/internal/collector/scheduler_metrics_collector_test.go b/internal/collector/scheduler_metrics_collector_test.go new file mode 100644 index 00000000..141e882b --- /dev/null +++ b/internal/collector/scheduler_metrics_collector_test.go @@ -0,0 +1,942 @@ +// internal/collector/scheduler_metrics_collector_test.go +package collector + +import ( + "context" + "fmt" + "io" + "net/http" + "strings" + "sync" + "testing" + "time" + + "github.com/go-logr/logr" + dto "github.com/prometheus/client_model/go" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + coordinationv1 "k8s.io/api/coordination/v1" + corev1 "k8s.io/api/core/v1" + apierrors "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/apimachinery/pkg/util/intstr" + k8sfake "k8s.io/client-go/kubernetes/fake" + ktesting "k8s.io/client-go/testing" +) + +// schedulerMetricsDoFunc lets a test build an HTTPClient from a plain function, +// mirroring nodemon/scraper_test.go's mock transport (that file's roundTripFunc +// implements http.RoundTripper instead, so this collector -- whose HTTPClient +// interface is satisfied by Do, not RoundTrip -- needs its own). +type schedulerMetricsDoFunc func(*http.Request) (*http.Response, error) + +func (f schedulerMetricsDoFunc) Do(req *http.Request) (*http.Response, error) { + return f(req) +} + +// testSchedulerMetricsBatchTime is short enough that ResourcesBatcher (started inside +// Start()) flushes well within a test's drain window, instead of waiting out the 5s +// production default. Tests must read from GetResourceChannel(), never batchChan +// directly -- batchChan is the batcher's own input and is being concurrently drained by +// its goroutine once Start() runs; reading it from a test races that goroutine for the +// same items non-deterministically. +const testSchedulerMetricsBatchTime = 20 * time.Millisecond + +// newTestSchedulerMetricsCollector uses a long scrape interval so Start's one immediate +// scrape is deterministically the only scrape within a test's drain window; +// TestSchedulerMetricsCollector_ScrapeFailureDoesNotKillTicker overrides this via +// newTestSchedulerMetricsCollectorWithInterval to exercise repeated ticks. +func newTestSchedulerMetricsCollector(k8sClient *k8sfake.Clientset, httpClient HTTPClient) *SchedulerMetricsCollector { + return newTestSchedulerMetricsCollectorWithInterval(k8sClient, httpClient, time.Hour) +} + +func newTestSchedulerMetricsCollectorWithInterval(k8sClient *k8sfake.Clientset, httpClient HTTPClient, interval time.Duration) *SchedulerMetricsCollector { + c, _ := newTestSchedulerMetricsCollectorWithTelemetry(k8sClient, httpClient, interval) + return c +} + +// newTestSchedulerMetricsCollectorWithTelemetry also returns the fakeTelemetryLogger +// (defined in node_collector_test.go, reused here since it's package-scoped) so tests can +// assert on what convertFamily reported, not just what reached the resource channel. +func newTestSchedulerMetricsCollectorWithTelemetry(k8sClient *k8sfake.Clientset, httpClient HTTPClient, interval time.Duration) (*SchedulerMetricsCollector, *fakeTelemetryLogger) { + fakeLogger := &fakeTelemetryLogger{} + c := NewSchedulerMetricsCollector( + k8sClient, + httpClient, + interval, + DefaultMaxBatchSize, + testSchedulerMetricsBatchTime, + logr.Discard(), + fakeLogger, + ) + return c, fakeLogger +} + +// dzSchedulerSelector mirrors the real Service's selector +// (operators/tunr/helm/dakr/templates/scheduler.yaml: component=scheduler, +// tier=control-plane -- deliberately NOT `app: dz-scheduler`, which looks plausible but +// does not exist in the real chart). Returns a fresh map every call (rather than a shared +// package var) so no test can accidentally mutate another test's fixture through it; +// dzSchedulerService and dzSchedulerPod both call this, which is what keeps them in sync: +// schedulerVerifyPodMatchesServiceSelector checks exactly this relationship. +func dzSchedulerSelector() map[string]string { + return map[string]string{"component": "scheduler", "tier": "control-plane"} +} + +// dzSchedulerService mirrors the real Service's Port/TargetPort split +// (operators/tunr/helm/dakr/templates/scheduler.yaml: port 443, targetPort 10259) -- +// distinct on purpose, since resolveEndpoint now reads whichever of the two fits the host +// it's about to scrape (see schedulerMetricsPortName's doc comment). A fixture that +// collapsed them to the same value could hide a Port/TargetPort mixup. +func dzSchedulerService() *corev1.Service { + return &corev1.Service{ + ObjectMeta: metav1.ObjectMeta{ + Name: schedulerMetricsServiceName, + Namespace: schedulerMetricsNamespace, + }, + Spec: corev1.ServiceSpec{ + Selector: dzSchedulerSelector(), + Ports: []corev1.ServicePort{ + {Name: "https-healthz", Port: 443, TargetPort: intstr.FromInt32(10259)}, + }, + }, + } +} + +// dzSchedulerLease builds the dz-scheduler leader-election Lease with the given +// holderIdentity. A nil holderIdentity is represented by passing "" -- schedulerLeaseHolderPodName +// treats a nil *string and an explicit "" identically (see resolveEndpoint), so most tests +// only need the string form; dzSchedulerLeaseNilHolder below covers the *string itself +// being nil, which this helper can never produce. +func dzSchedulerLease(holderIdentity string) *coordinationv1.Lease { + return &coordinationv1.Lease{ + ObjectMeta: metav1.ObjectMeta{ + Name: schedulerMetricsLeaseName, + Namespace: schedulerMetricsNamespace, + }, + Spec: coordinationv1.LeaseSpec{ + HolderIdentity: strPtr(holderIdentity), + }, + } +} + +// dzSchedulerLeaseNilHolder builds the Lease with a nil HolderIdentity *string -- +// unreachable via dzSchedulerLease(""), which always allocates a non-nil pointer to an +// empty string. Both must resolve identically (see resolveEndpoint's nil-guard), but only +// this fixture actually exercises the nil-pointer branch of that guard. +func dzSchedulerLeaseNilHolder() *coordinationv1.Lease { + return &coordinationv1.Lease{ + ObjectMeta: metav1.ObjectMeta{ + Name: schedulerMetricsLeaseName, + Namespace: schedulerMetricsNamespace, + }, + Spec: coordinationv1.LeaseSpec{ + HolderIdentity: nil, + }, + } +} + +// dzSchedulerPod builds a dz-scheduler replica pod with the given name and IP, labelled to +// match dzSchedulerSelector -- i.e. a genuine Service backend, the case +// schedulerVerifyPodMatchesServiceSelector is meant to accept. Tests that specifically +// exercise the selector-mismatch rejection build a Pod without these labels directly +// rather than through this fixture. +func dzSchedulerPod(name, podIP string) *corev1.Pod { + return &corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: schedulerMetricsNamespace, + Labels: dzSchedulerSelector(), + }, + Status: corev1.PodStatus{ + PodIP: podIP, + }, + } +} + +func TestSchedulerMetricsCollector_IsAvailable(t *testing.T) { + t.Run("false when the dz-scheduler Service does not exist", func(t *testing.T) { + c := newTestSchedulerMetricsCollector(k8sfake.NewSimpleClientset(), nil) + assert.False(t, c.IsAvailable(context.Background())) + }) + + t.Run("true when the dz-scheduler Service exists", func(t *testing.T) { + c := newTestSchedulerMetricsCollector(k8sfake.NewSimpleClientset(dzSchedulerService()), nil) + assert.True(t, c.IsAvailable(context.Background())) + }) +} + +// counterAndHistogramPayload is a scrape response mixing two allow-listed families +// (a counter and a histogram, the latter with an explicit +Inf bucket) with one +// non-allow-listed family that must never reach the resource channel. +const counterAndHistogramPayload = `# HELP scheduler_schedule_attempts_total Number of attempts to schedule pods +# TYPE scheduler_schedule_attempts_total counter +scheduler_schedule_attempts_total{profile="default-scheduler",result="scheduled"} 42 +# HELP scheduler_framework_extension_point_duration_seconds Latency for scheduler framework extension points. +# TYPE scheduler_framework_extension_point_duration_seconds histogram +scheduler_framework_extension_point_duration_seconds_bucket{extension_point="Filter",profile="default-scheduler",status="Success",le="0.001"} 5 +scheduler_framework_extension_point_duration_seconds_bucket{extension_point="Filter",profile="default-scheduler",status="Success",le="0.01"} 9 +scheduler_framework_extension_point_duration_seconds_bucket{extension_point="Filter",profile="default-scheduler",status="Success",le="+Inf"} 10 +scheduler_framework_extension_point_duration_seconds_sum{extension_point="Filter",profile="default-scheduler",status="Success"} 0.042 +scheduler_framework_extension_point_duration_seconds_count{extension_point="Filter",profile="default-scheduler",status="Success"} 10 +# HELP scheduler_cache_size Number of nodes/pods/assumed pods in the scheduler cache. +# TYPE scheduler_cache_size gauge +scheduler_cache_size{type="nodes"} 3 +` + +// unmodeledLabelPayload carries an allow-listed family whose series has a label +// ("node", not one of the six modeled columns) this collector cannot represent. +const unmodeledLabelPayload = `# HELP dz_scheduler_plugin_score Score assigned to a node by a scoring plugin. +# TYPE dz_scheduler_plugin_score gauge +dz_scheduler_plugin_score{plugin="NodeResourcesFit",profile="default-scheduler",node="ip-10-0-0-1"} 87 +` + +// staticScrapeClient always returns body with a 200 status. Tests exercising a non-200 +// response (TestSchedulerMetricsCollector_ScrapeFailureDoesNotKillTicker) build their own +// schedulerMetricsDoFunc since they need per-call behavior, not a fixed response. +func staticScrapeClient(body string) HTTPClient { + return schedulerMetricsDoFunc(func(req *http.Request) (*http.Response, error) { + return &http.Response{ + StatusCode: http.StatusOK, + Body: io.NopCloser(strings.NewReader(body)), + Header: make(http.Header), + }, nil + }) +} + +// drainSchedulerMetricsResources reads batches off the collector's post-batcher resource channel until +// timeout, flattening them into one slice. This is the channel the reconciler actually +// consumes in production (see CollectionManager), so it is also the correct one for +// tests to observe rather than the pre-batcher batchChan. +func drainSchedulerMetricsResources(t *testing.T, ch <-chan []CollectedResource, timeout time.Duration) []CollectedResource { + t.Helper() + var resources []CollectedResource + deadline := time.After(timeout) + for { + select { + case batch, ok := <-ch: + if !ok { + return resources + } + resources = append(resources, batch...) + case <-deadline: + return resources + } + } +} + +func TestSchedulerMetricsCollector_ScrapeAllowListsFamilies(t *testing.T) { + httpClient := staticScrapeClient(counterAndHistogramPayload) + c := newTestSchedulerMetricsCollector(k8sfake.NewSimpleClientset(dzSchedulerService()), httpClient) + + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + require.NoError(t, c.Start(ctx)) + defer func() { _ = c.Stop() }() + + resources := drainSchedulerMetricsResources(t, c.GetResourceChannel(), 500*time.Millisecond) + require.NotEmpty(t, resources) + + names := map[string]int{} + for _, r := range resources { + require.Equal(t, SchedulerPlacementMetric, r.ResourceType) + item, ok := r.Object.(map[string]interface{}) + require.True(t, ok) + name, _ := item["metric_name"].(string) + names[name]++ + // scheduler_cache_size is not in the six-family allow-list and must never + // appear on the channel. + assert.NotEqual(t, "scheduler_cache_size", name) + } + + assert.Equal(t, 1, names["scheduler_schedule_attempts_total"]) + // 3 buckets + sum + count = 5 rows for the one histogram series. + assert.Equal(t, 5, names["scheduler_framework_extension_point_duration_seconds"]) +} + +func TestSchedulerMetricsCollector_HistogramBucketsFlattenIncludingPlusInf(t *testing.T) { + httpClient := staticScrapeClient(counterAndHistogramPayload) + c := newTestSchedulerMetricsCollector(k8sfake.NewSimpleClientset(dzSchedulerService()), httpClient) + + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + require.NoError(t, c.Start(ctx)) + defer func() { _ = c.Stop() }() + + resources := drainSchedulerMetricsResources(t, c.GetResourceChannel(), 500*time.Millisecond) + require.NotEmpty(t, resources) + + var bucketLEs []interface{} + var sawSum, sawCount bool + for _, r := range resources { + item := r.Object.(map[string]interface{}) + if item["metric_name"] != "scheduler_framework_extension_point_duration_seconds" { + continue + } + switch item["metric_type"] { + case "histogram_bucket": + bucketLEs = append(bucketLEs, item["bucket_le"]) + case "histogram_sum": + sawSum = true + assert.InDelta(t, 0.042, item["value"], 0.0001) + case "histogram_count": + sawCount = true + assert.Equal(t, float64(10), item["value"]) + } + } + + require.True(t, sawSum, "expected a histogram_sum row") + require.True(t, sawCount, "expected a histogram_count row") + require.Len(t, bucketLEs, 3) + + // The overflow bucket MUST serialize as the JSON string "+Inf" -- Go cannot + // marshal math.Inf as a JSON number, and dakr's model only accepts the string + // form for that bucket. + assert.Contains(t, bucketLEs, "+Inf") + assert.Contains(t, bucketLEs, 0.001) + assert.Contains(t, bucketLEs, 0.01) +} + +func TestSchedulerMetricsCollector_UnmodeledLabelIsRejectedNotDropped(t *testing.T) { + httpClient := staticScrapeClient(unmodeledLabelPayload) + c := newTestSchedulerMetricsCollector(k8sfake.NewSimpleClientset(dzSchedulerService()), httpClient) + + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + require.NoError(t, c.Start(ctx)) + defer func() { _ = c.Stop() }() + + // The series carries a label ("node") outside the six modeled columns, so the + // collector must reject it outright rather than silently dropping the label -- + // dropping it would collapse two distinct observations into one indistinguishable + // row with no error anywhere in the pipeline. Nothing should reach the channel. + resources := drainSchedulerMetricsResources(t, c.GetResourceChannel(), 200*time.Millisecond) + assert.Empty(t, resources) +} + +func TestSchedulerMetricsCollector_ScrapeFailureDoesNotKillTicker(t *testing.T) { + var calls int + var mu sync.Mutex + httpClient := schedulerMetricsDoFunc(func(req *http.Request) (*http.Response, error) { + mu.Lock() + defer mu.Unlock() + calls++ + if calls == 1 { + return &http.Response{ + StatusCode: http.StatusInternalServerError, + Body: io.NopCloser(strings.NewReader("")), + Header: make(http.Header), + }, nil + } + return &http.Response{ + StatusCode: http.StatusOK, + Body: io.NopCloser(strings.NewReader(counterAndHistogramPayload)), + Header: make(http.Header), + }, nil + }) + + c := newTestSchedulerMetricsCollectorWithInterval(k8sfake.NewSimpleClientset(dzSchedulerService()), httpClient, 50*time.Millisecond) + + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + require.NoError(t, c.Start(ctx)) + defer func() { _ = c.Stop() }() + + resources := drainSchedulerMetricsResources(t, c.GetResourceChannel(), time.Second) + require.NotEmpty(t, resources, "a later successful scrape should still emit after an earlier failure") + + mu.Lock() + defer mu.Unlock() + assert.GreaterOrEqual(t, calls, 2, "the ticker should have retried after the first failed scrape") +} + +func TestSchedulerMetricsCollector_StopIsClean(t *testing.T) { + httpClient := staticScrapeClient(counterAndHistogramPayload) + c := newTestSchedulerMetricsCollector(k8sfake.NewSimpleClientset(dzSchedulerService()), httpClient) + + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + require.NoError(t, c.Start(ctx)) + + assert.NoError(t, c.Stop()) + // A second Stop must not panic (double-close protection). + assert.NoError(t, c.Stop()) +} + +func TestSchedulerMetricsCollector_GetType(t *testing.T) { + c := newTestSchedulerMetricsCollector(k8sfake.NewSimpleClientset(), nil) + assert.Equal(t, "scheduler_placement_metric", c.GetType()) +} + +// TestSchedulerMetricsCollector_CounterRowAlwaysCarriesAllSixLabelKeys guards the wire +// invariant dakr's model requires: every one of the six label columns is present as a key +// on every row, "" when the series didn't carry that Prometheus label -- never omitted. +// An "only set labels that are present" cleanup would still pass every other test in this +// file while silently producing rows dakr's decoder can't distinguish from a genuinely +// different series (an omitted JSON key and an explicit "" both decode to Go's zero value, +// so nothing downstream would even error). +func TestSchedulerMetricsCollector_CounterRowAlwaysCarriesAllSixLabelKeys(t *testing.T) { + httpClient := staticScrapeClient(counterAndHistogramPayload) + c := newTestSchedulerMetricsCollector(k8sfake.NewSimpleClientset(dzSchedulerService()), httpClient) + + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + require.NoError(t, c.Start(ctx)) + defer func() { _ = c.Stop() }() + + resources := drainSchedulerMetricsResources(t, c.GetResourceChannel(), 500*time.Millisecond) + require.NotEmpty(t, resources) + + var found bool + for _, r := range resources { + item := r.Object.(map[string]interface{}) + if item["metric_name"] != "scheduler_schedule_attempts_total" { + continue + } + found = true + for _, key := range []string{"plugin", "extension_point", "result", "status", "profile", "queue"} { + require.Containsf(t, item, key, "row is missing label key %q entirely, not just empty", key) + } + // scheduler_schedule_attempts_total{profile="default-scheduler",result="scheduled"} + // -- profile and result are the labels this series actually carries. + assert.Equal(t, "default-scheduler", item["profile"]) + assert.Equal(t, "scheduled", item["result"]) + // The four the series does NOT carry must be present and empty, not absent. + assert.Equal(t, "", item["plugin"]) + assert.Equal(t, "", item["extension_point"]) + assert.Equal(t, "", item["status"]) + assert.Equal(t, "", item["queue"]) + } + require.True(t, found, "expected a scheduler_schedule_attempts_total row") +} + +// TestSchedulerMetricsCollector_ScrapeURLReflectsServiceAndReResolvesEachTick closes two +// gaps at once: nothing previously asserted the scrape URL at all (a bug could point it at +// the wrong path or host and every other test would still pass), and nothing verified +// resolveEndpoint is actually called fresh every tick rather than once at Start and cached +// -- which would silently break the "rescheduled/reconfigured scheduler picked up without +// a zxporter restart" requirement this collector exists to satisfy. +func TestSchedulerMetricsCollector_ScrapeURLReflectsServiceAndReResolvesEachTick(t *testing.T) { + urls := make(chan string, 16) + httpClient := schedulerMetricsDoFunc(func(req *http.Request) (*http.Response, error) { + urls <- req.URL.String() + return &http.Response{ + StatusCode: http.StatusOK, + Body: io.NopCloser(strings.NewReader(counterAndHistogramPayload)), + Header: make(http.Header), + }, nil + }) + + k8sClient := k8sfake.NewSimpleClientset(dzSchedulerService()) + c := newTestSchedulerMetricsCollectorWithInterval(k8sClient, httpClient, 30*time.Millisecond) + + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + require.NoError(t, c.Start(ctx)) + defer func() { _ = c.Stop() }() + + var firstURL string + select { + case firstURL = <-urls: + case <-time.After(time.Second): + t.Fatal("timed out waiting for the first scrape request") + } + assert.Equal(t, "https://dz-scheduler.devzero-system.svc:443/metrics", firstURL) + + // Change the Service's port. If resolveEndpoint were hoisted out of the per-tick path + // (cached once at Start), every subsequent request would keep using the stale URL and + // this would time out. + updated := dzSchedulerService() + updated.Spec.Ports[0].Port = 8443 + _, err := k8sClient.CoreV1().Services(schedulerMetricsNamespace).Update(context.Background(), updated, metav1.UpdateOptions{}) + require.NoError(t, err) + + deadline := time.After(2 * time.Second) + for { + select { + case u := <-urls: + if u == "https://dz-scheduler.devzero-system.svc:8443/metrics" { + return + } + case <-deadline: + t.Fatal("scrape URL never reflected the updated Service port -- endpoint is being cached instead of re-resolved every tick") + } + } +} + +// TestSchedulerMetricsCollector_LeasePresentScrapesLeaderPodDirectly is the core +// regression test for issue #10055: when the dz-scheduler Lease exists, the scrape must +// target the leader pod's own IP and the Service's TargetPort (10259) -- never the Service +// DNS name or its Port (443), which is what let kube-proxy/keep-alive stickiness pin a +// scrape to a standby replica forever. +func TestSchedulerMetricsCollector_LeasePresentScrapesLeaderPodDirectly(t *testing.T) { + urls := make(chan string, 16) + httpClient := schedulerMetricsDoFunc(func(req *http.Request) (*http.Response, error) { + urls <- req.URL.String() + return &http.Response{ + StatusCode: http.StatusOK, + Body: io.NopCloser(strings.NewReader(counterAndHistogramPayload)), + Header: make(http.Header), + }, nil + }) + + leaderPod := dzSchedulerPod("dz-scheduler-68b79bbff5-w4jhd", "10.244.0.6") + lease := dzSchedulerLease("dz-scheduler-68b79bbff5-w4jhd_8f7453e4-c02d-4fe5-96b2-eb88ad29983a") + k8sClient := k8sfake.NewSimpleClientset(dzSchedulerService(), leaderPod, lease) + c := newTestSchedulerMetricsCollector(k8sClient, httpClient) + + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + require.NoError(t, c.Start(ctx)) + defer func() { _ = c.Stop() }() + + select { + case url := <-urls: + assert.Equal(t, "https://10.244.0.6:10259/metrics", url, + "must scrape the leader pod's IP on the Service's TargetPort, not the Service DNS name/Port") + case <-time.After(time.Second): + t.Fatal("timed out waiting for the scrape request") + } +} + +// TestSchedulerMetricsCollector_LeaderFailoverRetargetsNextScrape is the failover property +// resolveEndpoint's doc comment promises: since the endpoint is re-resolved every tick, a +// leader change is picked up on the very next scrape with no zxporter restart. +func TestSchedulerMetricsCollector_LeaderFailoverRetargetsNextScrape(t *testing.T) { + urls := make(chan string, 16) + httpClient := schedulerMetricsDoFunc(func(req *http.Request) (*http.Response, error) { + urls <- req.URL.String() + return &http.Response{ + StatusCode: http.StatusOK, + Body: io.NopCloser(strings.NewReader(counterAndHistogramPayload)), + Header: make(http.Header), + }, nil + }) + + pod1 := dzSchedulerPod("dz-scheduler-68b79bbff5-w4jhd", "10.244.0.6") + pod2 := dzSchedulerPod("dz-scheduler-68b79bbff5-bf78z", "10.244.2.5") + lease := dzSchedulerLease("dz-scheduler-68b79bbff5-w4jhd_8f7453e4-c02d-4fe5-96b2-eb88ad29983a") + k8sClient := k8sfake.NewSimpleClientset(dzSchedulerService(), pod1, pod2, lease) + c := newTestSchedulerMetricsCollectorWithInterval(k8sClient, httpClient, 30*time.Millisecond) + + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + require.NoError(t, c.Start(ctx)) + defer func() { _ = c.Stop() }() + + select { + case url := <-urls: + assert.Equal(t, "https://10.244.0.6:10259/metrics", url, "first scrape should target the original leader") + case <-time.After(time.Second): + t.Fatal("timed out waiting for the first scrape request") + } + + // Leader election re-elects: the Lease's holderIdentity flips to the other replica. + updated := lease.DeepCopy() + updated.Spec.HolderIdentity = strPtr("dz-scheduler-68b79bbff5-bf78z_9a1b2c3d-0000-1111-2222-333344445555") + _, err := k8sClient.CoordinationV1().Leases(schedulerMetricsNamespace).Update(context.Background(), updated, metav1.UpdateOptions{}) + require.NoError(t, err) + + deadline := time.After(2 * time.Second) + for { + select { + case url := <-urls: + if url == "https://10.244.2.5:10259/metrics" { + return + } + // Stale scrapes still in flight against the old leader are expected; only + // fail if the new leader's URL never shows up before the deadline. + case <-deadline: + t.Fatal("scrape never retargeted to the new leader after failover") + } + } +} + +// TestSchedulerMetricsCollector_ResolveEndpoint_LeaseAbsentFallsBackToServiceVIP is the +// dedicated, direct-call counterpart to +// TestSchedulerMetricsCollector_ScrapeURLReflectsServiceAndReResolvesEachTick (which also +// exercises this path incidentally, since it creates no Lease): a genuinely absent Lease +// falls back to the Service VIP, on the Service's own Port (443), not the TargetPort used +// for direct pod scrapes. (This is NOT the single-replica case under the real chart -- +// leaderElect is hardcoded true regardless of replica count -- see resolveEndpoint's doc +// comment for which cases actually reach this branch in practice.) +func TestSchedulerMetricsCollector_ResolveEndpoint_LeaseAbsentFallsBackToServiceVIP(t *testing.T) { + k8sClient := k8sfake.NewSimpleClientset(dzSchedulerService()) + c := newTestSchedulerMetricsCollector(k8sClient, nil) + + endpoint, err := c.resolveEndpoint(context.Background()) + require.NoError(t, err) + assert.Equal(t, "https://dz-scheduler.devzero-system.svc:443/metrics", endpoint) +} + +// TestSchedulerMetricsCollector_ResolveEndpoint_LeaseForbiddenErrorsNotFallsBack is the +// branch the whole leader-scrape design rests on and was previously untested: a Lease Get +// that fails for a reason OTHER than NotFound (RBAC denial being the realistic one -- see +// NewSchedulerMetricsHTTPClient's doc comment on which principals can and can't read this +// Lease) must be treated as "endpoint unresolvable, skip this tick," never as "Lease +// absent, fall back to the Service VIP." Collapsing Forbidden into the NotFound branch +// would silently reintroduce the exact bug this collector exists to fix, on every cluster +// where the RBAC grant is missing or misconfigured, which is precisely the failure mode +// the code-review round that added this test caught. +func TestSchedulerMetricsCollector_ResolveEndpoint_LeaseForbiddenErrorsNotFallsBack(t *testing.T) { + k8sClient := k8sfake.NewSimpleClientset(dzSchedulerService()) + k8sClient.PrependReactor("get", "leases", func(action ktesting.Action) (bool, runtime.Object, error) { + return true, nil, apierrors.NewForbidden( + schema.GroupResource{Group: "coordination.k8s.io", Resource: "leases"}, + schedulerMetricsLeaseName, + fmt.Errorf("simulated RBAC denial"), + ) + }) + c := newTestSchedulerMetricsCollector(k8sClient, nil) + + endpoint, err := c.resolveEndpoint(context.Background()) + require.Error(t, err) + assert.Empty(t, endpoint) + assert.NotEqual(t, "https://dz-scheduler.devzero-system.svc:443/metrics", endpoint, + "a Forbidden Lease Get must never resolve to the Service VIP URL") +} + +// TestSchedulerMetricsCollector_ResolveEndpoint_LeaseHolderUnresolvable covers the "Lease +// exists but the holder can't be resolved to a usable pod IP" branch, which must error +// rather than silently fall back to the Service VIP -- falling back there would reproduce +// exactly the bug this collector exists to fix (see resolveEndpoint's doc comment). +func TestSchedulerMetricsCollector_ResolveEndpoint_LeaseHolderUnresolvable(t *testing.T) { + t.Run("holder pod does not exist", func(t *testing.T) { + lease := dzSchedulerLease("dz-scheduler-68b79bbff5-w4jhd_8f7453e4-c02d-4fe5-96b2-eb88ad29983a") + k8sClient := k8sfake.NewSimpleClientset(dzSchedulerService(), lease) + c := newTestSchedulerMetricsCollector(k8sClient, nil) + + _, err := c.resolveEndpoint(context.Background()) + require.Error(t, err) + assert.Contains(t, err.Error(), "dz-scheduler-68b79bbff5-w4jhd", "error must name the unresolvable holder") + }) + + t.Run("holder pod has no IP yet", func(t *testing.T) { + lease := dzSchedulerLease("dz-scheduler-68b79bbff5-w4jhd_8f7453e4-c02d-4fe5-96b2-eb88ad29983a") + pod := dzSchedulerPod("dz-scheduler-68b79bbff5-w4jhd", "") + k8sClient := k8sfake.NewSimpleClientset(dzSchedulerService(), lease, pod) + c := newTestSchedulerMetricsCollector(k8sClient, nil) + + _, err := c.resolveEndpoint(context.Background()) + require.Error(t, err) + assert.Contains(t, err.Error(), "dz-scheduler-68b79bbff5-w4jhd", "error must name the unresolvable holder") + // dzSchedulerPod carries labels matching the Service selector, so the selector + // check (which now runs before the PodIP check) passes and this pins the + // empty-IP branch specifically. Without this assertion the substring above + // would pass identically if the selector check rejected the pod instead -- + // every sibling subtest in this table asserts on message text; this was the + // one relying on fixture shape alone to land in the right branch. + assert.Contains(t, err.Error(), "has no pod IP yet") + }) + + t.Run("holder identity is empty", func(t *testing.T) { + lease := dzSchedulerLease("") + k8sClient := k8sfake.NewSimpleClientset(dzSchedulerService(), lease) + c := newTestSchedulerMetricsCollector(k8sClient, nil) + + _, err := c.resolveEndpoint(context.Background()) + require.Error(t, err) + }) + + t.Run("holder identity is nil, not just empty", func(t *testing.T) { + // dzSchedulerLease("") always allocates a non-nil *string; only this fixture + // exercises the actual nil-pointer branch of resolveEndpoint's guard (a real + // Lease that has genuinely never had a leader elected has a nil + // HolderIdentity, not a pointer to ""). + lease := dzSchedulerLeaseNilHolder() + k8sClient := k8sfake.NewSimpleClientset(dzSchedulerService(), lease) + c := newTestSchedulerMetricsCollector(k8sClient, nil) + + _, err := c.resolveEndpoint(context.Background()) + require.Error(t, err, "a nil HolderIdentity must be treated as no leader, not dereferenced") + assert.Contains(t, err.Error(), "no leader elected yet") + }) + + t.Run("holder pod exists but doesn't match the Service's own selector", func(t *testing.T) { + // An "imposter" pod: the Lease's holderIdentity resolves it by NAME, and it has + // an IP, but its labels don't satisfy the dz-scheduler Service's selector -- the + // case schedulerVerifyPodMatchesServiceSelector exists to catch (issue #10055 + // review: without this check, anything able to write the Lease's holderIdentity + // could point a scrape, and zxporter's bearer token, at an arbitrary pod). + imposter := &corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Name: "dz-scheduler-68b79bbff5-w4jhd", + Namespace: schedulerMetricsNamespace, + Labels: map[string]string{"component": "not-the-scheduler"}, + }, + Status: corev1.PodStatus{PodIP: "10.244.9.9"}, + } + lease := dzSchedulerLease("dz-scheduler-68b79bbff5-w4jhd_8f7453e4-c02d-4fe5-96b2-eb88ad29983a") + k8sClient := k8sfake.NewSimpleClientset(dzSchedulerService(), imposter, lease) + c := newTestSchedulerMetricsCollector(k8sClient, nil) + + _, err := c.resolveEndpoint(context.Background()) + require.Error(t, err) + assert.Contains(t, err.Error(), "dz-scheduler-68b79bbff5-w4jhd", "error must name the unresolvable holder") + assert.Contains(t, err.Error(), "not a Service backend") + }) +} + +// TestSchedulerVerifyPodMatchesServiceSelector is a direct unit test on the +// selector-membership check itself: prior coverage only exercised it indirectly through +// resolveEndpoint's happy path (every fixture pod already carried matching labels), which +// can't distinguish "the check runs and passes" from "the check was never wired in at +// all." This asserts the function's cases directly. +func TestSchedulerVerifyPodMatchesServiceSelector(t *testing.T) { + svc := dzSchedulerService() + + t.Run("pod carrying every selector label passes", func(t *testing.T) { + pod := dzSchedulerPod("dz-scheduler-1", "10.0.0.1") + assert.NoError(t, schedulerVerifyPodMatchesServiceSelector(svc, pod)) + }) + + t.Run("pod missing one selector key fails, naming it", func(t *testing.T) { + pod := &corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Name: "dz-scheduler-1", + Namespace: schedulerMetricsNamespace, + Labels: map[string]string{"component": "scheduler"}, // missing "tier" + }, + } + err := schedulerVerifyPodMatchesServiceSelector(svc, pod) + require.Error(t, err) + assert.Contains(t, err.Error(), "tier") + }) + + t.Run("empty service selector matches no pod", func(t *testing.T) { + noSelectorSvc := dzSchedulerService() + noSelectorSvc.Spec.Selector = nil + pod := dzSchedulerPod("dz-scheduler-1", "10.0.0.1") + err := schedulerVerifyPodMatchesServiceSelector(noSelectorSvc, pod) + require.Error(t, err, "an empty selector must not be treated as \"matches everything\"") + }) + + t.Run("selector value is empty string, pod lacks the label entirely -- must still fail", func(t *testing.T) { + // This is the exact case a `pod.Labels[key] != wantValue` comparison (no + // presence check) gets wrong: a missing key reads back as Go's zero value "" + // for a map, which is indistinguishable from a label that IS present with an + // explicit empty value -- something Kubernetes permits. An unlabelled pod must + // not pass just because the selector's value happens to be "". + emptyValueSvc := dzSchedulerService() + emptyValueSvc.Spec.Selector = map[string]string{"component": ""} + unlabelledPod := &corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Name: "dz-scheduler-1", + Namespace: schedulerMetricsNamespace, + // No Labels at all -- pod.Labels["component"] reads back "" either way. + }, + } + err := schedulerVerifyPodMatchesServiceSelector(emptyValueSvc, unlabelledPod) + require.Error(t, err, "an unlabelled pod must not match a selector whose value is the empty string") + }) +} + +// TestSchedulerLeaseHolderPodName_ParsesHolderIdentity is a direct unit test on the +// holderIdentity parsing rule: everything before the LAST underscore is the pod name +// (client-go's leaderelection writes "_", and a Kubernetes pod name can +// never itself contain "_" -- see schedulerLeaseHolderPodName's doc comment), a holder +// with no underscore is used as-is, and a holder whose derived pod name comes out empty +// (including a merely-empty holder) is an error. +func TestSchedulerLeaseHolderPodName_ParsesHolderIdentity(t *testing.T) { + t.Run("pod name and UUID split on the last underscore", func(t *testing.T) { + podName, err := schedulerLeaseHolderPodName("dz-scheduler-68b79bbff5-w4jhd_8f7453e4-c02d-4fe5-96b2-eb88ad29983a") + require.NoError(t, err) + assert.Equal(t, "dz-scheduler-68b79bbff5-w4jhd", podName) + }) + + t.Run("no underscore is used as-is", func(t *testing.T) { + podName, err := schedulerLeaseHolderPodName("dz-scheduler-leader") + require.NoError(t, err) + assert.Equal(t, "dz-scheduler-leader", podName) + }) + + t.Run("empty holder is an error", func(t *testing.T) { + _, err := schedulerLeaseHolderPodName("") + require.Error(t, err) + }) + + t.Run("holder that is just an underscore derives an empty pod name, which is an error", func(t *testing.T) { + _, err := schedulerLeaseHolderPodName("_") + require.Error(t, err, "must not silently resolve to Pods().Get(ctx, \"\")") + }) + + t.Run("holder starting with an underscore derives an empty pod name, which is an error", func(t *testing.T) { + _, err := schedulerLeaseHolderPodName("_8f7453e4-c02d-4fe5-96b2-eb88ad29983a") + require.Error(t, err) + }) +} + +func strPtr(s string) *string { return &s } + +// TestSchedulerLeaderTargetPort is a direct unit test on the Service-targetPort -> +// container-port resolution: an int targetPort is used as-is, a named targetPort is +// resolved against the pod's own container ports, and a named targetPort that isn't +// declared on any container is an error rather than a guess. dz-scheduler's real +// container declares no `ports:` at all today (see this function's doc comment), so the +// named-port branches below are exercised only here, not by any resolveEndpoint-level +// test. +func TestSchedulerLeaderTargetPort(t *testing.T) { + t.Run("int target port is used as-is", func(t *testing.T) { + pod := dzSchedulerPod("dz-scheduler-1", "10.0.0.1") + port, err := schedulerLeaderTargetPort(pod, intstr.FromInt32(10259)) + require.NoError(t, err) + assert.Equal(t, int32(10259), port) + }) + + t.Run("named target port resolves against the pod's own container ports", func(t *testing.T) { + pod := dzSchedulerPod("dz-scheduler-1", "10.0.0.1") + pod.Spec.Containers = []corev1.Container{ + { + Name: "kube-second-scheduler", + Ports: []corev1.ContainerPort{{Name: "https-healthz", ContainerPort: 10259}}, + }, + } + port, err := schedulerLeaderTargetPort(pod, intstr.FromString("https-healthz")) + require.NoError(t, err) + assert.Equal(t, int32(10259), port) + }) + + t.Run("named target port absent from every container is an error, not a guess", func(t *testing.T) { + // This is dz-scheduler's actual shape today: a container with no declared + // ports at all. + pod := dzSchedulerPod("dz-scheduler-1", "10.0.0.1") + _, err := schedulerLeaderTargetPort(pod, intstr.FromString("https-healthz")) + require.Error(t, err) + assert.Contains(t, err.Error(), "https-healthz") + }) +} + +// TestSchedulerMetricLabels_RejectsUnmodeledLabelNamingIt is a direct unit test on the +// rejection function itself: prior coverage only asserted "nothing came out the other +// end," which a logr.Discard()-based test can't tell apart from "the label name silently +// disappeared from the log line" -- deleting the log entirely (not just discarding it) +// would still pass that kind of assertion. This asserts the returned error names the +// specific offending label. +func TestSchedulerMetricLabels_RejectsUnmodeledLabelNamingIt(t *testing.T) { + pairs := []*dto.LabelPair{ + {Name: strPtr("plugin"), Value: strPtr("NodeResourcesFit")}, + {Name: strPtr("node"), Value: strPtr("ip-10-0-0-1")}, + } + + _, err := schedulerMetricLabels("dz_scheduler_plugin_score", pairs) + + require.Error(t, err) + assert.Contains(t, err.Error(), "node", "error must name the offending label") + assert.Contains(t, err.Error(), "dz_scheduler_plugin_score", "error must name the metric family") +} + +// TestSchedulerMetricsCollector_UnmodeledLabelRejectsOnlyThatSeriesAndReportsTelemetry +// covers the sibling-isolation claim convertFamily's own doc comment makes (rejecting one +// series must not affect its siblings in the same family/scrape) and the telemetry-report +// half of I5: a rejection must be visible outside pod logs, not just logger.Error. +func TestSchedulerMetricsCollector_UnmodeledLabelRejectsOnlyThatSeriesAndReportsTelemetry(t *testing.T) { + const mixedPayload = `# HELP dz_scheduler_plugin_score Score assigned to a node by a scoring plugin. +# TYPE dz_scheduler_plugin_score gauge +dz_scheduler_plugin_score{plugin="NodeResourcesFit",profile="default-scheduler"} 55 +dz_scheduler_plugin_score{plugin="NodeResourcesFit",profile="default-scheduler",node="ip-10-0-0-1"} 87 +` + httpClient := staticScrapeClient(mixedPayload) + k8sClient := k8sfake.NewSimpleClientset(dzSchedulerService()) + c, fakeLogger := newTestSchedulerMetricsCollectorWithTelemetry(k8sClient, httpClient, time.Hour) + + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + require.NoError(t, c.Start(ctx)) + defer func() { _ = c.Stop() }() + + resources := drainSchedulerMetricsResources(t, c.GetResourceChannel(), 500*time.Millisecond) + require.Len(t, resources, 1, "the rejected series must not suppress its accepted sibling in the same family/scrape") + + item := resources[0].Object.(map[string]interface{}) + assert.Equal(t, "dz_scheduler_plugin_score", item["metric_name"]) + assert.Equal(t, "NodeResourcesFit", item["plugin"]) + assert.Equal(t, float64(55), item["value"]) + + fakeLogger.mu.Lock() + reports := append([]fakeReport(nil), fakeLogger.reports...) + fakeLogger.mu.Unlock() + require.Len(t, reports, 1, "expected exactly one telemetry report for the rejected series") + require.Error(t, reports[0].err) + assert.Contains(t, reports[0].err.Error(), "node") + assert.Equal(t, "dz_scheduler_plugin_score", reports[0].fields["metric_family"]) +} + +// TestSchedulerMetricsCollector_AllowedFamiliesMatchesDakrIngestAllowList is the +// zxporter-side half of a pin that must live on both sides of the module boundary. +// dakr's ingest-side allow-list (schedulerPlacementMetricAllowedNames in +// dakr/server/metrics_collector.go) hardcodes this exact six-family set in its own +// test (dakr/server/scheduler_placement_metrics_wiring_test.go, +// TestSchedulerPlacementMetricCrossModuleRoundTrip's "metric family allow-list..." +// subtest) and asserts it against dakr's real map -- so that test catches a dakr-side +// rename or removal, but NOT a family added or removed here, because its copy of this +// list is a frozen literal, not a read of schedulerMetricsAllowedFamilies. This test is +// the missing other direction: it reads THIS collector's real map, so adding or +// removing a family here without updating the mirrored literal on dakr's side fails +// here instead of silently dropping (or never emitting) an entire metric family in +// production. A mechanical shared import isn't available -- operators/zxporter/go.mod +// does not require the root module -- so two mirrored literals, each pinned against +// its own side's real map, is the actual closure; neither test alone provides it. +// +// It carries the equivalent pin for metric_type. Before this, the histogram trio +// (schedulerMetricTypeHistogramBucket/Sum/Count) was pinned only incidentally, by the +// literal switch in TestSchedulerMetricsCollector_HistogramBucketsFlattenIncludingPlusInf, +// and nothing asserted schedulerMetricTypeCounter or schedulerMetricTypeGauge at all -- +// a rename or typo of either constant would have passed both modules' test suites and +// silently dropped every counter and gauge row at dakr ingest (schedulerPlacementMetricDropReason +// rejects any metric_type outside models.AllSchedulerMetricTypes, logged only as a +// throttled Warn). This asserts all five of this collector's metric_type constants +// against dakr's vocabulary, set-for-set, the same bidirectional way as the family +// check above -- so a rename on either side that is not mirrored on both fails here. +func TestSchedulerMetricsCollector_AllowedFamiliesMatchesDakrIngestAllowList(t *testing.T) { + dakrAllowList := map[string]bool{ + "dz_scheduler_plugin_score": true, + "scheduler_schedule_attempts_total": true, + "scheduler_framework_extension_point_duration_seconds": true, + "scheduler_plugin_execution_duration_seconds": true, + "scheduler_plugin_evaluation_total": true, + "scheduler_pending_pods": true, + } + + require.Len(t, schedulerMetricsAllowedFamilies, len(dakrAllowList), + "this collector's allow-list must have exactly the families dakr's ingest allow-list accepts") + for name := range dakrAllowList { + assert.True(t, schedulerMetricsAllowedFamilies[name], + "dakr's ingest allow-list has family %q that this collector does not emit", name) + } + for name := range schedulerMetricsAllowedFamilies { + assert.True(t, dakrAllowList[name], + "this collector emits family %q that dakr's ingest allow-list does not accept", name) + } + + // dakr's models.AllSchedulerMetricTypes, hardcoded rather than imported for the same + // separate-module reason as dakrAllowList above. + dakrMetricTypes := map[string]bool{ + "counter": true, + "gauge": true, + "histogram_bucket": true, + "histogram_sum": true, + "histogram_count": true, + } + // This collector's own constants, read directly so a rename or typo of any of them + // fails this test. + zxporterMetricTypes := map[string]bool{ + schedulerMetricTypeCounter: true, + schedulerMetricTypeGauge: true, + schedulerMetricTypeHistogramBucket: true, + schedulerMetricTypeHistogramSum: true, + schedulerMetricTypeHistogramCount: true, + } + + require.Len(t, zxporterMetricTypes, len(dakrMetricTypes), + "this collector's metric_type constants must have exactly the types dakr's vocabulary accepts") + for name := range dakrMetricTypes { + assert.True(t, zxporterMetricTypes[name], + "dakr's metric_type vocabulary has %q that this collector's constants do not produce", name) + } + for name := range zxporterMetricTypes { + assert.True(t, dakrMetricTypes[name], + "this collector emits metric_type %q that dakr's vocabulary does not accept", name) + } +} diff --git a/internal/collector/service_collector.go b/internal/collector/service_collector.go index 71a8bcb3..dc7ab4a6 100644 --- a/internal/collector/service_collector.go +++ b/internal/collector/service_collector.go @@ -167,8 +167,8 @@ func (c *ServiceCollector) serviceChanged(oldService, newService *corev1.Service oldService.ObjectMeta, newService.ObjectMeta, ) - if changed != IgnoreChanges { - return changed == PushChanges + if changed == PushChanges { + return true } // Check for spec changes diff --git a/internal/collector/serviceaccount_collector.go b/internal/collector/serviceaccount_collector.go index a1c9ec1b..9ab38956 100644 --- a/internal/collector/serviceaccount_collector.go +++ b/internal/collector/serviceaccount_collector.go @@ -172,8 +172,8 @@ func (c *ServiceAccountCollector) serviceAccountChanged(oldSA, newSA *corev1.Ser oldSA.ObjectMeta, newSA.ObjectMeta, ) - if changed != IgnoreChanges { - return changed == PushChanges + if changed == PushChanges { + return true } // Check for secret reference changes diff --git a/internal/collector/statefulset_collector.go b/internal/collector/statefulset_collector.go index 17788123..cf47c871 100644 --- a/internal/collector/statefulset_collector.go +++ b/internal/collector/statefulset_collector.go @@ -172,8 +172,8 @@ func (c *StatefulSetCollector) statefulSetChanged( oldStatefulSet.ObjectMeta, newStatefulSet.ObjectMeta, ) - if changed != IgnoreChanges { - return changed == PushChanges + if changed == PushChanges { + return true } // Check for spec changes @@ -299,7 +299,7 @@ func (c *StatefulSetCollector) GetResourceChannel() <-chan []CollectedResource { // GetType returns the type of resource this collector handles func (c *StatefulSetCollector) GetType() string { - return "statefulset" + return StatefulSet.String() } // IsAvailable checks if StatefulSet resources can be accessed in the cluster diff --git a/internal/collector/storageclass_collector.go b/internal/collector/storageclass_collector.go index 6d051c63..bcb9baaa 100644 --- a/internal/collector/storageclass_collector.go +++ b/internal/collector/storageclass_collector.go @@ -164,8 +164,8 @@ func (c *StorageClassCollector) storageClassChanged(oldSC, newSC *storagev1.Stor oldSC.ObjectMeta, newSC.ObjectMeta, ) - if changed != IgnoreChanges { - return changed == PushChanges + if changed == PushChanges { + return true } // Check for provisioner changes diff --git a/internal/collector/types.go b/internal/collector/types.go index bb316de1..7980f22b 100644 --- a/internal/collector/types.go +++ b/internal/collector/types.go @@ -16,6 +16,13 @@ func AllResourceTypes() []ResourceType { CNPGCluster, KyvernoPolicy, KyvernoPolicyReport, GatekeeperConstraintTemplate, GatekeeperConstraint, MigPartedConfig, + ArgoCDApplication, ArgoCDApplicationSet, + FluxGitRepository, FluxOCIRepository, FluxHelmRepository, FluxHelmChart, FluxBucket, + FluxKustomization, FluxHelmRelease, + FluxExternalArtifact, FluxAlert, FluxProvider, FluxReceiver, FluxInstance, FluxReport, + FluxResourceSetInputProvider, FluxResourceSet, + FluxImageRepository, FluxImagePolicy, FluxImageUpdateAutomation, + FlaggerCanary, FlaggerMetricTemplate, FlaggerAlertProvider, } } @@ -197,6 +204,24 @@ type ExcludedScheduledSparkApplication struct { Name string `json:"name"` } +// ExcludedArgoCDApplication identifies an Argo CD Application to exclude +type ExcludedArgoCDApplication struct { + // Namespace is the Argo CD Application's namespace + Namespace string `json:"namespace"` + + // Name is the Argo CD Application's name + Name string `json:"name"` +} + +// ExcludedArgoCDApplicationSet identifies an Argo CD ApplicationSet to exclude +type ExcludedArgoCDApplicationSet struct { + // Namespace is the Argo CD ApplicationSet's namespace + Namespace string `json:"namespace"` + + // Name is the Argo CD ApplicationSet's name + Name string `json:"name"` +} + // ExcludedCNPGCluster identifies a CloudNativePG cluster to exclude from collection type ExcludedCNPGCluster struct { // Namespace is the CNPG cluster's namespace @@ -205,3 +230,130 @@ type ExcludedCNPGCluster struct { // Name is the CNPG cluster's name Name string `json:"name"` } + +// ExcludedFluxGitRepository identifies a FluxCD GitRepository to exclude +type ExcludedFluxGitRepository struct { + Namespace string `json:"namespace"` + Name string `json:"name"` +} + +// ExcludedFluxOCIRepository identifies a FluxCD OCIRepository to exclude +type ExcludedFluxOCIRepository struct { + Namespace string `json:"namespace"` + Name string `json:"name"` +} + +// ExcludedFluxHelmRepository identifies a FluxCD HelmRepository to exclude +type ExcludedFluxHelmRepository struct { + Namespace string `json:"namespace"` + Name string `json:"name"` +} + +// ExcludedFluxHelmChart identifies a FluxCD HelmChart to exclude +type ExcludedFluxHelmChart struct { + Namespace string `json:"namespace"` + Name string `json:"name"` +} + +// ExcludedFluxBucket identifies a FluxCD Bucket to exclude +type ExcludedFluxBucket struct { + Namespace string `json:"namespace"` + Name string `json:"name"` +} + +// ExcludedFluxKustomization identifies a FluxCD Kustomization to exclude +type ExcludedFluxKustomization struct { + Namespace string `json:"namespace"` + Name string `json:"name"` +} + +// ExcludedFluxHelmRelease identifies a FluxCD HelmRelease to exclude +type ExcludedFluxHelmRelease struct { + Namespace string `json:"namespace"` + Name string `json:"name"` +} + +// ExcludedFluxExternalArtifact identifies a FluxCD ExternalArtifact to exclude +type ExcludedFluxExternalArtifact struct { + Namespace string `json:"namespace"` + Name string `json:"name"` +} + +// ExcludedFluxAlert identifies a FluxCD Alert to exclude +type ExcludedFluxAlert struct { + Namespace string `json:"namespace"` + Name string `json:"name"` +} + +// ExcludedFluxProvider identifies a FluxCD Provider to exclude +type ExcludedFluxProvider struct { + Namespace string `json:"namespace"` + Name string `json:"name"` +} + +// ExcludedFluxReceiver identifies a FluxCD Receiver to exclude +type ExcludedFluxReceiver struct { + Namespace string `json:"namespace"` + Name string `json:"name"` +} + +// ExcludedFluxInstance identifies a Flux Operator FluxInstance to exclude +type ExcludedFluxInstance struct { + Namespace string `json:"namespace"` + Name string `json:"name"` +} + +// ExcludedFluxReport identifies a Flux Operator FluxReport to exclude +type ExcludedFluxReport struct { + Namespace string `json:"namespace"` + Name string `json:"name"` +} + +// ExcludedFluxResourceSetInputProvider identifies a Flux Operator ResourceSetInputProvider to exclude +type ExcludedFluxResourceSetInputProvider struct { + Namespace string `json:"namespace"` + Name string `json:"name"` +} + +// ExcludedFluxResourceSet identifies a Flux Operator ResourceSet to exclude +type ExcludedFluxResourceSet struct { + Namespace string `json:"namespace"` + Name string `json:"name"` +} + +// ExcludedFluxImageRepository identifies a FluxCD ImageRepository to exclude +type ExcludedFluxImageRepository struct { + Namespace string `json:"namespace"` + Name string `json:"name"` +} + +// ExcludedFluxImagePolicy identifies a FluxCD ImagePolicy to exclude +type ExcludedFluxImagePolicy struct { + Namespace string `json:"namespace"` + Name string `json:"name"` +} + +// ExcludedFluxImageUpdateAutomation identifies a FluxCD ImageUpdateAutomation to exclude +type ExcludedFluxImageUpdateAutomation struct { + Namespace string `json:"namespace"` + Name string `json:"name"` +} + +// ExcludedFlaggerResource identifies a Flagger object to exclude from +// collection. One shape serves all three Flagger kinds because one collector +// does -- see FlaggerResourceCollector. The per-kind aliases below name the +// kind each CollectionPolicy list belongs to, and give a future kind-specific +// shape somewhere to land. +type ExcludedFlaggerResource struct { + Namespace string `json:"namespace"` + Name string `json:"name"` +} + +// ExcludedFlaggerCanary identifies a Flagger Canary to exclude +type ExcludedFlaggerCanary = ExcludedFlaggerResource + +// ExcludedFlaggerMetricTemplate identifies a Flagger MetricTemplate to exclude +type ExcludedFlaggerMetricTemplate = ExcludedFlaggerResource + +// ExcludedFlaggerAlertProvider identifies a Flagger AlertProvider to exclude +type ExcludedFlaggerAlertProvider = ExcludedFlaggerResource diff --git a/internal/collector/volumeattachment_collector.go b/internal/collector/volumeattachment_collector.go index 997f112a..d817ebdc 100644 --- a/internal/collector/volumeattachment_collector.go +++ b/internal/collector/volumeattachment_collector.go @@ -164,8 +164,8 @@ func (c *VolumeAttachmentCollector) volumeAttachmentChanged( oldVA.ObjectMeta, newVA.ObjectMeta, ) - if changed != IgnoreChanges { - return changed == PushChanges + if changed == PushChanges { + return true } // Check for spec changes diff --git a/internal/controller/cluster_token.go b/internal/controller/cluster_token.go new file mode 100644 index 00000000..d697c3b3 --- /dev/null +++ b/internal/controller/cluster_token.go @@ -0,0 +1,97 @@ +package controller + +import ( + "context" + "fmt" + "strings" + + "connectrpc.com/connect" + "github.com/go-logr/logr" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/client-go/kubernetes" + + "github.com/devzero-inc/zxporter/internal/transport" +) + +// clusterTokenSourceMirror is the CLUSTER_TOKEN_SOURCE value the chart and +// the kubectl manifests set when CLUSTER_TOKEN is fed from the mirror Secret +// devzero-zxporter-token, which zxporter itself writes (KUB-158). +const clusterTokenSourceMirror = "mirror" + +// envTokenIsMirrorFed reports whether the env token is only the pod-start +// snapshot of the mirror Secret. The polarity is fail-safe: only an explicit +// mirror marker in Secret mode counts. An unset marker (an older chart), an +// existingSecret, a direct clusterToken and ConfigMap mode all keep the env +// token authoritative. +func envTokenIsMirrorFed(useSecret bool, source string) bool { + return useSecret && source == clusterTokenSourceMirror +} + +// clusterTokenResolver decides which cluster token this zxporter runs with. +// It only reads: the caller persists the result to the mirror Secret or +// ConfigMap. +type clusterTokenResolver struct { + k8s kubernetes.Interface + // mirrorFed says the env token is the pod-start snapshot of the mirror + // Secret that readStored reads live (see envTokenIsMirrorFed). + mirrorFed bool + readStored func(ctx context.Context) string + exchange func(ctx context.Context, req transport.ClusterTokenExchangeRequest) (transport.ClusterTokenExchangeResult, error) + log logr.Logger +} + +// resolve picks the cluster token. +// +// When the env token is mirror-fed, the live mirror wins and the env snapshot +// is only a fallback: a standby promoted to leader after another replica +// re-exchanged must not run with, and write back, its older token. Otherwise +// the env token is explicit and wins, and the stored token is the fallback. +// +// Without either, a PAT is exchanged for a new token. The exchange carries +// the kube-system namespace UID, so dakr re-attaches the same cluster, and +// the trimmed CLUSTER_ID. A CLUSTER_ID that dakr does not know fails startup +// rather than registering a new cluster. +func (r *clusterTokenResolver) resolve(ctx context.Context, envToken, pat, clusterName, provider, clusterID string) (string, error) { + var token, source string + if r.mirrorFed { + token, source = r.readStored(ctx), "live mirror Secret" + if token == "" { + token, source = envToken, "env" + } + } else { + token, source = envToken, "env" + if token == "" { + token, source = r.readStored(ctx), "stored" + } + } + if token != "" { + r.log.Info("Using cluster token", "source", source) + return token, nil + } + if pat == "" { + return "", nil + } + + clusterID = strings.TrimSpace(clusterID) + res, err := r.exchange(ctx, transport.ClusterTokenExchangeRequest{ + PATToken: pat, ClusterName: clusterName, K8sProvider: provider, + KubeSystemUID: r.kubeSystemUID(ctx), ClusterID: clusterID, + }) + if err != nil { + if clusterID != "" && connect.CodeOf(err) == connect.CodeNotFound { + return "", fmt.Errorf("cluster %s named by CLUSTER_ID is not a live cluster of this token's team; refusing to register a new one: %w", clusterID, err) + } + return "", err + } + r.log.Info("Using cluster token", "source", "exchange", "clusterId", res.ClusterID, "reattached", res.Reattached) + return res.Token, nil +} + +func (r *clusterTokenResolver) kubeSystemUID(ctx context.Context) string { + ns, err := r.k8s.CoreV1().Namespaces().Get(ctx, "kube-system", metav1.GetOptions{}) + if err != nil { + r.log.Info("Could not read kube-system namespace; exchanging without a cluster fingerprint", "error", err.Error()) + return "" + } + return string(ns.UID) +} diff --git a/internal/controller/cluster_token_persist_test.go b/internal/controller/cluster_token_persist_test.go new file mode 100644 index 00000000..9b14a645 --- /dev/null +++ b/internal/controller/cluster_token_persist_test.go @@ -0,0 +1,94 @@ +package controller + +import ( + "context" + "testing" + + "github.com/go-logr/logr" + "go.uber.org/zap" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/types" + "k8s.io/client-go/kubernetes/fake" + + "github.com/devzero-inc/zxporter/internal/transport" +) + +// exchangeCountingClient is a DakrClient whose PAT exchange returns a fixed +// token and counts the calls. +type exchangeCountingClient struct { + transport.DakrClient + exchanges *int +} + +func (c exchangeCountingClient) ExchangePATForClusterToken( + context.Context, transport.ClusterTokenExchangeRequest, +) (transport.ClusterTokenExchangeResult, error) { + *c.exchanges++ + return transport.ClusterTokenExchangeResult{Token: "dakr-exchanged", ClusterID: "c-1"}, nil +} + +// In ConfigMap mode a PAT install persists the exchanged token into the +// env ConfigMap, so the next start reads it back instead of exchanging (and, +// since KUB-158, rotating) the token again. +func TestInitializeTelemetryComponentsPersistsToConfigMapMode(t *testing.T) { + const ns, cmName = "devzero-system", "devzero-zxporter-env-config" + t.Setenv("CLUSTER_TOKEN", "") + t.Setenv("PAT_TOKEN", "dzu-pat") + t.Setenv("DAKR_URL", "") + t.Setenv("CLUSTER_ID", "") + t.Setenv("CLUSTER_TOKEN_SOURCE", "") + t.Setenv("USE_SECRET_FOR_TOKEN", "false") + t.Setenv("TOKEN_CONFIGMAP_NAME", "") + t.Setenv("POD_NAMESPACE", ns) + + k := fake.NewSimpleClientset( + &corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: "kube-system", UID: types.UID("ks-uid")}}, + &corev1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{Name: cmName, Namespace: ns}, + Data: map[string]string{"CLUSTER_TOKEN": "", "OTHER": "kept"}, + }, + ) + exchanges := 0 + newController := func() *EnvBasedController { + return &EnvBasedController{ + Log: logr.Discard(), + K8sClient: k, + Reconciler: &CollectionPolicyReconciler{ZapLogger: zap.NewNop()}, + dakrClientFactory: func(string, string, logr.Logger) transport.DakrClient { + return exchangeCountingClient{DakrClient: transport.NewSimpleDakrClient(logr.Discard()), exchanges: &exchanges} + }, + } + } + initialize := func() { + t.Helper() + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + c := newController() + if err := c.initializeTelemetryComponents(ctx); err != nil { + t.Fatalf("initializeTelemetryComponents: %v", err) + } + c.Reconciler.TelemetryLogger.Stop() + } + + initialize() + cm, err := k.CoreV1().ConfigMaps(ns).Get(context.Background(), cmName, metav1.GetOptions{}) + if err != nil { + t.Fatalf("get ConfigMap: %v", err) + } + if cm.Data["CLUSTER_TOKEN"] != "dakr-exchanged" || cm.Data["OTHER"] != "kept" { + t.Fatalf("ConfigMap after the first start: token persisted=%t, other keys kept=%t", + cm.Data["CLUSTER_TOKEN"] == "dakr-exchanged", cm.Data["OTHER"] == "kept") + } + if exchanges != 1 { + t.Fatalf("first start made %d exchanges, want 1", exchanges) + } + + initialize() + if exchanges != 1 { + t.Fatalf("second start exchanged again (%d exchanges in total); want it to read the stored token", exchanges) + } + if s, _ := k.CoreV1().Secrets(ns).List(context.Background(), metav1.ListOptions{}); len(s.Items) != 0 { + t.Fatalf("ConfigMap mode wrote %d Secrets", len(s.Items)) + } +} diff --git a/internal/controller/cluster_token_test.go b/internal/controller/cluster_token_test.go new file mode 100644 index 00000000..e1840e2c --- /dev/null +++ b/internal/controller/cluster_token_test.go @@ -0,0 +1,137 @@ +package controller + +import ( + "context" + "errors" + "strings" + "testing" + + "connectrpc.com/connect" + "github.com/go-logr/logr" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/types" + "k8s.io/client-go/kubernetes/fake" + + "github.com/devzero-inc/zxporter/internal/transport" +) + +func TestResolveClusterToken(t *testing.T) { + notFound := connect.NewError(connect.CodeNotFound, errors.New("resource not found")) + cases := []struct { + name string + mirrorFed bool + env string + stored string // the live mirror Secret or ConfigMap token + pat string + clusterID string + noKubeSystem bool + exchangeErr error + want string + wantErr string // substring of the expected error + wantExchanges int + wantUID string + wantSentID string + }{ + {name: "env token is used", env: "dakr-A", stored: "dakr-B", pat: "dzu-pat", want: "dakr-A"}, + {name: "stored token when env is empty", stored: "dakr-B", pat: "dzu-pat", want: "dakr-B"}, + { + name: "exchange carries the kube-system UID and the trimmed CLUSTER_ID", pat: "dzu-pat", clusterID: " c-7\n", + want: "dakr-exchanged", wantExchanges: 1, wantUID: "ks-uid", wantSentID: "c-7", + }, + { + name: "exchange without a UID when kube-system is unreadable", pat: "dzu-pat", noKubeSystem: true, + want: "dakr-exchanged", wantExchanges: 1, + }, + { + name: "unknown CLUSTER_ID fails startup", pat: "dzu-pat", clusterID: "c-9", exchangeErr: notFound, + wantErr: "c-9", wantExchanges: 1, wantUID: "ks-uid", wantSentID: "c-9", + }, + {name: "no token and no PAT resolves nothing"}, + { + name: "mirror-fed env loses to the newer live mirror", mirrorFed: true, env: "dakr-A", stored: "dakr-B", pat: "dzu-pat", + want: "dakr-B", + }, + { + name: "mirror-fed env is kept when the live mirror is empty", mirrorFed: true, env: "dakr-A", pat: "dzu-pat", + want: "dakr-A", + }, + { + name: "mirror-fed start with nothing stored exchanges", mirrorFed: true, pat: "dzu-pat", + want: "dakr-exchanged", wantExchanges: 1, wantUID: "ks-uid", + }, + { + name: "an explicit env token wins over the live mirror", env: "dakr-A", stored: "dakr-B", pat: "dzu-pat", + want: "dakr-A", + }, + } + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + var objs []runtime.Object + if !tc.noKubeSystem { + objs = append(objs, &corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: "kube-system", UID: types.UID("ks-uid")}}) + } + k := fake.NewSimpleClientset(objs...) + var calls []transport.ClusterTokenExchangeRequest + r := &clusterTokenResolver{ + k8s: k, + mirrorFed: tc.mirrorFed, + readStored: func(context.Context) string { return tc.stored }, + exchange: func(_ context.Context, req transport.ClusterTokenExchangeRequest) (transport.ClusterTokenExchangeResult, error) { + calls = append(calls, req) + if tc.exchangeErr != nil { + return transport.ClusterTokenExchangeResult{}, tc.exchangeErr + } + return transport.ClusterTokenExchangeResult{Token: "dakr-exchanged", ClusterID: "c-1"}, nil + }, + log: logr.Discard(), + } + + got, err := r.resolve(context.Background(), tc.env, tc.pat, "name", "aws", tc.clusterID) + switch { + case tc.wantErr != "" && (err == nil || !strings.Contains(err.Error(), tc.wantErr)): + t.Fatalf("want an error naming %q, got %v", tc.wantErr, err) + case tc.wantErr == "" && err != nil: + t.Fatalf("unexpected error %v", err) + } + if got != tc.want || len(calls) != tc.wantExchanges { + t.Fatalf("got %q, %d exchanges; want %q, %d", got, len(calls), tc.want, tc.wantExchanges) + } + if tc.wantExchanges > 0 { + req := calls[0] + if req.KubeSystemUID != tc.wantUID || req.ClusterID != tc.wantSentID || req.PATToken != tc.pat { + t.Fatalf("exchange request uid=%q clusterID=%q, want uid=%q clusterID=%q", + req.KubeSystemUID, req.ClusterID, tc.wantUID, tc.wantSentID) + } + } + // resolve only reads; the caller persists the token to the mirror. + if s, _ := k.CoreV1().Secrets("").List(context.Background(), metav1.ListOptions{}); len(s.Items) != 0 { + t.Fatalf("resolve wrote %d Secrets", len(s.Items)) + } + }) + } +} + +// Only the default Secret-mode chart, which feeds CLUSTER_TOKEN from the +// mirror Secret, marks the env token as mirror-fed. Anything else, including +// an unset marker and ConfigMap mode, keeps the env token authoritative. +func TestEnvTokenIsMirrorFed(t *testing.T) { + cases := []struct { + useSecret bool + source string + want bool + }{ + {useSecret: true, source: "mirror", want: true}, + {useSecret: true, source: "", want: false}, + {useSecret: true, source: "existingSecret", want: false}, + {useSecret: true, source: "Mirror", want: false}, + {useSecret: false, source: "mirror", want: false}, + {useSecret: false, source: "", want: false}, + } + for _, tc := range cases { + if got := envTokenIsMirrorFed(tc.useSecret, tc.source); got != tc.want { + t.Errorf("envTokenIsMirrorFed(%v, %q) = %v, want %v", tc.useSecret, tc.source, got, tc.want) + } + } +} diff --git a/internal/controller/collectionpolicy_controller.go b/internal/controller/collectionpolicy_controller.go index 51c29dbc..1e53b68e 100644 --- a/internal/controller/collectionpolicy_controller.go +++ b/internal/controller/collectionpolicy_controller.go @@ -20,6 +20,7 @@ import ( "context" "fmt" "reflect" + "slices" "strconv" "sync" "time" @@ -146,6 +147,11 @@ type PolicyConfig struct { ExcludedSparkApplications []collector.ExcludedSparkApplication ExcludedScheduledSparkApplications []collector.ExcludedScheduledSparkApplication ExcludedCNPGClusters []collector.ExcludedCNPGCluster + ExcludedArgoCDApplications []collector.ExcludedArgoCDApplication + ExcludedArgoCDApplicationSets []collector.ExcludedArgoCDApplicationSet + ExcludedFlaggerCanaries []collector.ExcludedFlaggerCanary + ExcludedFlaggerMetricTemplates []collector.ExcludedFlaggerMetricTemplate + ExcludedFlaggerAlertProviders []collector.ExcludedFlaggerAlertProvider DisabledCollectors []string @@ -167,6 +173,17 @@ type PolicyConfig struct { NodeMetricsConcurrency int NodemonRequestTimeout time.Duration KubeletFallbackTimeout time.Duration + + // SchedulerMetricsScrapeInterval bounds how often the SchedulerMetrics + // collector scrapes dz-scheduler's /metrics endpoint. See + // collector.DefaultSchedulerMetricsScrapeInterval's doc comment for the + // default's reasoning. + SchedulerMetricsScrapeInterval time.Duration + + // KEDAMetricsEndpoint is the full URL of the KEDA operator /metrics endpoint + // scraped by the KedaScalerMetrics collector. Empty means the collector + // applies collector.DefaultKedaMetricsEndpoint (KEDA in devzero-system). + KEDAMetricsEndpoint string } // ======================================== @@ -218,6 +235,16 @@ type PolicyConfig struct { // nodes/proxy: kubelet Summary API fallback (/stats/summary) for nodes without a nodemon pod // +kubebuilder:rbac:groups="",resources=nodes/proxy,verbs=get +// SchedulerMetricsCollector (issue #10055): reads the dz-scheduler Lease's holderIdentity +// to scrape the current leader pod directly instead of the Service VIP, which could +// otherwise get stuck scraping a standby replica that reports all-zero placement metrics. +// Restricted to the one named Lease dz-scheduler ships -- this collector never needs any +// other Lease in the cluster. zxporter's own leader-election Role (config/rbac/leader_election_role.yaml) +// does NOT cover this: that Role is namespaced to wherever zxporter itself is deployed, +// which is not always devzero-system (dz-scheduler's namespace) -- see +// docs/prometheus-migration-guide.md's devzero-zxporter examples. +// +kubebuilder:rbac:groups=coordination.k8s.io,resources=leases,resourceNames=dz-scheduler,verbs=get + // Core Kubernetes resources (READ-ONLY monitoring and collection) // +kubebuilder:rbac:groups="",resources=pods,verbs=get;list;watch // +kubebuilder:rbac:groups="",resources=pods/status,verbs=get @@ -287,6 +314,9 @@ type PolicyConfig struct { // Optional third-party monitoring integrations // +kubebuilder:rbac:groups=datadoghq.com,resources=extendeddaemonsetreplicasets,verbs=get;list;watch // +kubebuilder:rbac:groups=argoproj.io,resources=rollouts,verbs=get;list;watch +// applications and applicationsets are Argo CD, a different project from the +// Argo Rollouts rollouts above that happens to share the argoproj.io group. +// +kubebuilder:rbac:groups=argoproj.io,resources=applications;applicationsets,verbs=get;list;watch // +kubebuilder:rbac:groups=keda.sh,resources=scaledobjects;scaledjobs;triggerauthentications;clustertriggerauthentications,verbs=get;list;watch // +kubebuilder:rbac:groups=kubeflow.org,resources=notebooks,verbs=get;list;watch // +kubebuilder:rbac:groups=batch.volcano.sh,resources=jobs,verbs=get;list;watch @@ -309,6 +339,19 @@ type PolicyConfig struct { // +kubebuilder:rbac:groups=constraints.gatekeeper.sh,resources=*,verbs=get;list;watch // +kubebuilder:rbac:groups=status.gatekeeper.sh,resources=*,verbs=get;list;watch +// FluxCD GitOps sources, reconcilers and notification kinds (optional - only +// if Flux installed). image.toolkit.fluxcd.io is the image-reflector/ +// image-automation controllers, an optional add-on even on Flux clusters. +// +kubebuilder:rbac:groups=source.toolkit.fluxcd.io,resources=gitrepositories;ocirepositories;helmrepositories;helmcharts;buckets;externalartifacts,verbs=get;list;watch +// +kubebuilder:rbac:groups=kustomize.toolkit.fluxcd.io,resources=kustomizations,verbs=get;list;watch +// +kubebuilder:rbac:groups=helm.toolkit.fluxcd.io,resources=helmreleases,verbs=get;list;watch +// +kubebuilder:rbac:groups=notification.toolkit.fluxcd.io,resources=alerts;providers;receivers,verbs=get;list;watch +// +kubebuilder:rbac:groups=fluxcd.controlplane.io,resources=fluxinstances;fluxreports;resourcesetinputproviders;resourcesets,verbs=get;list;watch +// +kubebuilder:rbac:groups=image.toolkit.fluxcd.io,resources=imagerepositories;imagepolicies;imageupdateautomations,verbs=get;list;watch + +// Flagger progressive-delivery kinds (optional - only if Flagger installed). +// +kubebuilder:rbac:groups=flagger.app,resources=canaries;metrictemplates;alertproviders,verbs=get;list;watch + // 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 *CollectionPolicyReconciler) Reconcile( @@ -369,8 +412,12 @@ func (r *CollectionPolicyReconciler) Reconcile( return ctrl.Result{RequeueAfter: 5 * time.Minute}, nil } -// retryPendingCollectors checks if previously unavailable collectors (e.g., CRDs installed -// after zxporter startup) have become available and registers/starts them. +// retryPendingCollectors registers and starts collectors that could not run earlier: +// CRD-based collectors whose CRD was absent at startup (e.g. KEDA installed after +// zxporter), and collectors whose Start failed on a restart or re-enable path (see +// requeueFailedStart). An entry stays pending until its collector is running, so one +// failed Start is retried on the next reconcile rather than disabling the collector +// until the pod restarts. func (r *CollectionPolicyReconciler) retryPendingCollectors(ctx context.Context) { r.pendingMu.Lock() defer r.pendingMu.Unlock() @@ -383,29 +430,138 @@ func (r *CollectionPolicyReconciler) retryPendingCollectors(ctx context.Context) var stillPending []pendingCollector for _, pc := range r.pendingCollectors { + if r.CurrentConfig != nil && slices.Contains(r.CurrentConfig.DisabledCollectors, pc.name.String()) { + logger.Info("Pending collector has since been disabled, dropping it", "type", pc.name.String()) + continue + } newCollector := pc.factory() - if newCollector.IsAvailable(ctx) { - logger.Info("Previously unavailable collector is now available, registering", - "type", pc.name.String()) - if err := r.CollectionManager.RegisterCollector(newCollector); err != nil { - logger.Error(err, "Failed to register collector", "type", pc.name.String()) - stillPending = append(stillPending, pc) - continue - } - if err := r.CollectionManager.StartCollector(ctx, pc.name.String()); err != nil { - logger.Error(err, "Failed to start collector", "type", pc.name.String()) - continue - } - logger.Info("Successfully started previously unavailable collector", - "type", pc.name.String()) - } else { + if newCollector == nil { + logger.Info("No collector can be built for pending type, dropping it", "type", pc.name.String()) + continue + } + if !newCollector.IsAvailable(ctx) { + stillPending = append(stillPending, pc) + continue + } + + // RegisterCollector keys the collector by its GetType(), so every lookup below + // uses that same key. Starting by pc.name used to fail as "not registered" for any + // collector whose GetType() differed from its ResourceType name (the KEDA + // ScaledJob/ScaledObject collectors did), and that failure path leaked the + // manager mutex. TestResourceCollectorSpecs_TypeMatchesName pins the two equal. + key := newCollector.GetType() + if r.CollectionManager.IsCollectorRunning(key) { + logger.Info("Pending collector is already running, dropping it", "type", key) + continue + } + if r.CollectionManager.GetCollector(key) != nil { + // A registered instance that is not running is one whose Start failed. It + // would refuse this fresh instance as "already registered"; replace it. + _ = r.CollectionManager.DeregisterCollector(key) + } + + logger.Info("Previously unavailable collector is now available, registering", "type", key) + if err := r.CollectionManager.RegisterCollector(newCollector); err != nil { + logger.Error(err, "Failed to register collector", "type", key) + stillPending = append(stillPending, pc) + continue + } + if err := r.CollectionManager.StartCollector(ctx, key); err != nil { + logger.Error(err, "Failed to start collector, will retry", "type", key) + // Deregister the failed instance so the next attempt can register a fresh + // one, and keep the entry: dropping it made one transient failure permanent. + _ = r.CollectionManager.DeregisterCollector(key) stillPending = append(stillPending, pc) + continue } + r.rewireCollector(key) + logger.Info("Successfully started previously unavailable collector", "type", key) } r.pendingCollectors = stillPending } +// requeueFailedStart handles a collector that registered but failed to Start on the +// restart or re-enable path: it deregisters the failed instance (which would otherwise +// refuse any replacement as "already registered") and queues the type for +// retryPendingCollectors, which rebuilds it from the configuration current at retry time. +func (r *CollectionPolicyReconciler) requeueFailedStart( + logger logr.Logger, + registeredKey string, + collectorType string, + metricsClient *metricsv1.Clientset, +) { + _ = r.CollectionManager.DeregisterCollector(registeredKey) + + var resourceType collector.ResourceType + found := false + for _, rt := range collector.AllResourceTypes() { + if rt.String() == collectorType { + resourceType, found = rt, true + break + } + } + if !found { + logger.Info("No resource type for failed collector, it will not be retried", "type", collectorType) + return + } + + r.pendingMu.Lock() + defer r.pendingMu.Unlock() + for _, pc := range r.pendingCollectors { + if pc.name == resourceType { + return + } + } + r.pendingCollectors = append(r.pendingCollectors, pendingCollector{ + name: resourceType, + factory: func() collector.ResourceCollector { + return r.buildCollectorFromSpecs(logger, resourceType, metricsClient) + }, + }) + logger.Info("Queued collector for retry after a failed start", "type", collectorType) +} + +// buildCollectorFromSpecs builds a fresh collector of the given type from the current +// configuration, or returns nil when registerResourceCollectors does not manage that type. +func (r *CollectionPolicyReconciler) buildCollectorFromSpecs( + logger logr.Logger, + resourceType collector.ResourceType, + metricsClient *metricsv1.Clientset, +) collector.ResourceCollector { + config := r.CurrentConfig + if config == nil { + config = &PolicyConfig{} + } + for _, spec := range r.resourceCollectorSpecs(logger, config, metricsClient) { + if spec.name != resourceType { + continue + } + if spec.factory != nil { + return spec.factory() + } + return spec.collector + } + return nil +} + +// rewireCollector restores the cross-collector wiring a freshly started collector of this +// type needs. A replacement is a new object and has lost whatever was set on the old one. +func (r *CollectionPolicyReconciler) rewireCollector(collectorType string) { + switch collectorType { + case collector.Pod.String(): + r.wireMpaPublisherIntoPodCollector() + r.wireOOMReconcilerIntoPodCollector() + case collector.Event.String(): + // The PDB / node / Karpenter collectors need no equivalent handling: the + // EventCollector resolves those through the CollectionManager on every event, so + // replacing one of them is picked up without re-wiring. + r.wireDisruptionSourcesIntoEventCollector() + case collector.Node.String(): + r.wireNodeClaimSourceIntoNodeCollector() + } +} + // createNewConfig creates a new config by merging policy and environment variables // //nolint:gocyclo @@ -427,7 +583,7 @@ func (r *CollectionPolicyReconciler) createNewConfig( ClusterToken: envSpec.Policies.ClusterToken, DisableGPUMetrics: envSpec.Policies.DisableGPUMetrics, MaskSecretData: envSpec.Policies.MaskSecretData, - DisabledCollectors: envSpec.Policies.DisabledCollectors, + DisabledCollectors: collector.CanonicalCollectorTypes(envSpec.Policies.DisabledCollectors), BufferSize: envSpec.Policies.BufferSize, MigPartedConfigMapName: envSpec.Policies.MigPartedConfigMapName, @@ -513,6 +669,25 @@ func (r *CollectionPolicyReconciler) createNewConfig( newConfig.KubeletFallbackTimeout = 15 * time.Second // Default } + // Bound how often the SchedulerMetrics collector scrapes dz-scheduler's + // /metrics endpoint. + schedulerMetricsScrapeIntervalStr := envSpec.Policies.SchedulerMetricsScrapeInterval + if schedulerMetricsScrapeIntervalStr != "" { + if interval, err := time.ParseDuration(schedulerMetricsScrapeIntervalStr); err == nil && interval > 0 { + newConfig.SchedulerMetricsScrapeInterval = interval + } else { + logger.Error(err, "Error parsing scheduler metrics scrape interval", "interval", schedulerMetricsScrapeIntervalStr) + newConfig.SchedulerMetricsScrapeInterval = collector.DefaultSchedulerMetricsScrapeInterval + } + } else { + newConfig.SchedulerMetricsScrapeInterval = collector.DefaultSchedulerMetricsScrapeInterval + } + + // KEDA operator /metrics endpoint for the KedaScalerMetrics collector. + // Passed through verbatim; empty lets the collector apply its default + // (collector.DefaultKedaMetricsEndpoint, KEDA in devzero-system). + newConfig.KEDAMetricsEndpoint = envSpec.Policies.KEDAMetricsEndpoint + // Set cluster snapshot interval (defaults to 3h for reduced network usage) clusterSnapshotIntervalStr := envSpec.Policies.ClusterSnapshotInterval if clusterSnapshotIntervalStr != "" { @@ -828,6 +1003,61 @@ func (r *CollectionPolicyReconciler) createNewConfig( }) } + // Argo CD Applications + for _, app := range envSpec.Exclusions.ExcludedArgoCDApplications { + newConfig.ExcludedArgoCDApplications = append( + newConfig.ExcludedArgoCDApplications, + collector.ExcludedArgoCDApplication{ + Namespace: app.Namespace, + Name: app.Name, + }, + ) + } + + // Argo CD ApplicationSets + for _, appSet := range envSpec.Exclusions.ExcludedArgoCDApplicationSets { + newConfig.ExcludedArgoCDApplicationSets = append( + newConfig.ExcludedArgoCDApplicationSets, + collector.ExcludedArgoCDApplicationSet{ + Namespace: appSet.Namespace, + Name: appSet.Name, + }, + ) + } + + // Flagger Canaries + for _, canary := range envSpec.Exclusions.ExcludedFlaggerCanaries { + newConfig.ExcludedFlaggerCanaries = append( + newConfig.ExcludedFlaggerCanaries, + collector.ExcludedFlaggerCanary{ + Namespace: canary.Namespace, + Name: canary.Name, + }, + ) + } + + // Flagger MetricTemplates + for _, template := range envSpec.Exclusions.ExcludedFlaggerMetricTemplates { + newConfig.ExcludedFlaggerMetricTemplates = append( + newConfig.ExcludedFlaggerMetricTemplates, + collector.ExcludedFlaggerMetricTemplate{ + Namespace: template.Namespace, + Name: template.Name, + }, + ) + } + + // Flagger AlertProviders + for _, provider := range envSpec.Exclusions.ExcludedFlaggerAlertProviders { + newConfig.ExcludedFlaggerAlertProviders = append( + newConfig.ExcludedFlaggerAlertProviders, + collector.ExcludedFlaggerAlertProvider{ + Namespace: provider.Namespace, + Name: provider.Name, + }, + ) + } + // Events - these are special with more fields for _, event := range envSpec.Exclusions.ExcludedEvents { newConfig.ExcludedEvents = append(newConfig.ExcludedEvents, collector.ExcludedEvent{ @@ -1004,6 +1234,14 @@ func (r *CollectionPolicyReconciler) identifyAffectedCollectors( affectedCollectors["cluster_snapshot"] = true } + if oldConfig.SchedulerMetricsScrapeInterval != newConfig.SchedulerMetricsScrapeInterval { + affectedCollectors["scheduler_placement_metric"] = true + } + + if oldConfig.KEDAMetricsEndpoint != newConfig.KEDAMetricsEndpoint { + affectedCollectors[collector.KedaScalerMetrics.String()] = true + } + if !reflect.DeepEqual(oldConfig.ExcludedCSINodes, newConfig.ExcludedCSINodes) { affectedCollectors["csi_node"] = true } @@ -1066,6 +1304,38 @@ func (r *CollectionPolicyReconciler) identifyAffectedCollectors( affectedCollectors["cnpg_cluster"] = true } + if !reflect.DeepEqual( + oldConfig.ExcludedArgoCDApplications, + newConfig.ExcludedArgoCDApplications, + ) { + affectedCollectors[collector.ArgoCDApplication.String()] = true + } + + if !reflect.DeepEqual( + oldConfig.ExcludedArgoCDApplicationSets, + newConfig.ExcludedArgoCDApplicationSets, + ) { + affectedCollectors[collector.ArgoCDApplicationSet.String()] = true + } + + if !reflect.DeepEqual(oldConfig.ExcludedFlaggerCanaries, newConfig.ExcludedFlaggerCanaries) { + affectedCollectors[collector.FlaggerCanary.String()] = true + } + + if !reflect.DeepEqual( + oldConfig.ExcludedFlaggerMetricTemplates, + newConfig.ExcludedFlaggerMetricTemplates, + ) { + affectedCollectors[collector.FlaggerMetricTemplate.String()] = true + } + + if !reflect.DeepEqual( + oldConfig.ExcludedFlaggerAlertProviders, + newConfig.ExcludedFlaggerAlertProviders, + ) { + affectedCollectors[collector.FlaggerAlertProvider.String()] = true + } + // Check if the special node collectors are affected by the update interval change if oldConfig.UpdateInterval != newConfig.UpdateInterval || oldConfig.DisableGPUMetrics != newConfig.DisableGPUMetrics { @@ -1862,6 +2132,83 @@ func (r *CollectionPolicyReconciler) restartCollectors( logger, r.TelemetryLogger, ) + case collector.ArgoCDApplication.String(): + replacedCollector = collector.NewArgoCDApplicationCollector( + r.DynamicClient, + newConfig.TargetNamespaces, + newConfig.ExcludedArgoCDApplications, + collector.DefaultMaxBatchSize, + collector.DefaultMaxBatchTime, + logger, + r.TelemetryLogger, + ) + case collector.ArgoCDApplicationSet.String(): + replacedCollector = collector.NewArgoCDApplicationSetCollector( + r.DynamicClient, + newConfig.TargetNamespaces, + newConfig.ExcludedArgoCDApplicationSets, + collector.DefaultMaxBatchSize, + collector.DefaultMaxBatchTime, + logger, + r.TelemetryLogger, + ) + case collector.FlaggerCanary.String(): + replacedCollector = collector.NewFlaggerResourceCollector( + r.DynamicClient, + collector.FlaggerCanaryGVR, + collector.FlaggerCanary, + newConfig.TargetNamespaces, + newConfig.ExcludedFlaggerCanaries, + collector.DefaultMaxBatchSize, + collector.DefaultMaxBatchTime, + logger, + r.TelemetryLogger, + ) + case collector.FlaggerMetricTemplate.String(): + replacedCollector = collector.NewFlaggerResourceCollector( + r.DynamicClient, + collector.FlaggerMetricTemplateGVR, + collector.FlaggerMetricTemplate, + newConfig.TargetNamespaces, + newConfig.ExcludedFlaggerMetricTemplates, + collector.DefaultMaxBatchSize, + collector.DefaultMaxBatchTime, + logger, + r.TelemetryLogger, + ) + case collector.FlaggerAlertProvider.String(): + replacedCollector = collector.NewFlaggerResourceCollector( + r.DynamicClient, + collector.FlaggerAlertProviderGVR, + collector.FlaggerAlertProvider, + newConfig.TargetNamespaces, + newConfig.ExcludedFlaggerAlertProviders, + collector.DefaultMaxBatchSize, + collector.DefaultMaxBatchTime, + logger, + r.TelemetryLogger, + ) + case "scheduler_placement_metric": + replacedCollector = collector.NewSchedulerMetricsCollector( + r.K8sClient, + collector.NewSchedulerMetricsHTTPClientOrFailing(logger), + newConfig.SchedulerMetricsScrapeInterval, + collector.DefaultMaxBatchSize, + collector.DefaultMaxBatchTime, + logger, + r.TelemetryLogger, + ) + case collector.KedaScalerMetrics.String(): + // Rebuild with the (possibly changed) KEDA metrics endpoint so a + // KEDA_METRICS_ENDPOINT change actually takes effect. Without this + // case the collector is stopped above but never recreated. + replacedCollector = collector.NewKedaScalerMetricsCollector( + collector.KedaScalerMetricsConfig{Endpoint: newConfig.KEDAMetricsEndpoint}, + r.KEDAClient, + r.DakrClient, + logger, + r.TelemetryLogger, + ) default: logger.Info("Collector type not handled in selective restart", "type", collectorType) continue @@ -1880,7 +2227,8 @@ func (r *CollectionPolicyReconciler) restartCollectors( } // Start the collector - if err := r.CollectionManager.StartCollector(ctx, collectorType); err != nil { + // Start by the key RegisterCollector stored it under, as retryPendingCollectors does. + if err := r.CollectionManager.StartCollector(ctx, replacedCollector.GetType()); err != nil { logger.Error(err, "Failed to start collector", "type", collectorType) r.TelemetryLogger.Report( gen.LogLevel_LOG_LEVEL_ERROR, @@ -1893,6 +2241,7 @@ func (r *CollectionPolicyReconciler) restartCollectors( "zxporter_version": version.Get().String(), }, ) + r.requeueFailedStart(logger, replacedCollector.GetType(), collectorType, metricsClient) } else { r.TelemetryLogger.Report( gen.LogLevel_LOG_LEVEL_INFO, @@ -1907,24 +2256,9 @@ func (r *CollectionPolicyReconciler) restartCollectors( ) logger.Info("Successfully restarted collector", "type", collectorType) - // Re-wire MPA publisher and OOM reconciler into replaced PodCollector - if collectorType == "pod" { - r.wireMpaPublisherIntoPodCollector() - r.wireOOMReconcilerIntoPodCollector() - } - - // A replaced EventCollector is a fresh object and has lost its disruption - // sources. The PDB / node / Karpenter collectors need no equivalent handling: - // the EventCollector resolves those through the CollectionManager on every - // event, so replacing one of them is picked up without re-wiring. - if collectorType == "event" { - r.wireDisruptionSourcesIntoEventCollector() - } - - // Same for a replaced NodeCollector and its NodeClaim lookup. - if collectorType == "node" { - r.wireNodeClaimSourceIntoNodeCollector() - } + // Re-wire the MPA publisher / OOM reconciler (pod), disruption sources + // (event) and NodeClaim lookup (node) into the replacement. + r.rewireCollector(collectorType) } } @@ -2601,13 +2935,108 @@ func (r *CollectionPolicyReconciler) registerResourceCollectors( disabledCollectorsMap[collectorType] = true } - // List of collectors to register - // factory is non-nil for CRD-based collectors that may not be available at startup - collectors := []struct { - collector collector.ResourceCollector - name collector.ResourceType - factory func() collector.ResourceCollector - }{ + collectors := r.resourceCollectorSpecs(logger, config, metricsClient) + + // Register all collectors + for _, c := range collectors { + if disabledCollectorsMap[c.name.String()] { + r.TelemetryLogger.Report( + gen.LogLevel_LOG_LEVEL_WARN, + "CollectionPolicyReconciler_registerResourceCollectors", + "Collector is disabled, skipping registration", + nil, + map[string]string{ + "collector_type": c.name.String(), + "event_type": "collector_disabled", + "zxporter_version": version.Get().String(), + }, + ) + logger.Info("Skipping disabled collector", "type", c.name.String()) + continue + } + if c.collector.IsAvailable(context.Background()) { + logger.Info("Registering collector", "name", c.name.String()) + if err := r.CollectionManager.RegisterCollector(c.collector); err != nil { + r.TelemetryLogger.Report( + gen.LogLevel_LOG_LEVEL_ERROR, + "CollectionPolicyReconciler_registerResourceCollectors", + "Failed to register collector", + err, + map[string]string{ + "collector_type": c.name.String(), + "error_type": "collector_registration_failed", + "zxporter_version": version.Get().String(), + }, + ) + logger.Error(err, "Failed to register collector", "collector", c.name.String()) + } else { + r.TelemetryLogger.Report( + gen.LogLevel_LOG_LEVEL_INFO, + "CollectionPolicyReconciler_registerResourceCollectors", + "Successfully registered collector", + nil, + map[string]string{ + "collector_type": c.name.String(), + "event_type": "collector_registration_success", + "zxporter_version": version.Get().String(), + }, + ) + logger.Info("Registered collector", "collector", c.name.String()) + } + } else { + r.TelemetryLogger.Report( + gen.LogLevel_LOG_LEVEL_WARN, + "CollectionPolicyReconciler_registerResourceCollectors", + "Collector is not available, skipping registration", + nil, + map[string]string{ // Note: we should get the reason why it's not available + "collector_type": c.name.String(), + "event_type": "collector_not_available", + "zxporter_version": version.Get().String(), + }, + ) + logger.Info("Collector not available, skipping registration", "type", c.name.String()) + + // Track CRD-based collectors for deferred retry when their CRDs become available + if c.factory != nil { + r.pendingMu.Lock() + r.pendingCollectors = append(r.pendingCollectors, pendingCollector{ + name: c.name, + factory: c.factory, + }) + r.pendingMu.Unlock() + logger.Info("Added collector to pending retry list", "type", c.name.String()) + } + } + } + + return nil +} + +// resourceCollectorSpec is one entry of the collector set registerResourceCollectors +// installs. +// +// Invariant: collector.GetType() (and factory().GetType(), when factory is set) equals +// name.String(). The CollectionManager keys collectors by GetType(), while the reconciler +// starts, restarts and disables them by name, so any drift makes those lookups miss. +// TestResourceCollectorSpecs_TypeMatchesName enforces it. +type resourceCollectorSpec struct { + collector collector.ResourceCollector + name collector.ResourceType + // factory is non-nil for CRD-based collectors that may not be available at startup; + // it builds a fresh collector for the deferred retry in retryPendingCollectors. + factory func() collector.ResourceCollector +} + +// resourceCollectorSpecs builds the list of collectors to register. +// +//nolint:gocyclo +func (r *CollectionPolicyReconciler) resourceCollectorSpecs( + logger logr.Logger, + config *PolicyConfig, + metricsClient *metricsv1.Clientset, +) []resourceCollectorSpec { + return []resourceCollectorSpec{ { collector: collector.NewEndpointCollector( r.K8sClient, @@ -3146,6 +3575,29 @@ func (r *CollectionPolicyReconciler) registerResourceCollectors( ) }, }, + { + // KedaScalerMetrics scrapes the KEDA operator /metrics endpoint and + // forwards per-trigger scaler values directly to dakr via + // SendScalerMetrics. IsAvailable returns false when the endpoint is + // unreachable so the collector is silently skipped on non-KEDA clusters. + collector: collector.NewKedaScalerMetricsCollector( + collector.KedaScalerMetricsConfig{Endpoint: config.KEDAMetricsEndpoint}, + r.KEDAClient, + r.DakrClient, + logger, + r.TelemetryLogger, + ), + name: collector.KedaScalerMetrics, + factory: func() collector.ResourceCollector { + return collector.NewKedaScalerMetricsCollector( + collector.KedaScalerMetricsConfig{Endpoint: config.KEDAMetricsEndpoint}, + r.KEDAClient, + r.DakrClient, + logger, + r.TelemetryLogger, + ) + }, + }, { collector: snap.NewClusterSnapshotter( r.K8sClient, @@ -3344,6 +3796,212 @@ func (r *CollectionPolicyReconciler) registerResourceCollectors( ), name: collector.CNPGCluster, }, + { + // factory is set: Argo CD is frequently installed after zxporter, so + // the CRD can be absent at startup and appear later. + collector: collector.NewArgoCDApplicationCollector( + r.DynamicClient, + config.TargetNamespaces, + config.ExcludedArgoCDApplications, + collector.DefaultMaxBatchSize, + collector.DefaultMaxBatchTime, + logger, + r.TelemetryLogger, + ), + name: collector.ArgoCDApplication, + factory: func() collector.ResourceCollector { + return collector.NewArgoCDApplicationCollector( + r.DynamicClient, + config.TargetNamespaces, + config.ExcludedArgoCDApplications, + collector.DefaultMaxBatchSize, + collector.DefaultMaxBatchTime, + logger, + r.TelemetryLogger, + ) + }, + }, + { + collector: collector.NewArgoCDApplicationSetCollector( + r.DynamicClient, + config.TargetNamespaces, + config.ExcludedArgoCDApplicationSets, + collector.DefaultMaxBatchSize, + collector.DefaultMaxBatchTime, + logger, + r.TelemetryLogger, + ), + name: collector.ArgoCDApplicationSet, + factory: func() collector.ResourceCollector { + return collector.NewArgoCDApplicationSetCollector( + r.DynamicClient, + config.TargetNamespaces, + config.ExcludedArgoCDApplicationSets, + collector.DefaultMaxBatchSize, + collector.DefaultMaxBatchTime, + logger, + r.TelemetryLogger, + ) + }, + }, + // FluxCD GitOps kinds. factory is set for every one of them: like Argo + // CD, Flux (and especially the Flux Operator and image automation + // controllers, which are optional add-ons) is frequently installed + // after zxporter, so the CRD can be absent at startup and appear + // later. All eighteen share FluxResourceCollector -- see its doc + // comment for why a generic collector replaces per-kind files here. + { + collector: collector.NewFluxResourceCollector(r.DynamicClient, collector.FluxGitRepositoryGVR, collector.FluxGitRepository, config.TargetNamespaces, collector.DefaultMaxBatchSize, collector.DefaultMaxBatchTime, logger, r.TelemetryLogger), + name: collector.FluxGitRepository, + factory: func() collector.ResourceCollector { + return collector.NewFluxResourceCollector(r.DynamicClient, collector.FluxGitRepositoryGVR, collector.FluxGitRepository, config.TargetNamespaces, collector.DefaultMaxBatchSize, collector.DefaultMaxBatchTime, logger, r.TelemetryLogger) + }, + }, + { + collector: collector.NewFluxResourceCollector(r.DynamicClient, collector.FluxOCIRepositoryGVR, collector.FluxOCIRepository, config.TargetNamespaces, collector.DefaultMaxBatchSize, collector.DefaultMaxBatchTime, logger, r.TelemetryLogger), + name: collector.FluxOCIRepository, + factory: func() collector.ResourceCollector { + return collector.NewFluxResourceCollector(r.DynamicClient, collector.FluxOCIRepositoryGVR, collector.FluxOCIRepository, config.TargetNamespaces, collector.DefaultMaxBatchSize, collector.DefaultMaxBatchTime, logger, r.TelemetryLogger) + }, + }, + { + collector: collector.NewFluxResourceCollector(r.DynamicClient, collector.FluxHelmRepositoryGVR, collector.FluxHelmRepository, config.TargetNamespaces, collector.DefaultMaxBatchSize, collector.DefaultMaxBatchTime, logger, r.TelemetryLogger), + name: collector.FluxHelmRepository, + factory: func() collector.ResourceCollector { + return collector.NewFluxResourceCollector(r.DynamicClient, collector.FluxHelmRepositoryGVR, collector.FluxHelmRepository, config.TargetNamespaces, collector.DefaultMaxBatchSize, collector.DefaultMaxBatchTime, logger, r.TelemetryLogger) + }, + }, + { + collector: collector.NewFluxResourceCollector(r.DynamicClient, collector.FluxHelmChartGVR, collector.FluxHelmChart, config.TargetNamespaces, collector.DefaultMaxBatchSize, collector.DefaultMaxBatchTime, logger, r.TelemetryLogger), + name: collector.FluxHelmChart, + factory: func() collector.ResourceCollector { + return collector.NewFluxResourceCollector(r.DynamicClient, collector.FluxHelmChartGVR, collector.FluxHelmChart, config.TargetNamespaces, collector.DefaultMaxBatchSize, collector.DefaultMaxBatchTime, logger, r.TelemetryLogger) + }, + }, + { + collector: collector.NewFluxResourceCollector(r.DynamicClient, collector.FluxBucketGVR, collector.FluxBucket, config.TargetNamespaces, collector.DefaultMaxBatchSize, collector.DefaultMaxBatchTime, logger, r.TelemetryLogger), + name: collector.FluxBucket, + factory: func() collector.ResourceCollector { + return collector.NewFluxResourceCollector(r.DynamicClient, collector.FluxBucketGVR, collector.FluxBucket, config.TargetNamespaces, collector.DefaultMaxBatchSize, collector.DefaultMaxBatchTime, logger, r.TelemetryLogger) + }, + }, + { + collector: collector.NewFluxResourceCollector(r.DynamicClient, collector.FluxKustomizationGVR, collector.FluxKustomization, config.TargetNamespaces, collector.DefaultMaxBatchSize, collector.DefaultMaxBatchTime, logger, r.TelemetryLogger), + name: collector.FluxKustomization, + factory: func() collector.ResourceCollector { + return collector.NewFluxResourceCollector(r.DynamicClient, collector.FluxKustomizationGVR, collector.FluxKustomization, config.TargetNamespaces, collector.DefaultMaxBatchSize, collector.DefaultMaxBatchTime, logger, r.TelemetryLogger) + }, + }, + { + collector: collector.NewFluxResourceCollector(r.DynamicClient, collector.FluxHelmReleaseGVR, collector.FluxHelmRelease, config.TargetNamespaces, collector.DefaultMaxBatchSize, collector.DefaultMaxBatchTime, logger, r.TelemetryLogger), + name: collector.FluxHelmRelease, + factory: func() collector.ResourceCollector { + return collector.NewFluxResourceCollector(r.DynamicClient, collector.FluxHelmReleaseGVR, collector.FluxHelmRelease, config.TargetNamespaces, collector.DefaultMaxBatchSize, collector.DefaultMaxBatchTime, logger, r.TelemetryLogger) + }, + }, + { + collector: collector.NewFluxResourceCollector(r.DynamicClient, collector.FluxExternalArtifactGVR, collector.FluxExternalArtifact, config.TargetNamespaces, collector.DefaultMaxBatchSize, collector.DefaultMaxBatchTime, logger, r.TelemetryLogger), + name: collector.FluxExternalArtifact, + factory: func() collector.ResourceCollector { + return collector.NewFluxResourceCollector(r.DynamicClient, collector.FluxExternalArtifactGVR, collector.FluxExternalArtifact, config.TargetNamespaces, collector.DefaultMaxBatchSize, collector.DefaultMaxBatchTime, logger, r.TelemetryLogger) + }, + }, + { + collector: collector.NewFluxResourceCollector(r.DynamicClient, collector.FluxAlertGVR, collector.FluxAlert, config.TargetNamespaces, collector.DefaultMaxBatchSize, collector.DefaultMaxBatchTime, logger, r.TelemetryLogger), + name: collector.FluxAlert, + factory: func() collector.ResourceCollector { + return collector.NewFluxResourceCollector(r.DynamicClient, collector.FluxAlertGVR, collector.FluxAlert, config.TargetNamespaces, collector.DefaultMaxBatchSize, collector.DefaultMaxBatchTime, logger, r.TelemetryLogger) + }, + }, + { + collector: collector.NewFluxResourceCollector(r.DynamicClient, collector.FluxProviderGVR, collector.FluxProvider, config.TargetNamespaces, collector.DefaultMaxBatchSize, collector.DefaultMaxBatchTime, logger, r.TelemetryLogger), + name: collector.FluxProvider, + factory: func() collector.ResourceCollector { + return collector.NewFluxResourceCollector(r.DynamicClient, collector.FluxProviderGVR, collector.FluxProvider, config.TargetNamespaces, collector.DefaultMaxBatchSize, collector.DefaultMaxBatchTime, logger, r.TelemetryLogger) + }, + }, + { + collector: collector.NewFluxResourceCollector(r.DynamicClient, collector.FluxReceiverGVR, collector.FluxReceiver, config.TargetNamespaces, collector.DefaultMaxBatchSize, collector.DefaultMaxBatchTime, logger, r.TelemetryLogger), + name: collector.FluxReceiver, + factory: func() collector.ResourceCollector { + return collector.NewFluxResourceCollector(r.DynamicClient, collector.FluxReceiverGVR, collector.FluxReceiver, config.TargetNamespaces, collector.DefaultMaxBatchSize, collector.DefaultMaxBatchTime, logger, r.TelemetryLogger) + }, + }, + { + collector: collector.NewFluxResourceCollector(r.DynamicClient, collector.FluxInstanceGVR, collector.FluxInstance, config.TargetNamespaces, collector.DefaultMaxBatchSize, collector.DefaultMaxBatchTime, logger, r.TelemetryLogger), + name: collector.FluxInstance, + factory: func() collector.ResourceCollector { + return collector.NewFluxResourceCollector(r.DynamicClient, collector.FluxInstanceGVR, collector.FluxInstance, config.TargetNamespaces, collector.DefaultMaxBatchSize, collector.DefaultMaxBatchTime, logger, r.TelemetryLogger) + }, + }, + { + collector: collector.NewFluxResourceCollector(r.DynamicClient, collector.FluxReportGVR, collector.FluxReport, config.TargetNamespaces, collector.DefaultMaxBatchSize, collector.DefaultMaxBatchTime, logger, r.TelemetryLogger), + name: collector.FluxReport, + factory: func() collector.ResourceCollector { + return collector.NewFluxResourceCollector(r.DynamicClient, collector.FluxReportGVR, collector.FluxReport, config.TargetNamespaces, collector.DefaultMaxBatchSize, collector.DefaultMaxBatchTime, logger, r.TelemetryLogger) + }, + }, + { + collector: collector.NewFluxResourceCollector(r.DynamicClient, collector.FluxResourceSetInputProviderGVR, collector.FluxResourceSetInputProvider, config.TargetNamespaces, collector.DefaultMaxBatchSize, collector.DefaultMaxBatchTime, logger, r.TelemetryLogger), + name: collector.FluxResourceSetInputProvider, + factory: func() collector.ResourceCollector { + return collector.NewFluxResourceCollector(r.DynamicClient, collector.FluxResourceSetInputProviderGVR, collector.FluxResourceSetInputProvider, config.TargetNamespaces, collector.DefaultMaxBatchSize, collector.DefaultMaxBatchTime, logger, r.TelemetryLogger) + }, + }, + { + collector: collector.NewFluxResourceCollector(r.DynamicClient, collector.FluxResourceSetGVR, collector.FluxResourceSet, config.TargetNamespaces, collector.DefaultMaxBatchSize, collector.DefaultMaxBatchTime, logger, r.TelemetryLogger), + name: collector.FluxResourceSet, + factory: func() collector.ResourceCollector { + return collector.NewFluxResourceCollector(r.DynamicClient, collector.FluxResourceSetGVR, collector.FluxResourceSet, config.TargetNamespaces, collector.DefaultMaxBatchSize, collector.DefaultMaxBatchTime, logger, r.TelemetryLogger) + }, + }, + { + collector: collector.NewFluxResourceCollector(r.DynamicClient, collector.FluxImageRepositoryGVR, collector.FluxImageRepository, config.TargetNamespaces, collector.DefaultMaxBatchSize, collector.DefaultMaxBatchTime, logger, r.TelemetryLogger), + name: collector.FluxImageRepository, + factory: func() collector.ResourceCollector { + return collector.NewFluxResourceCollector(r.DynamicClient, collector.FluxImageRepositoryGVR, collector.FluxImageRepository, config.TargetNamespaces, collector.DefaultMaxBatchSize, collector.DefaultMaxBatchTime, logger, r.TelemetryLogger) + }, + }, + { + collector: collector.NewFluxResourceCollector(r.DynamicClient, collector.FluxImagePolicyGVR, collector.FluxImagePolicy, config.TargetNamespaces, collector.DefaultMaxBatchSize, collector.DefaultMaxBatchTime, logger, r.TelemetryLogger), + name: collector.FluxImagePolicy, + factory: func() collector.ResourceCollector { + return collector.NewFluxResourceCollector(r.DynamicClient, collector.FluxImagePolicyGVR, collector.FluxImagePolicy, config.TargetNamespaces, collector.DefaultMaxBatchSize, collector.DefaultMaxBatchTime, logger, r.TelemetryLogger) + }, + }, + { + collector: collector.NewFluxResourceCollector(r.DynamicClient, collector.FluxImageUpdateAutomationGVR, collector.FluxImageUpdateAutomation, config.TargetNamespaces, collector.DefaultMaxBatchSize, collector.DefaultMaxBatchTime, logger, r.TelemetryLogger), + name: collector.FluxImageUpdateAutomation, + factory: func() collector.ResourceCollector { + return collector.NewFluxResourceCollector(r.DynamicClient, collector.FluxImageUpdateAutomationGVR, collector.FluxImageUpdateAutomation, config.TargetNamespaces, collector.DefaultMaxBatchSize, collector.DefaultMaxBatchTime, logger, r.TelemetryLogger) + }, + }, + // Flagger progressive-delivery kinds. factory is set for all three: + // Flagger is an optional add-on, so its CRDs are frequently absent at + // startup and appear later. All three share FlaggerResourceCollector -- + // see its doc comment for why one generic collector replaces three + // per-kind files here. + { + collector: collector.NewFlaggerResourceCollector(r.DynamicClient, collector.FlaggerCanaryGVR, collector.FlaggerCanary, config.TargetNamespaces, config.ExcludedFlaggerCanaries, collector.DefaultMaxBatchSize, collector.DefaultMaxBatchTime, logger, r.TelemetryLogger), + name: collector.FlaggerCanary, + factory: func() collector.ResourceCollector { + return collector.NewFlaggerResourceCollector(r.DynamicClient, collector.FlaggerCanaryGVR, collector.FlaggerCanary, config.TargetNamespaces, config.ExcludedFlaggerCanaries, collector.DefaultMaxBatchSize, collector.DefaultMaxBatchTime, logger, r.TelemetryLogger) + }, + }, + { + collector: collector.NewFlaggerResourceCollector(r.DynamicClient, collector.FlaggerMetricTemplateGVR, collector.FlaggerMetricTemplate, config.TargetNamespaces, config.ExcludedFlaggerMetricTemplates, collector.DefaultMaxBatchSize, collector.DefaultMaxBatchTime, logger, r.TelemetryLogger), + name: collector.FlaggerMetricTemplate, + factory: func() collector.ResourceCollector { + return collector.NewFlaggerResourceCollector(r.DynamicClient, collector.FlaggerMetricTemplateGVR, collector.FlaggerMetricTemplate, config.TargetNamespaces, config.ExcludedFlaggerMetricTemplates, collector.DefaultMaxBatchSize, collector.DefaultMaxBatchTime, logger, r.TelemetryLogger) + }, + }, + { + collector: collector.NewFlaggerResourceCollector(r.DynamicClient, collector.FlaggerAlertProviderGVR, collector.FlaggerAlertProvider, config.TargetNamespaces, config.ExcludedFlaggerAlertProviders, collector.DefaultMaxBatchSize, collector.DefaultMaxBatchTime, logger, r.TelemetryLogger), + name: collector.FlaggerAlertProvider, + factory: func() collector.ResourceCollector { + return collector.NewFlaggerResourceCollector(r.DynamicClient, collector.FlaggerAlertProviderGVR, collector.FlaggerAlertProvider, config.TargetNamespaces, config.ExcludedFlaggerAlertProviders, collector.DefaultMaxBatchSize, collector.DefaultMaxBatchTime, logger, r.TelemetryLogger) + }, + }, { collector: collector.NewKyvernoPolicyCollector( r.DynamicClient, @@ -3426,6 +4084,29 @@ func (r *CollectionPolicyReconciler) registerResourceCollectors( ) }, }, + { + collector: collector.NewSchedulerMetricsCollector( + r.K8sClient, + collector.NewSchedulerMetricsHTTPClientOrFailing(logger), + config.SchedulerMetricsScrapeInterval, + collector.DefaultMaxBatchSize, + collector.DefaultMaxBatchTime, + logger, + r.TelemetryLogger, + ), + name: collector.SchedulerPlacementMetric, + factory: func() collector.ResourceCollector { + return collector.NewSchedulerMetricsCollector( + r.K8sClient, + collector.NewSchedulerMetricsHTTPClientOrFailing(logger), + config.SchedulerMetricsScrapeInterval, + collector.DefaultMaxBatchSize, + collector.DefaultMaxBatchTime, + logger, + r.TelemetryLogger, + ) + }, + }, { collector: collector.NewMigPartedConfigCollector( r.K8sClient, @@ -3449,81 +4130,6 @@ func (r *CollectionPolicyReconciler) registerResourceCollectors( name: collector.ClusterAutoscalerStatus, }, } - - // Register all collectors - for _, c := range collectors { - if disabledCollectorsMap[c.name.String()] { - r.TelemetryLogger.Report( - gen.LogLevel_LOG_LEVEL_WARN, - "CollectionPolicyReconciler_registerResourceCollectors", - "Collector is disabled, skipping registration", - nil, - map[string]string{ - "collector_type": c.name.String(), - "event_type": "collector_disabled", - "zxporter_version": version.Get().String(), - }, - ) - logger.Info("Skipping disabled collector", "type", c.name.String()) - continue - } - if c.collector.IsAvailable(context.Background()) { - logger.Info("Registering collector", "name", c.name.String()) - if err := r.CollectionManager.RegisterCollector(c.collector); err != nil { - r.TelemetryLogger.Report( - gen.LogLevel_LOG_LEVEL_ERROR, - "CollectionPolicyReconciler_registerResourceCollectors", - "Failed to register collector", - err, - map[string]string{ - "collector_type": c.name.String(), - "error_type": "collector_registration_failed", - "zxporter_version": version.Get().String(), - }, - ) - logger.Error(err, "Failed to register collector", "collector", c.name.String()) - } else { - r.TelemetryLogger.Report( - gen.LogLevel_LOG_LEVEL_INFO, - "CollectionPolicyReconciler_registerResourceCollectors", - "Successfully registered collector", - nil, - map[string]string{ - "collector_type": c.name.String(), - "event_type": "collector_registration_success", - "zxporter_version": version.Get().String(), - }, - ) - logger.Info("Registered collector", "collector", c.name.String()) - } - } else { - r.TelemetryLogger.Report( - gen.LogLevel_LOG_LEVEL_WARN, - "CollectionPolicyReconciler_registerResourceCollectors", - "Collector is not available, skipping registration", - nil, - map[string]string{ // Note: we should get the reason why it's not available - "collector_type": c.name.String(), - "event_type": "collector_not_available", - "zxporter_version": version.Get().String(), - }, - ) - logger.Info("Collector not available, skipping registration", "type", c.name.String()) - - // Track CRD-based collectors for deferred retry when their CRDs become available - if c.factory != nil { - r.pendingMu.Lock() - r.pendingCollectors = append(r.pendingCollectors, pendingCollector{ - name: c.name, - factory: c.factory, - }) - r.pendingMu.Unlock() - logger.Info("Added collector to pending retry list", "type", c.name.String()) - } - } - } - - return nil } // processCollectedResources reads from collection channel and forwards to sender @@ -4201,6 +4807,62 @@ func (r *CollectionPolicyReconciler) handleDisabledCollectorsChange( logger, r.TelemetryLogger, ) + case collector.ArgoCDApplication.String(): + replacedCollector = collector.NewArgoCDApplicationCollector( + r.DynamicClient, + newConfig.TargetNamespaces, + newConfig.ExcludedArgoCDApplications, + collector.DefaultMaxBatchSize, + collector.DefaultMaxBatchTime, + logger, + r.TelemetryLogger, + ) + case collector.ArgoCDApplicationSet.String(): + replacedCollector = collector.NewArgoCDApplicationSetCollector( + r.DynamicClient, + newConfig.TargetNamespaces, + newConfig.ExcludedArgoCDApplicationSets, + collector.DefaultMaxBatchSize, + collector.DefaultMaxBatchTime, + logger, + r.TelemetryLogger, + ) + case collector.FlaggerCanary.String(): + replacedCollector = collector.NewFlaggerResourceCollector( + r.DynamicClient, + collector.FlaggerCanaryGVR, + collector.FlaggerCanary, + newConfig.TargetNamespaces, + newConfig.ExcludedFlaggerCanaries, + collector.DefaultMaxBatchSize, + collector.DefaultMaxBatchTime, + logger, + r.TelemetryLogger, + ) + case collector.FlaggerMetricTemplate.String(): + replacedCollector = collector.NewFlaggerResourceCollector( + r.DynamicClient, + collector.FlaggerMetricTemplateGVR, + collector.FlaggerMetricTemplate, + newConfig.TargetNamespaces, + newConfig.ExcludedFlaggerMetricTemplates, + collector.DefaultMaxBatchSize, + collector.DefaultMaxBatchTime, + logger, + r.TelemetryLogger, + ) + case collector.FlaggerAlertProvider.String(): + replacedCollector = collector.NewFlaggerResourceCollector( + r.DynamicClient, + collector.FlaggerAlertProviderGVR, + collector.FlaggerAlertProvider, + newConfig.TargetNamespaces, + newConfig.ExcludedFlaggerAlertProviders, + collector.DefaultMaxBatchSize, + collector.DefaultMaxBatchTime, + logger, + r.TelemetryLogger, + ) default: logger.Info("Unknown collector type, skipping", "type", collectorType) continue @@ -4222,7 +4884,8 @@ func (r *CollectionPolicyReconciler) handleDisabledCollectorsChange( continue } - if err := r.CollectionManager.StartCollector(ctx, collectorType); err != nil { + // Start by the key RegisterCollector stored it under, as retryPendingCollectors does. + if err := r.CollectionManager.StartCollector(ctx, replacedCollector.GetType()); err != nil { logger.Error(err, "Failed to start collector", "type", collectorType) r.TelemetryLogger.Report( gen.LogLevel_LOG_LEVEL_ERROR, @@ -4234,7 +4897,10 @@ func (r *CollectionPolicyReconciler) handleDisabledCollectorsChange( "zxporter_version": version.Get().String(), }, ) + r.requeueFailedStart(logger, replacedCollector.GetType(), collectorType, metricsClient) + continue } + r.rewireCollector(collectorType) } } diff --git a/internal/controller/collector_retry_and_alias_test.go b/internal/controller/collector_retry_and_alias_test.go new file mode 100644 index 00000000..4f973a00 --- /dev/null +++ b/internal/controller/collector_retry_and_alias_test.go @@ -0,0 +1,143 @@ +package controller + +import ( + "context" + "errors" + "os" + "path/filepath" + "sync/atomic" + "testing" + + monitoringv1 "github.com/devzero-inc/zxporter/api/v1" + "github.com/devzero-inc/zxporter/internal/collector" + "github.com/go-logr/logr" +) + +// flakyCollector fails Start while failStarts is positive, then succeeds. +type flakyCollector struct { + keyedCollector + failStarts *atomic.Int32 +} + +func (c *flakyCollector) Start(context.Context) error { + if c.failStarts.Add(-1) >= 0 { + return errors.New("transient start failure") + } + return nil +} + +// TestRetryPendingCollectors_RetriesAfterFailedStart: a pending collector whose first +// Start fails must be retried on the next reconcile. The failed instance used to stay +// registered while its pending entry was dropped, so the next attempt could not register +// a fresh one ("already registered") and the collector stayed off until the pod +// restarted. +func TestRetryPendingCollectors_RetriesAfterFailedStart(t *testing.T) { + mgr := newTestCollectionManager() + r := &CollectionPolicyReconciler{Log: logr.Discard(), CollectionManager: mgr} + failStarts := &atomic.Int32{} + failStarts.Store(1) + r.pendingCollectors = []pendingCollector{{ + name: collector.KedaScaledJob, + factory: func() collector.ResourceCollector { + return &flakyCollector{ + keyedCollector: keyedCollector{ + collectorType: collector.KedaScaledJob.String(), + resourceCh: make(chan []collector.CollectedResource, 1), + }, + failStarts: failStarts, + } + }, + }} + key := collector.KedaScaledJob.String() + + r.retryPendingCollectors(context.Background()) + if mgr.IsCollectorRunning(key) { + t.Fatal("precondition: the first Start was meant to fail") + } + if len(r.pendingCollectors) != 1 { + t.Fatalf("a failed start must stay pending, got %d entries", len(r.pendingCollectors)) + } + + r.retryPendingCollectors(context.Background()) + if !mgr.IsCollectorRunning(key) { + t.Fatal("the retry after a failed start did not register and start the collector") + } + if len(r.pendingCollectors) != 0 { + t.Errorf("a started collector must leave the pending list, got %d entries", len(r.pendingCollectors)) + } + _ = mgr.StopCollector(key) +} + +// writeTestKubeconfig points KUBECONFIG at an unreachable cluster, because +// handleDisabledCollectorsChange calls ctrl.GetConfigOrDie, which exits the process when +// no configuration can be found. +func writeTestKubeconfig(t *testing.T) { + t.Helper() + path := filepath.Join(t.TempDir(), "kubeconfig") + const kubeconfig = `apiVersion: v1 +kind: Config +clusters: +- cluster: {server: "http://127.0.0.1:1"} + name: test +contexts: +- context: {cluster: test, user: test} + name: test +current-context: test +users: +- name: test + user: {} +` + if err := os.WriteFile(path, []byte(kubeconfig), 0o600); err != nil { + t.Fatal(err) + } + t.Setenv("KUBECONFIG", path) +} + +// TestDisabledCollectors_LegacySpellingsStillDisable pins that every spelling older +// releases accepted (the API doc advertised "statefulset" and "daemonset"; the others +// were those collectors' registry keys) still disables its collector, both at startup +// (registerResourceCollectors compares the parsed list with the canonical name) and when +// the list changes at runtime (handleDisabledCollectorsChange compares it with the +// registry key). +func TestDisabledCollectors_LegacySpellingsStillDisable(t *testing.T) { + writeTestKubeconfig(t) + + cases := map[string]collector.ResourceType{ + "statefulset": collector.StatefulSet, + "daemonset": collector.DaemonSet, + "persistentvolume": collector.PersistentVolume, + "persistentvolumeclaim": collector.PersistentVolumeClaim, + "crd": collector.CustomResourceDefinition, + "scaledjob": collector.KedaScaledJob, + "scaledobject": collector.KedaScaledObject, + } + for legacy, resourceType := range cases { + canonical := resourceType.String() + t.Run(legacy, func(t *testing.T) { + r := &CollectionPolicyReconciler{ + Log: logr.Discard(), + CollectionManager: newTestCollectionManager(), + TelemetryLogger: noopTelemetryLogger{}, + } + spec := &monitoringv1.CollectionPolicySpec{ + Policies: monitoringv1.Policies{DisabledCollectors: []string{legacy}}, + } + newConfig, _ := r.createNewConfig(spec, logr.Discard()) + + if len(newConfig.DisabledCollectors) != 1 || newConfig.DisabledCollectors[0] != canonical { + t.Fatalf("DisabledCollectors = %v, want [%q]", newConfig.DisabledCollectors, canonical) + } + + running := &keyedCollector{collectorType: canonical, resourceCh: make(chan []collector.CollectedResource, 1)} + if err := r.CollectionManager.RegisterCollector(running); err != nil { + t.Fatal(err) + } + if err := r.handleDisabledCollectorsChange(context.Background(), logr.Discard(), &PolicyConfig{}, newConfig); err != nil { + t.Fatal(err) + } + if r.CollectionManager.GetCollector(canonical) != nil { + t.Errorf("disabledCollectors: [%q] did not deregister the %q collector", legacy, canonical) + } + }) + } +} diff --git a/internal/controller/collector_specs_test.go b/internal/controller/collector_specs_test.go new file mode 100644 index 00000000..a8414c56 --- /dev/null +++ b/internal/controller/collector_specs_test.go @@ -0,0 +1,97 @@ +package controller + +import ( + "testing" + + kedaclient "github.com/kedacore/keda/v2/pkg/generated/clientset/versioned" + apiextensionsclientset "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset" + "k8s.io/client-go/discovery" + "k8s.io/client-go/dynamic" + "k8s.io/client-go/kubernetes" + "k8s.io/client-go/metadata" + "k8s.io/client-go/rest" + metricsv1 "k8s.io/metrics/pkg/client/clientset/versioned" + + "github.com/go-logr/logr" +) + +// newSpecTestReconciler builds a reconciler whose clients point at an address nothing +// listens on. resourceCollectorSpecs only constructs collectors; none of them is started, +// so no request is ever made. +func newSpecTestReconciler(t *testing.T) (*CollectionPolicyReconciler, *metricsv1.Clientset) { + t.Helper() + cfg := &rest.Config{Host: "http://127.0.0.1:1"} + must := func(err error) { + t.Helper() + if err != nil { + t.Fatalf("building client: %v", err) + } + } + + k8s, err := kubernetes.NewForConfig(cfg) + must(err) + dyn, err := dynamic.NewForConfig(cfg) + must(err) + meta, err := metadata.NewForConfig(cfg) + must(err) + disc, err := discovery.NewDiscoveryClientForConfig(cfg) + must(err) + apiext, err := apiextensionsclientset.NewForConfig(cfg) + must(err) + keda, err := kedaclient.NewForConfig(cfg) + must(err) + metrics, err := metricsv1.NewForConfig(cfg) + must(err) + + return &CollectionPolicyReconciler{ + Log: logr.Discard(), + K8sClient: k8s, + DynamicClient: dyn, + MetadataClient: meta, + DiscoveryClient: disc, + ApiExtensions: apiext, + KEDAClient: keda, + TelemetryLogger: noopTelemetryLogger{}, + TelemetryMetrics: getTestTelemetryMetrics(), + CollectionManager: newTestCollectionManager(), + }, metrics +} + +// TestResourceCollectorSpecs_TypeMatchesName pins, for every collector the reconciler +// registers, that the key the CollectionManager stores it under (GetType) is the +// ResourceType name the reconciler later starts, restarts and disables it by. When the +// two drift, lookups by name miss: the KEDA ScaledJob/ScaledObject collectors returned +// "scaledjob"/"scaledobject", so their deferred start after the KEDA CRDs appeared failed +// as "not registered" — the path that leaked the manager mutex on 2026-09-25 — and +// DISABLE_COLLECTORS=keda_scaled_job could never deregister them at runtime. +// +// Factory-built collectors are checked separately because the deferred retry registers +// the factory's output, not the eagerly built instance. +func TestResourceCollectorSpecs_TypeMatchesName(t *testing.T) { + r, metricsClient := newSpecTestReconciler(t) + specs := r.resourceCollectorSpecs(logr.Discard(), &PolicyConfig{}, metricsClient) + if len(specs) == 0 { + t.Fatal("no collector specs returned") + } + + seen := make(map[string]bool, len(specs)) + for _, spec := range specs { + name := spec.name.String() + t.Run(name, func(t *testing.T) { + if seen[name] { + t.Errorf("ResourceType %q is listed twice", name) + } + seen[name] = true + + if got := spec.collector.GetType(); got != name { + t.Errorf("collector.GetType() = %q, want %q (the name it is started and disabled by)", got, name) + } + if spec.factory != nil { + if got := spec.factory().GetType(); got != name { + t.Errorf("factory().GetType() = %q, want %q (retryPendingCollectors registers the factory's output)", + got, name) + } + } + }) + } +} diff --git a/internal/controller/custom.go b/internal/controller/custom.go index a395f296..26d40b3f 100644 --- a/internal/controller/custom.go +++ b/internal/controller/custom.go @@ -401,54 +401,50 @@ func (c *EnvBasedController) initializeTelemetryComponents(ctx context.Context) // Continue with default values } - // Handle PAT token exchange if no cluster token is available - if envSpec.Policies.ClusterToken == "" && envSpec.Policies.PATToken != "" { - // First try to recover existing stored token - if storedToken := c.tryRecoverStoredClusterToken(ctx); storedToken != "" { - c.Log.Info("Found existing cluster token in storage, skipping PAT exchange") - envSpec.Policies.ClusterToken = storedToken - } else { - // Only do PAT exchange if no stored token found - c.Log.Info("No stored cluster token found, attempting PAT token exchange") - - // Get cluster name and provider - clusterName := envSpec.Policies.KubeContextName - if clusterName == "" { - clusterName = "zxporter-cluster" - } - - k8sProvider := "other" - if provider := os.Getenv("K8S_PROVIDER"); provider != "" { - k8sProvider = provider - } - - // Use a temporary DakrClient just for PAT exchange - dakrURL := envSpec.Policies.DakrURL - if dakrURL == "" { - dakrURL = "https://dakr.devzero.io" - } - - // Create a temporary client with empty cluster token for PAT exchange - tempClient := c.dakrClientFactory(dakrURL, "", c.Log) - - // Exchange PAT for cluster token - token, clusterId, err := tempClient.ExchangePATForClusterToken(ctx, envSpec.Policies.PATToken, clusterName, k8sProvider) - if err != nil { - c.Log.Error(err, "Failed to exchange PAT for cluster token") - return fmt.Errorf("failed to exchange PAT for cluster token: %w", err) - } - c.Log.Info("Successfully obtained cluster token", "clusterId", clusterId) - envSpec.Policies.ClusterToken = token + // Resolve the cluster token: env or the stored mirror (the live mirror + // first when the chart marks env as its snapshot), then a PAT exchange + // that re-attaches to this cluster by its kube-system UID (KUB-158). + if envSpec.Policies.ClusterToken != "" || envSpec.Policies.PATToken != "" { + clusterName := envSpec.Policies.KubeContextName + if clusterName == "" { + clusterName = "zxporter-cluster" + } + k8sProvider := "other" + if provider := util.GetConfigValue("K8S_PROVIDER"); provider != "" { + k8sProvider = provider + } + dakrURL := envSpec.Policies.DakrURL + if dakrURL == "" { + dakrURL = "https://dakr.devzero.io" + } + resolver := &clusterTokenResolver{ + k8s: c.K8sClient, + mirrorFed: envTokenIsMirrorFed(c.shouldUseSecretStorage(), util.GetConfigValue("CLUSTER_TOKEN_SOURCE")), + readStored: c.tryRecoverStoredClusterToken, + exchange: func(ctx context.Context, req transport.ClusterTokenExchangeRequest) (transport.ClusterTokenExchangeResult, error) { + return c.dakrClientFactory(dakrURL, "", c.Log).ExchangePATForClusterToken(ctx, req) + }, + log: c.Log, + } + token, err := resolver.resolve(ctx, envSpec.Policies.ClusterToken, envSpec.Policies.PATToken, + clusterName, k8sProvider, util.GetConfigValue("CLUSTER_ID")) + if err != nil { + c.Log.Error(err, "Failed to resolve cluster token") + return fmt.Errorf("failed to resolve cluster token: %w", err) } + envSpec.Policies.ClusterToken = token } - // Persist the resolved token to the runtime Secret so other operators (dakr, trezr, etc.) - // can read it via secretKeyRef. This covers both direct-token installs (where the token - // arrives via env var from devzero-zxporter-credentials and was never written to the - // runtime Secret) and PAT-exchange installs. Idempotent — safe on every startup. - if envSpec.Policies.ClusterToken != "" && c.shouldUseSecretStorage() { + // Persist the resolved token to the store this install uses: the runtime + // Secret in Secret mode, the env ConfigMap (TOKEN_CONFIGMAP_NAME) in + // ConfigMap mode. Other operators (trezr, tunr) read it from there, and the + // next start reads it back instead of exchanging the PAT again, which since + // KUB-158 would rotate the token. This covers direct-token installs (the + // token arrives via env and was never written to the store) and + // PAT-exchange installs. Idempotent, safe on every startup. + if envSpec.Policies.ClusterToken != "" { if err := c.persistClusterToken(ctx, envSpec.Policies.ClusterToken); err != nil { - c.Log.Error(err, "Failed to persist cluster token to runtime Secret") + c.Log.Error(err, "Failed to persist cluster token") } } @@ -573,17 +569,7 @@ func (c *EnvBasedController) persistClusterTokenToConfigMap( token string, ) error { // Get namespace from environment variable or use default - namespace := os.Getenv("POD_NAMESPACE") - if namespace == "" { - // Try to read from service account namespace file - if data, err := os.ReadFile("/var/run/secrets/kubernetes.io/serviceaccount/namespace"); err == nil { - namespace = strings.TrimSpace(string(data)) - } else { - // Fall back to default if all else fails - namespace = defaultNamespace - c.Log.Info("Could not determine namespace, using default", "namespace", namespace) - } - } + namespace := c.podNamespace() // Get ConfigMap name from environment variable with fallback to default configMapName := os.Getenv("TOKEN_CONFIGMAP_NAME") if configMapName == "" { @@ -626,17 +612,7 @@ func (c *EnvBasedController) persistClusterTokenToConfigMap( // persistClusterTokenToSecret persists the cluster token to a Kubernetes Secret func (c *EnvBasedController) persistClusterTokenToSecret(ctx context.Context, token string) error { // Get namespace from environment variable or use default - namespace := os.Getenv("POD_NAMESPACE") - if namespace == "" { - // Try to read from service account namespace file - if data, err := os.ReadFile("/var/run/secrets/kubernetes.io/serviceaccount/namespace"); err == nil { - namespace = strings.TrimSpace(string(data)) - } else { - // Fall back to default if all else fails - namespace = defaultNamespace - c.Log.Info("Could not determine namespace, using default", "namespace", namespace) - } - } + namespace := c.podNamespace() // Get runtime Secret name from environment variable with fallback to default // This is the Secret where exchanged tokens are stored (system-managed) @@ -724,16 +700,7 @@ func (c *EnvBasedController) tryRecoverStoredClusterToken(ctx context.Context) s // readClusterTokenFromSecret reads cluster token from runtime secret func (c *EnvBasedController) readClusterTokenFromSecret(ctx context.Context) string { // Get namespace from environment variable or use default - namespace := os.Getenv("POD_NAMESPACE") - if namespace == "" { - // Try to read from service account namespace file - if data, err := os.ReadFile("/var/run/secrets/kubernetes.io/serviceaccount/namespace"); err == nil { - namespace = strings.TrimSpace(string(data)) - } else { - // Fall back to default if all else fails - namespace = defaultNamespace - } - } + namespace := c.podNamespace() // Get runtime Secret name from environment variable with fallback to default runtimeSecretName := os.Getenv("TOKEN_RUNTIME_SECRET_NAME") @@ -795,16 +762,7 @@ func (c *EnvBasedController) readClusterTokenFromSecret(ctx context.Context) str // readClusterTokenFromConfigMap reads cluster token from configmap func (c *EnvBasedController) readClusterTokenFromConfigMap(ctx context.Context) string { // Get namespace from environment variable or use default - namespace := os.Getenv("POD_NAMESPACE") - if namespace == "" { - // Try to read from service account namespace file - if data, err := os.ReadFile("/var/run/secrets/kubernetes.io/serviceaccount/namespace"); err == nil { - namespace = strings.TrimSpace(string(data)) - } else { - // Fall back to default if all else fails - namespace = defaultNamespace - } - } + namespace := c.podNamespace() // Get ConfigMap name from environment variable with fallback to default configMapName := os.Getenv("TOKEN_CONFIGMAP_NAME") @@ -853,3 +811,14 @@ func (c *EnvBasedController) readClusterTokenFromConfigMap(ctx context.Context) c.Log.Info("No cluster token found in ConfigMap", "configMap", configMapName) return "" } + +// podNamespace is the namespace zxporter runs in. +func (c *EnvBasedController) podNamespace() string { + if ns := os.Getenv("POD_NAMESPACE"); ns != "" { + return ns + } + if data, err := os.ReadFile("/var/run/secrets/kubernetes.io/serviceaccount/namespace"); err == nil { + return strings.TrimSpace(string(data)) + } + return defaultNamespace +} diff --git a/internal/controller/pending_collectors_test.go b/internal/controller/pending_collectors_test.go new file mode 100644 index 00000000..810ab9a1 --- /dev/null +++ b/internal/controller/pending_collectors_test.go @@ -0,0 +1,91 @@ +package controller + +import ( + "context" + "sync" + "testing" + "time" + + gen "github.com/devzero-inc/zxporter/gen/api/v1" + "github.com/devzero-inc/zxporter/internal/collector" + "github.com/go-logr/logr" +) + +type noopTelemetryLogger struct{} + +func (noopTelemetryLogger) Report(gen.LogLevel, string, string, error, map[string]string) {} +func (noopTelemetryLogger) Stop() {} + +// keyedCollector is a ResourceCollector whose GetType() is whatever the test says, so a +// test can make it disagree with the ResourceType it is tracked under. +type keyedCollector struct { + collectorType string + resourceCh chan []collector.CollectedResource +} + +func (c *keyedCollector) Start(context.Context) error { return nil } +func (c *keyedCollector) Stop() error { close(c.resourceCh); return nil } +func (c *keyedCollector) GetType() string { return c.collectorType } +func (c *keyedCollector) GetResourceChannel() <-chan []collector.CollectedResource { + return c.resourceCh +} +func (c *keyedCollector) IsAvailable(context.Context) bool { return true } +func (c *keyedCollector) AddResource(resource interface{}) error { return nil } + +var ( + testTelemetryMetrics *collector.TelemetryMetrics + testTelemetryMetricsOnce sync.Once +) + +// NewTelemetryMetrics registers with the default Prometheus registry, so build it once. +func getTestTelemetryMetrics() *collector.TelemetryMetrics { + testTelemetryMetricsOnce.Do(func() { testTelemetryMetrics = collector.NewTelemetryMetrics() }) + return testTelemetryMetrics +} + +func newTestCollectionManager() *collector.CollectionManager { + return collector.NewCollectionManager( + &collector.CollectionConfig{BufferSize: 10}, + nil, + getTestTelemetryMetrics(), + logr.Discard(), + noopTelemetryLogger{}, + nil, + ) +} + +// TestRetryPendingCollectors_StartsUnderRegisteredKey covers the deferred start that runs +// once a CRD appears after zxporter started (the documented KEDA install order). The +// manager keys a collector by its GetType(); the retry used to start it by the pending +// entry's ResourceType name instead, so a collector whose two names differed was +// registered but never started — and the failed Start leaked the manager mutex. +func TestRetryPendingCollectors_StartsUnderRegisteredKey(t *testing.T) { + mgr := newTestCollectionManager() + r := &CollectionPolicyReconciler{Log: logr.Discard(), CollectionManager: mgr} + r.pendingCollectors = []pendingCollector{{ + name: collector.KedaScaledJob, // wire name "keda_scaled_job" + factory: func() collector.ResourceCollector { + return &keyedCollector{collectorType: "scaledjob", resourceCh: make(chan []collector.CollectedResource, 1)} + }, + }} + + done := make(chan struct{}) + go func() { + r.retryPendingCollectors(context.Background()) + close(done) + }() + select { + case <-done: + case <-time.After(2 * time.Second): + t.Fatal("retryPendingCollectors did not return") + } + + if !mgr.IsCollectorRunning("scaledjob") { + t.Fatalf("the collector registered under %q was not started", "scaledjob") + } + if len(r.pendingCollectors) != 0 { + t.Errorf("a collector that registered and started should leave the pending list, got %d entries", + len(r.pendingCollectors)) + } + _ = mgr.StopCollector("scaledjob") +} diff --git a/internal/controller/requeue_failed_start_test.go b/internal/controller/requeue_failed_start_test.go new file mode 100644 index 00000000..b9fd2f24 --- /dev/null +++ b/internal/controller/requeue_failed_start_test.go @@ -0,0 +1,38 @@ +package controller + +import ( + "testing" + + "github.com/devzero-inc/zxporter/internal/collector" + "github.com/go-logr/logr" +) + +// TestRequeueFailedStart_DeregistersAndQueuesFreshInstance covers the restart and +// re-enable paths: after their Start fails, the failed instance is removed (so a +// replacement is not refused as "already registered") and the type is queued, with a +// factory that builds a fresh collector registered under the same key. +func TestRequeueFailedStart_DeregistersAndQueuesFreshInstance(t *testing.T) { + r, metricsClient := newSpecTestReconciler(t) + key := collector.DaemonSet.String() + failed := &keyedCollector{collectorType: key, resourceCh: make(chan []collector.CollectedResource, 1)} + if err := r.CollectionManager.RegisterCollector(failed); err != nil { + t.Fatal(err) + } + + r.requeueFailedStart(logr.Discard(), key, key, metricsClient) + r.requeueFailedStart(logr.Discard(), key, key, metricsClient) // idempotent + + if r.CollectionManager.GetCollector(key) != nil { + t.Error("the instance whose Start failed is still registered") + } + if len(r.pendingCollectors) != 1 || r.pendingCollectors[0].name != collector.DaemonSet { + t.Fatalf("want exactly one pending %q entry, got %+v", key, r.pendingCollectors) + } + fresh := r.pendingCollectors[0].factory() + if fresh == nil || fresh == collector.ResourceCollector(failed) { + t.Fatal("the pending factory must build a fresh collector") + } + if got := fresh.GetType(); got != key { + t.Errorf("fresh collector GetType() = %q, want %q", got, key) + } +} diff --git a/internal/controller/scheduler_metrics_scrape_interval_test.go b/internal/controller/scheduler_metrics_scrape_interval_test.go new file mode 100644 index 00000000..d06a5c3a --- /dev/null +++ b/internal/controller/scheduler_metrics_scrape_interval_test.go @@ -0,0 +1,67 @@ +package controller + +import ( + "testing" + "time" + + monitoringv1 "github.com/devzero-inc/zxporter/api/v1" + "github.com/devzero-inc/zxporter/internal/collector" + "github.com/go-logr/logr" +) + +// TestSchedulerMetricsScrapeIntervalConfiguration exercises createNewConfig's +// parsing of Policies.SchedulerMetricsScrapeInterval. Guards against a +// regression that silently drops the interval>0 guard or the default +// fallback branch: the collector's own constructor also falls back to +// DefaultSchedulerMetricsScrapeInterval on a non-positive value (see +// NewSchedulerMetricsCollector's doc comment), so a bug here would produce +// no visible symptom other than an unexpectedly short/long scrape cadence. +func TestSchedulerMetricsScrapeIntervalConfiguration(t *testing.T) { + tests := []struct { + name string + envValue string + expectedResult time.Duration + }{ + { + name: "Default interval when not specified", + envValue: "", + expectedResult: collector.DefaultSchedulerMetricsScrapeInterval, + }, + { + name: "Custom 30 second interval", + envValue: "30s", + expectedResult: 30 * time.Second, + }, + { + name: "Zero duration falls back to default", + envValue: "0s", + expectedResult: collector.DefaultSchedulerMetricsScrapeInterval, + }, + { + name: "Malformed value falls back to default", + envValue: "bogus", + expectedResult: collector.DefaultSchedulerMetricsScrapeInterval, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + mockSpec := &monitoringv1.CollectionPolicySpec{ + Policies: monitoringv1.Policies{ + SchedulerMetricsScrapeInterval: tt.envValue, + }, + } + + r := &CollectionPolicyReconciler{} + config, _ := r.createNewConfig(mockSpec, logr.Discard()) + + if config.SchedulerMetricsScrapeInterval != tt.expectedResult { + t.Errorf( + "Expected SchedulerMetricsScrapeInterval %v, got %v", + tt.expectedResult, + config.SchedulerMetricsScrapeInterval, + ) + } + }) + } +} diff --git a/internal/health/component_names.go b/internal/health/component_names.go index e9e70809..3440d051 100644 --- a/internal/health/component_names.go +++ b/internal/health/component_names.go @@ -13,4 +13,12 @@ const ( ComponentKarpenterDeployment = "karpenter_deployment" ComponentGPURuntimeResolver = "gpu_runtime_resolver" ComponentMemoryPressure = "memory_pressure" + + // ComponentContainerRedMetrics covers the socket probe behind container RED + // metrics. It is registered only when the feature is switched on, and is + // DEREGISTERED rather than reported unhealthy when the kernel cannot support + // the probe -- an unsupported kernel is a node that will never report, not a + // node that is failing, and reporting it unhealthy would put a privileged + // DaemonSet into a permanent alarm state it can do nothing about. + ComponentContainerRedMetrics = "container_red_metrics" ) diff --git a/internal/health/component_names_test.go b/internal/health/component_names_test.go index 23df271e..1e0fc93c 100644 --- a/internal/health/component_names_test.go +++ b/internal/health/component_names_test.go @@ -15,6 +15,7 @@ func TestComponentNames_AreDistinct(t *testing.T) { ComponentPrometheus, ComponentGPURuntimeResolver, ComponentMemoryPressure, + ComponentContainerRedMetrics, } seen := make(map[string]bool) for _, name := range names { @@ -31,3 +32,7 @@ func TestComponentGPURuntimeResolver_Name(t *testing.T) { func TestComponentMemoryPressure_Name(t *testing.T) { assert.Equal(t, "memory_pressure", ComponentMemoryPressure) } + +func TestComponentContainerRedMetrics_Name(t *testing.T) { + assert.Equal(t, "container_red_metrics", ComponentContainerRedMetrics) +} diff --git a/internal/logger/logger.go b/internal/logger/logger.go index 22fe3b21..daff0e77 100644 --- a/internal/logger/logger.go +++ b/internal/logger/logger.go @@ -118,6 +118,14 @@ func (l *logger) Report( Fields: fields, } + // After Stop nothing reads the queue; drop instead of filling it. The + // queue is never closed, so a Report racing Stop cannot panic either. + select { + case <-l.stopCh: + return + default: + } + select { case l.logQueue <- entry: // Log successfully queued @@ -146,14 +154,7 @@ func (l *logger) run(ctx context.Context) { for { select { - case logEntry, ok := <-l.logQueue: - if !ok { - // Channel closed, flush remaining batch and exit - if len(batch) > 0 { - l.flush(ctx, batch) - } - return - } + case logEntry := <-l.logQueue: batch = append(batch, logEntry) if len(batch) >= l.config.BatchSize { l.flush(ctx, batch) @@ -165,8 +166,24 @@ func (l *logger) run(ctx context.Context) { batch = make([]*gen.LogEntry, 0, l.config.BatchSize) } case <-l.stopCh: - // flush the queue one last time before exiting - close(l.logQueue) + // Flush what is queued once and exit. The queue is never closed: + // Report may still be sending, and the stop channel stays ready on + // every later select, so closing here closed it twice. + drain: + for { + select { + case logEntry := <-l.logQueue: + batch = append(batch, logEntry) + if len(batch) >= l.config.BatchSize { + l.flush(ctx, batch) + batch = make([]*gen.LogEntry, 0, l.config.BatchSize) + } + default: + break drain + } + } + l.flush(ctx, batch) + return } } } diff --git a/internal/logger/logger_test.go b/internal/logger/logger_test.go new file mode 100644 index 00000000..2e0b82c0 --- /dev/null +++ b/internal/logger/logger_test.go @@ -0,0 +1,90 @@ +package logger + +import ( + "context" + "errors" + "fmt" + "sync" + "testing" + "time" + + "github.com/go-logr/logr" + "go.uber.org/zap" + + gen "github.com/devzero-inc/zxporter/gen/api/v1" +) + +// recordingSender records every batch the logger sends. +type recordingSender struct { + mu sync.Mutex + logs []*gen.LogEntry +} + +func (s *recordingSender) SendTelemetryLogs( + _ context.Context, + in *gen.SendTelemetryLogsRequest, +) (*gen.SendTelemetryLogsResponse, error) { + s.mu.Lock() + defer s.mu.Unlock() + s.logs = append(s.logs, in.Logs...) + return &gen.SendTelemetryLogsResponse{}, nil +} + +func (s *recordingSender) count() int { + s.mu.Lock() + defer s.mu.Unlock() + return len(s.logs) +} + +func newTestLogger(sender TelemetryLogSender) Logger { + return NewLogger(context.Background(), sender, Config{ + BatchSize: 100, + FlushInterval: time.Hour, // only Stop flushes + QueueSize: 100, + }, zap.NewNop(), logr.Discard()) +} + +// Stop is what the reconciler calls when cluster registration with dakr +// fails; the collectors keep reporting after it. Neither may panic: a panic +// turns a dakr outage into a crash loop. +func TestReportAfterStopDoesNotPanic(t *testing.T) { + // Many instances: the stop path used to close the queue on every select + // that picked the (still closed) stop channel, so a second close panicked + // only when the select chose it again. + for i := 0; i < 200; i++ { + l := newTestLogger(&recordingSender{}) + l.Stop() + l.Stop() + l.Report(gen.LogLevel_LOG_LEVEL_ERROR, "test", fmt.Sprintf("after stop %d", i), errors.New("boom"), nil) + } +} + +func TestStopFlushesQueuedLogs(t *testing.T) { + sender := &recordingSender{} + l := newTestLogger(sender) + for i := 0; i < 5; i++ { + l.Report(gen.LogLevel_LOG_LEVEL_ERROR, "test", fmt.Sprintf("queued %d", i), nil, nil) + } + l.Stop() + if got := sender.count(); got != 5 { + t.Fatalf("Stop flushed %d of 5 queued logs", got) + } +} + +func TestReportConcurrentWithStop(t *testing.T) { + for i := 0; i < 50; i++ { + l := newTestLogger(&recordingSender{}) + var wg sync.WaitGroup + for g := 0; g < 4; g++ { + wg.Add(1) + go func(g int) { + defer wg.Done() + for j := 0; j < 50; j++ { + l.Report(gen.LogLevel_LOG_LEVEL_ERROR, "test", fmt.Sprintf("g%d-%d", g, j), nil, nil) + } + }(g) + } + l.Stop() + wg.Wait() + } +} diff --git a/internal/networkmonitor/cilium_client_linux.go b/internal/networkmonitor/cilium_client_linux.go index 806dc175..a93ffb45 100644 --- a/internal/networkmonitor/cilium_client_linux.go +++ b/internal/networkmonitor/cilium_client_linux.go @@ -11,6 +11,7 @@ import ( "github.com/cilium/cilium/pkg/bpf" "github.com/cilium/cilium/pkg/defaults" "github.com/cilium/cilium/pkg/maps/ctmap" + "github.com/cilium/cilium/pkg/maps/nat" "inet.af/netaddr" ) @@ -63,6 +64,13 @@ func listRecords( srcIP := k.DestAddr.IP() // Addresses are swapped due to cilium issue #21346. dstIP := k.SourceAddr.IP() val := v.(*ctmap.CtEntry) + // No DstTranslated on this path. A Cilium CT entry's key holds one + // tuple and no reference to the backend a service connection was + // load-balanced to -- that mapping lives in the separate lb4 backend + // and SNAT maps this collector does not read. DNAT'd flows collected + // here therefore report only the address the client dialed, and the + // control plane cannot attribute them to the pod that answered. + // See NetworkTrafficItem.dst_ip_translated. expireSeconds := timeDiff(int64(val.Lifetime)) record := &Entry{ Src: netaddr.IPPortFrom( @@ -93,8 +101,13 @@ func listRecords( func initMaps() []interface{} { // We might need to handle failure here gracefully or ensure we have permissions - ctmap.InitMapInfo(true, false, true) - maps := ctmap.GlobalMaps(true, false) + // IPv4-only global CT maps, paired with the IPv4 global SNAT map. Cilium + // 1.19 dropped InitMapInfo(v4, v6, nodeport) in favour of passing the NAT + // maps in, and renamed GlobalMaps to Maps; this is the same configuration + // the 1.17 InitMapInfo(true, false, true) call built internally. + nat4, _ := nat.GlobalMaps(nil, true, false) + ctmap.InitMapInfo(nat4, nil) + maps := ctmap.Maps(true, false) ctMaps := make([]interface{}, len(maps)) for i, m := range maps { ctMaps[i] = m diff --git a/internal/networkmonitor/conntrack_client.go b/internal/networkmonitor/conntrack_client.go index 31e332e1..60c0a1e3 100644 --- a/internal/networkmonitor/conntrack_client.go +++ b/internal/networkmonitor/conntrack_client.go @@ -13,14 +13,31 @@ import ( // Entry represents a single conntrack entry type Entry struct { - Src netaddr.IPPort - Dst netaddr.IPPort - TxBytes uint64 - TxPackets uint64 - RxBytes uint64 - RxPackets uint64 - Lifetime time.Time - Proto uint8 + Src netaddr.IPPort + // Dst is the destination the client dialed, taken from conntrack's ORIGINAL + // tuple. For a ClusterIP connection that is the service address, not the pod + // that answered. + Dst netaddr.IPPort + // DstTranslated is the post-DNAT destination, taken from the source of + // conntrack's REPLY tuple: the backend that actually served the connection. + // + // It is the zero IPPort when the kernel did not translate the destination, + // which is the common case for pod-to-pod and egress traffic. Use + // HasTranslatedDst to test it rather than comparing against Dst, since a + // non-DNAT'd flow leaves this zero rather than echoing Dst. + DstTranslated netaddr.IPPort + TxBytes uint64 + TxPackets uint64 + RxBytes uint64 + RxPackets uint64 + Lifetime time.Time + Proto uint8 +} + +// HasTranslatedDst reports whether the kernel DNAT'd this connection, i.e. +// whether the address that answered differs from the address the client dialed. +func (e *Entry) HasTranslatedDst() bool { + return e.DstTranslated.IP().IsValid() } type EntriesFilter func(e *Entry) bool @@ -112,9 +129,15 @@ func processSessions(sessions []ct.Con, filter EntriesFilter) []*Entry { entry.Lifetime = now.Add(time.Second * time.Duration(*sess.Timeout)) } + // A DNAT'd connection (the kernel rewrote the destination, e.g. kube-proxy + // steering a ClusterIP to one backend pod) shows up as a REPLY tuple whose + // source is not the destination the client dialed. Record the backend + // separately instead of overwriting Dst: the control plane needs the + // dialed address to name the service and the backend address to decide + // which node -- and so which availability zone -- actually served it. replySrc := netaddr.IPPortFrom(ipFromStdIP(*reply.Src), *reply.Proto.SrcPort) if entry.Dst != replySrc { - entry.Dst = replySrc + entry.DstTranslated = replySrc } if filter == nil || filter(entry) { diff --git a/internal/networkmonitor/conntrack_client_test.go b/internal/networkmonitor/conntrack_client_test.go index 75fdd591..c790f2b6 100644 --- a/internal/networkmonitor/conntrack_client_test.go +++ b/internal/networkmonitor/conntrack_client_test.go @@ -5,6 +5,7 @@ import ( "testing" ct "github.com/florianl/go-conntrack" + "inet.af/netaddr" ) func TestProcessSessions(t *testing.T) { @@ -92,3 +93,131 @@ func TestProcessSessions(t *testing.T) { } }) } + +// TestProcessSessionsTranslatedDst covers the DNAT case. A connection to a +// ClusterIP is rewritten by kube-proxy to one backend pod, so the REPLY tuple's +// source is not the destination the client dialed. Both addresses have to +// survive: the dialed one names the Service, the reply one names the pod whose +// zone decides the price. +func TestProcessSessionsTranslatedDst(t *testing.T) { + uint8Ptr := func(v uint8) *uint8 { return &v } + uint16Ptr := func(v uint16) *uint16 { return &v } + uint64Ptr := func(v uint64) *uint64 { return &v } + + clientIP := net.ParseIP("10.0.0.5") + clusterIP := net.ParseIP("10.96.0.10") + backendIP := net.ParseIP("10.0.1.50") + + dnatSess := ct.Con{ + Origin: &ct.IPTuple{ + Src: &clientIP, + Dst: &clusterIP, + Proto: &ct.ProtoTuple{ + Number: uint8Ptr(6), + SrcPort: uint16Ptr(40000), + DstPort: uint16Ptr(80), + }, + }, + Reply: &ct.IPTuple{ + // The backend answers from its own address and its own port. + Src: &backendIP, + Dst: &clientIP, + Proto: &ct.ProtoTuple{ + Number: uint8Ptr(6), + SrcPort: uint16Ptr(8080), + DstPort: uint16Ptr(40000), + }, + }, + CounterOrigin: &ct.Counter{Bytes: uint64Ptr(100), Packets: uint64Ptr(1)}, + CounterReply: &ct.Counter{Bytes: uint64Ptr(200), Packets: uint64Ptr(2)}, + } + + t.Run("a DNAT'd session keeps the dialed address and records the backend", func(t *testing.T) { + res := processSessions([]ct.Con{dnatSess}, nil) + if len(res) != 1 { + t.Fatalf("expected 1 entry, got %d", len(res)) + } + e := res[0] + + if got := e.Dst.IP().String(); got != "10.96.0.10" { + t.Errorf("Dst should stay the dialed ClusterIP, got %s", got) + } + if got := e.Dst.Port(); got != 80 { + t.Errorf("Dst port should stay the service port, got %d", got) + } + if !e.HasTranslatedDst() { + t.Fatal("HasTranslatedDst should be true for a DNAT'd session") + } + if got := e.DstTranslated.IP().String(); got != "10.0.1.50" { + t.Errorf("DstTranslated should be the backend pod, got %s", got) + } + if got := e.DstTranslated.Port(); got != 8080 { + t.Errorf("DstTranslated port should be the backend port, got %d", got) + } + }) + + t.Run("an untranslated session records no backend", func(t *testing.T) { + // Reply source equals the dialed destination: no rewrite happened. + plain := dnatSess + plain.Reply = &ct.IPTuple{ + Src: &clusterIP, + Dst: &clientIP, + Proto: &ct.ProtoTuple{ + Number: uint8Ptr(6), + SrcPort: uint16Ptr(80), + DstPort: uint16Ptr(40000), + }, + } + + res := processSessions([]ct.Con{plain}, nil) + if len(res) != 1 { + t.Fatalf("expected 1 entry, got %d", len(res)) + } + if res[0].HasTranslatedDst() { + t.Errorf("no translation should leave DstTranslated zero, got %s", res[0].DstTranslated.String()) + } + }) +} + +// TestEntryGroupKeyDistinguishesBackends pins the property that makes the +// translated address worth carrying: two connections to the same ClusterIP that +// landed on different backends are different flows. Folding them together would +// report one backend's zone for both, which is exactly the mis-pricing this is +// meant to fix. +func TestEntryGroupKeyDistinguishesBackends(t *testing.T) { + mk := func(backend string, port uint16) *Entry { + e := &Entry{ + Src: netaddr.IPPortFrom(netaddr.IPv4(10, 0, 0, 5), 40000), + Dst: netaddr.IPPortFrom(netaddr.IPv4(10, 96, 0, 10), 80), + Proto: 6, + } + if backend != "" { + ip, err := netaddr.ParseIP(backend) + if err != nil { + t.Fatalf("bad test IP %q: %v", backend, err) + } + e.DstTranslated = netaddr.IPPortFrom(ip, port) + } + return e + } + + backendA := entryGroupKey(mk("10.0.1.50", 8080)) + backendB := entryGroupKey(mk("10.0.2.51", 8080)) + noBackend := entryGroupKey(mk("", 0)) + + if backendA == backendB { + t.Error("two backends of the same service must not share a group key") + } + if backendA == noBackend { + t.Error("a translated flow must not share a group key with an untranslated one") + } + if backendA != entryGroupKey(mk("10.0.1.50", 8080)) { + t.Error("the same flow must hash to the same group key") + } + + // The delta cache key carries the same distinction, so counters for one + // backend are never subtracted from another's. + if conntrackEntryKey(mk("10.0.1.50", 8080)) == conntrackEntryKey(mk("10.0.2.51", 8080)) { + t.Error("two backends of the same service must not share a conntrack cache key") + } +} diff --git a/internal/networkmonitor/dns/ip2dns.go b/internal/networkmonitor/dns/ip2dns.go index 89dcb241..703bb366 100644 --- a/internal/networkmonitor/dns/ip2dns.go +++ b/internal/networkmonitor/dns/ip2dns.go @@ -31,7 +31,9 @@ var _ DNSCollector = (*IP2DNS)(nil) type tracer interface { Run(ctx context.Context) error - Events() <-chan ebpf.DNSEvent + // Events yields DNS events in batches. The tracer groups them so that a busy node + // costs one channel handoff and one wakeup per batch rather than per packet. + Events() <-chan []ebpf.DNSEvent } var defaultDNSTTL = 2 * time.Minute @@ -64,52 +66,70 @@ func (d *IP2DNS) Start(ctx context.Context) error { select { case <-ctx.Done(): return ctx.Err() - case ev, ok := <-evCh: + case batch, ok := <-evCh: if !ok { return nil } + d.handleBatch(batch) + } + } +} - // Process DNS Event - var resolvedIPs []string - var domain string +// handleBatch records the lookups in one batch of DNS events, taking the lock once for +// the whole batch rather than once per event. +func (d *IP2DNS) handleBatch(batch []ebpf.DNSEvent) { + lookups := make([]DNSLookup, 0, len(batch)) + now := time.Now() - for _, answer := range ev.Answers { - name := string(answer.Name) - if domain == "" { - domain = name - } + for i := range batch { + ev := &batch[i] - switch answer.Type { - case layers.DNSTypeA: - ip, ok := netaddr.FromStdIP(answer.IP) - if !ok { - continue - } - resolvedIPs = append(resolvedIPs, ip.String()) - d.ipToName.Set(ip.String(), name, cache.WithExpiration(defaultDNSTTL)) - case layers.DNSTypeCNAME: - // CNAME handling if needed - } + var resolvedIPs []string + var domain string + + for _, answer := range ev.Answers { + if domain == "" { + domain = answer.Name } - if len(resolvedIPs) > 0 { - var clientIP string - // eBPF Tracer (CgroupIngress): SrcIP = Server, DstIP = Client (Pod) - if ip, ok := netaddr.FromStdIP(ev.DstIP); ok { - clientIP = ip.String() + switch answer.Type { + case layers.DNSTypeA: + ip, ok := netaddr.FromStdIP(answer.IP) + if !ok { + continue } - - d.mu.Lock() - d.lookups = append(d.lookups, DNSLookup{ - ClientIP: clientIP, - Domain: domain, - ResolvedIPs: resolvedIPs, - Timestamp: time.Now(), - }) - d.mu.Unlock() + resolvedIPs = append(resolvedIPs, ip.String()) + d.ipToName.Set(ip.String(), answer.Name, cache.WithExpiration(defaultDNSTTL)) + case layers.DNSTypeCNAME: + // CNAME handling if needed } } + + if len(resolvedIPs) == 0 { + continue + } + + var clientIP string + // eBPF Tracer (CgroupIngress): SrcIP = Server, DstIP = Client (Pod) + if ip, ok := netaddr.FromStdIP(ev.DstIP); ok { + clientIP = ip.String() + } + + lookups = append(lookups, DNSLookup{ + ClientIP: clientIP, + Domain: domain, + ResolvedIPs: resolvedIPs, + Timestamp: now, + }) } + + if len(lookups) == 0 { + return + } + + d.mu.Lock() + d.lookups = append(d.lookups, lookups...) + d.mu.Unlock() } // Lookups returns the accumulated lookups and clears the buffer (Delta) diff --git a/internal/networkmonitor/ebpf/tracer.go b/internal/networkmonitor/ebpf/tracer.go index ae49657b..32e2246f 100644 --- a/internal/networkmonitor/ebpf/tracer.go +++ b/internal/networkmonitor/ebpf/tracer.go @@ -2,7 +2,13 @@ package ebpf import "github.com/go-logr/logr" +// dnsBatchSize is how many events accumulate before the reader hands them over. +// It lives here rather than beside the reader because NewTracer sizes the channel +// from it, and NewTracer is built on every platform. +const dnsBatchSize = 64 + type Config struct { + // QueueSize is how many DNS events may be buffered on the way to the consumer. QueueSize int CustomBTFFilePath string } @@ -11,16 +17,24 @@ func NewTracer(log logr.Logger, cfg Config) *Tracer { if cfg.QueueSize == 0 { cfg.QueueSize = 1000 } + // The channel carries batches, so its depth is in batches; QueueSize stays + // denominated in events so that the bound on how much is buffered -- which is what + // a caller setting it is choosing -- does not silently grow by a factor of + // dnsBatchSize when the batching changes. + depth := cfg.QueueSize / dnsBatchSize + if depth < 1 { + depth = 1 + } return &Tracer{ log: log.WithName("ebpf_tracer"), cfg: cfg, - events: make(chan DNSEvent, cfg.QueueSize), + events: make(chan []DNSEvent, depth), } } type Tracer struct { log logr.Logger cfg Config - events chan DNSEvent + events chan []DNSEvent objs *bpfObjects } diff --git a/internal/networkmonitor/ebpf/tracer_linux.go b/internal/networkmonitor/ebpf/tracer_linux.go index 3f68d4a4..50bd10b0 100644 --- a/internal/networkmonitor/ebpf/tracer_linux.go +++ b/internal/networkmonitor/ebpf/tracer_linux.go @@ -10,6 +10,7 @@ import ( "net" "os" "strings" + "time" "github.com/cilium/ebpf" "github.com/cilium/ebpf/btf" @@ -161,19 +162,74 @@ func (t *Tracer) Run(ctx context.Context) error { } defer reader.Close() + return t.readDNS(ctx, reader) +} + +// dnsFlushInterval bounds how long a partly-filled batch waits. It only has to be +// short relative to how often the consumer's output is read, which is on the order of +// seconds. +const dnsFlushInterval = 10 * time.Millisecond + +// readDNS drains the perf ring, decodes the DNS packets on it and hands them to the +// consumer in batches. +// +// Batching is the point. Sending one event per packet meant a channel operation and a +// consumer wakeup for every DNS packet crossing the node, and on a busy node that was +// 9% of this agent's CPU in the send itself plus the larger part of another 13% spent +// in the scheduler waking the reader back up. Nothing downstream needs an event the +// instant it is decoded -- the consumer's output is polled on a much slower cycle -- +// so the wakeups were pure overhead. +func (t *Tracer) readDNS(ctx context.Context, reader *perf.Reader) error { + dec := newDNSDecoder() + batch := make([]DNSEvent, 0, dnsBatchSize) + + flush := func() { + if len(batch) == 0 { + return + } + select { + case t.events <- batch: + // The consumer owns that slice now, so the next batch needs its own + // backing array. One allocation per dnsBatchSize packets. + batch = make([]DNSEvent, 0, dnsBatchSize) + default: + t.log.V(1).Info("dropping events, queue is full", "count", len(batch)) + batch = batch[:0] + } + } + for { select { case <-ctx.Done(): + // A batch filled during the last iteration is still pending here, and + // this is the last chance to hand it over. flush is non-blocking, so a + // consumer that has already stopped reading costs nothing. + flush() return ctx.Err() default: } + // The deadline is armed only when something is waiting to be sent. An idle + // node should sit blocked in Read, not wake a hundred times a second to + // discover it has nothing to flush. + if len(batch) > 0 { + reader.SetDeadline(time.Now().Add(dnsFlushInterval)) + } else { + reader.SetDeadline(time.Time{}) + } + record, err := reader.Read() if err != nil { - if errors.Is(err, perf.ErrClosed) { + switch { + case errors.Is(err, os.ErrDeadlineExceeded): + flush() + continue + case errors.Is(err, perf.ErrClosed): + flush() return nil + default: + return err } - return err } if len(record.RawSample) < 1 { @@ -181,19 +237,27 @@ func (t *Tracer) Run(ctx context.Context) error { continue } - // Parse the full raw packet captured from eBPF - event, err := parseEvent(record.RawSample) - if err != nil { + if err := dec.decode(record.RawSample); err != nil { t.log.V(1).Info("skipping non-DNS event", "err", err) continue } - select { - case t.events <- event: - default: - t.log.V(1).Info("dropping event, queue is full") + // Queries, NXDOMAIN and anything else that resolved nothing are dropped here + // rather than sent and ignored. Kubernetes resolution is amplified by the + // ndots:5 search path -- a single hostname fans out into several queries, most + // of which answer nothing -- so the packets that carry an answer are a small + // fraction of the packets that arrive, and the rest cost a decode into reused + // memory and nothing else. This is not a narrowing of what the consumer sees: + // an event with no answers resolves no IPs, and an event that resolves no IPs + // is discarded on arrival (see dns.IP2DNS.Start). + if !dec.hasAnswers() { continue } + + batch = append(batch, dec.event()) + if len(batch) >= dnsBatchSize { + flush() + } } } @@ -282,7 +346,7 @@ func (t *Tracer) CollectNetworkSummary() (map[NetworkTrafficKey]TrafficSummary, return result, nil } -func (t *Tracer) Events() <-chan DNSEvent { +func (t *Tracer) Events() <-chan []DNSEvent { return t.events } @@ -302,64 +366,134 @@ func InitCgroupv2(log logr.Logger) error { return nil } -func parseEvent(data []byte) (DNSEvent, error) { - if len(data) == 0 { - return DNSEvent{}, errors.New("empty data") - } +// dnsDecoder decodes DNS packets off the perf ring without allocating per packet. +// +// What it replaced was gopacket.NewPacket with gopacket.Default, which copies the +// payload and eagerly decodes every layer into freshly allocated structures. That one +// call was the most expensive thing this agent did: 16% of its CPU and 54% of every +// byte it allocated, on a node carrying nothing but ordinary Kubernetes traffic. The +// shape of a DNS packet is known in advance -- IP, UDP, DNS, and the kernel side +// already filters to UDP/53 -- so there is nothing for a general packet decoder to +// discover. +// +// A DecodingLayerParser over preallocated layer structs is gopacket's answer to that. +// layers.DNS is written for it: it truncates its record slices rather than +// reallocating them and decodes names into a scratch buffer it keeps, so a steady +// stream of packets settles into allocating nothing at all. +// +// The price is that everything reachable from the decoded layers is invalidated by the +// next call to decode. A decoder therefore belongs to exactly one goroutine, and +// anything that outlives the call is copied out by event(). +type dnsDecoder struct { + ip4 layers.IPv4 + ip6 layers.IPv6 + udp layers.UDP + dns layers.DNS + + v4 *gopacket.DecodingLayerParser + v6 *gopacket.DecodingLayerParser + decoded []gopacket.LayerType + + srcIP net.IP + dstIP net.IP +} - // Detect IP version - var layerType gopacket.LayerType - version := data[0] >> 4 - if version == 6 { - layerType = layers.LayerTypeIPv6 - } else { - layerType = layers.LayerTypeIPv4 - } +func newDNSDecoder() *dnsDecoder { + d := &dnsDecoder{decoded: make([]gopacket.LayerType, 0, 4)} + + // Two parsers, because a DecodingLayerParser is fixed at construction to the layer + // type it starts from and the version is only known once the packet is in hand. + // They share the layer structs, so the second one costs nothing per packet. + d.v4 = gopacket.NewDecodingLayerParser(layers.LayerTypeIPv4, &d.ip4, &d.udp, &d.dns) + d.v6 = gopacket.NewDecodingLayerParser(layers.LayerTypeIPv6, &d.ip6, &d.udp, &d.dns) + + // A packet whose next layer has no decoder registered -- a fragment, an unexpected + // protocol -- stops the parse rather than failing it. It is then caught below by + // the absence of a DNS layer, which is the condition actually being tested. + d.v4.IgnoreUnsupported = true + d.v6.IgnoreUnsupported = true + + return d +} - packet := gopacket.NewPacket( - data, - layerType, - gopacket.Default, - ) +// decode parses one raw packet into the decoder's layer structs. The decoded values +// are valid until the next call to decode and no longer. +func (d *dnsDecoder) decode(data []byte) error { + d.srcIP, d.dstIP = nil, nil - var res DNSEvent - if packet == nil { - return res, errors.New("parsing packet") + if len(data) == 0 { + return errors.New("empty data") } - ipLayer := packet.NetworkLayer() - if ipLayer == nil { - return res, errors.New("layer L3 is missing") + parser := d.v4 + if data[0]>>4 == 6 { + parser = d.v6 } - appLayer := packet.ApplicationLayer() - if appLayer == nil { - return res, errors.New("layer L7 is missing") + d.decoded = d.decoded[:0] + if err := parser.DecodeLayers(data, &d.decoded); err != nil { + return fmt.Errorf("decoding packet: %w", err) } - dns, ok := appLayer.(*layers.DNS) - if !ok { - return res, fmt.Errorf("expected dns layer, actual type %T", appLayer) + var haveDNS bool + for _, layerType := range d.decoded { + switch layerType { + case layers.LayerTypeIPv4: + d.srcIP, d.dstIP = d.ip4.SrcIP, d.ip4.DstIP + case layers.LayerTypeIPv6: + d.srcIP, d.dstIP = d.ip6.SrcIP, d.ip6.DstIP + case layers.LayerTypeDNS: + haveDNS = true + } } - srcIP, dstIP := getIPs(ipLayer) + if d.srcIP == nil { + return errors.New("layer L3 is missing") + } + if !haveDNS { + return errors.New("layer L7 is missing") + } + return nil +} - return DNSEvent{ - SrcIP: srcIP, - DstIP: dstIP, - Questions: dns.Questions, - Answers: dns.Answers, - }, nil +// hasAnswers reports whether the last decoded packet resolved anything. +func (d *dnsDecoder) hasAnswers() bool { return len(d.dns.Answers) > 0 } + +// event copies the last decoded packet into a DNSEvent that owns its own memory and is +// therefore safe to send to another goroutine. +func (d *dnsDecoder) event() DNSEvent { + ev := DNSEvent{ + SrcIP: cloneIP(d.srcIP), + DstIP: cloneIP(d.dstIP), + Questions: make([]DNSQuestion, 0, len(d.dns.Questions)), + Answers: make([]DNSAnswer, 0, len(d.dns.Answers)), + } + for i := range d.dns.Questions { + q := &d.dns.Questions[i] + ev.Questions = append(ev.Questions, DNSQuestion{ + Name: string(q.Name), + Type: q.Type, + Class: q.Class, + }) + } + for i := range d.dns.Answers { + a := &d.dns.Answers[i] + ev.Answers = append(ev.Answers, DNSAnswer{ + Name: string(a.Name), + Type: a.Type, + Class: a.Class, + IP: cloneIP(a.IP), + CNAME: string(a.CNAME), + }) + } + return ev } -func getIPs(l gopacket.NetworkLayer) (net.IP, net.IP) { - if ipv4, ok := l.(*layers.IPv4); ok { - return ipv4.SrcIP, ipv4.DstIP - } - if ipv6, ok := l.(*layers.IPv6); ok { - return ipv6.SrcIP, ipv6.DstIP +func cloneIP(ip net.IP) net.IP { + if ip == nil { + return nil } - return nil, nil + return append(net.IP(nil), ip...) } func detectCgroupPath() (string, error) { diff --git a/internal/networkmonitor/ebpf/tracer_linux_test.go b/internal/networkmonitor/ebpf/tracer_linux_test.go index fb1bc6d3..f9275b56 100644 --- a/internal/networkmonitor/ebpf/tracer_linux_test.go +++ b/internal/networkmonitor/ebpf/tracer_linux_test.go @@ -3,12 +3,22 @@ package ebpf import ( - "fmt" + "bytes" "testing" + + "github.com/google/gopacket/layers" ) -func TestTracer(t *testing.T) { - payload := []byte{ +// testQName is the name carried by dnsResponsePayload, in both its question and its +// answer. Named because both tests below assert on it from either section, and a +// payload edited without the assertions following would otherwise pass by accident. +const testQName = "google.lt" + +// dnsResponsePayload is a captured IPv4/UDP DNS response: one question and one answer, +// google.lt A 172.217.21.163, from 192.168.65.7:53 to 172.17.0.3:57381. It is the shape +// the cgroup programs put on the perf ring -- an IP header first, no ethernet. +func dnsResponsePayload() []byte { + return []byte{ 69, 0, 0, @@ -90,9 +100,123 @@ func TestTracer(t *testing.T) { 21, 163, } - event, err := parseEvent(payload) - if err != nil { - t.Fatal(err) +} + +func TestDNSDecoderDecodesResponse(t *testing.T) { + d := newDNSDecoder() + if err := d.decode(dnsResponsePayload()); err != nil { + t.Fatalf("decode: %v", err) + } + if !d.hasAnswers() { + t.Fatal("hasAnswers = false, want true for a response carrying an A record") + } + + ev := d.event() + + if got, want := ev.SrcIP.String(), "192.168.65.7"; got != want { + t.Errorf("SrcIP = %q, want %q", got, want) + } + if got, want := ev.DstIP.String(), "172.17.0.3"; got != want { + t.Errorf("DstIP = %q, want %q", got, want) + } + if got, want := len(ev.Questions), 1; got != want { + t.Fatalf("len(Questions) = %d, want %d", got, want) + } + if got, want := ev.Questions[0].Name, testQName; got != want { + t.Errorf("Questions[0].Name = %q, want %q", got, want) + } + if got, want := len(ev.Answers), 1; got != want { + t.Fatalf("len(Answers) = %d, want %d", got, want) + } + if got, want := ev.Answers[0].Name, testQName; got != want { + t.Errorf("Answers[0].Name = %q, want %q", got, want) + } + if got, want := ev.Answers[0].Type, layers.DNSTypeA; got != want { + t.Errorf("Answers[0].Type = %v, want %v", got, want) + } + if got, want := ev.Answers[0].IP.String(), "172.217.21.163"; got != want { + t.Errorf("Answers[0].IP = %q, want %q", got, want) + } +} + +// TestDNSDecoderEventSurvivesTheNextPacket is the test the decoder's design turns on. +// It decodes into layer structs it reuses, so the only thing keeping an event that has +// been handed to another goroutine from being rewritten under it is that event() copies +// everything out. If that copy is ever weakened -- a []byte kept instead of a string, a +// net.IP aliased instead of cloned -- this fails. +func TestDNSDecoderEventSurvivesTheNextPacket(t *testing.T) { + second := dnsResponsePayload() + // The first occurrence of the name is the question, so this rewrites the question + // only: google.lt becomes moogle.lt, same length, so nothing downstream shifts. + second[bytes.Index(second, []byte("google"))] = 'm' + // The last four bytes are the A record's address. + second[len(second)-1] = 99 + + d := newDNSDecoder() + if err := d.decode(dnsResponsePayload()); err != nil { + t.Fatalf("decode first: %v", err) + } + first := d.event() + + if err := d.decode(second); err != nil { + t.Fatalf("decode second: %v", err) + } + next := d.event() + + if got, want := next.Questions[0].Name, "moogle.lt"; got != want { + t.Fatalf("second event Questions[0].Name = %q, want %q -- the payloads are not distinct", got, want) + } + if got, want := next.Answers[0].IP.String(), "172.217.21.99"; got != want { + t.Fatalf("second event Answers[0].IP = %q, want %q -- the payloads are not distinct", got, want) + } + + if got, want := first.Questions[0].Name, testQName; got != want { + t.Errorf("first event Questions[0].Name = %q, want %q: clobbered by the next packet", got, want) + } + if got, want := first.Answers[0].Name, testQName; got != want { + t.Errorf("first event Answers[0].Name = %q, want %q: clobbered by the next packet", got, want) + } + if got, want := first.Answers[0].IP.String(), "172.217.21.163"; got != want { + t.Errorf("first event Answers[0].IP = %q, want %q: clobbered by the next packet", got, want) + } +} + +func TestDNSDecoderRejectsNonDNS(t *testing.T) { + d := newDNSDecoder() + + if err := d.decode(nil); err == nil { + t.Error("decode(nil) = nil, want an error") + } + if err := d.decode([]byte{69, 0, 0, 4}); err == nil { + t.Error("decode(truncated IPv4) = nil, want an error") + } + + // A well-formed packet whose UDP ports are not 53: no DNS layer to find. + notDNS := dnsResponsePayload() + notDNS[20], notDNS[21] = 0x1f, 0x90 // source port 8080 + notDNS[22], notDNS[23] = 0x1f, 0x91 // destination port 8081 + if err := d.decode(notDNS); err == nil { + t.Error("decode(non-DNS UDP) = nil, want an error") + } +} + +// TestDNSDecoderDoesNotAllocatePerPacket guards the reason the decoder exists. The +// decode itself is the hot path -- most packets on a Kubernetes node answer nothing and +// are dropped without ever reaching event() -- so it is decode, not the copy, that has +// to stay free of allocation. +func TestDNSDecoderDoesNotAllocatePerPacket(t *testing.T) { + d := newDNSDecoder() + payload := dnsResponsePayload() + + // Warm the decoder's internal buffers: the first decode legitimately allocates them. + if err := d.decode(payload); err != nil { + t.Fatalf("decode: %v", err) + } + + got := testing.AllocsPerRun(100, func() { + _ = d.decode(payload) + }) + if got != 0 { + t.Errorf("decode allocated %v objects per packet, want 0", got) } - fmt.Println(event.String()) } diff --git a/internal/networkmonitor/ebpf/tracer_nolinux.go b/internal/networkmonitor/ebpf/tracer_nolinux.go index a50e8e32..20fa54ba 100644 --- a/internal/networkmonitor/ebpf/tracer_nolinux.go +++ b/internal/networkmonitor/ebpf/tracer_nolinux.go @@ -15,7 +15,7 @@ func (t *Tracer) Run(ctx context.Context) error { return nil } -func (t *Tracer) Events() <-chan DNSEvent { +func (t *Tracer) Events() <-chan []DNSEvent { return t.events } diff --git a/internal/networkmonitor/ebpf/types.go b/internal/networkmonitor/ebpf/types.go index 05c949b2..3bbb6624 100644 --- a/internal/networkmonitor/ebpf/types.go +++ b/internal/networkmonitor/ebpf/types.go @@ -8,23 +8,50 @@ import ( "github.com/google/gopacket/layers" ) +// DNSQuestion and DNSAnswer are the parts of a DNS message this agent has a use for. +// +// They are deliberately not gopacket's layers.DNSQuestion and layers.DNSResourceRecord. +// Those carry their names, IPs and RDATA as []byte pointing into the packet buffer and +// into the decoder's scratch space, and the decoder reuses both -- so a record that +// crossed the events channel would be rewritten underneath its reader by whichever +// packet arrived next. Names are held as strings here, which are copies by +// construction, and the only mutable field left is the IP, which the decoder clones. +// +// The narrower shape is also honest about what is filled in. A layers.DNSResourceRecord +// has fifteen fields; copying the three that are read would leave the other twelve +// looking valid and silently wrong. +type DNSQuestion struct { + Name string + Type layers.DNSType + Class layers.DNSClass +} + +type DNSAnswer struct { + Name string + Type layers.DNSType + Class layers.DNSClass + IP net.IP + CNAME string +} + +// DNSEvent is one decoded DNS message. It owns everything reachable from it. type DNSEvent struct { SrcIP net.IP DstIP net.IP - Questions []layers.DNSQuestion - Answers []layers.DNSResourceRecord + Questions []DNSQuestion + Answers []DNSAnswer } func (e DNSEvent) String() string { var str strings.Builder for _, v := range e.Questions { str.WriteString("Questions:\n") - str.WriteString(fmt.Sprintf("%s %s %s \n", v.Class, v.Type, string(v.Name))) + str.WriteString(fmt.Sprintf("%s %s %s \n", v.Class, v.Type, v.Name)) } for _, v := range e.Answers { str.WriteString("Answers:\n") str.WriteString( - fmt.Sprintf("%s %s %s [%s] [%s] \n", v.Class, v.Type, string(v.Name), v.IP, v.CNAME), + fmt.Sprintf("%s %s %s [%s] [%s] \n", v.Class, v.Type, v.Name, v.IP, v.CNAME), ) } return str.String() diff --git a/internal/networkmonitor/egress_path.go b/internal/networkmonitor/egress_path.go new file mode 100644 index 00000000..43a5f2a3 --- /dev/null +++ b/internal/networkmonitor/egress_path.go @@ -0,0 +1,599 @@ +package networkmonitor + +import ( + "context" + "encoding/json" + "fmt" + "io" + "net/http" + "strings" + "sync" + "time" + + "github.com/go-logr/logr" + + gen "github.com/devzero-inc/zxporter/gen/api/v1" +) + +// Bytes measured leaving a pod do not carry their own price. The same byte is +// free, or charged at a cross-zone rate, or at a NAT-gateway processing rate, or +// at a public-IP data-transfer rate, depending on where the node sits and +// whether it has a public address. None of that is visible from a flow record; +// it is visible only to the node, from its own instance metadata service. This +// file reads it once per process and reports it alongside the flows. +// +// Everything here is best-effort and must fail quietly. A node with no metadata +// service (bare metal, an unrecognised provider, a blocked link-local route) +// reports nothing, and nothing is exactly what the control plane must see -- +// never a guessed region or an assumed NAT path. + +const ( + // awsIMDSBase is the IPv4 link-local metadata endpoint, identical across AWS + // and Azure; the two are told apart by the paths and headers they answer. + awsIMDSBase = "http://169.254.169.254" + azureIMDSBase = "http://169.254.169.254" + gcpIMDSBase = "http://metadata.google.internal" + + // metadataTimeout bounds a single metadata request. The services are + // link-local and answer in single-digit milliseconds when present; when + // absent the route is usually rejected immediately, and this bound covers + // the case where it black-holes instead. + metadataTimeout = 2 * time.Second + + // imdsTokenTTLSeconds is the lifetime requested for an IMDSv2 token. We use + // the token within the same call, so the shortest practical TTL is enough. + imdsTokenTTLSeconds = "60" + + // probeRetryInterval is how long to wait before re-probing after a failed + // attempt. A node can easily out-run its own metadata service at startup -- + // IMDS not yet serving, metadata DNS not yet resolving, the hop-limited + // route not yet installed, or plain throttling -- and caching that first + // failure would blind the node until the pod restarts. Successes are cached + // forever; only failures come back here. + probeRetryInterval = 5 * time.Minute +) + +// newMetadataClient builds the HTTP client used for every metadata read. +// +// Proxy is explicitly nil. Go's default transport uses ProxyFromEnvironment, and +// a cluster that sets HTTP_PROXY/HTTPS_PROXY for its workloads would otherwise +// send these link-local requests through the proxy unless NO_PROXY happens to +// cover 169.254.169.254 and metadata.google.internal. That breaks provider +// detection, and it hands an IMDSv2 token request to a third party. +func newMetadataClient() *http.Client { + return &http.Client{ + Timeout: metadataTimeout, + Transport: &http.Transport{ + Proxy: nil, + }, + } +} + +// EgressPathResolver reads the node's instance metadata once and caches the +// answer for the lifetime of the process. The facts it reports -- provider, +// region, zone, VPC, subnet, whether the primary interface has a public address +// -- do not change while a node is running; a node that is re-addressed is a new +// node with a new pod. +type EgressPathResolver struct { + log logr.Logger + client *http.Client + now func() time.Time + + // baseURLs lets tests point the resolver at an httptest server. Empty + // entries fall back to the real link-local endpoints. + awsBase string + azureBase string + gcpBase string + + mu sync.Mutex + // cached holds the best answer so far, which may be incomplete. A probe can + // succeed at reading the zone while still failing to establish the egress + // path -- a throttled or 5xx read of public-ipv4s, an interface whose MAC + // could not be read -- and that partial answer is worth publishing, because + // zone, VPC and subnet are useful on their own. + // + // complete is what gates re-probing, NOT cached != nil. Caching a partial + // answer permanently would defeat the retry entirely for the one case it + // exists to cover: a node throttled at startup would report an unknown + // egress path until its pod restarts. + cached *gen.NodeEgressPath + complete bool + nextRetry time.Time + probing bool +} + +// isComplete reports whether a probe result settled the one fact that cannot be +// guessed. Everything else in the message is either present or genuinely absent +// for this node; has_public_ip is the only field with a third state. +func isComplete(path *gen.NodeEgressPath) bool { + return path != nil && path.HasPublicIp != nil //nolint:protogetter // presence is the question +} + +// NewEgressPathResolver builds a resolver against the real metadata endpoints. +func NewEgressPathResolver(log logr.Logger) *EgressPathResolver { + return &EgressPathResolver{ + log: log, + client: newMetadataClient(), + now: time.Now, + } +} + +// Cached returns the best answer so far, or nil if no probe has succeeded yet. +// The answer may be incomplete (no egress path); the control plane records that +// as unknown. It never performs IO and never blocks on the network, so it is +// safe to call from the flush path while a collection lock is held. +func (r *EgressPathResolver) Cached() *gen.NodeEgressPath { + r.mu.Lock() + defer r.mu.Unlock() + return r.cached +} + +// Prime probes the metadata service unless a COMPLETE answer is already held, +// one is already running, or a recent attempt is still inside its retry +// interval. It performs network IO and so must never be called while holding a +// collection lock -- a black-holed link-local route costs several +// metadataTimeout waits. +// +// Safe to call repeatedly; the monitor calls it once at startup and again from +// the flush loop for as long as the answer is still incomplete. +func (r *EgressPathResolver) Prime(ctx context.Context) { + r.mu.Lock() + if r.complete || r.probing || r.now().Before(r.nextRetry) { + r.mu.Unlock() + return + } + r.probing = true + r.mu.Unlock() + + path := r.probe(ctx) + + r.mu.Lock() + r.probing = false + switch { + case path == nil: + // Nothing at all. Keep whatever we had (possibly nothing) and retry. + r.nextRetry = r.now().Add(probeRetryInterval) + r.mu.Unlock() + r.log.V(1).Info("no cloud instance metadata service answered; "+ + "node egress path stays unknown until the next attempt", + "retry_in", probeRetryInterval.String()) + return + + case !isComplete(path): + // Partial: publish it -- zone, VPC and subnet are worth reporting -- but + // keep retrying, because the egress path is still unknown and that is + // the field the price depends on. + r.cached = path + r.nextRetry = r.now().Add(probeRetryInterval) + r.mu.Unlock() + r.log.V(1).Info("node metadata read, but the egress path is still unknown; "+ + "will re-probe", + "cloud_provider", path.GetCloudProvider(), + "zone", path.GetZone(), + "retry_in", probeRetryInterval.String()) + return + + default: + r.cached = path + r.complete = true + r.mu.Unlock() + } + + r.log.Info("resolved node egress path", + "cloud_provider", path.GetCloudProvider(), + "region", path.GetRegion(), + "zone", path.GetZone(), + "vpc_id", path.GetVpcId(), + "subnet_id", path.GetSubnetId(), + "has_public_ip", path.GetHasPublicIp(), + ) +} + +// Resolve primes if the answer is not yet complete and returns whatever is +// known. It can block on the network; prefer Cached plus a background Prime on +// hot paths. +func (r *EgressPathResolver) Resolve(ctx context.Context) *gen.NodeEgressPath { + r.Prime(ctx) + return r.Cached() +} + +// probe tries each provider in turn and returns the first that answers. Order is +// by fleet share; a provider that is not present rejects or times out quickly, +// and the whole probe is bounded by metadataTimeout per request. +func (r *EgressPathResolver) probe(ctx context.Context) *gen.NodeEgressPath { + if p, err := r.resolveAWS(ctx); err == nil && p != nil { + return p + } else if err != nil { + r.log.V(1).Info("AWS instance metadata unavailable", "error", err.Error()) + } + + if p, err := r.resolveGCP(ctx); err == nil && p != nil { + return p + } else if err != nil { + r.log.V(1).Info("GCP instance metadata unavailable", "error", err.Error()) + } + + if p, err := r.resolveAzure(ctx); err == nil && p != nil { + return p + } else if err != nil { + r.log.V(1).Info("Azure instance metadata unavailable", "error", err.Error()) + } + + return nil +} + +func (r *EgressPathResolver) base(override, fallback string) string { + if override != "" { + return override + } + return fallback +} + +// get performs one metadata GET and returns the trimmed body. A non-2xx status +// is an error: metadata services answer 404 for attributes an instance does not +// have (no public IP, for example), and the callers below treat that as "the +// attribute is absent", not as "the provider is absent". +func (r *EgressPathResolver) get(ctx context.Context, url string, headers map[string]string) (string, error) { + req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil) + if err != nil { + return "", err + } + for k, v := range headers { + req.Header.Set(k, v) + } + resp, err := r.client.Do(req) + if err != nil { + return "", err + } + defer func() { _ = resp.Body.Close() }() + + body, err := io.ReadAll(io.LimitReader(resp.Body, 64*1024)) + if err != nil { + return "", err + } + if resp.StatusCode < 200 || resp.StatusCode >= 300 { + return "", fmt.Errorf("metadata %s: status %d", url, resp.StatusCode) + } + return strings.TrimSpace(string(body)), nil +} + +// getOptional is get, with any failure reported as an empty string. Use it only +// where an unreadable attribute and an absent attribute deserve the same +// treatment -- never for has_public_ip, which needs the two told apart. +func (r *EgressPathResolver) getOptional(ctx context.Context, url string, headers map[string]string) string { + v, err := r.get(ctx, url, headers) + if err != nil { + return "" + } + return v +} + +// getDefinite distinguishes the three outcomes that has_public_ip depends on: +// the attribute exists (value, true), the service says it provably does not +// exist -- HTTP 404 -- ("", true), or we could not find out ("", false). +// +// The distinction is the whole point. Collapsing a timeout, a 5xx, throttling +// or an expired token into "no public address" bills that node's egress at the +// NAT rate, roughly 4x, on the strength of a failed request. +func (r *EgressPathResolver) getDefinite(ctx context.Context, url string, headers map[string]string) (string, bool) { + req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil) + if err != nil { + return "", false + } + for k, v := range headers { + req.Header.Set(k, v) + } + resp, err := r.client.Do(req) + if err != nil { + return "", false + } + defer func() { _ = resp.Body.Close() }() + + body, err := io.ReadAll(io.LimitReader(resp.Body, 64*1024)) + if err != nil { + return "", false + } + switch { + case resp.StatusCode == http.StatusNotFound: + // A definite "this instance has no such attribute". + return "", true + case resp.StatusCode >= 200 && resp.StatusCode < 300: + return strings.TrimSpace(string(body)), true + default: + return "", false + } +} + +// --- AWS (IMDSv2) --- + +// resolveAWS walks the IMDSv2 token flow. IMDSv1 is disabled on hardened +// accounts (and on every node launched with HttpTokens=required), so the token +// is obtained first and presented on each read. +func (r *EgressPathResolver) resolveAWS(ctx context.Context) (*gen.NodeEgressPath, error) { + base := r.base(r.awsBase, awsIMDSBase) + + tokenReq, err := http.NewRequestWithContext(ctx, http.MethodPut, base+"/latest/api/token", nil) + if err != nil { + return nil, err + } + tokenReq.Header.Set("X-aws-ec2-metadata-token-ttl-seconds", imdsTokenTTLSeconds) + tokenResp, err := r.client.Do(tokenReq) + if err != nil { + return nil, fmt.Errorf("imds token: %w", err) + } + tokenBody, err := io.ReadAll(io.LimitReader(tokenResp.Body, 8*1024)) + _ = tokenResp.Body.Close() + if err != nil { + return nil, fmt.Errorf("imds token body: %w", err) + } + if tokenResp.StatusCode < 200 || tokenResp.StatusCode >= 300 { + return nil, fmt.Errorf("imds token: status %d", tokenResp.StatusCode) + } + headers := map[string]string{"X-aws-ec2-metadata-token": strings.TrimSpace(string(tokenBody))} + + // The AZ is the one attribute we insist on. Without it there is nothing to + // distinguish an EC2 instance from any other host that happens to answer on + // the link-local address. + zone, err := r.get(ctx, base+"/latest/meta-data/placement/availability-zone", headers) + if err != nil { + return nil, err + } + + path := &gen.NodeEgressPath{ + CloudProvider: "aws", + Zone: zone, + Region: awsRegionFromAZ(zone), + PrivateIp: r.getOptional(ctx, base+"/latest/meta-data/local-ipv4", headers), + } + + // The network attributes hang off the primary interface's MAC. A node whose + // MAC cannot be read still reports provider, region and zone -- those are + // worth having on their own -- but it reports no opinion at all about its + // egress path, because it has not read the interface that would decide it. + mac := firstMAC(r.getOptional(ctx, base+"/latest/meta-data/mac", headers)) + if mac != "" { + ifaceBase := base + "/latest/meta-data/network/interfaces/macs/" + mac + path.SubnetId = r.getOptional(ctx, ifaceBase+"/subnet-id", headers) + path.VpcId = r.getOptional(ctx, ifaceBase+"/vpc-id", headers) + // public-ipv4s is 404 on an instance with no public address, which is + // precisely the "leaves through a NAT gateway" case -- and is a + // different thing from a read we could not complete. + if publicIPs, definite := r.getDefinite(ctx, ifaceBase+"/public-ipv4s", headers); definite { + path.HasPublicIp = ptrBool(publicIPs != "") + } + } + + return path, nil +} + +func ptrBool(v bool) *bool { return &v } + +// awsRegionFromAZ derives the region from an AWS availability zone, which is the +// only place IMDS exposes it without a second call. +// +// An AWS region name always ends at its first purely numeric segment +// ("us-west-2", "eu-central-1", "us-gov-west-1"), and everything after that +// belongs to a Local or Wavelength Zone ("us-west-2-lax-1a" sits in us-west-2). +// Cutting at that segment therefore gives the parent region in every form, +// which is what data transfer is priced by. Simply stripping the trailing letter +// would return "us-west-2-lax-1" -- a region that does not exist. +// +// Anything that does not match is returned unchanged rather than truncated into +// a region that was never reported. +func awsRegionFromAZ(az string) string { + if az == "" { + return "" + } + // Drop the zone letter first; it is glued to the last segment ("2a", "1a"). + last := az[len(az)-1] + if last < 'a' || last > 'z' { + return az + } + trimmed := az[:len(az)-1] + + segments := strings.Split(trimmed, "-") + for i, seg := range segments { + if !isAllDigits(seg) { + continue + } + return strings.Join(segments[:i+1], "-") + } + return az +} + +func isAllDigits(s string) bool { + for _, c := range s { + if c < '0' || c > '9' { + return false + } + } + return s != "" +} + +// firstMAC takes the first entry of IMDS's newline-separated MAC list and drops +// the trailing slash IMDS puts on directory-style entries. +func firstMAC(raw string) string { + for _, line := range strings.Split(raw, "\n") { + line = strings.TrimSpace(strings.TrimSuffix(strings.TrimSpace(line), "/")) + if line != "" { + return line + } + } + return "" +} + +// --- GCP --- + +type gcpNetworkInterface struct { + IP string `json:"ip"` + Network string `json:"network"` + Subnetmask string `json:"subnetmask"` + Subnetwork string `json:"subnetwork"` + AccessConfigs []struct { + ExternalIP string `json:"externalIp"` + Type string `json:"type"` + } `json:"accessConfigs"` +} + +// resolveGCP reads the recursive instance document, which returns zone, network +// interfaces and access configs in one request. +func (r *EgressPathResolver) resolveGCP(ctx context.Context) (*gen.NodeEgressPath, error) { + base := r.base(r.gcpBase, gcpIMDSBase) + headers := map[string]string{"Metadata-Flavor": "Google"} + + body, err := r.get(ctx, base+"/computeMetadata/v1/instance/?recursive=true", headers) + if err != nil { + return nil, err + } + + var doc struct { + Zone string `json:"zone"` + NetworkInterfaces []gcpNetworkInterface `json:"networkInterfaces"` + } + if err := json.Unmarshal([]byte(body), &doc); err != nil { + return nil, fmt.Errorf("gcp metadata: %w", err) + } + + zone := gcpLastPathSegment(doc.Zone) + if zone == "" { + return nil, fmt.Errorf("gcp metadata: no zone") + } + + path := &gen.NodeEgressPath{ + CloudProvider: "gcp", + Zone: zone, + Region: gcpRegionFromZone(zone), + } + + if len(doc.NetworkInterfaces) > 0 { + nic := doc.NetworkInterfaces[0] + path.PrivateIp = nic.IP + // GCP has no VPC id; the network and subnetwork names are the closest + // equivalents and are what its billing is scoped by. + path.VpcId = gcpLastPathSegment(nic.Network) + path.SubnetId = gcpLastPathSegment(nic.Subnetwork) + // The recursive instance document either parsed or it did not, and it + // did, so the access-config list is authoritative: an external IP is + // present or it provably is not. + hasPublic := false + for _, ac := range nic.AccessConfigs { + if ac.ExternalIP != "" { + hasPublic = true + break + } + } + path.HasPublicIp = ptrBool(hasPublic) + } + + return path, nil +} + +// gcpLastPathSegment reduces GCP's fully-qualified resource paths +// ("projects/123/zones/us-central1-a") to the bare name. +func gcpLastPathSegment(v string) string { + if v == "" { + return "" + } + if i := strings.LastIndex(v, "/"); i >= 0 { + return v[i+1:] + } + return v +} + +// gcpRegionFromZone drops the trailing zone suffix: "us-central1-a" -> +// "us-central1". +func gcpRegionFromZone(zone string) string { + if i := strings.LastIndex(zone, "-"); i > 0 { + return zone[:i] + } + return zone +} + +// --- Azure --- + +// resolveAzure reads the instance document. Azure reports the zone as a bare +// ordinal ("1") scoped to the region, so it is qualified here as "-" +// to stay unambiguous alongside the other providers. +func (r *EgressPathResolver) resolveAzure(ctx context.Context) (*gen.NodeEgressPath, error) { + base := r.base(r.azureBase, azureIMDSBase) + headers := map[string]string{"Metadata": "true"} + + body, err := r.get(ctx, base+"/metadata/instance?api-version=2021-02-01", headers) + if err != nil { + return nil, err + } + + var doc struct { + Compute struct { + Location string `json:"location"` + Zone string `json:"zone"` + } `json:"compute"` + Network struct { + Interface []struct { + IPv4 struct { + IPAddress []struct { + PrivateIPAddress string `json:"privateIpAddress"` + PublicIPAddress string `json:"publicIpAddress"` + } `json:"ipAddress"` + Subnet []struct { + Address string `json:"address"` + Prefix string `json:"prefix"` + } `json:"subnet"` + } `json:"ipv4"` + } `json:"interface"` + } `json:"network"` + } + if err := json.Unmarshal([]byte(body), &doc); err != nil { + return nil, fmt.Errorf("azure metadata: %w", err) + } + if doc.Compute.Location == "" { + return nil, fmt.Errorf("azure metadata: no location") + } + + path := &gen.NodeEgressPath{ + CloudProvider: "azure", + Region: doc.Compute.Location, + } + if doc.Compute.Zone != "" { + path.Zone = doc.Compute.Location + "-" + doc.Compute.Zone + } + + if len(doc.Network.Interface) > 0 { + // An interface can carry several IPv4 configurations, and the public one + // is not necessarily the first: indexing [0] reports a node as private + // when it is not, which bills its egress at the NAT rate. Scan every + // address on every interface -- a public address anywhere means this + // node can leave the VPC directly. + hasPublic := false + for _, iface := range doc.Network.Interface { + for _, addr := range iface.IPv4.IPAddress { + if addr.PublicIPAddress != "" { + hasPublic = true + break + } + } + if hasPublic { + break + } + } + // The document parsed, so this is a definite answer either way -- unlike + // AWS, there is no per-attribute read that could have failed on its own. + path.HasPublicIp = ptrBool(hasPublic) + + // Placement comes from the primary interface. + primary := doc.Network.Interface[0] + for _, addr := range primary.IPv4.IPAddress { + if addr.PrivateIPAddress != "" { + path.PrivateIp = addr.PrivateIPAddress + break + } + } + // Azure IMDS reports the subnet as an address/prefix pair rather than a + // resource id; the CIDR is what identifies it here. + if len(primary.IPv4.Subnet) > 0 && primary.IPv4.Subnet[0].Address != "" { + path.SubnetId = primary.IPv4.Subnet[0].Address + "/" + primary.IPv4.Subnet[0].Prefix + } + } + + return path, nil +} diff --git a/internal/networkmonitor/egress_path_test.go b/internal/networkmonitor/egress_path_test.go new file mode 100644 index 00000000..c22e252f --- /dev/null +++ b/internal/networkmonitor/egress_path_test.go @@ -0,0 +1,659 @@ +package networkmonitor + +import ( + "net/http" + "net/http/httptest" + "testing" + "time" + + "github.com/go-logr/logr" +) + +// testAZ is the availability zone the AWS fixtures report; its region half is +// what awsRegionFromAZ is expected to derive. +const ( + testAZ = "us-west-2a" + testRegion = "us-west-2" +) + +// newTestResolver points a resolver at an httptest server for one provider and +// at a dead address for the others, so a probe cannot reach the real link-local +// metadata service from a developer machine or from CI. +func newTestResolver(provider, baseURL string) *EgressPathResolver { + r := &EgressPathResolver{ + log: logr.Discard(), + client: newMetadataClient(), + now: time.Now, + // An address that refuses immediately, so an unmatched provider fails + // fast instead of hanging on the real 169.254.169.254. + awsBase: "http://127.0.0.1:1", + gcpBase: "http://127.0.0.1:1", + azureBase: "http://127.0.0.1:1", + } + switch provider { + case "aws": + r.awsBase = baseURL + case "gcp": + r.gcpBase = baseURL + case "azure": + r.azureBase = baseURL + } + return r +} + +// awsIMDSFixture is an EC2 instance as IMDSv2 answers for it: the token flow, +// the AZ, the MAC list, and the per-interface network attributes. publicIPs +// empty means the instance has no public address, in which case IMDS returns +// 404 for that path -- which is how a private-subnet node is recognised. +func awsIMDSFixture(t *testing.T, az, mac, vpc, subnet, privateIP, publicIPs string) *httptest.Server { + t.Helper() + const token = "AQAEALb-fixture-token==" + mux := http.NewServeMux() + mux.HandleFunc("/latest/api/token", func(w http.ResponseWriter, req *http.Request) { + if req.Method != http.MethodPut { + w.WriteHeader(http.StatusMethodNotAllowed) + return + } + if req.Header.Get("X-aws-ec2-metadata-token-ttl-seconds") == "" { + // IMDSv2 rejects a token request without a TTL; asserting it here + // keeps the client honest about the handshake it claims to speak. + w.WriteHeader(http.StatusBadRequest) + return + } + _, _ = w.Write([]byte(token)) + }) + guarded := func(body string) http.HandlerFunc { + return func(w http.ResponseWriter, req *http.Request) { + if req.Header.Get("X-aws-ec2-metadata-token") != token { + w.WriteHeader(http.StatusUnauthorized) + return + } + if body == "" { + w.WriteHeader(http.StatusNotFound) + return + } + _, _ = w.Write([]byte(body)) + } + } + mux.HandleFunc("/latest/meta-data/placement/availability-zone", guarded(az)) + mux.HandleFunc("/latest/meta-data/mac", guarded(mac+"/\n")) + mux.HandleFunc("/latest/meta-data/local-ipv4", guarded(privateIP)) + base := "/latest/meta-data/network/interfaces/macs/" + mac + mux.HandleFunc(base+"/subnet-id", guarded(subnet)) + mux.HandleFunc(base+"/vpc-id", guarded(vpc)) + mux.HandleFunc(base+"/public-ipv4s", guarded(publicIPs)) + + srv := httptest.NewServer(mux) + t.Cleanup(srv.Close) + return srv +} + +func TestResolveAWS(t *testing.T) { + t.Run("a private-subnet node reports the NAT-bound shape", func(t *testing.T) { + srv := awsIMDSFixture(t, testAZ, "0a:1b:2c:3d:4e:5f", "vpc-0abc123", "subnet-0def456", "10.0.1.20", "") + r := newTestResolver("aws", srv.URL) + + got := r.Resolve(t.Context()) + if got == nil { + t.Fatal("expected an egress path") + } + if got.GetCloudProvider() != "aws" { + t.Errorf("cloud_provider = %q", got.GetCloudProvider()) + } + if got.GetZone() != testAZ { + t.Errorf("zone = %q", got.GetZone()) + } + if got.GetRegion() != testRegion { + t.Errorf("region = %q, want it derived from the AZ", got.GetRegion()) + } + if got.GetVpcId() != "vpc-0abc123" { + t.Errorf("vpc_id = %q", got.GetVpcId()) + } + if got.GetSubnetId() != "subnet-0def456" { + t.Errorf("subnet_id = %q", got.GetSubnetId()) + } + if got.GetPrivateIp() != "10.0.1.20" { + t.Errorf("private_ip = %q", got.GetPrivateIp()) + } + // The fact the whole change exists for: no public address means egress + // is processed by a NAT gateway, at roughly 4x the direct rate. A 404 is + // a definite answer, so presence must be set, not merely falsy. + if got.HasPublicIp == nil { + t.Fatal("a 404 on public-ipv4s is a definite 'no public address'") + } + if got.GetHasPublicIp() { + t.Error("has_public_ip should be false when IMDS 404s public-ipv4s") + } + }) + + t.Run("a node with a public address reports it", func(t *testing.T) { + srv := awsIMDSFixture(t, "eu-central-1b", "aa:bb:cc:dd:ee:ff", "vpc-pub", "subnet-pub", "10.0.2.30", "52.1.2.3") + r := newTestResolver("aws", srv.URL) + + got := r.Resolve(t.Context()) + if got == nil { + t.Fatal("expected an egress path") + } + if got.HasPublicIp == nil || !got.GetHasPublicIp() { + t.Error("has_public_ip should be a definite true when public-ipv4s is non-empty") + } + if got.GetRegion() != "eu-central-1" { + t.Errorf("region = %q", got.GetRegion()) + } + }) + + t.Run("no metadata service means no claim at all", func(t *testing.T) { + r := newTestResolver("none", "") + if got := r.Resolve(t.Context()); got != nil { + t.Errorf("expected nil, got %+v", got) + } + }) + + t.Run("the answer is cached for the process lifetime", func(t *testing.T) { + var hits int + mux := http.NewServeMux() + mux.HandleFunc("/latest/api/token", func(w http.ResponseWriter, _ *http.Request) { + hits++ + _, _ = w.Write([]byte("tok")) + }) + mux.HandleFunc("/latest/meta-data/placement/availability-zone", func(w http.ResponseWriter, _ *http.Request) { + _, _ = w.Write([]byte("us-east-1a")) + }) + mux.HandleFunc("/", func(w http.ResponseWriter, _ *http.Request) { w.WriteHeader(http.StatusNotFound) }) + srv := httptest.NewServer(mux) + defer srv.Close() + + r := newTestResolver("aws", srv.URL) + first := r.Resolve(t.Context()) + second := r.Resolve(t.Context()) + + if first == nil || second == nil { + t.Fatal("expected an egress path from both calls") + } + if hits != 1 { + t.Errorf("metadata probed %d times, want 1 -- the node's placement does not change while it runs", hits) + } + }) +} + +// TestResolveAWSUnknownPublicIP covers the outcomes that are NOT a definite +// answer. Each one must leave has_public_ip absent, because the alternative -- +// reporting false -- makes the control plane bill the node's egress at the NAT +// rate on the strength of a failed request. +func TestResolveAWSUnknownPublicIP(t *testing.T) { + const ( + token = "tok" + mac = "0a:1b:2c:3d:4e:5f" + ) + + // publicIPStatus is what the metadata service does for public-ipv4s; + // serveMAC controls whether the interface can be found at all. + newSrv := func(t *testing.T, publicIPStatus int, serveMAC bool) *EgressPathResolver { + t.Helper() + mux := http.NewServeMux() + mux.HandleFunc("/latest/api/token", func(w http.ResponseWriter, _ *http.Request) { + _, _ = w.Write([]byte(token)) + }) + mux.HandleFunc("/latest/meta-data/placement/availability-zone", func(w http.ResponseWriter, _ *http.Request) { + _, _ = w.Write([]byte(testAZ)) + }) + mux.HandleFunc("/latest/meta-data/mac", func(w http.ResponseWriter, _ *http.Request) { + if !serveMAC { + w.WriteHeader(http.StatusInternalServerError) + return + } + _, _ = w.Write([]byte(mac + "/\n")) + }) + ifaceBase := "/latest/meta-data/network/interfaces/macs/" + mac + mux.HandleFunc(ifaceBase+"/subnet-id", func(w http.ResponseWriter, _ *http.Request) { + _, _ = w.Write([]byte("subnet-0def456")) + }) + mux.HandleFunc(ifaceBase+"/vpc-id", func(w http.ResponseWriter, _ *http.Request) { + _, _ = w.Write([]byte("vpc-0abc123")) + }) + mux.HandleFunc(ifaceBase+"/public-ipv4s", func(w http.ResponseWriter, _ *http.Request) { + w.WriteHeader(publicIPStatus) + }) + mux.HandleFunc("/", func(w http.ResponseWriter, _ *http.Request) { w.WriteHeader(http.StatusNotFound) }) + srv := httptest.NewServer(mux) + t.Cleanup(srv.Close) + return newTestResolver("aws", srv.URL) + } + + cases := []struct { + name string + publicIPStatus int + serveMAC bool + wantSubnetFilled bool + }{ + {"a throttled read is not a missing address", http.StatusTooManyRequests, true, true}, + {"a server error is not a missing address", http.StatusInternalServerError, true, true}, + {"an expired token is not a missing address", http.StatusUnauthorized, true, true}, + // Without the MAC there is no interface to ask, so nothing is known + // about the egress path -- and nothing about the subnet either. + {"an unreadable interface is not a missing address", http.StatusOK, false, false}, + } + + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + got := newSrv(t, tc.publicIPStatus, tc.serveMAC).Resolve(t.Context()) + if got == nil { + t.Fatal("the AZ resolved, so the node still reports a path") + } + if got.HasPublicIp != nil { + t.Errorf("has_public_ip = %v, want absent (unknown)", got.GetHasPublicIp()) + } + // Region and zone are still worth having on their own. + if got.GetZone() != testAZ { + t.Errorf("zone = %q", got.GetZone()) + } + if gotFilled := got.GetSubnetId() != ""; gotFilled != tc.wantSubnetFilled { + t.Errorf("subnet filled = %v, want %v", gotFilled, tc.wantSubnetFilled) + } + }) + } +} + +// TestProbeFailureIsRetried pins the lifecycle rule: a success is cached +// forever, a failure is not. A node that starts before its metadata service is +// serving would otherwise report an unknown egress path until the pod restarts. +func TestProbeFailureIsRetried(t *testing.T) { + var attempts int + var serving bool + mux := http.NewServeMux() + mux.HandleFunc("/latest/api/token", func(w http.ResponseWriter, _ *http.Request) { + attempts++ + if !serving { + w.WriteHeader(http.StatusServiceUnavailable) + return + } + _, _ = w.Write([]byte("tok")) + }) + mux.HandleFunc("/latest/meta-data/placement/availability-zone", func(w http.ResponseWriter, _ *http.Request) { + _, _ = w.Write([]byte("us-east-1a")) + }) + mux.HandleFunc("/", func(w http.ResponseWriter, _ *http.Request) { w.WriteHeader(http.StatusNotFound) }) + srv := httptest.NewServer(mux) + defer srv.Close() + + clock := time.Now() + r := newTestResolver("aws", srv.URL) + r.now = func() time.Time { return clock } + + // IMDS is not up yet. + if got := r.Resolve(t.Context()); got != nil { + t.Fatal("expected no egress path while the metadata service is down") + } + // Inside the retry interval nothing is re-probed, so a flushing node does + // not hammer the metadata service every few seconds. + before := attempts + r.Resolve(t.Context()) + if attempts != before { + t.Errorf("re-probed inside the retry interval (%d -> %d)", before, attempts) + } + + // The metadata service comes up and the retry interval elapses. + serving = true + clock = clock.Add(probeRetryInterval + time.Second) + got := r.Resolve(t.Context()) + if got == nil { + t.Fatal("expected the retry to succeed once the metadata service is up") + } + if got.GetZone() != "us-east-1a" { + t.Errorf("zone = %q", got.GetZone()) + } + + // And the success sticks. + after := attempts + r.Resolve(t.Context()) + if attempts != after { + t.Errorf("re-probed after a success (%d -> %d)", after, attempts) + } +} + +// TestMetadataClientIgnoresProxy pins that link-local metadata reads never go +// through HTTP_PROXY/HTTPS_PROXY. Go's default transport uses +// ProxyFromEnvironment, so on a proxied cluster an IMDSv2 token request would +// otherwise be handed to a third party and provider detection would break. +func TestMetadataClientIgnoresProxy(t *testing.T) { + t.Setenv("HTTP_PROXY", "http://proxy.invalid:3128") + t.Setenv("HTTPS_PROXY", "http://proxy.invalid:3128") + + transport, ok := newMetadataClient().Transport.(*http.Transport) + if !ok { + t.Fatal("expected an *http.Transport") + } + if transport.Proxy != nil { + t.Error("metadata client must not consult the environment proxy") + } + + // And it actually reaches a server while those are set. + srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { + _, _ = w.Write([]byte(testAZ)) + })) + defer srv.Close() + + r := newTestResolver("aws", srv.URL) + if got := r.Resolve(t.Context()); got == nil || got.GetZone() != testAZ { + t.Error("metadata read did not reach the server with a proxy configured") + } +} + +func TestResolveGCP(t *testing.T) { + const doc = `{ + "zone": "projects/123456789/zones/us-central1-a", + "networkInterfaces": [ + { + "ip": "10.128.0.7", + "network": "projects/123456789/networks/prod-vpc", + "subnetwork": "projects/123456789/regions/us-central1/subnetworks/prod-nodes", + "accessConfigs": [] + } + ] + }` + + newGCP := func(t *testing.T, body string) *EgressPathResolver { + t.Helper() + srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { + if req.Header.Get("Metadata-Flavor") != "Google" { + // GCP's metadata server requires this header; without it the + // request must not be mistaken for a successful probe. + w.WriteHeader(http.StatusForbidden) + return + } + _, _ = w.Write([]byte(body)) + })) + t.Cleanup(srv.Close) + return newTestResolver("gcp", srv.URL) + } + + t.Run("a node with no external access config leaves through Cloud NAT", func(t *testing.T) { + got := newGCP(t, doc).Resolve(t.Context()) + if got == nil { + t.Fatal("expected an egress path") + } + if got.GetCloudProvider() != "gcp" { + t.Errorf("cloud_provider = %q", got.GetCloudProvider()) + } + if got.GetZone() != "us-central1-a" { + t.Errorf("zone = %q, want the bare zone name", got.GetZone()) + } + if got.GetRegion() != "us-central1" { + t.Errorf("region = %q", got.GetRegion()) + } + if got.GetVpcId() != "prod-vpc" { + t.Errorf("vpc_id = %q, want the network name", got.GetVpcId()) + } + if got.GetSubnetId() != "prod-nodes" { + t.Errorf("subnet_id = %q, want the subnetwork name", got.GetSubnetId()) + } + if got.GetPrivateIp() != "10.128.0.7" { + t.Errorf("private_ip = %q", got.GetPrivateIp()) + } + if got.HasPublicIp == nil { + t.Fatal("a parsed instance document is a definite answer") + } + if got.GetHasPublicIp() { + t.Error("has_public_ip should be false with no access configs") + } + }) + + t.Run("an external access config means a public address", func(t *testing.T) { + const withExternal = `{ + "zone": "projects/1/zones/europe-west1-b", + "networkInterfaces": [ + { + "ip": "10.132.0.3", + "network": "projects/1/networks/default", + "subnetwork": "projects/1/regions/europe-west1/subnetworks/default", + "accessConfigs": [{"externalIp": "34.1.2.3", "type": "ONE_TO_ONE_NAT"}] + } + ] + }` + got := newGCP(t, withExternal).Resolve(t.Context()) + if got == nil { + t.Fatal("expected an egress path") + } + if got.HasPublicIp == nil || !got.GetHasPublicIp() { + t.Error("has_public_ip should be a definite true when an access config carries an external IP") + } + if got.GetRegion() != "europe-west1" { + t.Errorf("region = %q", got.GetRegion()) + } + }) +} + +func TestResolveAzure(t *testing.T) { + const doc = `{ + "compute": {"location": "westus2", "zone": "1"}, + "network": { + "interface": [ + { + "ipv4": { + "ipAddress": [{"privateIpAddress": "10.240.0.4", "publicIpAddress": ""}], + "subnet": [{"address": "10.240.0.0", "prefix": "16"}] + } + } + ] + } + }` + + srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { + if req.Header.Get("Metadata") != "true" { + // Azure IMDS refuses without this header, which is what stops a + // browser from reaching it. Honour the same contract. + w.WriteHeader(http.StatusBadRequest) + return + } + _, _ = w.Write([]byte(doc)) + })) + defer srv.Close() + + got := newTestResolver("azure", srv.URL).Resolve(t.Context()) + if got == nil { + t.Fatal("expected an egress path") + } + if got.GetCloudProvider() != "azure" { + t.Errorf("cloud_provider = %q", got.GetCloudProvider()) + } + if got.GetRegion() != "westus2" { + t.Errorf("region = %q", got.GetRegion()) + } + // Azure reports the zone as a bare ordinal scoped to the region, so it is + // qualified to stay comparable with the other providers' zone names. + if got.GetZone() != "westus2-1" { + t.Errorf("zone = %q, want the region-qualified form", got.GetZone()) + } + if got.GetPrivateIp() != "10.240.0.4" { + t.Errorf("private_ip = %q", got.GetPrivateIp()) + } + if got.GetSubnetId() != "10.240.0.0/16" { + t.Errorf("subnet_id = %q, want the CIDR Azure reports instead of a resource id", got.GetSubnetId()) + } + if got.HasPublicIp == nil { + t.Fatal("a parsed instance document is a definite answer") + } + if got.GetHasPublicIp() { + t.Error("has_public_ip should be false for an empty publicIpAddress") + } +} + +func TestAWSRegionFromAZ(t *testing.T) { + cases := []struct { + az string + want string + }{ + {testAZ, testRegion}, + {"eu-central-1b", "eu-central-1"}, + {"ap-southeast-4c", "ap-southeast-4"}, + // A Local Zone belongs to a parent region; data transfer is priced by + // that region, not by the zone group. Naively stripping the trailing + // letter would produce "us-west-2-lax-1", which is not a region. + {"us-west-2-lax-1a", "us-west-2"}, + // GovCloud has four segments and is still a region in its own right. + {"us-gov-west-1a", "us-gov-west-1"}, + {"", ""}, + } + for _, tc := range cases { + if got := awsRegionFromAZ(tc.az); got != tc.want { + t.Errorf("awsRegionFromAZ(%q) = %q, want %q", tc.az, got, tc.want) + } + } +} + +func TestFirstMAC(t *testing.T) { + cases := []struct { + raw string + want string + }{ + {"0a:1b:2c:3d:4e:5f/", "0a:1b:2c:3d:4e:5f"}, + {"0a:1b:2c:3d:4e:5f/\n0a:ff:ff:ff:ff:ff/", "0a:1b:2c:3d:4e:5f"}, + {"\n\n0a:1b:2c:3d:4e:5f", "0a:1b:2c:3d:4e:5f"}, + {"", ""}, + } + for _, tc := range cases { + if got := firstMAC(tc.raw); got != tc.want { + t.Errorf("firstMAC(%q) = %q, want %q", tc.raw, got, tc.want) + } + } +} + +// TestIncompleteProbeIsRetried is the case the tri-state fix and the retry fix +// each get right alone and used to get wrong together. resolveAWS returns a +// non-nil path as soon as the AZ reads, so a throttled public-ipv4s read yields +// a result that is non-nil but has no egress path. Caching that permanently -- +// the obvious "cached != nil" gate -- leaves the node reporting an unknown +// egress path until its pod restarts, which is exactly what probeRetryInterval +// exists to prevent. +// +// The published answer must still carry zone, VPC and subnet in the meantime: +// those read fine and are useful on their own. +func TestIncompleteProbeIsRetried(t *testing.T) { + const ( + token = "tok" + mac = "0a:1b:2c:3d:4e:5f" + ) + var ( + publicIPCalls int + publicIPStatus = http.StatusTooManyRequests + ) + + mux := http.NewServeMux() + mux.HandleFunc("/latest/api/token", func(w http.ResponseWriter, _ *http.Request) { + _, _ = w.Write([]byte(token)) + }) + mux.HandleFunc("/latest/meta-data/placement/availability-zone", func(w http.ResponseWriter, _ *http.Request) { + _, _ = w.Write([]byte(testAZ)) + }) + mux.HandleFunc("/latest/meta-data/mac", func(w http.ResponseWriter, _ *http.Request) { + _, _ = w.Write([]byte(mac + "/\n")) + }) + ifaceBase := "/latest/meta-data/network/interfaces/macs/" + mac + mux.HandleFunc(ifaceBase+"/subnet-id", func(w http.ResponseWriter, _ *http.Request) { + _, _ = w.Write([]byte("subnet-0def456")) + }) + mux.HandleFunc(ifaceBase+"/vpc-id", func(w http.ResponseWriter, _ *http.Request) { + _, _ = w.Write([]byte("vpc-0abc123")) + }) + mux.HandleFunc(ifaceBase+"/public-ipv4s", func(w http.ResponseWriter, _ *http.Request) { + publicIPCalls++ + if publicIPStatus != http.StatusOK { + w.WriteHeader(publicIPStatus) + return + } + _, _ = w.Write([]byte("52.1.2.3")) + }) + mux.HandleFunc("/", func(w http.ResponseWriter, _ *http.Request) { w.WriteHeader(http.StatusNotFound) }) + srv := httptest.NewServer(mux) + defer srv.Close() + + clock := time.Now() + r := newTestResolver("aws", srv.URL) + r.now = func() time.Time { return clock } + + // First probe: throttled on public-ipv4s, so the egress path is unknown. + first := r.Resolve(t.Context()) + if first == nil { + t.Fatal("the AZ read fine, so a partial answer should still be published") + } + if first.HasPublicIp != nil { + t.Error("a throttled read must not produce an egress path") + } + // The partial answer is still worth having. + if first.GetZone() != testAZ || first.GetVpcId() != "vpc-0abc123" || first.GetSubnetId() != "subnet-0def456" { + t.Errorf("partial answer dropped usable placement: %+v", first) + } + + // Inside the retry interval nothing re-probes. + before := publicIPCalls + r.Resolve(t.Context()) + if publicIPCalls != before { + t.Errorf("re-probed inside the retry interval (%d -> %d)", before, publicIPCalls) + } + + // IMDS stops throttling and the interval elapses: the answer must upgrade. + publicIPStatus = http.StatusOK + clock = clock.Add(probeRetryInterval + time.Second) + second := r.Resolve(t.Context()) + if second.HasPublicIp == nil { + t.Fatal("an incomplete answer was cached permanently; the retry never fired") + } + if !second.GetHasPublicIp() { + t.Error("has_public_ip should be a definite true once public-ipv4s answers") + } + + // And now that it is definite, it sticks. + after := publicIPCalls + r.Resolve(t.Context()) + if publicIPCalls != after { + t.Errorf("re-probed after a complete answer (%d -> %d)", after, publicIPCalls) + } +} + +// TestResolveAzureScansAllAddresses pins that the public address is found +// wherever it sits in the interface's address list. Indexing [0] reports a node +// as private when it is not, and a node wrongly marked private is billed NAT at +// roughly 4x. +func TestResolveAzureScansAllAddresses(t *testing.T) { + // The public address is on the SECOND ipAddress entry. + const doc = `{ + "compute": {"location": "westus2", "zone": "1"}, + "network": { + "interface": [ + { + "ipv4": { + "ipAddress": [ + {"privateIpAddress": "10.240.0.4", "publicIpAddress": ""}, + {"privateIpAddress": "10.240.0.5", "publicIpAddress": "20.1.2.3"} + ], + "subnet": [{"address": "10.240.0.0", "prefix": "16"}] + } + } + ] + } + }` + + srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { + if req.Header.Get("Metadata") != "true" { + w.WriteHeader(http.StatusBadRequest) + return + } + _, _ = w.Write([]byte(doc)) + })) + defer srv.Close() + + got := newTestResolver("azure", srv.URL).Resolve(t.Context()) + if got == nil { + t.Fatal("expected an egress path") + } + if got.HasPublicIp == nil || !got.GetHasPublicIp() { + t.Error("a public address on a later ipAddress entry must still count") + } + // Placement still comes from the primary configuration. + if got.GetPrivateIp() != "10.240.0.4" { + t.Errorf("private_ip = %q, want the primary address", got.GetPrivateIp()) + } + if got.GetSubnetId() != "10.240.0.0/16" { + t.Errorf("subnet_id = %q", got.GetSubnetId()) + } +} diff --git a/internal/networkmonitor/monitor.go b/internal/networkmonitor/monitor.go index 6e840950..a883d915 100644 --- a/internal/networkmonitor/monitor.go +++ b/internal/networkmonitor/monitor.go @@ -4,6 +4,7 @@ import ( "context" "encoding/binary" "encoding/json" + "errors" "hash/maphash" "net/http" "sync" @@ -16,6 +17,7 @@ import ( "github.com/devzero-inc/zxporter/internal/health" "github.com/devzero-inc/zxporter/internal/networkmonitor/dns" "github.com/devzero-inc/zxporter/internal/networkmonitor/ebpf" + "github.com/devzero-inc/zxporter/internal/networkmonitor/redmetrics" "github.com/devzero-inc/zxporter/internal/transport" gen "github.com/devzero-inc/zxporter/gen/api/v1" @@ -39,6 +41,12 @@ type NetworkFlow struct { SrcDomain string `json:"src_domain,omitempty"` DstDomain string `json:"dst_domain,omitempty"` + // Post-DNAT destination, empty when the kernel did not translate one. + // DstIP stays the address the client dialed (a ClusterIP, typically); these + // name the backend that actually answered. See Entry.DstTranslated. + DstIPTranslated string `json:"dst_ip_translated,omitempty"` + DstPortTranslated uint16 `json:"dst_port_translated,omitempty"` + // Pod Metadata SrcPodName string `json:"src_pod_name,omitempty"` SrcPodNamespace string `json:"src_pod_namespace,omitempty"` @@ -81,6 +89,17 @@ type Monitor struct { dakrClient transport.DakrClient healthManager *health.HealthManager + // redMetrics is nil unless container RED metrics were explicitly enabled. The + // feature ships off, so the nil case is the normal one and every use of this + // field is guarded. + redMetrics *redmetrics.Aggregator + + // egressPath reads this node's instance metadata once and caches it, so the + // control plane can tell traffic that leaves through a NAT gateway from + // traffic that leaves directly. Nil-safe: a node with no metadata service + // reports no egress path at all. + egressPath *EgressPathResolver + mu sync.RWMutex // Cache of previous conntrack entries for delta calculation @@ -105,9 +124,22 @@ func NewMonitor(cfg Config, log logr.Logger, podCache *PodCache, ct Client, trac healthManager: healthManager, entriesCache: make(map[uint64]*Entry), podMetrics: make(map[uint64]*NetworkFlow), + egressPath: NewEgressPathResolver(log.WithName("egress-path")), }, nil } +// EnableContainerRedMetrics attaches a container RED metrics aggregator, whose +// closed minute buckets are drained on every flush. +// +// It is a setter rather than a NewMonitor parameter because the feature is off by +// default: a nil aggregator has to remain an ordinary, well-tested state, and +// threading a nil through the constructor invites the opposite. +func (m *Monitor) EnableContainerRedMetrics(agg *redmetrics.Aggregator) { + m.mu.Lock() + defer m.mu.Unlock() + m.redMetrics = agg +} + func (m *Monitor) updateHealth(component string, status health.HealthStatus, message string) { if m.healthManager != nil { m.healthManager.UpdateStatus(component, status, message, nil) @@ -132,6 +164,13 @@ func (m *Monitor) Start(ctx context.Context) { }() } + // Read the node's egress path off the collection path entirely. Probing can + // cost several seconds when the link-local route black-holes, and flush + // holds the collection lock, so doing it inline would stall collect(). + if m.egressPath != nil { + go m.egressPath.Prime(ctx) + } + for { select { case <-ctx.Done(): @@ -265,7 +304,7 @@ func (m *Monitor) collect() error { } pm.Timestamp = time.Now() } else { - m.podMetrics[groupKey] = &NetworkFlow{ + flow := &NetworkFlow{ SrcIP: entry.Src.IP().String(), DstIP: entry.Dst.IP().String(), Protocol: entry.Proto, @@ -277,6 +316,11 @@ func (m *Monitor) collect() error { Timestamp: time.Now(), lifetime: entry.Lifetime, } + if entry.HasTranslatedDst() { + flow.DstIPTranslated = entry.DstTranslated.IP().String() + flow.DstPortTranslated = entry.DstTranslated.Port() + } + m.podMetrics[groupKey] = flow } } @@ -322,7 +366,14 @@ func (m *Monitor) GetMetricsHandler(w http.ResponseWriter, r *http.Request) { } } - if dstIP, err := netaddr.ParseIP(f.DstIP); err == nil { + // Look the destination pod up by the address that answered. For a + // DNAT'd flow that is the translated address; the dialed ClusterIP + // belongs to a Service and never resolves to a pod. + dstLookupIP := f.DstIP + if f.DstIPTranslated != "" { + dstLookupIP = f.DstIPTranslated + } + if dstIP, err := netaddr.ParseIP(dstLookupIP); err == nil { if dstPod, ok := m.podCache.GetPodByIP(dstIP); ok { f.DstPodName = dstPod.Name f.DstPodNamespace = dstPod.Namespace @@ -332,19 +383,21 @@ func (m *Monitor) GetMetricsHandler(w http.ResponseWriter, r *http.Request) { // Create a copy to return (Peek behavior, no reset) flows = append(flows, &NetworkFlow{ - SrcIP: f.SrcIP, - DstIP: f.DstIP, - SrcPodName: f.SrcPodName, - SrcPodNamespace: f.SrcPodNamespace, - DstPodName: f.DstPodName, - DstPodNamespace: f.DstPodNamespace, - Protocol: f.Protocol, - DstPort: f.DstPort, - TxBytes: f.TxBytes, - RxBytes: f.RxBytes, - TxPackets: f.TxPackets, - RxPackets: f.RxPackets, - Timestamp: f.Timestamp, + SrcIP: f.SrcIP, + DstIP: f.DstIP, + DstIPTranslated: f.DstIPTranslated, + DstPortTranslated: f.DstPortTranslated, + SrcPodName: f.SrcPodName, + SrcPodNamespace: f.SrcPodNamespace, + DstPodName: f.DstPodName, + DstPodNamespace: f.DstPodNamespace, + Protocol: f.Protocol, + DstPort: f.DstPort, + TxBytes: f.TxBytes, + RxBytes: f.RxBytes, + TxPackets: f.TxPackets, + RxPackets: f.RxPackets, + Timestamp: f.Timestamp, }) } @@ -404,6 +457,15 @@ func conntrackEntryKey(e *Entry) uint64 { binary.LittleEndian.PutUint16(dstPort[:], e.Dst.Port()) h.Write(dstPort[:]) + // Included for the same reason as in entryGroupKey: if conntrack reuses an + // origin tuple against a new backend, the cached counters belong to the old + // one and must not be subtracted from the new one's. + translatedIP := e.DstTranslated.IP().As16() + h.Write(translatedIP[:]) + var translatedPort [2]byte + binary.LittleEndian.PutUint16(translatedPort[:], e.DstTranslated.Port()) + h.Write(translatedPort[:]) + h.WriteByte(e.Proto) return h.Sum64() } @@ -421,6 +483,17 @@ func entryGroupKey(e *Entry) uint64 { binary.LittleEndian.PutUint16(dstPort[:], e.Dst.Port()) h.Write(dstPort[:]) + // The translated destination is part of the identity of a flow, not a + // decoration on it. Two connections to the same ClusterIP that kube-proxy + // steered to different backend pods are different flows -- possibly in + // different availability zones, and so at different prices -- and folding + // them into one group would report a single backend for all of them. + translatedIP := e.DstTranslated.IP().As16() + h.Write(translatedIP[:]) + var translatedPort [2]byte + binary.LittleEndian.PutUint16(translatedPort[:], e.DstTranslated.Port()) + h.Write(translatedPort[:]) + h.WriteByte(e.Proto) return h.Sum64() } @@ -430,6 +503,11 @@ func (m *Monitor) flush(ctx context.Context) error { m.mu.Lock() defer m.mu.Unlock() + // Drained before the client check, not after. A minute bucket closes when the + // wall clock passes it whether or not anyone is listening, and an aggregate + // nobody drains would grow for as long as the process runs. + redItems := m.flushContainerRedMetricsLocked() + if m.dakrClient == nil { return nil } @@ -484,7 +562,14 @@ func (m *Monitor) flush(ctx context.Context) error { f.SrcPodNamespace = srcPod.Namespace } } - if dstIP, err := netaddr.ParseIP(f.DstIP); err == nil { + // Look the destination pod up by the address that answered. For a + // DNAT'd flow that is the translated address; the dialed ClusterIP + // belongs to a Service and never resolves to a pod. + dstLookupIP := f.DstIP + if f.DstIPTranslated != "" { + dstLookupIP = f.DstIPTranslated + } + if dstIP, err := netaddr.ParseIP(dstLookupIP); err == nil { if dstPod, ok := m.podCache.GetPodByIP(dstIP); ok { f.DstPodName = dstPod.Name f.DstPodNamespace = dstPod.Namespace @@ -515,6 +600,9 @@ func (m *Monitor) flush(ctx context.Context) error { TxPackets: int64(f.TxPackets), RxPackets: int64(f.RxPackets), Timestamp: timestamppb.New(f.Timestamp), + + DstIpTranslated: f.DstIPTranslated, + DstPortTranslated: int32(f.DstPortTranslated), }) // Reset counters (Delta behavior for Control Plane) @@ -530,7 +618,7 @@ func (m *Monitor) flush(ctx context.Context) error { } if len(items) == 0 && len(dnsLookupItems) == 0 { - return nil + return m.sendContainerRedMetricsLocked(ctx, redItems) } // 4. Send Request @@ -543,6 +631,54 @@ func (m *Monitor) flush(ctx context.Context) error { OperatorCommit: &m.cfg.OperatorCommit, } + // Describes the node, not any single flow, so it rides once per report. + // + // Cached never touches the network: this runs under m.mu, and a probe here + // would block collect() for as long as the metadata service takes to fail. + // If the answer is still unknown -- a node that started before its metadata + // service did -- kick another attempt off the lock. The resolver rate-limits + // those itself and keeps the first success forever. + if m.egressPath != nil { + if egress := m.egressPath.Cached(); egress != nil { + req.NodeEgress = egress + } else { + go m.egressPath.Prime(ctx) + } + } + _, err := m.dakrClient.SendNetworkTrafficMetrics(ctx, req) + + // Both sends are attempted and both errors are reported. They carry different + // grains to different tables, so letting one failure skip the other would + // discard data that was ready to go for a reason unrelated to it. + return errors.Join(err, m.sendContainerRedMetricsLocked(ctx, redItems)) +} + +// flushContainerRedMetricsLocked drains the aggregator's closed minute buckets, +// returning nil when the feature is off. The caller holds the lock. +func (m *Monitor) flushContainerRedMetricsLocked() []*gen.ContainerRedMetricsItem { + if m.redMetrics == nil { + return nil + } + return m.redMetrics.Flush() +} + +// sendContainerRedMetricsLocked ships one batch of closed minute buckets. +// +// There is no retry and no buffering of a failed batch. The items are per-minute +// deltas destined for a SummingMergeTree, so a re-sent batch adds to whatever +// already landed and reads as real extra traffic. A lost minute is visible as a +// gap; a doubled one is invisible and wrong. +func (m *Monitor) sendContainerRedMetricsLocked(ctx context.Context, items []*gen.ContainerRedMetricsItem) error { + if len(items) == 0 || m.dakrClient == nil { + return nil + } + + _, err := m.dakrClient.SendContainerRedMetrics(ctx, &gen.SendContainerRedMetricsRequest{ + NodeName: m.cfg.NodeName, + Items: items, + OperatorVersion: &m.cfg.OperatorVersion, + OperatorCommit: &m.cfg.OperatorCommit, + }) return err } diff --git a/internal/networkmonitor/monitor_redmetrics_test.go b/internal/networkmonitor/monitor_redmetrics_test.go new file mode 100644 index 00000000..24e1cde9 --- /dev/null +++ b/internal/networkmonitor/monitor_redmetrics_test.go @@ -0,0 +1,283 @@ +package networkmonitor + +import ( + "context" + "net/netip" + "sync" + "testing" + "time" + + "github.com/go-logr/logr" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + gen "github.com/devzero-inc/zxporter/gen/api/v1" + "github.com/devzero-inc/zxporter/internal/networkmonitor/redmetrics" + "github.com/devzero-inc/zxporter/internal/transport" +) + +// fakeDakrClient captures what the monitor actually put on the wire. +// +// It embeds the interface rather than implementing it: transport.DakrClient carries +// a dozen methods this test has no opinion about, and stubbing all of them would +// bury the one call under test. An unexpected call panics on the nil embedded value, +// which is the right outcome -- a flush reaching for anything else is a finding. +type fakeDakrClient struct { + transport.DakrClient + + mu sync.Mutex + requests []*gen.SendContainerRedMetricsRequest +} + +func (c *fakeDakrClient) SendContainerRedMetrics( + ctx context.Context, + req *gen.SendContainerRedMetricsRequest, +) (*gen.SendContainerRedMetricsResponse, error) { + c.mu.Lock() + defer c.mu.Unlock() + c.requests = append(c.requests, req) + return &gen.SendContainerRedMetricsResponse{ProcessedCount: int32(len(req.Items))}, nil +} + +func (c *fakeDakrClient) SendNetworkTrafficMetrics( + ctx context.Context, + req *gen.SendNetworkTrafficMetricsRequest, +) (*gen.SendNetworkTrafficMetricsResponse, error) { + return &gen.SendNetworkTrafficMetricsResponse{}, nil +} + +func (c *fakeDakrClient) sent() []*gen.SendContainerRedMetricsRequest { + c.mu.Lock() + defer c.mu.Unlock() + out := make([]*gen.SendContainerRedMetricsRequest, len(c.requests)) + copy(out, c.requests) + return out +} + +// redTestClock drives the aggregator's notion of when a minute closed. +type redTestClock struct { + mu sync.Mutex + now time.Time +} + +func (c *redTestClock) Now() time.Time { + c.mu.Lock() + defer c.mu.Unlock() + return c.now +} + +func (c *redTestClock) set(t time.Time) { + c.mu.Lock() + defer c.mu.Unlock() + c.now = t +} + +const ( + redTestPodIP = "10.42.0.5" + redTestPort = 8080 +) + +// redTestPodCache seeds a standalone pod cache with one served pod, exactly as the +// informer would have populated it. +func redTestPodCache() *PodCache { + pc := NewPodCache(nil) + pc.updatePod(&corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Name: "checkout-0", + Namespace: "shop", + UID: "uid-checkout-0", + }, + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + {Name: "app", Ports: []corev1.ContainerPort{{ContainerPort: redTestPort}}}, + {Name: "istio-proxy", Ports: []corev1.ContainerPort{{ContainerPort: 15001}}}, + }, + }, + Status: corev1.PodStatus{PodIP: redTestPodIP}, + }) + return pc +} + +func redTestEvent(cookie uint64, at time.Time) redmetrics.SocketEvent { + return redmetrics.SocketEvent{ + SocketCookie: cookie, + Path: redmetrics.SyscallRecv, + LocalAddr: netip.MustParseAddr(redTestPodIP), + LocalPort: redTestPort, + RemoteAddr: netip.MustParseAddr("192.168.9.9"), + RemotePort: 40000, + TrueLen: 256, + Timestamp: at, + } +} + +func redTestMonitor(t *testing.T, client transport.DakrClient) *Monitor { + t.Helper() + m, err := NewMonitor( + Config{NodeName: "node-a", OperatorVersion: "v-test", OperatorCommit: "sha-test"}, + logr.Discard(), + redTestPodCache(), + nil, nil, nil, + client, + nil, + ) + if err != nil { + t.Fatalf("NewMonitor: %v", err) + } + return m +} + +func redTestTime(t *testing.T, s string) time.Time { + t.Helper() + parsed, err := time.Parse(time.RFC3339, s) + if err != nil { + t.Fatalf("bad timestamp %q: %v", s, err) + } + return parsed +} + +// TestMonitorShipsContainerRedMetrics drives the whole agent path a socket event +// travels: probe observation, port attribution against the real pod cache, minute +// bucketing, and the flush that puts a proto request on the wire. Everything below +// the probe itself is the real implementation -- the fake stops at the transport. +func TestMonitorShipsContainerRedMetrics(t *testing.T) { + client := &fakeDakrClient{} + monitor := redTestMonitor(t, client) + + clock := &redTestClock{now: redTestTime(t, "2026-08-25T09:00:20Z")} + agg := redmetrics.NewAggregator(redmetrics.Config{}, clock, monitor.podCache, logr.Discard()) + monitor.EnableContainerRedMetrics(agg) + + // Two connections in the 09:00 minute, one of them chatty. + agg.Observe(redTestEvent(1, clock.Now())) + agg.Observe(redTestEvent(1, clock.Now())) + agg.Observe(redTestEvent(1, clock.Now())) + agg.Observe(redTestEvent(2, clock.Now())) + + clock.set(redTestTime(t, "2026-08-25T09:01:10Z")) + if err := monitor.flush(context.Background()); err != nil { + t.Fatalf("flush: %v", err) + } + + sent := client.sent() + if len(sent) != 1 { + t.Fatalf("got %d requests, want 1", len(sent)) + } + + req := sent[0] + if req.NodeName != "node-a" { + t.Fatalf("node_name = %q, want node-a", req.NodeName) + } + if req.GetOperatorVersion() != "v-test" || req.GetOperatorCommit() != "sha-test" { + t.Fatalf("operator identity = %q/%q", req.GetOperatorVersion(), req.GetOperatorCommit()) + } + if len(req.Items) != 1 { + t.Fatalf("got %d items, want 1", len(req.Items)) + } + + item := req.Items[0] + if item.PodName != "checkout-0" || item.PodNamespace != "shop" || + item.PodUid != "uid-checkout-0" || item.ContainerName != "app" { + t.Fatalf("attribution = %+v", item) + } + if item.ConnCount != 2 { + t.Fatalf("conn_count = %d, want 2 connections (not 4 observations)", item.ConnCount) + } + if got := item.Timestamp.AsTime(); !got.Equal(redTestTime(t, "2026-08-25T09:00:00Z")) { + t.Fatalf("bucket minute = %v, want 09:00:00", got) + } + // The server rejects the entire request on any other length, so this is a + // contract test rather than a tidiness check. + if len(item.DurationBuckets) != redmetrics.DurationBucketCount { + t.Fatalf("duration_buckets length = %d, want %d", + len(item.DurationBuckets), redmetrics.DurationBucketCount) + } +} + +// TestMonitorSendsNothingWhenFeatureIsOff pins the off-by-default guarantee at the +// level that matters. The aggregator is never attached, and the monitor must behave +// exactly as it did before this feature existed -- no request, no error. +func TestMonitorSendsNothingWhenFeatureIsOff(t *testing.T) { + client := &fakeDakrClient{} + monitor := redTestMonitor(t, client) + + if err := monitor.flush(context.Background()); err != nil { + t.Fatalf("flush: %v", err) + } + if sent := client.sent(); len(sent) != 0 { + t.Fatalf("got %d requests, want none while the feature is off", len(sent)) + } +} + +// TestMonitorSendsNothingForAnEmptyMinute pins that a quiet minute costs nothing. A +// DaemonSet flushing on a ticker across a large fleet must not send an empty request +// per node per interval merely to say so. +func TestMonitorSendsNothingForAnEmptyMinute(t *testing.T) { + client := &fakeDakrClient{} + monitor := redTestMonitor(t, client) + + clock := &redTestClock{now: redTestTime(t, "2026-08-25T09:00:20Z")} + monitor.EnableContainerRedMetrics( + redmetrics.NewAggregator(redmetrics.Config{}, clock, monitor.podCache, logr.Discard()), + ) + + clock.set(redTestTime(t, "2026-08-25T09:02:00Z")) + if err := monitor.flush(context.Background()); err != nil { + t.Fatalf("flush: %v", err) + } + if sent := client.sent(); len(sent) != 0 { + t.Fatalf("got %d requests, want none for a minute with no traffic", len(sent)) + } +} + +// TestMonitorDrainsWithoutADakrClient pins the drain-before-the-client-check +// ordering. A minute closes whether or not anyone is listening; if the aggregator +// were only drained once a client existed, a node running without one would +// accumulate closed buckets for as long as the process lived. +func TestMonitorDrainsWithoutADakrClient(t *testing.T) { + monitor := redTestMonitor(t, nil) + + clock := &redTestClock{now: redTestTime(t, "2026-08-25T09:00:20Z")} + agg := redmetrics.NewAggregator(redmetrics.Config{}, clock, monitor.podCache, logr.Discard()) + monitor.EnableContainerRedMetrics(agg) + agg.Observe(redTestEvent(1, clock.Now())) + + clock.set(redTestTime(t, "2026-08-25T09:01:10Z")) + if err := monitor.flush(context.Background()); err != nil { + t.Fatalf("flush: %v", err) + } + + // The minute was drained and discarded, not held. Re-flushing with a client + // attached would resurface it if it had merely been parked. + if items := agg.Flush(); len(items) != 0 { + t.Fatalf("got %+v, want the closed minute already drained", items) + } +} + +// TestMonitorEmitsEachMinuteOnce pins the property the absence of retry rests on: +// consecutive flushes never re-send a minute, so nothing downstream can double count +// one by being called twice. +func TestMonitorEmitsEachMinuteOnce(t *testing.T) { + client := &fakeDakrClient{} + monitor := redTestMonitor(t, client) + + clock := &redTestClock{now: redTestTime(t, "2026-08-25T09:00:20Z")} + agg := redmetrics.NewAggregator(redmetrics.Config{}, clock, monitor.podCache, logr.Discard()) + monitor.EnableContainerRedMetrics(agg) + agg.Observe(redTestEvent(1, clock.Now())) + + clock.set(redTestTime(t, "2026-08-25T09:01:10Z")) + for i := 0; i < 3; i++ { + if err := monitor.flush(context.Background()); err != nil { + t.Fatalf("flush %d: %v", i, err) + } + } + + sent := client.sent() + if len(sent) != 1 { + t.Fatalf("got %d requests, want the minute sent exactly once", len(sent)) + } + if sent[0].Items[0].ConnCount != 1 { + t.Fatalf("conn_count = %d, want 1", sent[0].Items[0].ConnCount) + } +} diff --git a/internal/networkmonitor/redmetrics/aggregator.go b/internal/networkmonitor/redmetrics/aggregator.go new file mode 100644 index 00000000..7e91807d --- /dev/null +++ b/internal/networkmonitor/redmetrics/aggregator.go @@ -0,0 +1,716 @@ +package redmetrics + +import ( + "net/netip" + "sort" + "sync" + "time" + + "github.com/go-logr/logr" + "google.golang.org/protobuf/types/known/timestamppb" + "inet.af/netaddr" + corev1 "k8s.io/api/core/v1" + + gen "github.com/devzero-inc/zxporter/gen/api/v1" +) + +// DurationBucketCount is the number of cumulative latency buckets carried by one +// emitted item. The server REJECTS THE WHOLE REQUEST when an item carries any +// other number, on the grounds that a length mismatch means the two sides no +// longer agree about what the buckets mean. +const DurationBucketCount = 14 + +// PodLookup resolves a local pod by its IP. +// +// It is declared here rather than imported because the concrete cache lives in +// the parent package, which already depends on this one. The one method is +// satisfied structurally, so no wiring is needed at the call site. +type PodLookup interface { + GetPodByIP(ip netaddr.IP) (*corev1.Pod, bool) +} + +// Config tunes the aggregator's memory ceilings and how long it waits for an +// answer. None of them changes what is measured, only how much of it the agent is +// willing to hold and how long it holds it. +type Config struct { + // ConnTTL is how long a connection's state survives without an observation. + // + // It is a leak backstop rather than the mechanism that reclaims connections. The + // The probe attaches to both ways a connection ends, so this bounds only the + // entries whose close was never seen -- an event lost to a full ring buffer, or a + // socket already open when the probe attached. + // + // Which is why it can be generous, and why it should be. When idleness was the + // only available death signal this had to be short enough to bound the table, + // and a short one is actively destructive: a gRPC stream or an HTTP keep-alive + // connection quiet for longer than the TTL was swept, then decoded again from + // the middle when it resumed -- where nothing parses, so it landed opaque -- or, + // if its next syscall was a send, classified outbound and ignored for the rest + // of its life. Neither is a small loss on a node serving long-lived connections. + // + // Keep it at or above RequestTTL. Below it, a connection can be reclaimed while + // it still holds requests the sweep has not yet had cause to file, and those + // requests are lost rather than counted unanswered. + ConnTTL time.Duration + + // MaxConnections caps concurrently tracked connections. Beyond it, new + // connections are dropped rather than tracked -- the alternative is an + // unbounded map on a DaemonSet that shares a node with the workloads it + // measures. + // + // Outbound connections count against it too, even though nothing is measured + // about them. The cap bounds memory, and an outbound entry occupies the table + // exactly as an inbound one does; excluding them would make the number stop + // describing the thing it exists to bound. On a node whose outbound churn is + // large enough to fill the table this does cost inbound coverage, which the + // one-shot capacity log is there to make visible. + // + // What the cap holds changed with the close probe. It used to hold every + // connection seen within ConnTTL, which on a node churning short-lived + // connections is overwhelmingly sockets the kernel had already freed -- so the + // table filled with corpses and refused live connections at a fraction of the + // offered rate. It now holds connections that are actually open, which is a + // number set by concurrency rather than by rate. + MaxConnections int + + // RequestTTL is how long a request may stay outstanding before it is filed as + // unanswered: counted in request_count, in no latency bucket, and not in + // error_count. + // + // It is a deadline on the agent's patience, not a statement about the service. + // A request answered after it has been swept is not filed twice -- the response + // finds nothing outstanding and is dropped -- so raising this trades memory for + // the latency of genuinely slow requests being measured rather than merely + // counted, and lowering it trades the other way. + // + // It applies only to a request left outstanding on a connection that stays open. + // A connection that closes settles everything it was still holding at the close, + // so this does not have to be long enough to cover a client that hangs up. + RequestTTL time.Duration + + // MaxInFlightRequests caps outstanding requests across all connections. A + // request arriving with no room takes its connection opaque; see + // startRequestLocked for why the connection cannot simply skip it. + MaxInFlightRequests int + + // MaxBufferedBytes caps the bytes held over across socket events, across all + // connections, for units that arrived split. It exists because the per-buffer + // cap alone does not bound the agent: MaxConnections connections each holding + // two halves of one legitimately-split frame is a ceiling in the gigabytes, on + // a DaemonSet that shares a node with the workloads it measures. + // + // A connection that cannot get room takes itself opaque rather than resuming + // from the wrong offset, so exhausting this degrades coverage into conn_count + // instead of degrading the node. + MaxBufferedBytes int +} + +// DefaultConfig returns the shipped ceilings. +func DefaultConfig() Config { + return Config{ + ConnTTL: 10 * time.Minute, + MaxConnections: 16384, + RequestTTL: 15 * time.Second, + MaxInFlightRequests: 16384, + MaxBufferedBytes: 8 << 20, + } +} + +// connKey identifies one connection for as long as the aggregator tracks it. +// +// The socket cookie alone will not do. It is a kernel socket address, unique only +// among sockets alive at the same instant, and the kernel recycles those +// structures aggressively -- a node churning short-lived connections hands the +// same address to a new connection well inside the TTL. Keyed on the cookie +// alone, that new connection would be mistaken for the old one, inheriting its +// decode position and its outstanding requests: not merely undercounted, but +// paired wrongly. +// +// Pairing the cookie with the tuple it served closes that: a recycled socket +// serving a different peer is a different key and starts clean. What remains is a +// socket recycled onto the identical four-tuple within the TTL, which requires +// the peer to reuse its ephemeral port too, and which is the same logical client +// reconnecting in any case. +type connKey struct { + cookie uint64 + localAddr netip.Addr + localPort uint16 + remoteAddr netip.Addr + remotePort uint16 +} + +func connKeyOf(ev SocketEvent) connKey { + return connKey{ + cookie: ev.SocketCookie, + localAddr: ev.LocalAddr, + localPort: ev.LocalPort, + remoteAddr: ev.RemoteAddr, + remotePort: ev.RemotePort, + } +} + +// maxListeningPorts bounds the set of local endpoints remembered as inbound. +// +// It is a constant rather than a Config knob because there is no deployment whose +// answer differs: the set holds one entry per local endpoint this node serves on, +// which is a number set by how many listening containers the kubelet is running, +// not by traffic. Four thousand of them is already far past any node's reality. +// +// Overflowing it costs nothing that was being measured. A connection whose port is +// not in the set is treated exactly as it was before the set existed -- outbound, +// ignored -- so the only loss is the chance to re-acquire a connection that was +// already open when the agent started. +const maxListeningPorts = 4096 + +// listenKey is a local endpoint this node has been observed receiving on. +// +// The address is part of it because a port on its own says nothing: 8080 on one +// pod is a server and 8080 on another is whatever the kernel handed out. Pod IPs +// are recycled, so an entry outliving its pod could match a new pod's ephemeral +// port -- which is why entries expire on the same TTL as connections rather than +// living for the process's lifetime. +type listenKey struct { + addr netip.Addr + port uint16 +} + +// bucketKey is the aggregation key: a minute and a container, deliberately not a +// peer. Keying on the caller is exactly what would make this map grow with the +// number of distinct clients rather than with the number of containers the +// kubelet is running. +type bucketKey struct { + minute time.Time + container ContainerKey +} + +// counters accumulates one bucket's deltas. +// +// Every field is additive across agents and across minutes, which is the whole +// reason the table can be a SummingMergeTree -- except durationMaxUs, which merges +// as a maximum, and the histogram, whose buckets are additive one by one. +type counters struct { + // requestCount counts every request observed, answered or not. + requestCount uint64 + + // errorCount counts answered requests whose status was a 5XX. + errorCount uint64 + + // durationSumUs and hist cover ANSWERED requests only. An unanswered request + // has no duration to add, which is why the mean is this sum over the last + // histogram bucket rather than over requestCount. + durationSumUs uint64 + hist histogram + + // durationMaxUs is the slowest answered request in the bucket. + durationMaxUs uint64 + + // connCount counts connections that could not be decoded, at the moment they + // were given up on. A connection decoded cleanly for its whole life contributes + // nothing here; one that decoded for a while and then desynchronised contributes + // to this AND to requestCount, for the requests it did read. + connCount uint64 +} + +// Aggregator turns socket observations into per-container, per-minute items. +// +// Its output is deltas. A bucket closes when the wall clock passes the minute it +// covers, is emitted exactly once, and is never reopened -- an observation +// arriving for a closed minute is dropped rather than filed late, because a late +// row is indistinguishable from real extra traffic once the SummingMergeTree has +// merged it. For the same reason nothing that consumes Flush may retry. +type Aggregator struct { + cfg Config + clock Clock + pods PodLookup + log logr.Logger + + mu sync.Mutex + conns map[connKey]*connection + buckets map[bucketKey]*counters + + // listening is the local endpoints seen receiving before they sent, with the + // last time each was seen. It is how a connection that was already open when + // the agent started is recognised as inbound; see trackLocked. + listening map[listenKey]time.Time + + // inFlight is the total outstanding requests across all connections, kept as a + // running count because MaxInFlightRequests is a bound on the agent's memory + // rather than on any one connection. + inFlight int + + // buffered is the total bytes held over across socket events, for the same + // reason: the bound that matters is the agent's, not one connection's. + buffered int + + // closedBefore is the first minute that has NOT been emitted. Everything + // earlier is final and gone. + closedBefore time.Time + + // capacityLogged keeps the connection-cap warning to one line per process + // rather than one per dropped connection. + capacityLogged bool +} + +// NewAggregator builds an aggregator. A zero-valued Config field takes the +// default, so callers can override one knob without restating the others. +func NewAggregator(cfg Config, clock Clock, pods PodLookup, log logr.Logger) *Aggregator { + defaults := DefaultConfig() + if cfg.ConnTTL <= 0 { + cfg.ConnTTL = defaults.ConnTTL + } + if cfg.MaxConnections <= 0 { + cfg.MaxConnections = defaults.MaxConnections + } + if cfg.RequestTTL <= 0 { + cfg.RequestTTL = defaults.RequestTTL + } + if cfg.MaxInFlightRequests <= 0 { + cfg.MaxInFlightRequests = defaults.MaxInFlightRequests + } + if cfg.MaxBufferedBytes <= 0 { + cfg.MaxBufferedBytes = defaults.MaxBufferedBytes + } + if clock == nil { + clock = SystemClock{} + } + + return &Aggregator{ + cfg: cfg, + clock: clock, + pods: pods, + log: log, + conns: make(map[connKey]*connection), + buckets: make(map[bucketKey]*counters), + listening: make(map[listenKey]time.Time), + } +} + +// Observe files one socket observation. It is safe for concurrent use and is on +// the probe's hot path, so the expensive work -- pod lookup and port +// attribution -- happens once per connection and never again. +func (a *Aggregator) Observe(ev SocketEvent) { + a.mu.Lock() + defer a.mu.Unlock() + + key := connKeyOf(ev) + conn, ok := a.conns[key] + + // Handled before the capacity gate below, and before the ignored/opaque return + // further down. A close is not an observation of traffic: it must not consume an + // admission slot, and it has to be acted on for a connection whose decoding has + // already been given up, because releasing that state is the whole point. + if ev.Path == SyscallClose { + if ok { + a.closeLocked(key, conn, ev.Timestamp) + } + return + } + + if !ok { + if len(a.conns) >= a.cfg.MaxConnections { + if !a.capacityLogged { + a.capacityLogged = true + a.log.Info("container RED metrics connection table full, dropping new connections", + "max_connections", a.cfg.MaxConnections) + } + return + } + conn = a.trackLocked(ev) + a.conns[key] = conn + } + conn.lastSeen = ev.Timestamp + + // An ignored connection has nothing to measure and an opaque one has nothing + // left to read; both are kept in the table so that neither conclusion is reached + // twice, and so a resumed connection is not counted again. + if conn.state == stateIgnored || conn.state == stateOpaque { + return + } + + a.decodeLocked(conn, ev) +} + +// trackLocked decides, once, what a newly seen connection is worth measuring. +// +// Bytes arriving before any were sent mean this node is answering a request; the +// other way round usually means it is making one, and outbound connections are +// somebody else's inbound, measured on the node that serves them. +// +// Usually, but not always -- and the exception is the reason this is not simply a +// test of the first syscall. The first syscall is the first one THIS AGENT SAW, +// which for a connection already open when the agent started has nothing to do with +// who opened it. A server holding long-lived keep-alive connections restarts its +// agent and every one of those connections is written off as outbound at whatever +// syscall happened to arrive first, permanently: the state is kept precisely so the +// decision is never revisited, and the connections outlive the process. What that +// looks like from the outside is a service that stops reporting traffic after an +// agent upgrade and does not start again until its clients reconnect. +// +// The discriminator that survives a restart is the local port. An inbound +// connection is served on the port the container listens on, which every other +// inbound connection to that container shares; an outbound one goes out on an +// ephemeral port belonging to that connection alone. So a send-first connection on +// a port this node has been seen RECEIVING on is a connection the agent joined +// late, not one this node opened, and it is picked up rather than written off. +// +// The set of such ports is learned from the recv-first connections themselves +// rather than read out of the pod spec. containerPort is advisory in Kubernetes -- +// a container that declares nothing still listens perfectly well -- and consulting +// the spec would mean a pod lookup for every outbound connection on the node, which +// is a real cost on the agent's hot path to answer a question the traffic answers +// by itself within one request. +func (a *Aggregator) trackLocked(ev SocketEvent) *connection { + // stateNew is the zero value, and is named here rather than left implicit + // because it is the only place the decision is made: everything below is a reason + // NOT to decode, so a connection that reaches the end of this function is one the + // decoder will read -- as soon as its first bytes say which protocol to read it + // as. + conn := &connection{lastSeen: ev.Timestamp, state: stateNew} + + if ev.Path == SyscallRecv { + container, ok := a.attribute(ev) + if !ok { + conn.state = stateIgnored + return conn + } + conn.container = container + + // Recorded only once attribution has succeeded. An endpoint nothing can be + // filed against is no use to a later connection either, and admitting it + // would spend the set's capacity on ports that cannot produce a measurement. + a.learnListeningLocked(ev) + return conn + } + + // A send on a port nothing has been received on. That is what an outbound + // connection looks like, and it is by far the common case -- so this returns + // before the pod lookup rather than after it. + if !a.isListeningLocked(ev) { + conn.state = stateIgnored + return conn + } + + container, ok := a.attribute(ev) + if !ok { + conn.state = stateIgnored + return conn + } + + conn.container = container + conn.state = stateJoined + return conn +} + +// learnListeningLocked remembers that this node received on a local endpoint. The +// caller holds the lock. +// +// The timestamp is refreshed on every inbound connection, so an endpoint stays +// known for as long as it is being served and expires only once it stops. +func (a *Aggregator) learnListeningLocked(ev SocketEvent) { + key := listenKey{addr: ev.LocalAddr, port: ev.LocalPort} + if _, ok := a.listening[key]; !ok && len(a.listening) >= maxListeningPorts { + return + } + a.listening[key] = ev.Timestamp +} + +// isListeningLocked reports whether an event's local endpoint is one this node has +// been seen receiving on. The caller holds the lock. +func (a *Aggregator) isListeningLocked(ev SocketEvent) bool { + _, ok := a.listening[listenKey{addr: ev.LocalAddr, port: ev.LocalPort}] + return ok +} + +// attribute resolves the container serving an inbound connection, reporting +// false when it cannot be established. Failure is silent by design: there is no +// loss counter in this scope, and a guess would file one workload's traffic +// against another workload's resource recommendations. +// +// It touches no mutable aggregator state -- only the pod cache, which has its own +// synchronisation -- so it carries no Locked suffix and may be called with or +// without the aggregator's lock held. +func (a *Aggregator) attribute(ev SocketEvent) (ContainerKey, bool) { + if a.pods == nil { + return ContainerKey{}, false + } + + ip, ok := toNetaddr(ev.LocalAddr) + if !ok { + return ContainerKey{}, false + } + + pod, ok := a.pods.GetPodByIP(ip) + if !ok || pod == nil { + return ContainerKey{}, false + } + + name, ok := containerForPort(pod, ev.LocalPort) + if !ok { + return ContainerKey{}, false + } + + return ContainerKey{ + PodName: pod.Name, + PodNamespace: pod.Namespace, + PodUID: string(pod.UID), + ContainerName: name, + }, true +} + +// countersLocked is the bucket a measurement taken at this instant belongs in, or +// nil when that minute has already been emitted. +// +// EVERY path that files anything goes through here. The frontier check cannot live +// at the call sites: a measurement's minute is the minute it BECAME FINAL, and the +// sweep files requests under a deadline that has already passed, so "now" is not a +// safe proxy for "not yet closed" anywhere in this file. +func (a *Aggregator) countersLocked(at time.Time, container ContainerKey) *counters { + minute := at.Truncate(time.Minute) + if !a.closedBefore.IsZero() && minute.Before(a.closedBefore) { + return nil + } + + key := bucketKey{minute: minute, container: container} + bucket, ok := a.buckets[key] + if !ok { + bucket = &counters{} + a.buckets[key] = bucket + } + return bucket +} + +// fileAnsweredLocked records one request that was observed completing, against the +// minute it completed in. +// +// It is the counterpart of fileUnansweredLocked, and the pair is two functions +// rather than one with an answered flag because the flag made the caller pass a +// duration and a status that would be ignored: an unanswered request was filed as +// (0, 0, false), a triple that reads as a request answered instantly with status +// zero. Splitting them makes that state unrepresentable. +// +// It takes an error BIT rather than a status because the two protocols disagree +// about what an error is. On HTTP/1.1 it is a 5XX. On HTTP/2 it is a 5XX or a +// non-zero grpc-status on a response that said 200, and there is no status value +// that expresses the second -- a caller reducing it to a fake 500 would be filing a +// number the server never sent. +func (a *Aggregator) fileAnsweredLocked(c *connection, at time.Time, durationUs int64, isError bool) { + bucket := a.countersLocked(at, c.container) + if bucket == nil { + return + } + + bucket.requestCount++ + + if isError { + bucket.errorCount++ + } + + us := uint64(durationUs) + bucket.durationSumUs += us + if us > bucket.durationMaxUs { + bucket.durationMaxUs = us + } + bucket.hist.observe(durationUs) +} + +// fileUnansweredLocked records one request the agent gave up waiting for, against +// the minute it was given up in. +// +// An unanswered request is a request that happened, so it counts -- but it has no +// duration and no status, so it enters no latency bucket and cannot be an error. +// That asymmetry is deliberate and is why request_count and the last latency bucket +// are allowed to disagree. +func (a *Aggregator) fileUnansweredLocked(c *connection, at time.Time) { + bucket := a.countersLocked(at, c.container) + if bucket == nil { + return + } + bucket.requestCount++ +} + +// Flush closes every minute the wall clock has passed and returns them as items, +// holding back the minute in progress. Buckets are removed as they are emitted, +// so a second call returns nothing new -- and a caller that drops the result +// loses those measurements, which is the correct trade against re-sending them. +func (a *Aggregator) Flush() []*gen.ContainerRedMetricsItem { + a.mu.Lock() + defer a.mu.Unlock() + + // The clock is read under the lock so that two concurrent flushes cannot + // interleave a stale reading with a fresh one. Reading it first let the + // goroutine holding the earlier timestamp win the race to assign + // closedBefore, moving the frontier backwards and reopening a minute that + // had already been emitted -- a double count no test upstream could see. + now := a.clock.Now() + current := now.Truncate(time.Minute) + + // Swept BEFORE the buckets are collected, not after. The sweep files timed-out + // requests under a deadline already in the past, so running it afterwards would + // leave those requests sitting in a bucket this flush had just declared closed, + // where the next flush would refuse to emit them. + a.sweepLocked(now) + + items := make([]*gen.ContainerRedMetricsItem, 0, len(a.buckets)) + for key, bucket := range a.buckets { + if !key.minute.Before(current) { + continue + } + items = append(items, &gen.ContainerRedMetricsItem{ + PodName: key.container.PodName, + PodNamespace: key.container.PodNamespace, + PodUid: key.container.PodUID, + ContainerName: key.container.ContainerName, + Timestamp: timestamppb.New(key.minute), + RequestCount: bucket.requestCount, + ErrorCount: bucket.errorCount, + DurationSumUs: bucket.durationSumUs, + DurationMaxUs: bucket.durationMaxUs, + ConnCount: bucket.connCount, + DurationBuckets: bucket.hist.cumulative(), + }) + delete(a.buckets, key) + } + + // Monotonic by construction, for the same reason: the frontier is a claim that + // everything earlier is final, and a claim like that can only ever move forward. + if current.After(a.closedBefore) { + a.closedBefore = current + } + + // Map iteration order is random, so sort to make the emitted batch a function + // of what was observed rather than of Go's hash seed. Tests depend on it and + // so does anybody reading a log of what was sent. + sort.Slice(items, func(i, j int) bool { + l, r := items[i], items[j] + if !l.Timestamp.AsTime().Equal(r.Timestamp.AsTime()) { + return l.Timestamp.AsTime().Before(r.Timestamp.AsTime()) + } + if l.PodNamespace != r.PodNamespace { + return l.PodNamespace < r.PodNamespace + } + if l.PodName != r.PodName { + return l.PodName < r.PodName + } + return l.ContainerName < r.ContainerName + }) + + return items +} + +// sweepLocked files requests that have waited too long for an answer and drops +// connection state that has gone quiet for longer than the TTL. The caller holds +// the lock. +// +// The second half is a backstop. Connections are normally reclaimed by their close +// event, which arrives promptly and knows the connection is dead; what reaches the +// TTL is an entry whose close was missed, so ConnTTL can be long enough not to +// disturb connections that are merely idle. +func (a *Aggregator) sweepLocked(now time.Time) { + cutoff := now.Add(-a.cfg.ConnTTL) + + // Expired on the same clock as connections, because the two answer the same + // question. An endpoint is known to be listening on the evidence of connections + // that arrived on it, and once those are old enough to be swept the evidence has + // gone with them -- at which point the pod may be gone and its IP reissued. + for key, at := range a.listening { + if at.Before(cutoff) { + delete(a.listening, key) + } + } + + for key, conn := range a.conns { + a.sweepRequestsLocked(conn, now) + if conn.lastSeen.Before(cutoff) { + // Requests here are already impossible when ConnTTL >= RequestTTL, which + // the config comment asks for: a connection idle that long has had every + // one of its requests swept above. Under a shorter ConnTTL they are lost, + // silently, along with the rest of the connection's state. + a.inFlight -= len(conn.inflight) + a.releaseBuffersLocked(conn) + delete(a.conns, key) + } + } +} + +// closeLocked releases a connection the kernel has said is going away, settling +// whatever it was still holding. +// +// The connection is deleted rather than kept as a tombstone. Nothing can arrive on +// the key afterwards. From tcp_close that is because the local file descriptor is +// closing, and the two syscall paths that produce events cannot be entered on a +// closed descriptor. From tcp_done the descriptor is still open and a read can +// still drain what arrived before the reset -- but the same teardown that prompted +// the close already released the local port, so those events carry a different key +// and land as a connection of their own, which its own tcp_close then reclaims. +// A later socket recycled onto the same kernel address and the same four-tuple is +// the same logical client reconnecting, and starting it clean is correct. +func (a *Aggregator) closeLocked(key connKey, c *connection, at time.Time) { + // Anything already past its deadline is filed at that deadline, exactly as the + // sweep would have filed it: the moment the agent gave up is when the + // measurement became final, and that is not the moment the socket happened to + // close. + a.sweepRequestsLocked(c, at) + + // What is left was still legitimately outstanding, and the close is what ended + // it -- a client that hung up mid-response, or a server that died with the + // request on the floor. That is a request that happened and was not answered, so + // it counts, with no duration and no status, on the same terms as one that timed + // out. Waiting for RequestTTL to notice would only file it later and in the + // wrong minute. + for range c.inflight { + a.fileUnansweredLocked(c, at) + } + a.inFlight -= len(c.inflight) + c.inflight = nil + + a.releaseBuffersLocked(c) + delete(a.conns, key) +} + +// sweepRequestsLocked files one connection's timed-out requests as unanswered. +// +// Each is filed at ITS OWN deadline rather than at now: the deadline is the moment +// the agent gave up, which is the moment the measurement became final, and filing +// them all at the sweep's timestamp would pile a minute's worth of abandoned +// requests into whichever minute the flush happened to land in. +func (a *Aggregator) sweepRequestsLocked(c *connection, now time.Time) { + // The list is in observation order, which is deadline order on both protocols: + // every request gets the same TTL, so a request observed earlier expires earlier. + // HTTP/2 completes streams out of order and so removes entries from the middle, + // but removal preserves the order of what stays, so the expired ones are still a + // prefix and the scan still stops at the first survivor. + expired := 0 + for expired < len(c.inflight) { + deadline := c.inflight[expired].start.Add(a.cfg.RequestTTL) + if deadline.After(now) { + break + } + a.fileUnansweredLocked(c, deadline) + expired++ + } + if expired == 0 { + return + } + + copy(c.inflight, c.inflight[expired:]) + c.inflight = c.inflight[:len(c.inflight)-expired] + a.inFlight -= expired +} + +// toNetaddr converts a netip.Addr to the netaddr.IP the pod cache is keyed by. +// +// The two libraries have no interop, so the conversion goes through the raw +// bytes. Unmapping first matters: a v4-mapped v6 address and the plain v4 +// address are the same host but different map keys, and the cache stores what +// netaddr.ParseIP produced from the pod's status IP. +func toNetaddr(a netip.Addr) (netaddr.IP, bool) { + a = a.Unmap() + switch { + case a.Is4(): + return netaddr.IPFrom4(a.As4()), true + case a.Is6(): + return netaddr.IPFrom16(a.As16()), true + default: + return netaddr.IP{}, false + } +} diff --git a/internal/networkmonitor/redmetrics/aggregator_test.go b/internal/networkmonitor/redmetrics/aggregator_test.go new file mode 100644 index 00000000..dc3ac80c --- /dev/null +++ b/internal/networkmonitor/redmetrics/aggregator_test.go @@ -0,0 +1,645 @@ +package redmetrics + +import ( + "net/netip" + "sync" + "testing" + "time" + + "github.com/go-logr/logr" + "inet.af/netaddr" + corev1 "k8s.io/api/core/v1" +) + +// fakeClock lets a test move wall clock deliberately. Minute buckets are stamped by +// the agent, so every interesting property of this package is a statement about +// where a wall clock stood. +type fakeClock struct { + mu sync.Mutex + now time.Time +} + +func (c *fakeClock) Now() time.Time { + c.mu.Lock() + defer c.mu.Unlock() + return c.now +} + +func (c *fakeClock) set(t time.Time) { + c.mu.Lock() + defer c.mu.Unlock() + c.now = t +} + +// fakePods is a pod lookup keyed the same way the real cache is. +type fakePods map[netaddr.IP]*corev1.Pod + +func (p fakePods) GetPodByIP(ip netaddr.IP) (*corev1.Pod, bool) { + got, ok := p[ip] + return got, ok +} + +const ( + testPodIP = "10.0.0.7" + testPodPort = 8080 + testPodNS = "shop" + testPodName = "web-0" + testPodUID = "uid-web-0" + testAppName = "app" + otherPodNS = "billing" + otherPodName = "api-0" +) + +func testPods() fakePods { + served := pod([]corev1.Container{container(testAppName, testPodPort), container("proxy", 15001)}, nil) + served.Name = testPodName + served.Namespace = testPodNS + served.UID = testPodUID + return fakePods{netaddr.MustParseIP(testPodIP): served} +} + +// recvAt builds an inbound observation on a given socket at a given instant. +func recvAt(cookie uint64, at time.Time) SocketEvent { + return SocketEvent{ + SocketCookie: cookie, + Path: SyscallRecv, + LocalAddr: netip.MustParseAddr(testPodIP), + LocalPort: testPodPort, + RemoteAddr: netip.MustParseAddr("192.168.1.4"), + RemotePort: 54321, + TrueLen: 128, + Timestamp: at, + } +} + +// closeAt is the socket going away. It carries no bytes, so it is the one event +// that says something about a connection without observing any traffic on it. +func closeAt(cookie uint64, at time.Time) SocketEvent { + ev := recvAt(cookie, at) + ev.Path = SyscallClose + ev.TrueLen = 0 + return ev +} + +func newTestAggregator(clock Clock, pods PodLookup) *Aggregator { + return NewAggregator(Config{}, clock, pods, logr.Discard()) +} + +func minuteAt(t *testing.T, s string) time.Time { + t.Helper() + parsed, err := time.Parse(time.RFC3339, s) + if err != nil { + t.Fatalf("bad timestamp %q: %v", s, err) + } + return parsed +} + +// TestDefaultConfigKeepsConnectionsAtLeastAsLongAsRequests pins the one relationship +// between two knobs that are otherwise independent. A connection reclaimed while it +// still holds an unfiled request loses that request instead of counting it +// unanswered, so the connection has to outlive the deadline of anything on it. +// +// The two moved in opposite directions when the close probe arrived -- the connection +// TTL became a leak backstop and grew, the request deadline shrank to get data to the +// control plane sooner -- which is exactly the kind of change that would breach this +// silently. +func TestDefaultConfigKeepsConnectionsAtLeastAsLongAsRequests(t *testing.T) { + cfg := DefaultConfig() + if cfg.ConnTTL < cfg.RequestTTL { + t.Fatalf("ConnTTL = %s is shorter than RequestTTL = %s: a connection would be "+ + "reclaimed while still holding a request that was never filed", + cfg.ConnTTL, cfg.RequestTTL) + } +} + +// TestAggregatorCountsConnectionsNotObservations pins what conn_count means. A busy +// connection carrying a thousand syscalls is still one connection, and counting +// syscalls would make the number a proxy for traffic volume instead. +func TestAggregatorCountsConnectionsNotObservations(t *testing.T) { + clock := &fakeClock{now: minuteAt(t, "2026-08-25T10:00:30Z")} + agg := newTestAggregator(clock, testPods()) + + for i := 0; i < 50; i++ { + agg.Observe(recvAt(1, clock.Now())) + } + agg.Observe(recvAt(2, clock.Now())) + + clock.set(minuteAt(t, "2026-08-25T10:01:05Z")) + items := agg.Flush() + + if len(items) != 1 { + t.Fatalf("got %d items, want 1", len(items)) + } + if items[0].ConnCount != 2 { + t.Fatalf("conn_count = %d, want 2", items[0].ConnCount) + } + if items[0].ContainerName != testAppName || items[0].PodName != testPodName || + items[0].PodNamespace != testPodNS || items[0].PodUid != testPodUID { + t.Fatalf("attribution = %+v", items[0]) + } + if got := items[0].Timestamp.AsTime(); !got.Equal(minuteAt(t, "2026-08-25T10:00:00Z")) { + t.Fatalf("bucket minute = %v, want 10:00:00", got) + } +} + +// TestAggregatorAlwaysEmitsFullBucketLadder pins a contract with the server, which +// rejects the WHOLE request when an item carries any other number of buckets. This +// connection never decodes, so every entry is zero -- and they still have to be +// there, because the length is what says the two sides agree about what the buckets +// mean. +func TestAggregatorAlwaysEmitsFullBucketLadder(t *testing.T) { + clock := &fakeClock{now: minuteAt(t, "2026-08-25T10:00:30Z")} + agg := newTestAggregator(clock, testPods()) + agg.Observe(recvAt(1, clock.Now())) + + clock.set(minuteAt(t, "2026-08-25T10:01:00Z")) + items := agg.Flush() + + if len(items) != 1 { + t.Fatalf("got %d items, want 1", len(items)) + } + if len(items[0].DurationBuckets) != DurationBucketCount { + t.Fatalf("duration_buckets length = %d, want %d", + len(items[0].DurationBuckets), DurationBucketCount) + } + for i, v := range items[0].DurationBuckets { + if v != 0 { + t.Fatalf("bucket %d = %d, want 0 (an opaque connection has no latency)", i, v) + } + } +} + +// TestAggregatorHoldsBackTheMinuteInProgress pins the boundary rule. Emitting the +// current minute would ship a partial delta and then, on the next flush, ship the +// rest of the same minute under the same key -- which the SummingMergeTree adds, +// producing a number that is right only by luck. +func TestAggregatorHoldsBackTheMinuteInProgress(t *testing.T) { + clock := &fakeClock{now: minuteAt(t, "2026-08-25T10:00:10Z")} + agg := newTestAggregator(clock, testPods()) + agg.Observe(recvAt(1, clock.Now())) + + clock.set(minuteAt(t, "2026-08-25T10:00:50Z")) + if items := agg.Flush(); len(items) != 0 { + t.Fatalf("got %d items, want the in-progress minute held back", len(items)) + } + + clock.set(minuteAt(t, "2026-08-25T10:01:01Z")) + items := agg.Flush() + if len(items) != 1 || items[0].ConnCount != 1 { + t.Fatalf("got %+v, want the whole minute once it closed", items) + } +} + +// TestAggregatorEmitsEachBucketExactlyOnce pins the property the whole no-retry rule +// rests on. A bucket that were emitted twice would be added to itself downstream. +func TestAggregatorEmitsEachBucketExactlyOnce(t *testing.T) { + clock := &fakeClock{now: minuteAt(t, "2026-08-25T10:00:30Z")} + agg := newTestAggregator(clock, testPods()) + agg.Observe(recvAt(1, clock.Now())) + + clock.set(minuteAt(t, "2026-08-25T10:01:30Z")) + if items := agg.Flush(); len(items) != 1 { + t.Fatalf("first flush got %d items, want 1", len(items)) + } + if items := agg.Flush(); len(items) != 0 { + t.Fatalf("second flush got %d items, want nothing left", len(items)) + } +} + +// TestAggregatorNeverReopensAClosedMinute pins what happens to a straggler. Clock +// skew between the kernel timestamp and the flush can put an observation in a minute +// already declared final; filing it late would report traffic against a bucket the +// server has already merged. +func TestAggregatorNeverReopensAClosedMinute(t *testing.T) { + clock := &fakeClock{now: minuteAt(t, "2026-08-25T10:01:30Z")} + agg := newTestAggregator(clock, testPods()) + + // Close 10:00 and 10:01 with nothing in them. + agg.Flush() + + agg.Observe(recvAt(1, minuteAt(t, "2026-08-25T10:00:30Z"))) + + clock.set(minuteAt(t, "2026-08-25T10:02:30Z")) + if items := agg.Flush(); len(items) != 0 { + t.Fatalf("got %+v, want a straggler for a closed minute dropped", items) + } +} + +// TestAggregatorIgnoresOutboundConnections pins direction. An outbound connection is +// somebody else's inbound, counted on the node that serves it; counting it here would +// double every request in the cluster. +func TestAggregatorIgnoresOutboundConnections(t *testing.T) { + clock := &fakeClock{now: minuteAt(t, "2026-08-25T10:00:30Z")} + agg := newTestAggregator(clock, testPods()) + + outbound := recvAt(1, clock.Now()) + outbound.Path = SyscallSend + agg.Observe(outbound) + + // Later receives on the same socket are the response coming back, and must not + // turn the connection inbound after the fact. + agg.Observe(recvAt(1, clock.Now())) + + clock.set(minuteAt(t, "2026-08-25T10:01:30Z")) + if items := agg.Flush(); len(items) != 0 { + t.Fatalf("got %+v, want outbound connections ignored", items) + } +} + +// TestAggregatorDropsUnattributableTraffic pins the silent refusal. Traffic to an IP +// no local pod owns, or to a port two containers claim, is lost rather than filed +// under a guess. +func TestAggregatorDropsUnattributableTraffic(t *testing.T) { + clock := &fakeClock{now: minuteAt(t, "2026-08-25T10:00:30Z")} + + ambiguous := pod([]corev1.Container{container(testAppName), container("proxy")}, nil) + ambiguous.Name = "amb-0" + ambiguous.Namespace = testPodNS + pods := fakePods{netaddr.MustParseIP("10.0.0.9"): ambiguous} + + agg := newTestAggregator(clock, pods) + + unknownHost := recvAt(1, clock.Now()) + agg.Observe(unknownHost) + + unknownPort := recvAt(2, clock.Now()) + unknownPort.LocalAddr = netip.MustParseAddr("10.0.0.9") + agg.Observe(unknownPort) + + clock.set(minuteAt(t, "2026-08-25T10:01:30Z")) + if items := agg.Flush(); len(items) != 0 { + t.Fatalf("got %+v, want unattributable traffic dropped", items) + } +} + +// TestAggregatorReclaimsIdleConnections pins the TTL sweep as the backstop it is. +// Connections are normally released by their close event, so what reaches the TTL is +// an entry whose close never arrived -- lost to a full ring buffer, or a socket +// already open when the probe attached. Without the sweep those would accumulate for +// the life of the process. +func TestAggregatorReclaimsIdleConnections(t *testing.T) { + clock := &fakeClock{now: minuteAt(t, "2026-08-25T10:00:30Z")} + agg := NewAggregator(Config{ConnTTL: time.Minute}, clock, testPods(), logr.Discard()) + agg.Observe(recvAt(1, clock.Now())) + + if got := agg.connCount(); got != 1 { + t.Fatalf("tracked connections = %d, want 1", got) + } + + clock.set(minuteAt(t, "2026-08-25T10:05:00Z")) + agg.Flush() + + if got := agg.connCount(); got != 0 { + t.Fatalf("tracked connections = %d, want the idle one reclaimed", got) + } +} + +// TestAggregatorCapsTrackedConnections pins the memory ceiling. This runs as a +// DaemonSet beside the workloads it measures, so an unbounded map is a way to take +// down the node it is reporting on. +func TestAggregatorCapsTrackedConnections(t *testing.T) { + clock := &fakeClock{now: minuteAt(t, "2026-08-25T10:00:30Z")} + agg := NewAggregator(Config{MaxConnections: 2}, clock, testPods(), logr.Discard()) + + for cookie := uint64(1); cookie <= 10; cookie++ { + agg.Observe(recvAt(cookie, clock.Now())) + } + + if got := agg.connCount(); got != 2 { + t.Fatalf("tracked connections = %d, want the cap of 2", got) + } + + clock.set(minuteAt(t, "2026-08-25T10:01:30Z")) + items := agg.Flush() + if len(items) != 1 || items[0].ConnCount != 2 { + t.Fatalf("got %+v, want only the tracked connections counted", items) + } +} + +// TestAggregatorReleasesAConnectionOnClose pins the close event as the thing that +// reclaims a connection. Everything else about the table -- what the cap holds, how +// long the TTL can be -- follows from the kernel saying when a socket is gone +// instead of the agent guessing from silence. +func TestAggregatorReleasesAConnectionOnClose(t *testing.T) { + clock := &fakeClock{now: minuteAt(t, "2026-08-25T10:00:30Z")} + agg := newTestAggregator(clock, testPods()) + + agg.Observe(recvAt(1, clock.Now())) + if got := agg.connCount(); got != 1 { + t.Fatalf("tracked connections = %d, want 1", got) + } + + agg.Observe(closeAt(1, minuteAt(t, "2026-08-25T10:00:31Z"))) + if got := agg.connCount(); got != 0 { + t.Fatalf("tracked connections = %d, want the closed one released", got) + } +} + +// TestAggregatorCloseFreesAnAdmissionSlot is the reason the close probe exists. The +// cap used to hold every connection seen within the TTL, which on a node churning +// short-lived connections is overwhelmingly sockets the kernel had already freed -- +// so the table filled with corpses and refused live connections at a fraction of the +// offered rate. A slot has to come back when its socket does. +func TestAggregatorCloseFreesAnAdmissionSlot(t *testing.T) { + clock := &fakeClock{now: minuteAt(t, "2026-08-25T10:00:30Z")} + agg := NewAggregator(Config{MaxConnections: 1}, clock, testPods(), logr.Discard()) + + agg.Observe(recvAt(1, clock.Now())) + agg.Observe(recvAt(2, clock.Now())) + if got := agg.connCount(); got != 1 { + t.Fatalf("tracked connections = %d, want the second refused at the cap", got) + } + + agg.Observe(closeAt(1, clock.Now())) + agg.Observe(recvAt(2, clock.Now())) + + if got := agg.connCount(); got != 1 { + t.Fatalf("tracked connections = %d, want the freed slot reused", got) + } + clock.set(minuteAt(t, "2026-08-25T10:01:30Z")) + if items := agg.Flush(); len(items) != 1 { + t.Fatalf("got %d items, want the readmitted connection measured", len(items)) + } +} + +// TestAggregatorIgnoresACloseForAnUntrackedConnection pins that a close is not an +// observation. Most sockets on a node are nothing to do with a pod this agent +// measures, and their closes arrive all the same; if one created an entry, or +// consumed an admission slot on the way to being dropped, the cap would be spent on +// traffic that was never counted. +func TestAggregatorIgnoresACloseForAnUntrackedConnection(t *testing.T) { + clock := &fakeClock{now: minuteAt(t, "2026-08-25T10:00:30Z")} + agg := NewAggregator(Config{MaxConnections: 1}, clock, testPods(), logr.Discard()) + + agg.Observe(closeAt(99, clock.Now())) + if got := agg.connCount(); got != 0 { + t.Fatalf("tracked connections = %d, want a close for an unknown socket ignored", got) + } + + // The slot is still there to be used. + agg.Observe(recvAt(1, clock.Now())) + if got := agg.connCount(); got != 1 { + t.Fatalf("tracked connections = %d, want the slot still free", got) + } +} + +// TestAggregatorReleasesAnIgnoredConnectionOnClose pins the close path for a +// connection the aggregator decided not to decode. An outbound connection is still an +// entry in the table holding a slot, and it is the state the node has most of, so a +// close that only released decoded connections would leave the cap exactly as stuck +// as it was before there was a close probe. +func TestAggregatorReleasesAnIgnoredConnectionOnClose(t *testing.T) { + clock := &fakeClock{now: minuteAt(t, "2026-08-25T10:00:30Z")} + agg := newTestAggregator(clock, testPods()) + + outbound := recvAt(1, clock.Now()) + outbound.Path = SyscallSend + agg.Observe(outbound) + if got := agg.connCount(); got != 1 { + t.Fatalf("tracked connections = %d, want the outbound connection tracked", got) + } + + agg.Observe(closeAt(1, clock.Now())) + if got := agg.connCount(); got != 0 { + t.Fatalf("tracked connections = %d, want it released", got) + } +} + +// TestAggregatorFlushOrderIsDeterministic pins the emitted order. Go randomises map +// iteration, so without the sort the same observations would produce a different +// batch on every run -- untestable, and unreadable in a log of what was sent. +func TestAggregatorFlushOrderIsDeterministic(t *testing.T) { + clock := &fakeClock{now: minuteAt(t, "2026-08-25T10:00:30Z")} + + pods := testPods() + other := pod([]corev1.Container{container("api", testPodPort)}, nil) + other.Name = otherPodName + other.Namespace = otherPodNS + other.UID = "uid-api-0" + pods[netaddr.MustParseIP("10.0.0.8")] = other + + agg := newTestAggregator(clock, pods) + + second := recvAt(1, clock.Now()) + second.LocalAddr = netip.MustParseAddr("10.0.0.8") + agg.Observe(second) + agg.Observe(recvAt(2, clock.Now())) + + clock.set(minuteAt(t, "2026-08-25T10:01:30Z")) + items := agg.Flush() + + if len(items) != 2 { + t.Fatalf("got %d items, want 2", len(items)) + } + if items[0].PodNamespace != otherPodNS || items[1].PodNamespace != testPodNS { + t.Fatalf("order = %q, %q; want namespaces sorted", + items[0].PodNamespace, items[1].PodNamespace) + } +} + +// TestAggregatorNeverMovesTheFrontierBackwards pins the frontier as monotonic. +// +// A minute is emitted exactly once and "everything before this is final" is the +// only thing stopping it being emitted twice. If a flush reading an earlier clock +// could lower that mark -- two flushes racing, or a clock that steps back -- a +// minute already on the wire would accept new observations and be sent again. The +// second copy is added by the SummingMergeTree, not deduplicated, so it is +// indistinguishable from real traffic that never happened. +func TestAggregatorNeverMovesTheFrontierBackwards(t *testing.T) { + clock := &fakeClock{now: minuteAt(t, "2026-08-25T10:01:10Z")} + agg := newTestAggregator(clock, testPods()) + + agg.Observe(recvAt(1, minuteAt(t, "2026-08-25T10:00:30Z"))) + if items := agg.Flush(); len(items) != 1 { + t.Fatalf("got %d items, want the 10:00 minute emitted", len(items)) + } + + // The clock steps back inside the minute that was just declared final. + clock.set(minuteAt(t, "2026-08-25T10:00:50Z")) + agg.Flush() + + // A fresh connection in the already-emitted minute must still be refused. + agg.Observe(recvAt(2, minuteAt(t, "2026-08-25T10:00:40Z"))) + + clock.set(minuteAt(t, "2026-08-25T10:02:00Z")) + if items := agg.Flush(); len(items) != 0 { + t.Fatalf("got %+v, want nothing -- 10:00 was already final", items) + } +} + +// TestAggregatorCountsARecycledSocketAsANewConnection pins the connection identity +// against kernel socket reuse. +// +// The probe reports a kernel socket address, and the kernel recycles those. A node +// churning short-lived connections will hand the same address to a fresh +// connection well inside the TTL, and if that is taken for the connection that +// held it before, it is never counted -- conn_count would read low precisely where +// traffic is heaviest, with nothing to show it had happened. +func TestAggregatorCountsARecycledSocketAsANewConnection(t *testing.T) { + clock := &fakeClock{now: minuteAt(t, "2026-08-25T10:00:20Z")} + agg := newTestAggregator(clock, testPods()) + + first := recvAt(1, clock.Now()) + agg.Observe(first) + + // Same kernel socket address, a different client: a recycled struct sock. + recycled := first + recycled.RemotePort = 54322 + agg.Observe(recycled) + + clock.set(minuteAt(t, "2026-08-25T10:01:10Z")) + items := agg.Flush() + if len(items) != 1 { + t.Fatalf("got %d items, want 1", len(items)) + } + if items[0].ConnCount != 2 { + t.Fatalf("conn_count = %d, want 2 -- the recycled socket is a new connection", + items[0].ConnCount) + } +} + +// TestAggregatorIsConcurrencySafe drives Observe from many goroutines while flushing, +// which is how it actually runs: the probe reads the ring buffer on its own goroutine +// and the monitor flushes on a ticker. +func TestAggregatorIsConcurrencySafe(t *testing.T) { + clock := &fakeClock{now: minuteAt(t, "2026-08-25T10:00:30Z")} + agg := newTestAggregator(clock, testPods()) + + var wg sync.WaitGroup + for g := 0; g < 8; g++ { + wg.Add(1) + go func(g int) { + defer wg.Done() + for i := 0; i < 200; i++ { + agg.Observe(recvAt(uint64(g*1000+i), clock.Now())) + } + }(g) + } + wg.Add(1) + go func() { + defer wg.Done() + for i := 0; i < 50; i++ { + agg.Flush() + } + }() + wg.Wait() +} + +// connCount reports how many connections are currently tracked. It exists for the +// tests: the ceilings this package enforces are claims about the size of a map +// nothing outside the package can see. +func (a *Aggregator) connCount() int { + a.mu.Lock() + defer a.mu.Unlock() + return len(a.conns) +} + +// onPort moves an observation to a different local port, which is the only way a +// test can say "this is an outbound connection" in terms the aggregator can see. +func onPort(ev SocketEvent, port uint16) SocketEvent { + ev.LocalPort = port + return ev +} + +// TestAggregatorReacquiresAConnectionOpenBeforeItStarted pins the restart case. +// +// Nothing on a connection says who opened it, and for one that was already +// established when the agent attached, the first syscall the agent sees is simply +// whichever happened next. Reading that as the direction meant a server holding +// long-lived keep-alive connections was written off as outbound the moment its agent +// restarted, and stayed that way for as long as those connections lived -- which +// against a keep-alive client is indefinitely. +// +// Here the first thing seen on connection 2 is a send, on the port connection 1 has +// already been seen receiving on. That makes it an inbound connection joined late +// rather than one this node opened, and its next request has to be measured. +func TestAggregatorReacquiresAConnectionOpenBeforeItStarted(t *testing.T) { + clock := &fakeClock{now: at(t, 0)} + agg := newTestAggregator(clock, testPods()) + + // An ordinary connection, which is what teaches the aggregator that this node + // serves on this endpoint. + agg.Observe(reqAt(1, at(t, 0), request("GET", "/a", ""))) + agg.Observe(respAt(1, at(t, 10*time.Millisecond), response(200, "ok"))) + + // Connection 2 was already open. The first thing seen on it is the tail of a + // response to a request nobody watched arrive, so nothing is measured from it. + agg.Observe(respAt(2, at(t, 20*time.Millisecond), response(200, "mid"))) + + // The next request is a message boundary, and from there the connection decodes + // like any other. + agg.Observe(reqAt(2, at(t, 100*time.Millisecond), request("GET", "/b", ""))) + agg.Observe(respAt(2, at(t, 150*time.Millisecond), response(500, "no"))) + + item := flushOne(t, agg, clock, at(t, time.Minute+5*time.Second)) + + if item.RequestCount != 2 { + t.Fatalf("request_count = %d, want 2: the re-acquired connection was never measured", item.RequestCount) + } + if item.ErrorCount != 1 { + t.Fatalf("error_count = %d, want 1", item.ErrorCount) + } + if item.ConnCount != 0 { + t.Fatalf("conn_count = %d, want 0: resynchronising at a request boundary is not giving up", item.ConnCount) + } + if want := uint64((10 + 50) * time.Millisecond.Microseconds()); item.DurationSumUs != want { + t.Fatalf("duration_sum_us = %d, want %d", item.DurationSumUs, want) + } +} + +// TestAggregatorStillIgnoresOutboundConnectionsOnAServingNode pins the other half of +// the same rule. Learning that the node serves on one endpoint must not make every +// send-first connection inbound: a connection this node dials leaves from an +// ephemeral port of its own, and picking those up would count the cluster's traffic a +// second time on the wrong side. +func TestAggregatorStillIgnoresOutboundConnectionsOnAServingNode(t *testing.T) { + clock := &fakeClock{now: at(t, 0)} + agg := newTestAggregator(clock, testPods()) + + agg.Observe(reqAt(1, at(t, 0), request("GET", "/a", ""))) + agg.Observe(respAt(1, at(t, 10*time.Millisecond), response(200, "ok"))) + + // This pod calling somebody else: a request going out first, from a port nothing + // has ever been received on. + agg.Observe(onPort(respAt(2, at(t, 20*time.Millisecond), request("GET", "/x", "")), 43210)) + agg.Observe(onPort(reqAt(2, at(t, 30*time.Millisecond), response(200, "theirs")), 43210)) + + item := flushOne(t, agg, clock, at(t, time.Minute+5*time.Second)) + + if item.RequestCount != 1 { + t.Fatalf("request_count = %d, want 1: an outbound connection was measured as inbound", item.RequestCount) + } + if item.ConnCount != 0 { + t.Fatalf("conn_count = %d, want 0: an outbound connection is ignored, not given up on", item.ConnCount) + } +} + +// TestAggregatorForgetsAServingEndpointOnceItGoesQuiet pins the expiry. The endpoint +// set is evidence gathered from connections, and pod IPs are recycled -- so an entry +// that outlived every connection justifying it could go on to match the ephemeral +// port of whichever pod inherits the address next. +func TestAggregatorForgetsAServingEndpointOnceItGoesQuiet(t *testing.T) { + clock := &fakeClock{now: at(t, 0)} + agg := newTestAggregator(clock, testPods()) + + agg.Observe(reqAt(1, at(t, 0), request("GET", "/a", ""))) + agg.Observe(respAt(1, at(t, 10*time.Millisecond), response(200, "ok"))) + agg.Observe(closeAt(1, at(t, 20*time.Millisecond))) + + // Well past ConnTTL, so this flush's sweep drops the endpoint along with + // everything else that old. + quiet := at(t, 11*time.Minute) + clock.set(quiet) + agg.Flush() + + // The same endpoint, no longer recognised: a send-first connection on it reads as + // outbound again. + agg.Observe(respAt(2, quiet, response(200, "mid"))) + agg.Observe(reqAt(2, quiet.Add(time.Second), request("GET", "/b", ""))) + agg.Observe(respAt(2, quiet.Add(2*time.Second), response(200, "ok"))) + + clock.set(at(t, 13*time.Minute)) + if items := agg.Flush(); len(items) != 0 { + t.Fatalf("got %d items, want 0: a forgotten endpoint is not a serving one", len(items)) + } +} diff --git a/internal/networkmonitor/redmetrics/attribute.go b/internal/networkmonitor/redmetrics/attribute.go new file mode 100644 index 00000000..0c994017 --- /dev/null +++ b/internal/networkmonitor/redmetrics/attribute.go @@ -0,0 +1,94 @@ +package redmetrics + +import ( + corev1 "k8s.io/api/core/v1" +) + +// ContainerKey identifies the container a measurement belongs to, in the only +// terms this agent can see. The DevZero container, pod and node UUIDs are +// resolved server side from exactly these fields, by the same lookup the existing +// bucketed container metrics path uses -- which is what makes the two tables agree +// about which container a measurement belongs to. +type ContainerKey struct { + PodName string + PodNamespace string + PodUID string + ContainerName string +} + +// containerForPort names the container serving a port on a pod, reporting false +// when it cannot be established. +// +// Port is the discriminator because of a physical fact rather than a convention: +// the containers of a pod share one network namespace, so no two of them can bind +// the same port. What the pod spec declares is a different matter -- containerPort +// is advisory in Kubernetes and a container that never declares one still listens +// perfectly well -- so a declaration is treated as evidence, not as truth. +// +// The rules, in order: +// +// - Exactly one candidate declares the port: that container. +// - Several declare it: refuse. The declarations cannot all be true and there is +// nothing here to choose between them. +// - Nobody declares it and there is exactly one candidate: that container. +// Whoever is listening, it is the only one that can be. +// - Nobody declares it and there are several candidates: refuse. +// +// Refusing loses the measurement. That is the intended trade: a guess would file +// one workload's traffic against another workload's resource recommendations, and +// a wrong join is worse than a missing one. +func containerForPort(pod *corev1.Pod, port uint16) (string, bool) { + if pod == nil { + return "", false + } + + var ( + declared string + matches int + single string + count int + ) + + visit := func(c *corev1.Container) { + count++ + single = c.Name + for i := range c.Ports { + if uint16(c.Ports[i].ContainerPort) == port { //nolint:gosec // a port never exceeds 65535 + matches++ + declared = c.Name + break + } + } + } + + for i := range pod.Spec.Containers { + visit(&pod.Spec.Containers[i]) + } + // Native sidecars are init containers that keep running, so they can hold a + // listening socket for as long as the pod is up and belong in the candidate set. + // An ordinary init container has exited by the time any of this is observed, and + // counting it would break the single-container fallback for the common case of + // one app container behind one init container. + for i := range pod.Spec.InitContainers { + if isRestartableSidecar(&pod.Spec.InitContainers[i]) { + visit(&pod.Spec.InitContainers[i]) + } + } + + if matches == 1 { + return declared, true + } + if matches > 1 { + return "", false + } + if count == 1 { + return single, true + } + return "", false +} + +// isRestartableSidecar reports whether an init container is a native sidecar, the +// only kind that is still running when traffic arrives. +func isRestartableSidecar(c *corev1.Container) bool { + return c.RestartPolicy != nil && *c.RestartPolicy == corev1.ContainerRestartPolicyAlways +} diff --git a/internal/networkmonitor/redmetrics/attribute_test.go b/internal/networkmonitor/redmetrics/attribute_test.go new file mode 100644 index 00000000..9b2064c2 --- /dev/null +++ b/internal/networkmonitor/redmetrics/attribute_test.go @@ -0,0 +1,132 @@ +package redmetrics + +import ( + "testing" + + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +func container(name string, ports ...int32) corev1.Container { + c := corev1.Container{Name: name} + for _, p := range ports { + c.Ports = append(c.Ports, corev1.ContainerPort{ContainerPort: p}) + } + return c +} + +func sidecar(name string, ports ...int32) corev1.Container { + c := container(name, ports...) + always := corev1.ContainerRestartPolicyAlways + c.RestartPolicy = &always + return c +} + +func pod(containers []corev1.Container, initContainers []corev1.Container) *corev1.Pod { + return &corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{Name: "p", Namespace: "ns"}, + Spec: corev1.PodSpec{ + Containers: containers, + InitContainers: initContainers, + }, + } +} + +// TestContainerForPort covers the whole attribution decision, including every case +// where it refuses. +// +// The refusals matter more than the successes. Attribution feeds a join against +// resource recommendations, so a wrong answer files one workload's traffic against +// another workload's requests and limits -- a plausible-looking number that is +// simply about a different container. A missing answer is visible as a gap; a wrong +// one is not visible at all. +func TestContainerForPort(t *testing.T) { + tests := []struct { + name string + pod *corev1.Pod + port uint16 + want string + ok bool + }{ + { + name: "single container, port undeclared: whoever is listening, it is the only one that can be", + pod: pod([]corev1.Container{container("app")}, nil), + port: 8080, + want: "app", + ok: true, + }, + { + name: "single container declaring a different port still gets the traffic", + pod: pod([]corev1.Container{container("app", 9090)}, nil), + port: 8080, + want: "app", + ok: true, + }, + { + name: "two containers, one declares the port", + pod: pod([]corev1.Container{container("app", 8080), container("proxy", 15001)}, nil), + port: 8080, + want: "app", + ok: true, + }, + { + name: "two containers, neither declares the port: refuse rather than guess", + pod: pod([]corev1.Container{container("app"), container("proxy")}, nil), + port: 8080, + ok: false, + }, + { + name: "two containers declare the same port: the spec is not self-consistent, refuse", + pod: pod([]corev1.Container{container("app", 8080), container("proxy", 8080)}, nil), + port: 8080, + ok: false, + }, + { + name: "native sidecar declares the port", + pod: pod([]corev1.Container{container("app", 8080)}, []corev1.Container{sidecar("proxy", 15001)}), + port: 15001, + want: "proxy", + ok: true, + }, + { + name: "native sidecar is a candidate, so an undeclared port is now ambiguous", + pod: pod([]corev1.Container{container("app")}, []corev1.Container{sidecar("proxy")}), + port: 8080, + ok: false, + }, + { + // An ordinary init container has exited long before any traffic arrives. + // Counting it would break the single-container fallback for the very + // common one-app-container-behind-one-init-container pod. + name: "ordinary init container is not a candidate", + pod: pod([]corev1.Container{container("app")}, []corev1.Container{container("migrate")}), + port: 8080, + want: "app", + ok: true, + }, + { + name: "no pod", + pod: nil, + port: 8080, + ok: false, + }, + { + name: "no containers at all", + pod: pod(nil, nil), + port: 8080, + ok: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, ok := containerForPort(tt.pod, tt.port) + if ok != tt.ok { + t.Fatalf("ok = %v, want %v (container %q)", ok, tt.ok, got) + } + if ok && got != tt.want { + t.Fatalf("container = %q, want %q", got, tt.want) + } + }) + } +} diff --git a/internal/networkmonitor/redmetrics/clock.go b/internal/networkmonitor/redmetrics/clock.go new file mode 100644 index 00000000..a0b84ad0 --- /dev/null +++ b/internal/networkmonitor/redmetrics/clock.go @@ -0,0 +1,16 @@ +package redmetrics + +import "time" + +// Clock is this package's only source of wall clock time. Minute buckets are +// stamped by the agent, so tests need to move time deliberately rather than wait +// for it. +type Clock interface { + Now() time.Time +} + +// SystemClock reads the real wall clock. +type SystemClock struct{} + +// Now implements Clock. +func (SystemClock) Now() time.Time { return time.Now() } diff --git a/internal/networkmonitor/redmetrics/decode.go b/internal/networkmonitor/redmetrics/decode.go new file mode 100644 index 00000000..255da2cd --- /dev/null +++ b/internal/networkmonitor/redmetrics/decode.go @@ -0,0 +1,468 @@ +package redmetrics + +import ( + "time" +) + +// decodeState is what the decoder is still willing to do with one connection. +// +// A connection is read until something stops it being readable, and none of the +// reading states moves back: HTTP/1.1 does not become HTTP/2, and opaque and +// ignored are final. The one exception is stateJoined, which is not a reading +// state at all -- it is a connection waiting for a boundary to start reading from, +// and it exists only until it finds one. +type decodeState uint8 + +const ( + // stateNew is a connection whose protocol has not been established yet, which + // is every connection until its first bytes arrive. It is the zero value + // because it is what a connection is until something says otherwise. + stateNew decodeState = iota + + // stateHTTP1 and stateHTTP2 are connections being read as one protocol or the + // other. The choice is made once, from the first bytes, and is never revisited: + // a connection does not change protocol mid-stream, and a decoder willing to + // believe it did would resynchronise onto noise. + stateHTTP1 + stateHTTP2 + + // stateOpaque is a connection that is no longer being decoded, and has already + // contributed its conn_count. Nothing moves out of it. + stateOpaque + + // stateIgnored is a connection nothing is measured about: one this node + // originated, or one whose container could not be established. It is tracked + // only so the decision is not taken again on every later syscall. + stateIgnored + + // stateJoined is an inbound connection the agent started watching part-way + // through, recognised as inbound because it is served on a port this node has + // been seen receiving on. Its bytes cannot be read from where the agent came in + // -- the first thing seen was a response, and the request it answers went past + // before anyone was looking -- so it waits for the boundary that a response + // ending and a new request arriving makes, and becomes stateNew there. + stateJoined +) + +// maxBufferBytes caps one held-over buffer. It is the default HTTP/2 maximum +// frame size, which is what bounds the largest single unit the decoder ever has +// to see whole; a peer that negotiated something larger, or a header block spread +// over more CONTINUATIONs than that, takes its connection opaque rather than +// growing the agent. +const maxBufferBytes = 16384 + +// halfStream is one direction of a connection's byte stream, reduced to what the +// decoder has to remember between socket events. +// +// Inside a body nothing is parsed, only counted, which is what makes a truncated +// copy harmless there: the probe reports the call's true length even when it copied +// a 2KB prefix of it, so the position advances by the length that crossed the socket +// rather than by the length that reached userspace. +type halfStream struct { + // skip is the number of bytes still to consume before the next boundary: an + // HTTP/1.1 body, or an HTTP/2 frame payload the decoder does not read. Zero + // means the next byte on this half starts something the decoder reads. + skip uint64 + + // carry holds bytes from earlier events that did not add up to a whole unit, + // so the next event resumes rather than losing them. Only HTTP/2 fills it -- + // its frames declare their length, so a split one can be waited out. HTTP/1.1 + // has no such marker and goes opaque on a split header block instead. + carry []byte +} + +// pending is one outstanding request: observed starting, not yet observed +// completing. +// +// Its fields divide by protocol, because the two protocols pair a response with a +// request in entirely different ways. method is HTTP/1.1's, where the pairing is +// positional. streamID, status and grpcError are HTTP/2's, where the pairing is +// explicit and the outcome arrives in two pieces. +type pending struct { + // start is when the request's first byte crossed the socket, stamped by the + // agent. The duration is measured against the completion's stamp. + start time.Time + + // method is the HTTP/1.1 request method, kept because the response's framing + // depends on it: a HEAD response declares a content length and then sends no + // body. + method string + + // streamID is the HTTP/2 stream this request arrived on, and is how its + // response finds it again on a connection carrying many at once. + streamID uint32 + + // status is the :status of the HTTP/2 response so far, zero until its headers + // arrive. It is read at completion and then discarded. + status int + + // grpcError records that the response carried a non-zero grpc-status. It is + // separate from status because a failed gRPC call still answers 200, so the two + // have to be able to disagree. + grpcError bool +} + +// role says which half of the connection an event's bytes crossed on. +type role uint8 + +const ( + roleRequest role = iota + roleResponse +) + +// connection is everything remembered about one tracked connection. +type connection struct { + // lastSeen is the timestamp of its most recent syscall, and the only thing the + // idle sweep looks at. + lastSeen time.Time + + // container is the container serving it, resolved once at the first inbound + // observation. Every measurement the connection produces is filed against it, + // including a conn_count filed minutes later, so that a pod deleted mid-flight + // cannot change where its own traffic lands. + container ContainerKey + + state decodeState + + request, response halfStream + + // h2 is the HPACK and header-block state, allocated only for a connection + // established as HTTP/2. An HTTP/1.1 connection never pays for two decoders. + h2 *http2Conn + + // inflight holds requests in the order they were observed. On HTTP/1.1 that + // order IS the pairing -- nothing on the wire ties a response to a request, so + // the oldest outstanding one is the answer. On HTTP/2 the stream id does the + // pairing and the order only serves the sweep, which needs oldest first. + inflight []pending +} + +// stream is the half an event's bytes crossed on. +// consumeSkip spends what the walk owes the skip counter against this buffer and +// reports the position after it. The debt outlives the read that announced it: a +// body or a frame payload may be larger than the buffer it was declared in, and +// the bytes making up the rest of it arrive over later reads. +func (h *halfStream) consumeSkip(pos, trueLen uint64) uint64 { + consumed := min(h.skip, trueLen-pos) + h.skip -= consumed + return pos + consumed +} + +func (c *connection) stream(which role) *halfStream { + if which == roleRequest { + return &c.request + } + return &c.response +} + +// roleOf maps a syscall to a half. Only inbound connections are decoded, so bytes +// arriving are always a request and bytes leaving are always its answer. +func roleOf(p SyscallPath) role { + if p == SyscallRecv { + return roleRequest + } + return roleResponse +} + +// oldestMethod is the method of the oldest outstanding request, empty when there is +// none. The response parser needs it, and on HTTP/1.1 the oldest outstanding request +// is the one being answered. +func (c *connection) oldestMethod() string { + if len(c.inflight) == 0 { + return "" + } + return c.inflight[0].method +} + +// decodeLocked advances a connection's decode state by one socket event, filing +// every request that completed in it. The caller holds the lock. +func (a *Aggregator) decodeLocked(c *connection, ev SocketEvent) { + // The probe may hand over more bytes than the syscall carried only if something + // is wrong with the record; clamping keeps the walk's two lengths consistent + // with each other rather than trusting either alone. + payload := ev.Payload + if uint32(len(payload)) > ev.TrueLen { + payload = payload[:ev.TrueLen] + } + + which := roleOf(ev.Path) + + switch c.state { + case stateJoined: + // Neither a response nor an empty read is a place to start reading from: the + // first is the middle of a message this agent never saw the start of, and the + // second is not a message at all. Both leave the connection waiting. + if which == roleResponse || ev.TrueLen == 0 { + return + } + // A request arriving after a response went out is a fresh message start on + // HTTP/1.1, which is what makes this resynchronisation sound rather than + // hopeful. On HTTP/2 it is not -- frames interleave, so there is no such + // boundary -- and the sniff below will fail to find a preface and take the + // connection opaque, which is the honest answer for a multiplexed stream + // joined midway. + c.state = stateNew + a.sniffLocked(c, ev, payload) + + case stateNew: + if which == roleResponse { + // The server is talking on a connection whose first request was never + // seen. Whatever protocol this is, the decoder joined it after the point + // that would have identified it. + a.goOpaqueLocked(c, ev.Timestamp) + return + } + a.sniffLocked(c, ev, payload) + case stateHTTP1: + a.walkHTTP1Locked(c, which, ev, payload, uint64(ev.TrueLen)) + case stateHTTP2: + a.decodeHTTP2Locked(c, which, ev, payload) + } +} + +// joinCarry puts an event's bytes after whatever was held over from earlier ones, +// and reports how far the stream advances across the pair. +// +// The returned buffer never aliases the carry, so the caller may release the carry +// before it has finished reading. When nothing was held over it is the event's own +// bytes, which is the common case and costs nothing. +func joinCarry(hs *halfStream, payload []byte, trueLen uint64) ([]byte, uint64) { + if len(hs.carry) == 0 { + return payload, trueLen + } + buf := make([]byte, 0, len(hs.carry)+len(payload)) + buf = append(buf, hs.carry...) + buf = append(buf, payload...) + return buf, uint64(len(hs.carry)) + trueLen +} + +// holdLocked keeps the tail of a buffer for the next event, because it stopped +// part-way through something the decoder has to read whole. +// +// It can only do that when the event was copied in full. A buffer shorter than the +// length that crossed the socket means bytes the decoder needs never reached +// userspace, and resuming from what remains would silently skip them. +func (a *Aggregator) holdLocked(c *connection, hs *halfStream, ev SocketEvent, buf []byte, pos, trueLen uint64) { + if trueLen != uint64(len(buf)) { + a.goOpaqueLocked(c, ev.Timestamp) + return + } + a.releaseLocked(&hs.carry) + if !a.bufferLocked(&hs.carry, buf[pos:]) { + a.goOpaqueLocked(c, ev.Timestamp) + } +} + +// bufferLocked appends bytes to one of a connection's held-over buffers, reporting +// false when doing so would break either bound. +// +// There are two bounds and they answer different questions. maxBufferBytes says no +// single unit is that big, so a connection asking for more is not one worth +// following. MaxBufferedBytes is the one that matters on a busy node: it is the +// agent's total, so a thousand connections each holding a legitimate half-frame +// cannot add up to memory pressure on a DaemonSet sharing a node with the +// workloads it measures. +func (a *Aggregator) bufferLocked(dst *[]byte, b []byte) bool { + if len(*dst)+len(b) > maxBufferBytes { + return false + } + if a.buffered+len(b) > a.cfg.MaxBufferedBytes { + return false + } + a.buffered += len(b) + *dst = append(*dst, b...) + return true +} + +// releaseLocked empties one held-over buffer and refunds it to the agent's total. +func (a *Aggregator) releaseLocked(dst *[]byte) { + a.buffered -= len(*dst) + *dst = nil +} + +// releaseBuffersLocked gives back everything a connection was holding. It runs when +// the connection stops being decoded, whether that is because it went opaque or +// because the sweep dropped it. +func (a *Aggregator) releaseBuffersLocked(c *connection) { + a.releaseLocked(&c.request.carry) + a.releaseLocked(&c.response.carry) + if c.h2 != nil { + a.releaseLocked(&c.h2.request.block) + a.releaseLocked(&c.h2.response.block) + // Dropping the decoders drops two dynamic tables, which are the largest + // thing an HTTP/2 connection holds. + c.h2 = nil + } +} + +// walkHTTP1Locked walks one event as HTTP/1.1 messages. +// +// The walk is over the event's TRUE length rather than its copied prefix. Body bytes +// are consumed by count, so a body far larger than the copy limit costs nothing; a +// message boundary needs the bytes themselves, and when one lands past what the +// probe copied the connection goes opaque rather than guessing at what it could not +// see. +// +// Unlike the HTTP/2 walk it never holds bytes over. A split header block could in +// principle be waited out too, but HTTP/1.1 gives no length to wait for and no +// marker to resynchronise on, so the same code would be waiting on something that +// may never come while the connection's real traffic went unread. +func (a *Aggregator) walkHTTP1Locked(c *connection, which role, ev SocketEvent, buf []byte, trueLen uint64) { + stream := c.stream(which) + + for pos := uint64(0); pos < trueLen; { + if stream.skip > 0 { + pos = stream.consumeSkip(pos, trueLen) + continue + } + + if pos >= uint64(len(buf)) { + // The next message starts in bytes the probe did not copy. Reading the + // stream from here would mean picking a boundary at random. + a.goOpaqueLocked(c, ev.Timestamp) + return + } + + msg, ok := parseMessageStartLocked(c, which, buf[pos:]) + if !ok { + a.goOpaqueLocked(c, ev.Timestamp) + return + } + + pos += uint64(msg.headerLen) + if msg.bodyLen > 0 { + stream.skip = uint64(msg.bodyLen) + } + + a.recordMessageLocked(c, which, ev.Timestamp, msg) + + if msg.bodyLen == bodyLenUnknown { + // A chunked or close-delimited body: this message was accounted for, but + // where it ends is not knowable from a length, so neither is the next + // boundary on this connection. + a.goOpaqueLocked(c, ev.Timestamp) + return + } + + if c.state == stateOpaque { + // recordMessageLocked gave up on the connection -- the in-flight cap. + return + } + } +} + +// parseMessageStartLocked reads a message boundary from the start of b, as a request +// or a response depending on which half it crossed. It carries the suffix because it +// reads the connection's in-flight list, which only the aggregator's lock protects. +func parseMessageStartLocked(c *connection, which role, b []byte) (messageStart, bool) { + if which == roleResponse { + return parseResponseStart(b, c.oldestMethod()) + } + return parseRequestStart(b) +} + +// recordMessageLocked turns a message boundary into aggregate state: a request start +// goes on the in-flight list, a response completes the oldest one. +func (a *Aggregator) recordMessageLocked(c *connection, which role, at time.Time, msg messageStart) { + if which == roleRequest { + a.startRequestLocked(c, at, msg.method) + return + } + if informational(msg.status) { + // A 100 Continue answers nothing; the real response follows on the same + // request, and treating this as the completion would report a latency that + // measured the server's willingness to read a body. + return + } + a.completeRequestLocked(c, at, msg.status) +} + +// startRequestLocked begins tracking one HTTP/1.1 request. +// +// The cap is the memory bound on in-flight state. A request arriving with no room +// takes the connection opaque, rather than being dropped on its own: the response it +// will never be matched against would otherwise be paired with a LATER request on +// the same connection, reporting a duration made of two different requests. +func (a *Aggregator) startRequestLocked(c *connection, at time.Time, method string) { + if a.inFlight >= a.cfg.MaxInFlightRequests { + a.goOpaqueLocked(c, at) + return + } + c.inflight = append(c.inflight, pending{start: at, method: method}) + a.inFlight++ +} + +// completeRequestLocked files the oldest outstanding request as completed by this +// response. +// +// A response with nothing outstanding is dropped. Without a start there is no +// duration, and inventing one would put a number in the histogram that no request +// produced; counting it as a request without a duration would be worse still, since +// request_count would then disagree with the top bucket for reasons that have +// nothing to do with the service. It happens when the agent joined the connection +// between a request and its response, or after that request was swept. +func (a *Aggregator) completeRequestLocked(c *connection, at time.Time, status int) { + if len(c.inflight) == 0 { + return + } + + // Index zero because on HTTP/1.1 the oldest outstanding request is the one this + // response answers; HTTP/2 reaches the same function through a stream lookup. + req := a.popPendingAtLocked(c, 0) + + durationUs := at.Sub(req.start).Microseconds() + if durationUs < 0 { + // The two timestamps come from two different socket calls and can straddle a + // clock adjustment. A negative duration is not a measurement. + durationUs = 0 + } + + a.fileAnsweredLocked(c, at, durationUs, isServerError(status)) +} + +// popPendingAtLocked takes one outstanding request off the in-flight list. +// +// The remaining entries keep their relative order, which both callers depend on for +// different reasons: HTTP/1.1 needs the next response to find the next-oldest +// request, and the sweep needs the list sorted by start so it can stop at the first +// entry that has not expired. +func (a *Aggregator) popPendingAtLocked(c *connection, i int) pending { + req := c.inflight[i] + // Shifted rather than resliced: a keep-alive connection completes one request + // after another, and resliced capacity would make every one of them allocate. + copy(c.inflight[i:], c.inflight[i+1:]) + c.inflight = c.inflight[:len(c.inflight)-1] + a.inFlight-- + return req +} + +// goOpaqueLocked stops decoding a connection for good, and files the conn_count that +// says so. +// +// A decoder that lost the stream cannot regain it: neither protocol offers a +// resynchronisation point a reader can find from the middle, and a decoder guessing +// at boundaries reports plausible wrong latencies rather than failing. The +// connection is counted HERE rather than when it was first seen, which is what makes +// conn_count mean "traffic this agent could not read" instead of "traffic". A +// connection that decodes cleanly for its whole life contributes no conn_count at +// all. +// +// Requests still outstanding are dropped without being counted. They were observed, +// so counting them would be defensible -- but the decoder is giving up precisely +// because it no longer trusts what it read, and a request_count that included them +// would be asserting they were real requests on exactly the evidence just declared +// untrustworthy. The gap is what conn_count is for. +func (a *Aggregator) goOpaqueLocked(c *connection, at time.Time) { + if c.state == stateOpaque { + return + } + c.state = stateOpaque + + a.inFlight -= len(c.inflight) + c.inflight = nil + a.releaseBuffersLocked(c) + + if bucket := a.countersLocked(at, c.container); bucket != nil { + bucket.connCount++ + } +} diff --git a/internal/networkmonitor/redmetrics/decode_test.go b/internal/networkmonitor/redmetrics/decode_test.go new file mode 100644 index 00000000..dd5abfe8 --- /dev/null +++ b/internal/networkmonitor/redmetrics/decode_test.go @@ -0,0 +1,771 @@ +package redmetrics + +import ( + "fmt" + "net/http" + "strings" + "testing" + "time" + + "github.com/go-logr/logr" + + gen "github.com/devzero-inc/zxporter/gen/api/v1" +) + +// The tests in this file drive CONNECTIONS, not single messages. A decoder that +// resynchronises incorrectly after the first response produces plausible counts and +// plausible durations rather than an error, so a test that only ever sends one +// request per connection cannot tell a working decoder from a broken one. + +// dataAt builds an observation carrying bytes on one half of the test connection. +func dataAt(cookie uint64, path SyscallPath, at time.Time, b []byte) SocketEvent { + ev := recvAt(cookie, at) + ev.Path = path + ev.TrueLen = uint32(len(b)) + ev.Payload = b + return ev +} + +// reqAt is bytes arriving from the client. +func reqAt(cookie uint64, at time.Time, b []byte) SocketEvent { + return dataAt(cookie, SyscallRecv, at, b) +} + +// respAt is bytes leaving towards the client. +func respAt(cookie uint64, at time.Time, b []byte) SocketEvent { + return dataAt(cookie, SyscallSend, at, b) +} + +// truncate models the probe's copy limit: the syscall carried every byte, and only +// a prefix of them reached userspace. +func truncate(ev SocketEvent, copied int) SocketEvent { + ev.Payload = ev.Payload[:copied] + return ev +} + +// request builds an HTTP/1.1 request with a Content-Length body. +func request(method, path, body string) []byte { + return []byte(fmt.Sprintf( + "%s %s HTTP/1.1\r\nHost: shop.svc\r\nContent-Length: %d\r\n\r\n%s", + method, path, len(body), body)) +} + +// response builds an HTTP/1.1 response with a Content-Length body. +func response(status int, body string) []byte { + return []byte(fmt.Sprintf( + "HTTP/1.1 %d %s\r\nContent-Length: %d\r\n\r\n%s", + status, http.StatusText(status), len(body), body)) +} + +// headers builds a message from raw header lines and no body bytes. +func headers(lines ...string) []byte { + return []byte(strings.Join(lines, "\r\n") + "\r\n\r\n") +} + +// testBase is the minute every test in this file starts from. Bucketing is a +// statement about where a wall clock stood, so the tests need a fixed one. +const testBase = "2026-08-25T10:00:00Z" + +// at is a timestamp inside the test's minute, offset by a duration. +func at(t *testing.T, offset time.Duration) time.Time { + return minuteAt(t, testBase).Add(offset) +} + +// flushOne runs a flush and insists it produced exactly one item. +func flushOne(t *testing.T, agg *Aggregator, clock *fakeClock, now time.Time) *gen.ContainerRedMetricsItem { + t.Helper() + clock.set(now) + items := agg.Flush() + if len(items) != 1 { + t.Fatalf("got %d items, want 1", len(items)) + } + return items[0] +} + +// wantBuckets asserts the cumulative ladder holds exactly one request at each named +// bound index and nothing before the first of them. +func wantBuckets(t *testing.T, got []uint64, want []uint64) { + t.Helper() + if len(got) != DurationBucketCount { + t.Fatalf("ladder length = %d, want %d", len(got), DurationBucketCount) + } + for i := range want { + if got[i] != want[i] { + t.Fatalf("bucket %d = %d, want %d (full ladder %v)", i, got[i], want[i], got) + } + } +} + +// TestDecodeMeasuresEveryRequestOnAKeepAliveConnection is the central test of this +// slice. Three requests with different latencies go down one connection, one after +// another, and every one of them has to be measured -- which can only happen if the +// decoder found the end of each body and put its stream position exactly on the next +// request line. +// +// The three durations are deliberately far apart. A decoder that paired the wrong +// response with the wrong request would produce three durations that still sum to +// roughly the same total, so the sum alone cannot catch it; the maximum and the +// ladder can. +func TestDecodeMeasuresEveryRequestOnAKeepAliveConnection(t *testing.T) { + clock := &fakeClock{now: at(t, 0)} + agg := newTestAggregator(clock, testPods()) + + // 1ms, then 30ms, then 500ms. + agg.Observe(reqAt(1, at(t, 0), request("POST", "/a", "hello"))) + agg.Observe(respAt(1, at(t, time.Millisecond), response(200, "ok"))) + + agg.Observe(reqAt(1, at(t, 100*time.Millisecond), request("POST", "/b", "world!!"))) + agg.Observe(respAt(1, at(t, 130*time.Millisecond), response(200, "fine"))) + + agg.Observe(reqAt(1, at(t, 200*time.Millisecond), request("GET", "/c", ""))) + agg.Observe(respAt(1, at(t, 700*time.Millisecond), response(200, "slow"))) + + item := flushOne(t, agg, clock, at(t, time.Minute+5*time.Second)) + + if item.RequestCount != 3 { + t.Fatalf("request_count = %d, want 3", item.RequestCount) + } + if item.ErrorCount != 0 { + t.Fatalf("error_count = %d, want 0", item.ErrorCount) + } + if item.ConnCount != 0 { + t.Fatalf("conn_count = %d, want 0: a connection decoded end to end is not an opaque connection", item.ConnCount) + } + if want := uint64((1 + 30 + 500) * time.Millisecond.Microseconds()); item.DurationSumUs != want { + t.Fatalf("duration_sum_us = %d, want %d", item.DurationSumUs, want) + } + if want := uint64(500 * time.Millisecond.Microseconds()); item.DurationMaxUs != want { + t.Fatalf("duration_max_us = %d, want %d", item.DurationMaxUs, want) + } + // 1ms at bound 1ms, 30ms at bound 50ms, 500ms at bound 500ms. + wantBuckets(t, item.DurationBuckets, []uint64{0, 1, 1, 1, 1, 1, 2, 2, 2, 3, 3, 3, 3, 3}) +} + +// TestDecodePairsPipelinedResponsesInOrder pins the pairing rule. Two requests are +// outstanding at once and nothing on the wire says which response answers which, so +// the decoder pairs them in order -- which is what HTTP/1.1 requires of the server. +// +// The offsets are chosen so that a decoder pairing them the other way round would +// report the same duration SUM. Only the maximum and the ladder separate the two. +func TestDecodePairsPipelinedResponsesInOrder(t *testing.T) { + clock := &fakeClock{now: at(t, 0)} + agg := newTestAggregator(clock, testPods()) + + agg.Observe(reqAt(1, at(t, 0), request("GET", "/first", ""))) + agg.Observe(reqAt(1, at(t, 10*time.Millisecond), request("GET", "/second", ""))) + + // In order, the durations are 20ms and 190ms. Paired backwards they would be + // 10ms and 200ms -- the same total, a different maximum. + agg.Observe(respAt(1, at(t, 20*time.Millisecond), response(200, "1"))) + agg.Observe(respAt(1, at(t, 200*time.Millisecond), response(200, "2"))) + + item := flushOne(t, agg, clock, at(t, time.Minute+5*time.Second)) + + if item.RequestCount != 2 { + t.Fatalf("request_count = %d, want 2", item.RequestCount) + } + if want := uint64(190 * time.Millisecond.Microseconds()); item.DurationMaxUs != want { + t.Fatalf("duration_max_us = %d, want %d: responses were paired out of order", item.DurationMaxUs, want) + } + // 20ms at bound 20ms, 190ms at bound 200ms. Backwards pairing would put one at + // the 10ms bound instead. + wantBuckets(t, item.DurationBuckets, []uint64{0, 0, 0, 0, 0, 1, 1, 1, 2, 2, 2, 2, 2, 2}) +} + +// TestDecodeStepsOverABodyLargerThanTheCopyLimit pins the reason the probe reports a +// true length at all. A body far larger than what reached userspace must not cost +// the connection its position, or every upload would look like an opaque connection. +func TestDecodeStepsOverABodyLargerThanTheCopyLimit(t *testing.T) { + clock := &fakeClock{now: at(t, 0)} + agg := newTestAggregator(clock, testPods()) + + big := request("POST", "/upload", strings.Repeat("x", 64*1024)) + agg.Observe(truncate(reqAt(1, at(t, 0), big), PayloadCap)) + agg.Observe(respAt(1, at(t, 5*time.Millisecond), response(200, "stored"))) + + // The second request only decodes if the first body was stepped over exactly. + agg.Observe(reqAt(1, at(t, 10*time.Millisecond), request("GET", "/after", ""))) + agg.Observe(respAt(1, at(t, 12*time.Millisecond), response(200, "ok"))) + + item := flushOne(t, agg, clock, at(t, time.Minute+5*time.Second)) + + if item.RequestCount != 2 { + t.Fatalf("request_count = %d, want 2: the oversized body lost the stream position", item.RequestCount) + } + if item.ConnCount != 0 { + t.Fatalf("conn_count = %d, want 0: a truncated BODY is not a truncated boundary", item.ConnCount) + } +} + +// TestDecodeHandlesAResponseSplitAcrossReads pins that a message may arrive in +// several socket calls. A server that writes its headers and then its body does not +// owe the agent one syscall per message. +func TestDecodeHandlesAResponseSplitAcrossReads(t *testing.T) { + clock := &fakeClock{now: at(t, 0)} + agg := newTestAggregator(clock, testPods()) + + agg.Observe(reqAt(1, at(t, 0), request("GET", "/split", ""))) + agg.Observe(respAt(1, at(t, 2*time.Millisecond), + headers("HTTP/1.1 200 OK", "Content-Length: 11"))) + agg.Observe(respAt(1, at(t, 3*time.Millisecond), []byte("hello world"))) + + // Proof the position survived the split: the next exchange decodes too. + agg.Observe(reqAt(1, at(t, 4*time.Millisecond), request("GET", "/after", ""))) + agg.Observe(respAt(1, at(t, 6*time.Millisecond), response(200, "ok"))) + + item := flushOne(t, agg, clock, at(t, time.Minute+5*time.Second)) + + if item.RequestCount != 2 { + t.Fatalf("request_count = %d, want 2", item.RequestCount) + } + if item.ConnCount != 0 { + t.Fatalf("conn_count = %d, want 0", item.ConnCount) + } + // The completion is stamped by the HEADERS, which is when the response became + // an answer; the body that follows is not part of the latency. The reasoning -- + // why time-to-first-byte is the right thing to put beside a resource + // recommendation -- is in docs/features/red-metrics/INVARIANTS.md. + if want := uint64(2 * time.Millisecond.Microseconds()); item.DurationMaxUs != want { + t.Fatalf("duration_max_us = %d, want %d", item.DurationMaxUs, want) + } +} + +// TestDecodeGoesOpaqueWhenAMessageBOUNDARYWasTruncated is the other half of the +// truncation rule. A header block the probe could not copy whole is not something to +// guess at: the connection is given up on, and the conn_count says so. +func TestDecodeGoesOpaqueWhenAMessageBoundaryWasTruncated(t *testing.T) { + clock := &fakeClock{now: at(t, 0)} + agg := newTestAggregator(clock, testPods()) + + agg.Observe(reqAt(1, at(t, 0), request("GET", "/fine", ""))) + agg.Observe(respAt(1, at(t, time.Millisecond), response(200, "ok"))) + + // A request whose headers ran past the copy limit: no terminator arrived, so + // where the body starts is unknowable. + huge := request("GET", "/"+strings.Repeat("q", 8*1024), "") + agg.Observe(truncate(reqAt(1, at(t, 2*time.Millisecond), huge), 512)) + + // Everything after it is unreadable, and must not be counted as if it were. + agg.Observe(respAt(1, at(t, 3*time.Millisecond), response(200, "ignored"))) + agg.Observe(reqAt(1, at(t, 4*time.Millisecond), request("GET", "/ignored", ""))) + + item := flushOne(t, agg, clock, at(t, time.Minute+5*time.Second)) + + if item.RequestCount != 1 { + t.Fatalf("request_count = %d, want 1: only the exchange before the truncation is knowable", item.RequestCount) + } + if item.ConnCount != 1 { + t.Fatalf("conn_count = %d, want 1", item.ConnCount) + } +} + +// TestDecodeGoesOpaqueOnAChunkedBody pins the framing limit. A chunked response is +// accounted for -- it answered its request -- but the decoder cannot find where the +// body ends without reading it, so the connection stops being decoded there. +func TestDecodeGoesOpaqueOnAChunkedBody(t *testing.T) { + clock := &fakeClock{now: at(t, 0)} + agg := newTestAggregator(clock, testPods()) + + agg.Observe(reqAt(1, at(t, 0), request("GET", "/stream", ""))) + agg.Observe(respAt(1, at(t, 5*time.Millisecond), + headers("HTTP/1.1 200 OK", "Transfer-Encoding: chunked"))) + agg.Observe(reqAt(1, at(t, 50*time.Millisecond), request("GET", "/after", ""))) + + item := flushOne(t, agg, clock, at(t, time.Minute+5*time.Second)) + + if item.RequestCount != 1 { + t.Fatalf("request_count = %d, want 1", item.RequestCount) + } + if want := uint64(5 * time.Millisecond.Microseconds()); item.DurationSumUs != want { + t.Fatalf("duration_sum_us = %d, want %d: the chunked response still answered its request", item.DurationSumUs, want) + } + if item.ConnCount != 1 { + t.Fatalf("conn_count = %d, want 1", item.ConnCount) + } +} + +// TestDecodeCountsAnUnansweredRequestWithoutALatency pins the asymmetry that lets +// request_count and the last latency bucket disagree. A request nobody answered +// happened, so it counts; it has no duration, so it enters no bucket; and it is not +// an error, because no status ever said so. +func TestDecodeCountsAnUnansweredRequestWithoutALatency(t *testing.T) { + clock := &fakeClock{now: at(t, 0)} + agg := newTestAggregator(clock, testPods()) + + agg.Observe(reqAt(1, at(t, 50*time.Second), request("GET", "/hangs", ""))) + + // DefaultConfig gives the request fifteen seconds, so it is filed at 10:01:05 -- + // in the minute it was given up on, not the one it started in. The request starts + // late in its minute deliberately: with a short deadline, a request that starts + // early times out in the same minute it began, and the test would pass without + // distinguishing the two rules. + item := flushOne(t, agg, clock, at(t, 2*time.Minute+5*time.Second)) + + if got := item.Timestamp.AsTime(); !got.Equal(minuteAt(t, "2026-08-25T10:01:00Z")) { + t.Fatalf("timestamp = %s, want the minute the request timed out in", got) + } + if item.RequestCount != 1 { + t.Fatalf("request_count = %d, want 1", item.RequestCount) + } + if item.ErrorCount != 0 { + t.Fatalf("error_count = %d, want 0: nothing answered, so nothing failed", item.ErrorCount) + } + if item.DurationSumUs != 0 || item.DurationMaxUs != 0 { + t.Fatalf("duration sum/max = %d/%d, want 0/0", item.DurationSumUs, item.DurationMaxUs) + } + for i, v := range item.DurationBuckets { + if v != 0 { + t.Fatalf("bucket %d = %d, want 0: an unanswered request has no latency", i, v) + } + } +} + +// TestDecodeSettlesAnInFlightRequestWhenTheConnectionCloses pins where an +// interrupted request lands. A client that hangs up mid-response, or a server that +// dies with the request on the floor, produced a request that happened and was never +// answered -- so it counts, on the same terms as one that timed out. +// +// The instant matters as much as the count. The request below would have been filed +// at 10:01:05 by the deadline alone, in the wrong minute; the close is what ended it, +// and that is when the measurement became final. +func TestDecodeSettlesAnInFlightRequestWhenTheConnectionCloses(t *testing.T) { + clock := &fakeClock{now: at(t, 0)} + agg := newTestAggregator(clock, testPods()) + + agg.Observe(reqAt(1, at(t, 50*time.Second), request("GET", "/hangs", ""))) + agg.Observe(closeAt(1, at(t, 52*time.Second))) + + item := flushOne(t, agg, clock, at(t, time.Minute+5*time.Second)) + + if got := item.Timestamp.AsTime(); !got.Equal(minuteAt(t, "2026-08-25T10:00:00Z")) { + t.Fatalf("timestamp = %s, want the minute the connection closed in", got) + } + if item.RequestCount != 1 { + t.Fatalf("request_count = %d, want 1", item.RequestCount) + } + if item.ErrorCount != 0 { + t.Fatalf("error_count = %d, want 0: nothing answered, so nothing failed", item.ErrorCount) + } + if item.DurationSumUs != 0 || item.DurationMaxUs != 0 { + t.Fatalf("duration sum/max = %d/%d, want 0/0", item.DurationSumUs, item.DurationMaxUs) + } +} + +// TestDecodeFilesAnAbandonedRequestAtItsDeadlineNotTheClose pins the other half of +// the close path. A connection can sit open long after the agent gave up waiting for +// a response, and closing it does not change when that happened: the request expired +// at its deadline, so it belongs to the minute the deadline fell in. Filing it at the +// close would pile every abandoned request onto whichever minute the socket happened +// to be torn down in, which for a pooled client is arbitrary. +func TestDecodeFilesAnAbandonedRequestAtItsDeadlineNotTheClose(t *testing.T) { + clock := &fakeClock{now: at(t, 0)} + agg := newTestAggregator(clock, testPods()) + + // Deadline is 10:01:05, two minutes before the close. + agg.Observe(reqAt(1, at(t, 50*time.Second), request("GET", "/hangs", ""))) + agg.Observe(closeAt(1, at(t, 3*time.Minute))) + + item := flushOne(t, agg, clock, at(t, 4*time.Minute)) + + if got := item.Timestamp.AsTime(); !got.Equal(minuteAt(t, "2026-08-25T10:01:00Z")) { + t.Fatalf("timestamp = %s, want the minute the request was given up on", got) + } + if item.RequestCount != 1 { + t.Fatalf("request_count = %d, want 1", item.RequestCount) + } +} + +// TestDecodeReleasesAnOpaqueConnectionOnClose pins that giving up on decoding a +// connection does not exempt it from being reclaimed. An opaque connection holds no +// requests and no buffers, but it holds a table entry and the admission slot that +// goes with it -- and on a node running TLS, which is most of them, that is the state +// the table is mostly made of. +func TestDecodeReleasesAnOpaqueConnectionOnClose(t *testing.T) { + clock := &fakeClock{now: at(t, 0)} + agg := newTestAggregator(clock, testPods()) + + agg.Observe(reqAt(1, at(t, 0), []byte{0x16, 0x03, 0x01, 0x02, 0x00, 0x01, 0x00})) + if got := agg.connCount(); got != 1 { + t.Fatalf("tracked connections = %d, want the opaque connection tracked", got) + } + + agg.Observe(closeAt(1, at(t, time.Second))) + if got := agg.connCount(); got != 0 { + t.Fatalf("tracked connections = %d, want it released", got) + } + + // It was counted once when it went opaque, and the close does not count it again. + item := flushOne(t, agg, clock, at(t, time.Minute+5*time.Second)) + if item.ConnCount != 1 { + t.Fatalf("conn_count = %d, want 1", item.ConnCount) + } + if item.RequestCount != 0 { + t.Fatalf("request_count = %d, want 0", item.RequestCount) + } +} + +// TestDecodeFilesARequestUnderTheMinuteItCompleted pins the bucketing rule against +// the obvious alternative. A request that began at 10:00:59 and was answered at +// 10:01:02 belongs to 10:01, because that is when its measurement became final -- +// filing it under 10:00 would mean reopening a minute that may already have shipped. +func TestDecodeFilesARequestUnderTheMinuteItCompleted(t *testing.T) { + clock := &fakeClock{now: minuteAt(t, "2026-08-25T10:00:59Z")} + agg := newTestAggregator(clock, testPods()) + + agg.Observe(reqAt(1, minuteAt(t, "2026-08-25T10:00:59Z"), request("GET", "/late", ""))) + agg.Observe(respAt(1, minuteAt(t, "2026-08-25T10:01:02Z"), response(200, "ok"))) + + item := flushOne(t, agg, clock, minuteAt(t, "2026-08-25T10:02:05Z")) + + if got := item.Timestamp.AsTime(); !got.Equal(minuteAt(t, "2026-08-25T10:01:00Z")) { + t.Fatalf("timestamp = %s, want 10:01:00", got) + } + if want := uint64(3 * time.Second.Microseconds()); item.DurationSumUs != want { + t.Fatalf("duration_sum_us = %d, want %d", item.DurationSumUs, want) + } +} + +// TestDecodeCountsServerErrorsOnly pins what error_count means. A 4XX is the caller +// being wrong; counting it would make the error rate track client behaviour rather +// than the health of the container the number sits beside. +func TestDecodeCountsServerErrorsOnly(t *testing.T) { + clock := &fakeClock{now: at(t, 0)} + agg := newTestAggregator(clock, testPods()) + + statuses := []int{200, 404, 400, 500, 503, 301} + for i, status := range statuses { + offset := time.Duration(i) * 10 * time.Millisecond + agg.Observe(reqAt(1, at(t, offset), request("GET", "/x", ""))) + agg.Observe(respAt(1, at(t, offset+time.Millisecond), response(status, "body"))) + } + + item := flushOne(t, agg, clock, at(t, time.Minute+5*time.Second)) + + if item.RequestCount != uint64(len(statuses)) { + t.Fatalf("request_count = %d, want %d", item.RequestCount, len(statuses)) + } + if item.ErrorCount != 2 { + t.Fatalf("error_count = %d, want 2 (the 500 and the 503 only)", item.ErrorCount) + } +} + +// TestDecodeIgnoresInformationalResponses pins that a 100 Continue is not an answer. +// Treating it as one would report a latency that measured the server's willingness +// to read a body, and would leave the real response with nothing outstanding. +func TestDecodeIgnoresInformationalResponses(t *testing.T) { + clock := &fakeClock{now: at(t, 0)} + agg := newTestAggregator(clock, testPods()) + + agg.Observe(reqAt(1, at(t, 0), request("POST", "/expect", "payload"))) + agg.Observe(respAt(1, at(t, time.Millisecond), headers("HTTP/1.1 100 Continue"))) + agg.Observe(respAt(1, at(t, 40*time.Millisecond), response(200, "done"))) + + item := flushOne(t, agg, clock, at(t, time.Minute+5*time.Second)) + + if item.RequestCount != 1 { + t.Fatalf("request_count = %d, want 1", item.RequestCount) + } + if want := uint64(40 * time.Millisecond.Microseconds()); item.DurationSumUs != want { + t.Fatalf("duration_sum_us = %d, want %d: the latency is measured to the real response", item.DurationSumUs, want) + } + if item.ConnCount != 0 { + t.Fatalf("conn_count = %d, want 0", item.ConnCount) + } +} + +// TestDecodeHandlesHeadResponses pins the one framing rule that cannot be read off +// the response alone. A HEAD response declares the length the body WOULD have had +// and then sends nothing, so a decoder that skipped that many bytes would land in +// the middle of the next response and never recover. +func TestDecodeHandlesHeadResponses(t *testing.T) { + clock := &fakeClock{now: at(t, 0)} + agg := newTestAggregator(clock, testPods()) + + agg.Observe(reqAt(1, at(t, 0), request("HEAD", "/thing", ""))) + agg.Observe(respAt(1, at(t, time.Millisecond), + headers("HTTP/1.1 200 OK", "Content-Length: 4096"))) + + agg.Observe(reqAt(1, at(t, 2*time.Millisecond), request("GET", "/thing", ""))) + agg.Observe(respAt(1, at(t, 3*time.Millisecond), response(200, "real body"))) + + item := flushOne(t, agg, clock, at(t, time.Minute+5*time.Second)) + + if item.RequestCount != 2 { + t.Fatalf("request_count = %d, want 2: the phantom HEAD body was skipped as if it were real", item.RequestCount) + } + if item.ConnCount != 0 { + t.Fatalf("conn_count = %d, want 0", item.ConnCount) + } +} + +// TestDecodeDropsAResponseWithNothingOutstanding pins what happens when the wire +// says something the decoder has no start for. Counting it would put a request in +// the total that has no duration and no cause; inventing a duration would put a +// number in the ladder that no request produced. +func TestDecodeDropsAResponseWithNothingOutstanding(t *testing.T) { + clock := &fakeClock{now: at(t, 0)} + agg := newTestAggregator(clock, testPods()) + + agg.Observe(reqAt(1, at(t, 0), request("GET", "/one", ""))) + agg.Observe(respAt(1, at(t, time.Millisecond), response(200, "ok"))) + agg.Observe(respAt(1, at(t, 2*time.Millisecond), response(500, "stray"))) + + item := flushOne(t, agg, clock, at(t, time.Minute+5*time.Second)) + + if item.RequestCount != 1 { + t.Fatalf("request_count = %d, want 1", item.RequestCount) + } + if item.ErrorCount != 0 { + t.Fatalf("error_count = %d, want 0: a 5XX answering nothing is not a failed request", item.ErrorCount) + } +} + +// TestDecodeTreatsUnreadableFirstBytesAsAnOpaqueConnection pins the sniff. TLS, a +// protocol nobody taught this decoder, and a connection joined midstream all look +// the same from here: bytes with no request line, and no way to synchronise. +func TestDecodeTreatsUnreadableFirstBytesAsAnOpaqueConnection(t *testing.T) { + + cases := []struct { + name string + first []byte + }{ + {"tls client hello", []byte{0x16, 0x03, 0x01, 0x02, 0x00, 0x01, 0x00}}, + {"midstream body bytes", []byte("...continuation of somebody's body\r\n\r\n")}, + // An HTTP/2 connection the agent joined after its preface. The frame bytes + // are perfectly valid HTTP/2 -- a HEADERS frame on stream 1 -- and are still + // unreadable, because the preface is the only marker HTTP/2 offers and + // without it there is nothing to say where a frame starts. Guessing would + // mean reading a length out of the middle of somebody's payload. + {"midstream http/2 frames", []byte{0x00, 0x00, 0x04, 0x01, 0x05, 0x00, 0x00, 0x00, 0x01, 0x82, 0x84, 0x86, 0x41}}, + } + + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + clock := &fakeClock{now: at(t, 0)} + agg := newTestAggregator(clock, testPods()) + + agg.Observe(reqAt(1, at(t, 0), tc.first)) + agg.Observe(respAt(1, at(t, time.Millisecond), response(200, "ok"))) + + item := flushOne(t, agg, clock, at(t, time.Minute+5*time.Second)) + + if item.ConnCount != 1 { + t.Fatalf("conn_count = %d, want 1", item.ConnCount) + } + if item.RequestCount != 0 { + t.Fatalf("request_count = %d, want 0", item.RequestCount) + } + }) + } +} + +// TestDecodeSurvivesAnEmptyFirstRead pins that a syscall carrying no bytes decides +// nothing about a connection. A zero-length recv is what a poll loop or a peek looks +// like from the probe, and it is evidence of nothing: there are no first bytes to +// fail to recognise, so the connection is still new and its real first request is +// still to come. +// +// The distinction matters because the alternative is silent and expensive. Treating +// an empty read as unreadable would count a conn_count against a container that was +// serving perfectly readable HTTP, and would then decode none of the traffic that +// followed on that connection -- opacity being permanent by design. +func TestDecodeSurvivesAnEmptyFirstRead(t *testing.T) { + clock := &fakeClock{now: at(t, 0)} + agg := newTestAggregator(clock, testPods()) + + agg.Observe(reqAt(1, at(t, 0), nil)) + agg.Observe(reqAt(1, at(t, time.Millisecond), request("GET", "/x", ""))) + agg.Observe(respAt(1, at(t, 3*time.Millisecond), response(200, "ok"))) + + item := flushOne(t, agg, clock, at(t, time.Minute+5*time.Second)) + + if item.ConnCount != 0 { + t.Fatalf("conn_count = %d, want 0: an empty read is not an unreadable connection", item.ConnCount) + } + if item.RequestCount != 1 { + t.Fatalf("request_count = %d, want 1", item.RequestCount) + } + if want := uint64(2 * time.Millisecond.Microseconds()); item.DurationSumUs != want { + t.Fatalf("duration_sum_us = %d, want %d", item.DurationSumUs, want) + } +} + +// TestDecodeCountsAnOpaqueConnectionOnce pins that conn_count is a count of +// connections given up on, not of unreadable syscalls. A connection that goes opaque +// and then carries a thousand more reads is still one opaque connection. +func TestDecodeCountsAnOpaqueConnectionOnce(t *testing.T) { + clock := &fakeClock{now: at(t, 0)} + agg := newTestAggregator(clock, testPods()) + + agg.Observe(reqAt(1, at(t, 0), []byte{0x16, 0x03, 0x01, 0x00, 0x05})) + for i := 1; i < 200; i++ { + agg.Observe(reqAt(1, at(t, time.Duration(i)*time.Millisecond), []byte("more ciphertext"))) + } + + item := flushOne(t, agg, clock, at(t, time.Minute+5*time.Second)) + + if item.ConnCount != 1 { + t.Fatalf("conn_count = %d, want 1", item.ConnCount) + } +} + +// TestDecodeCountsBothWhenAConnectionDegradesMidStream pins the case the glossary's +// "an opaque connection contributes a connection count and nothing else" does not +// describe: a connection is not opaque or readable for its whole life, it is readable +// until the moment it is not. The requests read before that moment are real +// measurements and stay; the conn_count marks where the reading stopped. +func TestDecodeCountsBothWhenAConnectionDegradesMidStream(t *testing.T) { + clock := &fakeClock{now: at(t, 0)} + agg := newTestAggregator(clock, testPods()) + + agg.Observe(reqAt(1, at(t, 0), request("GET", "/a", ""))) + agg.Observe(respAt(1, at(t, time.Millisecond), response(200, "ok"))) + agg.Observe(reqAt(1, at(t, 2*time.Millisecond), request("GET", "/b", ""))) + agg.Observe(respAt(1, at(t, 4*time.Millisecond), response(200, "ok"))) + + // An upgrade, a proxied CONNECT tunnel, anything: from here the bytes are not + // HTTP/1.1 messages. + agg.Observe(reqAt(1, at(t, 6*time.Millisecond), []byte{0x00, 0x01, 0x02, 0x03})) + + item := flushOne(t, agg, clock, at(t, time.Minute+5*time.Second)) + + if item.RequestCount != 2 { + t.Fatalf("request_count = %d, want 2", item.RequestCount) + } + if item.ConnCount != 1 { + t.Fatalf("conn_count = %d, want 1", item.ConnCount) + } +} + +// TestDecodeDropsOutstandingRequestsWhenAConnectionGoesOpaque pins the deliberate +// gap. The decoder gives up because it no longer trusts what it read, so counting +// the requests it was still holding would assert they were real on exactly the +// evidence just declared untrustworthy. +func TestDecodeDropsOutstandingRequestsWhenAConnectionGoesOpaque(t *testing.T) { + clock := &fakeClock{now: at(t, 0)} + agg := newTestAggregator(clock, testPods()) + + agg.Observe(reqAt(1, at(t, 0), request("GET", "/never-answered", ""))) + agg.Observe(reqAt(1, at(t, time.Millisecond), []byte{0xff, 0xfe, 0xfd})) + + // Long enough that the request sweep would have filed it, had it survived. + item := flushOne(t, agg, clock, at(t, 5*time.Minute)) + + if item.RequestCount != 0 { + t.Fatalf("request_count = %d, want 0", item.RequestCount) + } + if item.ConnCount != 1 { + t.Fatalf("conn_count = %d, want 1", item.ConnCount) + } +} + +// TestDecodeTakesTheConnectionOpaqueAtTheInFlightCap pins the memory bound's +// behaviour. Skipping the request that found no room would leave its response to be +// paired with a LATER request on the same connection, reporting a duration made of +// two different requests -- so the connection goes with it. +func TestDecodeTakesTheConnectionOpaqueAtTheInFlightCap(t *testing.T) { + clock := &fakeClock{now: at(t, 0)} + agg := NewAggregator(Config{MaxInFlightRequests: 2}, clock, testPods(), logr.Discard()) + + for i := 0; i < 3; i++ { + agg.Observe(reqAt(1, at(t, time.Duration(i)*time.Millisecond), request("GET", "/x", ""))) + } + agg.Observe(respAt(1, at(t, 10*time.Millisecond), response(200, "ok"))) + + item := flushOne(t, agg, clock, at(t, 5*time.Minute)) + + if item.ConnCount != 1 { + t.Fatalf("conn_count = %d, want 1", item.ConnCount) + } + if item.RequestCount != 0 { + t.Fatalf("request_count = %d, want 0: the two it was holding went with the connection", item.RequestCount) + } +} + +// TestDecodeReleasesInFlightRequestsBackToTheCap pins that the cap is a ceiling on +// concurrency rather than a lifetime budget. A completed request has to give its slot +// back, or a long-lived agent would eventually refuse everything. +func TestDecodeReleasesInFlightRequestsBackToTheCap(t *testing.T) { + clock := &fakeClock{now: at(t, 0)} + agg := NewAggregator(Config{MaxInFlightRequests: 1}, clock, testPods(), logr.Discard()) + + for i := 0; i < 5; i++ { + offset := time.Duration(i) * 10 * time.Millisecond + agg.Observe(reqAt(1, at(t, offset), request("GET", "/x", ""))) + agg.Observe(respAt(1, at(t, offset+time.Millisecond), response(200, "ok"))) + } + + item := flushOne(t, agg, clock, at(t, time.Minute+5*time.Second)) + + if item.RequestCount != 5 { + t.Fatalf("request_count = %d, want 5", item.RequestCount) + } + if item.ConnCount != 0 { + t.Fatalf("conn_count = %d, want 0: each request freed its slot", item.ConnCount) + } +} + +// TestDecodeGoesOpaqueWhenAPipelinedBoundaryWasTruncated is the truncation rule at +// the harder position. The read carried two whole requests and the probe copied only +// the first, so the second request's boundary sits in bytes that exist on the wire +// and not in memory. Walking only the copied prefix would end the walk quietly and +// leave the connection looking healthy while its position was already wrong. +func TestDecodeGoesOpaqueWhenAPipelinedBoundaryWasTruncated(t *testing.T) { + clock := &fakeClock{now: at(t, 0)} + agg := newTestAggregator(clock, testPods()) + + first := request("GET", "/first", "") + both := append(append([]byte{}, first...), request("GET", "/second", "")...) + + agg.Observe(truncate(reqAt(1, at(t, 0), both), len(first))) + + // Long enough that a request left in flight would have been swept and counted. + item := flushOne(t, agg, clock, at(t, 5*time.Minute)) + + if item.ConnCount != 1 { + t.Fatalf("conn_count = %d, want 1: the second boundary was never copied", item.ConnCount) + } + if item.RequestCount != 0 { + t.Fatalf("request_count = %d, want 0: the request in flight went with the connection", item.RequestCount) + } +} + +// TestDecodeKeepsConnectionsIndependent pins that decode state is per connection. +// A shared in-flight list would pair a response on one connection with a request on +// another -- a duration made of two different clients' requests -- and a shared +// stream position would desynchronise both the moment either one sent a body. +func TestDecodeKeepsConnectionsIndependent(t *testing.T) { + clock := &fakeClock{now: at(t, 0)} + agg := newTestAggregator(clock, testPods()) + + // Two clients, interleaved, with very different latencies. + agg.Observe(reqAt(1, at(t, 0), request("POST", "/slow", "body-on-one"))) + agg.Observe(reqAt(2, at(t, time.Millisecond), request("GET", "/fast", ""))) + agg.Observe(respAt(2, at(t, 2*time.Millisecond), response(200, "quick"))) + agg.Observe(respAt(1, at(t, 300*time.Millisecond), response(500, "eventually"))) + + // Both must still be readable afterwards, which only holds if each kept its own + // position through the other's traffic. + agg.Observe(reqAt(2, at(t, 310*time.Millisecond), request("GET", "/again", ""))) + agg.Observe(respAt(2, at(t, 311*time.Millisecond), response(200, "ok"))) + + item := flushOne(t, agg, clock, at(t, time.Minute+5*time.Second)) + + if item.RequestCount != 3 { + t.Fatalf("request_count = %d, want 3", item.RequestCount) + } + if item.ErrorCount != 1 { + t.Fatalf("error_count = %d, want 1", item.ErrorCount) + } + if item.ConnCount != 0 { + t.Fatalf("conn_count = %d, want 0", item.ConnCount) + } + // 300ms on connection 1, 1ms and 1ms on connection 2. Crossed pairing would put + // the maximum at 299ms or 310ms instead. + if want := uint64(300 * time.Millisecond.Microseconds()); item.DurationMaxUs != want { + t.Fatalf("duration_max_us = %d, want %d: responses crossed between connections", item.DurationMaxUs, want) + } + if want := uint64((300 + 1 + 1) * time.Millisecond.Microseconds()); item.DurationSumUs != want { + t.Fatalf("duration_sum_us = %d, want %d", item.DurationSumUs, want) + } +} diff --git a/internal/networkmonitor/redmetrics/doc.go b/internal/networkmonitor/redmetrics/doc.go new file mode 100644 index 00000000..eb870c5e --- /dev/null +++ b/internal/networkmonitor/redmetrics/doc.go @@ -0,0 +1,19 @@ +// Package redmetrics turns socket observations into per-container, per-minute RED +// measurements for the containers running on this node. +// +// The shape of the thing is fixed by where it ends up. Rows land in a ClickHouse +// SummingMergeTree keyed by (cluster_id, timestamp, container_id), which means +// merges ADD rows that share a key. Three consequences run through this package: +// +// 1. Every value emitted is a DELTA for one minute, never a running total. +// 2. A minute bucket closes when the wall clock passes it and is never reopened, +// so a bucket is emitted exactly once. +// 3. Nothing may retry. A re-sent item is indistinguishable from real extra +// traffic, and no amount of care downstream can undo it. +// +// See docs/features/red-metrics/INVARIANTS.md. +// +// The aggregation is keyed by {minute, container} and deliberately NOT by peer. +// That is what keeps the in-memory footprint bounded by the number of containers +// the kubelet is running rather than by how many distinct callers reach them. +package redmetrics diff --git a/internal/networkmonitor/redmetrics/event.go b/internal/networkmonitor/redmetrics/event.go new file mode 100644 index 00000000..86d88035 --- /dev/null +++ b/internal/networkmonitor/redmetrics/event.go @@ -0,0 +1,96 @@ +package redmetrics + +import ( + "net/netip" + "time" +) + +// PayloadCap is the most bytes a single socket observation carries into +// userspace. The probe copies at most this much per call and always reports the +// call's true length separately, so a decoder can advance its stream position +// through a body it never saw. +// +// It is the reason a truncated message boundary costs a connection its +// decodability while a truncated body does not: a body is walked past by count, +// but a header block has to be read, and 2KB is the budget for reading one. +const PayloadCap = 2048 + +// SyscallPath says which socket call the event came from. Direction falls out of +// the two that carry bytes: the first bytes seen on a connection arriving on the +// receive path mean this node is answering a request, and on the send path mean it +// is making one. +type SyscallPath uint8 + +// The socket calls the probe attaches to. +const ( + // SyscallSend is tcp_sendmsg: bytes leaving this node. + SyscallSend SyscallPath = iota + // SyscallRecv is tcp_recvmsg: bytes arriving at this node. + SyscallRecv + // SyscallClose is the socket going away, from either tcp_close or tcp_done. It + // carries no bytes -- TrueLen is zero and Payload is nil -- and says only that this + // connection will never be seen again, so its state can be released there instead + // of waiting for an idle timer. + // + // A connection reset by the kernel produces one from each probe, and the second + // finds nothing left to release. That is expected; see c/socketprobe.c. + SyscallClose +) + +// String renders the syscall path for logs. +func (p SyscallPath) String() string { + switch p { + case SyscallSend: + return "send" + case SyscallRecv: + return "recv" + case SyscallClose: + return "close" + default: + return "unknown" + } +} + +// SocketEvent is one observation of bytes crossing a TCP socket, read off the +// socket structure rather than parsed out of packet headers. Reading the socket +// rather than the wire is what removes both header parsing and NAT ambiguity: +// the addresses are the ones the kernel itself associates with the connection. +type SocketEvent struct { + // SocketCookie identifies the socket the bytes crossed. It is opaque and only + // unique among sockets alive at the same instant: the kernel recycles the + // structure it derives from, so it must be paired with the connection's + // four-tuple before two observations can be called the same connection. + SocketCookie uint64 + + // Path is the syscall the bytes crossed on. + Path SyscallPath + + // LocalAddr and LocalPort are this node's side of the connection. For an + // inbound service request these are the served pod and the port it listens on, + // which is what attribution keys off. + LocalAddr netip.Addr + LocalPort uint16 + + // RemoteAddr and RemotePort are the peer's side of the connection. They are + // recorded but never aggregated by: keying on the caller is exactly what would + // make the in-memory map grow with caller cardinality. + RemoteAddr netip.Addr + RemotePort uint16 + + // TrueLen is the length the syscall actually carried, whatever prefix of it the + // probe copied. The decoder walks by this rather than by len(Payload), which is + // what lets a body larger than the copy limit be stepped over without losing the + // stream position. + TrueLen uint32 + + // Payload is the prefix of those bytes the probe copied, at most PayloadCap of + // them and often fewer. It is a copy rather than a view of the ring buffer + // sample, which the kernel reuses the moment the record is released. + // + // It may be shorter than TrueLen and may be empty. A decoder must treat a + // boundary that falls beyond it as unreadable rather than as absent. + Payload []byte + + // Timestamp is when the kernel saw the call, already mapped onto wall clock. + Timestamp time.Time +} diff --git a/internal/networkmonitor/redmetrics/histogram.go b/internal/networkmonitor/redmetrics/histogram.go new file mode 100644 index 00000000..2d2ebf4b --- /dev/null +++ b/internal/networkmonitor/redmetrics/histogram.go @@ -0,0 +1,76 @@ +package redmetrics + +// durationBoundsUs are the latency ladder's inclusive upper bounds, in +// microseconds and ascending. A request whose duration is exactly a bound lands in +// that bound's bucket rather than the next one up. +// +// The ladder is a 1-2-5 progression from 500us to 5s, plus an overflow bucket. It +// is FIXED FOR THE LIFE OF THE FEATURE. The table stores one column per bound, so +// inserting a bound later does not add detail to history -- it reinterprets every +// row already written, because the column that used to mean "at or below 5ms" now +// means something else, and nothing in the row says which era it came from. +// Widening at the top has the same problem in reverse. +// +// The reasoning, and why a fixed ladder was chosen over a percentile sketch, is in +// docs/features/red-metrics/INVARIANTS.md. +var durationBoundsUs = [...]int64{ + 500, // 500us + 1_000, // 1ms + 2_000, // 2ms + 5_000, // 5ms + 10_000, // 10ms + 20_000, // 20ms + 50_000, // 50ms + 100_000, // 100ms + 200_000, // 200ms + 500_000, // 500ms + 1_000_000, // 1s + 2_000_000, // 2s + 5_000_000, // 5s +} + +// The wire contract and the ladder have to be the same length, and the server +// rejects the whole request when they are not. Asserted from both sides so that +// adding a bound without changing the contract fails to compile here rather than +// failing every request at runtime. +const ( + _ = uint(DurationBucketCount - (len(durationBoundsUs) + 1)) + _ = uint((len(durationBoundsUs) + 1) - DurationBucketCount) +) + +// histogram counts durations per bucket EXCLUSIVELY: bucket i holds the requests +// that fell above bound i-1 and at or below bound i, and the last bucket holds +// everything slower than the last bound. +// +// The wire wants cumulative counters, which cumulative() derives at emit time. +// Counting exclusively keeps the hot path to a single increment, and makes the +// emitted counters monotonic by construction rather than by care -- a hot path that +// incremented every bucket at or above the duration would be O(buckets) per request +// and would go non-monotonic the moment one of those increments was missed. +type histogram [DurationBucketCount]uint64 + +// observe files one duration in microseconds. +func (h *histogram) observe(us int64) { h[bucketFor(us)]++ } + +// bucketFor is the index of the bucket a duration belongs in. +func bucketFor(us int64) int { + for i, bound := range durationBoundsUs { + if us <= bound { + return i + } + } + return DurationBucketCount - 1 +} + +// cumulative turns the exclusive counts into the counters the item carries: each +// one is the number of requests that completed at or below its bound. The last is +// the total, since every request is at or below infinity. +func (h histogram) cumulative() []uint64 { + out := make([]uint64, DurationBucketCount) + var running uint64 + for i, count := range h { + running += count + out[i] = running + } + return out +} diff --git a/internal/networkmonitor/redmetrics/histogram_test.go b/internal/networkmonitor/redmetrics/histogram_test.go new file mode 100644 index 00000000..0dc2afe1 --- /dev/null +++ b/internal/networkmonitor/redmetrics/histogram_test.go @@ -0,0 +1,218 @@ +package redmetrics + +import ( + "strconv" + "strings" + "testing" + "time" +) + +// TestHistogramLadderMatchesTheStoredColumns is the test that had to exist. The +// ladder is the one part of this feature that fails SILENTLY: every other mistake +// either fails to compile, fails a length check at the server, or produces a number +// somebody notices. A bound quietly changed from 20ms to 25ms keeps the ladder the +// right length, keeps it ascending, keeps every inclusivity property -- and from +// then on the column named dur_le_20ms holds a count of requests at or below 25ms, +// with nothing in the row saying so and no way to tell the two eras apart later. +// +// So the expectation is not a second copy of the numbers, which would be changed in +// the same edit as the first. It is the ClickHouse column NAMES, in order, parsed +// back into microseconds. Those names are what the stored data means; the ladder has +// to agree with them or the table is mislabelled. They cannot be imported -- the +// migration lives in the dakr module and this is the zxporter module -- so they are +// transcribed, and transcribing a NAME is a different act from restating a number. +// +// Source of truth: dakr/clickhousemigrations/m20260825_container_red_metrics.sql. +func TestHistogramLadderMatchesTheStoredColumns(t *testing.T) { + // The column list from the migration, in the order the columns are declared, + // which is the order the emitted ladder is read in. + columns := []string{ + "dur_le_500us", + "dur_le_1ms", + "dur_le_2ms", + "dur_le_5ms", + "dur_le_10ms", + "dur_le_20ms", + "dur_le_50ms", + "dur_le_100ms", + "dur_le_200ms", + "dur_le_500ms", + "dur_le_1s", + "dur_le_2s", + "dur_le_5s", + "dur_le_inf", + } + + if len(columns) != DurationBucketCount { + t.Fatalf("transcribed %d columns, want %d", len(columns), DurationBucketCount) + } + // The last column is the overflow bucket and has no bound; every other column + // names one. + if got := columns[len(columns)-1]; got != "dur_le_inf" { + t.Fatalf("last column = %q, want the overflow bucket", got) + } + if len(columns)-1 != len(durationBoundsUs) { + t.Fatalf("%d bounded columns, %d bounds", len(columns)-1, len(durationBoundsUs)) + } + + for i, column := range columns[:len(columns)-1] { + want := boundFromColumnName(t, column) + if durationBoundsUs[i] != want { + t.Fatalf("bound %d is %dus, but column %q says %dus", + i, durationBoundsUs[i], column, want) + } + } +} + +// boundFromColumnName reads a bound back out of the column that stores it, so the +// test's expectation is the stored name rather than a restatement of the constant +// it is checking. +func boundFromColumnName(t *testing.T, column string) int64 { + t.Helper() + + suffix, ok := strings.CutPrefix(column, "dur_le_") + if !ok { + t.Fatalf("column %q is not a latency bucket", column) + } + + for unit, scale := range map[string]time.Duration{ + "us": time.Microsecond, + "ms": time.Millisecond, + "s": time.Second, + } { + digits, ok := strings.CutSuffix(suffix, unit) + if !ok { + continue + } + n, err := strconv.ParseInt(digits, 10, 64) + if err != nil { + continue + } + return (time.Duration(n) * scale).Microseconds() + } + + t.Fatalf("column %q has no readable duration in its name", column) + return 0 +} + +// TestHistogramLadderIsTheContractLength pins the one property the server enforces. +// The handler rejects an item whose ladder is not exactly this long, so a bound added +// without changing the contract would fail every request at runtime. +func TestHistogramLadderIsTheContractLength(t *testing.T) { + if got := len(durationBoundsUs) + 1; got != DurationBucketCount { + t.Fatalf("ladder = %d bounds plus overflow = %d, want %d", len(durationBoundsUs), got, DurationBucketCount) + } + var h histogram + if got := len(h.cumulative()); got != DurationBucketCount { + t.Fatalf("cumulative length = %d, want %d", got, DurationBucketCount) + } +} + +// TestHistogramLadderAscends pins that the bounds are ordered. bucketFor returns the +// first bound a duration fits under, so an out-of-order ladder would file durations +// in a bucket that means something else without any test of a single duration +// noticing. +func TestHistogramLadderAscends(t *testing.T) { + for i := 1; i < len(durationBoundsUs); i++ { + if durationBoundsUs[i] <= durationBoundsUs[i-1] { + t.Fatalf("bound %d (%d) is not above bound %d (%d)", + i, durationBoundsUs[i], i-1, durationBoundsUs[i-1]) + } + } +} + +// TestHistogramBoundsAreInclusive pins which side of a bound a duration falls on. A +// request that took exactly 1ms belongs in the bucket that says "at or below 1ms", +// which is what the emitted counter is read as. +func TestHistogramBoundsAreInclusive(t *testing.T) { + for i, bound := range durationBoundsUs { + if got := bucketFor(bound); got != i { + t.Fatalf("a duration of exactly %dus went to bucket %d, want %d", bound, got, i) + } + if got := bucketFor(bound + 1); got != i+1 { + t.Fatalf("a duration of %dus went to bucket %d, want %d", bound+1, got, i+1) + } + } +} + +// TestHistogramOverflowCatchesEverythingSlower pins the top of the ladder. A request +// slower than the last bound still has to land somewhere, or the total the last +// counter carries would disagree with request_count for slow services specifically. +func TestHistogramOverflowCatchesEverythingSlower(t *testing.T) { + last := durationBoundsUs[len(durationBoundsUs)-1] + for _, us := range []int64{last + 1, 10 * last, 1 << 40} { + if got := bucketFor(us); got != DurationBucketCount-1 { + t.Fatalf("a duration of %dus went to bucket %d, want the overflow bucket %d", + us, got, DurationBucketCount-1) + } + } +} + +// TestHistogramCountsAZeroDuration pins the bottom. A duration of zero is what a +// clock adjustment between two syscalls produces, and it is a measurement of +// something -- it belongs in the fastest bucket, not below the ladder. +func TestHistogramCountsAZeroDuration(t *testing.T) { + if got := bucketFor(0); got != 0 { + t.Fatalf("a zero duration went to bucket %d, want 0", got) + } +} + +// TestHistogramCumulativeIsMonotonicAndTotalled pins the shape the wire expects. +// Each counter is the number of requests at or below its bound, so the counters can +// only ascend, and the last one is the total because every request is at or below +// infinity. A consumer computing a quantile reads the ladder as a distribution +// function; a dip in it is not a slow bucket, it is a nonsensical answer. +func TestHistogramCumulativeIsMonotonicAndTotalled(t *testing.T) { + var h histogram + durations := []int64{0, 400, 500, 501, 1_500, 9_999, 250_000, 4_000_000, 30_000_000} + for _, us := range durations { + h.observe(us) + } + + got := h.cumulative() + for i := 1; i < len(got); i++ { + if got[i] < got[i-1] { + t.Fatalf("counter %d (%d) is below counter %d (%d): %v", i, got[i], i-1, got[i-1], got) + } + } + if want := uint64(len(durations)); got[len(got)-1] != want { + t.Fatalf("last counter = %d, want %d (every request is at or below infinity)", got[len(got)-1], want) + } +} + +// TestHistogramCumulativeCountsEachDurationOnce walks a known set through to the +// emitted counters. It is the one test that would catch an off-by-one between +// observe's exclusive counting and cumulative's running total. +func TestHistogramCumulativeCountsEachDurationOnce(t *testing.T) { + var h histogram + // 500us at bound 0, 2ms at bound 2, 2ms again, 5s at the last bound, and one + // slower than the ladder. + for _, us := range []int64{500, 2_000, 2_000, 5_000_000, 6_000_000} { + h.observe(us) + } + + want := []uint64{1, 1, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 5} + got := h.cumulative() + for i := range want { + if got[i] != want[i] { + t.Fatalf("counter %d = %d, want %d (full ladder %v)", i, got[i], want[i], got) + } + } +} + +// TestHistogramIsEmptyUntilSomethingIsObserved pins that a bucket with no completed +// requests emits a full ladder of zeroes rather than a short one. The length is what +// says the two sides agree about what the buckets mean, so it cannot depend on +// whether anything happened. +func TestHistogramIsEmptyUntilSomethingIsObserved(t *testing.T) { + var h histogram + got := h.cumulative() + if len(got) != DurationBucketCount { + t.Fatalf("ladder length = %d, want %d", len(got), DurationBucketCount) + } + for i, v := range got { + if v != 0 { + t.Fatalf("counter %d = %d, want 0", i, v) + } + } +} diff --git a/internal/networkmonitor/redmetrics/http1.go b/internal/networkmonitor/redmetrics/http1.go new file mode 100644 index 00000000..4f3595a2 --- /dev/null +++ b/internal/networkmonitor/redmetrics/http1.go @@ -0,0 +1,219 @@ +package redmetrics + +import ( + "bufio" + "bytes" + "net/http" + "sync" +) + +// headerTerminator ends an HTTP/1.1 header block. Finding it is what tells the +// decoder that the whole block was copied: the standard library's readers would +// otherwise block on a truncated block, and a decoder that guessed at the missing +// bytes would emit plausible wrong numbers rather than fail. +var headerTerminator = []byte("\r\n\r\n") + +// bodyLenUnknown is the body length of a message whose framing does not declare +// one: a chunked body, or a response delimited by the connection closing. The +// decoder cannot find the next message boundary past such a body, so the connection +// becomes opaque once the message it belongs to has been accounted for. +const bodyLenUnknown int64 = -1 + +// minStatusCode and maxStatusCode bound the status codes treated as HTTP. The +// standard library accepts any three digits and rejects only a negative one, so +// both a 900 and a 000 parse; a decoder that accepted either would be reading +// something that is not HTTP and reporting it as if it were. +// +// The bottom of the range matters as much as the top, and less obviously: a +// sub-100 code is not merely admitted, it is admitted as INFORMATIONAL, so those +// bytes advance the stream and answer nothing, and the request they were mistaken +// for stays outstanding until the sweep files it as unanswered. +const ( + minStatusCode = 100 + maxStatusCode = 600 +) + +// messageStart is what one message boundary told the decoder. Only one of method +// and status is set, depending on which half of the connection the bytes crossed. +type messageStart struct { + // headerLen is how many bytes the header block occupied, terminator included. It + // is the offset of the body from the start of the message. + headerLen int + + // bodyLen is the declared body length, or bodyLenUnknown. + bodyLen int64 + + // method is the request method. It is carried on the in-flight request because + // the response's framing depends on it: a HEAD response declares a length and + // then sends no body at all. + method string + + // status is the response status code. + status int +} + +// http1Reader is the scratch space one parse needs: the bufio.Reader the standard +// library's message readers insist on, and the bytes.Reader it wraps. +// +// It is pooled because it was not free. A fresh bufio.NewReader is a 4KB allocation, +// and the decoder made one per request and another per response -- 243MB, 22% of +// everything this agent allocated, on a node doing nothing unusual. None of it +// outlives the parse: the header block is decoded into values that are copied out, +// and neither http.ReadRequest nor http.ReadResponse hands back anything still +// pointing at the reader that we go on to read. +type http1Reader struct { + block bytes.Reader + buf *bufio.Reader +} + +var http1ReaderPool = sync.Pool{ + New: func() any { + r := &http1Reader{} + r.buf = bufio.NewReader(&r.block) + return r + }, +} + +// acquireHTTP1Reader returns a reader positioned at the start of block. +func acquireHTTP1Reader(block []byte) *http1Reader { + r, ok := http1ReaderPool.Get().(*http1Reader) + if !ok { + r = &http1Reader{} + r.buf = bufio.NewReader(&r.block) + } + r.block.Reset(block) + // Reset the bufio too: its buffer still holds the tail of whatever it read last, + // and Reset is what discards that rather than serving it to the next caller. + r.buf.Reset(&r.block) + return r +} + +// releaseHTTP1Reader returns a reader to the pool. +// +// It clears the block first. The block is a window into the aggregator's copy of a +// connection, and a reader idling in the pool holding a reference to one would keep +// that copy alive for as long as it sat there -- turning a pool meant to save memory +// into one that pins it. +func releaseHTTP1Reader(r *http1Reader) { + r.block.Reset(nil) + r.buf.Reset(&r.block) + http1ReaderPool.Put(r) +} + +// parseRequestStart reads a request message boundary from the start of b. +// +// The parsing is the standard library's. A bespoke request-line reader would have +// to be right about absolute-form URIs, obsolete line folding and every framing +// rule that decides where the body ends, and being wrong about any of those +// desynchronises the stream silently rather than failing. +func parseRequestStart(b []byte) (messageStart, bool) { + block, ok := headerBlock(b) + if !ok { + return messageStart{}, false + } + + r := acquireHTTP1Reader(block) + defer releaseHTTP1Reader(r) + + req, err := http.ReadRequest(r.buf) + if err != nil { + return messageStart{}, false + } + // An HTTP/2 connection preface parses cleanly as a request called PRI, so the + // version is checked rather than assumed. The sniff sends h2c to its own decoder + // before this is ever reached (see sniffLocked), so the check is a second line + // rather than the only one -- but it is what stops a preface arriving by some + // path the sniff did not see from being counted as one very strange HTTP/1.1 + // request. + if req.ProtoMajor != 1 { + return messageStart{}, false + } + + return messageStart{ + headerLen: len(block), + bodyLen: req.ContentLength, + method: req.Method, + }, true +} + +// parseResponseStart reads a response message boundary from the start of b. +// +// method is the method of the request this answers, empty when the decoder does not +// know it. It is passed through to the standard library because the response's +// framing depends on it, and getting that wrong walks the stream position into the +// middle of the next message. +func parseResponseStart(b []byte, method string) (messageStart, bool) { + block, ok := headerBlock(b) + if !ok { + return messageStart{}, false + } + + var req *http.Request + if method != "" { + req = &http.Request{Method: method} + } + r := acquireHTTP1Reader(block) + defer releaseHTTP1Reader(r) + + resp, err := http.ReadResponse(r.buf, req) + if err != nil { + return messageStart{}, false + } + if resp.ProtoMajor != 1 { + return messageStart{}, false + } + if resp.StatusCode < minStatusCode || resp.StatusCode >= maxStatusCode { + return messageStart{}, false + } + + return messageStart{ + headerLen: len(block), + bodyLen: responseBodyLen(resp), + status: resp.StatusCode, + }, true +} + +// responseBodyLen is how many body bytes follow the header block, or +// bodyLenUnknown. +// +// It is not resp.ContentLength. On a HEAD response the standard library reports the +// declared length even though no body follows, so that a caller can see what the GET +// would have returned; a decoder that skipped those bytes would walk the stream +// position into the middle of the next response. The body reader carries the framing +// decision instead, and it is the same reader the standard library would have used +// to find the next message itself. +func responseBodyLen(resp *http.Response) int64 { + if resp.Body == http.NoBody { + return 0 + } + return resp.ContentLength +} + +// headerBlock is the complete header block at the start of b, terminator included. +// It reports false when b does not contain a whole block, which is what the probe's +// copy limit looks like from here. +func headerBlock(b []byte) ([]byte, bool) { + end := bytes.Index(b, headerTerminator) + if end < 0 { + return nil, false + } + return b[:end+len(headerTerminator)], true +} + +// informational reports whether a status code is a 1XX. An informational response +// is not a completion -- a 100 Continue is followed by the real response on the same +// request -- so it advances the stream and nothing else. +// +// It is only ever asked about a status parseResponseStart already accepted, which is +// what makes the single comparison enough: nothing below minStatusCode gets here. +func informational(status int) bool { return status < 200 } + +// isServerError reports whether a status counts towards error_count. Only 5XX does. +// +// A 4XX is deliberately not an error: it is a statement about the caller, and +// counting it would make the error rate track client behaviour rather than the +// health of the container being measured -- which is the thing these numbers exist +// to put beside a resource recommendation. The status code is read and then +// discarded; the table has no status dimension, and this is the only decision made +// from it. +func isServerError(status int) bool { return status/100 == 5 } diff --git a/internal/networkmonitor/redmetrics/http1_test.go b/internal/networkmonitor/redmetrics/http1_test.go new file mode 100644 index 00000000..ec95c1aa --- /dev/null +++ b/internal/networkmonitor/redmetrics/http1_test.go @@ -0,0 +1,244 @@ +package redmetrics + +import ( + "strings" + "testing" +) + +// These tests are about FRAMING, not about parsing. Whether a header field was read +// correctly barely matters here; where the next message starts matters entirely, +// because a decoder that is one byte out reports plausible wrong latencies for the +// rest of the connection's life rather than failing. + +func TestParseRequestStartFraming(t *testing.T) { + cases := []struct { + name string + raw string + ok bool + bodyLen int64 + method string + }{ + { + name: "content length declares the body", + raw: "POST /x HTTP/1.1\r\nHost: h\r\nContent-Length: 5\r\n\r\nhello", + ok: true, + bodyLen: 5, + method: "POST", + }, + { + name: "no body means the next message starts at the terminator", + raw: "GET /x HTTP/1.1\r\nHost: h\r\n\r\n", + ok: true, + bodyLen: 0, + method: "GET", + }, + { + // Where the body ends can only be learned by reading it, which the decoder + // does not do. The connection goes opaque once this message is accounted for. + name: "chunked has no declared length", + raw: "POST /x HTTP/1.1\r\nHost: h\r\nTransfer-Encoding: chunked\r\n\r\n", + ok: true, + bodyLen: bodyLenUnknown, + method: "POST", + }, + { + // The connection preface of an h2c client. It parses, which is exactly why + // the version has to be checked rather than assumed. + name: "http/2 preface is not an http/1.1 request", + raw: "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n", + ok: false, + }, + { + name: "an incomplete header block is not a boundary", + raw: "GET /x HTTP/1.1\r\nHost: h\r\n", + ok: false, + }, + { + name: "bytes from the middle of somebody's body are not a request", + raw: "\x16\x03\x01\x02\x00\r\n\r\n", + ok: false, + }, + } + + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + msg, ok := parseRequestStart([]byte(tc.raw)) + if ok != tc.ok { + t.Fatalf("ok = %v, want %v", ok, tc.ok) + } + if !tc.ok { + return + } + if msg.bodyLen != tc.bodyLen { + t.Fatalf("bodyLen = %d, want %d", msg.bodyLen, tc.bodyLen) + } + if msg.method != tc.method { + t.Fatalf("method = %q, want %q", msg.method, tc.method) + } + // The header length is the offset of the body, so it counts the blank line + // that ends the block. Reporting it one terminator short would leave the + // decoder parsing "\r\n\r\n" as the next request line. + if want := strings.Index(tc.raw, "\r\n\r\n") + 4; msg.headerLen != want { + t.Fatalf("headerLen = %d, want %d", msg.headerLen, want) + } + }) + } +} + +func TestParseResponseStartFraming(t *testing.T) { + cases := []struct { + name string + method string + raw string + ok bool + bodyLen int64 + status int + }{ + { + name: "content length declares the body", + raw: "HTTP/1.1 200 OK\r\nContent-Length: 2\r\n\r\nhi", + ok: true, + bodyLen: 2, + status: 200, + }, + { + // The declared length describes the body a GET would have returned. Skipping + // it would land the decoder inside the next response. + name: "a head response declares a length and sends nothing", + method: "HEAD", + raw: "HTTP/1.1 200 OK\r\nContent-Length: 4096\r\n\r\n", + ok: true, + bodyLen: 0, + status: 200, + }, + { + name: "204 has no body whatever it declares", + raw: "HTTP/1.1 204 No Content\r\n\r\n", + ok: true, + bodyLen: 0, + status: 204, + }, + { + name: "304 has no body whatever it declares", + raw: "HTTP/1.1 304 Not Modified\r\nContent-Length: 100\r\n\r\n", + ok: true, + bodyLen: 0, + status: 304, + }, + { + name: "chunked has no declared length", + raw: "HTTP/1.1 200 OK\r\nTransfer-Encoding: chunked\r\n\r\n", + ok: true, + bodyLen: bodyLenUnknown, + status: 200, + }, + { + // Delimited by the connection closing: there is no next message to find, so + // the length is unknown in the same way chunked is. + name: "a response with no framing at all runs to close", + raw: "HTTP/1.1 200 OK\r\nServer: x\r\n\r\n", + ok: true, + bodyLen: bodyLenUnknown, + status: 200, + }, + { + name: "informational responses parse and answer nothing", + raw: "HTTP/1.1 100 Continue\r\n\r\n", + ok: true, + bodyLen: 0, + status: 100, + }, + { + // The standard library accepts any three digits. A 900 is not HTTP being + // unusual, it is bytes that are not HTTP parsing by coincidence. + name: "a status above the real range is not a response", + raw: "HTTP/1.1 900 Nonsense\r\n\r\n", + ok: false, + }, + { + // The bottom of the range is the more dangerous half. The standard library + // rejects only a negative code, so a 000 parses -- and a status below 100 + // reads as informational, which would advance the stream past these bytes + // and leave the request they answered nothing of outstanding. + name: "a status below the real range is not a response", + raw: "HTTP/1.1 000 Nonsense\r\n\r\n", + ok: false, + }, + { + name: "a two-digit status padded to three is not a response", + raw: "HTTP/1.1 099 Nonsense\r\n\r\n", + ok: false, + }, + { + name: "an incomplete header block is not a boundary", + raw: "HTTP/1.1 200 OK\r\nContent-Length: 2\r\n", + ok: false, + }, + } + + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + msg, ok := parseResponseStart([]byte(tc.raw), tc.method) + if ok != tc.ok { + t.Fatalf("ok = %v, want %v", ok, tc.ok) + } + if !tc.ok { + return + } + if msg.bodyLen != tc.bodyLen { + t.Fatalf("bodyLen = %d, want %d", msg.bodyLen, tc.bodyLen) + } + if msg.status != tc.status { + t.Fatalf("status = %d, want %d", msg.status, tc.status) + } + if want := strings.Index(tc.raw, "\r\n\r\n") + 4; msg.headerLen != want { + t.Fatalf("headerLen = %d, want %d", msg.headerLen, want) + } + }) + } +} + +// TestParseResponseStartWithoutAKnownMethod pins what happens when the decoder is +// holding no request to attribute the response to. It still has to parse -- the +// alternative is treating every such response as a lost stream -- and it falls back +// to the framing a GET would have. +func TestParseResponseStartWithoutAKnownMethod(t *testing.T) { + msg, ok := parseResponseStart([]byte("HTTP/1.1 200 OK\r\nContent-Length: 7\r\n\r\npayload"), "") + if !ok { + t.Fatal("a response with no known request should still parse") + } + if msg.bodyLen != 7 { + t.Fatalf("bodyLen = %d, want 7", msg.bodyLen) + } +} + +// TestIsServerError pins the boundary of error_count. A 4XX is the caller being +// wrong, and counting it would make the error rate track client behaviour rather +// than the health of the container the number sits beside. +func TestIsServerError(t *testing.T) { + for _, status := range []int{200, 204, 301, 400, 404, 429, 499} { + if isServerError(status) { + t.Fatalf("status %d counted as a server error", status) + } + } + for _, status := range []int{500, 502, 503, 599} { + if !isServerError(status) { + t.Fatalf("status %d not counted as a server error", status) + } + } +} + +// TestInformational pins where a response stops being provisional. 199 is still the +// server saying "not yet"; 200 is an answer. +func TestInformational(t *testing.T) { + for _, status := range []int{100, 101, 103, 199} { + if !informational(status) { + t.Fatalf("status %d should be informational", status) + } + } + for _, status := range []int{200, 404, 500} { + if informational(status) { + t.Fatalf("status %d should not be informational", status) + } + } +} diff --git a/internal/networkmonitor/redmetrics/http2.go b/internal/networkmonitor/redmetrics/http2.go new file mode 100644 index 00000000..80909b85 --- /dev/null +++ b/internal/networkmonitor/redmetrics/http2.go @@ -0,0 +1,561 @@ +package redmetrics + +import ( + "encoding/binary" + "strconv" + "time" + + "golang.org/x/net/http2/hpack" +) + +// http2Preface is the 24 bytes an h2c client sends before its first frame. It is +// the only unambiguous marker HTTP/2 has: everything after it is binary framing +// that looks like nothing in particular, so a connection the agent joined after +// the preface cannot be told from noise and is not decoded at all. +var http2Preface = []byte("PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n") + +// frameHeaderLen is the fixed size of an HTTP/2 frame header: a 24-bit length, a +// type, a set of flags, and a 31-bit stream id behind a reserved bit. +const frameHeaderLen = 9 + +// Frame types. Only the ones whose presence changes what is measured are named; +// everything else is stepped over by its declared length, which is the whole +// reason HTTP/2 is cheaper to walk than HTTP/1.1 -- every frame says how long it +// is, so there is no body whose end has to be inferred. +const ( + frameData uint8 = 0x0 + frameHeaders uint8 = 0x1 + frameRSTStream uint8 = 0x3 + frameSettings uint8 = 0x4 + framePushPromise uint8 = 0x5 + frameContinuation uint8 = 0x9 +) + +// Frame flags. END_STREAM and END_HEADERS share their bit positions with flags +// that mean something else on other frame types -- 0x1 is ACK on SETTINGS and on +// PING -- so each is only ever read off a frame type it is defined for. +const ( + flagEndStream uint8 = 0x1 + flagEndHeaders uint8 = 0x4 + flagPadded uint8 = 0x8 + flagPriority uint8 = 0x20 +) + +// settingHeaderTableSize is the SETTINGS parameter that raises the HPACK dynamic +// table ceiling. It is read because the decoder rejects a table size update above +// what it was told to allow, and a peer that negotiated a larger table will send +// one. +const settingHeaderTableSize uint16 = 0x1 + +// defaultHeaderTableSize is the HPACK dynamic table size every connection starts +// at, from RFC 7541. A peer may raise it in SETTINGS. +const defaultHeaderTableSize = 4096 + +// http2Half is the HPACK and header-block state of one direction. +// +// There is one per direction because HPACK is stateful and the two directions +// have SEPARATE dynamic tables: a request header block and a response header +// block are encoded against different histories, and feeding both to one decoder +// desynchronises it into decoding subsequent blocks as garbage -- or, worse, as +// plausible wrong headers. +type http2Half struct { + // dec holds this direction's dynamic table. Every header block that crosses + // this direction must be fed to it, in order: a block that is skipped is a + // block whose table updates were skipped, and everything after it decodes + // against a table the peer is not using. + dec *hpack.Decoder + + // block accumulates a header block that HTTP/2 split across a HEADERS frame + // and its CONTINUATIONs. HPACK cannot decode a fragment, so nothing is read + // until END_HEADERS arrives. + block []byte + + // blockOpen says a header block is still accumulating. While it is set, the + // only legal frame on this direction is a CONTINUATION on the same stream. + blockOpen bool + + // blockStream and blockEndStream carry the HEADERS frame's stream and its + // END_STREAM flag forward across the CONTINUATIONs, which have neither: a + // CONTINUATION cannot say the stream ended, so the frame that opened the block + // is the only place that is known. + blockStream uint32 + blockEndStream bool +} + +// http2Conn is the per-connection HTTP/2 state, allocated when a connection is +// recognised as HTTP/2 and never for an HTTP/1.1 one. +type http2Conn struct { + request, response http2Half + + // maxStarted is the highest stream id a request-half header block has opened. + // HTTP/2 requires every new client-initiated stream to use an id greater than + // all the client has opened before (RFC 9113 5.1.1), so this is what tells a + // new request apart from a second block on a stream already accounted for. + maxStarted uint32 +} + +func newHTTP2Conn() *http2Conn { + return &http2Conn{ + request: http2Half{dec: hpack.NewDecoder(defaultHeaderTableSize, nil)}, + response: http2Half{dec: hpack.NewDecoder(defaultHeaderTableSize, nil)}, + } +} + +// h2half is the HTTP/2 state of the half an event's bytes crossed on. +func (c *connection) h2half(which role) *http2Half { + if which == roleRequest { + return &c.h2.request + } + return &c.h2.response +} + +// oppositeHalf is the other direction. A SETTINGS frame describes what its SENDER +// will accept, so it constrains the decoder reading the other direction. +func oppositeHalf(which role) role { + if which == roleRequest { + return roleResponse + } + return roleRequest +} + +// sniffLocked decides, from the first bytes to arrive on a connection, which +// decoder reads the rest of it. +// +// The h2c preface is the only thing that has to be recognised, because everything +// that is not it is either HTTP/1.1 -- which announces itself with a request line +// the HTTP/1.1 walk already parses -- or something this agent does not read. A +// connection the agent joined midstream lands here too: mid-connection HTTP/2 +// bytes are frames, and a frame is not a preface, so such a connection becomes +// opaque rather than being decoded from an unknown position. +func (a *Aggregator) sniffLocked(c *connection, ev SocketEvent, payload []byte) { + if ev.TrueLen == 0 { + // A zero-length read is evidence of nothing. There are no first bytes to + // fail to recognise, so the connection is still new and its real first + // message is still to come. + return + } + + hs := c.stream(roleRequest) + buf, trueLen := joinCarry(hs, payload, uint64(ev.TrueLen)) + + switch { + case uint64(len(buf)) >= uint64(len(http2Preface)) && + string(buf[:len(http2Preface)]) == string(http2Preface): + a.releaseLocked(&hs.carry) + c.state = stateHTTP2 + c.h2 = newHTTP2Conn() + // The preface is not a frame and is consumed here; the walk starts at the + // first frame header after it. + a.walkHTTP2Locked(c, roleRequest, ev, buf[len(http2Preface):], trueLen-uint64(len(http2Preface))) + + case trueLen < uint64(len(http2Preface)) && + uint64(len(buf)) == trueLen && + string(http2Preface[:len(buf)]) == string(buf): + // Every byte so far matches the preface and the preface is not finished. The + // bytes are held rather than decided on: a client that wrote its preface in + // two pieces is an h2c client, and calling it HTTP/1.1 here would make the + // next read parse frame bytes as a request line. + a.releaseLocked(&hs.carry) + if !a.bufferLocked(&hs.carry, buf) { + a.goOpaqueLocked(c, ev.Timestamp) + } + + default: + a.releaseLocked(&hs.carry) + c.state = stateHTTP1 + a.walkHTTP1Locked(c, roleRequest, ev, buf, trueLen) + } +} + +// decodeHTTP2Locked walks one event as HTTP/2 frames. +// +// Like the HTTP/1.1 walk it advances over the event's TRUE length rather than its +// copied prefix, and for the same reason: a DATA frame far larger than the copy +// limit costs nothing, because its length is declared and its contents are never +// read. What HTTP/2 adds is that a unit the decoder DOES have to read -- a frame +// header, a header block -- can be split across syscalls without anything being +// lost, so those bytes are held over rather than surrendering the connection. +func (a *Aggregator) decodeHTTP2Locked(c *connection, which role, ev SocketEvent, payload []byte) { + hs := c.stream(which) + buf, trueLen := joinCarry(hs, payload, uint64(ev.TrueLen)) + a.walkHTTP2Locked(c, which, ev, buf, trueLen) +} + +// walkHTTP2Locked is the frame loop, over a buffer that may already include bytes +// held over from earlier events. trueLen is how far the stream advances over that +// same buffer, which is longer than the buffer itself exactly when the probe's +// copy limit dropped bytes. +func (a *Aggregator) walkHTTP2Locked(c *connection, which role, ev SocketEvent, buf []byte, trueLen uint64) { + hs := c.stream(which) + + for pos := uint64(0); pos < trueLen; { + if hs.skip > 0 { + pos = hs.consumeSkip(pos, trueLen) + continue + } + + if pos+frameHeaderLen > trueLen { + a.holdLocked(c, hs, ev, buf, pos, trueLen) + return + } + if pos+frameHeaderLen > uint64(len(buf)) { + // The frame header is inside bytes the probe did not copy. Everything + // after it would be read at an offset chosen at random. + a.goOpaqueLocked(c, ev.Timestamp) + return + } + + h := parseFrameHeader(buf[pos:]) + end := pos + frameHeaderLen + uint64(h.length) + + read := framePayloadIsRead(h.kind) + if read { + if end > trueLen { + a.holdLocked(c, hs, ev, buf, pos, trueLen) + return + } + if end > uint64(len(buf)) { + a.goOpaqueLocked(c, ev.Timestamp) + return + } + } + + a.applyFrameLocked(c, which, ev, h, framePayload(buf, pos, end)) + if c.state == stateOpaque { + return + } + + if read { + pos = end + continue + } + + // The payload was never copied, so the walk cannot step over it in this + // buffer. It is owed to the skip counter, which spends it against this read + // and the ones after it, exactly as an HTTP/1.1 body is stepped over. + hs.skip = uint64(h.length) + pos += frameHeaderLen + } + + a.releaseLocked(&hs.carry) +} + +// framePayload is the frame's payload bytes, empty when the walk is stepping over +// them rather than reading them. +func framePayload(buf []byte, pos, end uint64) []byte { + start := pos + frameHeaderLen + if end <= start || end > uint64(len(buf)) { + return nil + } + return buf[start:end] +} + +// framePayloadIsRead reports whether a frame type's payload has to be READ rather +// than counted. +// +// Only three types qualify. HEADERS and CONTINUATION carry the header block, which +// is the whole point of decoding at all; SETTINGS carries the HPACK table ceiling, +// and a decoder that missed a raise would reject the very header blocks it exists +// to read. Everything else -- DATA above all -- is stepped over. +func framePayloadIsRead(kind uint8) bool { + return kind == frameHeaders || kind == frameContinuation || kind == frameSettings +} + +// applyFrameLocked turns one frame into aggregate state. +func (a *Aggregator) applyFrameLocked(c *connection, which role, ev SocketEvent, h frameHeader, payload []byte) { + h2 := c.h2half(which) + + // A header block admits nothing between its HEADERS and its CONTINUATIONs, on + // any stream. A frame arriving here means the decoder has lost the framing or + // the peer is not speaking HTTP/2, and continuing would feed HPACK a block it + // cannot make sense of. + if h2.blockOpen && (h.kind != frameContinuation || h.streamID != h2.blockStream) { + a.goOpaqueLocked(c, ev.Timestamp) + return + } + + // The mirror of the same rule: a CONTINUATION continues a block or it is not a + // CONTINUATION. Accumulating one with no block open would take the stream and + // the END_STREAM flag from a frame that carries neither, so the request it + // eventually filed would be attributed by guesswork. + if h.kind == frameContinuation && !h2.blockOpen { + a.goOpaqueLocked(c, ev.Timestamp) + return + } + + switch h.kind { + case frameHeaders, frameContinuation: + a.accumulateHeadersLocked(c, which, ev, h, payload) + + case frameData: + if which == roleResponse && h.flags&flagEndStream != 0 { + a.completeStreamLocked(c, ev.Timestamp, h.streamID) + } + + case frameRSTStream: + // A cancelled stream is a request that happened and was never answered. It + // is deliberately not an error: a client hanging up, or a deadline the + // caller set, says nothing about the health of the container serving it -- + // the same reasoning that keeps 4XX out of error_count. + a.abandonStreamLocked(c, ev.Timestamp, h.streamID) + + case frameSettings: + a.applySettingsLocked(c, which, payload) + + case framePushPromise: + // A pushed stream carries a header block that HPACK must see to stay in + // step, and creates a response with no request to pair it with. Server push + // is effectively extinct -- browsers removed it and gRPC never used it -- so + // the connection is surrendered rather than carrying code for it. + a.goOpaqueLocked(c, ev.Timestamp) + } +} + +// accumulateHeadersLocked appends one header-block fragment and decodes the block +// once it is complete. +func (a *Aggregator) accumulateHeadersLocked(c *connection, which role, ev SocketEvent, h frameHeader, payload []byte) { + h2 := c.h2half(which) + + fragment, ok := headerFragment(h, payload) + if !ok { + a.goOpaqueLocked(c, ev.Timestamp) + return + } + + if !h2.blockOpen { + h2.blockOpen = true + h2.blockStream = h.streamID + h2.blockEndStream = h.flags&flagEndStream != 0 + } + if !a.bufferLocked(&h2.block, fragment) { + a.goOpaqueLocked(c, ev.Timestamp) + return + } + + if h.flags&flagEndHeaders == 0 { + return + } + + fields, err := h2.dec.DecodeFull(h2.block) + if err != nil { + // The dynamic table is no longer the peer's. Nothing later on this direction + // can be trusted, and a status read out of a mis-decoded block would be a + // number with no relation to what the server said. + a.goOpaqueLocked(c, ev.Timestamp) + return + } + + stream, endStream := h2.blockStream, h2.blockEndStream + h2.blockOpen = false + h2.blockEndStream = false + a.releaseLocked(&h2.block) + + a.applyHeadersLocked(c, which, ev.Timestamp, stream, endStream, fields) +} + +// applyHeadersLocked is where a decoded header block becomes a measurement. +// +// On the request half a block for an unseen stream starts a request. On the +// response half it carries the outcome: :status on the first block, and grpc-status +// on the trailers that a gRPC server sends after the body. Both are read into the +// outstanding request and neither is stored -- the table has no status dimension, +// and the only thing either decides is whether this request counted as an error. +func (a *Aggregator) applyHeadersLocked(c *connection, which role, at time.Time, stream uint32, endStream bool, fields []hpack.HeaderField) { + if which == roleRequest { + // Request trailers and the END_STREAM that ends a request body say only that + // the caller finished talking, which is not the completion being measured. + // Nothing outstanding for the stream is therefore not enough to call this a + // new request: on a stream whose response has already been filed it would + // start a pending that never completes, is swept as unanswered, and inflates + // request_count with no error anywhere to show for it. The id is what + // separates the two cases. + if stream > c.h2.maxStarted { + c.h2.maxStarted = stream + a.startStreamLocked(c, at, stream) + } + return + } + + i := a.pendingIndexForStream(c, stream) + if i < 0 { + // A response for a stream with nothing outstanding: the agent joined between + // the request and its answer, or the request was already swept. Inventing a + // start would put a duration in the histogram that no request produced. + return + } + + for _, f := range fields { + switch f.Name { + case ":status": + if status, err := strconv.Atoi(f.Value); err == nil { + c.inflight[i].status = status + } + case "grpc-status": + // Every gRPC call answers 200 and reports its real outcome here, so a + // gRPC server with no reachable dependency looks perfectly healthy to + // anything reading :status alone. + if code, err := strconv.Atoi(f.Value); err == nil && code != 0 { + c.inflight[i].grpcError = true + } + } + } + + if endStream { + a.completeStreamLocked(c, at, stream) + } +} + +// applySettingsLocked raises this connection's HPACK ceiling when a peer asks for +// one. +// +// A SETTINGS frame states what its SENDER is willing to receive, so a raise from +// the client governs the encoder the SERVER uses -- which is the decoder reading +// the response half. Applying it to the wrong direction is invisible until a peer +// actually uses the larger table, at which point every header block on that +// direction fails to decode. +func (a *Aggregator) applySettingsLocked(c *connection, which role, payload []byte) { + size, ok := headerTableSize(payload) + if !ok { + return + } + c.h2half(oppositeHalf(which)).dec.SetAllowedMaxDynamicTableSize(size) +} + +// startStreamLocked begins tracking one HTTP/2 request. +// +// The cap behaves exactly as it does on HTTP/1.1, and for a reason that survives +// the change of protocol: a request that could not be tracked would leave its +// response with nothing to pair against, and on a multiplexed connection that +// response is not merely unmatched -- it is a stream id the decoder has no record +// of, which is indistinguishable from a stream it has already finished. +func (a *Aggregator) startStreamLocked(c *connection, at time.Time, stream uint32) { + if a.inFlight >= a.cfg.MaxInFlightRequests { + a.goOpaqueLocked(c, at) + return + } + c.inflight = append(c.inflight, pending{start: at, streamID: stream}) + a.inFlight++ +} + +// completeStreamLocked files one stream as answered, at the moment its response +// ended. +// +// The completion is END_STREAM on the response half, NOT the response headers -- +// which is where HTTP/1.1 stops the clock, and the difference is deliberate. A +// gRPC call reports its outcome in trailers that only arrive when the call is +// over, so headers-time on a gRPC stream would measure how quickly the server +// agreed to answer rather than how long the call took, and would read every failed +// call as a success. docs/features/red-metrics/INVARIANTS.md argues both halves. +func (a *Aggregator) completeStreamLocked(c *connection, at time.Time, stream uint32) { + i := a.pendingIndexForStream(c, stream) + if i < 0 { + return + } + req := a.popPendingAtLocked(c, i) + + durationUs := at.Sub(req.start).Microseconds() + if durationUs < 0 { + // Two syscall timestamps can straddle a clock adjustment. A negative + // duration is not a measurement. + durationUs = 0 + } + + a.fileAnsweredLocked(c, at, durationUs, isServerError(req.status) || req.grpcError) +} + +// abandonStreamLocked drops a reset stream and files it as unanswered. +func (a *Aggregator) abandonStreamLocked(c *connection, at time.Time, stream uint32) { + i := a.pendingIndexForStream(c, stream) + if i < 0 { + return + } + a.popPendingAtLocked(c, i) + a.fileUnansweredLocked(c, at) +} + +// pendingIndexForStream finds the outstanding request on a stream, or -1. +// +// The scan is linear over the connection's outstanding requests, which is bounded +// by the peer's concurrent-stream limit and in practice is a handful. A map per +// connection would cost an allocation on every connection to save a walk over a +// list that is almost always shorter than one cache line's worth of entries. +func (a *Aggregator) pendingIndexForStream(c *connection, stream uint32) int { + for i := range c.inflight { + if c.inflight[i].streamID == stream { + return i + } + } + return -1 +} + +// frameHeader is a decoded 9-byte frame header. +type frameHeader struct { + length uint32 + kind uint8 + flags uint8 + streamID uint32 +} + +// parseFrameHeader reads a frame header from the start of b, which the caller has +// already established holds at least frameHeaderLen bytes. +func parseFrameHeader(b []byte) frameHeader { + return frameHeader{ + length: uint32(b[0])<<16 | uint32(b[1])<<8 | uint32(b[2]), + kind: b[3], + flags: b[4], + // The top bit is reserved and senders are not required to clear it, so it is + // masked off rather than assumed zero. + streamID: binary.BigEndian.Uint32(b[5:9]) & 0x7fffffff, + } +} + +// headerFragment is the HPACK bytes inside a HEADERS or CONTINUATION payload. +// +// A HEADERS frame may carry padding and a priority block, neither of which is +// HPACK; feeding them to the decoder corrupts the dynamic table for the rest of +// the connection. A CONTINUATION carries neither and is returned whole. +func headerFragment(h frameHeader, payload []byte) ([]byte, bool) { + if h.kind == frameContinuation { + return payload, true + } + + if h.flags&flagPadded != 0 { + if len(payload) < 1 { + return nil, false + } + pad := int(payload[0]) + payload = payload[1:] + if len(payload) < pad { + return nil, false + } + payload = payload[:len(payload)-pad] + } + if h.flags&flagPriority != 0 { + if len(payload) < 5 { + return nil, false + } + payload = payload[5:] + } + return payload, true +} + +// headerTableSize reads SETTINGS_HEADER_TABLE_SIZE out of a SETTINGS payload, +// reporting false when the frame does not carry one. A settings payload is a flat +// run of six-byte entries; a trailing partial entry is malformed and is ignored +// rather than read. +func headerTableSize(payload []byte) (uint32, bool) { + // A SETTINGS frame may carry the same parameter more than once, and the final + // value is the one that takes effect (RFC 9113 6.5.3). Returning the first + // would leave the decoder sized to a limit the peer has already superseded. + var ( + size uint32 + found bool + ) + for len(payload) >= 6 { + if binary.BigEndian.Uint16(payload[:2]) == settingHeaderTableSize { + size, found = binary.BigEndian.Uint32(payload[2:6]), true + } + payload = payload[6:] + } + return size, found +} diff --git a/internal/networkmonitor/redmetrics/http2_test.go b/internal/networkmonitor/redmetrics/http2_test.go new file mode 100644 index 00000000..0ca49574 --- /dev/null +++ b/internal/networkmonitor/redmetrics/http2_test.go @@ -0,0 +1,877 @@ +package redmetrics + +import ( + "bytes" + "encoding/binary" + "testing" + "time" + + "github.com/go-logr/logr" + "golang.org/x/net/http2/hpack" +) + +// The tests in this file drive CONNECTIONS carrying several streams at once, which +// is the thing HTTP/1.1 cannot do and therefore the thing none of the HTTP/1.1 tests +// can catch. A decoder that tracked one outstanding request per connection, or that +// paired responses positionally the way HTTP/1.1 requires, passes every sequential +// test and reports nonsense the moment a real gRPC client opens its second stream. + +// h2encoder is one direction's HPACK encoder. There are two per test connection for +// the same reason there are two decoders in the aggregator: an HPACK dynamic table +// is per-direction state, and an encoder shared between the halves would produce +// blocks that no correct decoder could read. +type h2encoder struct { + block bytes.Buffer + enc *hpack.Encoder +} + +func newH2Encoder() *h2encoder { + e := &h2encoder{} + e.enc = hpack.NewEncoder(&e.block) + return e +} + +// encode returns one header block. The bytes are copied out because the encoder +// reuses its buffer, and because the caller holds them until it has framed them. +func (e *h2encoder) encode(t *testing.T, fields ...hpack.HeaderField) []byte { + t.Helper() + e.block.Reset() + for _, f := range fields { + if err := e.enc.WriteField(f); err != nil { + t.Fatalf("encoding %s: %v", f.Name, err) + } + } + return bytes.Clone(e.block.Bytes()) +} + +func hf(name, value string) hpack.HeaderField { + return hpack.HeaderField{Name: name, Value: value} +} + +// frame builds one HTTP/2 frame: the 9-byte header the decoder parses by hand, +// followed by the payload. +func frame(kind, flags uint8, streamID uint32, payload []byte) []byte { + b := make([]byte, frameHeaderLen, frameHeaderLen+len(payload)) + b[0] = byte(len(payload) >> 16) + b[1] = byte(len(payload) >> 8) + b[2] = byte(len(payload)) + b[3] = kind + b[4] = flags + binary.BigEndian.PutUint32(b[5:9], streamID) + return append(b, payload...) +} + +func concat(parts ...[]byte) []byte { + var out []byte + for _, p := range parts { + out = append(out, p...) + } + return out +} + +// preface is the 24 bytes that identify an h2c connection, and the only thing that +// does. Every test here starts with it because a connection without it is one the +// decoder refuses to read -- which is itself pinned, in decode_test.go. +func preface() []byte { + return bytes.Clone(http2Preface) +} + +// grpcRequest is the client half of a gRPC call: headers, then the message. The +// request keeps its stream open, which is what a real client does -- END_STREAM +// arrives on the last DATA frame, and the decoder must not be waiting for it before +// it counts the request. +func grpcRequest(t *testing.T, enc *h2encoder, stream uint32, method string) []byte { + t.Helper() + block := enc.encode(t, + hf(":method", "POST"), + hf(":scheme", "http"), + hf(":path", method), + hf(":authority", "shop.svc"), + hf("content-type", "application/grpc"), + ) + return concat( + frame(frameHeaders, flagEndHeaders, stream, block), + frame(frameData, flagEndStream, stream, []byte("\x00\x00\x00\x00\x03abc")), + ) +} + +// grpcResponse is the server half: a 200, a message, and the trailer that carries +// the call's actual outcome. +func grpcResponse(t *testing.T, enc *h2encoder, stream uint32, grpcStatus string) []byte { + t.Helper() + head := enc.encode(t, + hf(":status", "200"), + hf("content-type", "application/grpc"), + ) + trailers := enc.encode(t, hf("grpc-status", grpcStatus)) + return concat( + frame(frameHeaders, flagEndHeaders, stream, head), + frame(frameData, 0, stream, []byte("\x00\x00\x00\x00\x02ok")), + frame(frameHeaders, flagEndHeaders|flagEndStream, stream, trailers), + ) +} + +// TestHTTP2MeasuresConcurrentStreamsOnOneConnection is the central test of this +// slice, and the one HTTP/1.1 has no equivalent of. +// +// Three streams are open at once on a single connection and they finish in an order +// that has nothing to do with the order they started in. The durations are far +// apart on purpose: a decoder pairing responses positionally -- correct on HTTP/1.1, +// catastrophic here -- would produce three durations that still sum to the same +// total, so the sum cannot catch it. The maximum and the ladder can. +func TestHTTP2MeasuresConcurrentStreamsOnOneConnection(t *testing.T) { + clock := &fakeClock{now: at(t, 0)} + agg := newTestAggregator(clock, testPods()) + client, server := newH2Encoder(), newH2Encoder() + + // All three streams are opened before any of them is answered. + agg.Observe(reqAt(1, at(t, 0), concat(preface(), grpcRequest(t, client, 1, "/Shop/Slow")))) + agg.Observe(reqAt(1, at(t, time.Millisecond), grpcRequest(t, client, 3, "/Shop/Fast"))) + agg.Observe(reqAt(1, at(t, 2*time.Millisecond), grpcRequest(t, client, 5, "/Shop/Middling"))) + + // Answered inside out: stream 3 took 2ms, stream 5 took 40ms, stream 1 took 300ms. + agg.Observe(respAt(1, at(t, 3*time.Millisecond), grpcResponse(t, server, 3, "0"))) + agg.Observe(respAt(1, at(t, 42*time.Millisecond), grpcResponse(t, server, 5, "0"))) + agg.Observe(respAt(1, at(t, 300*time.Millisecond), grpcResponse(t, server, 1, "0"))) + + item := flushOne(t, agg, clock, at(t, time.Minute+5*time.Second)) + + if item.RequestCount != 3 { + t.Fatalf("request_count = %d, want 3", item.RequestCount) + } + if item.ErrorCount != 0 { + t.Fatalf("error_count = %d, want 0: every call answered grpc-status 0", item.ErrorCount) + } + if want := uint64((300 * time.Millisecond).Microseconds()); item.DurationMaxUs != want { + t.Fatalf("duration_max_us = %d, want %d: the slowest stream is the one opened first", item.DurationMaxUs, want) + } + if want := uint64((342 * time.Millisecond).Microseconds()); item.DurationSumUs != want { + t.Fatalf("duration_sum_us = %d, want %d", item.DurationSumUs, want) + } + // 2ms at bound 2, 40ms at bound 6 (50ms), 300ms at bound 9 (500ms). + wantBuckets(t, item.DurationBuckets, []uint64{0, 0, 1, 1, 1, 1, 2, 2, 2, 3, 3, 3, 3, 3}) + if item.ConnCount != 0 { + t.Fatalf("conn_count = %d, want 0: the connection decoded cleanly", item.ConnCount) + } +} + +// TestHTTP2CountsANonZeroGrpcStatusAsAnError pins the reason this decoder reads +// trailers at all. +// +// Every gRPC call answers :status 200, including the ones that failed. A decoder +// reading only the status line reports a service with a dead dependency as perfectly +// healthy -- error_count zero, latency fine -- which is worse than reporting nothing, +// because it looks like evidence. +func TestHTTP2CountsANonZeroGrpcStatusAsAnError(t *testing.T) { + cases := []struct { + name string + status string + wantError uint64 + }{ + {"ok", "0", 0}, + {"internal", "13", 1}, + {"unavailable", "14", 1}, + // NOT FOUND is an answer, not a failure of the server -- but gRPC does not + // separate the two the way HTTP does, so it counts. The alternative is a + // list of codes this decoder decides are acceptable, which is a policy and + // does not belong in a probe. + {"not found", "5", 1}, + } + + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + clock := &fakeClock{now: at(t, 0)} + agg := newTestAggregator(clock, testPods()) + client, server := newH2Encoder(), newH2Encoder() + + agg.Observe(reqAt(1, at(t, 0), concat(preface(), grpcRequest(t, client, 1, "/Shop/Get")))) + agg.Observe(respAt(1, at(t, 5*time.Millisecond), grpcResponse(t, server, 1, tc.status))) + + item := flushOne(t, agg, clock, at(t, time.Minute+5*time.Second)) + + if item.RequestCount != 1 { + t.Fatalf("request_count = %d, want 1", item.RequestCount) + } + if item.ErrorCount != tc.wantError { + t.Fatalf("error_count = %d, want %d for grpc-status %s", item.ErrorCount, tc.wantError, tc.status) + } + // Whatever the outcome, the call took 5ms and belongs on the ladder. A + // failed request is still a measured one. + if want := uint64((5 * time.Millisecond).Microseconds()); item.DurationSumUs != want { + t.Fatalf("duration_sum_us = %d, want %d", item.DurationSumUs, want) + } + }) + } +} + +// TestHTTP2ReadsAGrpcStatusSentWithoutAMessage pins the Trailers-Only response: a +// gRPC server rejecting a call before it runs sends ONE frame carrying both the +// :status and the grpc-status, ends the stream on it, and sends no body at all. +// +// It is the shape a failing service produces most often -- an unauthenticated call, +// an unknown method, a dead upstream -- so a decoder that only looked for +// grpc-status in a second header block would miss errors precisely when they matter. +func TestHTTP2ReadsAGrpcStatusSentWithoutAMessage(t *testing.T) { + clock := &fakeClock{now: at(t, 0)} + agg := newTestAggregator(clock, testPods()) + client, server := newH2Encoder(), newH2Encoder() + + agg.Observe(reqAt(1, at(t, 0), concat(preface(), grpcRequest(t, client, 1, "/Shop/Get")))) + + trailersOnly := server.encode(t, + hf(":status", "200"), + hf("content-type", "application/grpc"), + hf("grpc-status", "16"), + hf("grpc-message", "unauthenticated"), + ) + agg.Observe(respAt(1, at(t, time.Millisecond), + frame(frameHeaders, flagEndHeaders|flagEndStream, 1, trailersOnly))) + + item := flushOne(t, agg, clock, at(t, time.Minute+5*time.Second)) + + if item.RequestCount != 1 { + t.Fatalf("request_count = %d, want 1", item.RequestCount) + } + if item.ErrorCount != 1 { + t.Fatalf("error_count = %d, want 1: a 200 carrying grpc-status 16 is a failed call", item.ErrorCount) + } +} + +// TestHTTP2CountsAServerErrorStatus pins that HTTP/2 has not stopped being HTTP. A +// plain h2c service answering 503 is an error for the same reason it is on +// HTTP/1.1, and nothing about the gRPC trailer rule may displace that. +func TestHTTP2CountsAServerErrorStatus(t *testing.T) { + clock := &fakeClock{now: at(t, 0)} + agg := newTestAggregator(clock, testPods()) + client, server := newH2Encoder(), newH2Encoder() + + agg.Observe(reqAt(1, at(t, 0), concat(preface(), grpcRequest(t, client, 1, "/health")))) + agg.Observe(respAt(1, at(t, time.Millisecond), + frame(frameHeaders, flagEndHeaders|flagEndStream, 1, server.encode(t, hf(":status", "503"))))) + + item := flushOne(t, agg, clock, at(t, time.Minute+5*time.Second)) + + if item.RequestCount != 1 || item.ErrorCount != 1 { + t.Fatalf("request_count = %d, error_count = %d, want 1 and 1", item.RequestCount, item.ErrorCount) + } +} + +// TestHTTP2DecodesAHeaderBlockSplitAcrossReads pins the carry buffer. +// +// A syscall boundary is not a protocol boundary, and where the kernel cuts one read +// from the next is not something the decoder gets to choose. The cuts here are +// deliberately hostile: one lands inside the connection preface, the next inside a +// frame HEADER -- three bytes of a length nobody can act on -- and the last inside +// the HPACK block itself. +func TestHTTP2DecodesAHeaderBlockSplitAcrossReads(t *testing.T) { + clock := &fakeClock{now: at(t, 0)} + agg := newTestAggregator(clock, testPods()) + client, server := newH2Encoder(), newH2Encoder() + + stream := concat(preface(), grpcRequest(t, client, 1, "/Shop/Get")) + cuts := []int{10, 26, 31} + + prev := 0 + for i, cut := range append(cuts, len(stream)) { + if cut > len(stream) { + t.Fatalf("cut %d is past the end of a %d-byte stream", cut, len(stream)) + } + agg.Observe(reqAt(1, at(t, time.Duration(i)*time.Microsecond), stream[prev:cut])) + prev = cut + } + + agg.Observe(respAt(1, at(t, 8*time.Millisecond), grpcResponse(t, server, 1, "0"))) + + item := flushOne(t, agg, clock, at(t, time.Minute+5*time.Second)) + + if item.ConnCount != 0 { + t.Fatalf("conn_count = %d, want 0: a split read is not a decode failure", item.ConnCount) + } + if item.RequestCount != 1 { + t.Fatalf("request_count = %d, want 1", item.RequestCount) + } + // The request starts at the read that COMPLETED its header block -- the fourth, + // at 3us -- not at the first byte to arrive. The decoder cannot know a request + // exists until it has read the headers that say so, and dating it from the read + // that first held bytes would credit the service with time it had not yet been + // asked to do anything in. + if want := uint64((8*time.Millisecond - 3*time.Microsecond).Microseconds()); item.DurationSumUs != want { + t.Fatalf("duration_sum_us = %d, want %d", item.DurationSumUs, want) + } +} + +// TestHTTP2DecodesAHeaderBlockSplitAcrossContinuationFrames pins the other way a +// header block arrives in pieces: not cut by the kernel, but deliberately framed +// that way by the sender because the block exceeded the peer's maximum frame size. +// +// CONTINUATION is where an HPACK decoder is easiest to get wrong, because the pieces +// are only decodable joined: each is a fragment of one compressed stream, so +// decoding them separately does not produce partial headers, it produces an error +// and a dynamic table that has diverged from the sender's for the rest of the +// connection. +func TestHTTP2DecodesAHeaderBlockSplitAcrossContinuationFrames(t *testing.T) { + clock := &fakeClock{now: at(t, 0)} + agg := newTestAggregator(clock, testPods()) + client, server := newH2Encoder(), newH2Encoder() + + block := client.encode(t, + hf(":method", "POST"), + hf(":scheme", "http"), + hf(":path", "/Shop/Get"), + hf(":authority", "shop.svc"), + hf("content-type", "application/grpc"), + ) + if len(block) < 6 { + t.Fatalf("block is %d bytes, too short to split meaningfully", len(block)) + } + a, b, c := block[:2], block[2:5], block[5:] + + agg.Observe(reqAt(1, at(t, 0), concat( + preface(), + frame(frameHeaders, 0, 1, a), + frame(frameContinuation, 0, 1, b), + frame(frameContinuation, flagEndHeaders, 1, c), + ))) + agg.Observe(respAt(1, at(t, 3*time.Millisecond), grpcResponse(t, server, 1, "0"))) + + item := flushOne(t, agg, clock, at(t, time.Minute+5*time.Second)) + + if item.ConnCount != 0 { + t.Fatalf("conn_count = %d, want 0", item.ConnCount) + } + if item.RequestCount != 1 { + t.Fatalf("request_count = %d, want 1", item.RequestCount) + } +} + +// TestHTTP2CountsACancelledStreamAsUnanswered pins RST_STREAM. +// +// A cancelled stream is a request the service was asked to do and never finished -- +// a client that hung up, a deadline that expired. It counts as a request because it +// happened, it carries no duration because it never completed, and it is NOT an +// error, for the same reason a 404 is not: the failure is the caller's, and folding +// it into error_count would make a service look unhealthy in proportion to how +// impatient its clients are. +func TestHTTP2CountsACancelledStreamAsUnanswered(t *testing.T) { + clock := &fakeClock{now: at(t, 0)} + agg := newTestAggregator(clock, testPods()) + client := newH2Encoder() + + agg.Observe(reqAt(1, at(t, 0), concat(preface(), grpcRequest(t, client, 1, "/Shop/Watch")))) + agg.Observe(reqAt(1, at(t, 2*time.Millisecond), + frame(frameRSTStream, 0, 1, []byte{0x00, 0x00, 0x00, 0x08}))) + + item := flushOne(t, agg, clock, at(t, time.Minute+5*time.Second)) + + if item.RequestCount != 1 { + t.Fatalf("request_count = %d, want 1: a cancelled request still happened", item.RequestCount) + } + if item.ErrorCount != 0 { + t.Fatalf("error_count = %d, want 0: the client cancelled, the server did not fail", item.ErrorCount) + } + if item.DurationSumUs != 0 { + t.Fatalf("duration_sum_us = %d, want 0: a cancelled stream has no completion to measure", item.DurationSumUs) + } + wantBuckets(t, item.DurationBuckets, make([]uint64, DurationBucketCount)) +} + +// TestHTTP2CountsANeverCompletingStreamAsUnanswered pins the server-streaming call +// that is still streaming when the agent stops waiting. +// +// It is the shape that would break a decoder measuring at the response HEADERS the +// way HTTP/1.1 does: the headers arrive immediately and the call runs for an hour. +// Stopping the clock there would report a watch that streamed for an hour as a +// sub-millisecond request, and the latency distribution of any service with a watch +// API would become a distribution of how fast it says hello. +func TestHTTP2CountsANeverCompletingStreamAsUnanswered(t *testing.T) { + clock := &fakeClock{now: at(t, 0)} + agg := newTestAggregator(clock, testPods()) + client, server := newH2Encoder(), newH2Encoder() + + agg.Observe(reqAt(1, at(t, 0), concat(preface(), grpcRequest(t, client, 1, "/Shop/Watch")))) + + // Headers, then messages, and the stream stays open. + agg.Observe(respAt(1, at(t, time.Millisecond), concat( + frame(frameHeaders, flagEndHeaders, 1, server.encode(t, + hf(":status", "200"), hf("content-type", "application/grpc"))), + frame(frameData, 0, 1, []byte("\x00\x00\x00\x00\x02up")), + frame(frameData, 0, 1, []byte("\x00\x00\x00\x00\x02up")), + ))) + + // The default RequestTTL is a minute, so the agent gives up at 10:01:00 and files + // the request in THAT minute -- the minute the measurement became final, not the + // one the call started in. The flush has to be late enough for 10:01 to have + // closed, which is why it is two minutes out rather than one. + item := flushOne(t, agg, clock, at(t, 2*time.Minute+5*time.Second)) + + if item.RequestCount != 1 { + t.Fatalf("request_count = %d, want 1", item.RequestCount) + } + if item.ErrorCount != 0 { + t.Fatalf("error_count = %d, want 0: a slow stream is not a failed one", item.ErrorCount) + } + if item.DurationSumUs != 0 { + t.Fatalf("duration_sum_us = %d, want 0: nothing completed, so nothing was measured", item.DurationSumUs) + } +} + +// TestHTTP2StepsOverADataFrameLargerThanTheCopyLimit pins that a body the probe +// could not copy does not cost the connection. +// +// The frame declares its own length, so the decoder steps over it by count without +// ever looking at the bytes -- which is what lets a service uploading megabytes stay +// measurable on a probe that copies two kilobytes. The second request is the actual +// assertion: it can only be read if the position landed exactly on the next frame +// header. +func TestHTTP2StepsOverADataFrameLargerThanTheCopyLimit(t *testing.T) { + clock := &fakeClock{now: at(t, 0)} + agg := newTestAggregator(clock, testPods()) + client, server := newH2Encoder(), newH2Encoder() + + block := client.encode(t, + hf(":method", "POST"), hf(":scheme", "http"), + hf(":path", "/Shop/Upload"), hf(":authority", "shop.svc"), + ) + big := concat( + preface(), + frame(frameHeaders, flagEndHeaders, 1, block), + frame(frameData, flagEndStream, 1, bytes.Repeat([]byte("x"), 40000)), + ) + agg.Observe(truncate(reqAt(1, at(t, 0), big), 2048)) + + agg.Observe(respAt(1, at(t, 4*time.Millisecond), grpcResponse(t, server, 1, "0"))) + + // A second stream, which only decodes if the first one's payload was stepped + // over by exactly its declared length. + agg.Observe(reqAt(1, at(t, 5*time.Millisecond), grpcRequest(t, client, 3, "/Shop/Get"))) + agg.Observe(respAt(1, at(t, 6*time.Millisecond), grpcResponse(t, server, 3, "0"))) + + item := flushOne(t, agg, clock, at(t, time.Minute+5*time.Second)) + + if item.ConnCount != 0 { + t.Fatalf("conn_count = %d, want 0: an uncopied payload is not a decode failure", item.ConnCount) + } + if item.RequestCount != 2 { + t.Fatalf("request_count = %d, want 2", item.RequestCount) + } +} + +// TestHTTP2AppliesTheHeaderTableSizeFromSettings pins the one SETTINGS value this +// decoder cannot ignore. +// +// SETTINGS_HEADER_TABLE_SIZE announces what its SENDER will accept, so a client +// raising it is licensing the SERVER to compress harder -- and the decoder that has +// to keep up is the one reading the other direction. Miss it and the failure is +// total and delayed: the response header block that first exceeds the default is +// rejected, and every block after it on that direction is rejected too, because the +// dynamic table has diverged. +func TestHTTP2AppliesTheHeaderTableSizeFromSettings(t *testing.T) { + const raised = 8192 + + clock := &fakeClock{now: at(t, 0)} + agg := newTestAggregator(clock, testPods()) + client, server := newH2Encoder(), newH2Encoder() + + settings := make([]byte, 6) + binary.BigEndian.PutUint16(settings[:2], settingHeaderTableSize) + binary.BigEndian.PutUint32(settings[2:], raised) + + agg.Observe(reqAt(1, at(t, 0), concat( + preface(), + frame(frameSettings, 0, 0, settings), + grpcRequest(t, client, 1, "/Shop/Get"), + ))) + + // The server takes the client up on it, which puts a dynamic table size update + // of 8192 at the front of its next header block. + server.enc.SetMaxDynamicTableSizeLimit(raised) + server.enc.SetMaxDynamicTableSize(raised) + agg.Observe(respAt(1, at(t, 2*time.Millisecond), grpcResponse(t, server, 1, "0"))) + + item := flushOne(t, agg, clock, at(t, time.Minute+5*time.Second)) + + if item.ConnCount != 0 { + t.Fatalf("conn_count = %d, want 0: the client licensed the larger table", item.ConnCount) + } + if item.RequestCount != 1 { + t.Fatalf("request_count = %d, want 1", item.RequestCount) + } +} + +// TestHTTP2GoesOpaqueOnUnreadableHpackState pins what happens when the compression +// context is lost. +// +// HPACK is not a per-message encoding: every block mutates a table both sides must +// agree on, so one block the decoder cannot read means it can never read another on +// that direction. The connection is surrendered rather than skipped past, because a +// decoder continuing here would emit statuses read from a table that no longer +// matches the sender's -- plausible values, arbitrarily wrong. +func TestHTTP2GoesOpaqueOnUnreadableHpackState(t *testing.T) { + clock := &fakeClock{now: at(t, 0)} + agg := newTestAggregator(clock, testPods()) + + // 0x80 is an indexed field referring to index 0, which HPACK defines as illegal. + agg.Observe(reqAt(1, at(t, 0), concat( + preface(), + frame(frameHeaders, flagEndHeaders, 1, []byte{0x80}), + ))) + + item := flushOne(t, agg, clock, at(t, time.Minute+5*time.Second)) + + if item.ConnCount != 1 { + t.Fatalf("conn_count = %d, want 1", item.ConnCount) + } + if item.RequestCount != 0 { + t.Fatalf("request_count = %d, want 0: nothing was read, so nothing may be claimed", item.RequestCount) + } +} + +// TestHTTP2GoesOpaqueOnServerPush pins the deliberate gap. PUSH_PROMISE carries a +// header block the HPACK decoder would have to consume to stay in step, and creates +// a response with no request for the pairing to find. Server push is disabled by +// default in every current client and removed from Chrome entirely, so the +// connection is surrendered rather than the code being carried indefinitely for it. +func TestHTTP2GoesOpaqueOnServerPush(t *testing.T) { + clock := &fakeClock{now: at(t, 0)} + agg := newTestAggregator(clock, testPods()) + client := newH2Encoder() + + agg.Observe(reqAt(1, at(t, 0), concat(preface(), grpcRequest(t, client, 1, "/index")))) + agg.Observe(respAt(1, at(t, time.Millisecond), + frame(framePushPromise, flagEndHeaders, 1, []byte{0x00, 0x00, 0x00, 0x02}))) + + item := flushOne(t, agg, clock, at(t, time.Minute+5*time.Second)) + + if item.ConnCount != 1 { + t.Fatalf("conn_count = %d, want 1", item.ConnCount) + } + // The outstanding request is dropped with the rest of the connection's state: + // the decoder is giving up because it no longer trusts what it read. + if item.RequestCount != 0 { + t.Fatalf("request_count = %d, want 0", item.RequestCount) + } +} + +// TestHTTP2GoesOpaqueWhenAFrameIsInterleavedIntoAHeaderBlock pins the rule that +// makes CONTINUATION safe to reassemble: between a HEADERS frame and its END_HEADERS +// nothing else may appear on the connection, not even on another stream. +// +// Without the check the fragments of two different blocks could be concatenated into +// one buffer and handed to HPACK, which would decode SOMETHING from the mixture -- +// header fields belonging to neither request. +func TestHTTP2GoesOpaqueWhenAFrameIsInterleavedIntoAHeaderBlock(t *testing.T) { + clock := &fakeClock{now: at(t, 0)} + agg := newTestAggregator(clock, testPods()) + client := newH2Encoder() + + block := client.encode(t, hf(":method", "POST"), hf(":path", "/Shop/Get")) + + agg.Observe(reqAt(1, at(t, 0), concat( + preface(), + frame(frameHeaders, 0, 1, block[:2]), + frame(frameData, 0, 3, []byte("interloper")), + frame(frameContinuation, flagEndHeaders, 1, block[2:]), + ))) + + item := flushOne(t, agg, clock, at(t, time.Minute+5*time.Second)) + + if item.ConnCount != 1 { + t.Fatalf("conn_count = %d, want 1", item.ConnCount) + } + if item.RequestCount != 0 { + t.Fatalf("request_count = %d, want 0", item.RequestCount) + } +} + +// TestHTTP2DropsAResponseForAStreamItNeverSaw pins the same rule the HTTP/1.1 +// decoder has, arrived at differently. Without a start there is no duration, and +// counting the response as a request would put a number in request_count that no +// observed request produced. It happens whenever the agent starts mid-connection on +// a stream that was already open. +func TestHTTP2DropsAResponseForAStreamItNeverSaw(t *testing.T) { + clock := &fakeClock{now: at(t, 0)} + agg := newTestAggregator(clock, testPods()) + client, server := newH2Encoder(), newH2Encoder() + + agg.Observe(reqAt(1, at(t, 0), concat(preface(), grpcRequest(t, client, 1, "/Shop/Get")))) + // Stream 7 was never opened in front of this decoder. + agg.Observe(respAt(1, at(t, time.Millisecond), grpcResponse(t, server, 7, "13"))) + agg.Observe(respAt(1, at(t, 2*time.Millisecond), grpcResponse(t, server, 1, "0"))) + + item := flushOne(t, agg, clock, at(t, time.Minute+5*time.Second)) + + if item.RequestCount != 1 { + t.Fatalf("request_count = %d, want 1: only stream 1 was observed starting", item.RequestCount) + } + if item.ErrorCount != 0 { + t.Fatalf("error_count = %d, want 0: the failed call was never paired with a request", item.ErrorCount) + } + if item.ConnCount != 0 { + t.Fatalf("conn_count = %d, want 0: an unknown stream is not a lost stream position", item.ConnCount) + } +} + +// TestHTTP2KeepsConnectionsIndependent pins that HPACK state is per connection. Two +// connections encoding their own header blocks share nothing; a decoder holding one +// table for the process would read the second connection's blocks against the +// first's table and produce header fields from the wrong request. +func TestHTTP2KeepsConnectionsIndependent(t *testing.T) { + clock := &fakeClock{now: at(t, 0)} + agg := newTestAggregator(clock, testPods()) + + for cookie := uint64(1); cookie <= 2; cookie++ { + client, server := newH2Encoder(), newH2Encoder() + agg.Observe(reqAt(cookie, at(t, 0), concat(preface(), grpcRequest(t, client, 1, "/Shop/Get")))) + agg.Observe(respAt(cookie, at(t, time.Duration(cookie)*time.Millisecond), + grpcResponse(t, server, 1, "0"))) + } + + item := flushOne(t, agg, clock, at(t, time.Minute+5*time.Second)) + + if item.RequestCount != 2 { + t.Fatalf("request_count = %d, want 2", item.RequestCount) + } + if item.ConnCount != 0 { + t.Fatalf("conn_count = %d, want 0", item.ConnCount) + } + if want := uint64((3 * time.Millisecond).Microseconds()); item.DurationSumUs != want { + t.Fatalf("duration_sum_us = %d, want %d", item.DurationSumUs, want) + } +} + +// TestHTTP2ReleasesHeldBytesWhenAConnectionIsSwept pins that the carry buffer is +// given back. It is accounted globally, so a connection that goes idle holding a +// half-frame -- which a keep-alive connection between requests routinely does -- +// would otherwise consume a slice of the agent's budget permanently, and enough of +// them would stop every other connection on the node from ever holding anything. +func TestHTTP2ReleasesHeldBytesWhenAConnectionIsSwept(t *testing.T) { + clock := &fakeClock{now: at(t, 0)} + agg := newTestAggregator(clock, testPods()) + client := newH2Encoder() + + stream := concat(preface(), grpcRequest(t, client, 1, "/Shop/Get")) + // Stop three bytes into a frame header, which the decoder has to hold. + agg.Observe(reqAt(1, at(t, 0), stream[:len(http2Preface)+3])) + + agg.mu.Lock() + held := agg.buffered + agg.mu.Unlock() + if held == 0 { + t.Fatal("nothing was held over, so this test is not exercising the buffer") + } + + // Past ConnTTL, so the sweep drops the connection. The connection is left to the + // TTL rather than closed, because the buffer has to come back on both paths and + // the sweep is the one that used to leak it. + clock.set(at(t, 15*time.Minute)) + agg.Flush() + + agg.mu.Lock() + defer agg.mu.Unlock() + if agg.buffered != 0 { + t.Fatalf("buffered = %d after the connection was swept, want 0", agg.buffered) + } + if len(agg.conns) != 0 { + t.Fatalf("%d connections still tracked, want 0", len(agg.conns)) + } +} + +// TestHTTP2ReleasesHeldBytesWhenAConnectionCloses is the same accounting on the +// other release path. Every connection now ends at its close rather than at an idle +// timeout, so this is the path that actually runs; the sweep only sees the closes +// that went missing. +func TestHTTP2ReleasesHeldBytesWhenAConnectionCloses(t *testing.T) { + clock := &fakeClock{now: at(t, 0)} + agg := newTestAggregator(clock, testPods()) + client := newH2Encoder() + + stream := concat(preface(), grpcRequest(t, client, 1, "/Shop/Get")) + // Stop three bytes into a frame header, which the decoder has to hold. + agg.Observe(reqAt(1, at(t, 0), stream[:len(http2Preface)+3])) + + agg.mu.Lock() + held := agg.buffered + agg.mu.Unlock() + if held == 0 { + t.Fatal("nothing was held over, so this test is not exercising the buffer") + } + + agg.Observe(closeAt(1, at(t, time.Second))) + + agg.mu.Lock() + defer agg.mu.Unlock() + if agg.buffered != 0 { + t.Fatalf("buffered = %d after the connection closed, want 0", agg.buffered) + } + if agg.inFlight != 0 { + t.Fatalf("in-flight = %d after the connection closed, want 0", agg.inFlight) + } + if len(agg.conns) != 0 { + t.Fatalf("%d connections still tracked, want 0", len(agg.conns)) + } +} + +// TestHTTP2DoesNotCountRequestTrailersAsASecondRequest pins the one thing a stream +// id is for on the request half. A client may keep talking on a stream after the +// server has answered it -- request trailers, or a body it was still sending -- and +// a header block arriving then has nothing outstanding to pair with, exactly like +// the first block of a genuinely new request. Treating the two alike invents a +// request that is never answered, so it is swept as unanswered and lands in +// request_count with no error and no duration: the count is simply wrong, and +// nothing in the output says so. +func TestHTTP2DoesNotCountRequestTrailersAsASecondRequest(t *testing.T) { + clock := &fakeClock{now: at(t, 0)} + agg := newTestAggregator(clock, testPods()) + client, server := newH2Encoder(), newH2Encoder() + + agg.Observe(reqAt(1, at(t, 0), concat(preface(), grpcRequest(t, client, 1, "/Shop/Get")))) + agg.Observe(respAt(1, at(t, 4*time.Millisecond), grpcResponse(t, server, 1, "0"))) + + // The stream is finished and filed. The client now sends trailers on it. + agg.Observe(reqAt(1, at(t, 5*time.Millisecond), + frame(frameHeaders, flagEndHeaders|flagEndStream, 1, + client.encode(t, hf("x-client-trace", "done"))))) + + // Two minutes, not one. A phantom request started by the trailers would not be + // swept until its TTL expired a minute later, landing it in the minute AFTER the + // one the real request was filed under -- so a flush that closed only the first + // minute would report the correct number and hide the bug in a bucket it had not + // looked at yet. Waiting for both minutes to close means the phantom shows up + // either as a second item or not at all. + item := flushOne(t, agg, clock, at(t, 2*time.Minute+5*time.Second)) + + if item.RequestCount != 1 { + t.Fatalf("request_count = %d, want 1 -- the trailers started a phantom request", item.RequestCount) + } + if item.ErrorCount != 0 { + t.Fatalf("error_count = %d, want 0", item.ErrorCount) + } + if item.DurationSumUs != 4000 { + t.Fatalf("duration_sum_us = %d, want 4000", item.DurationSumUs) + } +} + +// TestHTTP2TrimsPaddingAndPriorityFromAHeaderBlock covers the two optional fields a +// HEADERS frame may carry ahead of and behind its fragment. They are rare enough on +// real traffic to go unnoticed and fatal when mishandled: a fragment trimmed by the +// wrong number of bytes does not fail here, it feeds HPACK garbage and desynchronises +// the dynamic table for every block after it on this direction. +func TestHTTP2TrimsPaddingAndPriorityFromAHeaderBlock(t *testing.T) { + clock := &fakeClock{now: at(t, 0)} + agg := newTestAggregator(clock, testPods()) + client, server := newH2Encoder(), newH2Encoder() + + // PADDED and PRIORITY together: one pad-length byte, five priority bytes, the + // fragment, then the padding itself. + block := client.encode(t, + hf(":method", "POST"), hf(":scheme", "http"), hf(":path", "/Shop/Get"), + hf(":authority", "shop.svc"), hf("content-type", "application/grpc")) + padded := concat( + []byte{4}, // pad length + []byte{0x00, 0x00, 0x00, 0x01, 15}, // stream dependency and weight + block, + []byte{0, 0, 0, 0}) // the padding + + agg.Observe(reqAt(1, at(t, 0), concat(preface(), + frame(frameHeaders, flagEndHeaders|flagPadded|flagPriority, 1, padded), + frame(frameData, flagEndStream, 1, []byte("\x00\x00\x00\x00\x03abc"))))) + agg.Observe(respAt(1, at(t, 6*time.Millisecond), grpcResponse(t, server, 1, "0"))) + + // A second request proves the dynamic table survived the trim: its block is + // encoded against the table the first one left behind, so a fragment read at the + // wrong offset would take this one down with it. + agg.Observe(reqAt(1, at(t, 7*time.Millisecond), grpcRequest(t, client, 3, "/Shop/List"))) + agg.Observe(respAt(1, at(t, 9*time.Millisecond), grpcResponse(t, server, 3, "0"))) + + item := flushOne(t, agg, clock, at(t, time.Minute+5*time.Second)) + + if item.RequestCount != 2 || item.ConnCount != 0 { + t.Fatalf("request_count = %d, conn_count = %d, want 2 and 0", item.RequestCount, item.ConnCount) + } + if item.DurationSumUs != 8000 { + t.Fatalf("duration_sum_us = %d, want 8000", item.DurationSumUs) + } +} + +// TestHTTP2TakesTheLastHeaderTableSizeInASettingsFrame pins duplicate handling. A +// SETTINGS frame may carry the same parameter twice and the last value is the one in +// force (RFC 9113 6.5.3), so a decoder sized from the first is sized to a limit the +// peer has already replaced -- and a peer that then uses the larger table produces +// blocks it cannot read. +func TestHTTP2TakesTheLastHeaderTableSizeInASettingsFrame(t *testing.T) { + clock := &fakeClock{now: at(t, 0)} + agg := newTestAggregator(clock, testPods()) + client, server := newH2Encoder(), newH2Encoder() + + // 512 first, then 8192. Only the second is in force. + settings := concat( + []byte{0x00, 0x01, 0x00, 0x00, 0x02, 0x00}, + []byte{0x00, 0x01, 0x00, 0x00, 0x20, 0x00}) + agg.Observe(reqAt(1, at(t, 0), concat(preface(), frame(frameSettings, 0, 0, settings)))) + + server.enc.SetMaxDynamicTableSizeLimit(8192) + server.enc.SetMaxDynamicTableSize(8192) + + agg.Observe(reqAt(1, at(t, time.Millisecond), grpcRequest(t, client, 1, "/Shop/Get"))) + agg.Observe(respAt(1, at(t, 3*time.Millisecond), grpcResponse(t, server, 1, "0"))) + + item := flushOne(t, agg, clock, at(t, time.Minute+5*time.Second)) + + if item.ConnCount != 0 { + t.Fatalf("conn_count = %d, want 0 -- the decoder was sized from the superseded value", item.ConnCount) + } + if item.RequestCount != 1 { + t.Fatalf("request_count = %d, want 1", item.RequestCount) + } +} + +// TestHTTP2GoesOpaqueOnAContinuationWithNoBlockOpen is the mirror of the rule that +// nothing may interleave into a header block. A CONTINUATION carries neither a +// stream that means anything on its own nor an END_STREAM flag, so accumulating one +// with no block open would attribute whatever it decoded to a stream taken from a +// frame that does not name one. +func TestHTTP2GoesOpaqueOnAContinuationWithNoBlockOpen(t *testing.T) { + clock := &fakeClock{now: at(t, 0)} + agg := newTestAggregator(clock, testPods()) + client := newH2Encoder() + + block := client.encode(t, hf(":method", "GET"), hf(":path", "/late")) + agg.Observe(reqAt(1, at(t, 0), concat(preface(), + frame(frameContinuation, flagEndHeaders, 1, block)))) + + item := flushOne(t, agg, clock, at(t, time.Minute+5*time.Second)) + + if item.ConnCount != 1 { + t.Fatalf("conn_count = %d, want 1", item.ConnCount) + } + if item.RequestCount != 0 { + t.Fatalf("request_count = %d, want 0", item.RequestCount) + } +} + +// TestHTTP2GoesOpaqueWhenTheBufferBudgetIsExhausted covers the agent-wide bound. +// Held-over bytes are the one thing a peer can make the agent allocate, and the +// budget is what stops a slow trickle of them from being unbounded. Exhausting it +// must cost the connection its visibility, not the agent its memory. +func TestHTTP2GoesOpaqueWhenTheBufferBudgetIsExhausted(t *testing.T) { + clock := &fakeClock{now: at(t, 0)} + agg := NewAggregator(Config{MaxBufferedBytes: 8}, clock, testPods(), logr.Discard()) + client := newH2Encoder() + + block := client.encode(t, hf(":method", "GET"), hf(":path", "/big")) + full := frame(frameHeaders, flagEndHeaders, 1, block) + + // The preface lands whole; the frame arrives one byte short of complete, so the + // bytes so far must be held -- and there is no budget to hold them in. + agg.Observe(reqAt(1, at(t, 0), preface())) + agg.Observe(reqAt(1, at(t, time.Millisecond), full[:len(full)-1])) + + item := flushOne(t, agg, clock, at(t, time.Minute+5*time.Second)) + + if item.ConnCount != 1 { + t.Fatalf("conn_count = %d, want 1 -- the connection kept buffering past its budget", item.ConnCount) + } + if agg.buffered != 0 { + t.Fatalf("buffered = %d, want 0 after the connection went opaque", agg.buffered) + } +} diff --git a/internal/networkmonitor/redmetrics/socketprobe/c/socketprobe.c b/internal/networkmonitor/redmetrics/socketprobe/c/socketprobe.c new file mode 100644 index 00000000..10e5c75e --- /dev/null +++ b/internal/networkmonitor/redmetrics/socketprobe/c/socketprobe.c @@ -0,0 +1,529 @@ +// Socket probes for service-level RED metrics. +// +// Six programs across three attach points, of which four attach on any one node: +// +// kprobe/tcp_sendmsg bytes leaving this node +// kprobe/tcp_close the socket is going away +// +// fentry/tcp_recvmsg remembers where the bytes will land +// fexit/tcp_recvmsg bytes that arrived at this node +// +// kprobe/tcp_recvmsg the same two, for kernels that cannot load the +// kretprobe/tcp_recvmsg trampoline pair +// +// The receive path is written twice on purpose and the loader picks one pair; see +// sr_recv_begin for why the trampoline is the one to want. Both pairs share the two +// helpers that do the actual work, so the fallback cannot drift into measuring +// something different from the primary. +// +// Addresses and ports are read off the socket structure, so nothing here parses +// an IP or TCP header. There is no connect probe and no accept probe: this file +// reports the local endpoint of every event and takes no view on direction. The +// aggregator decides that, from whether the local endpoint is one this node has +// been seen receiving on -- which is what lets a connection already open when the +// agent attached be recognised as inbound rather than written off. +// +// There IS a close probe, and earlier revisions of this file argued there should +// not be. Without one, death can only be inferred from idleness, and idleness +// does not distinguish a socket the kernel has already freed from a live gRPC +// stream between messages or an HTTP keep-alive connection between requests. The +// TTL that reclaims the first quickly enough to bound the connection table +// destroys the decode state of the second, which is unrecoverable: the HPACK +// dynamic table is gone, so later header blocks cannot be read at all. tcp_close +// runs exactly once per socket and its first argument is the same struct sock, +// so it yields the identical connection key through sr_fill_tuple with no new +// helper, no new map and no change to this record's layout. +// +// Two maps exist and neither is per-connection state. sr_events is the ring +// buffer the events travel on. sr_recv_inflight carries the destination buffer +// address from tcp_recvmsg entry to tcp_recvmsg return, because the address is +// only known at entry and the bytes only exist at return; its entries live for +// the duration of one syscall and are deleted on the way out. + +#include +#include + +#include +#include +#include +#include + +char __license[] SEC("license") = "Dual MIT/GPL"; + +// bpf_get_func_ret is helper 184. The vendored bpf_helper_defs.h stops at 180, so it +// is declared here rather than by regenerating a header shared with every other +// program in this tree -- the declaration is the ABI, and helper numbers never move. +// +// It exists because tcp_recvmsg's argument list has changed across kernel versions, +// and an fexit program reads its arguments and its return value out of one array by +// index: the return value sits at index nr_args, so naming it as a trailing BPF_PROG +// parameter would hardcode an index that is right on one kernel and reads an +// argument on another. This helper asks the kernel where it actually is. +// +// Kernels older than 5.17 do not have it, so the fexit program fails to load there +// and the loader falls back to the kretprobe pair. That is the intended behaviour and +// the only reason the fallback still exists. +static long (*bpf_get_func_ret)(void *ctx, __u64 *value) = (void *) 184; + +#ifndef statfunc +#define statfunc static __always_inline +#endif + +// SR_MAX_PAYLOAD is the copy cap. At most this many bytes of any one call are +// carried into userspace; the call's true length is reported separately and is +// always accurate. +#define SR_MAX_PAYLOAD 2048 + +#define SR_DIR_SEND 0 +#define SR_DIR_RECV 1 +// SR_DIR_CLOSE carries no bytes: true_len and copy_len are zero and the payload is +// untouched. It says only that this socket is being torn down, so userspace can +// stop holding state for it. +#define SR_DIR_CLOSE 2 + +// SR_EVENTS_BYTES sizes the ring buffer. It is the only sizeable allocation this +// program makes and is charged to the DaemonSet's memory limit. +#define SR_EVENTS_BYTES (1 << 23) // 8 MiB + +// SR_INFLIGHT_ENTRIES bounds the in-flight recvmsg scratch. It is an LRU map, so +// a leaked entry (a task killed between entry and return) is reclaimed by +// pressure rather than growing without bound. +#define SR_INFLIGHT_ENTRIES 8192 + +// sr_event is the wire format between kernel and userspace. Every field is +// naturally aligned, and the decoder in Go reads it by offset, so both sides +// pin the same literals: the offsets below are asserted here at compile time and +// again from the Go side's test. Reordering a field breaks this build rather +// than quietly shifting what userspace reads. +// The header is a struct of its own so that an event carrying no bytes can reserve +// only the 64 bytes it fills. A close event otherwise costs the same 2112 bytes as a +// full data event, and the ring buffer is the resource that decides whether a +// measurement is taken at all: when it fills, events are dropped silently, so bytes +// spent on a payload nobody wrote are bytes taken from requests nobody measures. +struct sr_event_hdr { + __u64 socket_cookie; + __u64 timestamp_ns; + __u8 saddr[16]; + __u8 daddr[16]; + __u16 family; + __u16 lport; + __u16 dport; + __u8 direction; + __u8 _pad; + __u32 true_len; + __u32 copy_len; +}; + +struct sr_event { + struct sr_event_hdr hdr; + __u8 payload[SR_MAX_PAYLOAD]; +}; + +// The record layout, pinned. These literals are the same ones the Go decoder +// reads by, so drift on either side is a build failure rather than a field read +// from the wrong place. +#define SR_ASSERT_OFFSET(field, want) \ + _Static_assert(__builtin_offsetof(struct sr_event, field) == (want), "sr_event." #field " moved") + +SR_ASSERT_OFFSET(hdr.socket_cookie, 0); +SR_ASSERT_OFFSET(hdr.timestamp_ns, 8); +SR_ASSERT_OFFSET(hdr.saddr, 16); +SR_ASSERT_OFFSET(hdr.daddr, 32); +SR_ASSERT_OFFSET(hdr.family, 48); +SR_ASSERT_OFFSET(hdr.lport, 50); +SR_ASSERT_OFFSET(hdr.dport, 52); +SR_ASSERT_OFFSET(hdr.direction, 54); +SR_ASSERT_OFFSET(hdr.true_len, 56); +SR_ASSERT_OFFSET(hdr.copy_len, 60); +SR_ASSERT_OFFSET(payload, 64); +_Static_assert(sizeof(struct sr_event) == 64 + SR_MAX_PAYLOAD, "sr_event grew or gained padding"); + +// Nesting must not add padding: the header-only reservation is exactly the prefix a +// full event starts with, so userspace decodes both with one set of offsets. +_Static_assert(sizeof(struct sr_event_hdr) == 64, "sr_event_hdr is no longer the 64-byte record header"); + +struct { + __uint(type, BPF_MAP_TYPE_RINGBUF); + __uint(max_entries, SR_EVENTS_BYTES); +} sr_events SEC(".maps"); + +// sr_inflight is one in-progress tcp_recvmsg, keyed by the calling task. +struct sr_inflight { + __u64 sk; + __u64 buf; + __u64 buf_len; +}; + +struct { + __uint(type, BPF_MAP_TYPE_LRU_HASH); + __uint(max_entries, SR_INFLIGHT_ENTRIES); + __type(key, __u64); + __type(value, struct sr_inflight); +} sr_recv_inflight SEC(".maps"); + +// CO-RE flavors for the iterator that carries a socket call's buffer. The member +// holding the buffer has moved three times: iov through 6.3, __iov from 6.4, and +// a separate single-buffer form from 6.0 (ubuf, becoming __ubuf_iovec in 6.10). +// Only the names matter here; libbpf resolves the offsets against the running +// kernel. +struct iov_iter___red { + __u8 iter_type; + void *ubuf; + struct iovec __ubuf_iovec; + const struct iovec *iov; + const struct iovec *__iov; +}; + +struct msghdr___red { + void *msg_name; + struct iov_iter___red msg_iter; +}; + +// iter_type___red exists so the iterator forms can be recognised by name rather +// than by a numeric value, which was renumbered in 6.4. Only these two describe +// a userspace buffer: ITER_BVEC, ITER_KVEC, ITER_XARRAY and ITER_DISCARD point at +// kernel memory or at nothing, and their first member is not an iovec at all. +enum iter_type___red { + ITER_IOVEC = 1000, + ITER_UBUF = 1001, +}; + +// sr_iter_buf finds the first buffer segment of a socket call, returning its +// address and length. A call whose payload spans several segments is copied only +// as far as the first: the true length still reports the whole call. +// +// It reads a buffer only from the two iterator forms that describe one. Anything +// else -- a bvec, a kvec, an xarray -- is left alone: its first member is a page +// or a kernel address, not an iov_base, so reading it as one would be reading a +// different field entirely. +statfunc int sr_iter_buf(struct msghdr *msg, __u64 *buf, __u64 *buf_len) +{ + struct msghdr___red *m = (struct msghdr___red *)msg; + + *buf = 0; + *buf_len = 0; + + if (bpf_core_field_exists(m->msg_iter.iter_type)) { + __u8 iter_type = 0xff; + bpf_core_read(&iter_type, sizeof(iter_type), &m->msg_iter.iter_type); + + if (bpf_core_enum_value_exists(enum iter_type___red, ITER_UBUF) && + iter_type == (__u8)bpf_core_enum_value(enum iter_type___red, ITER_UBUF)) { + if (bpf_core_field_exists(m->msg_iter.ubuf)) { + bpf_core_read(buf, sizeof(*buf), &m->msg_iter.ubuf); + } else if (bpf_core_field_exists(m->msg_iter.__ubuf_iovec)) { + bpf_core_read(buf, sizeof(*buf), &m->msg_iter.__ubuf_iovec.iov_base); + bpf_core_read(buf_len, sizeof(*buf_len), &m->msg_iter.__ubuf_iovec.iov_len); + } + return *buf != 0 ? 0 : -1; + } + + // Every kernel that has iter_type also has ITER_IOVEC, so a type that is + // neither it nor ITER_UBUF is an iterator this program does not read. + if (iter_type != (__u8)bpf_core_enum_value(enum iter_type___red, ITER_IOVEC)) + return -1; + } + + const struct iovec *iov = NULL; + if (bpf_core_field_exists(m->msg_iter.__iov)) + bpf_core_read(&iov, sizeof(iov), &m->msg_iter.__iov); + else if (bpf_core_field_exists(m->msg_iter.iov)) + bpf_core_read(&iov, sizeof(iov), &m->msg_iter.iov); + + if (!iov) + return -1; + + bpf_core_read(buf, sizeof(*buf), &iov->iov_base); + bpf_core_read(buf_len, sizeof(*buf_len), &iov->iov_len); + return *buf != 0 ? 0 : -1; +} + +// sr_fill_tuple reads both ends of the connection off the socket structure. +statfunc int sr_fill_tuple(struct sock *sk, struct sr_event_hdr *hdr) +{ + __u16 family = BPF_CORE_READ(sk, __sk_common.skc_family); + hdr->family = family; + + if (family == AF_INET) { + __be32 saddr = BPF_CORE_READ(sk, __sk_common.skc_rcv_saddr); + __be32 daddr = BPF_CORE_READ(sk, __sk_common.skc_daddr); + __builtin_memcpy(hdr->saddr, &saddr, sizeof(saddr)); + __builtin_memcpy(hdr->daddr, &daddr, sizeof(daddr)); + } else if (family == AF_INET6) { + BPF_CORE_READ_INTO(&hdr->saddr, sk, __sk_common.skc_v6_rcv_saddr); + BPF_CORE_READ_INTO(&hdr->daddr, sk, __sk_common.skc_v6_daddr); + } else { + return -1; + } + + // skc_num is already host order; skc_dport is not. + hdr->lport = BPF_CORE_READ(sk, __sk_common.skc_num); + hdr->dport = bpf_ntohs(BPF_CORE_READ(sk, __sk_common.skc_dport)); + return 0; +} + +// sr_copy_payload copies at most SR_MAX_PAYLOAD bytes and records how many landed. +// A failed read leaves copy_len at zero: the event is still emitted, because the +// true length is worth reporting on its own. +statfunc void sr_copy_payload(struct sr_event *ev, __u64 buf, __u64 avail) +{ + ev->hdr.copy_len = 0; + if (!buf || avail == 0) + return; + + __u64 want = avail; + if (want > SR_MAX_PAYLOAD) + want = SR_MAX_PAYLOAD; + + long err; + if (want == SR_MAX_PAYLOAD) + err = bpf_probe_read_user(ev->payload, SR_MAX_PAYLOAD, (const void *)buf); + else + err = bpf_probe_read_user(ev->payload, (__u32)want & (SR_MAX_PAYLOAD - 1), (const void *)buf); + + if (err == 0) + ev->hdr.copy_len = (__u32)want; +} + +// sr_fill_header writes every header field, and is the only thing that does. Both +// emit paths below go through it, so the header a close event carries cannot drift +// from the one a data event carries. +statfunc int sr_fill_header(struct sr_event_hdr *hdr, struct sock *sk, __u8 direction, __u64 true_len) +{ + if (sr_fill_tuple(sk, hdr) != 0) + return -1; + + // The kernel socket address identifies the socket, and is unique only among + // sockets alive at the same instant -- the kernel recycles the structure. It + // is not a bpf_get_socket_cookie() value: that helper is not available to + // kprobes, whose sock pointer the verifier will not accept for it. Userspace + // therefore pairs this with the four-tuple above before treating two + // observations as the same connection; the TTL sweep bounds memory and does + // nothing about reuse. + hdr->socket_cookie = (__u64)sk; + hdr->timestamp_ns = bpf_ktime_get_ns(); + hdr->direction = direction; + hdr->_pad = 0; + hdr->true_len = true_len > 0xffffffffULL ? 0xffffffffU : (__u32)true_len; + hdr->copy_len = 0; + return 0; +} + +// sr_emit builds and submits one event carrying a payload. +statfunc void sr_emit(struct sock *sk, __u8 direction, __u64 true_len, __u64 buf, __u64 buf_len) +{ + struct sr_event *ev = bpf_ringbuf_reserve(&sr_events, sizeof(*ev), 0); + if (!ev) + return; + + if (sr_fill_header(&ev->hdr, sk, direction, true_len) != 0) { + bpf_ringbuf_discard(ev, 0); + return; + } + + // Never read past the segment the buffer address belongs to. + __u64 avail = true_len; + if (buf_len > 0 && buf_len < avail) + avail = buf_len; + sr_copy_payload(ev, buf, avail); + + bpf_ringbuf_submit(ev, 0); +} + +// sr_emit_close submits a header with no payload behind it. +// +// It reserves sizeof(struct sr_event_hdr) rather than sizeof(struct sr_event) because +// a close carries no bytes: reserving the full record would spend 2112 bytes of ring +// buffer to deliver 64 bytes of meaning. The saving is not cosmetic. A single-request +// connection emits send, recv and close, so the full-size close is a third of the +// bytes that connection costs, and the ring buffer dropping events is invisible -- +// dropped requests look like requests that never happened. +// +// This is safe for userspace because the Go decoder is length-driven: it requires +// only the 64-byte header and clamps its payload copy to what the record actually +// contains, so a header-only record decodes with an empty payload rather than +// reading past its end. TestDecodeEvent_CloseRecordIsHeaderOnly pins that. +statfunc void sr_emit_close(struct sock *sk) +{ + struct sr_event_hdr *hdr = bpf_ringbuf_reserve(&sr_events, sizeof(*hdr), 0); + if (!hdr) + return; + + if (sr_fill_header(hdr, sk, SR_DIR_CLOSE, 0) != 0) { + bpf_ringbuf_discard(hdr, 0); + return; + } + + bpf_ringbuf_submit(hdr, 0); +} + +SEC("kprobe/tcp_sendmsg") +int BPF_KPROBE(sr_tcp_sendmsg, struct sock *sk, struct msghdr *msg, size_t size) +{ + if (!sk || !msg || size == 0) + return 0; + + __u64 buf = 0, buf_len = 0; + sr_iter_buf(msg, &buf, &buf_len); + sr_emit(sk, SR_DIR_SEND, size, buf, buf_len); + return 0; +} + +// sr_recv_begin remembers where a receive is going to land, because that is only +// knowable on the way in: by the time tcp_recvmsg returns, copy_to_iter has advanced +// the iterator past the bytes it just wrote, and for a buffer filled exactly to its +// end the iterator has moved on to the next segment entirely. The address has to be +// taken at entry or it is not the address the bytes went to. +// +// The entry observation is worth nothing on its own -- it says a read was attempted, +// not that anything arrived -- so it is paired with sr_recv_end below, and how that +// pairing is done is the whole reason this file carries two copies of the receive +// path. +// +// A kretprobe pairs them by taking a return instance from a pool at entry and holding +// it until the function returns. tcp_recvmsg blocks for as long as a keep-alive +// connection is idle, so a server holding N idle connections holds N instances, and +// the pool defaults to max(10, 2 * num_possible_cpus()) -- ten on a two-vCPU node. +// Past that rethook_try_get() returns NULL, the return handler never runs, the miss +// is counted in nmissed where nothing reads it, and every receive on the node is lost +// while the entry probe keeps firing. One moderately busy keep-alive server is enough +// to do that to every container on the node. +// +// A BPF trampoline has no pool. fentry and fexit run on a trampoline the kernel +// patches into the function, so calls in flight are bounded by the number of threads +// rather than by a preallocated array, and there is nothing to exhaust. +statfunc void sr_recv_begin(struct sock *sk, struct msghdr *msg) +{ + if (!sk || !msg) + return; + + struct sr_inflight inflight = {}; + inflight.sk = (__u64)sk; + if (sr_iter_buf(msg, &inflight.buf, &inflight.buf_len) != 0) + inflight.buf = 0; + + __u64 id = bpf_get_current_pid_tgid(); + bpf_map_update_elem(&sr_recv_inflight, &id, &inflight, BPF_ANY); +} + +// sr_recv_end emits what the paired sr_recv_begin was holding, and clears it either +// way: a receive that returned nothing still ends the syscall, and leaving the entry +// behind would let the next read on the same task emit against a stale buffer. +statfunc void sr_recv_end(long ret) +{ + __u64 id = bpf_get_current_pid_tgid(); + struct sr_inflight *inflight = bpf_map_lookup_elem(&sr_recv_inflight, &id); + if (!inflight) + return; + + struct sr_inflight saved = *inflight; + bpf_map_delete_elem(&sr_recv_inflight, &id); + + if (ret <= 0 || saved.sk == 0) + return; + + sr_emit((struct sock *)saved.sk, SR_DIR_RECV, (__u64)ret, saved.buf, saved.buf_len); +} + +// Only the first two arguments are named. tcp_recvmsg has gained and lost trailing +// parameters across kernel versions and BPF_PROG reads them positionally, so naming +// more would be naming a different argument depending on the node; sk and msg are at +// indices 0 and 1 under every variant. +SEC("fentry/tcp_recvmsg") +int BPF_PROG(sr_tcp_recvmsg_fentry, struct sock *sk, struct msghdr *msg) +{ + sr_recv_begin(sk, msg); + return 0; +} + +// No arguments are named here: the return value is the only thing this needs, and +// bpf_get_func_ret is what fetches it. +SEC("fexit/tcp_recvmsg") +int BPF_PROG(sr_tcp_recvmsg_fexit) +{ + __u64 ret = 0; + + if (bpf_get_func_ret(ctx, &ret) != 0) + return 0; + + // tcp_recvmsg returns int. Narrowing before widening keeps a negative errno + // negative instead of turning it into a very large positive length. + sr_recv_end((long)(int)ret); + return 0; +} + +// The fallback pair, for kernels without BTF trampolines or without helper 184. It +// is attached with an explicit maxactive rather than the default ten -- see +// retprobeMaxActive in probe_linux.go -- which does not make the pool unexhaustible, +// only large enough to cover the same number of concurrent receives the in-flight +// map itself can hold. +SEC("kprobe/tcp_recvmsg") +int BPF_KPROBE(sr_tcp_recvmsg_entry, struct sock *sk, struct msghdr *msg) +{ + sr_recv_begin(sk, msg); + return 0; +} + +SEC("kretprobe/tcp_recvmsg") +int BPF_KRETPROBE(sr_tcp_recvmsg_exit, int ret) +{ + sr_recv_end(ret); + return 0; +} + +// tcp_close is the local file descriptor being closed, which has to happen for the +// socket structure to be freed, and it runs once. For a connection the application +// closes, it is where userspace learns a connection key will never be seen again, so +// a half-decoded stream can be dropped and its requests settled there rather than at +// the end of an idle timer that also expires connections which are merely quiet. +// +// It is not sufficient on its own, which is what sr_tcp_done below is for: a +// connection the kernel ends by itself arrives here with its local port already +// released, so the key read here is not the key the connection was tracked under. +// +// The socket may still linger in TIME_WAIT afterwards, and a retransmit or a late +// ACK may still travel on it. None of that reaches tcp_sendmsg or tcp_recvmsg, +// which are the syscall paths, so no event can arrive for a key after its close. +SEC("kprobe/tcp_close") +int BPF_KPROBE(sr_tcp_close, struct sock *sk) +{ + if (!sk) + return 0; + + sr_emit_close(sk); + return 0; +} + +// tcp_done is the kernel ending a connection on its own -- a reset arriving, or a +// timeout firing -- rather than the application closing it. It has to be observed as +// well as tcp_close, because it destroys the part of the socket the connection key +// is built from while the file descriptor is still open: +// +// tcp_done -> tcp_set_state(sk, TCP_CLOSE) -> inet_put_port(sk) +// +// inet_put_port unhashes the socket and zeroes sk->__sk_common.skc_num, so the local +// port is gone. The descriptor stays open until the application gets round to +// closing it, and only then does tcp_close run -- by which point sr_fill_tuple reads +// local port 0 and builds a key that matches nothing. Userspace drops that close as +// one for a connection it never saw, and the entry it should have released survives +// to the idle sweep: ConnTTL of a bounded table held by a connection that ended +// seconds ago. +// +// Measured on a saturated single-threaded server on a 6.12 kernel, 77 of every 416 +// connections per second ended this way, filling a 16384-entry table in three +// minutes. The table is what admits new connections, so past that point the agent +// reported a few percent of the actual request rate until the sweep drained it. +// +// At kprobe entry the body of tcp_done has not run yet, so the tuple is still whole. +// Both probes stay attached: a socket closed without ever having connected never +// reaches tcp_done, and on the ordinary path both fire, the first releasing the entry +// and the second finding nothing to release. An unmatched close is already a no-op. +SEC("kprobe/tcp_done") +int BPF_KPROBE(sr_tcp_done, struct sock *sk) +{ + if (!sk) + return 0; + + sr_emit_close(sk); + return 0; +} diff --git a/internal/networkmonitor/redmetrics/socketprobe/generate.go b/internal/networkmonitor/redmetrics/socketprobe/generate.go new file mode 100644 index 00000000..08071438 --- /dev/null +++ b/internal/networkmonitor/redmetrics/socketprobe/generate.go @@ -0,0 +1,35 @@ +package socketprobe + +// The compiled objects and their loader are checked in, so an ordinary build and +// an ordinary `go generate ./...` need no clang at all. The directive below is +// deliberately inert, matching internal/networkmonitor/ebpf: regenerating needs a +// clang that can target BPF, and the one a Mac has by default cannot, so a live +// directive would break `go generate ./...` for everyone on a Mac. +// +// That is a statement about Apple's clang and not about the machine. A Homebrew LLVM +// targets BPF fine, so on a Mac with `brew install llvm` this runs in place, from +// this directory: +// +// GOPACKAGE=socketprobe go run github.com/cilium/ebpf/cmd/bpf2go -no-strip \ +// -target amd64,arm64 -cc /opt/homebrew/opt/llvm/bin/clang \ +// -cflags "-O2 -g -Wall -I../../ebpf/c/include" socketprobe ./c/socketprobe.c +// +// Check the toolchain first with `llc --version`: the target list has to include +// bpfel and bpfeb. Without such a clang, run the same bpf2go inside a container that +// has one: +// +// docker run --rm -v "$(git rev-parse --show-toplevel)/operators/zxporter":/src \ +// -w /src/internal/networkmonitor/redmetrics/socketprobe -e GOPACKAGE=socketprobe \ +// go run github.com/cilium/ebpf/cmd/bpf2go -no-strip \ +// -target amd64,arm64 -cc clang -cflags "-O2 -g -Wall -I../../ebpf/c/include" \ +// socketprobe ./c/socketprobe.c +// +// Regenerating is not optional when a program is added or renamed. The loader reads +// each program out of the object by its section name, so a hand-edited +// socketprobe_*_bpfel.go naming a program the object does not contain fails at load +// time on the node, not at compile time here. +// +// The C file asserts its own record offsets, so a layout change fails that +// compile rather than shifting what the Go decoder reads. +// +// //go:generate go run github.com/cilium/ebpf/cmd/bpf2go -no-strip -target amd64,arm64 -cc clang -cflags "-O2 -g -Wall -I../../ebpf/c/include" socketprobe ./c/socketprobe.c diff --git a/internal/networkmonitor/redmetrics/socketprobe/objects_test.go b/internal/networkmonitor/redmetrics/socketprobe/objects_test.go new file mode 100644 index 00000000..ac3b2fc1 --- /dev/null +++ b/internal/networkmonitor/redmetrics/socketprobe/objects_test.go @@ -0,0 +1,60 @@ +//go:build amd64 || arm64 + +package socketprobe + +import ( + "testing" + + bpf "github.com/cilium/ebpf" +) + +// The loader looks each program up in the object file by name and attaches it to a +// kernel function by name, and neither lookup is checked by the compiler: the object is +// a checked-in build artefact, so a program added to c/socketprobe.c without +// regenerating, or renamed on one side only, builds cleanly and fails at load time on +// the node. +func TestObjectContainsEveryProgramTheLoaderAttaches(t *testing.T) { + spec, err := loadSocketprobe() + if err != nil { + t.Fatalf("loading the embedded object: %v", err) + } + + // The receive path is deliberately compiled twice, and only one variant is loaded + // on any given kernel, so both have to be present. + want := map[string]struct { + typ bpf.ProgramType + attachTo string + }{ + "sr_tcp_sendmsg": {bpf.Kprobe, "tcp_sendmsg"}, + "sr_tcp_recvmsg_fentry": {bpf.Tracing, "tcp_recvmsg"}, + "sr_tcp_recvmsg_fexit": {bpf.Tracing, "tcp_recvmsg"}, + "sr_tcp_recvmsg_entry": {bpf.Kprobe, "tcp_recvmsg"}, + "sr_tcp_recvmsg_exit": {bpf.Kprobe, "tcp_recvmsg"}, + "sr_tcp_close": {bpf.Kprobe, "tcp_close"}, + // tcp_done is the kernel ending a connection by itself. Without it the close + // arrives from tcp_close with the local port already released, under a key that + // matches nothing, and the connection leaks until the idle sweep. See the + // comment on the program in c/socketprobe.c. + "sr_tcp_done": {bpf.Kprobe, "tcp_done"}, + } + + for name, w := range want { + prog, ok := spec.Programs[name] + if !ok { + t.Errorf("program %s is missing from the object; regenerate with bpf2go (see generate.go)", name) + continue + } + if prog.Type != w.typ { + t.Errorf("program %s has type %v, want %v", name, prog.Type, w.typ) + } + if prog.AttachTo != w.attachTo { + t.Errorf("program %s attaches to %q, want %q", name, prog.AttachTo, w.attachTo) + } + } + + for name := range spec.Programs { + if _, expected := want[name]; !expected { + t.Errorf("object contains program %s, which the loader never attaches", name) + } + } +} diff --git a/internal/networkmonitor/redmetrics/socketprobe/probe.go b/internal/networkmonitor/redmetrics/socketprobe/probe.go new file mode 100644 index 00000000..03193ea0 --- /dev/null +++ b/internal/networkmonitor/redmetrics/socketprobe/probe.go @@ -0,0 +1,167 @@ +// Package socketprobe attaches the socket probes that feed container RED metric +// aggregation and turns their ring buffer records into redmetrics.SocketEvent +// values. +// +// The probes are five eBPF programs across four attach points: tcp_sendmsg, +// tcp_recvmsg on both entry and return, and tcp_close and tcp_done for the two ways a +// connection ends. They read addresses and ports off the socket structure, so no packet +// header is parsed and no new capability, host mount or kernel requirement is needed. +// +// Only the loader is Linux-only. The record layout and its decoder live here so +// they can be tested anywhere. +package socketprobe + +import ( + "encoding/binary" + "errors" + "net/netip" + "time" + + "github.com/devzero-inc/zxporter/internal/networkmonitor/redmetrics" +) + +// ErrUnsupported is returned when the probes cannot run on this platform. +var ErrUnsupported = errors.New("socketprobe: socket probes require Linux") + +// Record layout, matching struct sr_event in c/socketprobe.c. The decoder reads +// by offset, so a change on either side has to be made on both. +const ( + offSocketCookie = 0 + offTimestampNs = 8 + offSrcAddr = 16 + offDstAddr = 32 + offFamily = 48 + offLocalPort = 50 + offRemotePort = 52 + offDirection = 54 + offTrueLen = 56 + offCopyLen = 60 + offPayload = 64 + + // recordHeaderSize is everything before the payload. + recordHeaderSize = offPayload + // recordSize is one whole ring buffer record. + recordSize = recordHeaderSize + redmetrics.PayloadCap +) + +// Address families as the kernel reports them on the socket. +const ( + afInet = 2 + afInet6 = 10 +) + +// Direction values written by the probe, matching SR_DIR_SEND, SR_DIR_RECV and +// SR_DIR_CLOSE. +const ( + dirSend = 0 + dirRecv = 1 + dirClose = 2 +) + +// clockOffset converts the kernel monotonic clock the probe stamps events with +// into wall clock. It is captured once, when the probe starts. +type clockOffset struct { + wall time.Time + baseNs uint64 +} + +// toWall maps a kernel monotonic timestamp onto the wall clock. +func (o clockOffset) toWall(ns uint64) time.Time { + if o.wall.IsZero() { + return time.Time{} + } + if ns >= o.baseNs { + return o.wall.Add(time.Duration(ns - o.baseNs)) + } + return o.wall.Add(-time.Duration(o.baseNs - ns)) +} + +// decodeEvent turns one ring buffer record into a socket event. It reports false +// for a record that is too short or carries an address family the probe does not +// handle, which is the same thing the probe already filters for. +func decodeEvent(raw []byte, off clockOffset) (redmetrics.SocketEvent, bool) { + if len(raw) < recordHeaderSize { + return redmetrics.SocketEvent{}, false + } + + family := binary.LittleEndian.Uint16(raw[offFamily:]) + local, ok := decodeAddr(raw[offSrcAddr:offSrcAddr+16], family) + if !ok { + return redmetrics.SocketEvent{}, false + } + remote, ok := decodeAddr(raw[offDstAddr:offDstAddr+16], family) + if !ok { + return redmetrics.SocketEvent{}, false + } + + // Mapped exhaustively rather than by defaulting the unrecognised value to a + // send. A direction this build does not know is drift against the compiled + // program, and calling it a send is the worst available guess: an unrecognised + // value on a connection's first event would classify it outbound, which is a + // verdict the aggregator never revisits. + var path redmetrics.SyscallPath + switch raw[offDirection] { + case dirSend: + path = redmetrics.SyscallSend + case dirRecv: + path = redmetrics.SyscallRecv + case dirClose: + path = redmetrics.SyscallClose + default: + return redmetrics.SocketEvent{}, false + } + + return redmetrics.SocketEvent{ + SocketCookie: binary.LittleEndian.Uint64(raw[offSocketCookie:]), + Path: path, + LocalAddr: local, + LocalPort: binary.LittleEndian.Uint16(raw[offLocalPort:]), + RemoteAddr: remote, + RemotePort: binary.LittleEndian.Uint16(raw[offRemotePort:]), + TrueLen: binary.LittleEndian.Uint32(raw[offTrueLen:]), + Payload: decodePayload(raw), + Timestamp: off.toWall(binary.LittleEndian.Uint64(raw[offTimestampNs:])), + }, true +} + +// decodePayload copies the prefix of the message the probe captured. +// +// The copy is not an oversight to be optimised away later. The ring buffer hands +// out a view of memory it reuses as soon as the record is released, so a decoder +// holding a sub-slice of it across events -- which is exactly what a partially +// consumed stream does -- would read whatever syscall came next. The cost is bounded +// by PayloadCap and is the price of reading message boundaries at all. +// +// copy_len is what the probe managed to read, which is at most true_len and at most +// the cap, but it arrives from the kernel and is clamped here rather than trusted: a +// bad value would otherwise index past the end of the record. +func decodePayload(raw []byte) []byte { + copyLen := int(binary.LittleEndian.Uint32(raw[offCopyLen:])) + if copyLen > redmetrics.PayloadCap { + copyLen = redmetrics.PayloadCap + } + if copyLen > len(raw)-offPayload { + copyLen = len(raw) - offPayload + } + if copyLen <= 0 { + return nil + } + payload := make([]byte, copyLen) + copy(payload, raw[offPayload:offPayload+copyLen]) + return payload +} + +// decodeAddr reads an address out of the fixed sixteen byte field. A v4 mapped +// v6 address is unmapped, so a dual stack socket and a v4 socket talking to the +// same pod produce the same string. +func decodeAddr(b []byte, family uint16) (netip.Addr, bool) { + switch family { + case afInet: + return netip.AddrFrom4([4]byte(b[:4])), true + case afInet6: + addr := netip.AddrFrom16([16]byte(b[:16])) + return addr.Unmap(), true + default: + return netip.Addr{}, false + } +} diff --git a/internal/networkmonitor/redmetrics/socketprobe/probe_linux.go b/internal/networkmonitor/redmetrics/socketprobe/probe_linux.go new file mode 100644 index 00000000..4c57e388 --- /dev/null +++ b/internal/networkmonitor/redmetrics/socketprobe/probe_linux.go @@ -0,0 +1,408 @@ +//go:build linux + +package socketprobe + +import ( + "context" + "errors" + "fmt" + "io" + "os" + "sync" + "time" + + bpf "github.com/cilium/ebpf" + "github.com/cilium/ebpf/link" + "github.com/cilium/ebpf/ringbuf" + "github.com/cilium/ebpf/rlimit" + "github.com/go-logr/logr" + "golang.org/x/sys/unix" + + "github.com/devzero-inc/zxporter/internal/networkmonitor/ebpf" + "github.com/devzero-inc/zxporter/internal/networkmonitor/redmetrics" +) + +// The kernel functions the probes attach to. All are TCP specific, so no +// filtering for protocol is needed in the programs. +const ( + fnTCPSendmsg = "tcp_sendmsg" + fnTCPRecvmsg = "tcp_recvmsg" + fnTCPClose = "tcp_close" + fnTCPDone = "tcp_done" +) + +// retprobeMaxActive is how many receives the kretprobe fallback can have in flight +// at once. It is set explicitly because the kernel's default is not a capacity so +// much as a placeholder: max(10, 2 * num_possible_cpus()), which is ten on a +// two-vCPU node. A kretprobe holds a return instance for as long as the probed +// function runs, and tcp_recvmsg runs for as long as a keep-alive connection is +// idle, so a server holding a few dozen idle connections exhausts the default and +// stops reporting receives for every container on the node -- silently, since the +// kernel counts the misses in nmissed and nothing reads it. +// +// The number is the in-flight map's capacity. Past that many concurrent receives +// the map starts evicting entries and a larger pool would buy nothing, and below it +// the pool would be the binding constraint. Each instance is a small fixed struct, +// so the reservation is on the order of half a megabyte. +// +// This only bounds the damage. The trampoline variant has no pool at all, which is +// why it is preferred and this is the fallback. +// +// Keep it equal to SR_INFLIGHT_ENTRIES in c/socketprobe.c. +const retprobeMaxActive = 8192 + +// recvObjects is a loaded object file, viewed through whichever of the two +// receive-side implementations the kernel accepted. Everything outside the receive +// path is identical between them. +type recvObjects interface { + io.Closer + + // shared returns the programs and maps that do not vary by variant. + shared() *commonObjects + // attachRecv attaches the receive pair, or returns an error having attached + // neither. + attachRecv() ([]link.Link, error) + // describe names the variant for logs. + describe() string +} + +// commonObjects are the parts of the object file both variants use. +type commonObjects struct { + Events *bpf.Map `ebpf:"sr_events"` + RecvInflight *bpf.Map `ebpf:"sr_recv_inflight"` + TCPSendmsg *bpf.Program `ebpf:"sr_tcp_sendmsg"` + TCPClose *bpf.Program `ebpf:"sr_tcp_close"` + TCPDone *bpf.Program `ebpf:"sr_tcp_done"` +} + +func (o *commonObjects) shared() *commonObjects { return o } + +func (o *commonObjects) closeShared() { + closeAll(o.Events, o.RecvInflight, o.TCPSendmsg, o.TCPClose, o.TCPDone) +} + +// tracingObjects is the variant to want: fentry and fexit run on a trampoline the +// kernel patches into tcp_recvmsg, so there is no preallocated pool of return +// instances and nothing that a few hundred idle keep-alive connections can exhaust. +// +// It needs BTF trampolines (5.5) and bpf_get_func_ret (5.17). Kernels without them +// reject the programs at load time, which is what selects the fallback. +type tracingObjects struct { + commonObjects + + RecvFentry *bpf.Program `ebpf:"sr_tcp_recvmsg_fentry"` + RecvFexit *bpf.Program `ebpf:"sr_tcp_recvmsg_fexit"` +} + +func (o *tracingObjects) describe() string { return "fentry/fexit" } + +func (o *tracingObjects) Close() error { + closeAll(o.RecvFentry, o.RecvFexit) + o.closeShared() + return nil +} + +func (o *tracingObjects) attachRecv() ([]link.Link, error) { + entry, err := link.AttachTracing(link.TracingOptions{ + Program: o.RecvFentry, + AttachType: bpf.AttachTraceFEntry, + }) + if err != nil { + return nil, fmt.Errorf("attaching fentry on %s: %w", fnTCPRecvmsg, err) + } + + exit, err := link.AttachTracing(link.TracingOptions{ + Program: o.RecvFexit, + AttachType: bpf.AttachTraceFExit, + }) + if err != nil { + _ = entry.Close() + return nil, fmt.Errorf("attaching fexit on %s: %w", fnTCPRecvmsg, err) + } + + return []link.Link{entry, exit}, nil +} + +// kprobeObjects is the fallback for kernels that will not load the trampoline pair. +// It measures the same thing through the same two helpers; what differs is that a +// kretprobe has a return-instance pool, and so an upper bound on how many receives +// can be in flight before it starts dropping them. +type kprobeObjects struct { + commonObjects + + RecvEntry *bpf.Program `ebpf:"sr_tcp_recvmsg_entry"` + RecvExit *bpf.Program `ebpf:"sr_tcp_recvmsg_exit"` +} + +func (o *kprobeObjects) describe() string { return "kprobe/kretprobe" } + +func (o *kprobeObjects) Close() error { + closeAll(o.RecvEntry, o.RecvExit) + o.closeShared() + return nil +} + +func (o *kprobeObjects) attachRecv() ([]link.Link, error) { + entry, err := link.Kprobe(fnTCPRecvmsg, o.RecvEntry, nil) + if err != nil { + return nil, fmt.Errorf("attaching kprobe on %s: %w", fnTCPRecvmsg, err) + } + + // RetprobeMaxActive is deprecated because it forces the older tracefs attach + // path rather than the perf one. That is a fair trade here: the perf path + // offers no way to size the pool at all, and the default size is the bug this + // whole variant exists to bound. + exit, err := link.Kretprobe(fnTCPRecvmsg, o.RecvExit, &link.KprobeOptions{ + RetprobeMaxActive: retprobeMaxActive, //nolint:staticcheck // the newer API cannot size the pool + }) + if err != nil { + _ = entry.Close() + return nil, fmt.Errorf("attaching kretprobe on %s: %w", fnTCPRecvmsg, err) + } + + return []link.Link{entry, exit}, nil +} + +// loadObjects loads the object file, preferring the trampoline variant. +// +// Both variants are compiled into the one object file, but LoadAndAssign only loads +// and verifies the programs the given struct names. So asking for the trampoline +// pair on a kernel that cannot run it costs one rejected load, and the kretprobe +// programs are never verified on a kernel that does not need them. +func loadObjects(log logr.Logger) (recvObjects, error) { + tracing := &tracingObjects{} + tracingErr := loadSocketprobeObjects(tracing, nil) + if tracingErr == nil { + return tracing, nil + } + + // Old kernels reject these programs, and that is the entire reason the fallback + // exists -- so this is expected rather than a warning. Any other cause fails the + // second load too, and is reported from there. + log.V(1).Info("Kernel would not load the trampoline programs; falling back to kretprobe", + "reason", tracingErr.Error()) + + kprobe := &kprobeObjects{} + if err := loadSocketprobeObjects(kprobe, nil); err != nil { + return nil, fmt.Errorf("loading socket probe objects: %w", err) + } + return kprobe, nil +} + +// closeAll releases loaded objects. A load that fails partway leaves the fields it +// never reached nil, and Close on a nil *ebpf.Map or *ebpf.Program is a no-op, so +// the caller does not have to know how far it got. +func closeAll(closers ...io.Closer) { + for _, c := range closers { + if c == nil { + continue + } + _ = c.Close() + } +} + +// Probe owns the loaded programs, their attachments and the ring buffer reader. +// Nothing is loaded or attached until Attach is called, so a disabled feature +// costs exactly nothing. +type Probe struct { + log logr.Logger + + mu sync.Mutex + objs recvObjects + links []link.Link + reader *ringbuf.Reader + offset clockOffset + attached bool +} + +// New builds an unattached probe. +func New(log logr.Logger) *Probe { + return &Probe{log: log.WithName("service_request_probe")} +} + +// Attach loads the programs and attaches all of them. It is the only step that can +// fail for reasons specific to the running kernel, so callers report health from +// its error rather than from the read loop. +func (p *Probe) Attach() error { + p.mu.Lock() + defer p.mu.Unlock() + + if p.attached { + return nil + } + // The programs relocate against the running kernel, so they need the kernel's + // own BTF. A node without it is unsupported rather than broken. + if !ebpf.IsKernelBTFAvailable() { + return fmt.Errorf("%w: the kernel does not expose its own BTF", ErrUnsupported) + } + + // The DaemonSet already runs with SYS_RESOURCE, so this raises no new + // privilege; on kernels with memcg accounting for BPF memory it is a no-op. + if err := rlimit.RemoveMemlock(); err != nil { + return fmt.Errorf("removing memlock limit: %w", err) + } + + objs, err := loadObjects(p.log) + if err != nil { + return err + } + + links, err := attachAll(objs) + if err != nil { + _ = objs.Close() + return err + } + + reader, err := ringbuf.NewReader(objs.shared().Events) + if err != nil { + closeLinks(links) + _ = objs.Close() + return fmt.Errorf("opening event ring buffer: %w", err) + } + + offset, err := captureClockOffset() + if err != nil { + _ = reader.Close() + closeLinks(links) + _ = objs.Close() + return fmt.Errorf("reading the monotonic clock: %w", err) + } + + p.objs = objs + p.links = links + p.reader = reader + p.offset = offset + p.attached = true + + p.log.V(1).Info("socket probes attached", "programs", len(links), "receivePath", objs.describe()) + return nil +} + +// attachAll attaches the send probe, the receive pair, and both close probes. All are +// required: without a close probe the connection table can only be reclaimed on +// idleness, and the sweep cannot tell a dead socket from a live idle one. The two close +// probes are not redundant -- tcp_close covers the application closing a descriptor, +// tcp_done the kernel ending a connection by itself, and only the latter still has a +// usable connection key. See the comments on both programs in c/socketprobe.c. +func attachAll(objs recvObjects) ([]link.Link, error) { + shared := objs.shared() + + var links []link.Link + + send, err := link.Kprobe(fnTCPSendmsg, shared.TCPSendmsg, nil) + if err != nil { + return nil, fmt.Errorf("attaching kprobe on %s: %w", fnTCPSendmsg, err) + } + links = append(links, send) + + recv, err := objs.attachRecv() + if err != nil { + closeLinks(links) + return nil, err + } + links = append(links, recv...) + + closed, err := link.Kprobe(fnTCPClose, shared.TCPClose, nil) + if err != nil { + closeLinks(links) + return nil, fmt.Errorf("attaching kprobe on %s: %w", fnTCPClose, err) + } + links = append(links, closed) + + done, err := link.Kprobe(fnTCPDone, shared.TCPDone, nil) + if err != nil { + closeLinks(links) + return nil, fmt.Errorf("attaching kprobe on %s: %w", fnTCPDone, err) + } + links = append(links, done) + + return links, nil +} + +func closeLinks(links []link.Link) { + for _, l := range links { + _ = l.Close() + } +} + +// captureClockOffset pins the kernel monotonic clock to wall clock once, so event +// timestamps can be turned into minute buckets without re-reading the clock per +// event and without losing sub-millisecond resolution. +func captureClockOffset() (clockOffset, error) { + var ts unix.Timespec + if err := unix.ClockGettime(unix.CLOCK_MONOTONIC, &ts); err != nil { + return clockOffset{}, err + } + return clockOffset{ + wall: time.Now(), + baseNs: uint64(ts.Sec)*uint64(time.Second) + uint64(ts.Nsec), //nolint:gosec // clock_gettime never returns negatives + }, nil +} + +// Run reads events until the context is cancelled or the probe is closed, +// handing each decoded event to sink. Attach must have succeeded first. +func (p *Probe) Run(ctx context.Context, sink func(redmetrics.SocketEvent)) error { + p.mu.Lock() + reader, offset, attached := p.reader, p.offset, p.attached + p.mu.Unlock() + + if !attached || reader == nil { + return errors.New("socketprobe: Run called before a successful Attach") + } + + // ringbuf.Reader.Read blocks; closing the reader is what unblocks it. + stop := context.AfterFunc(ctx, func() { _ = reader.Close() }) + defer stop() + + var ( + record ringbuf.Record + undecodable uint64 + ) + for { + if err := reader.ReadInto(&record); err != nil { + if errors.Is(err, ringbuf.ErrClosed) || errors.Is(err, os.ErrClosed) { + return nil + } + return fmt.Errorf("reading socket events: %w", err) + } + + event, ok := decodeEvent(record.RawSample, offset) + if !ok { + // A record userspace cannot read means this build's view of the event + // layout has drifted from the compiled program's, so every later record is + // undecodable too and the first one is the whole story. Log once and keep + // reading rather than counting it where nothing looks. + undecodable++ + if undecodable == 1 { + p.log.Info("Discarding undecodable socket event records", + "recordBytes", len(record.RawSample), "headerBytes", recordHeaderSize) + } + continue + } + sink(event) + } +} + +// Close detaches the programs and releases the ring buffer. It is safe to call +// on a probe that never attached. +func (p *Probe) Close() error { + p.mu.Lock() + defer p.mu.Unlock() + + if !p.attached { + return nil + } + p.attached = false + + if p.reader != nil { + _ = p.reader.Close() + p.reader = nil + } + closeLinks(p.links) + p.links = nil + if p.objs != nil { + _ = p.objs.Close() + p.objs = nil + } + return nil +} diff --git a/internal/networkmonitor/redmetrics/socketprobe/probe_nolinux.go b/internal/networkmonitor/redmetrics/socketprobe/probe_nolinux.go new file mode 100644 index 00000000..8e0aee38 --- /dev/null +++ b/internal/networkmonitor/redmetrics/socketprobe/probe_nolinux.go @@ -0,0 +1,34 @@ +//go:build !linux + +package socketprobe + +import ( + "context" + + "github.com/go-logr/logr" + + "github.com/devzero-inc/zxporter/internal/networkmonitor/redmetrics" +) + +// Probe is the non-Linux stand-in. It exists so the monitor compiles on a +// developer machine; every operation reports the platform as unsupported. +type Probe struct { + log logr.Logger +} + +// New builds an unattached probe. +func New(log logr.Logger) *Probe { + return &Probe{log: log.WithName("service_request_probe")} +} + +// Attach always fails off Linux, which is how the caller learns to deregister the +// health component rather than retry. +func (p *Probe) Attach() error { return ErrUnsupported } + +// Run never starts off Linux. +func (p *Probe) Run(_ context.Context, _ func(redmetrics.SocketEvent)) error { + return ErrUnsupported +} + +// Close is a no-op off Linux. +func (p *Probe) Close() error { return nil } diff --git a/internal/networkmonitor/redmetrics/socketprobe/probe_test.go b/internal/networkmonitor/redmetrics/socketprobe/probe_test.go new file mode 100644 index 00000000..5fee6c5c --- /dev/null +++ b/internal/networkmonitor/redmetrics/socketprobe/probe_test.go @@ -0,0 +1,427 @@ +package socketprobe + +import ( + "encoding/binary" + "net/netip" + "testing" + "time" + + "github.com/devzero-inc/zxporter/internal/networkmonitor/redmetrics" +) + +// record builds one ring buffer record by hand, the way the probe writes it. +type record struct { + cookie uint64 + timestampNs uint64 + src []byte + dst []byte + family uint16 + lport uint16 + dport uint16 + direction uint8 + trueLen uint32 + copyLen uint32 + payload []byte +} + +func (r record) bytes() []byte { + raw := make([]byte, recordSize) + binary.LittleEndian.PutUint64(raw[offSocketCookie:], r.cookie) + binary.LittleEndian.PutUint64(raw[offTimestampNs:], r.timestampNs) + copy(raw[offSrcAddr:offSrcAddr+16], r.src) + copy(raw[offDstAddr:offDstAddr+16], r.dst) + binary.LittleEndian.PutUint16(raw[offFamily:], r.family) + binary.LittleEndian.PutUint16(raw[offLocalPort:], r.lport) + binary.LittleEndian.PutUint16(raw[offRemotePort:], r.dport) + raw[offDirection] = r.direction + binary.LittleEndian.PutUint32(raw[offTrueLen:], r.trueLen) + binary.LittleEndian.PutUint32(raw[offCopyLen:], r.copyLen) + copy(raw[offPayload:], r.payload) + return raw +} + +func v4(a, b, c, d byte) []byte { return []byte{a, b, c, d} } + +var testOffset = clockOffset{ + wall: time.Date(2026, 8, 21, 10, 0, 30, 0, time.UTC), + baseNs: 1_000_000_000, +} + +// The record layout is a contract with c/socketprobe.c: the C side writes struct +// sr_event and this side reads it by offset, so a silent drift mis-decodes every +// field after the one that moved. Both sides assert against the same literals +// rather than against each other -- the C file carries a _Static_assert per field, +// so a field that moves there fails that compile, and one that moves here fails +// this test. +func TestRecordLayoutMatchesTheProgram(t *testing.T) { + fields := []struct { + name string + got int + want int + }{ + {"socket_cookie", offSocketCookie, 0}, + {"timestamp_ns", offTimestampNs, 8}, + {"saddr", offSrcAddr, 16}, + {"daddr", offDstAddr, 32}, + {"family", offFamily, 48}, + {"lport", offLocalPort, 50}, + {"dport", offRemotePort, 52}, + {"direction", offDirection, 54}, + {"true_len", offTrueLen, 56}, + {"copy_len", offCopyLen, 60}, + {"payload", offPayload, 64}, + } + for _, f := range fields { + if f.got != f.want { + t.Errorf("offset of sr_event.%s = %d, want %d", f.name, f.got, f.want) + } + } + + if recordHeaderSize != 64 { + t.Fatalf("header size = %d, want 64 (struct sr_event up to payload)", recordHeaderSize) + } + if redmetrics.PayloadCap != 2048 { + t.Fatalf("payload cap = %d, want 2048 (SR_MAX_PAYLOAD)", redmetrics.PayloadCap) + } + if recordSize != 2112 { + t.Fatalf("record size = %d, want 2112 (sizeof(struct sr_event))", recordSize) + } +} + +func TestDecodeEvent_SendRecord(t *testing.T) { + payload := []byte("GET / HTTP/1.1\r\n") + raw := record{ + cookie: 0xdeadbeef, + timestampNs: 3_000_000_000, // two seconds past the captured base + src: v4(10, 0, 0, 7), + dst: v4(10, 0, 1, 9), + family: afInet, + lport: 54321, + dport: 8080, + direction: dirSend, + trueLen: uint32(len(payload)), + copyLen: uint32(len(payload)), + payload: payload, + }.bytes() + + ev, ok := decodeEvent(raw, testOffset) + if !ok { + t.Fatal("decodeEvent did not decode a well formed record") + } + if ev.SocketCookie != 0xdeadbeef { + t.Errorf("socket cookie = %#x, want 0xdeadbeef", ev.SocketCookie) + } + if ev.Path != redmetrics.SyscallSend { + t.Errorf("path = %v, want send", ev.Path) + } + if string(ev.Payload) != string(payload) { + t.Errorf("payload = %q, want %q", ev.Payload, payload) + } + if got, want := ev.LocalAddr.String(), "10.0.0.7"; got != want { + t.Errorf("local address = %s, want %s", got, want) + } + if got, want := ev.RemoteAddr.String(), "10.0.1.9"; got != want { + t.Errorf("remote address = %s, want %s", got, want) + } + if ev.LocalPort != 54321 || ev.RemotePort != 8080 { + t.Errorf("ports = %d/%d, want 54321/8080", ev.LocalPort, ev.RemotePort) + } + if want := testOffset.wall.Add(2 * time.Second); !ev.Timestamp.Equal(want) { + t.Errorf("timestamp = %s, want %s", ev.Timestamp, want) + } +} + +func TestDecodeEvent_ReturnRecordIsAReceive(t *testing.T) { + raw := record{ + src: v4(10, 0, 0, 7), + dst: v4(10, 0, 1, 9), + family: afInet, + direction: dirRecv, + trueLen: 16, + }.bytes() + + ev, ok := decodeEvent(raw, testOffset) + if !ok { + t.Fatal("decodeEvent did not decode a well formed record") + } + if ev.Path != redmetrics.SyscallRecv { + t.Errorf("path = %v, want recv", ev.Path) + } +} + +// A close record carries no bytes. It is still a record about a real connection, so +// its tuple has to decode exactly like a data record's -- that is the whole reason +// the close probe reads the socket the same way, and it is what lets the aggregator +// find the entry it is being told to release. +func TestDecodeEvent_CloseRecord(t *testing.T) { + raw := record{ + src: v4(10, 0, 0, 7), + lport: 8080, + dst: v4(10, 0, 1, 9), + dport: 54321, + family: afInet, + direction: dirClose, + }.bytes() + + ev, ok := decodeEvent(raw, testOffset) + if !ok { + t.Fatal("decodeEvent did not decode a well formed record") + } + if ev.Path != redmetrics.SyscallClose { + t.Errorf("path = %v, want close", ev.Path) + } + if ev.TrueLen != 0 { + t.Errorf("true length = %d, want 0: a close carries no bytes", ev.TrueLen) + } + if len(ev.Payload) != 0 { + t.Errorf("payload = %d bytes, want none", len(ev.Payload)) + } + if ev.LocalPort != 8080 || ev.RemotePort != 54321 { + t.Errorf("ports = %d/%d, want 8080/54321", ev.LocalPort, ev.RemotePort) + } +} + +// The close probe reserves the 64-byte header alone rather than the whole 2112-byte +// record, because a close carries no payload and the ring buffer is the resource that +// decides whether a measurement is taken at all. That makes a truncated record the +// normal shape of a close, not a corruption, so the decoder has to read one correctly +// -- and it must not read the payload region that is genuinely not there. +// +// This is the Go half of that contract; the C half is the reservation size in +// sr_emit_close. +func TestDecodeEvent_CloseRecordIsHeaderOnly(t *testing.T) { + full := record{ + src: v4(10, 0, 0, 7), + lport: 8080, + dst: v4(10, 0, 1, 9), + dport: 54321, + family: afInet, + direction: dirClose, + }.bytes() + + raw := full[:recordHeaderSize] + + ev, ok := decodeEvent(raw, testOffset) + if !ok { + t.Fatalf("decodeEvent rejected a %d byte close record: the probe emits exactly this", len(raw)) + } + if ev.Path != redmetrics.SyscallClose { + t.Errorf("path = %v, want close", ev.Path) + } + if ev.Payload != nil { + t.Errorf("payload = %d bytes, want nil: there is no payload region in the record", len(ev.Payload)) + } + if ev.LocalPort != 8080 || ev.RemotePort != 54321 { + t.Errorf("ports = %d/%d, want 8080/54321: the tuple must survive the truncation", + ev.LocalPort, ev.RemotePort) + } + if ev.SocketCookie != 0 || ev.TrueLen != 0 { + t.Errorf("cookie = %d, true length = %d, want 0/0", ev.SocketCookie, ev.TrueLen) + } +} + +// The probe copies at most 2KB of any one call but always reports the call's true +// length, so a large request stays a large request in the aggregate. +func TestDecodeEvent_TruncatedCopyKeepsTheTrueLength(t *testing.T) { + payload := make([]byte, redmetrics.PayloadCap) + for i := range payload { + payload[i] = byte(i) + } + raw := record{ + src: v4(10, 0, 0, 7), + dst: v4(10, 0, 1, 9), + family: afInet, + trueLen: 1 << 20, + copyLen: redmetrics.PayloadCap, + payload: payload, + }.bytes() + + ev, ok := decodeEvent(raw, testOffset) + if !ok { + t.Fatal("decodeEvent did not decode a well formed record") + } + if ev.TrueLen != 1<<20 { + t.Errorf("true length = %d, want %d", ev.TrueLen, 1<<20) + } + if len(ev.Payload) != redmetrics.PayloadCap { + t.Errorf("payload length = %d, want the cap %d", len(ev.Payload), redmetrics.PayloadCap) + } +} + +// The ring buffer hands out a view of memory it reuses. A decoder keeps a partially +// consumed stream across events, so a payload that aliased the sample would be read +// again after the kernel had overwritten it with an unrelated syscall. +func TestDecodeEvent_PayloadDoesNotAliasTheSample(t *testing.T) { + payload := []byte("GET /orders HTTP/1.1\r\n\r\n") + raw := record{ + src: v4(10, 0, 0, 7), + dst: v4(10, 0, 1, 9), + family: afInet, + trueLen: uint32(len(payload)), + copyLen: uint32(len(payload)), + payload: payload, + }.bytes() + + ev, ok := decodeEvent(raw, testOffset) + if !ok { + t.Fatal("decodeEvent did not decode a well formed record") + } + if string(ev.Payload) != string(payload) { + t.Fatalf("payload = %q, want %q", ev.Payload, payload) + } + + // Stand in for the kernel reusing the sample. + for i := range raw { + raw[i] = 0xff + } + if string(ev.Payload) != string(payload) { + t.Fatalf("payload after the sample was reused = %q, want %q", ev.Payload, payload) + } +} + +// copy_len says how much of the call the probe managed to read. It arrives from the +// kernel, so a value past the end of the record has to be clamped rather than used +// to index: the alternative is a panic on the ring buffer's hot path. +func TestDecodeEvent_CopyLengthIsClampedToTheCap(t *testing.T) { + tests := []struct { + name string + copyLen uint32 + want int + }{ + {name: "no bytes copied", copyLen: 0, want: 0}, + {name: "the whole cap", copyLen: redmetrics.PayloadCap, want: redmetrics.PayloadCap}, + {name: "past the cap", copyLen: redmetrics.PayloadCap * 4, want: redmetrics.PayloadCap}, + } + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + raw := record{ + src: v4(10, 0, 0, 7), + dst: v4(10, 0, 1, 9), + family: afInet, + trueLen: 1 << 20, + copyLen: tc.copyLen, + payload: make([]byte, redmetrics.PayloadCap), + }.bytes() + + ev, ok := decodeEvent(raw, testOffset) + if !ok { + t.Fatal("decodeEvent did not decode a well formed record") + } + if len(ev.Payload) != tc.want { + t.Fatalf("payload length = %d, want %d", len(ev.Payload), tc.want) + } + }) + } +} + +func TestDecodeEvent_IPv6AndMappedAddresses(t *testing.T) { + six := netip.MustParseAddr("fd00::1").As16() + mapped := netip.MustParseAddr("::ffff:10.0.0.7").As16() + + tests := []struct { + name string + src, dst []byte + wantSrc string + wantDst string + }{ + { + name: "native v6 is preserved", + src: six[:], + dst: six[:], + wantSrc: "fd00::1", + wantDst: "fd00::1", + }, + { + // A dual stack socket reports v4 traffic as v4 mapped v6. Unmapping keeps + // it the same string dakr resolves for the v4 socket to the same pod. + name: "v4 mapped v6 is unmapped", + src: mapped[:], + dst: mapped[:], + wantSrc: "10.0.0.7", + wantDst: "10.0.0.7", + }, + } + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + raw := record{src: tc.src, dst: tc.dst, family: afInet6, trueLen: 8}.bytes() + ev, ok := decodeEvent(raw, testOffset) + if !ok { + t.Fatal("decodeEvent did not decode a well formed record") + } + if got := ev.LocalAddr.String(); got != tc.wantSrc { + t.Errorf("local address = %s, want %s", got, tc.wantSrc) + } + if got := ev.RemoteAddr.String(); got != tc.wantDst { + t.Errorf("remote address = %s, want %s", got, tc.wantDst) + } + }) + } +} + +func TestDecodeEvent_RejectsRecordsItCannotRead(t *testing.T) { + tests := []struct { + name string + raw []byte + }{ + { + name: "short record", + raw: make([]byte, recordHeaderSize-1), + }, + { + // The program filters for AF_INET and AF_INET6; anything else means the + // record is not what this decoder thinks it is. + name: "unhandled address family", + raw: record{src: v4(10, 0, 0, 7), dst: v4(10, 0, 1, 9), family: 1, trueLen: 8}.bytes(), + }, + { + // A direction this build does not know means the loaded program is not the + // one this decoder was compiled against. Rejecting is the only safe + // reading: the discarded alternative was to treat an unrecognised value as + // a send, and on a connection's first event that classifies it outbound -- + // a verdict the aggregator never revisits, so the connection is silently + // never measured at all. + name: "unknown direction", + raw: record{ + src: v4(10, 0, 0, 7), dst: v4(10, 0, 1, 9), + family: afInet, direction: dirClose + 1, trueLen: 8, + }.bytes(), + }, + } + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + if _, ok := decodeEvent(tc.raw, testOffset); ok { + t.Fatal("decodeEvent accepted a record it cannot read") + } + }) + } +} + +// Events stamped by the kernel are monotonic nanoseconds; the offset is captured +// once at attach and every event is placed relative to it. An event stamped just +// before the capture must land just before it, not far in the future. +func TestClockOffset_ToWall(t *testing.T) { + tests := []struct { + name string + ns uint64 + want time.Time + }{ + {name: "at the base", ns: testOffset.baseNs, want: testOffset.wall}, + {name: "after the base", ns: testOffset.baseNs + uint64(90*time.Second), want: testOffset.wall.Add(90 * time.Second)}, + {name: "before the base", ns: testOffset.baseNs - uint64(250*time.Millisecond), want: testOffset.wall.Add(-250 * time.Millisecond)}, + } + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + if got := testOffset.toWall(tc.ns); !got.Equal(tc.want) { + t.Errorf("toWall(%d) = %s, want %s", tc.ns, got, tc.want) + } + }) + } +} + +// Without an offset there is no way to place an event on the wall clock, and a +// zero time is the honest answer rather than a timestamp near the Unix epoch. +func TestClockOffset_ZeroOffsetYieldsZeroTime(t *testing.T) { + if got := (clockOffset{}).toWall(12345); !got.IsZero() { + t.Errorf("toWall on a zero offset = %s, want the zero time", got) + } +} diff --git a/internal/networkmonitor/redmetrics/socketprobe/socketprobe_arm64_bpfel.go b/internal/networkmonitor/redmetrics/socketprobe/socketprobe_arm64_bpfel.go new file mode 100644 index 00000000..fccf5520 --- /dev/null +++ b/internal/networkmonitor/redmetrics/socketprobe/socketprobe_arm64_bpfel.go @@ -0,0 +1,164 @@ +// Code generated by bpf2go; DO NOT EDIT. +//go:build arm64 + +package socketprobe + +import ( + "bytes" + _ "embed" + "fmt" + "io" + + "github.com/cilium/ebpf" +) + +type socketprobeSrInflight struct { + Sk uint64 + Buf uint64 + BufLen uint64 +} + +// loadSocketprobe returns the embedded CollectionSpec for socketprobe. +func loadSocketprobe() (*ebpf.CollectionSpec, error) { + reader := bytes.NewReader(_SocketprobeBytes) + spec, err := ebpf.LoadCollectionSpecFromReader(reader) + if err != nil { + return nil, fmt.Errorf("can't load socketprobe: %w", err) + } + + return spec, err +} + +// loadSocketprobeObjects loads socketprobe and converts it into a struct. +// +// The following types are suitable as obj argument: +// +// *socketprobeObjects +// *socketprobePrograms +// *socketprobeMaps +// +// See ebpf.CollectionSpec.LoadAndAssign documentation for details. +func loadSocketprobeObjects(obj interface{}, opts *ebpf.CollectionOptions) error { + spec, err := loadSocketprobe() + if err != nil { + return err + } + + return spec.LoadAndAssign(obj, opts) +} + +// socketprobeSpecs contains maps and programs before they are loaded into the kernel. +// +// It can be passed ebpf.CollectionSpec.Assign. +type socketprobeSpecs struct { + socketprobeProgramSpecs + socketprobeMapSpecs + socketprobeVariableSpecs +} + +// socketprobeProgramSpecs contains programs before they are loaded into the kernel. +// +// It can be passed ebpf.CollectionSpec.Assign. +type socketprobeProgramSpecs struct { + SrTcpClose *ebpf.ProgramSpec `ebpf:"sr_tcp_close"` + SrTcpDone *ebpf.ProgramSpec `ebpf:"sr_tcp_done"` + SrTcpRecvmsgEntry *ebpf.ProgramSpec `ebpf:"sr_tcp_recvmsg_entry"` + SrTcpRecvmsgExit *ebpf.ProgramSpec `ebpf:"sr_tcp_recvmsg_exit"` + SrTcpRecvmsgFentry *ebpf.ProgramSpec `ebpf:"sr_tcp_recvmsg_fentry"` + SrTcpRecvmsgFexit *ebpf.ProgramSpec `ebpf:"sr_tcp_recvmsg_fexit"` + SrTcpSendmsg *ebpf.ProgramSpec `ebpf:"sr_tcp_sendmsg"` +} + +// socketprobeMapSpecs contains maps before they are loaded into the kernel. +// +// It can be passed ebpf.CollectionSpec.Assign. +type socketprobeMapSpecs struct { + SrEvents *ebpf.MapSpec `ebpf:"sr_events"` + SrRecvInflight *ebpf.MapSpec `ebpf:"sr_recv_inflight"` +} + +// socketprobeVariableSpecs contains global variables before they are loaded into the kernel. +// +// It can be passed ebpf.CollectionSpec.Assign. +type socketprobeVariableSpecs struct { + TRACE_EVENT_FL_TRACEPOINT *ebpf.VariableSpec `ebpf:"TRACE_EVENT_FL_TRACEPOINT"` + TRACE_EVENT_FL_TRACEPOINT_BIT *ebpf.VariableSpec `ebpf:"TRACE_EVENT_FL_TRACEPOINT_BIT"` +} + +// socketprobeObjects contains all objects after they have been loaded into the kernel. +// +// It can be passed to loadSocketprobeObjects or ebpf.CollectionSpec.LoadAndAssign. +type socketprobeObjects struct { + socketprobePrograms + socketprobeMaps + socketprobeVariables +} + +func (o *socketprobeObjects) Close() error { + return _SocketprobeClose( + &o.socketprobePrograms, + &o.socketprobeMaps, + ) +} + +// socketprobeMaps contains all maps after they have been loaded into the kernel. +// +// It can be passed to loadSocketprobeObjects or ebpf.CollectionSpec.LoadAndAssign. +type socketprobeMaps struct { + SrEvents *ebpf.Map `ebpf:"sr_events"` + SrRecvInflight *ebpf.Map `ebpf:"sr_recv_inflight"` +} + +func (m *socketprobeMaps) Close() error { + return _SocketprobeClose( + m.SrEvents, + m.SrRecvInflight, + ) +} + +// socketprobeVariables contains all global variables after they have been loaded into the kernel. +// +// It can be passed to loadSocketprobeObjects or ebpf.CollectionSpec.LoadAndAssign. +type socketprobeVariables struct { + TRACE_EVENT_FL_TRACEPOINT *ebpf.Variable `ebpf:"TRACE_EVENT_FL_TRACEPOINT"` + TRACE_EVENT_FL_TRACEPOINT_BIT *ebpf.Variable `ebpf:"TRACE_EVENT_FL_TRACEPOINT_BIT"` +} + +// socketprobePrograms contains all programs after they have been loaded into the kernel. +// +// It can be passed to loadSocketprobeObjects or ebpf.CollectionSpec.LoadAndAssign. +type socketprobePrograms struct { + SrTcpClose *ebpf.Program `ebpf:"sr_tcp_close"` + SrTcpDone *ebpf.Program `ebpf:"sr_tcp_done"` + SrTcpRecvmsgEntry *ebpf.Program `ebpf:"sr_tcp_recvmsg_entry"` + SrTcpRecvmsgExit *ebpf.Program `ebpf:"sr_tcp_recvmsg_exit"` + SrTcpRecvmsgFentry *ebpf.Program `ebpf:"sr_tcp_recvmsg_fentry"` + SrTcpRecvmsgFexit *ebpf.Program `ebpf:"sr_tcp_recvmsg_fexit"` + SrTcpSendmsg *ebpf.Program `ebpf:"sr_tcp_sendmsg"` +} + +func (p *socketprobePrograms) Close() error { + return _SocketprobeClose( + p.SrTcpClose, + p.SrTcpDone, + p.SrTcpRecvmsgEntry, + p.SrTcpRecvmsgExit, + p.SrTcpRecvmsgFentry, + p.SrTcpRecvmsgFexit, + p.SrTcpSendmsg, + ) +} + +func _SocketprobeClose(closers ...io.Closer) error { + for _, closer := range closers { + if err := closer.Close(); err != nil { + return err + } + } + return nil +} + +// Do not access this directly. +// +//go:embed socketprobe_arm64_bpfel.o +var _SocketprobeBytes []byte diff --git a/internal/networkmonitor/redmetrics/socketprobe/socketprobe_arm64_bpfel.o b/internal/networkmonitor/redmetrics/socketprobe/socketprobe_arm64_bpfel.o new file mode 100644 index 0000000000000000000000000000000000000000..46dd89fc2155a6efb88fd69c6086cedd4478bf87 GIT binary patch literal 52088 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 S`tAEv%*U-(+kSgr`M&@OIlTn{ literal 0 HcmV?d00001 diff --git a/internal/networkmonitor/redmetrics/socketprobe/socketprobe_x86_bpfel.go b/internal/networkmonitor/redmetrics/socketprobe/socketprobe_x86_bpfel.go new file mode 100644 index 00000000..3408d5ba --- /dev/null +++ b/internal/networkmonitor/redmetrics/socketprobe/socketprobe_x86_bpfel.go @@ -0,0 +1,164 @@ +// Code generated by bpf2go; DO NOT EDIT. +//go:build 386 || amd64 + +package socketprobe + +import ( + "bytes" + _ "embed" + "fmt" + "io" + + "github.com/cilium/ebpf" +) + +type socketprobeSrInflight struct { + Sk uint64 + Buf uint64 + BufLen uint64 +} + +// loadSocketprobe returns the embedded CollectionSpec for socketprobe. +func loadSocketprobe() (*ebpf.CollectionSpec, error) { + reader := bytes.NewReader(_SocketprobeBytes) + spec, err := ebpf.LoadCollectionSpecFromReader(reader) + if err != nil { + return nil, fmt.Errorf("can't load socketprobe: %w", err) + } + + return spec, err +} + +// loadSocketprobeObjects loads socketprobe and converts it into a struct. +// +// The following types are suitable as obj argument: +// +// *socketprobeObjects +// *socketprobePrograms +// *socketprobeMaps +// +// See ebpf.CollectionSpec.LoadAndAssign documentation for details. +func loadSocketprobeObjects(obj interface{}, opts *ebpf.CollectionOptions) error { + spec, err := loadSocketprobe() + if err != nil { + return err + } + + return spec.LoadAndAssign(obj, opts) +} + +// socketprobeSpecs contains maps and programs before they are loaded into the kernel. +// +// It can be passed ebpf.CollectionSpec.Assign. +type socketprobeSpecs struct { + socketprobeProgramSpecs + socketprobeMapSpecs + socketprobeVariableSpecs +} + +// socketprobeProgramSpecs contains programs before they are loaded into the kernel. +// +// It can be passed ebpf.CollectionSpec.Assign. +type socketprobeProgramSpecs struct { + SrTcpClose *ebpf.ProgramSpec `ebpf:"sr_tcp_close"` + SrTcpDone *ebpf.ProgramSpec `ebpf:"sr_tcp_done"` + SrTcpRecvmsgEntry *ebpf.ProgramSpec `ebpf:"sr_tcp_recvmsg_entry"` + SrTcpRecvmsgExit *ebpf.ProgramSpec `ebpf:"sr_tcp_recvmsg_exit"` + SrTcpRecvmsgFentry *ebpf.ProgramSpec `ebpf:"sr_tcp_recvmsg_fentry"` + SrTcpRecvmsgFexit *ebpf.ProgramSpec `ebpf:"sr_tcp_recvmsg_fexit"` + SrTcpSendmsg *ebpf.ProgramSpec `ebpf:"sr_tcp_sendmsg"` +} + +// socketprobeMapSpecs contains maps before they are loaded into the kernel. +// +// It can be passed ebpf.CollectionSpec.Assign. +type socketprobeMapSpecs struct { + SrEvents *ebpf.MapSpec `ebpf:"sr_events"` + SrRecvInflight *ebpf.MapSpec `ebpf:"sr_recv_inflight"` +} + +// socketprobeVariableSpecs contains global variables before they are loaded into the kernel. +// +// It can be passed ebpf.CollectionSpec.Assign. +type socketprobeVariableSpecs struct { + TRACE_EVENT_FL_TRACEPOINT *ebpf.VariableSpec `ebpf:"TRACE_EVENT_FL_TRACEPOINT"` + TRACE_EVENT_FL_TRACEPOINT_BIT *ebpf.VariableSpec `ebpf:"TRACE_EVENT_FL_TRACEPOINT_BIT"` +} + +// socketprobeObjects contains all objects after they have been loaded into the kernel. +// +// It can be passed to loadSocketprobeObjects or ebpf.CollectionSpec.LoadAndAssign. +type socketprobeObjects struct { + socketprobePrograms + socketprobeMaps + socketprobeVariables +} + +func (o *socketprobeObjects) Close() error { + return _SocketprobeClose( + &o.socketprobePrograms, + &o.socketprobeMaps, + ) +} + +// socketprobeMaps contains all maps after they have been loaded into the kernel. +// +// It can be passed to loadSocketprobeObjects or ebpf.CollectionSpec.LoadAndAssign. +type socketprobeMaps struct { + SrEvents *ebpf.Map `ebpf:"sr_events"` + SrRecvInflight *ebpf.Map `ebpf:"sr_recv_inflight"` +} + +func (m *socketprobeMaps) Close() error { + return _SocketprobeClose( + m.SrEvents, + m.SrRecvInflight, + ) +} + +// socketprobeVariables contains all global variables after they have been loaded into the kernel. +// +// It can be passed to loadSocketprobeObjects or ebpf.CollectionSpec.LoadAndAssign. +type socketprobeVariables struct { + TRACE_EVENT_FL_TRACEPOINT *ebpf.Variable `ebpf:"TRACE_EVENT_FL_TRACEPOINT"` + TRACE_EVENT_FL_TRACEPOINT_BIT *ebpf.Variable `ebpf:"TRACE_EVENT_FL_TRACEPOINT_BIT"` +} + +// socketprobePrograms contains all programs after they have been loaded into the kernel. +// +// It can be passed to loadSocketprobeObjects or ebpf.CollectionSpec.LoadAndAssign. +type socketprobePrograms struct { + SrTcpClose *ebpf.Program `ebpf:"sr_tcp_close"` + SrTcpDone *ebpf.Program `ebpf:"sr_tcp_done"` + SrTcpRecvmsgEntry *ebpf.Program `ebpf:"sr_tcp_recvmsg_entry"` + SrTcpRecvmsgExit *ebpf.Program `ebpf:"sr_tcp_recvmsg_exit"` + SrTcpRecvmsgFentry *ebpf.Program `ebpf:"sr_tcp_recvmsg_fentry"` + SrTcpRecvmsgFexit *ebpf.Program `ebpf:"sr_tcp_recvmsg_fexit"` + SrTcpSendmsg *ebpf.Program `ebpf:"sr_tcp_sendmsg"` +} + +func (p *socketprobePrograms) Close() error { + return _SocketprobeClose( + p.SrTcpClose, + p.SrTcpDone, + p.SrTcpRecvmsgEntry, + p.SrTcpRecvmsgExit, + p.SrTcpRecvmsgFentry, + p.SrTcpRecvmsgFexit, + p.SrTcpSendmsg, + ) +} + +func _SocketprobeClose(closers ...io.Closer) error { + for _, closer := range closers { + if err := closer.Close(); err != nil { + return err + } + } + return nil +} + +// Do not access this directly. +// +//go:embed socketprobe_x86_bpfel.o +var _SocketprobeBytes []byte diff --git a/internal/networkmonitor/redmetrics/socketprobe/socketprobe_x86_bpfel.o b/internal/networkmonitor/redmetrics/socketprobe/socketprobe_x86_bpfel.o new file mode 100644 index 0000000000000000000000000000000000000000..399ccb3537ba08e67ca13bbe9181c9ddebc6120a GIT binary patch literal 51792 zcmeI53w&Hvz3-MvHF{8cj$93j8WA-h%KiP;UTd=RZ^GkV z@8_O-uKSb!-s`*G>%abMt-aTtnOwJa{rNt}QTA}upVSChU!`KbBkq)Trqlw&+&86C z)4qaQ_c}eJulYIOaIc{!+^g^F`nvz*V{g#$Xjr4#|Dhqx`#&`3`lE1`pYOzVghv4k zXg*;$8_uLht8}oMG}XNxJ>p(-5P$fMH{N*E=jQW%cwb35;-)V&{%&lPSt$aHcfx??LRQXm zu-z#%^ik7y%;!ePQ8pe&ez;CwkKUv&x_!BD9&L7cEWOx%VGrAS)fv0oRlwe+eN9sl zTsJvaD8{3{1Gv7Od>PN?%XB6vrV`P>;NN>y-e%SvrP~m5zEjF5MA$;_#09vGu!D$FuhPPV?D!#I>8FGjuj; z-#zX;wrGm0=%Wi<|4n8twM@IhaL_i!7>kNnJEb@;c>S#%?BA<{)$>Mwy9$-5+udb>F94`_A{%I$>WE-1(0B1qhxS&BZgbo5 zD5n?r&zs<=H{d?nNGp>J5NGZ-EN4pFo8$C~; zxomO1GgAjpkL;x5)|t04o|zBZ@aF3bt=H}MvDXhS${y5oH$U)TP~NyM=IG7>J<7hCQTw|wt7V~^NzWZN(8-L4b){*iTnIc4X^ z>)m{5FU-&8!}^eI`fU7d z;r2(&Jb4d8F~1->=gFg=GK_WM82Gq#gOex@=ED-CU61J59gk$a4oW+_p1B}BGLd>o zz&~pb+Zbykp6n0iKUyz8(Q{EyJ+H5JobEr_ZU0eEdpSU}!a{;S>jbAQCl|L*Y6`RnzbHw8Gd_Ibo*j+57+ z2j-8ztXGb+9${VXa zW9wV2|L-uLeMek-JZjdZMKfH5AH5Bg#gSe24cmU+{{c5VyPi)s>v@UmPrU>oRv)$> ztERi_Y1Ph=b)F^enr_y2J1&lx_1&4MGqmeG^~Z_hs2g1^TI$yKQ7$v^xB8yvH|zX% zom}5XrQo#p4Xj@Vl3nL_nsweCR^zYpV}vs=IUnwuUJGB8H<_hyJY4Qx?K*Gffk*$o z&X4mPIallJ@9Vr)!{68W?ETN^b$(w#xmFOzpnHGwe`?)!?+^a(uiNf?y+7j4k4OKX zUAN7B#}U`Qz4sMwzHYne-u}An>gk=Y+au``sN#RUb=$Sa|9b1TYoGb=c-?mOJ^s4w z=KFts-8SoW&bzf?j(J~Ad+_UEegDT>uA}Z_uF4N>)B(KTd8>W1zm9ny{a5!s`nY|w z^GDxD%jYW3I2vo;%ie$4=O}w$zpZQdxclc&VdGzoy+ktyR-ZC;hue2ZD!qpvl+2~o^Kfg(ScHee%KJI!m-u@QL z3qp3?WbSXx{*~>I=7O^kiv5{_v&_}K4{a_u)3Cc=(p+%5VfVhdx!@elW9>T~M2Ox0 z7%QB0I{tq3?{@!pyC3^`|G%@{bB^Oowr0<^`ozWSHZH!Pd_C|TAElUQf_MO}M9MbI z{Wn2A3i&kT5y&%;pFv)O1TldWK)7YT5ONx%46*^T6|xi31UUe?333PI2;?!yGmsxc zegXM4B!G2jCS(_+8FBz}3*-*SeUM(r4N z$bUj!f=tBy>vRZTN1g&%0ZBnFglvWEhTH`C9E5xLeUN{H3_$(^@-pOgh!6UYLgEmA zu)=wla)N=vPRH4x9Vy?{g@IJLFL`&Mye}7^eTO2>=0JCDU#_!RbGy&EJkaTD_v6wP zXba+*9l505k#}E^`D0Ts80ZU%0(4mNZa0k4Vk6Elox{PQs8iuqse&Gzq4tPd3+>X? zqzetP!6ui{;cl9~&|t`US711&E$k|!Cucav*}{~A6WSt38EA`iMw|_S&PaEz8_*u@ ziu$e&2fCtdxz1&b+7lg!I$Ht*(GH`hwp_T%m49L_bDbC{JnVxWyL=sfTn2(&A*yp+ z?{EU@2_%y2i)4A^_HgoUR|4ggxmPO>SM~+9$JJpIVIX)cG|Gp?giS5N-MX{k(HCTI zGqUbz_}HLiUqA#U$FBQ0-nD%JmkDmds_P+~?uK4^!=t^FQxHgXX$vd})MkwRW!kdl*XFd3GQW0Te`v(U z0{x+`tobqbks#*W>-2}vXAGeE(F4;W%#W@fuleb;S!-m3L8n)nUjgRU4sCwMr8A-7)&X@0~0b`vm+xo9M9peuwnY(PUX zd@BOSLPIb$UB9fWE2od<*O$|-&94B42p7z6R)Xd?E07x0i^8lxy8~5Y4EWGA9Wu<} zSfTuLbJUE4>=C~bBYjr8WM(?M>KjioD-%x(J0cx z8JVcFBdi2QC+W;`bs{du+JW^uuK)r5d2XUfQypgILd-1~5RBv9=j418J0G7Om=e@k zP38O0X~j6@XCp=#u^=Dej5s~t;iWKugd^F`C_%*-?*#hW!e(YmMxEl>x(8YJSrd^V zt3fz%y4yrVJIS;b&cvy@PDRKP+7 zI0se2?Urx$NU4iwIZLK8W<_*Ma5EAn8NLJsE?KU8g>)>1L;Ea4koH*~g;_D^RHoiE z$EIBo)rB(Tw4jPlqC+L8@gCN}9TeYb&N40l?lawE8pF7)LGcaegnfU?3HbfzftC%`9fHcpeAg}%I;aP$bA8iu0#QCSOpDFnXI6a6b8mE_cfd7SC(|e9?v|B*0-q{;yMxika4ld!Z00xV zRgwFCC;Dx;rn}|o8pYzeI*&v$u~|CS`MC$3*oiui_c?j_C;8FGzIDE67*|haSx;SA zQ)+_GE9%@HC%R;GM6KHVq6?YLGADW|Lr3bZo0~tomZ6vVqq|3^)uDyd!-9Z+;*2?J z#$0E{Jl~9B|BU&885mnSY+*n>Hsj>MoEfLQnB)2^4*2KJSfpl@I_d|W-JS+==V_0q%|wmgOTN|i~6F9oXUqL^-x$%Ixng2i>yfCSx9|WMNZcd?hi-K zSV*4-Vv*JFrsH#o$hq3_`4W{^0-u#mq3^V?Gcy%)R`}y*GMRIlU&U8lfIw%ZKdkaD zh3g#W*Lj<9J=;0mcNUDM67B%Aj=m44nGaPsC;H-RKLNLMl265Vo`=cEN%+d*b=ptZ}}=+lYEJITDxMtWW2I~6`t%%sXQ9_1x`sGb6)Qx@|f#I&Me=B zIlct7SkwZK3TMbc?SJFMHymVczeS?x2jQBzA?D2U$1l5=zQs`P<&V%cAIhD%Dd22y z;v3oD&Sf~|(Y`l3i)Owz=Dh60uXvU*{{$t)oZma~cfU-0#ZmFCf2HdWaJ?tOYW@+) zREXWL=aiTVQ6JhJRw3$8y927|Bt)-qUT0NbbK=_;GxeWYGGKndre6NoAB)b2qkJIldbpN%wGJWX&Pk)w`dD^!!{+Tb)^-W(zyz9GoHg~?| ztBl{r(cnDetBrqFSMXV1M$Oz4a~}7_n{Q@jUqw+Zx6<{`c$kl}&?p0DUK4Zf_s#k= zLmu$a_#g1idWiHnZTz3}#an+y{5&$aTD!ggm zKue6i2G`7c%be?d@$2@nwl^T3>-8|W(dUcba0C4g`U3F}bkX&G=;6a!H#cFB+^pO8 z0St}Xb^F?(n?sY>`VT@kAJ?_{kgp_uM-AOP~4+^ZN*fOA$+Jb3X3m zeF$DRIe(fEaQ+av@i4>x2t(_H^-^)r`CZY2;14;o9Sr1z6Tk6mj8_C3Df%W{ zA9PN1ideyRC+pFa&sS6bxs+|n)e7Cx%iaDyvcBI*v)$e zj_2s}uWZN4};V8!ICJq8*zm zBVX5!%WES~XvgMEh z^I1H!`QmyJbhcnb>9{JTCLK$uosl2t8<<)Z8PJZpaO8)a)Ybc9ksoQt zu0-VDxz4EiWaN46*u6CJW9?{I9{H}WZsUr`PqZU*TI4w{PU^~)k)LWu(<*lvaBuj+ zYW@rAWv!FR>SdZs$@kUEyK%d{;NR6N%=F|1|3kgP0Sng;)i76z1wT@+^2#9XpuUOE#!)<|rf8qy7gd47J5IGISi!09idSLjY*eYT!(s6=VXS{igd!-I^#25y4E%y; zX9(vdzFQNo;=0u5gJCRw8K)fHzo|yYmkhoJT;{(uakmdOfur#52pmXLG6mPAp%ZXk zu4Ws*Ik+N^MYx39p;DDW9^s|HRQp!u+!h(4?*qT*kv4u_8w#V=vP)n-2T*Xlt#e?90th!4Rb z*@HIOgF2at^08#m#9FA-x#BgGpmfF~a52THH{%ol1e3Eq250ibPx5@iKQU~(4toe4 zk=RM!nHijy#`p1j(hWGD$}SJ8+o06h?C#`TcJb08-NAEo2Zu}t7rGsc2G0GBddyb) zFS!r`3|YOoO!>+iKdCBe$qA8U#YttK9CjvPJK#iK`qK_!x(X{__N8Z6qC4{ z+v4-^A)iUL96^tvS;gytwA4+wo|?ufM}6J#v8Q(fmd@YHbIEl$YwL@((faN~qZiQn zPII*NouaMpWLn=c->rq({uXNcJ2h*6VQqhlwEdN8`&+E-Z)w*4CTROhYWrKF?Qfa3 zzsVl^TkhK5ls42t+uw3+e^ZiRV}HwC`FJNa#YObg7qCMU1q734 z{SnUOnQ!2%jVX!+#g{xs8`COnOlN3gIy-Ajx!RakYhyZ38&gUf)4AH1CLV80zgN%M zx<2RD^|!`HEh;G2Ch`~KQM%?4xLC!-9WgxnuB3@Phib092dGUWPkmpTMnap$`Pwut z$eKoB)-=jIrg5P*jdi0;<05Su8?&Y{Nt?!cZ5kJA)3`*N#=Ekn5!0q|sWy#rZ5kW2 zY0U7L#zNOLPH02DwP{?YP2&WQX)JV2W2VP678=utdrV`Y*EA-NWg45bXVDCh#*5lC)G2>ZS8(FGLSymw)mFV)sYSDfu_icI{M95at#|}3-3@O{#;)8k zt{5*m!DY_o$y3lbGjTd;fS$kMjJY`hUbU$$ zr%<5?q(%`{tMm%3(u7B)d9F%}GFGMU)+#MZfz40U;|&v~bgPZCcpf&`=4oxjeALEiV6BZ}t&MzNRvYih zYGeM})W*~?wDDf8jrqrGqa>@1wAMyR3Kbu%jRj+94b2z?kkWdAp?+KLb&n9eYq)+6Cov#Ga&0BTOc)%D&uQ@>{$fDu|Q#8FmMd7?1;%!0B=~b z*xKha?!3Ut)<6gEeDKogn6D>6Jk5&40UBJdigEKu2t1O0%%ldSO0zG(5L7&0UPzW!{+L2&Z zAk!1T>$d)Ya^{Y>{lS4;|HQ$NKd)_qKOfhE{;+@6aL_+@IH&@c0rAA84=7S$I4T&O zS^_y0aw%jBB~*a67(kdHvP-+B+^5eR$kYmg@(eUN_0^N^Pz?8N{wk3iUqiy`Mh zHbdSAxe>xG%r3~|kbcO|A+JHA80v8dHxkc>Tmh+pG(xU}+y=Q9(gW#*@RJYUhx`ZR zmyq8>{sM_&|@Le7Ak z2PubahU|niKw2O-LOu$)19A`Kvyg7cmmyC>VBG5ake@)f)xiBD?!h?Nsfa)(Lr#F4 z1ep(62w4tU4Jm_M45@@vLuw)QkPM^+auwui$h8pe+q@sr4*4+T7RarTLy$WlcSG)h zd>V2;22=OI6V{2Vd_c@@G>onYcqd61b9Zj#K0@RLBxA!k6& zfs{cuLEZz|0jY;v4Y?Nb5y(G6?ty#;@-XD95KI#4N08q`{si&kzGebsHslP*MUX9! zI>-UYMRKXRl90hGEI%!Yf7u|47hQ_>hsp7*IvH%l8x!} z*QYg>U%C!-?z)YtuAyc87%H8vudAwQXs%IJwcDD2t2b;|y(N9wx+~VEH*P6kn@+3d zrgY8TnueBU)v_;Bqjqm=O@o^1YML3;R8zG#UDvRqzHVo2i`rGQPwi=FuG`sAQ=O=9 zY}lz!$ac?))WvZ=RccBFN)kn6VU^sbtwhMIcCY(_F= zbCBJ#rHZke>Kb-3r>2_bnx?%q+HY4&-R_$7&YG5VL-VLe)pgBP+nTDipQo5TNIEKB z^PcUy>squIbE(?X)Px47Gj-MJmYsEO`rX?y={=e1Z7ntFn);gE*~mNgG*qEBZfZuV zZ*1JPCo>X>e$_}~6P{ase){}%YuB$ypSxw_+VrL8pMTlfjY{~kJ3M}CE7o1M@iLFc zW$P}uWbGP{$NJ04)}6mGy$M<$$!f!hCv~@@uBN`4%5O~Xs%xlL>o%_4kY0D`M&vj##r~PRC{PlFbG;fUE|)G zDur`;`!-lK9W=2~28p8i%vyBlH2yQZRKtQW=ytfVVQdYQW(;+tY-qL>f-jtnyXtBbhhL_#xw&q8 zeNCGFEjsPq6{f~@4J*=n5G&29LaD9;^)W8AhEcK&LBi23Gv!pFcI-0m&~HZBlx;1I zyX&e}EN9u6!E7y|Ks~Qe32^Brm1GHs;#eUZb@&iPd7AH*C1YI8%H%9+Sqh# z&MGt*g0>tTbY*i(6Qb4Wbam<7OumO_+Df`H<5s3Fy$4eR3?4mc4Xmzu*-}J80M5JI zOI?~%NHz{Y$HA3x+)JHlL|wXhyJ}`plT(wfY9P`zPfyQBZ+2@U0 zO+`RKXaO!enlbNgGtt!E9nHIGbxIop3`FB!R}wY{T`Bb1bJZ4127#TzjD68uT1ym6oPG zHQE+w!kAm!>f3QmjbLuro#9&N&NJ>5Waf@~J>gXAGc3MZRW)Yz*>zX1>S<=0*|x8~ z5uq%eOTF5*cUv8XU=w??KHah>gRzI{FY8l_^%=t)5p;$&{Z-qLwx&r3=o^FWH9PAX z(8uQXKwExR<%|K1q<854_IAcdNGp`VSDV zhfUvW0+S|wyU3?gq_^o}CLOmxc=Wj!6*lPy%++{jHT4eC-Tf;^|Lz5|NTOT{-m2Nd zM-7=$9BL_%MWvO;n$v$K1P@?5Y&@6nq5}_0_865$sW$D8JOZpTK0{{9QYXUA{8EsU zAvp5*0OM&fU**_iRK9$OFo%yH{4&R=IBbYHeAsQr%O8D&u{*5XZ!jr*>|n%%;Pi1v z;$jFg3ed#38^N?ZwnFGmK`J41w?TM7KY;|UY@e+g+lS3S9lsV=yui@$>v5$ndLi89 zz>&x9@V1+KS2sd%4Rqo9VMqal$44P_ry#c*cN?yE79B%3^0~vxFS!g|R_k=rpy^slT>+uJL6c{&ckh>|T zBU~rK1ztgpNgngvi#{XI0R5LDeu?p4W!woczng<2ya1ko9;N{ws_>cZE(f?x*0U&3g_caqXGyVp?-(f3}hS zR^M=Hf9jY1lbfyLcc0GGLD-=)aaMr`jho*b{f+CkT$x)#6*lVx#x`$&bi?MWX$ljB9MnJ842O#Y@_85CkSG}W zYjxSaW8ZQooks$~H(NH&CG^C>{4Uil*F6Jqke|+HgYjp1v`rlPT!c8*jay*efUZ7n zb8W0GHM861ZkpNqPrZ?*PrP%?-cgI)H15BdZ8o_zXP-mN8Ty!(X za{(WE0diXk+e@B(A?9cJ>sRCk>rwat!V-Ly`!m2|F6ih>HmzTeD5#Se8gE@A zZMx_MCR(N@)YLcEB-p=iof1VNojU^QMi!`CHl4Pyu^NTAi_XSL4~Yf3x??2cP#vWY zdQd0X=Xo%|ec_4a|4kiYVdK zcG-sXnspn}m#w{IjoY||39TYl#%<38I~|(sn#9R9>{-@HybviVX3qNU&4{9E_CiNV z=*8_9px#x&+~nQNc6VjR*^wQ!e_NG)`#aU#-_Gtj8yIsZS#OtT_nSwen7wsq6dN+x ziImH$PN8?Y=;Bs5p+~bA{aUR+IX2CG| zGhltF@sbq@S5xRLE_UZ#x?yd4!`jtrbiaGlU&?Z6>pZ6ZQIycxozeA#HmI1m#%=`wS8MUhLxLYFg?1-)htH=(sM#+ zXlbl%E*aILP(oH#=-rOTEsu<@;RkJ{Eoeq3=^EML&P%SQ0-bepDmU-Xu+>ua!Rpkk$VlN<4P zSLf=)?x@2vySt7+eRGKR@vcxe&fXR>fcx3d0cPZjS68IVS8rK=>FPCRJ=0;hHyASz zd*C*ov1-NEK?%$PRq)Q+?z`ubn!RR*9u;U_U}r5b(}$jLaC3cu zy|*5n3af*AukdEK*mfl!xtFGo+1$sRCHjpTL>2X&6f|6dn^CUKHa@%r>gA31io7ky zuK%8#rC7BC95VeMw*p%izTrjJcuh|fWc zqi$xVN25)R8jq2=e*1%rvxOj;?NiSOE8e0IDq$6^iVuT)pQO!KTb@w3`(noP94iOa z=tovNtaP%sIjUrd?BQLg;mEVIEfZ@*GCP_t+OT%xsK@1O*~nmFf`guYr0*^ngpci1 zm$aOg&#l*>&evKWxotsm{9(8w09&?IB zz?;pH*uWAi{6dYdV$CvNj@Gh_)oAh0Vhrl`bF|1z0JF(MuUYTFN-(Y?Z zfG4l_t79U3$gm$9cwubx^Y{sQ6?*NB7{3>Jk z8F2KlUv(SiCp>fT@l(F~Wd7t3_?Y2|=sRDxUkw{x177$r>cev3NA`h}h7W=#Rrpn< z;b+0YthlT?B6)(%fcW2e)ezcc*^DtDZ z`7DIzJ%{!i=2t-{KJVABh8e#VJmnbLZ^A9}l`+G=#_$)=KZ21&hrf<+|4V-T-i7g( z;iZWmFJXpE`1`;~P@JzhC%zr$fDm>wnkYA;TCxSDE zS0R2uZ$PygX8tkc-)^`D;ZyM?m=41Sz>}W{=uZK&eoQ|N>AOt$C&1BF0o7;ti{$la zpW%Nc^W)913#R`SIFbpdu;JIh)9|JKnBh3)7+(q6Z+Iox59O2@w(|k!q7sZb9?bVT zaPWzcp0~(-;K=fjo>RQxb{?_e36uUuh@ZD6q~DP-zMV%>Cj1qICsB!V4;O)J4cmF9 z)v%psx(wTSrpK_IXZj7>d1lbCooDR4V#7IMvVP;uGb>HFooC7nL*;s&sW5EknO4Iv z0X@(38MgCGzlS-`3>vocOxTS3@#dM8CVVMgT1@D{JRrE-F!g&n^l9fI%NY}X3DU>b zhg7R!ehqQr#*ls&N_~*SU^{Pp2<*dmSo=(TYaho9+j$ShrS)m&y%NLEAbqfMWL}zu zwJutVac{yca}8XMKI5V50ak+}cKCj97%yBCCj4XI&~QlGF5{DDgOeuwaqx`X9Q}^a zwrgT*NTp1;ZYQ|H@Q)C`=s-xn*Jb?qSc5}t(32-Wh5R}UpN8;R_<4k0!|w&-m$I_! z+0EcdheOKRot@tckvH?V^LwRXJHOlY%Cfa9JHNM?_;!BpF>L2^yM9@=c4p`InCUmm zc0IH6d%}d<`8{da&hHh5?fh=-jLiAH-Gtlu{ji5QzxNxq^SiYp%Y&Y9&hLfBPV9WW z(y*Q1tsU8Ln3~qFozG!vni<~xc0MmLZ0GZ&VLPAmdz6fC=X1MW+WDOG5yQ8`euEek^6VCZI-)2m>op0^@Z0Fl{PkPR`7|Oao?R;zJV>{pWn{YedLM1xBop0@YZ0Fmsm~ZX; zZ0FmQ3AgsnXYtvvIUcmD&x3>aj*Nrn!J!96=HH)#XW=0tW#TW#vwNr|n7!|?cG+ga zEw>xCcG+Rr+GU?%YnMZU!^Y069To~s7`Ap1yR_rP+T~#r zZtZfwa0%CC+MQrKE*MU`Oc*=O+MVE)hOHf@JmIv%G7oDzG;HnAjw5Slm7Z|gS;nxn zOFNFvLI3#<1+(`xTfu%zvUVIw9>;T)6X>DbfY!L@2GuKJt7xKSx3`xLUmrz-^g& z^y-=I3>!VMA9$328QOL(`-k`H#^m*UQAVT1K|AyI7ww2_`|GgiudwK^l<2Qk(OE9L@+kd?H<>SO3M&I#}`CEVf5(2-7z+9S)j`k~;SPuV-*yr~ z|4v4lR2n=lZV3KZvwb zLjGZ8oa@+%|Ty9 zX>ejF5u6d)8S?-6no?%{qz@TBb`3Io(ub3FJw(4}QK1?B_xnnN`62(wiQpvvbMT?_M^l7TebS;>OA1in~So(f)*uFW%N=}ssA?zO8-Xv zUyA-mo8G$rJ4W|E+I#5QRl$X5HVpt>a6529$p0H!z-`F$bX@#!|K`A|U<@kqLz%OK zVMN{n4a|fAeH*Pf&SuaN_g%ZnKY)fRe;0ZgmiQx@+h?vV#eXZO(!4|D%g|UjGCLSL zJ2;#DJP$qQe>X-%B3N`*Fmx6z4gEDMAO-L;il4*wm2!7FcV{ zKMx~mp|QyXvN_Hs;rEs{`I;KXD4)?rnJWEnYKf=8wjTOlv_vsqe;1mX=)Z8Z^{vp> zck$n??@edKL(VwXHwc}EW(Q~ae|K%70!v58|0%n0RJ_#?{ohzPc8n|>W3C4naYY<) zu_c(_m);Qa|ARZf|A(I6+sA9oNAc77x;5YYf2cKIFs&(7rKOAUSA);HEv{Nz*HBfz z2R}w4ze}}ucRhXyrnR(I@pEytHTC>ekm{Np&BjN6Q;1)^0Kdh-d-Q<%o&MjNGOAyh zn?@19&jpNi_}#C?9T()!e(QU5kYDc#jP@h>^*8&*^2Y<*7>ED3yG}QfUq8JZp8%iA zIo=aKe*M&Ue2`y%GkPq4`)%maVgBsSo%EkO+QX1P`vz_#cqeX)r+D;l@9X~wQ6^`< z&FJnYFct5lr+E6s-oNiP@y#QgN`QG>44LBTFZ&$8yY4BT{<8UO*Z$mna1Z+&z|VnA z@${Fy-+xN`kMx)IFN3{J*1v7&=3^cwRH^Z|Y_DHMK9f;`J@D}Wee@wd4+Lv%ba|M2 zTonw&!EkafgoouJ7spLs;t6N^4#B-1=66f2er`lK)a7yqR!DWK^yTso7$^~x}Tp^f0z2r?_DY#m2t>AjW z8Nn@rTLts;*52~l1RoUKF8F4_9fA)DJ}mey!JUHd72GBG0m0pZ9~Rsr_;JC#f}arF zC-@n`{eqtpJRtaa!N&x@Ab3#lOM-_4zan^8@auy0)+h|Zj^}{6-?7a5c(1+W3XTb$ zCb&>Af0M_XUqbL)!6kzA|D8hywth(wzFhE1!K(zP1g{ZXCV0Kza>0DJ<85Ds;H`oy z1y>8M6+#&do;KPFN65J{HUcp_09}wIv_+i04f*%*$ zEBFb)eS)76+%Nb!!2^Py7ko_c3xWp)za)4_@GF9c1-~vBn{=b>QU4eld>D=P7S^He z8&v*|U(CaCxC=eZ-(E|2nEMwc9_D^W(!-pJRtioDE)!fXxI%EH;99{M53`M}9;PbV z1h)(B5PVp0r{FHZ-5%y{XOD;ZioI8GpWuGM1A>nU9uz#}VSZL-Sg}`6UIf6r2)VCb(R1h2UeXG!FJ9?e8TI)Bf}i0piNO*Y?ax;ULqVOAIqT z?Rg=D2bnrqMZiI3n)L)6WXe_o4!YUD^#mMb_U{1#4l=8DGXV#gMR!7YkXfF!7t6iy z<6+s_gXLp7bR^vNw+*-bYuWavW!rz2ZGTy|{bSkohviDsAC@zM+XQzA?iAcDxL0t$ z;A4V^1Z$;w^cSO&J)97n6r2)VF1S)~MsSl?cPQl%Rdj~O3Qh^O?l?cPQl%RdjVOa z1y>5r2yPSHA-Gd;x8PpE{eq7P9ullyihBApW3zu;qnhXlL7EI!iSnE9wow*7*Wf>VOa1y>5r2yPSHA-Gd; zx8PpE{eq7P9ugdmjr1391qub12wo}JekXVOa1y>5r2yPSH zA-Gd;x8PpE{eq7P9ujOnqG{V3nomc01uqx8M({?#)q-0D9~68@@V$Z`7W{ujz* z$O{Fp61-mUR>Adx4|0qm}xJ|pH_$y4z`s%SMYMd zYXol;TrIdo@Ik?c1V7+m-furH_!+^^3w}xP>wA8^e;X()x z&J_$#5^yk#erpIgYs}Gwv+P!3)}P{PdLcb$22w?2bs>Z*+?8@I?eyy z(#-Z%vJp5KPUnoG@!@zWhIyWBoD@xRth-?4w; zS%)0|!_P)D_TMUrSXuA-`n|jLe`&_QV7H2Mf74l{a6L!<`@6|s|0=SGn)S0fg+{#E z*wMuQ-0S{;v937(DLpjSvTZxA`*!QA`MKlqeU^>H0Z zGZC1_N&UGEw$rA!{u9S3|F9{yRw&1&x5Z5ur~K3y<#T?q>23LQ$0^_L^R@|v+4Q#j z#5m=boAQ$;VhOICzifJ2{>E|2xBEVC)&BBv%5NK^{p=@M{u$$xZ=Z|p`$*G_Y&Yri&)tj`@Ly$!e5j7h&#B#`CveoFc=k9+32Io&1{Bjf&Mob=1*yXlwP8#)({ zE9+JGk2QZCGyPX-(v{#!leEpT_VWSw$vQDft@+2AcZxuP`hW0BKc@Gc#WTti^N)=C zAmWTw|I|ly^6UrfSbv(dtpC5lk9o>|j;w|!j;MM6/root // overlay: /proc//root/tmp/hsperfdata_*/ -// Returns "" if no file is found. -func findHsperfdata(pidDir string, nsPid int) string { - pattern := filepath.Join(pidDir, "root", "tmp", "hsperfdata_*", strconv.Itoa(nsPid)) - matches, err := filepath.Glob(pattern) - if err != nil || len(matches) == 0 { - return "" +// Returns "" if no file is found. The error is non-nil only when the lookup +// was refused (EACCES/EPERM) rather than finding nothing: filepath.Glob drops +// I/O errors, so without this check a denial (for example SELinux MCS +// isolation between containers) is indistinguishable from a JVM that writes +// no hsperfdata. The path result is "" in both cases. +func findHsperfdata(pidDir string, nsPid int) (string, error) { + tmpDir := filepath.Join(pidDir, "root", "tmp") + pid := strconv.Itoa(nsPid) + if matches, err := filepath.Glob(filepath.Join(tmpDir, "hsperfdata_*", pid)); err == nil && len(matches) > 0 { + return matches[0], nil + } + + // No match. Re-walk the two directory levels Glob read, keeping only + // permission errors; anything else (missing /tmp, exited process) is the + // ordinary "not found". + f, err := os.Open(tmpDir) + if err != nil { + return "", permissionError(err) + } + _, err = f.Readdirnames(1) + _ = f.Close() + if err != nil && !errors.Is(err, io.EOF) { + return "", permissionError(err) + } + userDirs, _ := filepath.Glob(filepath.Join(tmpDir, "hsperfdata_*")) + for _, dir := range userDirs { + if _, err := os.Lstat(filepath.Join(dir, pid)); err != nil && errors.Is(err, fs.ErrPermission) { + return "", err + } + } + return "", nil +} + +// permissionError returns err if it is a permission denial, else nil. +func permissionError(err error) error { + if errors.Is(err, fs.ErrPermission) { + return err } - return matches[0] + return nil +} + +// hsperfDeniedReporterMaxSeen bounds the dedupe set. When it fills, the set is +// cleared, so a long-lived nodemon logs a still-denied container again at +// most once per that many distinct containers. +const hsperfDeniedReporterMaxSeen = 1024 + +// hsperfDeniedReporter logs, once per container, that a JVM's hsperfdata could +// not be read because access to the container's /tmp was refused. Discovery +// runs every collection tick, so logging every occurrence would repeat the same +// line for each JVM every cycle. A nil *hsperfDeniedReporter discards reports. +type hsperfDeniedReporter struct { + log logr.Logger + + mu sync.Mutex + seen map[string]struct{} +} + +func newHsperfDeniedReporter(log logr.Logger) *hsperfDeniedReporter { + return &hsperfDeniedReporter{log: log, seen: make(map[string]struct{})} +} + +// report logs err for e unless this container (or, without a container ID, +// this host PID) was already reported. It returns whether it logged. +func (r *hsperfDeniedReporter) report(e procEntry, err error) bool { + if r == nil { + return false + } + key := e.ContainerID + if key == "" { + key = "pid:" + strconv.Itoa(e.PidHost) + } + + r.mu.Lock() + if _, ok := r.seen[key]; ok { + r.mu.Unlock() + return false + } + if len(r.seen) >= hsperfDeniedReporterMaxSeen { + clear(r.seen) + } + r.seen[key] = struct{}{} + r.mu.Unlock() + + r.log.Info("Permission denied reading a JVM's /tmp through /proc//root; no heap metrics for it. "+ + "On SELinux-enforcing nodes nodemon needs seLinuxOptions.level s0:c0.c1023", + "pid", e.PidHost, "containerID", e.ContainerID, "error", err.Error()) + return true } // ParseJVMFlags parses JVM memory and container-awareness flags from a process cmdline string. diff --git a/internal/nodemon/jvm_hsperfdata_access_test.go b/internal/nodemon/jvm_hsperfdata_access_test.go new file mode 100644 index 00000000..34beb8ba --- /dev/null +++ b/internal/nodemon/jvm_hsperfdata_access_test.go @@ -0,0 +1,187 @@ +package nodemon + +import ( + "io/fs" + "os" + "path/filepath" + "strconv" + "strings" + "sync" + "testing" + + "github.com/go-logr/logr" + "github.com/go-logr/logr/funcr" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +// skipIfRoot skips tests that rely on chmod 000 denying access: root bypasses +// file mode checks, so the denial cannot be reproduced there. +func skipIfRoot(t *testing.T) { + t.Helper() + if os.Geteuid() == 0 { + t.Skip("running as root: chmod 000 does not deny access") + } +} + +// chmodRestored sets mode on path and restores 0o755 on cleanup so +// t.TempDir can remove the tree. +func chmodRestored(t *testing.T, path string, mode os.FileMode) { + t.Helper() + require.NoError(t, os.Chmod(path, mode)) + t.Cleanup(func() { _ = os.Chmod(path, 0o755) }) +} + +// capturingLogger returns a logger that records every Info message. +func capturingLogger() (logr.Logger, func() []string) { + var mu sync.Mutex + var lines []string + log := funcr.New(func(prefix, args string) { + mu.Lock() + defer mu.Unlock() + lines = append(lines, prefix+" "+args) + }, funcr.Options{}) + return log, func() []string { + mu.Lock() + defer mu.Unlock() + return append([]string(nil), lines...) + } +} + +func TestFindHsperfdata_PermissionDeniedVsNotFound(t *testing.T) { + const nsPid = 7 + tests := []struct { + name string + // setup builds /root as needed and returns nothing. + setup func(t *testing.T, pidDir string) + needsChmod bool + wantFound bool + wantDenied bool + }{ + { + name: "found", + setup: func(t *testing.T, pidDir string) { + dir := filepath.Join(pidDir, "root", "tmp", "hsperfdata_app") + require.NoError(t, os.MkdirAll(dir, 0o755)) + require.NoError(t, os.WriteFile(filepath.Join(dir, strconv.Itoa(nsPid)), nil, 0o644)) + }, + wantFound: true, + }, + { + name: "no /tmp is not found, not denied", + setup: func(t *testing.T, pidDir string) { + require.NoError(t, os.MkdirAll(filepath.Join(pidDir, "root"), 0o755)) + }, + }, + { + name: "empty /tmp is not found, not denied", + setup: func(t *testing.T, pidDir string) { + require.NoError(t, os.MkdirAll(filepath.Join(pidDir, "root", "tmp"), 0o755)) + }, + }, + { + name: "unreadable /tmp is denied", + setup: func(t *testing.T, pidDir string) { + tmp := filepath.Join(pidDir, "root", "tmp") + dir := filepath.Join(tmp, "hsperfdata_app") + require.NoError(t, os.MkdirAll(dir, 0o755)) + require.NoError(t, os.WriteFile(filepath.Join(dir, strconv.Itoa(nsPid)), nil, 0o644)) + chmodRestored(t, tmp, 0) + }, + needsChmod: true, + wantDenied: true, + }, + { + name: "unsearchable hsperfdata user dir is denied", + setup: func(t *testing.T, pidDir string) { + dir := filepath.Join(pidDir, "root", "tmp", "hsperfdata_app") + require.NoError(t, os.MkdirAll(dir, 0o755)) + require.NoError(t, os.WriteFile(filepath.Join(dir, strconv.Itoa(nsPid)), nil, 0o644)) + chmodRestored(t, dir, 0) + }, + needsChmod: true, + wantDenied: true, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + if tc.needsChmod { + skipIfRoot(t) + } + pidDir := filepath.Join(t.TempDir(), "100") + tc.setup(t, pidDir) + + path, err := findHsperfdata(pidDir, nsPid) + + if tc.wantFound { + assert.Equal(t, filepath.Join(pidDir, "root", "tmp", "hsperfdata_app", strconv.Itoa(nsPid)), path) + } else { + // A denial must not change the path result. + assert.Empty(t, path) + } + if tc.wantDenied { + assert.ErrorIs(t, err, fs.ErrPermission) + } else { + assert.NoError(t, err) + } + }) + } +} + +func TestHsperfDeniedReporter_LogsOncePerContainer(t *testing.T) { + log, lines := capturingLogger() + r := newHsperfDeniedReporter(log) + + a := procEntry{PidHost: 1, ContainerID: "aaa"} + b := procEntry{PidHost: 2, ContainerID: "bbb"} + noID := procEntry{PidHost: 3} + + assert.True(t, r.report(a, fs.ErrPermission)) + assert.False(t, r.report(a, fs.ErrPermission), "same container must not log again") + assert.False(t, r.report(procEntry{PidHost: 9, ContainerID: "aaa"}, fs.ErrPermission), + "another JVM in an already-reported container must not log again") + assert.True(t, r.report(b, fs.ErrPermission)) + assert.True(t, r.report(noID, fs.ErrPermission)) + assert.False(t, r.report(noID, fs.ErrPermission), "without a container ID, dedupe falls back to host PID") + + assert.Len(t, lines(), 3) + + var nilReporter *hsperfDeniedReporter + assert.False(t, nilReporter.report(a, fs.ErrPermission), "a nil reporter discards reports") +} + +func TestHsperfDeniedReporter_BoundedSeenSet(t *testing.T) { + r := newHsperfDeniedReporter(logr.Discard()) + for i := range hsperfDeniedReporterMaxSeen + 10 { + r.report(procEntry{ContainerID: "c" + strconv.Itoa(i)}, fs.ErrPermission) + } + assert.LessOrEqual(t, len(r.seen), hsperfDeniedReporterMaxSeen) +} + +func TestDiscoverRuntimeProcesses_DeniedHsperfdataIsReportedOnce(t *testing.T) { + skipIfRoot(t) + + procRoot := t.TempDir() + containerID := buildFakeProcTree(t, procRoot, 800, javaComm) + chmodRestored(t, filepath.Join(procRoot, "800", "root", "tmp"), 0) + + log, lines := capturingLogger() + denied := newHsperfDeniedReporter(log) + + for range 2 { // two collection ticks + javaProcs, runtimeProcs, err := discoverRuntimeProcesses(procRoot, probeRuntimeProcess, denied) + require.NoError(t, err) + + // Return behaviour is unchanged: no heap metrics, but the JVM is still + // reported as a Java runtime. + assert.Empty(t, javaProcs) + require.Len(t, runtimeProcs, 1) + assert.Equal(t, runtimeNameJava, runtimeProcs[0].Runtime) + } + + got := lines() + require.Len(t, got, 1, "a denial is logged once, not once per tick") + assert.True(t, strings.Contains(got[0], containerID), got[0]) + assert.True(t, strings.Contains(got[0], "Permission denied"), got[0]) +} diff --git a/internal/nodemon/node_storage_test.go b/internal/nodemon/node_storage_test.go new file mode 100644 index 00000000..53066d26 --- /dev/null +++ b/internal/nodemon/node_storage_test.go @@ -0,0 +1,133 @@ +package nodemon + +import ( + "encoding/json" + "testing" + "time" +) + +const storageSummaryJSON = `{ + "node": { + "nodeName": "node-a", + "cpu": {"time": "2026-08-19T16:20:03Z", "usageNanoCores": 1500000000}, + "memory": {"workingSetBytes": 2147483648}, + "fs": { + "time": "2026-08-19T16:20:03Z", + "availableBytes": 94501658624, + "capacityBytes": 107294470144, + "usedBytes": 12792811520, + "inodesFree": 52254168, + "inodes": 52422640, + "inodesUsed": 168472 + }, + "runtime": { + "imageFs": { + "time": "2026-08-19T16:20:01Z", + "availableBytes": 94501658624, + "capacityBytes": 107294470144, + "usedBytes": 6701690880, + "inodesFree": 52254168, + "inodes": 52422640, + "inodesUsed": 78769 + } + } + }, + "pods": [] +}` + +func TestBuildNodeMetrics_Storage(t *testing.T) { + var stats StatsSummary + if err := json.Unmarshal([]byte(storageSummaryJSON), &stats); err != nil { + t.Fatalf("decode failed: %v", err) + } + + u := &UnifiedExporter{nodeName: "node-a", nodeNetRates: NewRateCalculator()} + got := u.buildNodeMetrics(&stats, nil, time.Now()) + + if got.NodefsUsedBytes != 12792811520 { + t.Errorf("NodefsUsedBytes = %d, want 12792811520", got.NodefsUsedBytes) + } + if got.NodefsCapacityBytes != 107294470144 { + t.Errorf("NodefsCapacityBytes = %d, want 107294470144", got.NodefsCapacityBytes) + } + if got.NodefsAvailableBytes != 94501658624 { + t.Errorf("NodefsAvailableBytes = %d, want 94501658624", got.NodefsAvailableBytes) + } + if got.NodefsInodesUsed != 168472 { + t.Errorf("NodefsInodesUsed = %d, want 168472", got.NodefsInodesUsed) + } + if got.NodefsInodesTotal != 52422640 { + t.Errorf("NodefsInodesTotal = %d, want 52422640", got.NodefsInodesTotal) + } + if got.ImagefsUsedBytes != 6701690880 { + t.Errorf("ImagefsUsedBytes = %d, want 6701690880", got.ImagefsUsedBytes) + } + if !got.ImagefsSharedDevice { + t.Error("ImagefsSharedDevice = false, want true (identical capacity and inode totals)") + } +} + +// A node with no fs section at all must yield zeros, not a nil-pointer panic. +func TestBuildNodeMetrics_StorageAbsent(t *testing.T) { + var stats StatsSummary + const noFS = `{"node": {"nodeName": "node-a", "cpu": {"usageNanoCores": 1}}, "pods": []}` + if err := json.Unmarshal([]byte(noFS), &stats); err != nil { + t.Fatalf("decode failed: %v", err) + } + + u := &UnifiedExporter{nodeName: "node-a", nodeNetRates: NewRateCalculator()} + got := u.buildNodeMetrics(&stats, nil, time.Now()) + + if got.NodefsCapacityBytes != 0 || got.ImagefsUsedBytes != 0 || got.ImagefsSharedDevice { + t.Errorf("expected zero-valued storage for a summary with no fs section, got %+v", got) + } +} + +func TestImagefsSharesDeviceWithNodefs(t *testing.T) { + u64 := func(v uint64) *uint64 { return &v } + + tests := []struct { + name string + nodefs, imagefs *FsStats + want bool + }{ + { + name: "same capacity and inodes is shared", + nodefs: &FsStats{CapacityBytes: u64(100), Inodes: u64(50)}, + imagefs: &FsStats{CapacityBytes: u64(100), Inodes: u64(50)}, + want: true, + }, + { + name: "different capacity is separate", + nodefs: &FsStats{CapacityBytes: u64(100), Inodes: u64(50)}, + imagefs: &FsStats{CapacityBytes: u64(200), Inodes: u64(50)}, + want: false, + }, + { + name: "same capacity but different inodes is separate", + nodefs: &FsStats{CapacityBytes: u64(100), Inodes: u64(50)}, + imagefs: &FsStats{CapacityBytes: u64(100), Inodes: u64(70)}, + want: false, + }, + { + name: "missing imagefs is not shared", + nodefs: &FsStats{CapacityBytes: u64(100), Inodes: u64(50)}, + imagefs: nil, + want: false, + }, + { + name: "zero capacity is not shared even when equal", + nodefs: &FsStats{CapacityBytes: u64(0), Inodes: u64(0)}, + imagefs: &FsStats{CapacityBytes: u64(0), Inodes: u64(0)}, + want: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := imagefsSharesDeviceWithNodefs(tt.nodefs, tt.imagefs); got != tt.want { + t.Errorf("got %v, want %v", got, tt.want) + } + }) + } +} diff --git a/internal/nodemon/runtime_collector.go b/internal/nodemon/runtime_collector.go index 7a544292..a3e4779d 100644 --- a/internal/nodemon/runtime_collector.go +++ b/internal/nodemon/runtime_collector.go @@ -57,6 +57,9 @@ type RuntimeCollector struct { index *PodContainerIndex procRoot string log logr.Logger + // hsperfDenied logs, once per container, JVMs whose /tmp nodemon was + // refused access to (see findHsperfdata). + hsperfDenied *hsperfDeniedReporter mu sync.RWMutex // versionCache caches version resolution keyed by containerID+"/"+runtime, @@ -88,11 +91,13 @@ type RuntimeCollector struct { // (or be started concurrently) — RuntimeCollector only reads from it. procRoot // defaults to "/proc". func NewRuntimeCollector(nodeName string, index *PodContainerIndex, log logr.Logger) *RuntimeCollector { + log = log.WithName("runtime-collector") return &RuntimeCollector{ nodeName: nodeName, index: index, procRoot: "/proc", - log: log.WithName("runtime-collector"), + log: log, + hsperfDenied: newHsperfDeniedReporter(log), versionCache: make(map[string]versionResolveInfo), probeCache: make(map[string]probeCacheEntry), cachedErr: errMetricsNotYetCollected, @@ -224,7 +229,7 @@ func (c *RuntimeCollector) collect(ctx context.Context) (RuntimeMetrics, error) c.mu.Unlock() probe, nextProbeCache := newMemoizedProbe(prevProbeCache, probeRuntimeProcess) - javaProcs, runtimeProcs, err := discoverRuntimeProcesses(c.procRoot, probe) + javaProcs, runtimeProcs, err := discoverRuntimeProcesses(c.procRoot, probe, c.hsperfDenied) if err != nil { return RuntimeMetrics{}, fmt.Errorf("discovering runtime processes: %w", err) } diff --git a/internal/nodemon/runtime_detectors.go b/internal/nodemon/runtime_detectors.go index 1c5e94da..7578981b 100644 --- a/internal/nodemon/runtime_detectors.go +++ b/internal/nodemon/runtime_detectors.go @@ -41,6 +41,12 @@ const ( runtimeNameDeno = "deno" runtimeNameBun = "bun" runtimeNameRust = "rust" + // runtimeNameJava is only ever reported for a JVM that has no hsperfdata + // file — see the processKindJava branch in discoverRuntimeProcesses. A JVM + // with hsperfdata goes to the dedicated JVMMetric pipeline instead and is + // deliberately absent from this bucket, so the two never double-report the + // same process. + runtimeNameJava = "java" ) // version sources reported in RuntimeProcessMetric.VersionSource. @@ -85,6 +91,14 @@ var ( // embeds in panic/backtrace metadata — the fallback detection signal for // binaries whose .comment section was stripped. rustcPathMarkerRe = regexp.MustCompile(`(/rustc/[0-9a-f]{40}/)`) + // javaPathVersionRe extracts a JDK version from a filesystem path that names + // one, e.g. /usr/lib/jvm/java-21-openjdk/bin/java, /usr/lib/jvm/jdk-17.0.9, + // /usr/lib/jvm/zulu11.60.19-ca-jdk11.0.17. Distro and vendor layouts all put + // the major (and often the full) version in the JDK directory name. Used only + // for JVMs with no hsperfdata, where java.property.java.version is + // unavailable; a path that carries no version (Temurin's /opt/java/openjdk) + // simply doesn't match and the caller falls through. + javaPathVersionRe = regexp.MustCompile(`(?:java|jdk|jre)-?(\d+(?:[._]\d+)*)`) // graalVMVersionRe best-effort matches the GraalVM release string embedded in // native-image binaries, e.g. "GraalVM CE 22.3.1" or "Oracle GraalVM 21+35.1". graalVMVersionRe = regexp.MustCompile(`GraalVM (?:[A-Z]{2} )?(\d+(?:\.\d+)*(?:\+[\d.]+)?)`) @@ -148,6 +162,37 @@ func isNodeProcess(comm, cmdline string) bool { return filepath.Base(parts[0]) == nodeBinName } +// resolveJavaVersion resolves a JVM's version without hsperfdata, for the +// processes the dedicated JVM pipeline has to skip (no perf file to read +// java.property.java.version from). Three passive sources, in descending order +// of precision: +// +// JAVA_VERSION env — set by eclipse-temurin, the Alpine openjdk packages and +// images built on them (openzipkin/zipkin-slim among them), and carries the +// full version ("21.0.7_p6"). +// /proc//exe — distro JDK layouts name the version in the directory +// (/usr/lib/jvm/java-21-openjdk/bin/java), usually major-only. +// JAVA_HOME env — same shape, for images whose exe path is version-free +// (Temurin's /opt/java/openjdk/bin/java). +// +// Nothing is executed: `java -version` would fork a second JVM per scrape. +func resolveJavaVersion(pidDir string) (version, source string) { + if v := envVersion(pidDir, "JAVA_VERSION"); v != "" { + return v, runtimeVersionSourceEnv + } + if exe := resolveExePath(pidDir); exe != "" { + if m := javaPathVersionRe.FindStringSubmatch(exe); len(m) == 2 { + return m[1], runtimeVersionSourceExePath + } + } + if home := envVersion(pidDir, "JAVA_HOME"); home != "" { + if m := javaPathVersionRe.FindStringSubmatch(home); len(m) == 2 { + return m[1], runtimeVersionSourceEnv + } + } + return "", "" +} + // resolveNodeVersion best-effort resolves the Node.js version: NODE_VERSION env // var (official Docker Hub node images), else a streamed scan of the on-disk // binary for the release-URL string Node embeds for its own process.release @@ -211,10 +256,13 @@ var extraRuntimeDetectors = []runtimeDetector{ } // runtimeNameForKind maps a processKind to the runtime name reported to the -// control plane. Covers every runtime except java, which has its own metric -// type and pipeline. +// control plane. java is included for the hsperfdata-less fallback only — a JVM +// with hsperfdata is reported through its own metric type and pipeline, and +// never reaches this bucket. func runtimeNameForKind(kind processKind) string { switch kind { + case processKindJava: + return runtimeNameJava case processKindNode: return runtimeNameNodeJS case processKindDotnet: @@ -241,6 +289,8 @@ func runtimeNameForKind(kind processKind) string { // resolveRuntimeVersion dispatches to the per-runtime passive version resolver. func resolveRuntimeVersion(kind processKind, pidDir string) (version, source string) { switch kind { + case processKindJava: + return resolveJavaVersion(pidDir) case processKindGo: return resolveGoVersion(pidDir) case processKindGraalVM: diff --git a/internal/nodemon/runtime_detectors_test.go b/internal/nodemon/runtime_detectors_test.go index ba8d4270..a6328f44 100644 --- a/internal/nodemon/runtime_detectors_test.go +++ b/internal/nodemon/runtime_detectors_test.go @@ -482,3 +482,52 @@ func TestProbeRust_CommentWithoutRustcIsNotRust(t *testing.T) { makeELFWithComment(t, pidDir, "GCC: (GNU) 12.2.0\x00") assert.Equal(t, processKindUnknown, probeRuntimeProcess(procEntry{PidDir: pidDir, Comm: "nginx"})) } + +// TestResolveJavaVersion covers the fallback path taken when a JVM has no +// hsperfdata file (readOnlyRootFilesystem, -XX:-UsePerfData): the dedicated JVM +// pipeline can't read java.property.java.version, so the generic bucket +// resolves it passively instead. +func TestResolveJavaVersion(t *testing.T) { + t.Run("from env", func(t *testing.T) { + pidDir := t.TempDir() + // openzipkin/zipkin-slim and the eclipse-temurin images both set this. + writePidFile(t, pidDir, "environ", []byte("PATH=/usr/bin\x00JAVA_VERSION=21.0.7_p6\x00")) + v, src := resolveJavaVersion(pidDir) + assert.Equal(t, "21.0.7_p6", v) + assert.Equal(t, runtimeVersionSourceEnv, src) + }) + + t.Run("from exe path", func(t *testing.T) { + pidDir := t.TempDir() + require.NoError(t, os.Symlink("/usr/lib/jvm/java-21-openjdk/bin/java", filepath.Join(pidDir, "exe"))) + v, src := resolveJavaVersion(pidDir) + assert.Equal(t, "21", v) + assert.Equal(t, runtimeVersionSourceExePath, src) + }) + + t.Run("from JAVA_HOME when exe path carries no version", func(t *testing.T) { + pidDir := t.TempDir() + // Temurin's exe path (/opt/java/openjdk/bin/java) has no version in it. + require.NoError(t, os.Symlink("/opt/java/openjdk/bin/java", filepath.Join(pidDir, "exe"))) + writePidFile(t, pidDir, "environ", []byte("JAVA_HOME=/usr/lib/jvm/jdk-17.0.9\x00")) + v, src := resolveJavaVersion(pidDir) + assert.Equal(t, "17.0.9", v) + assert.Equal(t, runtimeVersionSourceEnv, src) + }) + + t.Run("unresolvable", func(t *testing.T) { + pidDir := t.TempDir() + writePidFile(t, pidDir, "environ", []byte("PATH=/usr/bin\x00")) + v, src := resolveJavaVersion(pidDir) + assert.Empty(t, v) + assert.Empty(t, src) + }) + + t.Run("dispatched by resolveRuntimeVersion", func(t *testing.T) { + pidDir := t.TempDir() + writePidFile(t, pidDir, "environ", []byte("JAVA_VERSION=11.0.22\x00")) + v, src := resolveRuntimeVersion(processKindJava, pidDir) + assert.Equal(t, "11.0.22", v) + assert.Equal(t, runtimeVersionSourceEnv, src) + }) +} diff --git a/internal/nodemon/runtime_discovery.go b/internal/nodemon/runtime_discovery.go index a5d514c5..231b3e96 100644 --- a/internal/nodemon/runtime_discovery.go +++ b/internal/nodemon/runtime_discovery.go @@ -21,32 +21,46 @@ func classifyRuntimeProcess(comm, cmdline string) processKind { // per-runtime discovery would incur. probe classifies containerized processes // that comm/cmdline couldn't (callers pass probeRuntimeProcess or a memoized // wrapper around it). +// denied, if non-nil, is told about JVMs whose hsperfdata could not be read +// because access to the container's /tmp was refused. // Returns nil slices and nil error if procRoot does not exist (non-Linux hosts). -func discoverRuntimeProcesses(procRoot string, probe func(e procEntry) processKind) (javaProcs []JavaProcess, runtimeProcs []RuntimeProcess, err error) { +func discoverRuntimeProcesses(procRoot string, probe func(e procEntry) processKind, denied *hsperfDeniedReporter) (javaProcs []JavaProcess, runtimeProcs []RuntimeProcess, err error) { entries, err := walkProcEntriesProbed(procRoot, classifyRuntimeProcess, probe) if err != nil { return nil, nil, err } for _, e := range entries { - switch e.Kind { - case processKindJava: - if jp, ok := javaProcessFromEntry(e); ok { + if e.Kind == processKindUnknown { + // walkProcEntriesProbed already filters these out; unreachable. + continue + } + + if e.Kind == processKindJava { + if jp, ok := javaProcessFromEntry(e, denied); ok { javaProcs = append(javaProcs, jp) + continue } - case processKindUnknown: - // walkProcEntriesProbed already filters these out; unreachable. - default: - runtimeProcs = append(runtimeProcs, RuntimeProcess{ - Kind: e.Kind, - Runtime: runtimeNameForKind(e.Kind), - PidHost: e.PidHost, - PidNS: e.PidNS, - ContainerID: e.ContainerID, - CmdLine: e.CmdLine, - PidDir: e.PidDir, - }) + // No hsperfdata file. The JVM is running fine — HotSpot silently + // falls back to non-shared perf memory when it cannot create + // /tmp/hsperfdata_/, which is what happens under + // `readOnlyRootFilesystem: true` with no writable volume at /tmp + // (and under an explicit -XX:-UsePerfData / -XX:+PerfDisableSharedMem). + // Heap metrics are genuinely unavailable, but the process is still a + // Java workload, so report it in the generic bucket instead of + // dropping it from both surfaces — otherwise the container looks + // like it runs no known runtime at all. } + + runtimeProcs = append(runtimeProcs, RuntimeProcess{ + Kind: e.Kind, + Runtime: runtimeNameForKind(e.Kind), + PidHost: e.PidHost, + PidNS: e.PidNS, + ContainerID: e.ContainerID, + CmdLine: e.CmdLine, + PidDir: e.PidDir, + }) } return javaProcs, runtimeProcs, nil diff --git a/internal/nodemon/runtime_discovery_test.go b/internal/nodemon/runtime_discovery_test.go index c0517e54..758d3edd 100644 --- a/internal/nodemon/runtime_discovery_test.go +++ b/internal/nodemon/runtime_discovery_test.go @@ -83,7 +83,7 @@ func TestDiscoverRuntimeProcesses_SingleWalkBucketsAllKinds(t *testing.T) { require.NoError(t, err) require.NoError(t, os.Symlink(self, filepath.Join(procRoot, "400", "exe"))) - javaProcs, runtimeProcs, err := discoverRuntimeProcesses(procRoot, probeRuntimeProcess) + javaProcs, runtimeProcs, err := discoverRuntimeProcesses(procRoot, probeRuntimeProcess, nil) require.NoError(t, err) require.Len(t, javaProcs, 1) @@ -106,7 +106,7 @@ func TestDiscoverRuntimeProcesses_UnprobeableUnknownIsDropped(t *testing.T) { procRoot := t.TempDir() buildFakeProcTree(t, procRoot, 500, "someapp") // no exe symlink: probe fails - javaProcs, runtimeProcs, err := discoverRuntimeProcesses(procRoot, probeRuntimeProcess) + javaProcs, runtimeProcs, err := discoverRuntimeProcesses(procRoot, probeRuntimeProcess, nil) require.NoError(t, err) assert.Empty(t, javaProcs) assert.Empty(t, runtimeProcs) @@ -114,8 +114,57 @@ func TestDiscoverRuntimeProcesses_UnprobeableUnknownIsDropped(t *testing.T) { func TestDiscoverRuntimeProcesses_NoMatches(t *testing.T) { procRoot := t.TempDir() - javaProcs, runtimeProcs, err := discoverRuntimeProcesses(procRoot, probeRuntimeProcess) + javaProcs, runtimeProcs, err := discoverRuntimeProcesses(procRoot, probeRuntimeProcess, nil) require.NoError(t, err) assert.Empty(t, javaProcs) assert.Empty(t, runtimeProcs) } + +// buildFakeJavaProcTreeWithoutHsperfdata builds a Java /proc entry whose +// container has no hsperfdata file — what a JVM looks like when it could not +// write to /tmp (readOnlyRootFilesystem with no writable /tmp volume) or was +// started with -XX:-UsePerfData. +func buildFakeJavaProcTreeWithoutHsperfdata(t *testing.T, procRoot string, pid int) (containerID string) { + t.Helper() + containerID = buildFakeProcTree(t, procRoot, pid, javaComm) + require.NoError(t, os.RemoveAll(filepath.Join(procRoot, strconv.Itoa(pid), "root", "tmp"))) + return containerID +} + +func TestDiscoverRuntimeProcesses_JavaWithoutHsperfdataFallsBackToRuntimeBucket(t *testing.T) { + procRoot := t.TempDir() + noPerfID := buildFakeJavaProcTreeWithoutHsperfdata(t, procRoot, 600) + + javaProcs, runtimeProcs, err := discoverRuntimeProcesses(procRoot, probeRuntimeProcess, nil) + require.NoError(t, err) + + // No hsperfdata means no heap metrics are possible, so it must not appear + // in the JVM bucket... + assert.Empty(t, javaProcs) + + // ...but the process must still be reported as a Java runtime rather than + // disappearing from both surfaces. + require.Len(t, runtimeProcs, 1) + assert.Equal(t, runtimeNameJava, runtimeProcs[0].Runtime) + assert.Equal(t, processKindJava, runtimeProcs[0].Kind) + assert.Equal(t, noPerfID, runtimeProcs[0].ContainerID) + assert.NotEmpty(t, runtimeProcs[0].CmdLine) +} + +func TestDiscoverRuntimeProcesses_JavaWithHsperfdataStaysOutOfRuntimeBucket(t *testing.T) { + procRoot := t.TempDir() + perfID := buildFakeProcTree(t, procRoot, 700, javaComm) + + javaProcs, runtimeProcs, err := discoverRuntimeProcesses(procRoot, probeRuntimeProcess, nil) + require.NoError(t, err) + + // A JVM with hsperfdata keeps the richer dedicated pipeline and must not be + // double-reported in the generic bucket. + require.Len(t, javaProcs, 1) + assert.Equal(t, perfID, javaProcs[0].ContainerID) + assert.Empty(t, runtimeProcs) +} + +func TestRuntimeNameForKind_Java(t *testing.T) { + assert.Equal(t, runtimeNameJava, runtimeNameForKind(processKindJava)) +} diff --git a/internal/nodemon/runtime_types.go b/internal/nodemon/runtime_types.go index c06de0ee..98102265 100644 --- a/internal/nodemon/runtime_types.go +++ b/internal/nodemon/runtime_types.go @@ -8,7 +8,13 @@ import "time" // and payload-shape reasons. Scoped to existence + version, same as Node.js. type RuntimeProcessMetric struct { // Runtime is the detected runtime name: "dotnet" | "go" | - // "graalvm-native-image" | "python" | "ruby" | "deno" | "bun". + // "graalvm-native-image" | "python" | "ruby" | "deno" | "bun" | "java". + // + // "java" appears here only for a JVM with no hsperfdata file (it cannot + // write /tmp/hsperfdata_/ — readOnlyRootFilesystem with no + // writable /tmp, or -XX:-UsePerfData). Such a process has no heap metrics + // to report, so it is surfaced as a plain runtime detection rather than + // dropped; every other JVM goes to JVMMetric instead and never appears here. Runtime string `json:"runtime"` NodeName string `json:"node_name"` diff --git a/internal/nodemon/scraper.go b/internal/nodemon/scraper.go index 1a5e9f70..d8a74b54 100644 --- a/internal/nodemon/scraper.go +++ b/internal/nodemon/scraper.go @@ -77,7 +77,9 @@ func (s *scraper) Scrape(ctx context.Context, urls []string) ([]MetricFamilyMap, metrics := make([]MetricFamilyMap, 0, len(urls)) for r := range resultsChan { if r.err != nil { - s.log.Error(r.err, "Failed to scrape metrics") + // Debug level: on non-GPU nodes the dcgm-exporter sidecar never + // listens, so this fails on every refresh by design. + s.log.V(1).Info("Failed to scrape metrics", "error", r.err.Error()) continue } metrics = append(metrics, r.metricFamilyMap) diff --git a/internal/nodemon/stats_types.go b/internal/nodemon/stats_types.go index ccee9056..58e5e531 100644 --- a/internal/nodemon/stats_types.go +++ b/internal/nodemon/stats_types.go @@ -71,10 +71,32 @@ type PVCRef struct { Namespace string `json:"namespace"` } +// FsStats holds filesystem usage for one filesystem, as reported by the kubelet +// Summary API. Pointer fields are omitted by kubelet when unavailable, matching +// the convention used by CPUStats and MemStats above. +type FsStats struct { + Time time.Time `json:"time"` + AvailableBytes *uint64 `json:"availableBytes"` + CapacityBytes *uint64 `json:"capacityBytes"` + UsedBytes *uint64 `json:"usedBytes"` + Inodes *uint64 `json:"inodes"` + InodesFree *uint64 `json:"inodesFree"` + InodesUsed *uint64 `json:"inodesUsed"` +} + +// RuntimeStats holds the container runtime's filesystem stats. containerFs is +// deliberately not modeled: it equals imageFs on every runtime configuration +// except split-disk containerd, which we have not observed in the fleet. +type RuntimeStats struct { + ImageFs *FsStats `json:"imageFs"` +} + // NodeStats holds node-level metrics from kubelet stats/summary. type NodeStats struct { - NodeName string `json:"nodeName"` - CPU CPUStats `json:"cpu"` - Memory MemStats `json:"memory"` - Network NetworkStats `json:"network"` + NodeName string `json:"nodeName"` + CPU CPUStats `json:"cpu"` + Memory MemStats `json:"memory"` + Network NetworkStats `json:"network"` + Fs *FsStats `json:"fs"` + Runtime *RuntimeStats `json:"runtime"` } diff --git a/internal/nodemon/unified_exporter.go b/internal/nodemon/unified_exporter.go index 8e8be2ca..f63da4f8 100644 --- a/internal/nodemon/unified_exporter.go +++ b/internal/nodemon/unified_exporter.go @@ -347,11 +347,68 @@ func (u *UnifiedExporter) buildNodeMetrics( } nodeResult.NetworkRxBytesPerSec = u.nodeNetRates.Rate(u.nodeName, "rx_bytes", float64(nodeRxBytes), now) nodeResult.NetworkTxBytesPerSec = u.nodeNetRates.Rate(u.nodeName, "tx_bytes", float64(nodeTxBytes), now) + + // Filesystem occupancy comes straight from stats/summary — unlike the + // network and disk-I/O fields above, there is no cAdvisor aggregation + // and no per-second rate to derive. These are gauges, stored as read. + if fs := stats.Node.Fs; fs != nil { + nodeResult.NodefsUsedBytes = derefU64(fs.UsedBytes) + nodeResult.NodefsCapacityBytes = derefU64(fs.CapacityBytes) + nodeResult.NodefsAvailableBytes = derefU64(fs.AvailableBytes) + nodeResult.NodefsInodesUsed = derefU64(fs.InodesUsed) + nodeResult.NodefsInodesTotal = derefU64(fs.Inodes) + } + if rt := stats.Node.Runtime; rt != nil && rt.ImageFs != nil { + nodeResult.ImagefsUsedBytes = derefU64(rt.ImageFs.UsedBytes) + nodeResult.ImagefsCapacityBytes = derefU64(rt.ImageFs.CapacityBytes) + nodeResult.ImagefsAvailableBytes = derefU64(rt.ImageFs.AvailableBytes) + nodeResult.ImagefsSharedDevice = imagefsSharesDeviceWithNodefs(stats.Node.Fs, rt.ImageFs) + } } return nodeResult } +// derefU64 reads an optional kubelet counter, treating absent as zero. +func derefU64(v *uint64) uint64 { + if v == nil { + return 0 + } + return *v +} + +// imagefsSharesDeviceWithNodefs reports whether the container runtime's image +// filesystem is the same physical filesystem as nodefs. +// +// This matters because when they are shared, imagefs usage is a SUBSET of +// nodefs usage — on a stock EKS/GKE/AKS node imagefs reports the image layers +// that already count toward nodefs's used bytes. Summing them would +// double-count, which is why the two are stored separately and never combined +// upstream of this flag. +// +// The kubelet Summary API does not name the device, so this is inferred from +// total capacity AND total inode count both matching. Capacity alone is too +// weak — two volumes provisioned at the same size are common. availableBytes +// cannot be used at all: the fs and imageFs sections carry different sample +// timestamps and drift by a few blocks between them. +// +// Zero capacity means the kubelet reported nothing useful, which is not +// evidence of sharing, so it returns false. +// +// internal/collector carries a deliberate twin of this heuristic (it does not +// import internal/nodemon by design) — if you change this, change that one too. +func imagefsSharesDeviceWithNodefs(nodefs, imagefs *FsStats) bool { + if nodefs == nil || imagefs == nil { + return false + } + capacity := derefU64(nodefs.CapacityBytes) + inodes := derefU64(nodefs.Inodes) + if capacity == 0 || inodes == 0 { + return false + } + return capacity == derefU64(imagefs.CapacityBytes) && inodes == derefU64(imagefs.Inodes) +} + // QueryContainerMetrics implements UnifiedQuerier. func (u *UnifiedExporter) QueryContainerMetrics() []ContainerMetricsResponse { u.mu.RLock() diff --git a/internal/nodemon/unified_types.go b/internal/nodemon/unified_types.go index cecff9f3..b290ed20 100644 --- a/internal/nodemon/unified_types.go +++ b/internal/nodemon/unified_types.go @@ -78,6 +78,24 @@ type NodeMetricsResponse struct { DiskReadOpsPerSec float64 `json:"disk_read_ops_per_sec"` DiskWriteOpsPerSec float64 `json:"disk_write_ops_per_sec"` + // Node filesystem occupancy (gauges, from kubelet stats/summary). Distinct + // from the Disk I/O rates above: these answer "is the disk full", not + // "is the disk busy". + NodefsUsedBytes uint64 `json:"nodefs_used_bytes,omitempty"` + NodefsCapacityBytes uint64 `json:"nodefs_capacity_bytes,omitempty"` + NodefsAvailableBytes uint64 `json:"nodefs_available_bytes,omitempty"` + NodefsInodesUsed uint64 `json:"nodefs_inodes_used,omitempty"` + NodefsInodesTotal uint64 `json:"nodefs_inodes_total,omitempty"` + ImagefsUsedBytes uint64 `json:"imagefs_used_bytes,omitempty"` + ImagefsCapacityBytes uint64 `json:"imagefs_capacity_bytes,omitempty"` + ImagefsAvailableBytes uint64 `json:"imagefs_available_bytes,omitempty"` + // ImagefsSharedDevice reports that imagefs and nodefs are the same + // underlying filesystem, in which case ImagefsUsedBytes is a SUBSET of + // NodefsUsedBytes and the two must never be added together. The kubelet + // does not expose the device, so this is inferred; see + // imagefsSharesDeviceWithNodefs. + ImagefsSharedDevice bool `json:"imagefs_shared_device,omitempty"` + // GPU aggregates (optional) GPUUtilizationAvg float64 `json:"gpu_utilization_avg,omitempty"` GPUMemoryUsedMiBSum float64 `json:"gpu_memory_used_mib_sum,omitempty"` diff --git a/internal/proto/mpa.proto b/internal/proto/mpa.proto deleted file mode 100644 index 08f7cdbd..00000000 --- a/internal/proto/mpa.proto +++ /dev/null @@ -1,119 +0,0 @@ -syntax = "proto3"; - -package api.v1; - -import "google/protobuf/timestamp.proto"; - -option go_package = "github.com/devzero-inc/zxporter/gen/api/v1;gen"; -option java_multiple_files = true; -option java_package = "gen.api.v1"; - -// MpaService provides real-time metric streaming for in-cluster Multi-Dimensional Pod Autoscaler -service MpaService { - // StreamWorkloadMetrics establishes a bidirectional stream where the client (Dakr) - // subscribes to workloads, and the server (Zxporter) pushes metric updates. - rpc StreamWorkloadMetrics(stream MpaWorkloadSubscription) returns (stream MpaStreamResponse); -} - -// MpaWorkloadSubscription is a message sent by the client to update the subscription list -message MpaWorkloadSubscription { - repeated MpaWorkloadIdentifier workloads = 1; -} - -message MpaWorkloadIdentifier { - string namespace = 1; - string name = 2; // Workload name (e.g., deployment name) - string kind = 3; // Workload kind (e.g., Deployment) - map match_labels = 4; // Selectors to match pods -} - -// ContainerMetricsBatch contains a batch of metrics for containers belonging to subscribed workloads -message ContainerMetricsBatch { - repeated ContainerMetricItem items = 1; -} - -message ContainerMetricItem { - MpaWorkloadIdentifier workload = 1; // The workload this container belongs to - string pod_name = 2; - string container_name = 3; - - // The metric data - google.protobuf.Timestamp timestamp = 4; - int64 cpu_usage_millis = 5; - int64 memory_usage_bytes = 6; - int64 oom_kill_count = 7; // Cumulative if available, otherwise 0 - int32 restart_count = 8; - string last_termination_reason = 9; // e.g. "OOMKilled" - - // Resource requests and limits from pod spec (for utilization calculation) - int64 cpu_request_millis = 10; // CPU request in millicores - int64 memory_request_bytes = 11; // Memory request in bytes - int64 cpu_limit_millis = 12; // CPU limit in millicores - int64 memory_limit_bytes = 13; // Memory limit in bytes - - // Network metrics (bytes/sec, placeholder until collection extended) - int64 network_receive_bytes_per_sec = 14; - int64 network_transmit_bytes_per_sec = 15; - - // Disk metrics (bytes/sec, placeholder until collection extended) - int64 disk_read_bytes_per_sec = 16; - int64 disk_write_bytes_per_sec = 17; - - // CPU throttle fraction from CFS bandwidth controller (0.0-1.0) - // Derived from rate(container_cpu_cfs_throttled_periods_total) / rate(container_cpu_cfs_periods_total) - double cpu_throttle_fraction = 18; - - // Memory RSS in bytes (Resident Set Size from kubelet stats/summary). - int64 memory_rss_bytes = 19; -} - -// HistoricalMetricsSummary contains pre-aggregated percentile data from Prometheus -// for a workload over a 24-hour window. Sent on subscription start and refreshed periodically. -message HistoricalMetricsSummary { - MpaWorkloadIdentifier workload = 1; - repeated ContainerHistoricalMetrics containers = 2; - google.protobuf.Timestamp window_start = 3; - google.protobuf.Timestamp window_end = 4; - int32 sample_count = 5; -} - -message ContainerHistoricalMetrics { - string container_name = 1; - // CPU percentiles in millicores - int64 cpu_p50 = 2; - int64 cpu_p75 = 3; - int64 cpu_p80 = 4; - int64 cpu_p90 = 5; - int64 cpu_p99 = 6; - int64 cpu_p95 = 12; - // Memory percentiles in bytes - int64 mem_p50 = 7; - int64 mem_p75 = 8; - int64 mem_p80 = 9; - int64 mem_p90 = 10; - int64 mem_p99 = 11; - int64 mem_p95 = 13; - // Network percentiles in bytes/sec (placeholder until collection extended) - int64 net_p50 = 14; - int64 net_p80 = 15; - int64 net_p90 = 16; - int64 net_p95 = 17; - int64 net_p99 = 18; - // Disk percentiles in bytes/sec (placeholder until collection extended) - int64 disk_p50 = 19; - int64 disk_p80 = 20; - int64 disk_p90 = 21; - int64 disk_p95 = 22; - int64 disk_p99 = 23; - // Pmax (max observed over 24h window) for spike protection - int64 cpu_pmax = 24; // max CPU over 24h window (millicores) - int64 mem_pmax = 25; // max memory over 24h window (bytes) -} - -// MpaStreamResponse wraps both real-time and historical data on the same stream -message MpaStreamResponse { - oneof payload { - ContainerMetricsBatch realtime_metrics = 1; - HistoricalMetricsSummary historical_summary = 2; - } -} diff --git a/internal/transport/cluster_token_exchange_test.go b/internal/transport/cluster_token_exchange_test.go new file mode 100644 index 00000000..b4440914 --- /dev/null +++ b/internal/transport/cluster_token_exchange_test.go @@ -0,0 +1,51 @@ +package transport + +import ( + "context" + "net/http" + "net/http/httptest" + "testing" + + "connectrpc.com/connect" + "github.com/go-logr/logr" + + gen "github.com/devzero-inc/zxporter/gen/api/v1" + "github.com/devzero-inc/zxporter/gen/api/v1/apiv1connect" +) + +type exchangeServer struct { + apiv1connect.UnimplementedClusterServiceHandler + got *gen.CreateClusterTokenRequest + auth string +} + +func (s *exchangeServer) CreateClusterToken(_ context.Context, req *connect.Request[gen.CreateClusterTokenRequest]) (*connect.Response[gen.CreateClusterTokenResponse], error) { + s.got, s.auth = req.Msg, req.Header().Get("Authorization") + return connect.NewResponse(&gen.CreateClusterTokenResponse{Token: "dakr-new", ClusterId: "c-1", Reattached: true}), nil +} + +func TestExchangePATForClusterTokenSendsFingerprintAndID(t *testing.T) { + srv := &exchangeServer{} + mux := http.NewServeMux() + mux.Handle(apiv1connect.NewClusterServiceHandler(srv)) + ts := httptest.NewUnstartedServer(mux) + ts.EnableHTTP2 = true + ts.StartTLS() + defer ts.Close() + + c := NewDakrClient(ts.URL, "", logr.Discard()).(*RealDakrClient) + c.clusterClient = apiv1connect.NewClusterServiceClient(ts.Client(), ts.URL) + + res, err := c.ExchangePATForClusterToken(context.Background(), ClusterTokenExchangeRequest{ + PATToken: "dzu-x", ClusterName: "n", K8sProvider: "aws", KubeSystemUID: "uid-1", ClusterID: "c-1", + }) + if err != nil { + t.Fatal(err) + } + if srv.auth != "Bearer dzu-x" || srv.got.KubeSystemUid != "uid-1" || srv.got.ClusterId != "c-1" { + t.Fatalf("sent auth=%q uid=%q id=%q", srv.auth, srv.got.KubeSystemUid, srv.got.ClusterId) + } + if res != (ClusterTokenExchangeResult{Token: "dakr-new", ClusterID: "c-1", Reattached: true}) { + t.Fatalf("result %+v", res) + } +} diff --git a/internal/transport/connect_compression_test.go b/internal/transport/connect_compression_test.go index 3f656361..5fa0f8d3 100644 --- a/internal/transport/connect_compression_test.go +++ b/internal/transport/connect_compression_test.go @@ -135,6 +135,23 @@ func (c *MockMetricsServer) SendNetworkTrafficMetrics( return connect.NewResponse(&gen.SendNetworkTrafficMetricsResponse{}), nil } +// SendScalerMetrics sends KEDA scaler metric samples to Dakr +func (c *MockMetricsServer) SendScalerMetrics( + ctx context.Context, + req *connect.Request[gen.SendScalerMetricsRequest], +) (*connect.Response[gen.SendScalerMetricsResponse], error) { + return connect.NewResponse(&gen.SendScalerMetricsResponse{}), nil +} + +// SendContainerRedMetrics sends per-container RED metrics to Dakr +func (c *MockMetricsServer) SendContainerRedMetrics( + ctx context.Context, + req *connect.Request[gen.SendContainerRedMetricsRequest], +) (*connect.Response[gen.SendContainerRedMetricsResponse], error) { + // Return an empty response for the mock implementation + return connect.NewResponse(&gen.SendContainerRedMetricsResponse{}), nil +} + func TestConnectRPCCompressionIntegration(t *testing.T) { // Create mock server mockServer := &MockMetricsServer{} diff --git a/internal/transport/dakr_client.go b/internal/transport/dakr_client.go index 049c37fe..b8d3a6b5 100644 --- a/internal/transport/dakr_client.go +++ b/internal/transport/dakr_client.go @@ -116,6 +116,23 @@ func (h *ClientHeaders) AttachToRequest(header http.Header) { } } +// noRetryProcedures lists the RPCs that must never be retried by the client, no +// matter what the retry policy says is retryable. +// +// A retry is only safe when the server can tell a duplicate from a new fact. +// SendContainerRedMetrics cannot: the items are per-minute counter deltas written +// into a SummingMergeTree, so a batch that reached the server, wrote its rows and +// lost only its response is added to the one already stored. The result is a minute +// that looks busier than it was, with no error and nothing to detect it. Losing a +// minute is recoverable by reading the next one; inflating one is not recoverable at +// all. See docs/features/red-metrics/INVARIANTS.md, "Never retry a flush batch". +// +// Interceptors in connect-go are per-client, not per-method, so opting a single +// procedure out has to happen here rather than at the call site. +var noRetryProcedures = map[string]struct{}{ + genconnect.MetricsCollectorServiceSendContainerRedMetricsProcedure: {}, +} + // RealDakrClient implements communication with Dakr service type RealDakrClient struct { logger logr.Logger @@ -152,6 +169,10 @@ func NewDakrClient(dakrBaseURL string, clusterToken string, logger logr.Logger) func(next connect.UnaryFunc) connect.UnaryFunc { return connect.UnaryFunc( func(ctx context.Context, req connect.AnyRequest) (connect.AnyResponse, error) { + if _, exempt := noRetryProcedures[req.Spec().Procedure]; exempt { + return next(ctx, req) + } + var lastErr error for currentAttempt := 0; currentAttempt < retryPolicy.MaxAttempts; currentAttempt++ { if currentAttempt > 0 { // Backoff only for retries @@ -652,15 +673,17 @@ func (c *RealDakrClient) SendTelemetryLogs( // ExchangePATForClusterToken exchanges a PAT token for a cluster token func (c *RealDakrClient) ExchangePATForClusterToken( ctx context.Context, - patToken, clusterName, k8sProvider string, -) (string, string, error) { + in ClusterTokenExchangeRequest, +) (ClusterTokenExchangeResult, error) { // Create the request req := connect.NewRequest(&gen.CreateClusterTokenRequest{ - ClusterName: clusterName, - K8SProvider: k8sProvider, + ClusterName: in.ClusterName, + K8SProvider: in.K8sProvider, + KubeSystemUid: in.KubeSystemUID, + ClusterId: in.ClusterID, }) // Add PAT token to the request header (overrides the default client headers auth) - req.Header().Set("Authorization", fmt.Sprintf("Bearer %s", patToken)) + req.Header().Set("Authorization", fmt.Sprintf("Bearer %s", in.PATToken)) // Note: We use the clientHeaders but manually override the Authorization header above versionInfo := version.Get() @@ -681,11 +704,14 @@ func (c *RealDakrClient) ExchangePATForClusterToken( resp, err := c.clusterClient.CreateClusterToken(ctx, req) if err != nil { c.logger.Error(err, "Failed to exchange PAT for cluster token") - return "", "", fmt.Errorf("failed to exchange PAT for cluster token: %w", err) + return ClusterTokenExchangeResult{}, fmt.Errorf("failed to exchange PAT for cluster token: %w", err) } - c.logger.Info("Successfully exchanged PAT for cluster token", "clusterId", resp.Msg.ClusterId) - return resp.Msg.Token, resp.Msg.ClusterId, nil + c.logger.Info("Exchanged PAT for cluster token", + "clusterId", resp.Msg.ClusterId, "reattached", resp.Msg.Reattached) + return ClusterTokenExchangeResult{ + Token: resp.Msg.Token, ClusterID: resp.Msg.ClusterId, Reattached: resp.Msg.Reattached, + }, nil } // SendNetworkTrafficMetrics pushes network traffic metrics from a node @@ -717,6 +743,95 @@ func (c *RealDakrClient) SendNetworkTrafficMetrics( return resp.Msg, nil } +// SendScalerMetrics pushes a batch of KEDA scaler metric samples to Dakr. +// If clusterID or teamID are empty the values are resolved from the context +// ("cluster_id" / "team_id" keys) so callers that don't carry them at +// construction time can still pass "" and rely on context injection. +// +// Note: in production the manager starts collectors with a bare context, so +// the context path above is rarely populated. The authoritative cluster_id and +// team_id are set server-side by dakr's ClusterTokenAuth middleware from the +// Bearer token — the same pattern used by SendNetworkTrafficMetrics. Do not +// rely on the context values being present; they are a convenience fallback +// only. +func (c *RealDakrClient) SendScalerMetrics( + ctx context.Context, + clusterID string, + teamID string, + samples []*gen.ScalerMetricSample, +) error { + if clusterID == "" { + if v, ok := ctx.Value("cluster_id").(string); ok { + clusterID = v + } + } + if teamID == "" { + if v, ok := ctx.Value("team_id").(string); ok { + teamID = v + } + } + + req := &gen.SendScalerMetricsRequest{ + ClusterId: clusterID, + TeamId: teamID, + Samples: samples, + } + + connectReq := connect.NewRequest(req) + + c.logger.Info("Sending KEDA scaler metrics to Dakr", + "samples", len(samples), + "cluster_id", clusterID) + + _, err := c.client.SendScalerMetrics(ctx, connectReq) + if err != nil { + c.logger.Error(err, "Failed to send KEDA scaler metrics to Dakr") + return fmt.Errorf("failed to send KEDA scaler metrics to Dakr: %w", err) + } + + c.logger.Info("Successfully sent KEDA scaler metrics") + return nil +} + +// SendContainerRedMetrics pushes per-container inbound RED metrics from a node. +// +// There is no retry here, and there must not be one. The items are per-minute +// deltas destined for a SummingMergeTree, so a re-sent batch is added to the one +// that already landed and is indistinguishable from real extra traffic. Losing a +// minute is recoverable by looking at the next one; double counting one is not +// recoverable at all. +// +// That is enforced by noRetryProcedures rather than by this comment: the client's +// retry interceptor applies to every procedure it wraps, so the exemption has to be +// registered there. TestSendContainerRedMetricsIsNeverRetried is what fails if the +// registration is dropped. +func (c *RealDakrClient) SendContainerRedMetrics( + ctx context.Context, + req *gen.SendContainerRedMetricsRequest, +) (*gen.SendContainerRedMetricsResponse, error) { + connectReq := connect.NewRequest(req) + + // Pass through context values if needed (e.g. cluster_id) + if ctx.Value("cluster_id") != nil { + clusterString, _ := ctx.Value("cluster_id").(string) + connectReq.Header().Set("cluster_id", clusterString) + } + + c.logger.Info("Sending container RED metrics to Dakr", + "items", len(req.Items), + "node", req.NodeName) + + resp, err := c.client.SendContainerRedMetrics(ctx, connectReq) + if err != nil { + c.logger.Error(err, "Failed to send container RED metrics to Dakr") + return nil, fmt.Errorf("failed to send container RED metrics to Dakr: %w", err) + } + + c.logger.Info("Successfully sent container RED metrics", "status", "ok") + + return resp.Msg, nil +} + // ReportHealth reports the health status of the operator to Dakr func (c *RealDakrClient) ReportHealth(ctx context.Context, req *gen.ReportHealthRequest) error { _, err := c.operatorHealthClient.ReportHealth(ctx, connect.NewRequest(req)) diff --git a/internal/transport/dakr_client_retry_test.go b/internal/transport/dakr_client_retry_test.go new file mode 100644 index 00000000..70f1f358 --- /dev/null +++ b/internal/transport/dakr_client_retry_test.go @@ -0,0 +1,124 @@ +package transport + +import ( + "context" + "errors" + "net/http" + "net/http/httptest" + "sync" + "testing" + "time" + + "connectrpc.com/connect" + "github.com/go-logr/logr" + + gen "github.com/devzero-inc/zxporter/gen/api/v1" + genconnect "github.com/devzero-inc/zxporter/gen/api/v1/apiv1connect" + "github.com/devzero-inc/zxporter/internal/collector" +) + +// alwaysUnavailableServer fails every call it overrides with a retryable code and +// records how many times each procedure was actually delivered. Everything it does +// not override is inherited from MockMetricsServer. +type alwaysUnavailableServer struct { + *MockMetricsServer + + mu sync.Mutex + counts map[string]int +} + +func newAlwaysUnavailableServer() *alwaysUnavailableServer { + return &alwaysUnavailableServer{ + MockMetricsServer: &MockMetricsServer{}, + counts: map[string]int{}, + } +} + +func (s *alwaysUnavailableServer) bump(procedure string) error { + s.mu.Lock() + s.counts[procedure]++ + s.mu.Unlock() + return connect.NewError(connect.CodeUnavailable, errors.New("dakr is down")) +} + +func (s *alwaysUnavailableServer) count(procedure string) int { + s.mu.Lock() + defer s.mu.Unlock() + return s.counts[procedure] +} + +func (s *alwaysUnavailableServer) SendContainerRedMetrics( + _ context.Context, + _ *connect.Request[gen.SendContainerRedMetricsRequest], +) (*connect.Response[gen.SendContainerRedMetricsResponse], error) { + return nil, s.bump("SendContainerRedMetrics") +} + +func (s *alwaysUnavailableServer) SendResourceBatch( + _ context.Context, + _ *connect.Request[gen.SendResourceBatchRequest], +) (*connect.Response[gen.SendResourceBatchResponse], error) { + return nil, s.bump("SendResourceBatch") +} + +// TestSendContainerRedMetricsIsNeverRetried pins the invariant that makes the RED +// metrics table trustworthy. The items are per-minute counter deltas written into a +// SummingMergeTree, so a batch delivered twice is added twice and the minute reads as +// busier than it really was. A lost response is indistinguishable from a lost request, +// which is exactly why the client must not guess and send it again. +// +// The contrast case matters as much as the exemption: it proves the client still +// retries everything else, so passing here cannot mean the interceptor was disabled +// wholesale. +func TestSendContainerRedMetricsIsNeverRetried(t *testing.T) { + server := newAlwaysUnavailableServer() + + mux := http.NewServeMux() + path, handler := genconnect.NewMetricsCollectorServiceHandler(server) + mux.Handle(path, handler) + + httpServer := httptest.NewServer(mux) + defer httpServer.Close() + + client := NewDakrClient(httpServer.URL, "test-token", logr.Discard()) + ctx := context.Background() + + _, err := client.SendContainerRedMetrics(ctx, &gen.SendContainerRedMetricsRequest{ + ClusterId: "test-cluster", + NodeName: "node-a", + Items: []*gen.ContainerRedMetricsItem{{ + PodName: "pod-a", + PodNamespace: "default", + PodUid: "uid-a", + ContainerName: "app", + RequestCount: 1, + }}, + }) + if err == nil { + t.Fatal("expected SendContainerRedMetrics to fail against an unavailable server") + } + + if got := server.count("SendContainerRedMetrics"); got != 1 { + t.Errorf( + "SendContainerRedMetrics was delivered %d times, want exactly 1: a retried flush double counts a minute", + got, + ) + } + + // A retryable procedure on the same client must still be retried, otherwise this + // test would pass just as happily with the retry interceptor removed entirely. + _, err = client.SendResourceBatch(ctx, []collector.CollectedResource{{ + ResourceType: collector.Pod, + Object: createLargeKubernetesResource(0), + Timestamp: time.Now(), + EventType: collector.EventTypeAdd, + Key: "default/pod-a", + }}, collector.Pod) + if err == nil { + t.Fatal("expected SendResourceBatch to fail against an unavailable server") + } + + if got := server.count("SendResourceBatch"); got != 3 { + t.Errorf("SendResourceBatch was delivered %d times, want 3 (the retry policy's MaxAttempts)", got) + } +} diff --git a/internal/transport/interface.go b/internal/transport/interface.go index 4f3861a6..26c6d088 100644 --- a/internal/transport/interface.go +++ b/internal/transport/interface.go @@ -40,11 +40,10 @@ type DakrClient interface { ) (SnapshotBatchStream, error) // telemetry_logger.TelemetryLogSender sends a batch of log entries to Dakr telemetry_logger.TelemetryLogSender - // ExchangePATForClusterToken exchanges a PAT token for a cluster token - ExchangePATForClusterToken( - ctx context.Context, - patToken, clusterName, k8sProvider string, - ) (string, string, error) + // ExchangePATForClusterToken exchanges a personal access token for a + // cluster token. KubeSystemUID and ClusterID let dakr re-attach to an + // existing cluster instead of creating one (KUB-158). + ExchangePATForClusterToken(ctx context.Context, req ClusterTokenExchangeRequest) (ClusterTokenExchangeResult, error) // SendNetworkTrafficMetrics pushes network traffic metrics from a node SendNetworkTrafficMetrics( @@ -52,6 +51,24 @@ type DakrClient interface { req *gen.SendNetworkTrafficMetricsRequest, ) (*gen.SendNetworkTrafficMetricsResponse, error) + // SendScalerMetrics pushes a batch of KEDA scaler metric samples from a cluster. + SendScalerMetrics( + ctx context.Context, + clusterID string, + teamID string, + samples []*gen.ScalerMetricSample, + ) error + + // SendContainerRedMetrics pushes per-container inbound RED metrics from a node. + // It is a separate call from SendNetworkTrafficMetrics rather than a field on it + // because the two carry different grains -- flows between endpoints versus + // per-container per-minute aggregates -- and because a failure to deliver one + // must not discard the other. + SendContainerRedMetrics( + ctx context.Context, + req *gen.SendContainerRedMetricsRequest, + ) (*gen.SendContainerRedMetricsResponse, error) + // ReportHealth ReportHealth(ctx context.Context, req *gen.ReportHealthRequest) error @@ -106,3 +123,19 @@ type DirectSender interface { // // Flush attempts to send all buffered items // Flush(ctx context.Context) error // } + +// ClusterTokenExchangeRequest is the input to a PAT-for-cluster-token exchange. +type ClusterTokenExchangeRequest struct { + PATToken string + ClusterName string + K8sProvider string + KubeSystemUID string + ClusterID string +} + +// ClusterTokenExchangeResult is the cluster token dakr issued and the cluster it belongs to. +type ClusterTokenExchangeResult struct { + Token string + ClusterID string + Reattached bool +} diff --git a/internal/transport/sender.go b/internal/transport/sender.go index 2796132a..32d192b2 100644 --- a/internal/transport/sender.go +++ b/internal/transport/sender.go @@ -243,13 +243,13 @@ func (c *SimpleDakrClient) SendTelemetryLogs( // ExchangePATForClusterToken implements SimpleDakrClient. func (c *SimpleDakrClient) ExchangePATForClusterToken( ctx context.Context, - patToken, clusterName, k8sProvider string, -) (string, string, error) { + req ClusterTokenExchangeRequest, +) (ClusterTokenExchangeResult, error) { c.logger.Info("Would exchange PAT token for cluster token", - "clusterName", clusterName, - "k8sProvider", k8sProvider) + "clusterName", req.ClusterName, + "k8sProvider", req.K8sProvider) // Return empty values for simple client - return "", "", fmt.Errorf("PAT token exchange not supported in simple client") + return ClusterTokenExchangeResult{}, fmt.Errorf("PAT token exchange not supported in simple client") } // SendNetworkTrafficMetrics implements SimpleDakrClient. @@ -264,6 +264,30 @@ func (c *SimpleDakrClient) SendNetworkTrafficMetrics( return &gen.SendNetworkTrafficMetricsResponse{ProcessedCount: int32(len(req.Items))}, nil } +// SendScalerMetrics implements SimpleDakrClient (no-op). +func (c *SimpleDakrClient) SendScalerMetrics( + ctx context.Context, + clusterID string, + teamID string, + samples []*gen.ScalerMetricSample, +) error { + c.logger.Info("Would send KEDA scaler metrics to Dakr", + "clusterID", clusterID, + "samplesCount", len(samples)) + return nil +} + +// SendContainerRedMetrics implements SimpleDakrClient. +func (c *SimpleDakrClient) SendContainerRedMetrics( + ctx context.Context, + req *gen.SendContainerRedMetricsRequest, +) (*gen.SendContainerRedMetricsResponse, error) { + c.logger.Info("Would send container RED metrics to Dakr", + "nodeName", req.NodeName, + "itemsCount", len(req.Items)) + return &gen.SendContainerRedMetricsResponse{ProcessedCount: int32(len(req.Items))}, nil +} + // ReportHealth implements SimpleDakrClient. func (c *SimpleDakrClient) ReportHealth(ctx context.Context, req *gen.ReportHealthRequest) error { c.logger.Info( diff --git a/internal/util/env.go b/internal/util/env.go index 599541a2..72d4c070 100644 --- a/internal/util/env.go +++ b/internal/util/env.go @@ -87,6 +87,16 @@ const ( // Default value: 15s _ENV_KUBELET_FALLBACK_TIMEOUT = "KUBELET_FALLBACK_TIMEOUT" + // SCHEDULER_METRICS_SCRAPE_INTERVAL bounds how often the SchedulerMetrics + // collector scrapes dz-scheduler's /metrics endpoint. + // Default value: 60s + _ENV_SCHEDULER_METRICS_SCRAPE_INTERVAL = "SCHEDULER_METRICS_SCRAPE_INTERVAL" + + // KEDA_METRICS_ENDPOINT is the full URL of the KEDA operator /metrics endpoint + // scraped by the KedaScalerMetrics collector. + // Default value: http://dzkeda-operator.devzero-system.svc.cluster.local:8080/metrics + _ENV_KEDA_METRICS_ENDPOINT = "KEDA_METRICS_ENDPOINT" + // CLUSTER_SNAPSHOT_INTERVAL is how often to take cluster snapshots. // Default value: 3h _ENV_CLUSTER_SNAPSHOT_INTERVAL = "CLUSTER_SNAPSHOT_INTERVAL" @@ -97,7 +107,8 @@ const ( _ENV_WATCHED_CRDS = "WATCHED_CRDS" // DISABLED_COLLECTORS is a list of collector types to completely disable. - // This is a comma-separated list. + // This is a comma-separated list. It takes the same names as a CollectionPolicy's + // disabledCollectors, legacy spellings included (see collector.CanonicalCollectorType). // Default value: [] _ENV_DISABLED_COLLECTORS = "DISABLED_COLLECTORS" @@ -281,6 +292,10 @@ const ( const configVolumeMountPath = "/etc/zxporter/config" +// GetConfigValue returns key from the environment, else from the file the +// chart mounts at /etc/zxporter/config/. +func GetConfigValue(key string) string { return getEnv(key) } + func getEnv(key string) string { if data := os.Getenv(key); data != "" { return data @@ -584,6 +599,12 @@ func LoadCollectionPolicySpecFromEnv() (v1.CollectionPolicySpec, error) { if v := getEnv(_ENV_KUBELET_FALLBACK_TIMEOUT); v != "" { newSpec.Policies.KubeletFallbackTimeout = v } + if v := getEnv(_ENV_SCHEDULER_METRICS_SCRAPE_INTERVAL); v != "" { + newSpec.Policies.SchedulerMetricsScrapeInterval = v + } + if v := getEnv(_ENV_KEDA_METRICS_ENDPOINT); v != "" { + newSpec.Policies.KEDAMetricsEndpoint = v + } if v := getEnv(_ENV_CLUSTER_SNAPSHOT_INTERVAL); v != "" { newSpec.Policies.ClusterSnapshotInterval = v } diff --git a/proto/dakr_proto_descriptor.bin b/proto/dakr_proto_descriptor.bin deleted file mode 100644 index dc269ff3a14fd922cf7a5403e51007a6fc5220c5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 408979 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/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

    VBx($vj&B_nkJi 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 Z0~vY_6r^g)FgEgk*R5?Aiyz_q{(pR15%&N9 diff --git a/test/e2e/node_storage_test.go b/test/e2e/node_storage_test.go new file mode 100644 index 00000000..8804f465 --- /dev/null +++ b/test/e2e/node_storage_test.go @@ -0,0 +1,210 @@ +/* +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 e2e + +import ( + "context" + "time" + + . "github.com/onsi/ginkgo/v2" //nolint:golint,revive + . "github.com/onsi/gomega" //nolint:golint,revive + + "github.com/go-logr/logr" + "github.com/go-logr/zapr" + "go.uber.org/zap" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/client-go/kubernetes" + ctrl "sigs.k8s.io/controller-runtime" + + "github.com/devzero-inc/zxporter/internal/collector" +) + +// Node storage occupancy, end to end against a real kubelet. +// +// Every other test of this feature reads a captured fixture. This one drives +// the production client against the live kubelet on a real node and asserts +// the invariants the whole feature rests on. Three things only a real node +// can prove: +// +// 1. The kubelet actually serves node.fs and node.runtime.imageFs at all, +// through the API-server nodes/proxy subresource this client uses. A +// fixture cannot catch the kubelet dropping a section, renaming a field, +// or the RBAC verb going missing. +// 2. imagefs is a SUBSET of nodefs when they share a device. This is the +// invariant the entire feature is built on -- get it wrong and consumers +// double-count disk -- and it has only ever been asserted against numbers +// we wrote ourselves. +// 3. The shared-device heuristic classifies a real node correctly. It is a +// heuristic precisely because the kubelet does not name the device, so +// the only honest test of it is a node whose real layout we know. +// +// Unlike the gpu-mig spec this deploys no fixture and needs no port-forward: +// the client reads through the API server's nodes/proxy subresource, the same +// path kubectl already proves reachable from the runner. +var _ = Describe("Node storage occupancy", Ordered, Label("node-storage"), func() { + var ( + clientset kubernetes.Interface + log logr.Logger + nodeNames []string + ) + + BeforeAll(func() { + restConfig := ctrl.GetConfigOrDie() + + var err error + clientset, err = kubernetes.NewForConfig(restConfig) + Expect(err).NotTo(HaveOccurred()) + + zapLog, _ := zap.NewDevelopment() + log = zapr.NewLogger(zapLog) + + By("listing the cluster's nodes") + nodes, err := clientset.CoreV1().Nodes().List(context.Background(), metav1.ListOptions{}) + Expect(err).NotTo(HaveOccurred()) + Expect(nodes.Items).NotTo(BeEmpty(), "expected at least one node to read storage from") + for _, n := range nodes.Items { + nodeNames = append(nodeNames, n.Name) + } + }) + + // The kubelet populates node.fs from statfs on the filesystem backing + // /var/lib/kubelet. Any node that can run pods has one, so these bounds + // hold on every node in any cluster -- they are invariants, not + // Kind-specific values. + It("reads a real kubelet's filesystem occupancy through the API-server proxy", func() { + ctx := context.Background() + client := collector.NewKubeletSummaryClient(clientset, log, 30*time.Second) + + for _, nodeName := range nodeNames { + By("fetching node storage for " + nodeName) + + var m *collector.UnifiedNodeMetric + Eventually(func() error { + var err error + m, err = client.FetchNodeStorageByNode(ctx, nodeName) + return err + }, 2*time.Minute, 5*time.Second).Should(Succeed(), + "the kubelet Summary API should be reachable via nodes/proxy; a permission "+ + "error here means the nodes/proxy RBAC verb regressed") + + Expect(m).NotTo(BeNil()) + + // A node that can schedule pods has a filesystem with a nonzero + // size. Zero capacity is the sentinel the whole read path treats + // as "we do not know", so a real node reporting it would mean the + // section never arrived. + Expect(m.NodefsCapacityBytes).To(BeNumerically(">", 0), + "node %s reported zero nodefs capacity -- the kubelet's fs section did not arrive", nodeName) + Expect(m.NodefsUsedBytes).To(BeNumerically(">", 0), + "node %s has a filesystem holding at least the kubelet's own state", nodeName) + + // used + available is normally strictly LESS than capacity: + // ext4/xfs reserve blocks for root that belong to neither. The + // assertion is therefore one-sided -- an implementation that + // crossed two fields would break it, a correct one cannot. + Expect(m.NodefsUsedBytes+m.NodefsAvailableBytes).To(BeNumerically("<=", m.NodefsCapacityBytes), + "node %s: used+available must not exceed capacity", nodeName) + + Expect(m.NodefsInodesTotal).To(BeNumerically(">", 0), "node %s", nodeName) + Expect(m.NodefsInodesUsed).To(BeNumerically(">", 0), "node %s", nodeName) + Expect(m.NodefsInodesUsed).To(BeNumerically("<=", m.NodefsInodesTotal), + "node %s: inodes used cannot exceed inodes total", nodeName) + } + }) + + // The invariant the feature is built on. Whether a given node shares the + // device is an environment fact, so this asserts the CONSEQUENCE of the + // flag rather than its value -- it holds on a split-disk node too. + It("never reports imagefs usage exceeding nodefs usage on a shared device", func() { + ctx := context.Background() + client := collector.NewKubeletSummaryClient(clientset, log, 30*time.Second) + + sharedSeen := false + + for _, nodeName := range nodeNames { + m, err := client.FetchNodeStorageByNode(ctx, nodeName) + Expect(err).NotTo(HaveOccurred()) + Expect(m).NotTo(BeNil()) + + if m.ImagefsCapacityBytes == 0 { + // A runtime that reports no imagefs section at all. Nothing to + // check, and nothing downstream will read it: the read paths + // gate on a nonzero capacity. + continue + } + + if !m.ImagefsSharedDevice { + // A genuinely separate image filesystem. The two are then + // independent and no subset relationship is implied. + continue + } + + sharedSeen = true + + // This is the whole point. When the flag says shared, imagefs + // usage is image layers sitting INSIDE the nodefs total -- adding + // the two double-counts. If this ever fails, either the heuristic + // misclassified a split-disk node or the kubelet's two sections + // stopped describing the same filesystem, and every consumer that + // trusts the flag is now wrong. + Expect(m.ImagefsUsedBytes).To(BeNumerically("<=", m.NodefsUsedBytes), + "node %s: imagefs is flagged as sharing a device with nodefs, so its used bytes "+ + "must be a subset of nodefs used bytes, not an addition to it", nodeName) + + // The heuristic's own inputs, restated as an assertion so a + // failure says which half diverged rather than just "not shared". + Expect(m.ImagefsCapacityBytes).To(Equal(m.NodefsCapacityBytes), + "node %s: shared-device classification requires equal capacity", nodeName) + Expect(m.NodefsInodesTotal).To(BeNumerically(">", 0), + "node %s: shared-device classification requires a nonzero inode total", nodeName) + } + + // Kind runs containerd with its state on the same filesystem as the + // kubelet's, which is also the default on stock EKS AL2023, GKE COS + // and AKS Ubuntu. If no node classifies as shared, the heuristic has + // almost certainly stopped matching real kubelet output -- which is + // exactly the regression a fixture-based test cannot see. + Expect(sharedSeen).To(BeTrue(), + "expected at least one node to report imagefs sharing a device with nodefs; "+ + "on this cluster's runtime they are the same filesystem, so a universal "+ + "'not shared' means the heuristic no longer matches real kubelet output") + }) + + // Ephemeral storage comes from the Node object rather than the kubelet, + // so it is populated even for a node no metrics source ever reached. This + // asserts the two sources agree about roughly the same disk -- allocatable + // is capacity minus reservations, so it is bounded by it. + It("reports ephemeral-storage capacity and allocatable from the Node object", func() { + ctx := context.Background() + + for _, nodeName := range nodeNames { + node, err := clientset.CoreV1().Nodes().Get(ctx, nodeName, metav1.GetOptions{}) + Expect(err).NotTo(HaveOccurred()) + + capacity := node.Status.Capacity[corev1.ResourceEphemeralStorage] + allocatable := node.Status.Allocatable[corev1.ResourceEphemeralStorage] + + Expect(capacity.Value()).To(BeNumerically(">", 0), + "node %s reports no ephemeral-storage capacity; the scheduler admits pods "+ + "against this value, so a zero here would break the committed-vs-available view", nodeName) + Expect(allocatable.Value()).To(BeNumerically(">", 0), "node %s", nodeName) + Expect(allocatable.Value()).To(BeNumerically("<=", capacity.Value()), + "node %s: allocatable is capacity minus reservations", nodeName) + } + }) +}) diff --git a/test/fixtures/jvm-apps.yaml b/test/fixtures/jvm-apps.yaml index a916d857..33fe434c 100644 --- a/test/fixtures/jvm-apps.yaml +++ b/test/fixtures/jvm-apps.yaml @@ -216,6 +216,96 @@ spec: javac /tmp/DummyMain.java exec java -cp /tmp DummyMain --- +# The next two deployments are a matched pair: the same real-world JVM image, +# the same non-root uid, differing ONLY in readOnlyRootFilesystem. With a +# read-only root filesystem and no writable volume at /tmp, HotSpot cannot +# create /tmp/hsperfdata_/ and silently falls back to non-shared perf +# memory — the JVM runs fine, but nodemon's hsperfdata-backed pipeline has +# nothing to read. The pair pins both halves of that behaviour: +# +# zipkin-writable-rootfs -> jvm bucket, with heap metrics +# zipkin-readonly-rootfs -> runtimes bucket as runtime "java", no heap metrics +# +# Before the fallback existed the read-only half appeared in neither bucket, so +# the container looked like it ran no known runtime at all. +apiVersion: apps/v1 +kind: Deployment +metadata: + name: zipkin-writable-rootfs + namespace: jvm-spike +spec: + replicas: 1 + selector: + matchLabels: + app: zipkin-writable-rootfs + template: + metadata: + labels: + app: zipkin-writable-rootfs + spec: + containers: + - name: app + # Entrypoint execs `java -Xms160m -Xmx160m ...`, so comm is "java" + # and the flags come from the image, not from the pod spec. + image: openzipkin/zipkin-slim:3.5 + env: + - name: STORAGE_TYPE + value: mem + securityContext: + runAsUser: 1000 + runAsNonRoot: true + readOnlyRootFilesystem: false + resources: + requests: + memory: 256Mi + limits: + memory: 512Mi + readinessProbe: + httpGet: + path: /health + port: 9411 + initialDelaySeconds: 5 + periodSeconds: 5 +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: zipkin-readonly-rootfs + namespace: jvm-spike +spec: + replicas: 1 + selector: + matchLabels: + app: zipkin-readonly-rootfs + template: + metadata: + labels: + app: zipkin-readonly-rootfs + spec: + containers: + - name: app + image: openzipkin/zipkin-slim:3.5 + env: + - name: STORAGE_TYPE + value: mem + securityContext: + runAsUser: 1000 + runAsNonRoot: true + # The variable under test. Deliberately no volume at /tmp — adding + # one is the customer-side workaround and would defeat the fixture. + readOnlyRootFilesystem: true + resources: + requests: + memory: 256Mi + limits: + memory: 512Mi + readinessProbe: + httpGet: + path: /health + port: 9411 + initialDelaySeconds: 5 + periodSeconds: 5 +--- apiVersion: apps/v1 kind: Deployment metadata: diff --git a/test/testserver/main.go b/test/testserver/main.go index 6b80d0bd..8aaf7141 100644 --- a/test/testserver/main.go +++ b/test/testserver/main.go @@ -480,6 +480,58 @@ func (s *MetricsServer) SendNetworkTrafficMetrics( }), nil } +// SendScalerMetrics implements apiv1connect.MetricsCollectorServiceHandler. +func (s *MetricsServer) SendScalerMetrics( + ctx context.Context, + req *connect.Request[apiv1.SendScalerMetricsRequest], +) (*connect.Response[apiv1.SendScalerMetricsResponse], error) { + s.mu.Lock() + defer s.mu.Unlock() + + s.stats.TotalMessages++ + if s.stats.MessagesByType == nil { + s.stats.MessagesByType = make(map[string]int) + } + s.stats.MessagesByType["KEDA_SCALER_METRICS"]++ + + fmt.Fprintf(os.Stderr, "Received %d KEDA scaler metric samples for cluster %s\n", + len(req.Msg.Samples), req.Msg.ClusterId) + + return connect.NewResponse(&apiv1.SendScalerMetricsResponse{}), nil +} + +// SendContainerRedMetrics accepts per-container RED metrics. +// +// It records the message but keeps no per-item stats: the items are per-minute +// deltas whose only useful reading is a sum over a time range, which is what the +// real ClickHouse destination is for and what a counter in this process would +// only imitate badly. +func (s *MetricsServer) SendContainerRedMetrics( + ctx context.Context, + req *connect.Request[apiv1.SendContainerRedMetricsRequest], +) (*connect.Response[apiv1.SendContainerRedMetricsResponse], error) { + s.mu.Lock() + defer s.mu.Unlock() + + s.stats.TotalMessages++ + if s.stats.MessagesByType == nil { + s.stats.MessagesByType = make(map[string]int) + } + s.stats.MessagesByType["CONTAINER_RED_METRICS"]++ + + if s.stats.FirstMessageTime == nil { + now := time.Now() + s.stats.FirstMessageTime = &now + } + + fmt.Fprintf(os.Stderr, "Received %d container RED metrics items from node %s\n", + len(req.Msg.Items), req.Msg.NodeName) + + return connect.NewResponse(&apiv1.SendContainerRedMetricsResponse{ + ProcessedCount: int32(len(req.Msg.Items)), + }), nil +} + // StatsHandler handles the /stats HTTP endpoint func (s *MetricsServer) StatsHandler(w http.ResponseWriter, r *http.Request) { s.mu.Lock() diff --git a/verification/verify_e2e.sh b/verification/verify_e2e.sh index 8dc09ec5..1bf4874b 100755 --- a/verification/verify_e2e.sh +++ b/verification/verify_e2e.sh @@ -11,6 +11,11 @@ set -e CLUSTER_CONTEXT=${1:-kind-kind} NAMESPACE=${2:-devzero-zxporter} +# The charts vendor devzero-common into their gitignored charts/ directories. +for chart in zxporter-nodemon zxporter-netmon zxporter; do + helm dependency build "helm-chart/$chart" >/dev/null +done + echo "Using cluster context: $CLUSTER_CONTEXT" # 0. Ensure Kind cluster exists if using Kind diff --git a/verification/verify_gpu_runtime_helm.sh b/verification/verify_gpu_runtime_helm.sh index 254c65f0..930ed8e7 100755 --- a/verification/verify_gpu_runtime_helm.sh +++ b/verification/verify_gpu_runtime_helm.sh @@ -29,6 +29,11 @@ if [ ! -x "$HELM_BIN" ]; then exit 1 fi +# The charts vendor devzero-common into their gitignored charts/ directories. +for chart in zxporter-nodemon zxporter; do + "$HELM_BIN" dependency build "helm-chart/$chart" >/dev/null +done + if [ ! -x "$YQ_BIN" ]; then echo "yq not found; run 'make yq' first" >&2 exit 1 diff --git a/zxporter-descriptor.bin b/zxporter-descriptor.bin deleted file mode 100644 index a537f7cb675792340bcbddd3bf34e0108e1efced..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 175823 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%0DES}^7 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