Repository: etcd-io/etcd
Branch: main
Commit: 5791f2b80ecf
Files: 1424
Total size: 9.6 MB
Directory structure:
gitextract_dhrjhetk/
├── .devcontainer/
│ └── devcontainer.json
├── .github/
│ ├── ISSUE_TEMPLATE/
│ │ ├── bug-report.yml
│ │ ├── config.yml
│ │ ├── feature-request.yml
│ │ └── test-flake.yml
│ ├── OWNERS
│ ├── PULL_REQUEST_TEMPLATE.md
│ ├── SECURITY.md
│ ├── dependabot.yml
│ └── workflows/
│ ├── OWNERS
│ ├── antithesis-test.yml
│ ├── antithesis-verify.yml
│ ├── antithesis.debugger.yml
│ ├── cherrypick-bot-ok-to-test.yaml
│ ├── codeql-analysis.yml
│ ├── gh-workflow-approve.yaml
│ ├── measure-testgrid-flakiness.yaml
│ ├── scorecards.yml
│ ├── stale.yaml
│ └── verify-released-assets.yaml
├── .gitignore
├── .go-version
├── .header
├── ADOPTERS.md
├── CHANGELOG/
│ ├── CHANGELOG-2.3.md
│ ├── CHANGELOG-3.0.md
│ ├── CHANGELOG-3.1.md
│ ├── CHANGELOG-3.2.md
│ ├── CHANGELOG-3.3.md
│ ├── CHANGELOG-3.4.md
│ ├── CHANGELOG-3.5.md
│ ├── CHANGELOG-3.6.md
│ ├── CHANGELOG-3.7.md
│ ├── CHANGELOG-4.0.md
│ └── README.md
├── CONTRIBUTING.md
├── DCO
├── Dockerfile
├── Documentation/
│ ├── OWNERS
│ ├── README.md
│ ├── contributor-guide/
│ │ ├── branch_management.md
│ │ ├── bump_etcd_version_k8s.md
│ │ ├── community-membership.md
│ │ ├── dependency_management.md
│ │ ├── exit_codes.md
│ │ ├── features.md
│ │ ├── local_cluster.md
│ │ ├── logging.md
│ │ ├── modules.md
│ │ ├── prow_jobs.md
│ │ ├── release.md
│ │ ├── reporting_bugs.md
│ │ ├── roadmap.md
│ │ ├── triage_issues.md
│ │ └── triage_prs.md
│ ├── dev-guide/
│ │ └── apispec/
│ │ └── swagger/
│ │ ├── rpc.swagger.json
│ │ ├── v3election.swagger.json
│ │ └── v3lock.swagger.json
│ ├── etcd-internals/
│ │ └── diagrams/
│ │ ├── consistent_read_workflow.drawio
│ │ ├── etcd_internal_parts.drawio
│ │ ├── write_workflow_follower.drawio
│ │ └── write_workflow_leader.drawio
│ └── postmortems/
│ └── v3.5-data-inconsistency.md
├── GOVERNANCE.md
├── LICENSE
├── Makefile
├── OWNERS
├── OWNERS_ALIASES
├── Procfile
├── README.md
├── api/
│ ├── .gomodguard.yaml
│ ├── LICENSE
│ ├── authpb/
│ │ ├── auth.pb.go
│ │ ├── auth.proto
│ │ └── deprecated.go
│ ├── etcdserverpb/
│ │ ├── etcdserver.pb.go
│ │ ├── etcdserver.proto
│ │ ├── gw/
│ │ │ └── rpc.pb.gw.go
│ │ ├── raft_internal.pb.go
│ │ ├── raft_internal.proto
│ │ ├── raft_internal_stringer.go
│ │ ├── raft_internal_stringer_test.go
│ │ ├── rpc.pb.go
│ │ ├── rpc.proto
│ │ └── rpc_grpc.pb.go
│ ├── go.mod
│ ├── go.sum
│ ├── membershippb/
│ │ ├── membership.pb.go
│ │ └── membership.proto
│ ├── mvccpb/
│ │ ├── deprecated.go
│ │ ├── kv.pb.go
│ │ └── kv.proto
│ ├── v3rpc/
│ │ └── rpctypes/
│ │ ├── doc.go
│ │ ├── error.go
│ │ ├── error_test.go
│ │ ├── md.go
│ │ └── metadatafields.go
│ ├── version/
│ │ ├── version.go
│ │ └── version_test.go
│ └── versionpb/
│ ├── version.pb.go
│ └── version.proto
├── bill-of-materials.json
├── bill-of-materials.override.json
├── cache/
│ ├── LICENSE
│ ├── OWNERS
│ ├── README.md
│ ├── cache.go
│ ├── cache_test.go
│ ├── config.go
│ ├── demux.go
│ ├── demux_test.go
│ ├── go.mod
│ ├── go.sum
│ ├── predicate.go
│ ├── ready.go
│ ├── ready_test.go
│ ├── ringbuffer.go
│ ├── ringbuffer_test.go
│ ├── snapshot.go
│ ├── store.go
│ ├── store_test.go
│ └── watcher.go
├── client/
│ ├── pkg/
│ │ ├── .gomodguard.yaml
│ │ ├── LICENSE
│ │ ├── fileutil/
│ │ │ ├── dir_unix.go
│ │ │ ├── dir_windows.go
│ │ │ ├── doc.go
│ │ │ ├── filereader.go
│ │ │ ├── filereader_test.go
│ │ │ ├── fileutil.go
│ │ │ ├── fileutil_test.go
│ │ │ ├── lock.go
│ │ │ ├── lock_flock.go
│ │ │ ├── lock_linux.go
│ │ │ ├── lock_linux_test.go
│ │ │ ├── lock_plan9.go
│ │ │ ├── lock_solaris.go
│ │ │ ├── lock_test.go
│ │ │ ├── lock_unix.go
│ │ │ ├── lock_windows.go
│ │ │ ├── preallocate.go
│ │ │ ├── preallocate_darwin.go
│ │ │ ├── preallocate_test.go
│ │ │ ├── preallocate_unix.go
│ │ │ ├── preallocate_unsupported.go
│ │ │ ├── purge.go
│ │ │ ├── purge_test.go
│ │ │ ├── read_dir.go
│ │ │ ├── read_dir_test.go
│ │ │ ├── sync.go
│ │ │ ├── sync_darwin.go
│ │ │ └── sync_linux.go
│ │ ├── go.mod
│ │ ├── go.sum
│ │ ├── logutil/
│ │ │ ├── doc.go
│ │ │ ├── log_format.go
│ │ │ ├── log_format_test.go
│ │ │ ├── log_level.go
│ │ │ ├── zap.go
│ │ │ ├── zap_journal.go
│ │ │ ├── zap_journal_test.go
│ │ │ └── zap_test.go
│ │ ├── pathutil/
│ │ │ ├── path.go
│ │ │ └── path_test.go
│ │ ├── srv/
│ │ │ ├── srv.go
│ │ │ └── srv_test.go
│ │ ├── systemd/
│ │ │ ├── doc.go
│ │ │ └── journal.go
│ │ ├── testutil/
│ │ │ ├── assert.go
│ │ │ ├── before.go
│ │ │ ├── leak.go
│ │ │ ├── leak_test.go
│ │ │ ├── pauseable_handler.go
│ │ │ ├── recorder.go
│ │ │ ├── testingtb.go
│ │ │ ├── testutil.go
│ │ │ └── var.go
│ │ ├── tlsutil/
│ │ │ ├── cipher_suites.go
│ │ │ ├── cipher_suites_test.go
│ │ │ ├── doc.go
│ │ │ ├── tlsutil.go
│ │ │ ├── versions.go
│ │ │ └── versions_test.go
│ │ ├── transport/
│ │ │ ├── doc.go
│ │ │ ├── keepalive_listener.go
│ │ │ ├── keepalive_listener_openbsd.go
│ │ │ ├── keepalive_listener_test.go
│ │ │ ├── keepalive_listener_unix.go
│ │ │ ├── limit_listen.go
│ │ │ ├── listener.go
│ │ │ ├── listener_opts.go
│ │ │ ├── listener_test.go
│ │ │ ├── listener_tls.go
│ │ │ ├── sockopt.go
│ │ │ ├── sockopt_solaris.go
│ │ │ ├── sockopt_unix.go
│ │ │ ├── sockopt_wasm.go
│ │ │ ├── sockopt_windows.go
│ │ │ ├── timeout_conn.go
│ │ │ ├── timeout_dialer.go
│ │ │ ├── timeout_dialer_test.go
│ │ │ ├── timeout_listener.go
│ │ │ ├── timeout_listener_test.go
│ │ │ ├── timeout_transport.go
│ │ │ ├── timeout_transport_test.go
│ │ │ ├── tls.go
│ │ │ ├── tls_test.go
│ │ │ ├── transport.go
│ │ │ ├── transport_test.go
│ │ │ └── unix_listener.go
│ │ ├── types/
│ │ │ ├── doc.go
│ │ │ ├── id.go
│ │ │ ├── id_test.go
│ │ │ ├── set.go
│ │ │ ├── set_test.go
│ │ │ ├── slice.go
│ │ │ ├── slice_test.go
│ │ │ ├── urls.go
│ │ │ ├── urls_test.go
│ │ │ ├── urlsmap.go
│ │ │ └── urlsmap_test.go
│ │ └── verify/
│ │ └── verify.go
│ └── v3/
│ ├── .gomodguard.yaml
│ ├── LICENSE
│ ├── OWNERS
│ ├── README.md
│ ├── auth.go
│ ├── client.go
│ ├── client_test.go
│ ├── clientv3util/
│ │ ├── example_key_test.go
│ │ └── util.go
│ ├── cluster.go
│ ├── compact_op.go
│ ├── compact_op_test.go
│ ├── compare.go
│ ├── concurrency/
│ │ ├── doc.go
│ │ ├── election.go
│ │ ├── key.go
│ │ ├── main_test.go
│ │ ├── mutex.go
│ │ ├── session.go
│ │ ├── stm.go
│ │ └── stm_test.go
│ ├── config.go
│ ├── config_test.go
│ ├── credentials/
│ │ ├── credentials.go
│ │ └── credentials_test.go
│ ├── ctx.go
│ ├── ctx_test.go
│ ├── doc.go
│ ├── experimental/
│ │ └── recipes/
│ │ ├── barrier.go
│ │ ├── client.go
│ │ ├── doc.go
│ │ ├── double_barrier.go
│ │ ├── grpc_gateway/
│ │ │ └── user_add.sh
│ │ ├── key.go
│ │ ├── priority_queue.go
│ │ ├── queue.go
│ │ ├── rwmutex.go
│ │ └── watch.go
│ ├── go.mod
│ ├── go.sum
│ ├── internal/
│ │ ├── endpoint/
│ │ │ ├── endpoint.go
│ │ │ └── endpoint_test.go
│ │ └── resolver/
│ │ └── resolver.go
│ ├── kubernetes/
│ │ ├── client.go
│ │ └── interface.go
│ ├── kv.go
│ ├── lease.go
│ ├── leasing/
│ │ ├── cache.go
│ │ ├── doc.go
│ │ ├── kv.go
│ │ ├── txn.go
│ │ └── util.go
│ ├── logger.go
│ ├── main_test.go
│ ├── maintenance.go
│ ├── mirror/
│ │ └── syncer.go
│ ├── mock/
│ │ └── mockserver/
│ │ ├── doc.go
│ │ └── mockserver.go
│ ├── namespace/
│ │ ├── doc.go
│ │ ├── kv.go
│ │ ├── lease.go
│ │ ├── util.go
│ │ ├── util_test.go
│ │ └── watch.go
│ ├── naming/
│ │ ├── doc.go
│ │ ├── endpoints/
│ │ │ ├── endpoints.go
│ │ │ ├── endpoints_impl.go
│ │ │ └── internal/
│ │ │ └── update.go
│ │ └── resolver/
│ │ └── resolver.go
│ ├── op.go
│ ├── op_test.go
│ ├── options.go
│ ├── ordering/
│ │ ├── doc.go
│ │ ├── kv.go
│ │ ├── kv_test.go
│ │ └── util.go
│ ├── retry.go
│ ├── retry_interceptor.go
│ ├── retry_interceptor_test.go
│ ├── snapshot/
│ │ ├── doc.go
│ │ └── v3_snapshot.go
│ ├── sort.go
│ ├── txn.go
│ ├── txn_test.go
│ ├── utils.go
│ ├── watch.go
│ ├── watch_test.go
│ └── yaml/
│ ├── config.go
│ └── config_test.go
├── code-of-conduct.md
├── codecov.yml
├── contrib/
│ ├── OWNERS
│ ├── README.md
│ ├── lock/
│ │ └── README.md
│ ├── mixin/
│ │ ├── .gitignore
│ │ ├── .lint
│ │ ├── Makefile
│ │ ├── OWNERS
│ │ ├── README.md
│ │ ├── alerts/
│ │ │ └── alerts.libsonnet
│ │ ├── config.libsonnet
│ │ ├── dashboards/
│ │ │ ├── dashboards.libsonnet
│ │ │ ├── etcd-grafana7x.libsonnet
│ │ │ ├── etcd.libsonnet
│ │ │ ├── g.libsonnet
│ │ │ ├── panels.libsonnet
│ │ │ ├── targets.libsonnet
│ │ │ └── variables.libsonnet
│ │ ├── jsonnetfile.json
│ │ ├── jsonnetfile.lock.json
│ │ ├── mixin.libsonnet
│ │ └── test.yaml
│ ├── raftexample/
│ │ ├── Procfile
│ │ ├── README.md
│ │ ├── doc.go
│ │ ├── httpapi.go
│ │ ├── kvstore.go
│ │ ├── kvstore_test.go
│ │ ├── listener.go
│ │ ├── main.go
│ │ ├── raft.go
│ │ ├── raft_test.go
│ │ └── raftexample_test.go
│ └── systemd/
│ ├── etcd.service
│ ├── etcd3-multinode/
│ │ └── README.md
│ └── sysusers.d/
│ └── 20-etcd.conf
├── dummy.go
├── etcd.conf.yml.sample
├── etcdctl/
│ ├── .gomodguard.yaml
│ ├── LICENSE
│ ├── OWNERS
│ ├── README.md
│ ├── READMEv2.md
│ ├── ctlv3/
│ │ ├── command/
│ │ │ ├── alarm_command.go
│ │ │ ├── auth_command.go
│ │ │ ├── check.go
│ │ │ ├── compaction_command.go
│ │ │ ├── completion_command.go
│ │ │ ├── defrag_command.go
│ │ │ ├── del_command.go
│ │ │ ├── diagnosis/
│ │ │ │ ├── engine/
│ │ │ │ │ ├── diagnosis.go
│ │ │ │ │ └── intf/
│ │ │ │ │ └── plugin.go
│ │ │ │ ├── examples/
│ │ │ │ │ └── etcd_diagnosis_report.json
│ │ │ │ └── plugins/
│ │ │ │ ├── common/
│ │ │ │ │ ├── checker.go
│ │ │ │ │ └── client.go
│ │ │ │ ├── epstatus/
│ │ │ │ │ └── plugin.go
│ │ │ │ ├── membership/
│ │ │ │ │ └── plugin.go
│ │ │ │ ├── metrics/
│ │ │ │ │ └── plugin.go
│ │ │ │ └── read/
│ │ │ │ └── plugin.go
│ │ │ ├── diagnosis_command.go
│ │ │ ├── doc.go
│ │ │ ├── downgrade_command.go
│ │ │ ├── elect_command.go
│ │ │ ├── ep_command.go
│ │ │ ├── get_command.go
│ │ │ ├── global.go
│ │ │ ├── groups.go
│ │ │ ├── help_command.go
│ │ │ ├── lease_command.go
│ │ │ ├── lock_command.go
│ │ │ ├── make_mirror_command.go
│ │ │ ├── member_command.go
│ │ │ ├── move_leader_command.go
│ │ │ ├── options_command.go
│ │ │ ├── printer.go
│ │ │ ├── printer_fields.go
│ │ │ ├── printer_json.go
│ │ │ ├── printer_json_test.go
│ │ │ ├── printer_protobuf.go
│ │ │ ├── printer_simple.go
│ │ │ ├── printer_table.go
│ │ │ ├── put_command.go
│ │ │ ├── role_command.go
│ │ │ ├── snapshot_command.go
│ │ │ ├── txn_command.go
│ │ │ ├── user_command.go
│ │ │ ├── util.go
│ │ │ ├── util_test.go
│ │ │ ├── version_command.go
│ │ │ ├── watch_command.go
│ │ │ └── watch_command_test.go
│ │ └── ctl.go
│ ├── doc/
│ │ └── mirror_maker.md
│ ├── go.mod
│ ├── go.sum
│ ├── main.go
│ └── util/
│ └── normalizer.go
├── etcdutl/
│ ├── .gomodguard.yaml
│ ├── LICENSE
│ ├── OWNERS
│ ├── README.md
│ ├── ctl.go
│ ├── etcdutl/
│ │ ├── bucket_command.go
│ │ ├── common.go
│ │ ├── common_test.go
│ │ ├── completion_commmand.go
│ │ ├── defrag_command.go
│ │ ├── hashkv_command.go
│ │ ├── hashkv_command_test.go
│ │ ├── migrate_command.go
│ │ ├── printer.go
│ │ ├── printer_fields.go
│ │ ├── printer_json.go
│ │ ├── printer_protobuf.go
│ │ ├── printer_simple.go
│ │ ├── printer_table.go
│ │ ├── snapshot_command.go
│ │ └── version_command.go
│ ├── go.mod
│ ├── go.sum
│ ├── main.go
│ └── snapshot/
│ ├── doc.go
│ ├── v3_snapshot.go
│ └── v3_snapshot_test.go
├── go.mod
├── go.sum
├── go.work
├── go.work.sum
├── hack/
│ ├── README.md
│ ├── benchmark/
│ │ ├── README.md
│ │ └── bench.sh
│ ├── insta-discovery/
│ │ ├── Procfile
│ │ ├── README.md
│ │ └── discovery
│ ├── kubernetes-deploy/
│ │ ├── README.md
│ │ ├── etcd.yml
│ │ └── vulcand.yml
│ ├── patch/
│ │ ├── README.md
│ │ └── cherrypick.sh
│ └── tls-setup/
│ ├── Makefile
│ ├── Procfile
│ ├── README.md
│ └── config/
│ ├── ca-config.json
│ ├── ca-csr.json
│ └── req-csr.json
├── pkg/
│ ├── .gomodguard.yaml
│ ├── LICENSE
│ ├── README.md
│ ├── adt/
│ │ ├── README.md
│ │ ├── adt.go
│ │ ├── example_test.go
│ │ ├── interval_tree.go
│ │ └── interval_tree_test.go
│ ├── cobrautl/
│ │ ├── error.go
│ │ └── help.go
│ ├── contention/
│ │ ├── contention.go
│ │ └── doc.go
│ ├── cpuutil/
│ │ ├── doc.go
│ │ └── endian.go
│ ├── crc/
│ │ ├── crc.go
│ │ └── crc_test.go
│ ├── debugutil/
│ │ ├── doc.go
│ │ └── pprof.go
│ ├── expect/
│ │ ├── expect.go
│ │ └── expect_test.go
│ ├── featuregate/
│ │ ├── feature_gate.go
│ │ └── feature_gate_test.go
│ ├── flags/
│ │ ├── flag.go
│ │ ├── flag_test.go
│ │ ├── ignored.go
│ │ ├── selective_string.go
│ │ ├── selective_string_test.go
│ │ ├── strings.go
│ │ ├── strings_test.go
│ │ ├── uint32.go
│ │ ├── uint32_test.go
│ │ ├── unique_strings.go
│ │ ├── unique_strings_test.go
│ │ ├── unique_urls.go
│ │ ├── unique_urls_test.go
│ │ ├── urls.go
│ │ └── urls_test.go
│ ├── go.mod
│ ├── go.sum
│ ├── grpctesting/
│ │ ├── recorder.go
│ │ └── stub_server.go
│ ├── httputil/
│ │ ├── httputil.go
│ │ └── httputil_test.go
│ ├── idutil/
│ │ ├── id.go
│ │ └── id_test.go
│ ├── ioutil/
│ │ ├── pagewriter.go
│ │ ├── pagewriter_test.go
│ │ ├── readcloser.go
│ │ ├── readcloser_test.go
│ │ ├── reader.go
│ │ ├── reader_test.go
│ │ └── util.go
│ ├── netutil/
│ │ ├── doc.go
│ │ ├── host_normalize.go
│ │ ├── host_normalize_test.go
│ │ ├── netutil.go
│ │ ├── netutil_test.go
│ │ ├── routes.go
│ │ ├── routes_linux.go
│ │ └── routes_linux_test.go
│ ├── notify/
│ │ └── notify.go
│ ├── osutil/
│ │ ├── interrupt_unix.go
│ │ ├── interrupt_windows.go
│ │ ├── osutil.go
│ │ ├── osutil_test.go
│ │ ├── signal.go
│ │ └── signal_linux.go
│ ├── pbutil/
│ │ ├── pbutil.go
│ │ └── pbutil_test.go
│ ├── proxy/
│ │ ├── doc.go
│ │ ├── fixtures/
│ │ │ ├── ca-csr.json
│ │ │ ├── ca.crt
│ │ │ ├── gencert.json
│ │ │ ├── gencerts.sh
│ │ │ ├── server-ca-csr.json
│ │ │ ├── server.crt
│ │ │ └── server.key.insecure
│ │ ├── server.go
│ │ └── server_test.go
│ ├── report/
│ │ ├── doc.go
│ │ ├── perfdash.go
│ │ ├── report.go
│ │ ├── report_test.go
│ │ ├── timeseries.go
│ │ ├── timeseries_test.go
│ │ └── weighted.go
│ ├── runtime/
│ │ ├── fds_linux.go
│ │ └── fds_other.go
│ ├── schedule/
│ │ ├── doc.go
│ │ ├── schedule.go
│ │ └── schedule_test.go
│ ├── stringutil/
│ │ ├── doc.go
│ │ ├── rand.go
│ │ └── rand_test.go
│ ├── traceutil/
│ │ ├── trace.go
│ │ └── trace_test.go
│ └── wait/
│ ├── wait.go
│ ├── wait_test.go
│ ├── wait_time.go
│ └── wait_time_test.go
├── scripts/
│ ├── OWNERS
│ ├── README
│ ├── benchmark_test.sh
│ ├── build-binary.sh
│ ├── build-docker.sh
│ ├── build-release.sh
│ ├── build.sh
│ ├── build_lib.sh
│ ├── build_tools.sh
│ ├── codecov_upload.sh
│ ├── etcd_version_annotations.txt
│ ├── fix/
│ │ ├── bom.sh
│ │ ├── mod-tidy.sh
│ │ ├── shell_ws.sh
│ │ └── yamllint.sh
│ ├── fuzzing.sh
│ ├── genproto.sh
│ ├── markdown_diff_lint.sh
│ ├── measure-testgrid-flakiness.sh
│ ├── release.sh
│ ├── release_mod.sh
│ ├── release_notes.tpl.txt
│ ├── sync_go_toolchain_directive.sh
│ ├── test.sh
│ ├── test_images.sh
│ ├── test_lib.sh
│ ├── test_utils.sh
│ ├── update_dep.sh
│ ├── update_go_workspace.sh
│ ├── update_proto_annotations.sh
│ ├── verify_genproto.sh
│ ├── verify_go_versions.sh
│ ├── verify_golangci-lint_version.sh
│ ├── verify_grpc_experimental.sh
│ └── verify_proto_annotations.sh
├── security/
│ ├── OWNERS
│ ├── README.md
│ ├── email-templates.md
│ └── security-release-process.md
├── server/
│ ├── .gomodguard.yaml
│ ├── LICENSE
│ ├── auth/
│ │ ├── doc.go
│ │ ├── jwt.go
│ │ ├── jwt_test.go
│ │ ├── main_test.go
│ │ ├── metrics.go
│ │ ├── nop.go
│ │ ├── options.go
│ │ ├── range_perm_cache.go
│ │ ├── range_perm_cache_test.go
│ │ ├── simple_token.go
│ │ ├── simple_token_test.go
│ │ ├── store.go
│ │ ├── store_mock_test.go
│ │ └── store_test.go
│ ├── config/
│ │ ├── config.go
│ │ ├── config_test.go
│ │ ├── v2_deprecation.go
│ │ └── v2_deprecation_test.go
│ ├── embed/
│ │ ├── auth_test.go
│ │ ├── config.go
│ │ ├── config_logging.go
│ │ ├── config_logging_journal_unix.go
│ │ ├── config_logging_journal_windows.go
│ │ ├── config_test.go
│ │ ├── config_tracing.go
│ │ ├── config_tracing_test.go
│ │ ├── doc.go
│ │ ├── etcd.go
│ │ ├── etcd_test.go
│ │ ├── serve.go
│ │ ├── serve_test.go
│ │ └── util.go
│ ├── etcdmain/
│ │ ├── config.go
│ │ ├── config_test.go
│ │ ├── doc.go
│ │ ├── etcd.go
│ │ ├── gateway.go
│ │ ├── grpc_proxy.go
│ │ ├── grpc_proxy_logger.go
│ │ ├── grpc_proxy_logger_test.go
│ │ ├── help.go
│ │ ├── main.go
│ │ └── util.go
│ ├── etcdserver/
│ │ ├── adapters.go
│ │ ├── api/
│ │ │ ├── capability.go
│ │ │ ├── cluster.go
│ │ │ ├── doc.go
│ │ │ ├── etcdhttp/
│ │ │ │ ├── debug.go
│ │ │ │ ├── doc.go
│ │ │ │ ├── health.go
│ │ │ │ ├── health_test.go
│ │ │ │ ├── metrics.go
│ │ │ │ ├── peer.go
│ │ │ │ ├── peer_test.go
│ │ │ │ ├── types/
│ │ │ │ │ ├── errors.go
│ │ │ │ │ └── errors_test.go
│ │ │ │ ├── utils.go
│ │ │ │ ├── version.go
│ │ │ │ └── version_test.go
│ │ │ ├── membership/
│ │ │ │ ├── cluster.go
│ │ │ │ ├── cluster_opts.go
│ │ │ │ ├── cluster_test.go
│ │ │ │ ├── doc.go
│ │ │ │ ├── errors.go
│ │ │ │ ├── member.go
│ │ │ │ ├── member_test.go
│ │ │ │ ├── membership_test.go
│ │ │ │ ├── metrics.go
│ │ │ │ ├── store.go
│ │ │ │ ├── storev2.go
│ │ │ │ └── storev2_test.go
│ │ │ ├── rafthttp/
│ │ │ │ ├── coder.go
│ │ │ │ ├── doc.go
│ │ │ │ ├── fake_roundtripper_test.go
│ │ │ │ ├── functional_test.go
│ │ │ │ ├── http.go
│ │ │ │ ├── http_test.go
│ │ │ │ ├── metrics.go
│ │ │ │ ├── msg_codec.go
│ │ │ │ ├── msg_codec_test.go
│ │ │ │ ├── msgappv2_codec.go
│ │ │ │ ├── msgappv2_codec_test.go
│ │ │ │ ├── peer.go
│ │ │ │ ├── peer_status.go
│ │ │ │ ├── peer_test.go
│ │ │ │ ├── pipeline.go
│ │ │ │ ├── pipeline_test.go
│ │ │ │ ├── probing_status.go
│ │ │ │ ├── remote.go
│ │ │ │ ├── snapshot_sender.go
│ │ │ │ ├── snapshot_test.go
│ │ │ │ ├── stream.go
│ │ │ │ ├── stream_test.go
│ │ │ │ ├── transport.go
│ │ │ │ ├── transport_bench_test.go
│ │ │ │ ├── transport_test.go
│ │ │ │ ├── urlpick.go
│ │ │ │ ├── urlpick_test.go
│ │ │ │ ├── util.go
│ │ │ │ └── util_test.go
│ │ │ ├── snap/
│ │ │ │ ├── db.go
│ │ │ │ ├── doc.go
│ │ │ │ ├── message.go
│ │ │ │ ├── metrics.go
│ │ │ │ ├── snappb/
│ │ │ │ │ ├── snap.pb.go
│ │ │ │ │ └── snap.proto
│ │ │ │ ├── snapshotter.go
│ │ │ │ └── snapshotter_test.go
│ │ │ ├── v2error/
│ │ │ │ ├── error.go
│ │ │ │ └── error_test.go
│ │ │ ├── v2stats/
│ │ │ │ ├── leader.go
│ │ │ │ ├── queue.go
│ │ │ │ └── server.go
│ │ │ ├── v2store/
│ │ │ │ ├── doc.go
│ │ │ │ ├── event.go
│ │ │ │ ├── event_history.go
│ │ │ │ ├── event_queue.go
│ │ │ │ ├── event_test.go
│ │ │ │ ├── heap_test.go
│ │ │ │ ├── metrics.go
│ │ │ │ ├── node.go
│ │ │ │ ├── node_extern.go
│ │ │ │ ├── node_extern_test.go
│ │ │ │ ├── node_test.go
│ │ │ │ ├── stats.go
│ │ │ │ ├── stats_test.go
│ │ │ │ ├── store.go
│ │ │ │ ├── store_bench_test.go
│ │ │ │ ├── store_ttl_test.go
│ │ │ │ ├── ttl_key_heap.go
│ │ │ │ ├── watcher.go
│ │ │ │ ├── watcher_hub.go
│ │ │ │ ├── watcher_hub_test.go
│ │ │ │ └── watcher_test.go
│ │ │ ├── v3alarm/
│ │ │ │ └── alarms.go
│ │ │ ├── v3client/
│ │ │ │ ├── doc.go
│ │ │ │ └── v3client.go
│ │ │ ├── v3compactor/
│ │ │ │ ├── compactor.go
│ │ │ │ ├── compactor_test.go
│ │ │ │ ├── doc.go
│ │ │ │ ├── periodic.go
│ │ │ │ ├── periodic_test.go
│ │ │ │ ├── revision.go
│ │ │ │ └── revision_test.go
│ │ │ ├── v3discovery/
│ │ │ │ ├── discovery.go
│ │ │ │ └── discovery_test.go
│ │ │ ├── v3election/
│ │ │ │ ├── doc.go
│ │ │ │ ├── election.go
│ │ │ │ └── v3electionpb/
│ │ │ │ ├── gw/
│ │ │ │ │ └── v3election.pb.gw.go
│ │ │ │ ├── v3election.pb.go
│ │ │ │ ├── v3election.proto
│ │ │ │ └── v3election_grpc.pb.go
│ │ │ ├── v3lock/
│ │ │ │ ├── doc.go
│ │ │ │ ├── lock.go
│ │ │ │ └── v3lockpb/
│ │ │ │ ├── gw/
│ │ │ │ │ └── v3lock.pb.gw.go
│ │ │ │ ├── v3lock.pb.go
│ │ │ │ ├── v3lock.proto
│ │ │ │ └── v3lock_grpc.pb.go
│ │ │ └── v3rpc/
│ │ │ ├── auth.go
│ │ │ ├── codec.go
│ │ │ ├── grpc.go
│ │ │ ├── header.go
│ │ │ ├── health.go
│ │ │ ├── interceptor.go
│ │ │ ├── key.go
│ │ │ ├── key_test.go
│ │ │ ├── lease.go
│ │ │ ├── maintenance.go
│ │ │ ├── member.go
│ │ │ ├── metrics.go
│ │ │ ├── quota.go
│ │ │ ├── util.go
│ │ │ ├── util_test.go
│ │ │ ├── validationfuzz_test.go
│ │ │ ├── watch.go
│ │ │ └── watch_test.go
│ │ ├── apply/
│ │ │ ├── apply.go
│ │ │ ├── auth.go
│ │ │ ├── auth_test.go
│ │ │ ├── backend.go
│ │ │ ├── capped.go
│ │ │ ├── corrupt.go
│ │ │ ├── interface.go
│ │ │ ├── metrics.go
│ │ │ ├── quota.go
│ │ │ ├── uber_applier.go
│ │ │ └── uber_applier_test.go
│ │ ├── bootstrap.go
│ │ ├── bootstrap_test.go
│ │ ├── cindex/
│ │ │ ├── cindex.go
│ │ │ ├── cindex_test.go
│ │ │ └── doc.go
│ │ ├── cluster_util.go
│ │ ├── cluster_util_test.go
│ │ ├── corrupt.go
│ │ ├── corrupt_test.go
│ │ ├── doc.go
│ │ ├── errors/
│ │ │ └── errors.go
│ │ ├── metrics.go
│ │ ├── raft.go
│ │ ├── raft_test.go
│ │ ├── server.go
│ │ ├── server_access_control.go
│ │ ├── server_access_control_test.go
│ │ ├── server_test.go
│ │ ├── snapshot_merge.go
│ │ ├── tracing.go
│ │ ├── txn/
│ │ │ ├── delete.go
│ │ │ ├── metrics.go
│ │ │ ├── metrics_test.go
│ │ │ ├── put.go
│ │ │ ├── range.go
│ │ │ ├── txn.go
│ │ │ ├── txn_test.go
│ │ │ ├── util.go
│ │ │ ├── util_bench_test.go
│ │ │ └── util_test.go
│ │ ├── util.go
│ │ ├── util_test.go
│ │ ├── v3_server.go
│ │ ├── version/
│ │ │ ├── doc.go
│ │ │ ├── downgrade.go
│ │ │ ├── downgrade_test.go
│ │ │ ├── errors.go
│ │ │ ├── monitor.go
│ │ │ ├── monitor_test.go
│ │ │ ├── version.go
│ │ │ └── version_test.go
│ │ ├── zap_raft.go
│ │ └── zap_raft_test.go
│ ├── features/
│ │ └── etcd_features.go
│ ├── go.mod
│ ├── go.sum
│ ├── lease/
│ │ ├── doc.go
│ │ ├── lease.go
│ │ ├── lease_queue.go
│ │ ├── lease_queue_test.go
│ │ ├── leasehttp/
│ │ │ ├── doc.go
│ │ │ ├── http.go
│ │ │ └── http_test.go
│ │ ├── leasepb/
│ │ │ ├── lease.pb.go
│ │ │ └── lease.proto
│ │ ├── lessor.go
│ │ ├── lessor_bench_test.go
│ │ ├── lessor_test.go
│ │ └── metrics.go
│ ├── main.go
│ ├── mock/
│ │ ├── mockstorage/
│ │ │ ├── doc.go
│ │ │ └── storage_recorder.go
│ │ ├── mockstore/
│ │ │ ├── doc.go
│ │ │ └── store_recorder.go
│ │ └── mockwait/
│ │ ├── doc.go
│ │ └── wait_recorder.go
│ ├── proxy/
│ │ ├── grpcproxy/
│ │ │ ├── adapter/
│ │ │ │ ├── auth_client_adapter.go
│ │ │ │ ├── chan_stream.go
│ │ │ │ ├── cluster_client_adapter.go
│ │ │ │ ├── doc.go
│ │ │ │ ├── election_client_adapter.go
│ │ │ │ ├── kv_client_adapter.go
│ │ │ │ ├── lease_client_adapter.go
│ │ │ │ ├── lock_client_adapter.go
│ │ │ │ ├── maintenance_client_adapter.go
│ │ │ │ └── watch_client_adapter.go
│ │ │ ├── auth.go
│ │ │ ├── cache/
│ │ │ │ └── store.go
│ │ │ ├── cluster.go
│ │ │ ├── doc.go
│ │ │ ├── election.go
│ │ │ ├── health.go
│ │ │ ├── kv.go
│ │ │ ├── leader.go
│ │ │ ├── lease.go
│ │ │ ├── lock.go
│ │ │ ├── maintenance.go
│ │ │ ├── metrics.go
│ │ │ ├── register.go
│ │ │ ├── util.go
│ │ │ ├── watch.go
│ │ │ ├── watch_broadcast.go
│ │ │ ├── watch_broadcasts.go
│ │ │ ├── watch_ranges.go
│ │ │ └── watcher.go
│ │ └── tcpproxy/
│ │ ├── doc.go
│ │ ├── userspace.go
│ │ └── userspace_test.go
│ ├── storage/
│ │ ├── backend/
│ │ │ ├── backend.go
│ │ │ ├── backend_bench_test.go
│ │ │ ├── backend_test.go
│ │ │ ├── batch_tx.go
│ │ │ ├── batch_tx_test.go
│ │ │ ├── config_default.go
│ │ │ ├── config_linux.go
│ │ │ ├── config_windows.go
│ │ │ ├── doc.go
│ │ │ ├── export_test.go
│ │ │ ├── hooks.go
│ │ │ ├── hooks_test.go
│ │ │ ├── metrics.go
│ │ │ ├── read_tx.go
│ │ │ ├── testing/
│ │ │ │ └── betesting.go
│ │ │ ├── tx_buffer.go
│ │ │ ├── tx_buffer_test.go
│ │ │ ├── verify.go
│ │ │ └── verify_test.go
│ │ ├── backend.go
│ │ ├── datadir/
│ │ │ ├── datadir.go
│ │ │ ├── datadir_test.go
│ │ │ └── doc.go
│ │ ├── hooks.go
│ │ ├── metrics.go
│ │ ├── mvcc/
│ │ │ ├── doc.go
│ │ │ ├── hash.go
│ │ │ ├── hash_test.go
│ │ │ ├── index.go
│ │ │ ├── index_bench_test.go
│ │ │ ├── index_test.go
│ │ │ ├── key_index.go
│ │ │ ├── key_index_test.go
│ │ │ ├── kv.go
│ │ │ ├── kv_test.go
│ │ │ ├── kv_view.go
│ │ │ ├── kvstore.go
│ │ │ ├── kvstore_bench_test.go
│ │ │ ├── kvstore_compaction.go
│ │ │ ├── kvstore_compaction_test.go
│ │ │ ├── kvstore_test.go
│ │ │ ├── kvstore_txn.go
│ │ │ ├── metrics.go
│ │ │ ├── metrics_txn.go
│ │ │ ├── revision.go
│ │ │ ├── store.go
│ │ │ ├── store_test.go
│ │ │ ├── testutil/
│ │ │ │ └── hash.go
│ │ │ ├── watchable_store.go
│ │ │ ├── watchable_store_bench_test.go
│ │ │ ├── watchable_store_test.go
│ │ │ ├── watchable_store_txn.go
│ │ │ ├── watcher.go
│ │ │ ├── watcher_bench_test.go
│ │ │ ├── watcher_group.go
│ │ │ └── watcher_test.go
│ │ ├── quota.go
│ │ ├── schema/
│ │ │ ├── actions.go
│ │ │ ├── actions_test.go
│ │ │ ├── alarm.go
│ │ │ ├── auth.go
│ │ │ ├── auth_roles.go
│ │ │ ├── auth_roles_test.go
│ │ │ ├── auth_test.go
│ │ │ ├── auth_users.go
│ │ │ ├── auth_users_test.go
│ │ │ ├── bucket.go
│ │ │ ├── changes.go
│ │ │ ├── changes_test.go
│ │ │ ├── cindex.go
│ │ │ ├── confstate.go
│ │ │ ├── confstate_test.go
│ │ │ ├── lease.go
│ │ │ ├── lease_test.go
│ │ │ ├── membership.go
│ │ │ ├── migration.go
│ │ │ ├── migration_test.go
│ │ │ ├── schema.go
│ │ │ ├── schema_test.go
│ │ │ ├── version.go
│ │ │ └── version_test.go
│ │ ├── storage.go
│ │ ├── util.go
│ │ └── wal/
│ │ ├── decoder.go
│ │ ├── doc.go
│ │ ├── encoder.go
│ │ ├── file_pipeline.go
│ │ ├── file_pipeline_test.go
│ │ ├── metrics.go
│ │ ├── record_test.go
│ │ ├── repair.go
│ │ ├── repair_test.go
│ │ ├── testdata/
│ │ │ └── TestNew.wal
│ │ ├── testing/
│ │ │ └── waltesting.go
│ │ ├── util.go
│ │ ├── version.go
│ │ ├── version_test.go
│ │ ├── wal.go
│ │ ├── wal_bench_test.go
│ │ ├── wal_test.go
│ │ └── walpb/
│ │ ├── record.go
│ │ ├── record.pb.go
│ │ ├── record.proto
│ │ └── record_test.go
│ └── verify/
│ ├── doc.go
│ └── verify.go
├── tests/
│ ├── LICENSE
│ ├── OWNERS
│ ├── antithesis/
│ │ ├── Makefile
│ │ ├── README.md
│ │ ├── config/
│ │ │ ├── Dockerfile
│ │ │ ├── docker-compose-1-node.yml
│ │ │ ├── docker-compose-3-node.yml
│ │ │ └── manifests/
│ │ │ └── default-etcd-3-replicas.yaml
│ │ ├── server/
│ │ │ ├── Dockerfile
│ │ │ └── inject/
│ │ │ └── verify.patch
│ │ └── test-template/
│ │ ├── Dockerfile
│ │ ├── entrypoint/
│ │ │ └── main.go
│ │ └── robustness/
│ │ ├── common/
│ │ │ └── path.go
│ │ ├── finally/
│ │ │ └── main.go
│ │ └── traffic/
│ │ └── main.go
│ ├── common/
│ │ ├── alarm_test.go
│ │ ├── auth_test.go
│ │ ├── auth_util.go
│ │ ├── compact_test.go
│ │ ├── defrag_test.go
│ │ ├── e2e_test.go
│ │ ├── endpoint_test.go
│ │ ├── grpc_test.go
│ │ ├── hashkv_test.go
│ │ ├── integration_test.go
│ │ ├── kv_test.go
│ │ ├── lease_test.go
│ │ ├── main_test.go
│ │ ├── maintenance_auth_test.go
│ │ ├── member_test.go
│ │ ├── role_test.go
│ │ ├── status_test.go
│ │ ├── txn_test.go
│ │ ├── unit_test.go
│ │ ├── user_test.go
│ │ ├── wait_leader_test.go
│ │ └── watch_test.go
│ ├── e2e/
│ │ ├── cluster_downgrade_test.go
│ │ ├── cmux_test.go
│ │ ├── corrupt_test.go
│ │ ├── ctl_v3_auth_cluster_test.go
│ │ ├── ctl_v3_auth_no_proxy_test.go
│ │ ├── ctl_v3_auth_security_test.go
│ │ ├── ctl_v3_auth_test.go
│ │ ├── ctl_v3_completion_test.go
│ │ ├── ctl_v3_defrag_test.go
│ │ ├── ctl_v3_elect_test.go
│ │ ├── ctl_v3_kv_test.go
│ │ ├── ctl_v3_lease_test.go
│ │ ├── ctl_v3_lock_test.go
│ │ ├── ctl_v3_make_mirror_test.go
│ │ ├── ctl_v3_member_no_proxy_test.go
│ │ ├── ctl_v3_member_test.go
│ │ ├── ctl_v3_move_leader_test.go
│ │ ├── ctl_v3_role_test.go
│ │ ├── ctl_v3_snapshot_test.go
│ │ ├── ctl_v3_test.go
│ │ ├── ctl_v3_watch_test.go
│ │ ├── defrag_no_space_test.go
│ │ ├── discovery_v3_test.go
│ │ ├── doc.go
│ │ ├── etcd_config_test.go
│ │ ├── etcd_grpcproxy_test.go
│ │ ├── etcd_mix_versions_test.go
│ │ ├── etcd_release_upgrade_test.go
│ │ ├── failover_test.go
│ │ ├── force_new_cluster_test.go
│ │ ├── gateway_test.go
│ │ ├── graceful_shutdown_test.go
│ │ ├── http_health_check_test.go
│ │ ├── leader_snapshot_no_proxy_test.go
│ │ ├── logging_test.go
│ │ ├── main_test.go
│ │ ├── member_no_proxy_test.go
│ │ ├── metrics_test.go
│ │ ├── promote_experimental_flag_test.go
│ │ ├── reproduce_17780_test.go
│ │ ├── reproduce_18667_test.go
│ │ ├── reproduce_19406_test.go
│ │ ├── reproduce_20271_test.go
│ │ ├── runtime_reconfiguration_test.go
│ │ ├── utils.go
│ │ ├── utl_migrate_test.go
│ │ ├── v2store_deprecation_test.go
│ │ ├── v3_cipher_suite_test.go
│ │ ├── v3_curl_auth_test.go
│ │ ├── v3_curl_cluster_test.go
│ │ ├── v3_curl_election_test.go
│ │ ├── v3_curl_kv_test.go
│ │ ├── v3_curl_lease_test.go
│ │ ├── v3_curl_lock_test.go
│ │ ├── v3_curl_maintenance_test.go
│ │ ├── v3_curl_maxstream_test.go
│ │ ├── v3_curl_watch_test.go
│ │ ├── v3_lease_no_proxy_test.go
│ │ ├── watch_test.go
│ │ └── zap_logging_test.go
│ ├── fixtures/
│ │ ├── CommonName-root.crt
│ │ ├── CommonName-root.key
│ │ ├── ca-csr.json
│ │ ├── ca.crt
│ │ ├── client-ca-csr-nocn.json
│ │ ├── client-clientusage.crt
│ │ ├── client-clientusage.key.insecure
│ │ ├── client-nocn.crt
│ │ ├── client-nocn.key.insecure
│ │ ├── ed25519-private-key.pem
│ │ ├── ed25519-public-key.pem
│ │ ├── gencert.json
│ │ ├── gencerts.sh
│ │ ├── revoke.crl
│ │ ├── server-ca-csr-ecdsa.json
│ │ ├── server-ca-csr-ip.json
│ │ ├── server-ca-csr-ipv6.json
│ │ ├── server-ca-csr-wildcard.json
│ │ ├── server-ca-csr.json
│ │ ├── server-ca-csr2.json
│ │ ├── server-ca-csr3.json
│ │ ├── server-ecdsa.crt
│ │ ├── server-ecdsa.key.insecure
│ │ ├── server-ip.crt
│ │ ├── server-ip.key.insecure
│ │ ├── server-ipv6.crt
│ │ ├── server-ipv6.key.insecure
│ │ ├── server-revoked.crt
│ │ ├── server-revoked.key.insecure
│ │ ├── server-serverusage.crt
│ │ ├── server-serverusage.key.insecure
│ │ ├── server-wildcard.crt
│ │ ├── server-wildcard.key.insecure
│ │ ├── server.crt
│ │ ├── server.key.insecure
│ │ ├── server2.crt
│ │ ├── server2.key.insecure
│ │ ├── server3.crt
│ │ └── server3.key.insecure
│ ├── framework/
│ │ ├── config/
│ │ │ ├── client.go
│ │ │ └── cluster.go
│ │ ├── e2e/
│ │ │ ├── cluster.go
│ │ │ ├── cluster_direct.go
│ │ │ ├── cluster_proxy.go
│ │ │ ├── cluster_test.go
│ │ │ ├── config.go
│ │ │ ├── curl.go
│ │ │ ├── downgrade.go
│ │ │ ├── e2e.go
│ │ │ ├── etcd_process.go
│ │ │ ├── etcd_spawn.go
│ │ │ ├── etcdctl.go
│ │ │ ├── etcdctl_test.go
│ │ │ ├── flags.go
│ │ │ ├── lazyfs.go
│ │ │ ├── metrics.go
│ │ │ ├── testing.go
│ │ │ └── util.go
│ │ ├── integration/
│ │ │ ├── bridge.go
│ │ │ ├── cluster.go
│ │ │ ├── cluster_direct.go
│ │ │ ├── cluster_proxy.go
│ │ │ ├── config.go
│ │ │ ├── integration.go
│ │ │ └── testing.go
│ │ ├── interfaces/
│ │ │ └── interface.go
│ │ ├── testrunner.go
│ │ ├── testutils/
│ │ │ ├── execute.go
│ │ │ ├── helpters.go
│ │ │ ├── log_observer.go
│ │ │ ├── log_observer_test.go
│ │ │ └── path.go
│ │ └── unit/
│ │ └── unit.go
│ ├── go.mod
│ ├── go.sum
│ ├── integration/
│ │ ├── cache_test.go
│ │ ├── clientv3/
│ │ │ ├── cluster_test.go
│ │ │ ├── concurrency/
│ │ │ │ ├── election_test.go
│ │ │ │ ├── example_election_test.go
│ │ │ │ ├── example_mutex_test.go
│ │ │ │ ├── example_stm_test.go
│ │ │ │ ├── main_test.go
│ │ │ │ ├── mutex_test.go
│ │ │ │ └── session_test.go
│ │ │ ├── connectivity/
│ │ │ │ ├── black_hole_test.go
│ │ │ │ ├── dial_test.go
│ │ │ │ ├── doc.go
│ │ │ │ ├── main_test.go
│ │ │ │ ├── network_partition_test.go
│ │ │ │ └── server_shutdown_test.go
│ │ │ ├── doc.go
│ │ │ ├── examples/
│ │ │ │ ├── example_auth_test.go
│ │ │ │ ├── example_cluster_test.go
│ │ │ │ ├── example_kv_test.go
│ │ │ │ ├── example_lease_test.go
│ │ │ │ ├── example_maintenance_test.go
│ │ │ │ ├── example_metrics_test.go
│ │ │ │ ├── example_test.go
│ │ │ │ ├── example_watch_test.go
│ │ │ │ └── main_test.go
│ │ │ ├── experimental/
│ │ │ │ └── recipes/
│ │ │ │ ├── v3_barrier_test.go
│ │ │ │ ├── v3_double_barrier_test.go
│ │ │ │ ├── v3_lock_test.go
│ │ │ │ └── v3_queue_test.go
│ │ │ ├── kv_test.go
│ │ │ ├── lease/
│ │ │ │ ├── doc.go
│ │ │ │ ├── lease_test.go
│ │ │ │ ├── leasing_test.go
│ │ │ │ └── main_test.go
│ │ │ ├── main_test.go
│ │ │ ├── maintenance_test.go
│ │ │ ├── metrics_test.go
│ │ │ ├── mirror_auth_test.go
│ │ │ ├── mirror_test.go
│ │ │ ├── namespace_test.go
│ │ │ ├── naming/
│ │ │ │ ├── endpoints_test.go
│ │ │ │ ├── main_test.go
│ │ │ │ └── resolver_test.go
│ │ │ ├── ordering_kv_test.go
│ │ │ ├── ordering_util_test.go
│ │ │ ├── snapshot/
│ │ │ │ └── v3_snapshot_test.go
│ │ │ ├── txn_test.go
│ │ │ ├── user_test.go
│ │ │ ├── util.go
│ │ │ └── watch/
│ │ │ ├── v3_watch_restore_test.go
│ │ │ ├── v3_watch_test.go
│ │ │ ├── watch_fragment_test.go
│ │ │ └── watch_test.go
│ │ ├── cluster_test.go
│ │ ├── corrupt_test.go
│ │ ├── doc.go
│ │ ├── embed/
│ │ │ ├── embed_proxy_test.go
│ │ │ └── embed_test.go
│ │ ├── fixtures-expired/
│ │ │ ├── README
│ │ │ ├── ca-csr.json
│ │ │ ├── ca.crt
│ │ │ ├── gencert.json
│ │ │ ├── gencerts.sh
│ │ │ ├── server-ca-csr-ip.json
│ │ │ ├── server-ca-csr.json
│ │ │ ├── server-ip.crt
│ │ │ ├── server-ip.key.insecure
│ │ │ ├── server.crt
│ │ │ └── server.key.insecure
│ │ ├── lazy_cluster.go
│ │ ├── main_test.go
│ │ ├── member_test.go
│ │ ├── metrics_test.go
│ │ ├── network_partition_test.go
│ │ ├── proxy/
│ │ │ └── grpcproxy/
│ │ │ ├── cluster_test.go
│ │ │ ├── kv_test.go
│ │ │ └── register_test.go
│ │ ├── revision_test.go
│ │ ├── snapshot/
│ │ │ ├── member_test.go
│ │ │ └── v3_snapshot_test.go
│ │ ├── testing_test.go
│ │ ├── tracing_test.go
│ │ ├── util_test.go
│ │ ├── utl_wal_version_test.go
│ │ ├── v2store/
│ │ │ ├── main_test.go
│ │ │ ├── store_tag_test.go
│ │ │ └── store_test.go
│ │ ├── v3_alarm_test.go
│ │ ├── v3_auth_test.go
│ │ ├── v3_election_test.go
│ │ ├── v3_failover_test.go
│ │ ├── v3_grpc_inflight_test.go
│ │ ├── v3_grpc_test.go
│ │ ├── v3_kv_test.go
│ │ ├── v3_leadership_test.go
│ │ ├── v3_lease_test.go
│ │ ├── v3_stm_test.go
│ │ ├── v3_tls_test.go
│ │ ├── v3election_grpc_test.go
│ │ └── v3lock_grpc_test.go
│ ├── robustness/
│ │ ├── Makefile
│ │ ├── OWNERS
│ │ ├── README.md
│ │ ├── client/
│ │ │ ├── client.go
│ │ │ ├── kvhash.go
│ │ │ └── watch.go
│ │ ├── coverage/
│ │ │ ├── README.md
│ │ │ ├── apiserver-shared-conf/
│ │ │ │ └── tracing.yaml
│ │ │ ├── collect_kind_traces.sh
│ │ │ ├── contract_test.go
│ │ │ ├── coverage_test.go
│ │ │ ├── key_pattern_test.go
│ │ │ ├── kind-with-tracing.yaml
│ │ │ ├── matchers_test.go
│ │ │ ├── patches/
│ │ │ │ └── kubernetes/
│ │ │ │ ├── 0001-Add-Open-Telemetry-trace-event-when-passing-through-.patch
│ │ │ │ ├── 0002-Add-kubernetesEtcdContractTracker.patch
│ │ │ │ ├── 0003-Add-1m-timeout-to-Watch-to-ensure-Spans-are-exported.patch
│ │ │ │ └── 0004-Configure-cmd-tests.patch
│ │ │ ├── sort_test.go
│ │ │ └── testdata/
│ │ │ └── .gitignore
│ │ ├── failpoint/
│ │ │ ├── cluster.go
│ │ │ ├── failpoint.go
│ │ │ ├── gofail.go
│ │ │ ├── kill.go
│ │ │ ├── network.go
│ │ │ └── trigger.go
│ │ ├── identity/
│ │ │ ├── id.go
│ │ │ └── lease_ids.go
│ │ ├── main_test.go
│ │ ├── model/
│ │ │ ├── describe.go
│ │ │ ├── describe_test.go
│ │ │ ├── deterministic.go
│ │ │ ├── deterministic_test.go
│ │ │ ├── history.go
│ │ │ ├── history_test.go
│ │ │ ├── non_deterministic.go
│ │ │ ├── non_deterministic_test.go
│ │ │ ├── replay.go
│ │ │ ├── types.go
│ │ │ ├── types_test.go
│ │ │ └── watch.go
│ │ ├── options/
│ │ │ ├── cluster_options.go
│ │ │ ├── cluster_options_test.go
│ │ │ └── server_config_options.go
│ │ ├── patches/
│ │ │ ├── beforeSendWatchResponse/
│ │ │ │ ├── build.patch
│ │ │ │ └── watch.patch
│ │ │ └── compactBeforeSetFinishedCompact/
│ │ │ └── kvstore_compaction.patch
│ │ ├── random/
│ │ │ └── random.go
│ │ ├── report/
│ │ │ ├── client.go
│ │ │ ├── client_test.go
│ │ │ ├── failpoint.go
│ │ │ ├── report.go
│ │ │ ├── wal.go
│ │ │ └── wal_test.go
│ │ ├── scenarios/
│ │ │ └── scenarios.go
│ │ ├── testdata/
│ │ │ └── .gitignore
│ │ ├── traffic/
│ │ │ ├── etcd.go
│ │ │ ├── key_store.go
│ │ │ ├── kubernetes.go
│ │ │ ├── limiter.go
│ │ │ ├── limiter_test.go
│ │ │ └── traffic.go
│ │ └── validate/
│ │ ├── operations.go
│ │ ├── operations_test.go
│ │ ├── patch_history.go
│ │ ├── patch_history_test.go
│ │ ├── result.go
│ │ ├── validate.go
│ │ ├── validate_test.go
│ │ └── watch.go
│ └── semaphore.test.bash
└── tools/
├── .golangci.yaml
├── .markdownlint.jsonc
├── .yamlfmt
├── .yamllint
├── OWNERS
├── benchmark/
│ ├── OWNERS
│ ├── README.md
│ ├── cmd/
│ │ ├── doc.go
│ │ ├── lease.go
│ │ ├── mvcc-put.go
│ │ ├── mvcc.go
│ │ ├── put.go
│ │ ├── range.go
│ │ ├── root.go
│ │ ├── stm.go
│ │ ├── txn_mixed.go
│ │ ├── txn_put.go
│ │ ├── util.go
│ │ ├── watch.go
│ │ ├── watch_get.go
│ │ └── watch_latency.go
│ ├── doc.go
│ └── main.go
├── check-grpc-experimental/
│ ├── allowlist.txt
│ ├── doc.go
│ └── main.go
├── etcd-dump-db/
│ ├── OWNERS
│ ├── README.md
│ ├── backend.go
│ ├── doc.go
│ ├── main.go
│ ├── meta.go
│ ├── page.go
│ ├── scan.go
│ └── utils.go
├── etcd-dump-logs/
│ ├── OWNERS
│ ├── README.md
│ ├── doc.go
│ ├── etcd-dump-log_test.go
│ ├── expectedoutput/
│ │ ├── decoder_correctoutputformat.output
│ │ ├── decoder_wrongoutputformat.output
│ │ ├── listAll.output
│ │ ├── listConfigChange.output
│ │ ├── listConfigChangeIRRCompaction.output
│ │ ├── listIRRCompaction.output
│ │ ├── listIRRDeleteRange.output
│ │ ├── listIRRLeaseGrant.output
│ │ ├── listIRRLeaseRevoke.output
│ │ ├── listIRRPut.output
│ │ ├── listIRRRange.output
│ │ ├── listIRRTxn.output
│ │ ├── listInternalRaftRequest.output
│ │ ├── listNormal.output
│ │ └── listRequest.output
│ ├── main.go
│ ├── raw.go
│ ├── raw_test.go
│ └── testdecoder/
│ ├── decoder_correctoutputformat.sh
│ └── decoder_wrongoutputformat.sh
├── etcd-dump-metrics/
│ ├── OWNERS
│ ├── README.md
│ ├── etcd.go
│ ├── install_darwin.go
│ ├── install_linux.go
│ ├── install_windows.go
│ ├── main.go
│ ├── metrics.go
│ └── utils.go
├── local-tester/
│ ├── OWNERS
│ ├── Procfile
│ ├── README.md
│ ├── bridge/
│ │ ├── bridge.go
│ │ └── dispatch.go
│ ├── bridge.sh
│ └── faults.sh
├── mod/
│ ├── doc.go
│ ├── go.mod
│ ├── go.sum
│ ├── install_all.sh
│ ├── libs.go
│ └── tools.go
├── proto-annotations/
│ ├── cmd/
│ │ ├── etcd_version.go
│ │ └── root.go
│ └── main.go
└── testgrid-analysis/
├── OWNERS
├── cmd/
│ ├── data.go
│ ├── flaky.go
│ ├── github.go
│ └── root.go
├── go.mod
├── go.sum
└── main.go
================================================
FILE CONTENTS
================================================
================================================
FILE: .devcontainer/devcontainer.json
================================================
// For format details, see https://aka.ms/devcontainer.json. For config options, see the
// README at: https://github.com/devcontainers/templates/tree/main/src/go
{
"name": "Go",
// Or use a Dockerfile or Docker Compose file. More info: https://containers.dev/guide/dockerfile
"image": "mcr.microsoft.com/devcontainers/go:1.25-bookworm",
// Features to add to the dev container. More info: https://containers.dev/features.
"features": {
"ghcr.io/devcontainers/features/docker-in-docker:2": {},
"ghcr.io/devcontainers/features/github-cli:1": {},
"ghcr.io/devcontainers/features/kubectl-helm-minikube:1": {}
},
// Use 'forwardPorts' to make a list of ports inside the container available locally.
"forwardPorts": [
2379,
2380
],
// Use 'postCreateCommand' to run commands after the container is created.
"postCreateCommand": "make build"
// Configure tool-specific properties.
// "customizations": {},
}
================================================
FILE: .github/ISSUE_TEMPLATE/bug-report.yml
================================================
---
name: Bug Report
description: Report a bug encountered while operating etcd
labels:
- type/bug
body:
- type: checkboxes
id: confirmations
attributes:
label: Bug report criteria
description: Please confirm this bug report meets the following criteria.
options:
- label: This bug report is not security related, security issues should be disclosed privately via security@etcd.io.
- label: This is not a support request or question, support requests or questions should be raised in the etcd [discussion forums](https://github.com/etcd-io/etcd/discussions).
- label: You have read the etcd [bug reporting guidelines](https://github.com/etcd-io/etcd/blob/main/Documentation/contributor-guide/reporting_bugs.md).
- label: Existing open issues along with etcd [frequently asked questions](https://etcd.io/docs/latest/faq) have been checked and this is not a duplicate.
- type: markdown
attributes:
value: |
Please fill the form below and provide as much information as possible.
Not doing so may result in your bug not being addressed in a timely manner.
- type: textarea
id: problem
attributes:
label: What happened?
validations:
required: true
- type: textarea
id: expected
attributes:
label: What did you expect to happen?
validations:
required: true
- type: textarea
id: repro
attributes:
label: How can we reproduce it (as minimally and precisely as possible)?
validations:
required: true
- type: textarea
id: additional
attributes:
label: Anything else we need to know?
- type: textarea
id: etcdVersion
attributes:
label: Etcd version (please run commands below)
value: |
```console
$ etcd --version
# paste output here
$ etcdctl version
# paste output here
```
validations:
required: true
- type: textarea
id: config
attributes:
label: Etcd configuration (command line flags or environment variables)
value: |
# paste your configuration here
- type: textarea
id: etcdDebugInformation
attributes:
label: Etcd debug information (please run commands below, feel free to obfuscate the IP address or FQDN in the output)
value: |
```console
$ etcdctl member list -w table
# paste output here
$ etcdctl --endpoints= endpoint status -w table
# paste output here
```
- type: textarea
id: logs
attributes:
label: Relevant log output
description: Please copy and paste any relevant log output. This will be automatically formatted into code, so no need for backticks.
render: Shell
================================================
FILE: .github/ISSUE_TEMPLATE/config.yml
================================================
---
blank_issues_enabled: false
contact_links:
- name: Question
url: https://github.com/etcd-io/etcd/discussions
about: Question relating to Etcd
================================================
FILE: .github/ISSUE_TEMPLATE/feature-request.yml
================================================
---
name: Feature request
description: Provide idea for a new feature
labels:
- type/feature
body:
- type: textarea
id: feature
attributes:
label: What would you like to be added?
validations:
required: true
- type: textarea
id: rationale
attributes:
label: Why is this needed?
validations:
required: true
================================================
FILE: .github/ISSUE_TEMPLATE/test-flake.yml
================================================
---
name: Flaking Test
description: Report flaky tests
labels:
- type/flake
- area/testing
body:
- type: textarea
id: workflows
attributes:
label: Which Github Action / Prow Jobs are flaking?
validations:
required: true
- type: textarea
id: tests
attributes:
label: Which tests are flaking?
validations:
required: true
- type: input
id: link
attributes:
label: Github Action / Prow Job link
- type: textarea
id: reason
attributes:
label: Reason for failure (if possible)
- type: textarea
id: additional
attributes:
label: Anything else we need to know?
================================================
FILE: .github/OWNERS
================================================
# See the OWNERS docs at https://go.k8s.io/owners
approvers:
- ivanvc # Ivan Valdes
================================================
FILE: .github/PULL_REQUEST_TEMPLATE.md
================================================
================================================
FILE: .github/SECURITY.md
================================================
Please read https://github.com/etcd-io/etcd/blob/main/security/README.md.
================================================
FILE: .github/dependabot.yml
================================================
---
version: 2
updates:
- package-ecosystem: github-actions
directory: /
schedule:
interval: weekly
- package-ecosystem: gomod
directory: /
schedule:
interval: weekly
allow:
- dependency-type: all
- package-ecosystem: gomod
directory: /tools/mod # Not linked from /go.mod
schedule:
interval: weekly
allow:
- dependency-type: direct
- package-ecosystem: docker
directory: /
schedule:
interval: weekly
- package-ecosystem: docker
directory: /
target-branch: "release-3.4"
schedule:
interval: monthly
- package-ecosystem: docker
directory: /
target-branch: "release-3.5"
schedule:
interval: monthly
- package-ecosystem: docker
directory: /
target-branch: "release-3.6"
schedule:
interval: monthly
================================================
FILE: .github/workflows/OWNERS
================================================
# See the OWNERS docs at https://go.k8s.io/owners
labels:
- github_actions
================================================
FILE: .github/workflows/antithesis-test.yml
================================================
---
name: Build and trigger Antithesis exploration
on:
# pull_request:
# branches: [main]
schedule:
- cron: "0 0 * * *" # run every day at midnight
workflow_dispatch:
inputs:
test:
description: 'Test name'
required: false
type: string
duration:
description: 'Duration (exploration hours)'
required: true
type: int
description:
description: 'Description (avoid quotes, please!)'
required: true
type: string
etcd_ref:
description: 'etcd version to build etcd-server from'
required: false
type: string
email:
description: 'Additional email notification recipient (separate with ;)'
required: true
type: string
# Declare default permissions as read only.
permissions: read-all
env:
REGISTRY: us-central1-docker.pkg.dev
REPOSITORY: molten-verve-216720/linuxfoundation-repository
jobs:
build-and-push-and-test:
runs-on: ubuntu-latest
environment: Antithesis
steps:
- name: Checkout the code
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- name: Login to Antithesis Docker Registry
uses: docker/login-action@b45d80f862d83dbcd57f89517bcf500b2ab88fb2 # v4.0.0
with:
registry: ${{ env.REGISTRY }}
username: _json_key
password: ${{ secrets.ANTITHESIS_CONTAINER_REGISTRY_TOKEN }}
- name: Build and push config image
working-directory: ./tests/antithesis
run: |
make antithesis-build-config-image IMAGE_TAG=${{ inputs.etcd_ref || 'main' }}_${{ github.sha }}
export IMAGE="${{ env.REGISTRY }}/${{ env.REPOSITORY }}/etcd-config:${{ inputs.etcd_ref || 'main' }}_${{ github.sha }}"
docker tag etcd-config:latest $IMAGE
docker push $IMAGE
- name: Build and push client image
working-directory: ./tests/antithesis
run: |
make antithesis-build-client-docker-image
export IMAGE="${{ env.REGISTRY }}/${{ env.REPOSITORY }}/etcd-client:${{ inputs.etcd_ref || 'main' }}_${{ github.sha }}"
docker tag etcd-client:latest $IMAGE
docker push $IMAGE
- name: Build and push etcd image
working-directory: ./tests/antithesis
run: |
make antithesis-build-etcd-image REF=${{ inputs.etcd_ref || 'main' }}
export IMAGE="${{ env.REGISTRY }}/${{ env.REPOSITORY }}/etcd-server:${{ inputs.etcd_ref || 'main' }}_${{ github.sha }}"
docker tag etcd-server:latest $IMAGE
docker push $IMAGE
- name: Run Antithesis Tests
uses: antithesishq/antithesis-trigger-action@f6221e2ba819fe0ac3e36bd67a281fa439a03fba # v0.10
with:
notebook_name: etcd
tenant: linuxfoundation
username: ${{ secrets.ANTITHESIS_WEBHOOK_USERNAME }}
password: ${{ secrets.ANTITHESIS_WEBHOOK_PASSWORD }}
github_token: ${{ secrets.GH_PAT }}
config_image: us-central1-docker.pkg.dev/molten-verve-216720/linuxfoundation-repository/etcd-config:${{ inputs.etcd_ref || 'main' }}_${{ github.sha }}
images: us-central1-docker.pkg.dev/molten-verve-216720/linuxfoundation-repository/etcd-client:${{ inputs.etcd_ref || 'main' }}_${{ github.sha }};us-central1-docker.pkg.dev/molten-verve-216720/linuxfoundation-repository/etcd-server:${{ inputs.etcd_ref || 'main' }}_${{ github.sha }};docker.io/library/ubuntu:latest;us-central1-docker.pkg.dev/molten-verve-216720/linuxfoundation-repository/etcd-config:${{ inputs.etcd_ref || 'main' }}_${{ github.sha }}
description: ${{ inputs.description || 'etcd nightly antithesis run' }}
email_recipients: ${{ inputs.email || 'siarkowicz@google.com' }}
test_name: ${{ inputs.test || 'etcd nightly antithesis run' }}
additional_parameters: |-
custom.duration = ${{ inputs.duration || 12 }}
antithesis.source = ${{ inputs.etcd_ref || 'main' }}
================================================
FILE: .github/workflows/antithesis-verify.yml
================================================
---
name: Verify Antithesis Docker Compose Pipeline
on:
push:
branches:
- main
paths:
- 'tests/antithesis/**'
- '.github/workflows/antithesis-verify.yml'
pull_request:
paths:
- 'tests/antithesis/**'
- '.github/workflows/antithesis-verify.yml'
jobs:
test-docker-compose:
strategy:
matrix:
node-count: [1, 3]
name: Test ${{ matrix.node-count }}-node cluster
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- name: Build etcd-server and etcd-client images
run: |
make -C tests/antithesis antithesis-build-etcd-image
make -C tests/antithesis antithesis-build-client-docker-image
- name: Run docker-compose up
working-directory: ./tests/antithesis
run: |
make antithesis-docker-compose-up CFG_NODE_COUNT=${{ matrix.node-count }} &
- name: Check for healthy cluster
working-directory: ./tests/antithesis
run: |
timeout=120
interval=10
end_time=$(( $(date +%s) + timeout ))
while [ $(date +%s) -lt $end_time ]; do
# The client container might not be running yet, so ignore errors from docker compose logs
if docker compose -f config/docker-compose-${{ matrix.node-count }}-node.yml logs client 2>/dev/null | grep -q "Client \[entrypoint\]: cluster is healthy!"; then
echo "Cluster is healthy!"
exit 0
fi
echo "Waiting for cluster to become healthy..."
sleep $interval
done
echo "Cluster did not become healthy in ${timeout} seconds."
docker compose -f config/docker-compose-${{ matrix.node-count }}-node.yml logs
exit 1
- name: Run traffic
working-directory: ./tests/antithesis
run: make antithesis-run-container-traffic CFG_NODE_COUNT=${{ matrix.node-count }}
- name: Run validation
working-directory: ./tests/antithesis
run: make antithesis-run-container-validation CFG_NODE_COUNT=${{ matrix.node-count }}
- name: Clean up
if: always()
working-directory: ./tests/antithesis
run: make antithesis-clean CFG_NODE_COUNT=${{ matrix.node-count }}
================================================
FILE: .github/workflows/antithesis.debugger.yml
================================================
---
name: Trigger Antithesis debugger
on:
workflow_dispatch:
inputs:
session_id:
description: "The session_id of a test. Found at the bottom of a report."
required: true
type: string
input_hash:
description: "The input hash of a moment."
required: true
type: string
vtime:
description: "The vtime of a moment."
required: true
type: string
email:
description: 'Email notification recipient(s) (separate with ;)'
required: false
type: string
# Declare default permissions as read only.
permissions: read-all
jobs:
trigger-debugger:
runs-on: ubuntu-latest
environment: Antithesis
steps:
- name: Trigger Antithesis Debugger
uses: antithesishq/antithesis-trigger-action@f6221e2ba819fe0ac3e36bd67a281fa439a03fba # v0.10
with:
notebook_name: debugging
tenant: linuxfoundation
username: ${{ secrets.ANTITHESIS_WEBHOOK_USERNAME }}
password: ${{ secrets.ANTITHESIS_WEBHOOK_PASSWORD }}
github_token: ${{ secrets.GH_PAT }}
email_recipients: ${{ inputs.email || 'siarkowicz@google.com' }}
additional_parameters: |-
antithesis.debugging.session_id = ${{ inputs.session_id }}
antithesis.debugging.input_hash = ${{ inputs.input_hash }}
antithesis.debugging.vtime = ${{ inputs.vtime }}
================================================
FILE: .github/workflows/cherrypick-bot-ok-to-test.yaml
================================================
---
name: Auto-label ok-to-test (cherrypick bot)
permissions: read-all
on:
pull_request_target:
types:
- labeled
branches:
- release-3.6
- release-3.5
- release-3.4
jobs:
add-label:
name: Add label
# 90416843 = k8s-infra-cherrypick-robot account ID.
if: |
github.event.pull_request.user.id == 90416843 &&
github.event.label.name == 'needs-ok-to-test'
runs-on: ubuntu-latest
permissions:
pull-requests: write
steps:
- name: Update PR
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
debug: ${{ secrets.ACTIONS_RUNNER_DEBUG == 'true' }}
script: |
try {
await github.rest.issues.removeLabel({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
name: 'needs-ok-to-test'
});
} catch (e) {
if (e.status !== 404) throw e;
}
await github.rest.issues.addLabels({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
labels: ['ok-to-test']
});
================================================
FILE: .github/workflows/codeql-analysis.yml
================================================
---
# For most projects, this workflow file will not need changing; you simply need
# to commit it to your repository.
#
# You may wish to alter this file to override the set of languages analyzed,
# or to provide custom queries or build logic.
#
# ******** NOTE ********
# We have attempted to detect the languages in your repository. Please check
# the `language` matrix defined below to confirm you have the correct set of
# supported CodeQL languages.
#
name: "CodeQL"
on:
push:
branches: [main, release-3.4, release-3.5, release-3.6]
pull_request:
# The branches below must be a subset of the branches above
branches: [main]
schedule:
- cron: '20 14 * * 5'
permissions: read-all
jobs:
analyze:
name: Analyze
runs-on: ubuntu-latest
permissions:
actions: read
contents: read
security-events: write
strategy:
fail-fast: false
matrix:
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ]
# Learn more:
# https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed
language: ['go']
steps:
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@0d579ffd059c29b07949a3cce3983f0780820c98 # v4.32.6
with:
# If you wish to specify custom queries, you can do so here or in a config file.
# By default, queries listed here will override any specified in a config file.
# Prefix the list here with "+" to use these queries and those in the config file.
# queries: ./path/to/local/query, your-org/your-repo/queries@main
languages: ${{ matrix.language }}
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
uses: github/codeql-action/autobuild@0d579ffd059c29b07949a3cce3983f0780820c98 # v4.32.6
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@0d579ffd059c29b07949a3cce3983f0780820c98 # v4.32.6
================================================
FILE: .github/workflows/gh-workflow-approve.yaml
================================================
---
name: Approve GitHub Workflows
permissions: read-all
on:
pull_request_target:
types:
- labeled
- synchronize
branches:
- main
- release-3.6
- release-3.5
- release-3.4
jobs:
approve:
name: Approve ok-to-test
if: contains(github.event.pull_request.labels.*.name, 'ok-to-test')
runs-on: ubuntu-latest
permissions:
actions: write
steps:
- name: Update PR
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
continue-on-error: true
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
debug: ${{ secrets.ACTIONS_RUNNER_DEBUG == 'true' }}
script: |
const result = await github.rest.actions.listWorkflowRunsForRepo({
owner: context.repo.owner,
repo: context.repo.repo,
event: "pull_request",
status: "action_required",
head_sha: context.payload.pull_request.head.sha,
per_page: 100
});
for (var run of result.data.workflow_runs) {
await github.rest.actions.approveWorkflowRun({
owner: context.repo.owner,
repo: context.repo.repo,
run_id: run.id
});
}
================================================
FILE: .github/workflows/measure-testgrid-flakiness.yaml
================================================
---
name: Measure TestGrid Flakiness
on:
schedule:
- cron: "0 0 * * *" # run every day at midnight
permissions: read-all
jobs:
measure-testgrid-flakiness:
name: Measure TestGrid Flakiness
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- id: goversion
run: echo "goversion=$(cat .go-version)" >> "$GITHUB_OUTPUT"
- uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6.3.0
with:
go-version: ${{ steps.goversion.outputs.goversion }}
- env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
set -euo pipefail
./scripts/measure-testgrid-flakiness.sh
================================================
FILE: .github/workflows/scorecards.yml
================================================
---
name: Scorecards supply-chain security
on:
# Only the default branch is supported.
branch_protection_rule:
schedule:
- cron: '45 1 * * 0'
push:
branches: ["main"]
# Declare default permissions as read only.
permissions: read-all
jobs:
analysis:
name: Scorecards analysis
runs-on: ubuntu-latest
permissions:
# Needed to upload the results to code-scanning dashboard.
security-events: write
# Used to receive a badge.
id-token: write
steps:
- name: "Checkout code"
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: "Run analysis"
uses: ossf/scorecard-action@4eaacf0543bb3f2c246792bd56e8cdeffafb205a # v2.4.3
with:
results_file: results.sarif
results_format: sarif
# Publish the results for public repositories to enable scorecard badges. For more details, see
# https://github.com/ossf/scorecard-action#publishing-results.
# For private repositories, `publish_results` will automatically be set to `false`, regardless
# of the value entered here.
publish_results: true
# Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF
# format to the repository Actions tab.
- name: "Upload artifact"
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
with:
name: SARIF file
path: results.sarif
retention-days: 5
# Upload the results to GitHub's code scanning dashboard.
- name: "Upload to code-scanning"
uses: github/codeql-action/upload-sarif@0d579ffd059c29b07949a3cce3983f0780820c98 # v4.32.6
with:
sarif_file: results.sarif
================================================
FILE: .github/workflows/stale.yaml
================================================
---
name: Mark and close stale issues and PRs
on:
schedule:
- cron: '0 0 * * *'
permissions:
issues: write
pull-requests: write
jobs:
stale:
runs-on: ubuntu-latest
steps:
- uses: actions/stale@b5d41d4e1d5dceea10e7104786b73624c18a190f #v10.2.0
with:
days-before-stale: 90
days-before-close: 21
stale-issue-label: 'stale'
stale-pr-label: 'stale'
exempt-issue-labels: 'stage/tracked,help wanted'
exempt-pr-labels: 'stage/tracked,help wanted'
exempt-milestones: ''
exempt-assignees: ''
stale-issue-message: 'This issue has been automatically marked as stale because it has not had recent activity. It will be closed after 21 days if no further activity occurs. Thank you for your contributions.'
stale-pr-message: 'This pull request has been automatically marked as stale because it has not had recent activity. It will be closed after 21 days if no further activity occurs. Thank you for your contributions.'
close-issue-message: ''
close-pr-message: ''
operations-per-run: 30
================================================
FILE: .github/workflows/verify-released-assets.yaml
================================================
---
name: Verify released binary assets
permissions: read-all
on:
release:
types: [published]
jobs:
verify-assets:
name: Verify released binary assets
runs-on: ubuntu-latest
steps:
- name: Verify binary assets
env:
GH_TOKEN: ${{ github.token }}
RELEASE: ${{ github.event.release.tag_name }}
REPOSITORY: ${{ github.repository }}
run: |
mkdir github-assets
pushd github-assets
gh --repo "${REPOSITORY}" release download "${RELEASE}"
test_assets() {
if [ "$(wc -l is unhealthy: failed to connect: \". This change unified the error message, all error types
now have the same output "\ is unhealthy: failed to commit proposal: \".
### Metrics, Monitoring
See [List of metrics](https://github.com/etcd-io/etcd/tree/main/Documentation/metrics) for all metrics per release.
Note that any `etcd_debugging_*` metrics are experimental and subject to change.
- Fix bug where [db_compaction_total_duration_milliseconds metric incorrectly measured duration as 0](https://github.com/etcd-io/etcd/pull/10646).
---
## [v3.1.20](https://github.com/etcd-io/etcd/releases/tag/v3.1.20) (2018-10-10)
See [code changes](https://github.com/etcd-io/etcd/compare/v3.1.19...v3.1.20) and [v3.1 upgrade guide](https://etcd.io/docs/latest/upgrades/upgrade_3_1/) for any breaking changes.
**Again, before running upgrades from any previous release, please make sure to read change logs below and [v3.1 upgrade guide](https://etcd.io/docs/latest/upgrades/upgrade_3_1/).**
### Improved
- Improve ["became inactive" warning log](https://github.com/etcd-io/etcd/pull/10024), which indicates message send to a peer failed.
- Improve [read index wait timeout warning log](https://github.com/etcd-io/etcd/pull/10026), which indicates that local node might have slow network.
- Add [gRPC interceptor for debugging logs](https://github.com/etcd-io/etcd/pull/9990); enable `etcd --debug` flag to see per-request debug information.
- Add [consistency check in snapshot status](https://github.com/etcd-io/etcd/pull/10109). If consistency check on snapshot file fails, `snapshot status` returns `"snapshot file integrity check failed..."` error.
### Metrics, Monitoring
See [List of metrics](https://github.com/etcd-io/etcd/tree/main/Documentation/metrics) for all metrics per release.
Note that any `etcd_debugging_*` metrics are experimental and subject to change.
- Improve [`etcd_network_peer_round_trip_time_seconds`](https://github.com/etcd-io/etcd/pull/10155) Prometheus metric to track leader heartbeats.
- Previously, it only samples the TCP connection for snapshot messages.
- Display all registered [gRPC metrics at start](https://github.com/etcd-io/etcd/pull/10034).
- Add [`etcd_snap_db_fsync_duration_seconds_count`](https://github.com/etcd-io/etcd/pull/9997) Prometheus metric.
- Add [`etcd_snap_db_save_total_duration_seconds_bucket`](https://github.com/etcd-io/etcd/pull/9997) Prometheus metric.
- Add [`etcd_network_snapshot_send_success`](https://github.com/etcd-io/etcd/pull/9997) Prometheus metric.
- Add [`etcd_network_snapshot_send_failures`](https://github.com/etcd-io/etcd/pull/9997) Prometheus metric.
- Add [`etcd_network_snapshot_send_total_duration_seconds`](https://github.com/etcd-io/etcd/pull/9997) Prometheus metric.
- Add [`etcd_network_snapshot_receive_success`](https://github.com/etcd-io/etcd/pull/9997) Prometheus metric.
- Add [`etcd_network_snapshot_receive_failures`](https://github.com/etcd-io/etcd/pull/9997) Prometheus metric.
- Add [`etcd_network_snapshot_receive_total_duration_seconds`](https://github.com/etcd-io/etcd/pull/9997) Prometheus metric.
- Add [`etcd_server_id`](https://github.com/etcd-io/etcd/pull/9998) Prometheus metric.
- Add [`etcd_server_health_success`](https://github.com/etcd-io/etcd/pull/10156) Prometheus metric.
- Add [`etcd_server_health_failures`](https://github.com/etcd-io/etcd/pull/10156) Prometheus metric.
- Add [`etcd_server_read_indexes_failed_total`](https://github.com/etcd-io/etcd/pull/10094) Prometheus metric.
### client v3
- Fix logic on [release lock key if cancelled](https://github.com/etcd-io/etcd/pull/10153) in `clientv3/concurrency` package.
### Go
- Compile with [*Go 1.8.7*](https://golang.org/doc/devel/release.html#go1.8).
---
## [v3.1.19](https://github.com/etcd-io/etcd/releases/tag/v3.1.19) (2018-07-24)
See [code changes](https://github.com/etcd-io/etcd/compare/v3.1.18...v3.1.19) and [v3.1 upgrade guide](https://etcd.io/docs/latest/upgrades/upgrade_3_1/) for any breaking changes.
**Again, before running upgrades from any previous release, please make sure to read change logs below and [v3.1 upgrade guide](https://etcd.io/docs/latest/upgrades/upgrade_3_1/).**
### Improved
- Improve [Raft Read Index timeout warning messages](https://github.com/etcd-io/etcd/pull/9897).
### Metrics, Monitoring
See [List of metrics](https://github.com/etcd-io/etcd/tree/main/Documentation/metrics) for all metrics per release.
Note that any `etcd_debugging_*` metrics are experimental and subject to change.
- Add [`etcd_server_go_version`](https://github.com/etcd-io/etcd/pull/9957) Prometheus metric.
- Add [`etcd_server_slow_read_indexes_total`](https://github.com/etcd-io/etcd/pull/9897) Prometheus metric.
- Add [`etcd_server_quota_backend_bytes`](https://github.com/etcd-io/etcd/pull/9820) Prometheus metric.
- Use it with `etcd_mvcc_db_total_size_in_bytes` and `etcd_mvcc_db_total_size_in_use_in_bytes`.
- `etcd_server_quota_backend_bytes 2.147483648e+09` means current quota size is 2 GB.
- `etcd_mvcc_db_total_size_in_bytes 20480` means current physically allocated DB size is 20 KB.
- `etcd_mvcc_db_total_size_in_use_in_bytes 16384` means future DB size if defragment operation is complete.
- `etcd_mvcc_db_total_size_in_bytes - etcd_mvcc_db_total_size_in_use_in_bytes` is the number of bytes that can be saved on disk with defragment operation.
- Add [`etcd_mvcc_db_total_size_in_bytes`](https://github.com/etcd-io/etcd/pull/9819) Prometheus metric.
- In addition to [`etcd_debugging_mvcc_db_total_size_in_bytes`](https://github.com/etcd-io/etcd/pull/9819).
- Add [`etcd_mvcc_db_total_size_in_use_in_bytes`](https://github.com/etcd-io/etcd/pull/9256) Prometheus metric.
- Use it with `etcd_mvcc_db_total_size_in_bytes` and `etcd_mvcc_db_total_size_in_use_in_bytes`.
- `etcd_server_quota_backend_bytes 2.147483648e+09` means current quota size is 2 GB.
- `etcd_mvcc_db_total_size_in_bytes 20480` means current physically allocated DB size is 20 KB.
- `etcd_mvcc_db_total_size_in_use_in_bytes 16384` means future DB size if defragment operation is complete.
- `etcd_mvcc_db_total_size_in_bytes - etcd_mvcc_db_total_size_in_use_in_bytes` is the number of bytes that can be saved on disk with defragment operation.
### client v3
- Fix [lease keepalive interval updates when response queue is full](https://github.com/etcd-io/etcd/pull/9952).
- If `<-chan *clientv3LeaseKeepAliveResponse` from `clientv3.Lease.KeepAlive` was never consumed or channel is full, client was [sending keepalive request every 500ms](https://github.com/etcd-io/etcd/issues/9911) instead of expected rate of every "TTL / 3" duration.
### Go
- Compile with [*Go 1.8.7*](https://golang.org/doc/devel/release.html#go1.8).
---
## [v3.1.18](https://github.com/etcd-io/etcd/releases/tag/v3.1.18) (2018-06-15)
See [code changes](https://github.com/etcd-io/etcd/compare/v3.1.17...v3.1.18) and [v3.1 upgrade guide](https://etcd.io/docs/latest/upgrades/upgrade_3_1/) for any breaking changes.
**Again, before running upgrades from any previous release, please make sure to read change logs below and [v3.1 upgrade guide](https://etcd.io/docs/latest/upgrades/upgrade_3_1/).**
### Metrics, Monitoring
See [List of metrics](https://github.com/etcd-io/etcd/tree/main/Documentation/metrics) for all metrics per release.
Note that any `etcd_debugging_*` metrics are experimental and subject to change.
- Add [`etcd_server_version`](https://github.com/etcd-io/etcd/pull/8960) Prometheus metric.
- To replace [Kubernetes `etcd-version-monitor`](https://github.com/etcd-io/etcd/issues/8948).
### Go
- Compile with [*Go 1.8.7*](https://golang.org/doc/devel/release.html#go1.8).
---
## [v3.1.17](https://github.com/etcd-io/etcd/releases/tag/v3.1.17) (2018-06-06)
See [code changes](https://github.com/etcd-io/etcd/compare/v3.1.16...v3.1.17) and [v3.1 upgrade guide](https://etcd.io/docs/latest/upgrades/upgrade_3_1/) for any breaking changes.
**Again, before running upgrades from any previous release, please make sure to read change logs below and [v3.1 upgrade guide](https://etcd.io/docs/latest/upgrades/upgrade_3_1/).**
### etcd server
- Fix [v3 snapshot recovery](https://github.com/etcd-io/etcd/issues/7628).
- A follower receives a leader snapshot to be persisted as a `[SNAPSHOT-INDEX].snap.db` file on disk.
- Now, server [ensures that the incoming snapshot be persisted on disk before loading it](https://github.com/etcd-io/etcd/pull/7876).
- Otherwise, index mismatch happens and triggers server-side panic (e.g. newer WAL entry with outdated snapshot index).
### Go
- Compile with [*Go 1.8.7*](https://golang.org/doc/devel/release.html#go1.8).
---
## [v3.1.16](https://github.com/etcd-io/etcd/releases/tag/v3.1.16) (2018-05-31)
See [code changes](https://github.com/etcd-io/etcd/compare/v3.1.15...v3.1.16) and [v3.1 upgrade guide](https://etcd.io/docs/latest/upgrades/upgrade_3_1/) for any breaking changes.
**Again, before running upgrades from any previous release, please make sure to read change logs below and [v3.1 upgrade guide](https://etcd.io/docs/latest/upgrades/upgrade_3_1/).**
### etcd server
- Fix [`mvcc` server panic from restore operation](https://github.com/etcd-io/etcd/pull/9775).
- Let's assume that a watcher had been requested with a future revision X and sent to node A that became network-partitioned thereafter. Meanwhile, cluster makes progress. Then when the partition gets removed, the leader sends a snapshot to node A. Previously if the snapshot's latest revision is still lower than the watch revision X, **etcd server panicked** during snapshot restore operation.
- Now, this server-side panic has been fixed.
### Go
- Compile with [*Go 1.8.7*](https://golang.org/doc/devel/release.html#go1.8).
---
## [v3.1.15](https://github.com/etcd-io/etcd/releases/tag/v3.1.15) (2018-05-09)
See [code changes](https://github.com/etcd-io/etcd/compare/v3.1.14...v3.1.15) and [v3.1 upgrade guide](https://etcd.io/docs/latest/upgrades/upgrade_3_1/) for any breaking changes.
**Again, before running upgrades from any previous release, please make sure to read change logs below and [v3.1 upgrade guide](https://etcd.io/docs/latest/upgrades/upgrade_3_1/).**
### etcd server
- Purge old [`*.snap.db` snapshot files](https://github.com/etcd-io/etcd/pull/7967).
- Previously, etcd did not respect `--max-snapshots` flag to purge old `*.snap.db` files.
- Now, etcd purges old `*.snap.db` files to keep maximum `--max-snapshots` number of files on disk.
### Go
- Compile with [*Go 1.8.7*](https://golang.org/doc/devel/release.html#go1.8).
---
## [v3.1.14](https://github.com/etcd-io/etcd/releases/tag/v3.1.14) (2018-04-24)
See [code changes](https://github.com/etcd-io/etcd/compare/v3.1.13...v3.1.14) and [v3.1 upgrade guide](https://etcd.io/docs/latest/upgrades/upgrade_3_1/) for any breaking changes.
**Again, before running upgrades from any previous release, please make sure to read change logs below and [v3.1 upgrade guide](https://etcd.io/docs/latest/upgrades/upgrade_3_1/).**
### Metrics, Monitoring
See [List of metrics](https://github.com/etcd-io/etcd/tree/main/Documentation/metrics) for all metrics per release.
Note that any `etcd_debugging_*` metrics are experimental and subject to change.
- Add [`etcd_server_is_leader`](https://github.com/etcd-io/etcd/pull/9587) Prometheus metric.
### etcd server
- Add [`--initial-election-tick-advance`](https://github.com/etcd-io/etcd/pull/9591) flag to configure initial election tick fast-forward.
- By default, `--initial-election-tick-advance=true`, then local member fast-forwards election ticks to speed up "initial" leader election trigger.
- This benefits the case of larger election ticks. For instance, cross datacenter deployment may require longer election timeout of 10-second. If true, local node does not need wait up to 10-second. Instead, forwards its election ticks to 8-second, and have only 2-second left before leader election.
- Major assumptions are that: cluster has no active leader thus advancing ticks enables faster leader election. Or cluster already has an established leader, and rejoining follower is likely to receive heartbeats from the leader after tick advance and before election timeout.
- However, when network from leader to rejoining follower is congested, and the follower does not receive leader heartbeat within left election ticks, disruptive election has to happen thus affecting cluster availabilities.
- Now, this can be disabled by setting `--initial-election-tick-advance=false`.
- Disabling this would slow down initial bootstrap process for cross datacenter deployments. Make tradeoffs by configuring `--initial-election-tick-advance` at the cost of slow initial bootstrap.
- If single-node, it advances ticks regardless.
- Address [disruptive rejoining follower node](https://github.com/etcd-io/etcd/issues/9333).
### Go
- Compile with [*Go 1.8.7*](https://golang.org/doc/devel/release.html#go1.8).
---
## [v3.1.13](https://github.com/etcd-io/etcd/releases/tag/v3.1.13) (2018-03-29)
See [code changes](https://github.com/etcd-io/etcd/compare/v3.1.12...v3.1.13) and [v3.1 upgrade guide](https://etcd.io/docs/latest/upgrades/upgrade_3_1/) for any breaking changes.
**Again, before running upgrades from any previous release, please make sure to read change logs below and [v3.1 upgrade guide](https://etcd.io/docs/latest/upgrades/upgrade_3_1/).**
### Improved
- Adjust [election timeout on server restart](https://github.com/etcd-io/etcd/pull/9415) to reduce [disruptive rejoining servers](https://github.com/etcd-io/etcd/issues/9333).
- Previously, etcd fast-forwards election ticks on server start, with only one tick left for leader election. This is to speed up start phase, without having to wait until all election ticks elapse. Advancing election ticks is useful for cross datacenter deployments with larger election timeouts. However, it was affecting cluster availability if the last tick elapses before leader contacts the restarted node.
- Now, when etcd restarts, it adjusts election ticks with more than one tick left, thus more time for leader to prevent disruptive restart.
### Metrics, Monitoring
See [List of metrics](https://github.com/etcd-io/etcd/tree/main/Documentation/metrics) for all metrics per release.
Note that any `etcd_debugging_*` metrics are experimental and subject to change.
- Add missing [`etcd_network_peer_sent_failures_total` count](https://github.com/etcd-io/etcd/pull/9437).
### Go
- Compile with [*Go 1.8.7*](https://golang.org/doc/devel/release.html#go1.8).
---
## [v3.1.12](https://github.com/etcd-io/etcd/releases/tag/v3.1.12) (2018-03-08)
See [code changes](https://github.com/etcd-io/etcd/compare/v3.1.11...v3.1.12) and [v3.1 upgrade guide](https://etcd.io/docs/latest/upgrades/upgrade_3_1/) for any breaking changes.
**Again, before running upgrades from any previous release, please make sure to read change logs below and [v3.1 upgrade guide](https://etcd.io/docs/latest/upgrades/upgrade_3_1/).**
### etcd server
- Fix [`mvcc` "unsynced" watcher restore operation](https://github.com/etcd-io/etcd/pull/9297).
- "unsynced" watcher is watcher that needs to be in sync with events that have happened.
- That is, "unsynced" watcher is the slow watcher that was requested on old revision.
- "unsynced" watcher restore operation was not correctly populating its underlying watcher group.
- Which possibly causes [missing events from "unsynced" watchers](https://github.com/etcd-io/etcd/issues/9086).
- A node gets network partitioned with a watcher on a future revision, and falls behind receiving a leader snapshot after partition gets removed. When applying this snapshot, etcd watch storage moves current synced watchers to unsynced since sync watchers might have become stale during network partition. And reset synced watcher group to restart watcher routines. Previously, there was a bug when moving from synced watcher group to unsynced, thus client would miss events when the watcher was requested to the network-partitioned node.
### Go
- Compile with [*Go 1.8.7*](https://golang.org/doc/devel/release.html#go1.8).
---
## [v3.1.11](https://github.com/etcd-io/etcd/releases/tag/v3.1.11) (2017-11-28)
See [code changes](https://github.com/etcd-io/etcd/compare/v3.1.10...v3.1.11) and [v3.1 upgrade guide](https://etcd.io/docs/latest/upgrades/upgrade_3_1/) for any breaking changes.
**Again, before running upgrades from any previous release, please make sure to read change logs below and [v3.1 upgrade guide](https://etcd.io/docs/latest/upgrades/upgrade_3_1/).**
### etcd server
- [#8411](https://github.com/etcd-io/etcd/issues/8411),[#8806](https://github.com/etcd-io/etcd/pull/8806) backport "mvcc: sending events after restore"
- [#8009](https://github.com/etcd-io/etcd/issues/8009),[#8902](https://github.com/etcd-io/etcd/pull/8902) backport coreos/bbolt v1.3.1-coreos.5
### Go
- Compile with [*Go 1.8.5*](https://golang.org/doc/devel/release.html#go1.8).
---
## [v3.1.10](https://github.com/etcd-io/etcd/releases/tag/v3.1.10) (2017-07-14)
See [code changes](https://github.com/etcd-io/etcd/compare/v3.1.9...v3.1.10) and [v3.1 upgrade guide](https://etcd.io/docs/latest/upgrades/upgrade_3_1/) for any breaking changes.
**Again, before running upgrades from any previous release, please make sure to read change logs below and [v3.1 upgrade guide](https://etcd.io/docs/latest/upgrades/upgrade_3_1/).**
### Added
- Tag docker images with minor versions.
- e.g. `docker pull quay.io/coreos/etcd:v3.1` to fetch latest v3.1 versions.
### Go
- Compile with [*Go 1.8.3*](https://golang.org/doc/devel/release.html#go1.8).
- Fix panic on `net/http.CloseNotify`
---
## [v3.1.9](https://github.com/etcd-io/etcd/releases/tag/v3.1.9) (2017-06-09)
See [code changes](https://github.com/etcd-io/etcd/compare/v3.1.8...v3.1.9) and [v3.1 upgrade guide](https://etcd.io/docs/latest/upgrades/upgrade_3_1/) for any breaking changes.
**Again, before running upgrades from any previous release, please make sure to read change logs below and [v3.1 upgrade guide](https://etcd.io/docs/latest/upgrades/upgrade_3_1/).**
### etcd server
- Allow v2 snapshot over 512MB.
### Go
- Compile with [*Go 1.7.6*](https://golang.org/doc/devel/release.html#go1.7).
---
## [v3.1.8](https://github.com/etcd-io/etcd/releases/tag/v3.1.8) (2017-05-19)
See [code changes](https://github.com/etcd-io/etcd/compare/v3.1.7...v3.1.8) and [v3.1 upgrade guide](https://etcd.io/docs/latest/upgrades/upgrade_3_1/) for any breaking changes.
**Again, before running upgrades from any previous release, please make sure to read change logs below and [v3.1 upgrade guide](https://etcd.io/docs/latest/upgrades/upgrade_3_1/).**
### Go
- Compile with [*Go 1.7.5*](https://golang.org/doc/devel/release.html#go1.7).
---
## [v3.1.7](https://github.com/etcd-io/etcd/releases/tag/v3.1.7) (2017-04-28)
See [code changes](https://github.com/etcd-io/etcd/compare/v3.1.6...v3.1.7) and [v3.1 upgrade guide](https://etcd.io/docs/latest/upgrades/upgrade_3_1/) for any breaking changes.
**Again, before running upgrades from any previous release, please make sure to read change logs below and [v3.1 upgrade guide](https://etcd.io/docs/latest/upgrades/upgrade_3_1/).**
### Go
- Compile with [*Go 1.7.5*](https://golang.org/doc/devel/release.html#go1.7).
---
## [v3.1.6](https://github.com/etcd-io/etcd/releases/tag/v3.1.6) (2017-04-19)
See [code changes](https://github.com/etcd-io/etcd/compare/v3.1.5...v3.1.6) and [v3.1 upgrade guide](https://etcd.io/docs/latest/upgrades/upgrade_3_1/) for any breaking changes.
**Again, before running upgrades from any previous release, please make sure to read change logs below and [v3.1 upgrade guide](https://etcd.io/docs/latest/upgrades/upgrade_3_1/).**
### etcd server
- Fill in Auth API response header.
- Remove auth check in Status API.
### Go
- Compile with [*Go 1.7.5*](https://golang.org/doc/devel/release.html#go1.7).
---
## [v3.1.5](https://github.com/etcd-io/etcd/releases/tag/v3.1.5) (2017-03-27)
See [code changes](https://github.com/etcd-io/etcd/compare/v3.1.4...v3.1.5) and [v3.1 upgrade guide](https://etcd.io/docs/latest/upgrades/upgrade_3_1/) for any breaking changes.
**Again, before running upgrades from any previous release, please make sure to read change logs below and [v3.1 upgrade guide](https://etcd.io/docs/latest/upgrades/upgrade_3_1/).**
### etcd server
- Fix raft memory leak issue.
- Fix Windows file path issues.
### Other
- Add `/etc/nsswitch.conf` file to alpine-based Docker image.
### Go
- Compile with [*Go 1.7.5*](https://golang.org/doc/devel/release.html#go1.7).
---
## [v3.1.4](https://github.com/etcd-io/etcd/releases/tag/v3.1.4) (2017-03-22)
See [code changes](https://github.com/etcd-io/etcd/compare/v3.1.3...v3.1.4) and [v3.1 upgrade guide](https://etcd.io/docs/latest/upgrades/upgrade_3_1/) for any breaking changes.
**Again, before running upgrades from any previous release, please make sure to read change logs below and [v3.1 upgrade guide](https://etcd.io/docs/latest/upgrades/upgrade_3_1/).**
### Go
- Compile with [*Go 1.7.5*](https://golang.org/doc/devel/release.html#go1.7).
---
## [v3.1.3](https://github.com/etcd-io/etcd/releases/tag/v3.1.3) (2017-03-10)
See [code changes](https://github.com/etcd-io/etcd/compare/v3.1.2...v3.1.3) and [v3.1 upgrade guide](https://etcd.io/docs/latest/upgrades/upgrade_3_1/) for any breaking changes.
**Again, before running upgrades from any previous release, please make sure to read change logs below and [v3.1 upgrade guide](https://etcd.io/docs/latest/upgrades/upgrade_3_1/).**
### etcd gateway
- Fix `etcd gateway` schema handling in DNS discovery.
- Fix sd_notify behaviors in `gateway`, `grpc-proxy`.
### gRPC Proxy
- Fix sd_notify behaviors in `gateway`, `grpc-proxy`.
### Other
- Use machine default host when advertise URLs are default values(`localhost:2379,2380`) AND if listen URL is `0.0.0.0`.
### Go
- Compile with [*Go 1.7.5*](https://golang.org/doc/devel/release.html#go1.7).
---
## [v3.1.2](https://github.com/etcd-io/etcd/releases/tag/v3.1.2) (2017-02-24)
See [code changes](https://github.com/etcd-io/etcd/compare/v3.1.1...v3.1.2) and [v3.1 upgrade guide](https://etcd.io/docs/latest/upgrades/upgrade_3_1/) for any breaking changes.
**Again, before running upgrades from any previous release, please make sure to read change logs below and [v3.1 upgrade guide](https://etcd.io/docs/latest/upgrades/upgrade_3_1/).**
### etcd gateway
- Fix `etcd gateway` with multiple endpoints.
### Other
- Use IPv4 default host, by default (when IPv4 and IPv6 are available).
### Go
- Compile with [*Go 1.7.5*](https://golang.org/doc/devel/release.html#go1.7).
---
## [v3.1.1](https://github.com/etcd-io/etcd/releases/tag/v3.1.1) (2017-02-17)
See [code changes](https://github.com/etcd-io/etcd/compare/v3.1.0...v3.1.1) and [v3.1 upgrade guide](https://etcd.io/docs/latest/upgrades/upgrade_3_1/) for any breaking changes.
**Again, before running upgrades from any previous release, please make sure to read change logs below and [v3.1 upgrade guide](https://etcd.io/docs/latest/upgrades/upgrade_3_1/).**
### Go
- Compile with [*Go 1.7.5*](https://golang.org/doc/devel/release.html#go1.7).
---
## [v3.1.0](https://github.com/etcd-io/etcd/releases/tag/v3.1.0) (2017-01-20)
See [code changes](https://github.com/etcd-io/etcd/compare/v3.0.0...v3.1.0) and [v3.1 upgrade guide](https://etcd.io/docs/latest/upgrades/upgrade_3_1/) for any breaking changes.
**Again, before running upgrades from any previous release, please make sure to read change logs below and [v3.1 upgrade guide](https://etcd.io/docs/latest/upgrades/upgrade_3_1/).**
### Improved
- Faster linearizable reads (implements Raft [read-index](https://github.com/etcd-io/etcd/pull/6212)).
- v3 authentication API is now stable.
### Breaking Changes
- Deprecated following gRPC metrics in favor of [go-grpc-prometheus](https://github.com/grpc-ecosystem/go-grpc-prometheus).
- `etcd_grpc_requests_total`
- `etcd_grpc_requests_failed_total`
- `etcd_grpc_active_streams`
- `etcd_grpc_unary_requests_duration_seconds`
### Dependency
- Upgrade [`github.com/ugorji/go/codec`](https://github.com/ugorji/go) to [**`ugorji/go@9c7f9b7`**](https://github.com/ugorji/go/commit/9c7f9b7a2bc3a520f7c7b30b34b7f85f47fe27b6), and [regenerate v2 `client`](https://github.com/etcd-io/etcd/pull/6945).
### Security, Authentication
See [security doc](https://etcd.io/docs/latest/op-guide/security/) for more details.
- SRV records (e.g., infra1.example.com) must match the discovery domain (i.e., example.com) if no custom certificate authority is given.
- `TLSConfig.ServerName` is ignored with user-provided certificates for backwards compatibility; to be deprecated.
- For example, `etcd --discovery-srv=example.com` will only authenticate peers/clients when the provided certs have root domain `example.com` as an entry in Subject Alternative Name (SAN) field.
### etcd server
- Automatic leadership transfer when leader steps down.
- etcd flags
- `--strict-reconfig-check` flag is set by default.
- Add `--log-output` flag.
- Add `--metrics` flag.
- etcd uses default route IP if advertise URL is not given.
- Cluster rejects removing members if quorum will be lost.
- Discovery now has upper limit for waiting on retries.
- Warn on binding listeners through domain names; to be deprecated.
- v3.0 and v3.1 with `--auto-compaction-retention=10` run periodic compaction on v3 key-value store for every 10-hour.
- Compactor only supports periodic compaction.
- Compactor records latest revisions every 5-minute, until it reaches the first compaction period (e.g. 10-hour).
- In order to retain key-value history of last compaction period, it uses the last revision that was fetched before compaction period, from the revision records that were collected every 5-minute.
- When `--auto-compaction-retention=10`, compactor uses revision 100 for compact revision where revision 100 is the latest revision fetched from 10 hours ago.
- If compaction succeeds or requested revision has already been compacted, it resets period timer and starts over with new historical revision records (e.g. restart revision collect and compact for the next 10-hour period).
- If compaction fails, it retries in 5 minutes.
### client v3
- Add `SetEndpoints` method; update endpoints at runtime.
- Add `Sync` method; auto-update endpoints at runtime.
- Add `Lease TimeToLive` API; fetch lease information.
- replace Config.Logger field with global logger.
- Get API responses are sorted in ascending order by default.
### etcdctl v3
- Add `lease timetolive` command.
- Add `--print-value-only` flag to get command.
- Add `--dest-prefix` flag to make-mirror command.
- `get` command responses are sorted in ascending order by default.
### gRPC Proxy
- Experimental gRPC proxy feature.
### Other
- `recipes` now conform to sessions defined in `clientv3/concurrency`.
- ACI has symlinks to `/usr/local/bin/etcd*`.
### Go
- Compile with [*Go 1.7.4*](https://golang.org/doc/devel/release.html#go1.7).
---
================================================
FILE: CHANGELOG/CHANGELOG-3.2.md
================================================
Previous change logs can be found at [CHANGELOG-3.1](https://github.com/etcd-io/etcd/blob/main/CHANGELOG/CHANGELOG-3.1.md).
## v3.2.33 (TBD)
---
## [v3.2.32](https://github.com/etcd-io/etcd/releases/tag/v3.2.32) (2021-03-28)
See [code changes](https://github.com/etcd-io/etcd/compare/v3.2.31...v3.2.32) and [v3.2 upgrade guide](https://etcd.io/docs/latest/upgrades/upgrade_3_2/) for any breaking changes.
### Package `wal`
- add wal slice bound check to make sure entry index is not greater than the number of entries
- check slice size in decodeRecord
- fix panic when decoder not set
### Package `fileutil`
- fix constant for linux locking
### Go
- Compile with [*Go 1.12.17*](https://golang.org/doc/devel/release.html#go1.12).
---
## [v3.2.31](https://github.com/etcd-io/etcd/releases/tag/v3.2.31) (2020-08-18)
See [code changes](https://github.com/etcd-io/etcd/compare/v3.2.30...v3.2.31) and [v3.2 upgrade guide](https://etcd.io/docs/latest/upgrades/upgrade_3_2/) for any breaking changes.
### auth, etcdserver
- Improve [`runtime.FDUsage` call pattern to reduce objects malloc of Memory Usage and CPU Usage](https://github.com/etcd-io/etcd/pull/11986).
- [attaching a fake root token when calling `LeaseRevoke`](https://github.com/etcd-io/etcd/pull/11691).
- fix a data corruption bug caused by lease expiration when authentication is enabled and upgrading cluster from etcd-3.2 to etcd-3.3
### Package `runtime`
- Optimize [`runtime.FDUsage` by removing unnecessary sorting](https://github.com/etcd-io/etcd/pull/12214).
### Metrics, Monitoring
- Add [`os_fd_used` and `os_fd_limit` to monitor current OS file descriptors](https://github.com/etcd-io/etcd/pull/12214).
### Go
- Compile with [*Go 1.12.17*](https://golang.org/doc/devel/release.html#go1.12).
---
## [v3.2.30](https://github.com/etcd-io/etcd/releases/tag/v3.2.30) (2020-04-01)
See [code changes](https://github.com/etcd-io/etcd/compare/v3.2.29...v3.2.30) and [v3.2 upgrade guide](https://etcd.io/docs/latest/upgrades/upgrade_3_2/) for any breaking changes.
### Package `wal`
- Add [`etcd_wal_write_bytes_total`](https://github.com/etcd-io/etcd/pull/11738).
### Metrics, Monitoring
- Add [`etcd_wal_write_bytes_total`](https://github.com/etcd-io/etcd/pull/11738).
### Go
- Compile with [*Go 1.12.17*](https://golang.org/doc/devel/release.html#go1.12).
---
## [v3.2.29](https://github.com/etcd-io/etcd/releases/tag/v3.2.29) (2020-03-18)
See [code changes](https://github.com/etcd-io/etcd/compare/v3.2.28...v3.2.29) and [v3.2 upgrade guide](https://etcd.io/docs/latest/upgrades/upgrade_3_2/) for any breaking changes.
### etcd server
- [Fix corruption bug in defrag](https://github.com/etcd-io/etcd/pull/11613).
- Log [`[CLIENT-PORT]/health` check in server side](https://github.com/etcd-io/etcd/pull/11704).
### client v3
- Fix [`"hasleader"` metadata embedding](https://github.com/etcd-io/etcd/pull/11687).
- Previously, `clientv3.WithRequireLeader(ctx)` was overwriting existing context keys.
### Metrics, Monitoring
See [List of metrics](https://github.com/etcd-io/etcd/tree/main/Documentation/metrics) for all metrics per release.
- Add [`etcd_server_client_requests_total` with `"type"` and `"client_api_version"` labels](https://github.com/etcd-io/etcd/pull/11687).
### Go
- Compile with [*Go 1.8.7*](https://golang.org/doc/devel/release.html#go1.8).
---
## [v3.2.28](https://github.com/etcd-io/etcd/releases/tag/v3.2.28) (2019-11-10)
### Improved
- Add `etcd --experimental-peer-skip-client-san-verification` to [skip verification of peer client address](https://github.com/etcd-io/etcd/pull/11195).
### Metrics, Monitoring
See [List of metrics](https://github.com/etcd-io/etcd/tree/main/Documentation/metrics) for all metrics per release.
Note that any `etcd_debugging_*` metrics are experimental and subject to change.
- Add [`etcd_cluster_version`](https://github.com/etcd-io/etcd/pull/11271) Prometheus metric.
### etcdserver
- Fix [`wait purge file loop during shutdown`](https://github.com/etcd-io/etcd/pull/11308).
- Previously, during shutdown etcd could accidentally remove needed wal files, resulting in catastrophic error `etcdserver: open wal error: wal: file not found.` during startup.
- Now, etcd makes sure the purge file loop exits before server signals stop of the raft node.
### Go
- Compile with [*Go 1.8.7*](https://golang.org/doc/devel/release.html#go1.8).
---
## [v3.2.27](https://github.com/etcd-io/etcd/releases/tag/v3.2.27) (2019-09-17)
### etcdctl v3
- [Strip out insecure endpoints from DNS SRV records when using discovery](https://github.com/etcd-io/etcd/pull/10443) with etcdctl v2
- Add [`etcdctl endpoint health --write-out` support](https://github.com/etcd-io/etcd/pull/9540).
- Previously, [`etcdctl endpoint health --write-out json` did not work](https://github.com/etcd-io/etcd/issues/9532).
- The command output is changed. Previously, if endpoint is unreachable, the command output is
"\ is unhealthy: failed to connect: \". This change unified the error message, all error types
now have the same output "\ is unhealthy: failed to commit proposal: \".
- Fix [`etcdctl snapshot status` to not modify snapshot file](https://github.com/etcd-io/etcd/pull/11157).
- For example, start etcd `v3.3.10`
- Write some data
- Use etcdctl `v3.3.10` to save snapshot
- Somehow, upgrading Kubernetes fails, thus rolling back to previous version etcd `v3.2.24`
- Run etcdctl `v3.2.24` `snapshot status` against the snapshot file saved from `v3.3.10` server
- Run etcdctl `v3.2.24` `snapshot restore` fails with `"expected sha256 [12..."`
### Metrics, Monitoring
See [List of metrics](https://github.com/etcd-io/etcd/tree/main/Documentation/metrics) for all metrics per release.
Note that any `etcd_debugging_*` metrics are experimental and subject to change.
- Fix bug where [db_compaction_total_duration_milliseconds metric incorrectly measured duration as 0](https://github.com/etcd-io/etcd/pull/10646).
- Add [`etcd_debugging_mvcc_current_revision`](https://github.com/etcd-io/etcd/pull/11126) Prometheus metric.
- Add [`etcd_debugging_mvcc_compact_revision`](https://github.com/etcd-io/etcd/pull/11126) Prometheus metric.
### Go
- Compile with [*Go 1.8.7*](https://golang.org/doc/devel/release.html#go1.8).
---
## [v3.2.26](https://github.com/etcd-io/etcd/releases/tag/v3.2.26) (2019-01-11)
See [code changes](https://github.com/etcd-io/etcd/compare/v3.2.25...v3.2.26) and [v3.2 upgrade guide](https://etcd.io/docs/latest/upgrades/upgrade_3_3/) for any breaking changes.
**Again, before running upgrades from any previous release, please make sure to read change logs below and [v3.2 upgrade guide](https://etcd.io/docs/latest/upgrades/upgrade_3_2/).**
### gRPC Proxy
- Fix [memory leak in cache layer](https://github.com/etcd-io/etcd/pull/10327).
### Security, Authentication
- Disable [CommonName authentication for gRPC-gateway](https://github.com/etcd-io/etcd/pull/10366) gRPC-gateway proxy requests to etcd server use the etcd client server TLS certificate. If that certificate contains CommonName we do not want to use that for authentication as it could lead to permission escalation.
### Go
- Compile with [*Go 1.8.7*](https://golang.org/doc/devel/release.html#go1.8).
---
## [v3.2.25](https://github.com/etcd-io/etcd/releases/tag/v3.2.25) (2018-10-10)
See [code changes](https://github.com/etcd-io/etcd/compare/v3.2.24...v3.2.25) and [v3.2 upgrade guide](https://etcd.io/docs/latest/upgrades/upgrade_3_2/) for any breaking changes.
**Again, before running upgrades from any previous release, please make sure to read change logs below and [v3.2 upgrade guide](https://etcd.io/docs/latest/upgrades/upgrade_3_2/).**
### Improved
- Improve ["became inactive" warning log](https://github.com/etcd-io/etcd/pull/10024), which indicates message send to a peer failed.
- Improve [read index wait timeout warning log](https://github.com/etcd-io/etcd/pull/10026), which indicates that local node might have slow network.
- Add [gRPC interceptor for debugging logs](https://github.com/etcd-io/etcd/pull/9990); enable `etcd --debug` flag to see per-request debug information.
- Add [consistency check in snapshot status](https://github.com/etcd-io/etcd/pull/10109). If consistency check on snapshot file fails, `snapshot status` returns `"snapshot file integrity check failed..."` error.
### Metrics, Monitoring
See [List of metrics](https://github.com/etcd-io/etcd/tree/main/Documentation/metrics) for all metrics per release.
Note that any `etcd_debugging_*` metrics are experimental and subject to change.
- Improve [`etcd_network_peer_round_trip_time_seconds`](https://github.com/etcd-io/etcd/pull/10155) Prometheus metric to track leader heartbeats.
- Previously, it only samples the TCP connection for snapshot messages.
- Display all registered [gRPC metrics at start](https://github.com/etcd-io/etcd/pull/10032).
- Add [`etcd_snap_db_fsync_duration_seconds_count`](https://github.com/etcd-io/etcd/pull/9997) Prometheus metric.
- Add [`etcd_snap_db_save_total_duration_seconds_bucket`](https://github.com/etcd-io/etcd/pull/9997) Prometheus metric.
- Add [`etcd_network_snapshot_send_success`](https://github.com/etcd-io/etcd/pull/9997) Prometheus metric.
- Add [`etcd_network_snapshot_send_failures`](https://github.com/etcd-io/etcd/pull/9997) Prometheus metric.
- Add [`etcd_network_snapshot_send_total_duration_seconds`](https://github.com/etcd-io/etcd/pull/9997) Prometheus metric.
- Add [`etcd_network_snapshot_receive_success`](https://github.com/etcd-io/etcd/pull/9997) Prometheus metric.
- Add [`etcd_network_snapshot_receive_failures`](https://github.com/etcd-io/etcd/pull/9997) Prometheus metric.
- Add [`etcd_network_snapshot_receive_total_duration_seconds`](https://github.com/etcd-io/etcd/pull/9997) Prometheus metric.
- Add [`etcd_server_id`](https://github.com/etcd-io/etcd/pull/9998) Prometheus metric.
- Add [`etcd_server_health_success`](https://github.com/etcd-io/etcd/pull/10156) Prometheus metric.
- Add [`etcd_server_health_failures`](https://github.com/etcd-io/etcd/pull/10156) Prometheus metric.
- Add [`etcd_server_read_indexes_failed_total`](https://github.com/etcd-io/etcd/pull/10094) Prometheus metric.
### client v3
- Fix logic on [release lock key if cancelled](https://github.com/etcd-io/etcd/pull/10153) in `clientv3/concurrency` package.
### Go
- Compile with [*Go 1.8.7*](https://golang.org/doc/devel/release.html#go1.8).
---
## [v3.2.24](https://github.com/etcd-io/etcd/releases/tag/v3.2.24) (2018-07-24)
See [code changes](https://github.com/etcd-io/etcd/compare/v3.2.23...v3.2.24) and [v3.2 upgrade guide](https://etcd.io/docs/latest/upgrades/upgrade_3_2/) for any breaking changes.
**Again, before running upgrades from any previous release, please make sure to read change logs below and [v3.2 upgrade guide](https://etcd.io/docs/latest/upgrades/upgrade_3_2/).**
### Improved
- Improve [Raft Read Index timeout warning messages](https://github.com/etcd-io/etcd/pull/9897).
### Metrics, Monitoring
See [List of metrics](https://github.com/etcd-io/etcd/tree/main/Documentation/metrics) for all metrics per release.
Note that any `etcd_debugging_*` metrics are experimental and subject to change.
- Add [`etcd_server_go_version`](https://github.com/etcd-io/etcd/pull/9957) Prometheus metric.
- Add [`etcd_server_heartbeat_send_failures_total`](https://github.com/etcd-io/etcd/pull/9942) Prometheus metric.
- Add [`etcd_server_slow_apply_total`](https://github.com/etcd-io/etcd/pull/9942) Prometheus metric.
- Add [`etcd_disk_backend_defrag_duration_seconds`](https://github.com/etcd-io/etcd/pull/9942) Prometheus metric.
- Add [`etcd_mvcc_hash_duration_seconds`](https://github.com/etcd-io/etcd/pull/9942) Prometheus metric.
- Add [`etcd_server_slow_read_indexes_total`](https://github.com/etcd-io/etcd/pull/9897) Prometheus metric.
- Add [`etcd_server_quota_backend_bytes`](https://github.com/etcd-io/etcd/pull/9820) Prometheus metric.
- Use it with `etcd_mvcc_db_total_size_in_bytes` and `etcd_mvcc_db_total_size_in_use_in_bytes`.
- `etcd_server_quota_backend_bytes 2.147483648e+09` means current quota size is 2 GB.
- `etcd_mvcc_db_total_size_in_bytes 20480` means current physically allocated DB size is 20 KB.
- `etcd_mvcc_db_total_size_in_use_in_bytes 16384` means future DB size if defragment operation is complete.
- `etcd_mvcc_db_total_size_in_bytes - etcd_mvcc_db_total_size_in_use_in_bytes` is the number of bytes that can be saved on disk with defragment operation.
- Add [`etcd_mvcc_db_total_size_in_bytes`](https://github.com/etcd-io/etcd/pull/9819) Prometheus metric.
- In addition to [`etcd_debugging_mvcc_db_total_size_in_bytes`](https://github.com/etcd-io/etcd/pull/9819).
- Add [`etcd_mvcc_db_total_size_in_use_in_bytes`](https://github.com/etcd-io/etcd/pull/9256) Prometheus metric.
- Use it with `etcd_mvcc_db_total_size_in_bytes` and `etcd_server_quota_backend_bytes`.
- `etcd_server_quota_backend_bytes 2.147483648e+09` means current quota size is 2 GB.
- `etcd_mvcc_db_total_size_in_bytes 20480` means current physically allocated DB size is 20 KB.
- `etcd_mvcc_db_total_size_in_use_in_bytes 16384` means future DB size if defragment operation is complete.
- `etcd_mvcc_db_total_size_in_bytes - etcd_mvcc_db_total_size_in_use_in_bytes` is the number of bytes that can be saved on disk with defragment operation.
### gRPC Proxy
- Add [flags for specifying TLS for connecting to proxy](https://github.com/etcd-io/etcd/pull/9894):
- Add `grpc-proxy start --cert-file`, `grpc-proxy start --key-file` and `grpc-proxy start --trusted-ca-file` flags.
- Add [`grpc-proxy start --metrics-addr` flag for specifying a separate metrics listen address](https://github.com/etcd-io/etcd/pull/9894).
### client v3
- Fix [lease keepalive interval updates when response queue is full](https://github.com/etcd-io/etcd/pull/9952).
- If `<-chan *clientv3LeaseKeepAliveResponse` from `clientv3.Lease.KeepAlive` was never consumed or channel is full, client was [sending keepalive request every 500ms](https://github.com/etcd-io/etcd/issues/9911) instead of expected rate of every "TTL / 3" duration.
### Go
- Compile with [*Go 1.8.7*](https://golang.org/doc/devel/release.html#go1.8).
---
## [v3.2.23](https://github.com/etcd-io/etcd/releases/tag/v3.2.23) (2018-06-15)
See [code changes](https://github.com/etcd-io/etcd/compare/v3.2.22...v3.2.23) and [v3.2 upgrade guide](https://etcd.io/docs/latest/upgrades/upgrade_3_2/) for any breaking changes.
**Again, before running upgrades from any previous release, please make sure to read change logs below and [v3.2 upgrade guide](https://etcd.io/docs/latest/upgrades/upgrade_3_2/).**
### Improved
- Improve [slow request apply warning log](https://github.com/etcd-io/etcd/pull/9288).
- e.g. `read-only range request "key:\"/a\" range_end:\"/b\" " with result "range_response_count:3 size:96" took too long (97.966µs) to execute`.
- Redact [request value field](https://github.com/etcd-io/etcd/pull/9822).
- Provide [response size](https://github.com/etcd-io/etcd/pull/9826).
- Add [backoff on watch retries on transient errors](https://github.com/etcd-io/etcd/pull/9840).
### Metrics, Monitoring
See [List of metrics](https://github.com/etcd-io/etcd/tree/main/Documentation/metrics) for all metrics per release.
Note that any `etcd_debugging_*` metrics are experimental and subject to change.
- Add [`etcd_server_version`](https://github.com/etcd-io/etcd/pull/8960) Prometheus metric.
- To replace [Kubernetes `etcd-version-monitor`](https://github.com/etcd-io/etcd/issues/8948).
### Go
- Compile with [*Go 1.8.7*](https://golang.org/doc/devel/release.html#go1.8).
---
## [v3.2.22](https://github.com/etcd-io/etcd/releases/tag/v3.2.22) (2018-06-06)
See [code changes](https://github.com/etcd-io/etcd/compare/v3.2.21...v3.2.22) and [v3.2 upgrade guide](https://etcd.io/docs/latest/upgrades/upgrade_3_2/) for any breaking changes.
**Again, before running upgrades from any previous release, please make sure to read change logs below and [v3.2 upgrade guide](https://etcd.io/docs/latest/upgrades/upgrade_3_2/).**
### Security, Authentication
- Support TLS cipher suite whitelisting.
- To block [weak cipher suites](https://github.com/etcd-io/etcd/issues/8320).
- TLS handshake fails when client hello is requested with invalid cipher suites.
- Add [`etcd --cipher-suites`](https://github.com/etcd-io/etcd/pull/9801) flag.
- If empty, Go auto-populates the list.
### Go
- Compile with [*Go 1.8.7*](https://golang.org/doc/devel/release.html#go1.8).
---
## [v3.2.21](https://github.com/etcd-io/etcd/releases/tag/v3.2.21) (2018-05-31)
See [code changes](https://github.com/etcd-io/etcd/compare/v3.2.20...v3.2.21) and [v3.2 upgrade guide](https://etcd.io/docs/latest/upgrades/upgrade_3_2/) for any breaking changes.
**Again, before running upgrades from any previous release, please make sure to read change logs below and [v3.2 upgrade guide](https://etcd.io/docs/latest/upgrades/upgrade_3_2/).**
### etcd server
- Fix [auth storage panic when simple token provider is disabled](https://github.com/etcd-io/etcd/pull/8695).
- Fix [`mvcc` server panic from restore operation](https://github.com/etcd-io/etcd/pull/9775).
- Let's assume that a watcher had been requested with a future revision X and sent to node A that became network-partitioned thereafter. Meanwhile, cluster makes progress. Then when the partition gets removed, the leader sends a snapshot to node A. Previously if the snapshot's latest revision is still lower than the watch revision X, **etcd server panicked** during snapshot restore operation.
- Now, this server-side panic has been fixed.
### Go
- Compile with [*Go 1.8.7*](https://golang.org/doc/devel/release.html#go1.8).
---
## [v3.2.20](https://github.com/etcd-io/etcd/releases/tag/v3.2.20) (2018-05-09)
See [code changes](https://github.com/etcd-io/etcd/compare/v3.2.19...v3.2.20) and [v3.2 upgrade guide](https://etcd.io/docs/latest/upgrades/upgrade_3_2/) for any breaking changes.
**Again, before running upgrades from any previous release, please make sure to read change logs below and [v3.2 upgrade guide](https://etcd.io/docs/latest/upgrades/upgrade_3_2/).**
### etcd server
- Purge old [`*.snap.db` snapshot files](https://github.com/etcd-io/etcd/pull/7967).
- Previously, etcd did not respect `--max-snapshots` flag to purge old `*.snap.db` files.
- Now, etcd purges old `*.snap.db` files to keep maximum `--max-snapshots` number of files on disk.
### Go
- Compile with [*Go 1.8.7*](https://golang.org/doc/devel/release.html#go1.8).
---
## [v3.2.19](https://github.com/etcd-io/etcd/releases/tag/v3.2.19) (2018-04-24)
See [code changes](https://github.com/etcd-io/etcd/compare/v3.2.18...v3.2.19) and [v3.2 upgrade guide](https://etcd.io/docs/latest/upgrades/upgrade_3_2/) for any breaking changes.
**Again, before running upgrades from any previous release, please make sure to read change logs below and [v3.2 upgrade guide](https://etcd.io/docs/latest/upgrades/upgrade_3_2/).**
### Metrics, Monitoring
See [List of metrics](https://github.com/etcd-io/etcd/tree/main/Documentation/metrics) for all metrics per release.
Note that any `etcd_debugging_*` metrics are experimental and subject to change.
- Fix [`etcd_debugging_server_lease_expired_total`](https://github.com/etcd-io/etcd/pull/9557) Prometheus metric.
- Fix [race conditions in v2 server stat collecting](https://github.com/etcd-io/etcd/pull/9562).
- Add [`etcd_server_is_leader`](https://github.com/etcd-io/etcd/pull/9587) Prometheus metric.
### Security, Authentication
- Fix [TLS reload](https://github.com/etcd-io/etcd/pull/9570) when [certificate SAN field only includes IP addresses but no domain names](https://github.com/etcd-io/etcd/issues/9541).
- In Go, server calls `(*tls.Config).GetCertificate` for TLS reload if and only if server's `(*tls.Config).Certificates` field is not empty, or `(*tls.ClientHelloInfo).ServerName` is not empty with a valid SNI from the client. Previously, etcd always populates `(*tls.Config).Certificates` on the initial client TLS handshake, as non-empty. Thus, client was always expected to supply a matching SNI in order to pass the TLS verification and to trigger `(*tls.Config).GetCertificate` to reload TLS assets.
- However, a certificate whose SAN field does [not include any domain names but only IP addresses](https://github.com/etcd-io/etcd/issues/9541) would request `*tls.ClientHelloInfo` with an empty `ServerName` field, thus failing to trigger the TLS reload on initial TLS handshake; this becomes a problem when expired certificates need to be replaced online.
- Now, `(*tls.Config).Certificates` is created empty on initial TLS client handshake, first to trigger `(*tls.Config).GetCertificate`, and then to populate rest of the certificates on every new TLS connection, even when client SNI is empty (e.g. cert only includes IPs).
### etcd server
- Add [`etcd --initial-election-tick-advance`](https://github.com/etcd-io/etcd/pull/9591) flag to configure initial election tick fast-forward.
- By default, `etcd --initial-election-tick-advance=true`, then local member fast-forwards election ticks to speed up "initial" leader election trigger.
- This benefits the case of larger election ticks. For instance, cross datacenter deployment may require longer election timeout of 10-second. If true, local node does not need wait up to 10-second. Instead, forwards its election ticks to 8-second, and have only 2-second left before leader election.
- Major assumptions are that: cluster has no active leader thus advancing ticks enables faster leader election. Or cluster already has an established leader, and rejoining follower is likely to receive heartbeats from the leader after tick advance and before election timeout.
- However, when network from leader to rejoining follower is congested, and the follower does not receive leader heartbeat within left election ticks, disruptive election has to happen thus affecting cluster availabilities.
- Now, this can be disabled by setting `--initial-election-tick-advance=false`.
- Disabling this would slow down initial bootstrap process for cross datacenter deployments. Make tradeoffs by configuring `--initial-election-tick-advance` at the cost of slow initial bootstrap.
- If single-node, it advances ticks regardless.
- Address [disruptive rejoining follower node](https://github.com/etcd-io/etcd/issues/9333).
### Go
- Compile with [*Go 1.8.7*](https://golang.org/doc/devel/release.html#go1.8).
---
## [v3.2.18](https://github.com/etcd-io/etcd/releases/tag/v3.2.18) (2018-03-29)
See [code changes](https://github.com/etcd-io/etcd/compare/v3.2.17...v3.2.18) and [v3.2 upgrade guide](https://etcd.io/docs/latest/upgrades/upgrade_3_2/) for any breaking changes.
**Again, before running upgrades from any previous release, please make sure to read change logs below and [v3.2 upgrade guide](https://etcd.io/docs/latest/upgrades/upgrade_3_2/).**
### Improved
- Adjust [election timeout on server restart](https://github.com/etcd-io/etcd/pull/9415) to reduce [disruptive rejoining servers](https://github.com/etcd-io/etcd/issues/9333).
- Previously, etcd fast-forwards election ticks on server start, with only one tick left for leader election. This is to speed up start phase, without having to wait until all election ticks elapse. Advancing election ticks is useful for cross datacenter deployments with larger election timeouts. However, it was affecting cluster availability if the last tick elapses before leader contacts the restarted node.
- Now, when etcd restarts, it adjusts election ticks with more than one tick left, thus more time for leader to prevent disruptive restart.
### Metrics, Monitoring
See [List of metrics](https://github.com/etcd-io/etcd/tree/main/Documentation/metrics) for all metrics per release.
Note that any `etcd_debugging_*` metrics are experimental and subject to change.
- Add missing [`etcd_network_peer_sent_failures_total` count](https://github.com/etcd-io/etcd/pull/9437).
### Go
- Compile with [*Go 1.8.7*](https://golang.org/doc/devel/release.html#go1.8).
---
## [v3.2.17](https://github.com/etcd-io/etcd/releases/tag/v3.2.17) (2018-03-08)
See [code changes](https://github.com/etcd-io/etcd/compare/v3.2.16...v3.2.17) and [v3.2 upgrade guide](https://etcd.io/docs/latest/upgrades/upgrade_3_2/) for any breaking changes.
**Again, before running upgrades from any previous release, please make sure to read change logs below and [v3.2 upgrade guide](https://etcd.io/docs/latest/upgrades/upgrade_3_2/).**
### etcd server
- Fix [server panic on invalid Election Proclaim/Resign HTTP(S) requests](https://github.com/etcd-io/etcd/pull/9379).
- Previously, wrong-formatted HTTP requests to Election API could trigger panic in etcd server.
- e.g. `curl -L http://localhost:2379/v3/election/proclaim -X POST -d '{"value":""}'`, `curl -L http://localhost:2379/v3/election/resign -X POST -d '{"value":""}'`.
- Prevent [overflow by large `TTL` values for `Lease` `Grant`](https://github.com/etcd-io/etcd/pull/9399).
- `TTL` parameter to `Grant` request is unit of second.
- Leases with too large `TTL` values exceeding `math.MaxInt64` [expire in unexpected ways](https://github.com/etcd-io/etcd/issues/9374).
- Server now returns `rpctypes.ErrLeaseTTLTooLarge` to client, when the requested `TTL` is larger than *9,000,000,000 seconds* (which is >285 years).
- Again, etcd `Lease` is meant for short-periodic keepalives or sessions, in the range of seconds or minutes. Not for hours or days!
- Enable etcd server [`raft.Config.CheckQuorum` when starting with `ForceNewCluster`](https://github.com/etcd-io/etcd/pull/9347).
### Proxy v2
- Fix [v2 proxy leaky HTTP requests](https://github.com/etcd-io/etcd/pull/9336).
### Go
- Compile with [*Go 1.8.7*](https://golang.org/doc/devel/release.html#go1.8).
---
## [v3.2.16](https://github.com/etcd-io/etcd/releases/tag/v3.2.16) (2018-02-12)
See [code changes](https://github.com/etcd-io/etcd/compare/v3.2.15...v3.2.16) and [v3.2 upgrade guide](https://etcd.io/docs/latest/upgrades/upgrade_3_2/) for any breaking changes.
**Again, before running upgrades from any previous release, please make sure to read change logs below and [v3.2 upgrade guide](https://etcd.io/docs/latest/upgrades/upgrade_3_2/).**
### etcd server
- Fix [`mvcc` "unsynced" watcher restore operation](https://github.com/etcd-io/etcd/pull/9297).
- "unsynced" watcher is watcher that needs to be in sync with events that have happened.
- That is, "unsynced" watcher is the slow watcher that was requested on old revision.
- "unsynced" watcher restore operation was not correctly populating its underlying watcher group.
- Which possibly causes [missing events from "unsynced" watchers](https://github.com/etcd-io/etcd/issues/9086).
- A node gets network partitioned with a watcher on a future revision, and falls behind receiving a leader snapshot after partition gets removed. When applying this snapshot, etcd watch storage moves current synced watchers to unsynced since sync watchers might have become stale during network partition. And reset synced watcher group to restart watcher routines. Previously, there was a bug when moving from synced watcher group to unsynced, thus client would miss events when the watcher was requested to the network-partitioned node.
### Go
- Compile with [*Go 1.8.5*](https://golang.org/doc/devel/release.html#go1.8).
---
## [v3.2.15](https://github.com/etcd-io/etcd/releases/tag/v3.2.15) (2018-01-22)
See [code changes](https://github.com/etcd-io/etcd/compare/v3.2.14...v3.2.15) and [v3.2 upgrade guide](https://etcd.io/docs/latest/upgrades/upgrade_3_2/) for any breaking changes.
**Again, before running upgrades from any previous release, please make sure to read change logs below and [v3.2 upgrade guide](https://etcd.io/docs/latest/upgrades/upgrade_3_2/).**
### etcd server
- Prevent [server panic from member update/add](https://github.com/etcd-io/etcd/pull/9174) with [wrong scheme URLs](https://github.com/etcd-io/etcd/issues/9173).
- Log [user context cancel errors on stream APIs in debug level with TLS](https://github.com/etcd-io/etcd/pull/9178).
### Go
- Compile with [*Go 1.8.5*](https://golang.org/doc/devel/release.html#go1.8).
---
## [v3.2.14](https://github.com/etcd-io/etcd/releases/tag/v3.2.14) (2018-01-11)
See [code changes](https://github.com/etcd-io/etcd/compare/v3.2.13...v3.2.14) and [v3.2 upgrade guide](https://etcd.io/docs/latest/upgrades/upgrade_3_2/) for any breaking changes.
**Again, before running upgrades from any previous release, please make sure to read change logs below and [v3.2 upgrade guide](https://etcd.io/docs/latest/upgrades/upgrade_3_2/).**
### Improved
- Log [user context cancel errors on stream APIs in debug level](https://github.com/etcd-io/etcd/pull/9105).
### etcd server
- Fix [`mvcc/backend.defragdb` nil-pointer dereference on create bucket failure](https://github.com/etcd-io/etcd/pull/9119).
### Go
- Compile with [*Go 1.8.5*](https://golang.org/doc/devel/release.html#go1.8).
---
## [v3.2.13](https://github.com/etcd-io/etcd/releases/tag/v3.2.13) (2018-01-02)
See [code changes](https://github.com/etcd-io/etcd/compare/v3.2.12...v3.2.13) and [v3.2 upgrade guide](https://etcd.io/docs/latest/upgrades/upgrade_3_2/) for any breaking changes.
**Again, before running upgrades from any previous release, please make sure to read change logs below and [v3.2 upgrade guide](https://etcd.io/docs/latest/upgrades/upgrade_3_2/).**
### etcd server
- Remove [verbose error messages on stream cancel and gRPC info-level logs](https://github.com/etcd-io/etcd/pull/9080) in server-side.
- Fix [gRPC server panic on `GracefulStop` TLS-enabled server](https://github.com/etcd-io/etcd/pull/8987).
### Go
- Compile with [*Go 1.8.5*](https://golang.org/doc/devel/release.html#go1.8).
---
## [v3.2.12](https://github.com/etcd-io/etcd/releases/tag/v3.2.12) (2017-12-20)
See [code changes](https://github.com/etcd-io/etcd/compare/v3.2.11...v3.2.12) and [v3.2 upgrade guide](https://etcd.io/docs/latest/upgrades/upgrade_3_2/) for any breaking changes.
**Again, before running upgrades from any previous release, please make sure to read change logs below and [v3.2 upgrade guide](https://etcd.io/docs/latest/upgrades/upgrade_3_2/).**
### Dependency
- Upgrade [`google.golang.org/grpc`](https://github.com/grpc/grpc-go/releases/tag) from [**`v1.7.4`**](https://github.com/grpc/grpc-go/releases/tag/v1.7.4) to [**`v1.7.5`**](https://github.com/grpc/grpc-go/releases/tag/v1.7.5).
- Upgrade [`github.com/grpc-ecosystem/grpc-gateway`](https://github.com/grpc-ecosystem/grpc-gateway/releases) from [**`v1.3`**](https://github.com/grpc-ecosystem/grpc-gateway/releases/tag/v1.3) to [**`v1.3.0`**](https://github.com/grpc-ecosystem/grpc-gateway/releases/tag/v1.3.0).
### etcd server
- Fix [error message of `Revision` compactor](https://github.com/etcd-io/etcd/pull/8999) in server-side.
### client v3
- Add [`MaxCallSendMsgSize` and `MaxCallRecvMsgSize`](https://github.com/etcd-io/etcd/pull/9047) fields to [`clientv3.Config`](https://godoc.org/github.com/etcd-io/etcd/clientv3#Config).
- Fix [exceeded response size limit error in client-side](https://github.com/etcd-io/etcd/issues/9043).
- Address [kubernetes#51099](https://github.com/kubernetes/kubernetes/issues/51099).
- In previous versions(v3.2.10, v3.2.11), client response size was limited to only 4 MiB.
- `MaxCallSendMsgSize` default value is 2 MiB, if not configured.
- `MaxCallRecvMsgSize` default value is `math.MaxInt32`, if not configured.
### Go
- Compile with [*Go 1.8.5*](https://golang.org/doc/devel/release.html#go1.8).
---
## [v3.2.11](https://github.com/etcd-io/etcd/releases/tag/v3.2.11) (2017-12-05)
See [code changes](https://github.com/etcd-io/etcd/compare/v3.2.10...v3.2.11) and [v3.2 upgrade guide](https://etcd.io/docs/latest/upgrades/upgrade_3_2/) for any breaking changes.
**Again, before running upgrades from any previous release, please make sure to read change logs below and [v3.2 upgrade guide](https://etcd.io/docs/latest/upgrades/upgrade_3_2/).**
### Dependency
- Upgrade [`google.golang.org/grpc`](https://github.com/grpc/grpc-go/releases/tag) from [**`v1.7.3`**](https://github.com/grpc/grpc-go/releases/tag/v1.7.3) to [**`v1.7.4`**](https://github.com/grpc/grpc-go/releases/tag/v1.7.4).
### Security, Authentication
See [security doc](https://etcd.io/docs/latest/op-guide/security/) for more details.
- Log [more details on TLS handshake failures](https://github.com/etcd-io/etcd/pull/8952/files).
### client v3
- Fix racey grpc-go's server handler transport `WriteStatus` call to prevent [TLS-enabled etcd server crash](https://github.com/etcd-io/etcd/issues/8904).
- Add [gRPC RPC failure warnings](https://github.com/etcd-io/etcd/pull/8939) to help debug such issues in the future.
### Documentation
- Remove `--listen-metrics-urls` flag in monitoring document (non-released in `v3.2.x`, planned for `v3.3.x`).
### Go
- Compile with [*Go 1.8.5*](https://golang.org/doc/devel/release.html#go1.8).
---
## [v3.2.10](https://github.com/etcd-io/etcd/releases/tag/v3.2.10) (2017-11-16)
See [code changes](https://github.com/etcd-io/etcd/compare/v3.2.9...v3.2.10) and [v3.2 upgrade guide](https://etcd.io/docs/latest/upgrades/upgrade_3_2/) for any breaking changes.
**Again, before running upgrades from any previous release, please make sure to read change logs below and [v3.2 upgrade guide](https://etcd.io/docs/latest/upgrades/upgrade_3_2/).**
### Dependency
- Upgrade [`google.golang.org/grpc`](https://github.com/grpc/grpc-go/releases/tag) from [**`v1.2.1`**](https://github.com/grpc/grpc-go/releases/tag/v1.2.1) to [**`v1.7.3`**](https://github.com/grpc/grpc-go/releases/tag/v1.7.3).
- Upgrade [`github.com/grpc-ecosystem/grpc-gateway`](https://github.com/grpc-ecosystem/grpc-gateway/releases) from [**`v1.2.0`**](https://github.com/grpc-ecosystem/grpc-gateway/releases/tag/v1.2.0) to [**`v1.3`**](https://github.com/grpc-ecosystem/grpc-gateway/releases/tag/v1.3).
### Security, Authentication
See [security doc](https://etcd.io/docs/latest/op-guide/security/) for more details.
- Revert [discovery SRV auth `ServerName` with `*.{ROOT_DOMAIN}`](https://github.com/etcd-io/etcd/pull/8651) to support non-wildcard subject alternative names in the certs (see [issue #8445](https://github.com/etcd-io/etcd/issues/8445) for more contexts).
- For instance, `etcd --discovery-srv=etcd.local` will only authenticate peers/clients when the provided certs have root domain `etcd.local` (**not `*.etcd.local`**) as an entry in Subject Alternative Name (SAN) field.
### etcd server
- Replace backend key-value database `boltdb/bolt` with [`coreos/bbolt`](https://github.com/coreos/bbolt/releases) to address [backend database size issue](https://github.com/etcd-io/etcd/issues/8009).
### client v3
- Rewrite balancer to handle [network partitions](https://github.com/etcd-io/etcd/issues/8711).
### Go
- Compile with [*Go 1.8.5*](https://golang.org/doc/devel/release.html#go1.8).
---
## [v3.2.9](https://github.com/etcd-io/etcd/releases/tag/v3.2.9) (2017-10-06)
See [code changes](https://github.com/etcd-io/etcd/compare/v3.2.8...v3.2.9) and [v3.2 upgrade guide](https://etcd.io/docs/latest/upgrades/upgrade_3_2/) for any breaking changes.
**Again, before running upgrades from any previous release, please make sure to read change logs below and [v3.2 upgrade guide](https://etcd.io/docs/latest/upgrades/upgrade_3_2/).**
### Security, Authentication
See [security doc](https://etcd.io/docs/latest/op-guide/security/) for more details.
- Update `golang.org/x/crypto/bcrypt` (see [golang/crypto@6c586e1](https://github.com/golang/crypto/commit/6c586e17d90a7d08bbbc4069984180dce3b04117)).
- Fix discovery SRV bootstrapping to [authenticate `ServerName` with `*.{ROOT_DOMAIN}`](https://github.com/etcd-io/etcd/pull/8651), in order to support sub-domain wildcard matching (see [issue #8445](https://github.com/etcd-io/etcd/issues/8445) for more contexts).
- For instance, `etcd --discovery-srv=etcd.local` will only authenticate peers/clients when the provided certs have root domain `*.etcd.local` as an entry in Subject Alternative Name (SAN) field.
### Go
- Compile with [*Go 1.8.4*](https://golang.org/doc/devel/release.html#go1.8).
---
## [v3.2.8](https://github.com/etcd-io/etcd/releases/tag/v3.2.8) (2017-09-29)
See [code changes](https://github.com/etcd-io/etcd/compare/v3.2.7...v3.2.8) and [v3.2 upgrade guide](https://etcd.io/docs/latest/upgrades/upgrade_3_2/) for any breaking changes.
**Again, before running upgrades from any previous release, please make sure to read change logs below and [v3.2 upgrade guide](https://etcd.io/docs/latest/upgrades/upgrade_3_2/).**
### client v2
- Fix v2 client failover to next endpoint on mutable operation.
### gRPC Proxy
- Handle [`KeysOnly` flag](https://github.com/etcd-io/etcd/pull/8552).
### Go
- Compile with [*Go 1.8.3*](https://golang.org/doc/devel/release.html#go1.8).
---
## [v3.2.7](https://github.com/etcd-io/etcd/releases/tag/v3.2.7) (2017-09-01)
See [code changes](https://github.com/etcd-io/etcd/compare/v3.2.6...v3.2.7) and [v3.2 upgrade guide](https://etcd.io/docs/latest/upgrades/upgrade_3_2/) for any breaking changes.
**Again, before running upgrades from any previous release, please make sure to read change logs below and [v3.2 upgrade guide](https://etcd.io/docs/latest/upgrades/upgrade_3_2/).**
### Security, Authentication
- Fix [server-side auth so concurrent auth operations do not return old revision error](https://github.com/etcd-io/etcd/pull/8306).
### client v3
- Fix [`concurrency/stm` Put with serializable snapshot](https://github.com/etcd-io/etcd/pull/8439).
- Use store revision from first fetch to resolve write conflicts instead of modified revision.
### Go
- Compile with [*Go 1.8.3*](https://golang.org/doc/devel/release.html#go1.8).
---
## [v3.2.6](https://github.com/etcd-io/etcd/releases/tag/v3.2.6) (2017-08-21)
See [code changes](https://github.com/etcd-io/etcd/compare/v3.2.5...v3.2.6) and [v3.2 upgrade guide](https://etcd.io/docs/latest/upgrades/upgrade_3_2/) for any breaking changes.
**Again, before running upgrades from any previous release, please make sure to read change logs below and [v3.2 upgrade guide](https://etcd.io/docs/latest/upgrades/upgrade_3_2/).**
### etcd server
- Fix watch restore from snapshot.
- Fix multiple URLs for `--listen-peer-urls` flag.
- Add `--enable-pprof` flag to etcd configuration file format.
### Metrics, Monitoring
See [List of metrics](https://github.com/etcd-io/etcd/tree/main/Documentation/metrics) for all metrics per release.
Note that any `etcd_debugging_*` metrics are experimental and subject to change.
- Fix `etcd_debugging_mvcc_keys_total` inconsistency.
### Go
- Compile with [*Go 1.8.3*](https://golang.org/doc/devel/release.html#go1.8).
---
## [v3.2.5](https://github.com/etcd-io/etcd/releases/tag/v3.2.5) (2017-08-04)
See [code changes](https://github.com/etcd-io/etcd/compare/v3.2.4...v3.2.5) and [v3.2 upgrade guide](https://etcd.io/docs/latest/upgrades/upgrade_3_2/) for any breaking changes.
**Again, before running upgrades from any previous release, please make sure to read change logs below and [v3.2 upgrade guide](https://etcd.io/docs/latest/upgrades/upgrade_3_2/).**
### etcdctl v3
- Return non-zero exit code on unhealthy `endpoint health`.
### Security, Authentication
See [security doc](https://etcd.io/docs/latest/op-guide/security/) for more details.
- [Server supports reverse-lookup on wildcard DNS `SAN`](https://github.com/etcd-io/etcd/pull/8281). For instance, if peer cert contains only DNS names (no IP addresses) in Subject Alternative Name (SAN) field, server first reverse-lookups the remote IP address to get a list of names mapping to that address (e.g. `nslookup IPADDR`). Then accepts the connection if those names have a matching name with peer cert's DNS names (either by exact or wildcard match). If none is matched, server forward-lookups each DNS entry in peer cert (e.g. look up `example.default.svc` when the entry is `*.example.default.svc`), and accepts connection only when the host's resolved addresses have the matching IP address with the peer's remote IP address. For example, peer B's CSR (with `cfssl`) SAN field is `["*.example.default.svc", "*.example.default.svc.cluster.local"]` when peer B's remote IP address is `10.138.0.2`. When peer B tries to join the cluster, peer A reverse-lookup the IP `10.138.0.2` to get the list of host names. And either exact or wildcard match the host names with peer B's cert DNS names in Subject Alternative Name (SAN) field. If none of reverse/forward lookups worked, it returns an error `"tls: "10.138.0.2" does not match any of DNSNames ["*.example.default.svc","*.example.default.svc.cluster.local"]`. See [issue#8268](https://github.com/etcd-io/etcd/issues/8268) for more detail.
### Metrics, Monitoring
See [List of metrics](https://github.com/etcd-io/etcd/tree/main/Documentation/metrics) for all metrics per release.
Note that any `etcd_debugging_*` metrics are experimental and subject to change.
- Fix unreachable `/metrics` endpoint when `--enable-v2=false`.
### gRPC Proxy
- Handle [`PrevKv` flag](https://github.com/etcd-io/etcd/pull/8366).
### Other
- Add container registry `gcr.io/etcd-development/etcd`.
### Go
- Compile with [*Go 1.8.3*](https://golang.org/doc/devel/release.html#go1.8).
---
## [v3.2.4](https://github.com/etcd-io/etcd/releases/tag/v3.2.4) (2017-07-19)
See [code changes](https://github.com/etcd-io/etcd/compare/v3.2.3...v3.2.4) and [v3.2 upgrade guide](https://etcd.io/docs/latest/upgrades/upgrade_3_2/) for any breaking changes.
**Again, before running upgrades from any previous release, please make sure to read change logs below and [v3.2 upgrade guide](https://etcd.io/docs/latest/upgrades/upgrade_3_2/).**
### etcd server
- Do not block on active client stream when stopping server
### gRPC proxy
- Fix gRPC proxy Snapshot RPC error handling
### Go
- Compile with [*Go 1.8.3*](https://golang.org/doc/devel/release.html#go1.8).
---
## [v3.2.3](https://github.com/etcd-io/etcd/releases/tag/v3.2.3) (2017-07-14)
See [code changes](https://github.com/etcd-io/etcd/compare/v3.2.2...v3.2.3) and [v3.2 upgrade guide](https://etcd.io/docs/latest/upgrades/upgrade_3_2/) for any breaking changes.
**Again, before running upgrades from any previous release, please make sure to read change logs below and [v3.2 upgrade guide](https://etcd.io/docs/latest/upgrades/upgrade_3_2/).**
### client v3
- Let clients establish unlimited streams
### Other
- Tag docker images with minor versions
- e.g. `docker pull quay.io/coreos/etcd:v3.2` to fetch latest v3.2 versions
### Go
- Compile with [*Go 1.8.3*](https://golang.org/doc/devel/release.html#go1.8).
---
## [v3.2.2](https://github.com/etcd-io/etcd/releases/tag/v3.2.2) (2017-07-07)
See [code changes](https://github.com/etcd-io/etcd/compare/v3.2.1...v3.2.2) and [v3.2 upgrade guide](https://etcd.io/docs/latest/upgrades/upgrade_3_2/) for any breaking changes.
**Again, before running upgrades from any previous release, please make sure to read change logs below and [v3.2 upgrade guide](https://etcd.io/docs/latest/upgrades/upgrade_3_2/).**
### Improved
- Rate-limit lease revoke on expiration.
- Extend leases on promote to avoid queueing effect on lease expiration.
### Security, Authentication
See [security doc](https://etcd.io/docs/latest/op-guide/security/) for more details.
- [Server accepts connections if IP matches, without checking DNS entries](https://github.com/etcd-io/etcd/pull/8223). For instance, if peer cert contains IP addresses and DNS names in Subject Alternative Name (SAN) field, and the remote IP address matches one of those IP addresses, server just accepts connection without further checking the DNS names. For example, peer B's CSR (with `cfssl`) SAN field is `["invalid.domain", "10.138.0.2"]` when peer B's remote IP address is `10.138.0.2` and `invalid.domain` is a invalid host. When peer B tries to join the cluster, peer A successfully authenticates B, since Subject Alternative Name (SAN) field has a valid matching IP address. See [issue#8206](https://github.com/etcd-io/etcd/issues/8206) for more detail.
### etcd server
- Accept connection with matched IP SAN but no DNS match.
- Don't check DNS entries in certs if there's a matching IP.
### gRPC gateway
- Use user-provided listen address to connect to gRPC gateway.
- `net.Listener` rewrites IPv4 0.0.0.0 to IPv6 [::], breaking IPv6 disabled hosts.
- Only v3.2.0, v3.2.1 are affected.
### Go
- Compile with [*Go 1.8.3*](https://golang.org/doc/devel/release.html#go1.8).
---
## [v3.2.1](https://github.com/etcd-io/etcd/releases/tag/v3.2.1) (2017-06-23)
See [code changes](https://github.com/etcd-io/etcd/compare/v3.2.0...v3.2.1) and [v3.2 upgrade guide](https://etcd.io/docs/latest/upgrades/upgrade_3_2/) for any breaking changes.
**Again, before running upgrades from any previous release, please make sure to read change logs below and [v3.2 upgrade guide](https://etcd.io/docs/latest/upgrades/upgrade_3_2/).**
### etcd server
- Fix backend database in-memory index corruption issue on restore (only 3.2.0 is affected).
### gRPC gateway
- Fix Txn marshaling.
### Metrics, Monitoring
See [List of metrics](https://github.com/etcd-io/etcd/tree/main/Documentation/metrics) for all metrics per release.
Note that any `etcd_debugging_*` metrics are experimental and subject to change.
- Fix backend database size debugging metrics.
### Go
- Compile with [*Go 1.8.3*](https://golang.org/doc/devel/release.html#go1.8).
---
## [v3.2.0](https://github.com/etcd-io/etcd/releases/tag/v3.2.0) (2017-06-09)
See [code changes](https://github.com/etcd-io/etcd/compare/v3.1.0...v3.2.0) and [v3.2 upgrade guide](https://etcd.io/docs/latest/upgrades/upgrade_3_2/) for any breaking changes.
**Again, before running upgrades from any previous release, please make sure to read change logs below and [v3.2 upgrade guide](https://etcd.io/docs/latest/upgrades/upgrade_3_2/).**
### Improved
- Improve backend read concurrency.
### Breaking Changes
- Increased [`--snapshot-count` default value from 10,000 to 100,000](https://github.com/etcd-io/etcd/pull/7160).
- Higher snapshot count means it holds Raft entries in memory for longer before discarding old entries.
- It is a trade-off between less frequent snapshotting and [higher memory usage](https://github.com/kubernetes/kubernetes/issues/60589#issuecomment-371977156).
- User lower `--snapshot-count` value for lower memory usage.
- User higher `--snapshot-count` value for better availabilities of slow followers (less frequent snapshots from leader).
- `clientv3.Lease.TimeToLive` returns `LeaseTimeToLiveResponse.TTL == -1` on lease not found.
- `clientv3.NewFromConfigFile` is moved to `clientv3/yaml.NewConfig`.
- `embed.Etcd.Peers` field is now `[]*peerListener`.
- Rejects domains names for `--listen-peer-urls` and `--listen-client-urls` (3.1 only prints out warnings), since [domain name is invalid for network interface binding](https://github.com/etcd-io/etcd/issues/6336).
### Dependency
- Upgrade [`google.golang.org/grpc`](https://github.com/grpc/grpc-go/releases) from [**`v1.0.4`**](https://github.com/grpc/grpc-go/releases/tag/v1.0.4) to [**`v1.2.1`**](https://github.com/grpc/grpc-go/releases/tag/v1.2.1).
- Upgrade [`github.com/grpc-ecosystem/grpc-gateway`](https://github.com/grpc-ecosystem/grpc-gateway/releases) to [**`v1.2.0`**](https://github.com/grpc-ecosystem/grpc-gateway/releases/tag/v1.2.0).
### Metrics, Monitoring
See [List of metrics](https://github.com/etcd-io/etcd/tree/main/Documentation/metrics) for all metrics per release.
Note that any `etcd_debugging_*` metrics are experimental and subject to change.
- Add [`etcd_disk_backend_snapshot_duration_seconds`](https://github.com/etcd-io/etcd/pull/7892)
- Add `etcd_debugging_server_lease_expired_total` metrics.
### Security, Authentication
See [security doc](https://etcd.io/docs/latest/op-guide/security/) for more details.
- [TLS certificates get reloaded on every client connection](https://github.com/etcd-io/etcd/pull/7829). This is useful when replacing expiry certs without stopping etcd servers; it can be done by overwriting old certs with new ones. Refreshing certs for every connection should not have too much overhead, but can be improved in the future, with caching layer. Example tests can be found [here](https://github.com/etcd-io/etcd/blob/b041ce5d514a4b4aaeefbffb008f0c7570a18986/integration/v3_grpc_test.go#L1601-L1757).
- [Server denies incoming peer certs with wrong IP `SAN`](https://github.com/etcd-io/etcd/pull/7687). For instance, if peer cert contains any IP addresses in Subject Alternative Name (SAN) field, server authenticates a peer only when the remote IP address matches one of those IP addresses. This is to prevent unauthorized endpoints from joining the cluster. For example, peer B's CSR (with `cfssl`) SAN field is `["*.example.default.svc", "*.example.default.svc.cluster.local", "10.138.0.27"]` when peer B's actual IP address is `10.138.0.2`, not `10.138.0.27`. When peer B tries to join the cluster, peer A will reject B with the error `x509: certificate is valid for 10.138.0.27, not 10.138.0.2`, because B's remote IP address does not match the one in Subject Alternative Name (SAN) field.
- [Server resolves TLS `DNSNames` when checking `SAN`](https://github.com/etcd-io/etcd/pull/7767). For instance, if peer cert contains only DNS names (no IP addresses) in Subject Alternative Name (SAN) field, server authenticates a peer only when forward-lookups (`dig b.com`) on those DNS names have matching IP with the remote IP address. For example, peer B's CSR (with `cfssl`) SAN field is `["b.com"]` when peer B's remote IP address is `10.138.0.2`. When peer B tries to join the cluster, peer A looks up the incoming host `b.com` to get the list of IP addresses (e.g. `dig b.com`). And rejects B if the list does not contain the IP `10.138.0.2`, with the error `tls: 10.138.0.2 does not match any of DNSNames ["b.com"]`.
- Auth support JWT token.
### etcd server
- RPCs
- Add Election, Lock service.
- Native client `etcdserver/api/v3client`
- client "embedded" in the server.
- Logging, monitoring
- Server warns large snapshot operations.
- Add `etcd --enable-v2` flag to enable v2 API server.
- `etcd --enable-v2=true` by default.
- Add `etcd --auth-token` flag.
- v3.2 compactor runs [every hour](https://github.com/etcd-io/etcd/pull/7875).
- Compactor only supports periodic compaction.
- Compactor continues to record latest revisions every 5-minute.
- For every hour, it uses the last revision that was fetched before compaction period, from the revision records that were collected every 5-minute.
- That is, for every hour, compactor discards historical data created before compaction period.
- The retention window of compaction period moves to next hour.
- For instance, when hourly writes are 100 and `--auto-compaction-retention=10`, v3.1 compacts revision 1000, 2000, and 3000 for every 10-hour, while v3.2 compacts revision 1000, 1100, and 1200 for every 1-hour.
- If compaction succeeds or requested revision has already been compacted, it resets period timer and removes used compacted revision from historical revision records (e.g. start next revision collect and compaction from previously collected revisions).
- If compaction fails, it retries in 5 minutes.
- Allow snapshot over 512MB.
### client v3
- STM prefetching.
- Add namespace feature.
- Add `ErrOldCluster` with server version checking.
- Translate `WithPrefix()` into `WithFromKey()` for empty key.
### etcdctl v3
- Add `check perf` command.
- Add `etcdctl --from-key` flag to role grant-permission command.
- `lock` command takes an optional command to execute.
### gRPC Proxy
- Proxy endpoint discovery.
- Namespaces.
- Coalesce lease requests.
### etcd gateway
- Support [DNS SRV priority](https://github.com/etcd-io/etcd/pull/7882) for [smart proxy routing](https://github.com/etcd-io/etcd/issues/4378).
### Other
- v3 client
- concurrency package's elections updated to match RPC interfaces.
- let client dial endpoints not in the balancer.
- Release
- Annotate acbuild with supports-systemd-notify.
- Add `nsswitch.conf` to Docker container image.
- Add ppc64le, arm64(experimental) builds.
### Go
- Compile with [*Go 1.8.3*](https://golang.org/doc/devel/release.html#go1.8).
---
================================================
FILE: CHANGELOG/CHANGELOG-3.3.md
================================================
Previous change logs can be found at [CHANGELOG-3.2](https://github.com/etcd-io/etcd/blob/main/CHANGELOG/CHANGELOG-3.2.md).
---
## v3.3.27 (2021-10-15)
See [code changes](https://github.com/etcd-io/etcd/compare/v3.3.26...v3.3.27) and [v3.3 upgrade guide](https://etcd.io/docs/latest/upgrades/upgrade_3_3/) for any breaking changes.
### Other
- Updated [base image](https://github.com/etcd-io/etcd/pull/13386) from `debian:buster-v1.4.0` to `debian:bullseye-20210927` to fix the following critical CVEs:
- [CVE-2021-3711](https://nvd.nist.gov/vuln/detail/CVE-2021-3711): miscalculation of a buffer size in openssl's SM2 decryption
- [CVE-2021-35942](https://nvd.nist.gov/vuln/detail/CVE-2021-35942): integer overflow flaw in glibc
- [CVE-2019-9893](https://nvd.nist.gov/vuln/detail/CVE-2019-9893): incorrect syscall argument generation in libseccomp
- [CVE-2021-36159](https://nvd.nist.gov/vuln/detail/CVE-2021-36159): libfetch in apk-tools mishandles numeric strings in FTP and HTTP protocols to allow out of bound reads.
---
## v3.3.26 (2021-10-03)
See [code changes](https://github.com/etcd-io/etcd/compare/v3.3.25...v3.3.26) and [v3.3 upgrade guide](https://etcd.io/docs/latest/upgrades/upgrade_3_3/) for any breaking changes.
### Package `clientv3`
- Fix [auth token invalid after watch reconnects](https://github.com/etcd-io/etcd/pull/12264). Get AuthToken automatically when clientConn is ready.
### Package `fileutil`
- Fix [constant](https://github.com/etcd-io/etcd/pull/12440) for linux locking.
### Go
- Compile with [*Go 1.12.17*](https://golang.org/doc/devel/release.html#go1.12).
---
## v3.3.25 (2020-08-24)
See [code changes](https://github.com/etcd-io/etcd/compare/v3.3.23...v3.3.25) and [v3.3 upgrade guide](https://etcd.io/docs/latest/upgrades/upgrade_3_3/) for any breaking changes.
### Security
- A [log warning](https://github.com/etcd-io/etcd/pull/12242) is added when etcd use any existing directory that has a permission different than 700 on Linux and 777 on Windows.
## [v3.3.24](https://github.com/etcd-io/etcd/releases/tag/v3.3.24) (2020-08-18)
See [code changes](https://github.com/etcd-io/etcd/compare/v3.3.23...v3.3.24) and [v3.3 upgrade guide](https://etcd.io/docs/latest/upgrades/upgrade_3_3/) for any breaking changes.
### Package `etcd server`
- Fix [`int64` convert panic in raft logger](https://github.com/etcd-io/etcd/pull/12106).
- Fix [kubernetes/kubernetes#91937](https://github.com/kubernetes/kubernetes/issues/91937).
### Package `runtime`
- Optimize [`runtime.FDUsage` by removing unnecessary sorting](https://github.com/etcd-io/etcd/pull/12214).
### Metrics, Monitoring
- Add [`os_fd_used` and `os_fd_limit` to monitor current OS file descriptors](https://github.com/etcd-io/etcd/pull/12214).
### Go
- Compile with [*Go 1.12.17*](https://golang.org/doc/devel/release.html#go1.12).
---
## [v3.3.23](https://github.com/etcd-io/etcd/releases/tag/v3.3.23) (2020-07-16)
See [code changes](https://github.com/etcd-io/etcd/compare/v3.3.22...v3.3.23) and [v3.3 upgrade guide](https://etcd.io/docs/latest/upgrades/upgrade_3_3/) for any breaking changes.
### Breaking Changes
- Fix [incorrect package dependency when etcd clientv3 used as libary](https://github.com/etcd-io/etcd/issues/12068).
- Changed behavior on [existing dir permission](https://github.com/etcd-io/etcd/pull/11798).
- Previously, the permission was not checked on existing data directory and the directory used for automatically generating self-signed certificates for TLS connections with clients. Now a check is added to make sure those directories, if already exist, has a desired permission of 700 on Linux and 777 on Windows.
### Package `wal`
### etcd server
- Fix [watch stream got closed if one watch request is not permitted](https://github.com/etcd-io/etcd/pull/11758).
- Add [etcd --auth-token-ttl](https://github.com/etcd-io/etcd/pull/11980) flag to customize `simpleTokenTTL` settings.
- Improve [runtime.FDUsage objects malloc of Memory Usage and CPU Usage](https://github.com/etcd-io/etcd/pull/11986).
- Improve [mvcc.watchResponse channel Memory Usage](https://github.com/etcd-io/etcd/pull/11987).
### Go
- Compile with [*Go 1.12.17*](https://golang.org/doc/devel/release.html#go1.12).
---
## [v3.3.22](https://github.com/etcd-io/etcd/releases/tag/v3.3.22) (2020-05-20)
See [code changes](https://github.com/etcd-io/etcd/compare/v3.3.21...v3.3.22) and [v3.3 upgrade guide](https://etcd.io/docs/latest/upgrades/upgrade_3_3/) for any breaking changes.
### Package `wal`
- Add [missing CRC checksum check in WAL validate method otherwise causes panic](https://github.com/etcd-io/etcd/pull/11924).
- See https://github.com/etcd-io/etcd/issues/11918.
### Go
- Compile with [*Go 1.12.17*](https://golang.org/doc/devel/release.html#go1.12).
---
## [v3.3.21](https://github.com/etcd-io/etcd/releases/tag/v3.3.21) (2020-05-18)
See [code changes](https://github.com/etcd-io/etcd/compare/v3.3.20...v3.3.21) and [v3.3 upgrade guide](https://etcd.io/docs/latest/upgrades/upgrade_3_3/) for any breaking changes.
### `etcdctl`
- Make sure [save snapshot downloads checksum for integrity checks](https://github.com/etcd-io/etcd/pull/11896).
### Package `clientv3`
- Make sure [save snapshot downloads checksum for integrity checks](https://github.com/etcd-io/etcd/pull/11896).
### etcd server
- Improve logging around snapshot send and receive.
- [Add log when etcdserver failed to apply command](https://github.com/etcd-io/etcd/pull/11670).
- [Fix deadlock bug in mvcc](https://github.com/etcd-io/etcd/pull/11817).
- Fix [inconsistency between WAL and server snapshot](https://github.com/etcd-io/etcd/pull/11888).
- Previously, server restore fails if it had crashed after persisting raft hard state but before saving snapshot.
- See https://github.com/etcd-io/etcd/issues/10219 for more.
### Package `auth`
- [Fix a data corruption bug by saving consistent index](https://github.com/etcd-io/etcd/pull/11652).
### Metrics, Monitoring
- Add [`etcd_debugging_auth_revision`](https://github.com/etcd-io/etcd/commit/f14d2a087f7b0fd6f7980b95b5e0b945109c95f3).
### Go
- Compile with [*Go 1.12.17*](https://golang.org/doc/devel/release.html#go1.12).
---
## [v3.3.20](https://github.com/etcd-io/etcd/releases/tag/v3.3.20) (2020-04-01)
See [code changes](https://github.com/etcd-io/etcd/compare/v3.3.19...v3.3.20) and [v3.2 upgrade guide](https://etcd.io/docs/latest/upgrades/upgrade_3_3/) for any breaking changes.
### Package `wal`
- Add [`etcd_wal_write_bytes_total`](https://github.com/etcd-io/etcd/pull/11738).
### Metrics, Monitoring
- Add [`etcd_wal_write_bytes_total`](https://github.com/etcd-io/etcd/pull/11738).
### Go
- Compile with [*Go 1.12.17*](https://golang.org/doc/devel/release.html#go1.12).
---
## [v3.3.19](https://github.com/etcd-io/etcd/releases/tag/v3.3.19) (2020-03-18)
See [code changes](https://github.com/etcd-io/etcd/compare/v3.3.18...v3.3.19) and [v3.3 upgrade guide](https://etcd.io/docs/latest/upgrades/upgrade_3_3/) for any breaking changes.
### client v3
- Fix [`"hasleader"` metadata embedding](https://github.com/etcd-io/etcd/pull/11687).
- Previously, `clientv3.WithRequireLeader(ctx)` was overwriting existing context keys.
### etcd server
- [Fix corruption bug in defrag](https://github.com/etcd-io/etcd/pull/11613).
- Log [`[CLIENT-PORT]/health` check in server side](https://github.com/etcd-io/etcd/pull/11704).
### etcdctl v3
- Fix [`etcdctl member add`](https://github.com/etcd-io/etcd/pull/11638) command to prevent potential timeout.
### Metrics, Monitoring
See [List of metrics](https://github.com/etcd-io/etcd/tree/main/Documentation/metrics) for all metrics per release.
- Add [`etcd_server_client_requests_total` with `"type"` and `"client_api_version"` labels](https://github.com/etcd-io/etcd/pull/11687).
### gRPC Proxy
- Fix [`panic on error`](https://github.com/etcd-io/etcd/pull/11694) for metrics handler.
### Go
- Compile with [*Go 1.12.17*](https://golang.org/doc/devel/release.html#go1.12).
---
## [v3.3.18](https://github.com/etcd-io/etcd/releases/tag/v3.3.18) (2019-11-26)
### Metrics, Monitoring
See [List of metrics](https://github.com/etcd-io/etcd/tree/main/Documentation/metrics) for all metrics per release.
Note that any `etcd_debugging_*` metrics are experimental and subject to change.
- Add [`etcd_cluster_version`](https://github.com/etcd-io/etcd/pull/11261) Prometheus metric.
- Add [`etcd_debugging_mvcc_total_put_size_in_bytes`](https://github.com/etcd-io/etcd/pull/11374) Prometheus metric.
### etcdserver
- Fix [`wait purge file loop during shutdown`](https://github.com/etcd-io/etcd/pull/11308).
- Previously, during shutdown etcd could accidentally remove needed wal files, resulting in catastrophic error `etcdserver: open wal error: wal: file not found.` during startup.
- Now, etcd makes sure the purge file loop exits before server signals stop of the raft node.
---
## [v3.3.17](https://github.com/etcd-io/etcd/releases/tag/v3.3.17) (2019-10-11)
See [code changes](https://github.com/etcd-io/etcd/compare/v3.3.16...v3.3.17) and [v3.3 upgrade guide](https://etcd.io/docs/latest/upgrades/upgrade_3_3/) for any breaking changes.
### Release details
This release replaces 3.3.16.
Due to the etcd 3.3.16 release being incorrectly released (see details below), please use this release instead.
---
## [v3.3.16](https://github.com/etcd-io/etcd/releases/tag/v3.3.16) (2019-10-10)
**WARNING: This is a bad release! Please use etcd 3.3.17 instead. See https://github.com/etcd-io/etcd/issues/11241 for details.**
### Issues with release
- go mod for 'v3.3.16' may return a different hash if retrieved from a go mod proxy than if retrieved directly from github. Depending on this version is unsafe. See https://github.com/etcd-io/etcd/issues/11241 for details.
- The binaries and docker image for this release have been published and will be left as-is, but will not be signed since this is a bad release.
See [code changes](https://github.com/etcd-io/etcd/compare/v3.3.15...v3.3.16) and [v3.3 upgrade guide](https://etcd.io/docs/latest/upgrades/upgrade_3_3/) for any breaking changes.
**Again, before running upgrades from any previous release, please make sure to read change logs below and [v3.3 upgrade guide](https://etcd.io/docs/latest/upgrades/upgrade_3_3/).**
### Improved
- Add `etcd --experimental-peer-skip-client-san-verification` to [skip verification of peer client address](https://github.com/etcd-io/etcd/pull/11196).
### Metrics, Monitoring
See [List of metrics](https://github.com/etcd-io/etcd/tree/main/Documentation/metrics) for all metrics per release.
Note that any `etcd_debugging_*` metrics are experimental and subject to change.
- Add [`etcd_debugging_mvcc_current_revision`](https://github.com/etcd-io/etcd/pull/11126) Prometheus metric.
- Add [`etcd_debugging_mvcc_compact_revision`](https://github.com/etcd-io/etcd/pull/11126) Prometheus metric.
### Dependency
- Upgrade [`github.com/coreos/bbolt`](https://github.com/etcd-io/bbolt/releases) from [**`v1.3.1-coreos.6`**](https://github.com/etcd-io/bbolt/releases/tag/v1.3.1-coreos.6) to [**`v1.3.3`**](https://github.com/etcd-io/bbolt/releases/tag/v1.3.3).
### etcdctl v3
- Fix [`etcdctl member add`](https://github.com/etcd-io/etcd/pull/11194) command to prevent potential timeout.
### Go
- Compile with [*Go 1.12.9*](https://golang.org/doc/devel/release.html#go1.12) including [*Go 1.12.8*](https://groups.google.com/d/msg/golang-announce/65QixT3tcmg/DrFiG6vvCwAJ) security fixes.
### client v3
- Fix [client balancer failover against multiple endpoints](https://github.com/etcd-io/etcd/pull/11184).
- Fix ["kube-apiserver: failover on multi-member etcd cluster fails certificate check on DNS mismatch" (kubernetes#83028)](https://github.com/kubernetes/kubernetes/issues/83028).
- Fix [IPv6 endpoint parsing in client](https://github.com/etcd-io/etcd/pull/11211).
- Fix ["1.16: etcd client does not parse IPv6 addresses correctly when members are joining" (kubernetes#83550)](https://github.com/kubernetes/kubernetes/issues/83550).
---
## [v3.3.15](https://github.com/etcd-io/etcd/releases/tag/v3.3.15) (2019-08-19)
See [code changes](https://github.com/etcd-io/etcd/compare/v3.3.14...v3.3.15) and [v3.3 upgrade guide](https://etcd.io/docs/latest/upgrades/upgrade_3_3/) for any breaking changes.
**Again, before running upgrades from any previous release, please make sure to read change logs below and [v3.3 upgrade guide](https://etcd.io/docs/latest/upgrades/upgrade_3_3/).**
NOTE: This patch release had to include some new features from 3.4, while trying to minimize the difference between client balancer implementation. This release fixes ["kube-apiserver 1.13.x refuses to work when first etcd-server is not available" (kubernetes#72102)](https://github.com/kubernetes/kubernetes/issues/72102).
### Breaking Changes
- Revert "Migrate dependency management tool from `glide` to [Go module](https://github.com/etcd-io/etcd/pull/10063)".
- Now, etcd >= v3.3.15 uses `glide` for dependency management.
- See [kubernetes#81434](https://github.com/kubernetes/kubernetes/pull/81434) for more contexts.
### Go
- Require [*Go 1.12+*](https://github.com/etcd-io/etcd/pull/10045).
- Compile with [*Go 1.12.9*](https://golang.org/doc/devel/release.html#go1.12) including [*Go 1.12.8*](https://groups.google.com/d/msg/golang-announce/65QixT3tcmg/DrFiG6vvCwAJ) security fixes.
---
## [v3.3.14](https://github.com/etcd-io/etcd/releases/tag/v3.3.14) (2019-08-16)
See [code changes](https://github.com/etcd-io/etcd/compare/v3.3.13...v3.3.14) and [v3.3 upgrade guide](https://etcd.io/docs/latest/upgrades/upgrade_3_3/) for any breaking changes.
- [v3.3.14-rc.0](https://github.com/etcd-io/etcd/releases/tag/v3.3.14-rc.0) (2019-08-15), see [code changes](https://github.com/etcd-io/etcd/compare/v3.3.14-beta.0...v3.3.14-rc.0).
- [v3.3.14-beta.0](https://github.com/etcd-io/etcd/releases/tag/v3.3.14-beta.0) (2019-08-14), see [code changes](https://github.com/etcd-io/etcd/compare/v3.3.13...v3.3.14-beta.0).
**Again, before running upgrades from any previous release, please make sure to read change logs below and [v3.3 upgrade guide](https://etcd.io/docs/latest/upgrades/upgrade_3_3/).**
NOTE: This patch release had to include some new features from 3.4, while trying to minimize the difference between client balancer implementation. This release fixes ["kube-apiserver 1.13.x refuses to work when first etcd-server is not available" (kubernetes#72102)](https://github.com/kubernetes/kubernetes/issues/72102).
### Breaking Changes
- Rewrite [client balancer](https://github.com/etcd-io/etcd/pull/9860) with [new gRPC balancer interface](https://github.com/etcd-io/etcd/issues/9106).
- Upgrade [gRPC to v1.23.0](https://github.com/etcd-io/etcd/pull/10911).
- Improve [client balancer failover against secure endpoints](https://github.com/etcd-io/etcd/pull/10911).
- Fix ["kube-apiserver 1.13.x refuses to work when first etcd-server is not available" (kubernetes#72102)](https://github.com/kubernetes/kubernetes/issues/72102).
- [The new client balancer](https://etcd.io/docs/latest/learning/design-client/) uses an asynchronous resolver to pass endpoints to the gRPC dial function. to block until the underlying connection is up, pass `grpc.WithBlock()` to `clientv3.Config.DialOptions`.
- Require [*Go 1.12+*](https://github.com/etcd-io/etcd/pull/10045).
- Compile with [*Go 1.12.9*](https://golang.org/doc/devel/release.html#go1.12) including [*Go 1.12.8*](https://groups.google.com/d/msg/golang-announce/65QixT3tcmg/DrFiG6vvCwAJ) security fixes.
- Migrate dependency management tool from `glide` to [Go module](https://github.com/etcd-io/etcd/pull/10063).
- <= 3.3 puts `vendor` directory under `cmd/vendor` directory to [prevent conflicting transitive dependencies](https://github.com/etcd-io/etcd/issues/4913).
- 3.4 moves `cmd/vendor` directory to `vendor` at repository root.
- Remove recursive symlinks in `cmd` directory.
- Now `go get/install/build` on `etcd` packages (e.g. `clientv3`, `tools/benchmark`) enforce builds with etcd `vendor` directory.
- Deprecated `latest` [release container](https://console.cloud.google.com/gcr/images/etcd-development/GLOBAL/etcd) tag.
- **`docker pull gcr.io/etcd-development/etcd:latest` would not be up-to-date**.
- Deprecated [minor](https://semver.org/) version [release container](https://console.cloud.google.com/gcr/images/etcd-development/GLOBAL/etcd) tags.
- `docker pull gcr.io/etcd-development/etcd:v3.3` would still work but may be stale.
- **`docker pull gcr.io/etcd-development/etcd:v3.4` would not work**.
- Use **`docker pull gcr.io/etcd-development/etcd:v3.3.14`** instead, with the exact patch version.
- Deprecated [ACIs from official release](https://github.com/etcd-io/etcd/pull/9059).
- [AppC was officially suspended](https://github.com/appc/spec#-disclaimer-), as of late 2016.
- [`acbuild`](https://github.com/containers/build#this-project-is-currently-unmaintained) is not maintained anymore.
- `*.aci` files are not available from `v3.4` release.
### etcd server
- Add [`rpctypes.ErrLeaderChanged`](https://github.com/etcd-io/etcd/pull/10094).
- Now linearizable requests with read index would fail fast when there is a leadership change, instead of waiting until context timeout.
- Fix [race condition in `rafthttp` transport pause/resume](https://github.com/etcd-io/etcd/pull/10826).
### API
- Add [`watch_id` field to `etcdserverpb.WatchCreateRequest`](https://github.com/etcd-io/etcd/pull/9065) to allow user-provided watch ID to `mvcc`.
- Corresponding `watch_id` is returned via `etcdserverpb.WatchResponse`, if any.
- Add [`fragment` field to `etcdserverpb.WatchCreateRequest`](https://github.com/etcd-io/etcd/pull/9291) to request etcd server to [split watch events](https://github.com/etcd-io/etcd/issues/9294) when the total size of events exceeds `etcd --max-request-bytes` flag value plus gRPC-overhead 512 bytes.
- The default server-side request bytes limit is `embed.DefaultMaxRequestBytes` which is 1.5 MiB plus gRPC-overhead 512 bytes.
- If watch response events exceed this server-side request limit and watch request is created with `fragment` field `true`, the server will split watch events into a set of chunks, each of which is a subset of watch events below server-side request limit.
- Useful when client-side has limited bandwidths.
- For example, watch response contains 10 events, where each event is 1 MiB. And server `etcd --max-request-bytes` flag value is 1 MiB. Then, server will send 10 separate fragmented events to the client.
- For example, watch response contains 5 events, where each event is 2 MiB. And server `etcd --max-request-bytes` flag value is 1 MiB and `clientv3.Config.MaxCallRecvMsgSize` is 1 MiB. Then, server will try to send 5 separate fragmented events to the client, and the client will error with `"code = ResourceExhausted desc = grpc: received message larger than max (...)"`.
- Client must implement fragmented watch event merge (which `clientv3` does in etcd v3.4).
- Add [`WatchRequest.WatchProgressRequest`](https://github.com/etcd-io/etcd/pull/9869).
- To manually trigger broadcasting watch progress event (empty watch response with latest header) to all associated watch streams.
- Think of it as `WithProgressNotify` that can be triggered manually.
### Metrics, Monitoring
See [List of metrics](https://github.com/etcd-io/etcd/tree/main/Documentation/metrics) for all metrics per release.
Note that any `etcd_debugging_*` metrics are experimental and subject to change.
- Add [`etcd_network_snapshot_send_inflights_total`](https://github.com/etcd-io/etcd/pull/11009) Prometheus metric.
- Add [`etcd_network_snapshot_receive_inflights_total`](https://github.com/etcd-io/etcd/pull/11009) Prometheus metric.
- Add [`etcd_server_snapshot_apply_in_progress_total`](https://github.com/etcd-io/etcd/pull/11009) Prometheus metric.
### client v3
- Fix [gRPC panic "send on closed channel](https://github.com/etcd-io/etcd/issues/9956) by upgrading [`google.golang.org/grpc`](https://github.com/grpc/grpc-go/releases) from [**`v1.7.5`**](https://github.com/grpc/grpc-go/releases/tag/v1.7.5) to [**`v1.23.0`**](https://github.com/grpc/grpc-go/releases/tag/v1.23.0).
- Rewrite [client balancer](https://github.com/etcd-io/etcd/pull/9860) with [new gRPC balancer interface](https://github.com/etcd-io/etcd/issues/9106).
- Upgrade [gRPC to v1.23.0](https://github.com/etcd-io/etcd/pull/10911).
- Improve [client balancer failover against secure endpoints](https://github.com/etcd-io/etcd/pull/10911).
- Fix ["kube-apiserver 1.13.x refuses to work when first etcd-server is not available" (kubernetes#72102)](https://github.com/kubernetes/kubernetes/issues/72102).
- [The new client balancer](https://etcd.io/docs/latest/learning/design-client/) uses an asynchronous resolver to pass endpoints to the gRPC dial function. to block until the underlying connection is up, pass `grpc.WithBlock()` to `clientv3.Config.DialOptions`.
### etcdctl v3
- Add [`etcdctl endpoint health --write-out` support](https://github.com/etcd-io/etcd/pull/9540).
- Previously, [`etcdctl endpoint health --write-out json` did not work](https://github.com/etcd-io/etcd/issues/9532).
- The command output is changed. Previously, if endpoint is unreachable, the command output is
"\ is unhealthy: failed to connect: \". This change unified the error message, all error types
now have the same output "\ is unhealthy: failed to commit proposal: \".
- Add [missing newline in `etcdctl endpoint health`](https://github.com/etcd-io/etcd/pull/10793).
### Package `pkg/adt`
- Change [`pkg/adt.IntervalTree` from `struct` to `interface`](https://github.com/etcd-io/etcd/pull/10959).
- See [`pkg/adt` README](https://github.com/etcd-io/etcd/tree/main/pkg/adt) and [`pkg/adt` godoc](https://godoc.org/go.etcd.io/etcd/pkg/adt).
- Improve [`pkg/adt.IntervalTree` test coverage](https://github.com/etcd-io/etcd/pull/10959).
- See [`pkg/adt` README](https://github.com/etcd-io/etcd/tree/main/pkg/adt) and [`pkg/adt` godoc](https://godoc.org/go.etcd.io/etcd/pkg/adt).
- Fix [Red-Black tree to maintain black-height property](https://github.com/etcd-io/etcd/pull/10978).
- Previously, delete operation violates [black-height property](https://github.com/etcd-io/etcd/issues/10965).
### Go
- Require [*Go 1.12+*](https://github.com/etcd-io/etcd/pull/10045).
- Compile with [*Go 1.12.9*](https://golang.org/doc/devel/release.html#go1.12) including [*Go 1.12.8*](https://groups.google.com/d/msg/golang-announce/65QixT3tcmg/DrFiG6vvCwAJ) security fixes.
---
## [v3.3.13](https://github.com/etcd-io/etcd/releases/tag/v3.3.13) (2019-05-02)
See [code changes](https://github.com/etcd-io/etcd/compare/v3.3.12...v3.3.13) and [v3.3 upgrade guide](https://etcd.io/docs/latest/upgrades/upgrade_3_3/) for any breaking changes.
**Again, before running upgrades from any previous release, please make sure to read change logs below and [v3.3 upgrade guide](https://etcd.io/docs/latest/upgrades/upgrade_3_3/).**
### Improved
- Improve [heartbeat send failure logging](https://github.com/etcd-io/etcd/pull/10663).
- Add [`Verify` function to perform corruption check on WAL contents](https://github.com/etcd-io/etcd/pull/10603).
### Metrics, Monitoring
See [List of metrics](https://github.com/etcd-io/etcd/tree/main/Documentation/metrics) for all metrics per release.
Note that any `etcd_debugging_*` metrics are experimental and subject to change.
- Fix bug where [db_compaction_total_duration_milliseconds metric incorrectly measured duration as 0](https://github.com/etcd-io/etcd/pull/10646).
### client v3
- Fix [`(*Client).Endpoints()` method race condition](https://github.com/etcd-io/etcd/pull/10595).
### Package `wal`
- Add [`Verify` function to perform corruption check on WAL contents](https://github.com/etcd-io/etcd/pull/10603).
### Dependency
- Migrate [`github.com/ugorji/go/codec`](https://github.com/ugorji/go/releases) to [**`github.com/json-iterator/go`**](https://github.com/json-iterator/go) (See [#10667](https://github.com/etcd-io/etcd/pull/10667) for more).
- Migrate [`github.com/ghodss/yaml`](https://github.com/ghodss/yaml/releases) to [**`sigs.k8s.io/yaml`**](https://github.com/kubernetes-sigs/yaml) (See [#10718](https://github.com/etcd-io/etcd/pull/10718) for more).
### Go
- Compile with [*Go 1.10.8*](https://golang.org/doc/devel/release.html#go1.10).
---
## [v3.3.12](https://github.com/etcd-io/etcd/releases/tag/v3.3.12) (2019-02-07)
See [code changes](https://github.com/etcd-io/etcd/compare/v3.3.11...v3.3.12) and [v3.3 upgrade guide](https://etcd.io/docs/latest/upgrades/upgrade_3_3/) for any breaking changes.
**Again, before running upgrades from any previous release, please make sure to read change logs below and [v3.3 upgrade guide](https://etcd.io/docs/latest/upgrades/upgrade_3_3/).**
### etcdctl v3
- [Strip out insecure endpoints from DNS SRV records when using discovery](https://github.com/etcd-io/etcd/pull/10443) with etcdctl v2
### Go
- Compile with [*Go 1.10.8*](https://golang.org/doc/devel/release.html#go1.10).
---
## [v3.3.11](https://github.com/etcd-io/etcd/releases/tag/v3.3.11) (2019-01-11)
See [code changes](https://github.com/etcd-io/etcd/compare/v3.3.10...v3.3.11) and [v3.3 upgrade guide](https://etcd.io/docs/latest/upgrades/upgrade_3_3/) for any breaking changes.
**Again, before running upgrades from any previous release, please make sure to read change logs below and [v3.3 upgrade guide](https://etcd.io/docs/latest/upgrades/upgrade_3_3/).**
### gRPC Proxy
- Fix [memory leak in cache layer](https://github.com/etcd-io/etcd/pull/10327).
### Security, Authentication
- Disable [CommonName authentication for gRPC-gateway](https://github.com/etcd-io/etcd/pull/10366) gRPC-gateway proxy requests to etcd server use the etcd client server TLS certificate. If that certificate contains CommonName we do not want to use that for authentication as it could lead to permission escalation.
### Go
- Compile with [*Go 1.10.7*](https://golang.org/doc/devel/release.html#go1.10).
---
## [v3.3.10](https://github.com/etcd-io/etcd/releases/tag/v3.3.10) (2018-10-10)
See [code changes](https://github.com/etcd-io/etcd/compare/v3.3.9...v3.3.10) and [v3.3 upgrade guide](https://etcd.io/docs/latest/upgrades/upgrade_3_3/) for any breaking changes.
**Again, before running upgrades from any previous release, please make sure to read change logs below and [v3.3 upgrade guide](https://etcd.io/docs/latest/upgrades/upgrade_3_3/).**
### Improved
- Improve ["became inactive" warning log](https://github.com/etcd-io/etcd/pull/10024), which indicates message send to a peer failed.
- Improve [read index wait timeout warning log](https://github.com/etcd-io/etcd/pull/10026), which indicates that local node might have slow network.
- Add [gRPC interceptor for debugging logs](https://github.com/etcd-io/etcd/pull/9990); enable `etcd --debug` flag to see per-request debug information.
- Add [consistency check in snapshot status](https://github.com/etcd-io/etcd/pull/10109). If consistency check on snapshot file fails, `snapshot status` returns `"snapshot file integrity check failed..."` error.
### Metrics, Monitoring
See [List of metrics](https://github.com/etcd-io/etcd/tree/main/Documentation/metrics) for all metrics per release.
Note that any `etcd_debugging_*` metrics are experimental and subject to change.
- Improve [`etcd_network_peer_round_trip_time_seconds`](https://github.com/etcd-io/etcd/pull/10155) Prometheus metric to track leader heartbeats.
- Previously, it only samples the TCP connection for snapshot messages.
- Add [`etcd_snap_db_fsync_duration_seconds_count`](https://github.com/etcd-io/etcd/pull/9997) Prometheus metric.
- Add [`etcd_snap_db_save_total_duration_seconds_bucket`](https://github.com/etcd-io/etcd/pull/9997) Prometheus metric.
- Add [`etcd_network_snapshot_send_success`](https://github.com/etcd-io/etcd/pull/9997) Prometheus metric.
- Add [`etcd_network_snapshot_send_failures`](https://github.com/etcd-io/etcd/pull/9997) Prometheus metric.
- Add [`etcd_network_snapshot_send_total_duration_seconds`](https://github.com/etcd-io/etcd/pull/9997) Prometheus metric.
- Add [`etcd_network_snapshot_receive_success`](https://github.com/etcd-io/etcd/pull/9997) Prometheus metric.
- Add [`etcd_network_snapshot_receive_failures`](https://github.com/etcd-io/etcd/pull/9997) Prometheus metric.
- Add [`etcd_network_snapshot_receive_total_duration_seconds`](https://github.com/etcd-io/etcd/pull/9997) Prometheus metric.
- Add [`etcd_server_id`](https://github.com/etcd-io/etcd/pull/9998) Prometheus metric.
- Add [`etcd_server_health_success`](https://github.com/etcd-io/etcd/pull/10156) Prometheus metric.
- Add [`etcd_server_health_failures`](https://github.com/etcd-io/etcd/pull/10156) Prometheus metric.
- Add [`etcd_server_read_indexes_failed_total`](https://github.com/etcd-io/etcd/pull/10094) Prometheus metric.
### client v3
- Fix logic on [release lock key if cancelled](https://github.com/etcd-io/etcd/pull/10153) in `clientv3/concurrency` package.
### Go
- Compile with [*Go 1.10.4*](https://golang.org/doc/devel/release.html#go1.10).
---
## [v3.3.9](https://github.com/etcd-io/etcd/releases/tag/v3.3.9) (2018-07-24)
See [code changes](https://github.com/etcd-io/etcd/compare/v3.3.8...v3.3.9) and [v3.3 upgrade guide](https://etcd.io/docs/latest/upgrades/upgrade_3_3/) for any breaking changes.
**Again, before running upgrades from any previous release, please make sure to read change logs below and [v3.3 upgrade guide](https://etcd.io/docs/latest/upgrades/upgrade_3_3/).**
### Improved
- Improve [Raft Read Index timeout warning messages](https://github.com/etcd-io/etcd/pull/9897).
### Security, Authentication
- Compile with [*Go 1.10.3*](https://golang.org/doc/devel/release.html#go1.10) to support [crypto/x509 "Name Constraints"](https://github.com/etcd-io/etcd/issues/9912).
### Metrics, Monitoring
See [List of metrics](https://github.com/etcd-io/etcd/tree/main/Documentation/metrics) for all metrics per release.
Note that any `etcd_debugging_*` metrics are experimental and subject to change.
- Add [`etcd_server_go_version`](https://github.com/etcd-io/etcd/pull/9957) Prometheus metric.
- Add [`etcd_server_heartbeat_send_failures_total`](https://github.com/etcd-io/etcd/pull/9940) Prometheus metric.
- Add [`etcd_server_slow_apply_total`](https://github.com/etcd-io/etcd/pull/9940) Prometheus metric.
- Add [`etcd_disk_backend_defrag_duration_seconds`](https://github.com/etcd-io/etcd/pull/9940) Prometheus metric.
- Add [`etcd_mvcc_hash_duration_seconds`](https://github.com/etcd-io/etcd/pull/9940) Prometheus metric.
- Add [`etcd_mvcc_hash_rev_duration_seconds`](https://github.com/etcd-io/etcd/pull/9940) Prometheus metric.
- Add [`etcd_server_slow_read_indexes_total`](https://github.com/etcd-io/etcd/pull/9897) Prometheus metric.
- Add [`etcd_server_quota_backend_bytes`](https://github.com/etcd-io/etcd/pull/9820) Prometheus metric.
- Use it with `etcd_mvcc_db_total_size_in_bytes` and `etcd_mvcc_db_total_size_in_use_in_bytes`.
- `etcd_server_quota_backend_bytes 2.147483648e+09` means current quota size is 2 GB.
- `etcd_mvcc_db_total_size_in_bytes 20480` means current physically allocated DB size is 20 KB.
- `etcd_mvcc_db_total_size_in_use_in_bytes 16384` means future DB size if defragment operation is complete.
- `etcd_mvcc_db_total_size_in_bytes - etcd_mvcc_db_total_size_in_use_in_bytes` is the number of bytes that can be saved on disk with defragment operation.
- Add [`etcd_mvcc_db_total_size_in_bytes`](https://github.com/etcd-io/etcd/pull/9819) Prometheus metric.
- In addition to [`etcd_debugging_mvcc_db_total_size_in_bytes`](https://github.com/etcd-io/etcd/pull/9819).
- Add [`etcd_mvcc_db_total_size_in_use_in_bytes`](https://github.com/etcd-io/etcd/pull/9256) Prometheus metric.
- Use it with `etcd_mvcc_db_total_size_in_bytes` and `etcd_mvcc_db_total_size_in_use_in_bytes`.
- `etcd_server_quota_backend_bytes 2.147483648e+09` means current quota size is 2 GB.
- `etcd_mvcc_db_total_size_in_bytes 20480` means current physically allocated DB size is 20 KB.
- `etcd_mvcc_db_total_size_in_use_in_bytes 16384` means future DB size if defragment operation is complete.
- `etcd_mvcc_db_total_size_in_bytes - etcd_mvcc_db_total_size_in_use_in_bytes` is the number of bytes that can be saved on disk with defragment operation.
### client v3
- Fix [lease keepalive interval updates when response queue is full](https://github.com/etcd-io/etcd/pull/9952).
- If `<-chan *clientv3LeaseKeepAliveResponse` from `clientv3.Lease.KeepAlive` was never consumed or channel is full, client was [sending keepalive request every 500ms](https://github.com/etcd-io/etcd/issues/9911) instead of expected rate of every "TTL / 3" duration.
### Go
- Compile with [*Go 1.10.3*](https://golang.org/doc/devel/release.html#go1.10).
---
## [v3.3.8](https://github.com/etcd-io/etcd/releases/tag/v3.3.8) (2018-06-15)
See [code changes](https://github.com/etcd-io/etcd/compare/v3.3.7...v3.3.8) and [v3.3 upgrade guide](https://etcd.io/docs/latest/upgrades/upgrade_3_3/) for any breaking changes.
**Again, before running upgrades from any previous release, please make sure to read change logs below and [v3.3 upgrade guide](https://etcd.io/docs/latest/upgrades/upgrade_3_3/).**
### Improved
- Improve [slow request apply warning log](https://github.com/etcd-io/etcd/pull/9288).
- e.g. `read-only range request "key:\"/a\" range_end:\"/b\" " with result "range_response_count:3 size:96" took too long (97.966µs) to execute`.
- Redact [request value field](https://github.com/etcd-io/etcd/pull/9822).
- Provide [response size](https://github.com/etcd-io/etcd/pull/9826).
- Add [backoff on watch retries on transient errors](https://github.com/etcd-io/etcd/pull/9840).
### Go
- Compile with [*Go 1.9.7*](https://golang.org/doc/devel/release.html#go1.9).
---
## [v3.3.7](https://github.com/etcd-io/etcd/releases/tag/v3.3.7) (2018-06-06)
See [code changes](https://github.com/etcd-io/etcd/compare/v3.3.6...v3.3.7) and [v3.3 upgrade guide](https://etcd.io/docs/latest/upgrades/upgrade_3_3/) for any breaking changes.
**Again, before running upgrades from any previous release, please make sure to read change logs below and [v3.3 upgrade guide](https://etcd.io/docs/latest/upgrades/upgrade_3_3/).**
### Security, Authentication
- Support TLS cipher suite whitelisting.
- To block [weak cipher suites](https://github.com/etcd-io/etcd/issues/8320).
- TLS handshake fails when client hello is requested with invalid cipher suites.
- Add [`etcd --cipher-suites`](https://github.com/etcd-io/etcd/pull/9801) flag.
- If empty, Go auto-populates the list.
### etcdctl v3
- Fix [`etcdctl move-leader` command for TLS-enabled endpoints](https://github.com/etcd-io/etcd/pull/9807).
### Go
- Compile with [*Go 1.9.6*](https://golang.org/doc/devel/release.html#go1.9).
---
## [v3.3.6](https://github.com/etcd-io/etcd/releases/tag/v3.3.6) (2018-05-31)
See [code changes](https://github.com/etcd-io/etcd/compare/v3.3.5...v3.3.6) and [v3.3 upgrade guide](https://etcd.io/docs/latest/upgrades/upgrade_3_3/) for any breaking changes.
**Again, before running upgrades from any previous release, please make sure to read change logs below and [v3.3 upgrade guide](https://etcd.io/docs/latest/upgrades/upgrade_3_3/).**
### etcd server
- Allow [empty auth token](https://github.com/etcd-io/etcd/pull/9369).
- Previously, when auth token is an empty string, it returns [`failed to initialize the etcd server: auth: invalid auth options` error](https://github.com/etcd-io/etcd/issues/9349).
- Fix [auth storage panic on server lease revoke routine with JWT token](https://github.com/etcd-io/etcd/issues/9695).
- Fix [`mvcc` server panic from restore operation](https://github.com/etcd-io/etcd/pull/9775).
- Let's assume that a watcher had been requested with a future revision X and sent to node A that became network-partitioned thereafter. Meanwhile, cluster makes progress. Then when the partition gets removed, the leader sends a snapshot to node A. Previously if the snapshot's latest revision is still lower than the watch revision X, **etcd server panicked** during snapshot restore operation.
- Now, this server-side panic has been fixed.
### Go
- Compile with [*Go 1.9.6*](https://golang.org/doc/devel/release.html#go1.9).
---
## [v3.3.5](https://github.com/etcd-io/etcd/releases/tag/v3.3.5) (2018-05-09)
See [code changes](https://github.com/etcd-io/etcd/compare/v3.3.4...v3.3.5) and [v3.3 upgrade guide](https://etcd.io/docs/latest/upgrades/upgrade_3_3/) for any breaking changes.
**Again, before running upgrades from any previous release, please make sure to read change logs below and [v3.3 upgrade guide](https://etcd.io/docs/latest/upgrades/upgrade_3_3/).**
### etcdctl v3
- Fix [`etcdctl watch [key] [range_end] -- [exec-command…]`](https://github.com/etcd-io/etcd/pull/9688) parsing.
- Previously, `ETCDCTL_API=3 ./bin/etcdctl watch foo -- echo watch event received` panicked.
### Go
- Compile with [*Go 1.9.6*](https://golang.org/doc/devel/release.html#go1.9).
---
## [v3.3.4](https://github.com/etcd-io/etcd/releases/tag/v3.3.4) (2018-04-24)
See [code changes](https://github.com/etcd-io/etcd/compare/v3.3.3...v3.3.4) and [v3.3 upgrade guide](https://etcd.io/docs/latest/upgrades/upgrade_3_3/) for any breaking changes.
**Again, before running upgrades from any previous release, please make sure to read change logs below and [v3.3 upgrade guide](https://etcd.io/docs/latest/upgrades/upgrade_3_3/).**
### Metrics, Monitoring
See [List of metrics](https://github.com/etcd-io/etcd/tree/main/Documentation/metrics) for all metrics per release.
Note that any `etcd_debugging_*` metrics are experimental and subject to change.
- Add [`etcd_server_is_leader`](https://github.com/etcd-io/etcd/pull/9587) Prometheus metric.
- Fix [`etcd_debugging_server_lease_expired_total`](https://github.com/etcd-io/etcd/pull/9557) Prometheus metric.
- Fix [race conditions in v2 server stat collecting](https://github.com/etcd-io/etcd/pull/9562).
### Security, Authentication
- Fix [TLS reload](https://github.com/etcd-io/etcd/pull/9570) when [certificate SAN field only includes IP addresses but no domain names](https://github.com/etcd-io/etcd/issues/9541).
- In Go, server calls `(*tls.Config).GetCertificate` for TLS reload if and only if server's `(*tls.Config).Certificates` field is not empty, or `(*tls.ClientHelloInfo).ServerName` is not empty with a valid SNI from the client. Previously, etcd always populates `(*tls.Config).Certificates` on the initial client TLS handshake, as non-empty. Thus, client was always expected to supply a matching SNI in order to pass the TLS verification and to trigger `(*tls.Config).GetCertificate` to reload TLS assets.
- However, a certificate whose SAN field does [not include any domain names but only IP addresses](https://github.com/etcd-io/etcd/issues/9541) would request `*tls.ClientHelloInfo` with an empty `ServerName` field, thus failing to trigger the TLS reload on initial TLS handshake; this becomes a problem when expired certificates need to be replaced online.
- Now, `(*tls.Config).Certificates` is created empty on initial TLS client handshake, first to trigger `(*tls.Config).GetCertificate`, and then to populate rest of the certificates on every new TLS connection, even when client SNI is empty (e.g. cert only includes IPs).
### etcd server
- Add [`etcd --initial-election-tick-advance`](https://github.com/etcd-io/etcd/pull/9591) flag to configure initial election tick fast-forward.
- By default, `etcd --initial-election-tick-advance=true`, then local member fast-forwards election ticks to speed up "initial" leader election trigger.
- This benefits the case of larger election ticks. For instance, cross datacenter deployment may require longer election timeout of 10-second. If true, local node does not need wait up to 10-second. Instead, forwards its election ticks to 8-second, and have only 2-second left before leader election.
- Major assumptions are that: cluster has no active leader thus advancing ticks enables faster leader election. Or cluster already has an established leader, and rejoining follower is likely to receive heartbeats from the leader after tick advance and before election timeout.
- However, when network from leader to rejoining follower is congested, and the follower does not receive leader heartbeat within left election ticks, disruptive election has to happen thus affecting cluster availabilities.
- Now, this can be disabled by setting `--initial-election-tick-advance=false`.
- Disabling this would slow down initial bootstrap process for cross datacenter deployments. Make tradeoffs by configuring `etcd --initial-election-tick-advance` at the cost of slow initial bootstrap.
- If single-node, it advances ticks regardless.
- Address [disruptive rejoining follower node](https://github.com/etcd-io/etcd/issues/9333).
### Package `embed`
- Add [`embed.Config.InitialElectionTickAdvance`](https://github.com/etcd-io/etcd/pull/9591) to enable/disable initial election tick fast-forward.
- `embed.NewConfig()` would return `*embed.Config` with `InitialElectionTickAdvance` as true by default.
### Go
- Compile with [*Go 1.9.5*](https://golang.org/doc/devel/release.html#go1.9).
---
## [v3.3.3](https://github.com/etcd-io/etcd/releases/tag/v3.3.3) (2018-03-29)
See [code changes](https://github.com/etcd-io/etcd/compare/v3.3.2...v3.3.3) and [v3.3 upgrade guide](https://etcd.io/docs/latest/upgrades/upgrade_3_3/) for any breaking changes.
**Again, before running upgrades from any previous release, please make sure to read change logs below and [v3.3 upgrade guide](https://etcd.io/docs/latest/upgrades/upgrade_3_3/).**
### Improved
- Adjust [election timeout on server restart](https://github.com/etcd-io/etcd/pull/9415) to reduce [disruptive rejoining servers](https://github.com/etcd-io/etcd/issues/9333).
- Previously, etcd fast-forwards election ticks on server start, with only one tick left for leader election. This is to speed up start phase, without having to wait until all election ticks elapse. Advancing election ticks is useful for cross datacenter deployments with larger election timeouts. However, it was affecting cluster availability if the last tick elapses before leader contacts the restarted node.
- Now, when etcd restarts, it adjusts election ticks with more than one tick left, thus more time for leader to prevent disruptive restart.
- Adjust [periodic compaction retention window](https://github.com/etcd-io/etcd/pull/9485).
- e.g. `etcd --auto-compaction-mode=revision --auto-compaction-retention=1000` automatically `Compact` on `"latest revision" - 1000` every 5-minute (when latest revision is 30000, compact on revision 29000).
- e.g. Previously, `etcd --auto-compaction-mode=periodic --auto-compaction-retention=72h` automatically `Compact` with 72-hour retention windown for every 7.2-hour. **Now, `Compact` happens, for every 1-hour but still with 72-hour retention window.**
- e.g. Previously, `etcd --auto-compaction-mode=periodic --auto-compaction-retention=30m` automatically `Compact` with 30-minute retention windown for every 3-minute. **Now, `Compact` happens, for every 30-minute but still with 30-minute retention window.**
- Periodic compactor keeps recording latest revisions for every compaction period when given period is less than 1-hour, or for every 1-hour when given compaction period is greater than 1-hour (e.g. 1-hour when `etcd --auto-compaction-mode=periodic --auto-compaction-retention=24h`).
- For every compaction period or 1-hour, compactor uses the last revision that was fetched before compaction period, to discard historical data.
- The retention window of compaction period moves for every given compaction period or hour.
- For instance, when hourly writes are 100 and `etcd --auto-compaction-mode=periodic --auto-compaction-retention=24h`, `v3.2.x`, `v3.3.0`, `v3.3.1`, and `v3.3.2` compact revision 2400, 2640, and 2880 for every 2.4-hour, while `v3.3.3` *or later* compacts revision 2400, 2500, 2600 for every 1-hour.
- Furthermore, when `etcd --auto-compaction-mode=periodic --auto-compaction-retention=30m` and writes per minute are about 1000, `v3.3.0`, `v3.3.1`, and `v3.3.2` compact revision 30000, 33000, and 36000, for every 3-minute, while `v3.3.3` *or later* compacts revision 30000, 60000, and 90000, for every 30-minute.
### Metrics, Monitoring
See [List of metrics](https://github.com/etcd-io/etcd/tree/main/Documentation/metrics) for all metrics per release.
Note that any `etcd_debugging_*` metrics are experimental and subject to change.
- Add missing [`etcd_network_peer_sent_failures_total` count](https://github.com/etcd-io/etcd/pull/9437).
### Go
- Compile with [*Go 1.9.5*](https://golang.org/doc/devel/release.html#go1.9).
---
## [v3.3.2](https://github.com/etcd-io/etcd/releases/tag/v3.3.2) (2018-03-08)
See [code changes](https://github.com/etcd-io/etcd/compare/v3.3.1...v3.3.2) and [v3.3 upgrade guide](https://etcd.io/docs/latest/upgrades/upgrade_3_3/) for any breaking changes.
**Again, before running upgrades from any previous release, please make sure to read change logs below and [v3.3 upgrade guide](https://etcd.io/docs/latest/upgrades/upgrade_3_3/).**
### etcd server
- Fix [server panic on invalid Election Proclaim/Resign HTTP(S) requests](https://github.com/etcd-io/etcd/pull/9379).
- Previously, wrong-formatted HTTP requests to Election API could trigger panic in etcd server.
- e.g. `curl -L http://localhost:2379/v3/election/proclaim -X POST -d '{"value":""}'`, `curl -L http://localhost:2379/v3/election/resign -X POST -d '{"value":""}'`.
- Fix [revision-based compaction retention parsing](https://github.com/etcd-io/etcd/pull/9339).
- Previously, `etcd --auto-compaction-mode revision --auto-compaction-retention 1` was [translated to revision retention 3600000000000](https://github.com/etcd-io/etcd/issues/9337).
- Now, `etcd --auto-compaction-mode revision --auto-compaction-retention 1` is correctly parsed as revision retention 1.
- Prevent [overflow by large `TTL` values for `Lease` `Grant`](https://github.com/etcd-io/etcd/pull/9399).
- `TTL` parameter to `Grant` request is unit of second.
- Leases with too large `TTL` values exceeding `math.MaxInt64` [expire in unexpected ways](https://github.com/etcd-io/etcd/issues/9374).
- Server now returns `rpctypes.ErrLeaseTTLTooLarge` to client, when the requested `TTL` is larger than *9,000,000,000 seconds* (which is >285 years).
- Again, etcd `Lease` is meant for short-periodic keepalives or sessions, in the range of seconds or minutes. Not for hours or days!
- Enable etcd server [`raft.Config.CheckQuorum` when starting with `ForceNewCluster`](https://github.com/etcd-io/etcd/pull/9347).
### Proxy v2
- Fix [v2 proxy leaky HTTP requests](https://github.com/etcd-io/etcd/pull/9336).
### Go
- Compile with [*Go 1.9.4*](https://golang.org/doc/devel/release.html#go1.9).
---
## [v3.3.1](https://github.com/etcd-io/etcd/releases/tag/v3.3.1) (2018-02-12)
See [code changes](https://github.com/etcd-io/etcd/compare/v3.3.0...v3.3.1) and [v3.3 upgrade guide](https://etcd.io/docs/latest/upgrades/upgrade_3_3/) for any breaking changes.
**Again, before running upgrades from any previous release, please make sure to read change logs below and [v3.3 upgrade guide](https://etcd.io/docs/latest/upgrades/upgrade_3_3/).**
### Improved
- Add [warnings on requests taking too long](https://github.com/etcd-io/etcd/pull/9288).
- e.g. `etcdserver: read-only range request "key:\"\\000\" range_end:\"\\000\" " took too long [3.389041388s] to execute`
### etcd server
- Fix [`mvcc` "unsynced" watcher restore operation](https://github.com/etcd-io/etcd/pull/9281).
- "unsynced" watcher is watcher that needs to be in sync with events that have happened.
- That is, "unsynced" watcher is the slow watcher that was requested on old revision.
- "unsynced" watcher restore operation was not correctly populating its underlying watcher group.
- Which possibly causes [missing events from "unsynced" watchers](https://github.com/etcd-io/etcd/issues/9086).
- A node gets network partitioned with a watcher on a future revision, and falls behind receiving a leader snapshot after partition gets removed. When applying this snapshot, etcd watch storage moves current synced watchers to unsynced since sync watchers might have become stale during network partition. And reset synced watcher group to restart watcher routines. Previously, there was a bug when moving from synced watcher group to unsynced, thus client would miss events when the watcher was requested to the network-partitioned node.
### Go
- Compile with [*Go 1.9.4*](https://golang.org/doc/devel/release.html#go1.9).
---
## [v3.3.0](https://github.com/etcd-io/etcd/releases/tag/v3.3.0) (2018-02-01)
See [code changes](https://github.com/etcd-io/etcd/compare/v3.2.0...v3.3.0) and [v3.3 upgrade guide](https://etcd.io/docs/latest/upgrades/upgrade_3_3/) for any breaking changes.
- [v3.3.0](https://github.com/etcd-io/etcd/releases/tag/v3.3.0) (2018-02-01), see [code changes](https://github.com/etcd-io/etcd/compare/v3.3.0-rc.4...v3.3.0).
- [v3.3.0-rc.4](https://github.com/etcd-io/etcd/releases/tag/v3.3.0-rc.4) (2018-01-22), see [code changes](https://github.com/etcd-io/etcd/compare/v3.3.0-rc.3...v3.3.0-rc.4).
- [v3.3.0-rc.3](https://github.com/etcd-io/etcd/releases/tag/v3.3.0-rc.3) (2018-01-17), see [code changes](https://github.com/etcd-io/etcd/compare/v3.3.0-rc.2...v3.3.0-rc.3).
- [v3.3.0-rc.2](https://github.com/etcd-io/etcd/releases/tag/v3.3.0-rc.2) (2018-01-11), see [code changes](https://github.com/etcd-io/etcd/compare/v3.3.0-rc.1...v3.3.0-rc.2).
- [v3.3.0-rc.1](https://github.com/etcd-io/etcd/releases/tag/v3.3.0-rc.1) (2018-01-02), see [code changes](https://github.com/etcd-io/etcd/compare/v3.3.0-rc.0...v3.3.0-rc.1).
- [v3.3.0-rc.0](https://github.com/etcd-io/etcd/releases/tag/v3.3.0-rc.0) (2017-12-20), see [code changes](https://github.com/etcd-io/etcd/compare/v3.2.0...v3.3.0-rc.0).
**Again, before running upgrades from any previous release, please make sure to read change logs below and [v3.3 upgrade guide](https://etcd.io/docs/latest/upgrades/upgrade_3_3/).**
### Improved
- Use [`coreos/bbolt`](https://github.com/coreos/bbolt/releases) to replace [`boltdb/bolt`](https://github.com/boltdb/bolt#project-status).
- Fix [etcd database size grows until `mvcc: database space exceeded`](https://github.com/etcd-io/etcd/issues/8009).
- [Support database size larger than 8GiB](https://github.com/etcd-io/etcd/pull/7525) (8GiB is now a suggested maximum size for normal environments)
- [Reduce memory allocation](https://github.com/etcd-io/etcd/pull/8428) on [Range operations](https://github.com/etcd-io/etcd/pull/8475).
- [Rate limit](https://github.com/etcd-io/etcd/pull/8099) and [randomize](https://github.com/etcd-io/etcd/pull/8101) lease revoke on restart or leader elections.
- Prevent [spikes in Raft proposal rate](https://github.com/etcd-io/etcd/issues/8096).
- Support `clientv3` balancer failover under [network faults/partitions](https://github.com/etcd-io/etcd/issues/8711).
- Better warning on [mismatched `etcd --initial-cluster`](https://github.com/etcd-io/etcd/pull/8083) flag.
- etcd compares `etcd --initial-advertise-peer-urls` against corresponding `etcd --initial-cluster` URLs with forward-lookup.
- If resolved IP addresses of `etcd --initial-advertise-peer-urls` and `etcd --initial-cluster` do not match (e.g. [due to DNS error](https://github.com/etcd-io/etcd/pull/9210)), etcd will exit with errors.
- v3.2 error: `etcd --initial-cluster must include s1=https://s1.test:2380 given --initial-advertise-peer-urls=https://s1.test:2380`.
- v3.3 error: `failed to resolve https://s1.test:2380 to match --initial-cluster=s1=https://s1.test:2380 (failed to resolve "https://s1.test:2380" (error ...))`.
### Breaking Changes
- Require [`google.golang.org/grpc`](https://github.com/grpc/grpc-go/releases) [**`v1.7.4`**](https://github.com/grpc/grpc-go/releases/tag/v1.7.4) or [**`v1.7.5`**](https://github.com/grpc/grpc-go/releases/tag/v1.7.5).
- Deprecate [`metadata.Incoming/OutgoingContext`](https://github.com/etcd-io/etcd/pull/7896).
- Deprecate `grpclog.Logger`, upgrade to [`grpclog.LoggerV2`](https://github.com/etcd-io/etcd/pull/8533).
- Deprecate [`grpc.ErrClientConnTimeout`](https://github.com/etcd-io/etcd/pull/8505) errors in `clientv3`.
- Use [`MaxRecvMsgSize` and `MaxSendMsgSize`](https://github.com/etcd-io/etcd/pull/8437) to limit message size, in etcd server.
- Translate [gRPC status error in v3 client `Snapshot` API](https://github.com/etcd-io/etcd/pull/9038).
- v3 `etcdctl` [`lease timetolive LEASE_ID`](https://github.com/etcd-io/etcd/issues/9028) on expired lease now prints [`"lease LEASE_ID already expired"`](https://github.com/etcd-io/etcd/pull/9047).
- <=3.2 prints `"lease LEASE_ID granted with TTL(0s), remaining(-1s)"`.
- Replace [gRPC gateway](https://github.com/grpc-ecosystem/grpc-gateway) endpoint `/v3alpha` with [`/v3beta`](https://github.com/etcd-io/etcd/pull/8880).
- To deprecate [`/v3alpha`](https://github.com/etcd-io/etcd/issues/8125) in v3.4.
- In v3.3, `curl -L http://localhost:2379/v3alpha/kv/put -X POST -d '{"key": "Zm9v", "value": "YmFy"}'` still works as a fallback to `curl -L http://localhost:2379/v3beta/kv/put -X POST -d '{"key": "Zm9v", "value": "YmFy"}'`, but `curl -L http://localhost:2379/v3alpha/kv/put -X POST -d '{"key": "Zm9v", "value": "YmFy"}'` won't work in v3.4. Use `curl -L http://localhost:2379/v3beta/kv/put -X POST -d '{"key": "Zm9v", "value": "YmFy"}'` instead.
- Change `etcd --auto-compaction-retention` flag to [accept string values](https://github.com/etcd-io/etcd/pull/8563) with [finer granularity](https://github.com/etcd-io/etcd/issues/8503).
- Now that `etcd --auto-compaction-retention` accepts string values, etcd configuration YAML file `auto-compaction-retention` field must be changed to `string` type.
- Previously, `--config-file etcd.config.yaml` can have `auto-compaction-retention: 24` field, now must be `auto-compaction-retention: "24"` or `auto-compaction-retention: "24h"`.
- If configured as `etcd --auto-compaction-mode periodic --auto-compaction-retention "24h"`, the time duration value for `etcd --auto-compaction-retention` flag must be valid for [`time.ParseDuration`](https://golang.org/pkg/time/#ParseDuration) function in Go.
### Dependency
- Upgrade [`boltdb/bolt`](https://github.com/boltdb/bolt#project-status) from [**`v1.3.0`**](https://github.com/boltdb/bolt/releases/tag/v1.3.0) to [`coreos/bbolt`](https://github.com/coreos/bbolt/releases) [**`v1.3.1-coreos.6`**](https://github.com/coreos/bbolt/releases/tag/v1.3.1-coreos.6).
- Upgrade [`google.golang.org/grpc`](https://github.com/grpc/grpc-go/releases) from [**`v1.2.1`**](https://github.com/grpc/grpc-go/releases/tag/v1.2.1) to [**`v1.7.5`**](https://github.com/grpc/grpc-go/releases/tag/v1.7.5).
- Upgrade [`github.com/ugorji/go/codec`](https://github.com/ugorji/go) to [**`v1.1`**](https://github.com/ugorji/go/releases/tag/v1.1), and [regenerate v2 `client`](https://github.com/etcd-io/etcd/pull/8721).
- Upgrade [`github.com/ugorji/go/codec`](https://github.com/ugorji/go) to [**`ugorji/go@54210f4e0`**](https://github.com/ugorji/go/commit/54210f4e076c57f351166f0ed60e67d3fca57a36), and [regenerate v2 `client`](https://github.com/etcd-io/etcd/pull/8574).
- Upgrade [`github.com/grpc-ecosystem/grpc-gateway`](https://github.com/grpc-ecosystem/grpc-gateway/releases) from [**`v1.2.2`**](https://github.com/grpc-ecosystem/grpc-gateway/releases/tag/v1.2.2) to [**`v1.3.0`**](https://github.com/grpc-ecosystem/grpc-gateway/releases/tag/v1.3.0).
- Upgrade [`golang.org/x/crypto/bcrypt`](https://github.com/golang/crypto) to [**`golang/crypto@6c586e17d`**](https://github.com/golang/crypto/commit/6c586e17d90a7d08bbbc4069984180dce3b04117).
### Metrics, Monitoring
See [List of metrics](https://github.com/etcd-io/etcd/tree/main/Documentation/metrics) for all metrics per release.
Note that any `etcd_debugging_*` metrics are experimental and subject to change.
- Add [`etcd --listen-metrics-urls`](https://github.com/etcd-io/etcd/pull/8242) flag for additional `/metrics` and `/health` endpoints.
- Useful for [bypassing critical APIs when monitoring etcd](https://github.com/etcd-io/etcd/issues/8060).
- Add [`etcd_server_version`](https://github.com/etcd-io/etcd/pull/8960) Prometheus metric.
- To replace [Kubernetes `etcd-version-monitor`](https://github.com/etcd-io/etcd/issues/8948).
- Add [`etcd_debugging_mvcc_db_compaction_keys_total`](https://github.com/etcd-io/etcd/pull/8280) Prometheus metric.
- Add [`etcd_debugging_server_lease_expired_total`](https://github.com/etcd-io/etcd/pull/8064) Prometheus metric.
- To improve [lease revoke monitoring](https://github.com/etcd-io/etcd/issues/8050).
- Document [Prometheus 2.0 rules](https://github.com/etcd-io/etcd/pull/8879).
- Initialize gRPC server [metrics with zero values](https://github.com/etcd-io/etcd/pull/8878).
- Fix [range/put/delete operation metrics](https://github.com/etcd-io/etcd/pull/8054) with transaction.
- `etcd_debugging_mvcc_range_total`
- `etcd_debugging_mvcc_put_total`
- `etcd_debugging_mvcc_delete_total`
- `etcd_debugging_mvcc_txn_total`
- Fix [`etcd_debugging_mvcc_keys_total`](https://github.com/etcd-io/etcd/pull/8390) on restore.
- Fix [`etcd_debugging_mvcc_db_total_size_in_bytes`](https://github.com/etcd-io/etcd/pull/8120) on restore.
- Also change to [`prometheus.NewGaugeFunc`](https://github.com/etcd-io/etcd/pull/8150).
### Security, Authentication
See [security doc](https://etcd.io/docs/latest/op-guide/security/) for more details.
- Add [CRL based connection rejection](https://github.com/etcd-io/etcd/pull/8124) to manage [revoked certs](https://github.com/etcd-io/etcd/issues/4034).
- Document [TLS authentication changes](https://github.com/etcd-io/etcd/pull/8895).
- [Server accepts connections if IP matches, without checking DNS entries](https://github.com/etcd-io/etcd/pull/8223). For instance, if peer cert contains IP addresses and DNS names in Subject Alternative Name (SAN) field, and the remote IP address matches one of those IP addresses, server just accepts connection without further checking the DNS names.
- [Server supports reverse-lookup on wildcard DNS `SAN`](https://github.com/etcd-io/etcd/pull/8281). For instance, if peer cert contains only DNS names (no IP addresses) in Subject Alternative Name (SAN) field, server first reverse-lookups the remote IP address to get a list of names mapping to that address (e.g. `nslookup IPADDR`). Then accepts the connection if those names have a matching name with peer cert's DNS names (either by exact or wildcard match). If none is matched, server forward-lookups each DNS entry in peer cert (e.g. look up `example.default.svc` when the entry is `*.example.default.svc`), and accepts connection only when the host's resolved addresses have the matching IP address with the peer's remote IP address.
- Add [`etcd --peer-cert-allowed-cn`](https://github.com/etcd-io/etcd/pull/8616) flag.
- To support [CommonName(CN) based auth](https://github.com/etcd-io/etcd/issues/8262) for inter peer connection.
- [Swap priority](https://github.com/etcd-io/etcd/pull/8594) of cert CommonName(CN) and username + password.
- To address ["username and password specified in the request should take priority over CN in the cert"](https://github.com/etcd-io/etcd/issues/8584).
- Protect [lease revoke with auth](https://github.com/etcd-io/etcd/pull/8031).
- Provide user's role on [auth permission error](https://github.com/etcd-io/etcd/pull/8164).
- Fix [auth store panic with disabled token](https://github.com/etcd-io/etcd/pull/8695).
### etcd server
- Add [`etcd --experimental-initial-corrupt-check`](https://github.com/etcd-io/etcd/pull/8554) flag to [check cluster database hashes before serving client/peer traffic](https://github.com/etcd-io/etcd/issues/8313).
- `etcd --experimental-initial-corrupt-check=false` by default.
- v3.4 will enable `--initial-corrupt-check=true` by default.
- Add [`etcd --experimental-corrupt-check-time`](https://github.com/etcd-io/etcd/pull/8420) flag to [raise corrupt alarm monitoring](https://github.com/etcd-io/etcd/issues/7125).
- `etcd --experimental-corrupt-check-time=0s` disabled by default.
- Add [`etcd --experimental-enable-v2v3`](https://github.com/etcd-io/etcd/pull/8407) flag to [emulate v2 API with v3](https://github.com/etcd-io/etcd/issues/6925).
- `etcd --experimental-enable-v2v3=false` by default.
- Add [`etcd --max-txn-ops`](https://github.com/etcd-io/etcd/pull/7976) flag to [configure maximum number operations in transaction](https://github.com/etcd-io/etcd/issues/7826).
- Add [`etcd --max-request-bytes`](https://github.com/etcd-io/etcd/pull/7968) flag to [configure maximum client request size](https://github.com/etcd-io/etcd/issues/7923).
- If not configured, it defaults to 1.5 MiB.
- Add [`etcd --client-crl-file`, `--peer-crl-file`](https://github.com/etcd-io/etcd/pull/8124) flags for [Certificate revocation list](https://github.com/etcd-io/etcd/issues/4034).
- Add [`etcd --peer-cert-allowed-cn`](https://github.com/etcd-io/etcd/pull/8616) flag to support [CN-based auth for inter-peer connection](https://github.com/etcd-io/etcd/issues/8262).
- Add [`etcd --listen-metrics-urls`](https://github.com/etcd-io/etcd/pull/8242) flag for additional `/metrics` and `/health` endpoints.
- Support [additional (non) TLS `/metrics` endpoints for a TLS-enabled cluster](https://github.com/etcd-io/etcd/pull/8282).
- e.g. `etcd --listen-metrics-urls=https://localhost:2378,http://localhost:9379` to serve `/metrics` and `/health` on secure port 2378 and insecure port 9379.
- Useful for [bypassing critical APIs when monitoring etcd](https://github.com/etcd-io/etcd/issues/8060).
- Add [`etcd --auto-compaction-mode`](https://github.com/etcd-io/etcd/pull/8123) flag to [support revision-based compaction](https://github.com/etcd-io/etcd/issues/8098).
- Change `etcd --auto-compaction-retention` flag to [accept string values](https://github.com/etcd-io/etcd/pull/8563) with [finer granularity](https://github.com/etcd-io/etcd/issues/8503).
- Now that `etcd --auto-compaction-retention` accepts string values, etcd configuration YAML file `auto-compaction-retention` field must be changed to `string` type.
- Previously, `etcd --config-file etcd.config.yaml` can have `auto-compaction-retention: 24` field, now must be `auto-compaction-retention: "24"` or `auto-compaction-retention: "24h"`.
- If configured as `--auto-compaction-mode periodic --auto-compaction-retention "24h"`, the time duration value for `etcd --auto-compaction-retention` flag must be valid for [`time.ParseDuration`](https://golang.org/pkg/time/#ParseDuration) function in Go.
- e.g. `etcd --auto-compaction-mode=revision --auto-compaction-retention=1000` automatically `Compact` on `"latest revision" - 1000` every 5-minute (when latest revision is 30000, compact on revision 29000).
- e.g. `etcd --auto-compaction-mode=periodic --auto-compaction-retention=72h` automatically `Compact` with 72-hour retention windown, for every 7.2-hour.
- e.g. `etcd --auto-compaction-mode=periodic --auto-compaction-retention=30m` automatically `Compact` with 30-minute retention windown, for every 3-minute.
- Periodic compactor continues to record latest revisions for every 1/10 of given compaction period (e.g. 1-hour when `etcd --auto-compaction-mode=periodic --auto-compaction-retention=10h`).
- For every 1/10 of given compaction period, compactor uses the last revision that was fetched before compaction period, to discard historical data.
- The retention window of compaction period moves for every 1/10 of given compaction period.
- For instance, when hourly writes are 100 and `--auto-compaction-retention=10`, v3.1 compacts revision 1000, 2000, and 3000 for every 10-hour, while v3.2.x, v3.3.0, v3.3.1, and v3.3.2 compact revision 1000, 1100, and 1200 for every 1-hour. Furthermore, when writes per minute are 1000, v3.3.0, v3.3.1, and v3.3.2 with `--auto-compaction-mode=periodic --auto-compaction-retention=30m` compact revision 30000, 33000, and 36000, for every 3-minute with more finer granularity.
- Whether compaction succeeds or not, this process repeats for every 1/10 of given compaction period. If compaction succeeds, it just removes compacted revision from historical revision records.
- Add [`etcd --grpc-keepalive-min-time`, `etcd --grpc-keepalive-interval`, `etcd --grpc-keepalive-timeout`](https://github.com/etcd-io/etcd/pull/8535) flags to configure server-side keepalive policies.
- Serve [`/health` endpoint as unhealthy](https://github.com/etcd-io/etcd/pull/8272) when [alarm (e.g. `NOSPACE`) is raised or there's no leader](https://github.com/etcd-io/etcd/issues/8207).
- Define [`etcdhttp.Health`](https://godoc.org/github.com/coreos/etcd/etcdserver/api/etcdhttp#Health) struct with JSON encoder.
- Note that `"health"` field is [`string` type, not `bool`](https://github.com/etcd-io/etcd/pull/9143).
- e.g. `{"health":"false"}`, `{"health":"true"}`
- [Remove `"errors"` field](https://github.com/etcd-io/etcd/pull/9162) since `v3.3.0-rc.3` (did exist only in `v3.3.0-rc.0`, `v3.3.0-rc.1`, `v3.3.0-rc.2`).
- Move [logging setup to embed package](https://github.com/etcd-io/etcd/pull/8810)
- Disable gRPC server info-level logs by default (can be enabled with `etcd --debug` flag).
- Use [monotonic time in Go 1.9](https://github.com/etcd-io/etcd/pull/8507) for `lease` package.
- Warn on [empty hosts in advertise URLs](https://github.com/etcd-io/etcd/pull/8384).
- Address [advertise client URLs accepts empty hosts](https://github.com/etcd-io/etcd/issues/8379).
- etcd v3.4 will exit on this error.
- e.g. `etcd --advertise-client-urls=http://:2379`.
- Warn on [shadowed environment variables](https://github.com/etcd-io/etcd/pull/8385).
- Address [error on shadowed environment variables](https://github.com/etcd-io/etcd/issues/8380).
- etcd v3.4 will exit on this error.
### API
- Support [ranges in transaction comparisons](https://github.com/etcd-io/etcd/pull/8025) for [disconnected linearized reads](https://github.com/etcd-io/etcd/issues/7924).
- Add [nested transactions](https://github.com/etcd-io/etcd/pull/8102) to extend [proxy use cases](https://github.com/etcd-io/etcd/issues/7857).
- Add [lease comparison target in transaction](https://github.com/etcd-io/etcd/pull/8324).
- Add [lease list](https://github.com/etcd-io/etcd/pull/8358).
- Add [hash by revision](https://github.com/etcd-io/etcd/pull/8263) for [better corruption checking against boltdb](https://github.com/etcd-io/etcd/issues/8016).
### client v3
- Add [health balancer](https://github.com/etcd-io/etcd/pull/8545) to fix [watch API hangs](https://github.com/etcd-io/etcd/issues/7247), improve [endpoint switch under network faults](https://github.com/etcd-io/etcd/issues/7941).
- [Refactor balancer](https://github.com/etcd-io/etcd/pull/8840) and add [client-side keepalive pings](https://github.com/etcd-io/etcd/pull/8199) to handle [network partitions](https://github.com/etcd-io/etcd/issues/8711).
- Add [`MaxCallSendMsgSize` and `MaxCallRecvMsgSize`](https://github.com/etcd-io/etcd/pull/9047) fields to [`clientv3.Config`](https://godoc.org/github.com/coreos/etcd/clientv3#Config).
- Fix [exceeded response size limit error in client-side](https://github.com/etcd-io/etcd/issues/9043).
- Address [kubernetes#51099](https://github.com/kubernetes/kubernetes/issues/51099).
- In previous versions(v3.2.10, v3.2.11), client response size was limited to only 4 MiB.
- `MaxCallSendMsgSize` default value is 2 MiB, if not configured.
- `MaxCallRecvMsgSize` default value is `math.MaxInt32`, if not configured.
- Accept [`Compare_LEASE`](https://github.com/etcd-io/etcd/pull/8324) in [`clientv3.Compare`](https://godoc.org/github.com/coreos/etcd/clientv3#Compare).
- Add [`LeaseValue` helper](https://github.com/etcd-io/etcd/pull/8488) to `Cmp` `LeaseID` values in `Txn`.
- Add [`MoveLeader`](https://github.com/etcd-io/etcd/pull/8153) to `Maintenance`.
- Add [`HashKV`](https://github.com/etcd-io/etcd/pull/8351) to `Maintenance`.
- Add [`Leases`](https://github.com/etcd-io/etcd/pull/8358) to `Lease`.
- Add [`clientv3/ordering`](https://github.com/etcd-io/etcd/pull/8092) for enforce [ordering in serialized requests](https://github.com/etcd-io/etcd/issues/7623).
- Fix ["put at-most-once" violation](https://github.com/etcd-io/etcd/pull/8335).
- Fix [`WatchResponse.Canceled`](https://github.com/etcd-io/etcd/pull/8283) on [compacted watch request](https://github.com/etcd-io/etcd/issues/8231).
- Fix [`concurrency/stm` `Put` with serializable snapshot](https://github.com/etcd-io/etcd/pull/8439).
- Use store revision from first fetch to resolve write conflicts instead of modified revision.
### etcdctl v3
- Add [`etcdctl --discovery-srv`](https://github.com/etcd-io/etcd/pull/8462) flag.
- Add [`etcdctl --keepalive-time`, `--keepalive-timeout`](https://github.com/etcd-io/etcd/pull/8663) flags.
- Add [`etcdctl lease list`](https://github.com/etcd-io/etcd/pull/8358) command.
- Add [`etcdctl lease keep-alive --once`](https://github.com/etcd-io/etcd/pull/8775) flag.
- Make [`lease timetolive LEASE_ID`](https://github.com/etcd-io/etcd/issues/9028) on expired lease print [`lease LEASE_ID already expired`](https://github.com/etcd-io/etcd/pull/9047).
- <=3.2 prints `lease LEASE_ID granted with TTL(0s), remaining(-1s)`.
- Add [`etcdctl snapshot restore --wal-dir`](https://github.com/etcd-io/etcd/pull/9124) flag.
- Add [`etcdctl defrag --data-dir`](https://github.com/etcd-io/etcd/pull/8367) flag.
- Add [`etcdctl move-leader`](https://github.com/etcd-io/etcd/pull/8153) command.
- Add [`etcdctl endpoint hashkv`](https://github.com/etcd-io/etcd/pull/8351) command.
- Add [`etcdctl endpoint --cluster`](https://github.com/etcd-io/etcd/pull/8143) flag, equivalent to [v2 `etcdctl cluster-health`](https://github.com/etcd-io/etcd/issues/8117).
- Make `etcdctl endpoint health` command terminate with [non-zero exit code on unhealthy status](https://github.com/etcd-io/etcd/pull/8342).
- Add [`etcdctl lock --ttl`](https://github.com/etcd-io/etcd/pull/8370) flag.
- Support [`etcdctl watch [key] [range_end] -- [exec-command…]`](https://github.com/etcd-io/etcd/pull/8919), equivalent to [v2 `etcdctl exec-watch`](https://github.com/etcd-io/etcd/issues/8814).
- Make `etcdctl watch -- [exec-command]` set environmental variables [`ETCD_WATCH_REVISION`, `ETCD_WATCH_EVENT_TYPE`, `ETCD_WATCH_KEY`, `ETCD_WATCH_VALUE`](https://github.com/etcd-io/etcd/pull/9142) for each event.
- Support [`etcdctl watch` with environmental variables `ETCDCTL_WATCH_KEY` and `ETCDCTL_WATCH_RANGE_END`](https://github.com/etcd-io/etcd/pull/9142).
- Enable [`clientv3.WithRequireLeader(context.Context)` for `watch`](https://github.com/etcd-io/etcd/pull/8672) command.
- Print [`"del"` instead of `"delete"`](https://github.com/etcd-io/etcd/pull/8297) in `txn` interactive mode.
- Print [`ETCD_INITIAL_ADVERTISE_PEER_URLS` in `member add`](https://github.com/etcd-io/etcd/pull/8332).
- Fix [`etcdctl snapshot status` to not modify snapshot file](https://github.com/etcd-io/etcd/pull/8815).
- For example, start etcd `v3.3.10`
- Write some data
- Use etcdctl `v3.3.10` to save snapshot
- Somehow, upgrading Kubernetes fails, thus rolling back to previous version etcd `v3.2.24`
- Run etcdctl `v3.2.24` `snapshot status` against the snapshot file saved from `v3.3.10` server
- Run etcdctl `v3.2.24` `snapshot restore` fails with `"expected sha256 [12..."`
### etcdctl v3
- Handle [empty key permission](https://github.com/etcd-io/etcd/pull/8514) in `etcdctl`.
### etcdctl v2
- Add [`etcdctl backup --with-v3`](https://github.com/etcd-io/etcd/pull/8479) flag.
### gRPC Proxy
- Add [`grpc-proxy start --experimental-leasing-prefix`](https://github.com/etcd-io/etcd/pull/8341) flag.
- For disconnected linearized reads.
- Based on [V system leasing](https://github.com/etcd-io/etcd/issues/6065).
- See ["Disconnected consistent reads with etcd" blog post](https://coreos.com/blog/coreos-labs-disconnected-consistent-reads-with-etcd).
- Add [`grpc-proxy start --experimental-serializable-ordering`](https://github.com/etcd-io/etcd/pull/8315) flag.
- To ensure serializable reads have monotonically increasing store revisions across endpoints.
- Add [`grpc-proxy start --metrics-addr`](https://github.com/etcd-io/etcd/pull/8242) flag for an additional `/metrics` endpoint.
- Set `--metrics-addr=http://[HOST]:9379` to serve `/metrics` in insecure port 9379.
- Serve [`/health` endpoint in grpc-proxy](https://github.com/etcd-io/etcd/pull/8322).
- Add [`grpc-proxy start --debug`](https://github.com/etcd-io/etcd/pull/8994) flag.
- Add [`grpc-proxy start --max-send-bytes`](https://github.com/etcd-io/etcd/pull/9250) flag to [configure maximum client request size](https://github.com/etcd-io/etcd/issues/7923).
- Add [`grpc-proxy start --max-recv-bytes`](https://github.com/etcd-io/etcd/pull/9250) flag to [configure maximum client request size](https://github.com/etcd-io/etcd/issues/7923).
- Fix [Snapshot API error handling](https://github.com/etcd-io/etcd/commit/dbd16d52fbf81e5fd806d21ff5e9148d5bf203ab).
- Fix [KV API `PrevKv` flag handling](https://github.com/etcd-io/etcd/pull/8366).
- Fix [KV API `KeysOnly` flag handling](https://github.com/etcd-io/etcd/pull/8552).
### gRPC gateway
- Replace [gRPC gateway](https://github.com/grpc-ecosystem/grpc-gateway) endpoint `/v3alpha` with [`/v3beta`](https://github.com/etcd-io/etcd/pull/8880).
- To deprecate [`/v3alpha`](https://github.com/etcd-io/etcd/issues/8125) in v3.4.
- In v3.3, `curl -L http://localhost:2379/v3alpha/kv/put -X POST -d '{"key": "Zm9v", "value": "YmFy"}'` still works as a fallback to `curl -L http://localhost:2379/v3beta/kv/put -X POST -d '{"key": "Zm9v", "value": "YmFy"}'`, but `curl -L http://localhost:2379/v3alpha/kv/put -X POST -d '{"key": "Zm9v", "value": "YmFy"}'` won't work in v3.4. Use `curl -L http://localhost:2379/v3beta/kv/put -X POST -d '{"key": "Zm9v", "value": "YmFy"}'` instead.
- Support ["authorization" token](https://github.com/etcd-io/etcd/pull/7999).
- Support [websocket for bi-directional streams](https://github.com/etcd-io/etcd/pull/8257).
- Fix [`Watch` API with gRPC gateway](https://github.com/etcd-io/etcd/issues/8237).
- Upgrade gRPC gateway to [v1.3.0](https://github.com/etcd-io/etcd/issues/8838).
### etcd server
- Fix [backend database in-memory index corruption](https://github.com/etcd-io/etcd/pull/8127) issue on restore (only 3.2.0 is affected).
- Fix [watch restore from snapshot](https://github.com/etcd-io/etcd/pull/8427).
- Fix [`mvcc/backend.defragdb` nil-pointer dereference on create bucket failure](https://github.com/etcd-io/etcd/pull/9119).
- Fix [server crash](https://github.com/etcd-io/etcd/pull/8010) on [invalid transaction request from gRPC gateway](https://github.com/etcd-io/etcd/issues/7889).
- Prevent [server panic from member update/add](https://github.com/etcd-io/etcd/pull/9174) with [wrong scheme URLs](https://github.com/etcd-io/etcd/issues/9173).
- Make [peer dial timeout longer](https://github.com/etcd-io/etcd/pull/8599).
- See [coreos/etcd-operator#1300](https://github.com/etcd-io/etcd-operator/issues/1300) for more detail.
- Make server [wait up to request time-out](https://github.com/etcd-io/etcd/pull/8267) with [pending RPCs](https://github.com/etcd-io/etcd/issues/8224).
- Fix [`grpc.Server` panic on `GracefulStop`](https://github.com/etcd-io/etcd/pull/8987) with [TLS-enabled server](https://github.com/etcd-io/etcd/issues/8916).
- Fix ["multiple peer URLs cannot start" issue](https://github.com/etcd-io/etcd/issues/8383).
- Fix server-side auth so [concurrent auth operations do not return old revision error](https://github.com/etcd-io/etcd/pull/8442).
- Handle [WAL renaming failure on Windows](https://github.com/etcd-io/etcd/pull/8286).
- Upgrade [`coreos/go-systemd`](https://github.com/coreos/go-systemd/releases) to `v15` (see https://github.com/coreos/go-systemd/releases/tag/v15).
- [Put back `/v2/machines`](https://github.com/etcd-io/etcd/pull/8062) endpoint for python-etcd wrapper.
### client v2
- [Fail-over v2 client](https://github.com/etcd-io/etcd/pull/8519) to next endpoint on [oneshot failure](https://github.com/etcd-io/etcd/issues/8515).
### Package `raft`
- Add [non-voting member](https://github.com/etcd-io/etcd/pull/8751).
- To implement [Raft thesis 4.2.1 Catching up new servers](https://github.com/etcd-io/etcd/issues/8568).
- `Learner` node does not vote or promote itself.
### Other
- Support previous two minor versions (see our [new release policy](https://github.com/etcd-io/etcd/pull/8805)).
- `v3.3.x` is the last release cycle that supports `ACI`.
- [AppC was officially suspended](https://github.com/appc/spec#-disclaimer-), as of late 2016.
- [`acbuild`](https://github.com/containers/build#this-project-is-currently-unmaintained) is not maintained anymore.
- `*.aci` files won't be available from etcd v3.4 release.
- Add container registry [`gcr.io/etcd-development/etcd`](https://gcr.io/etcd-development/etcd).
- [quay.io/coreos/etcd](https://quay.io/coreos/etcd) is still supported as secondary.
### Go
- Require [*Go 1.9+*](https://github.com/etcd-io/etcd/issues/6174).
- Compile with [*Go 1.9.3*](https://golang.org/doc/devel/release.html#go1.9).
- Deprecate [`golang.org/x/net/context`](https://github.com/etcd-io/etcd/pull/8511).
---
================================================
FILE: CHANGELOG/CHANGELOG-3.4.md
================================================
Previous change logs can be found at [CHANGELOG-3.3](https://github.com/etcd-io/etcd/blob/main/CHANGELOG/CHANGELOG-3.3.md).
---
## v3.4.42 (TBC)
### etcd server
- Fix [Race between read index and leader change](https://github.com/etcd-io/etcd/pull/21385)
- Fix [Stale reads caused by process pausing](https://github.com/etcd-io/etcd/pull/21423)
### Dependencies
- Compile binaries using [go 1.25.7](https://github.com/etcd-io/etcd/pull/21406)
- [Bump golang.org/x/net to v0.51.0 to resolve GO-2026-4559](https://github.com/etcd-io/etcd/pull/21444)
---
## v3.4.41 (2026-02-13)
### Package `clientv3`
- [Remove the use of grpc-go's Metadata field](https://github.com/etcd-io/etcd/pull/21243)
### Dependencies
- Compile binaries using [go 1.24.13](https://github.com/etcd-io/etcd/pull/21266). This addresses [CVE-2025-61726](https://github.com/advisories/GHSA-gm9r-q53w-2gh4), [CVE-2025-61731](https://github.com/advisories/GHSA-xvqr-69v8-f3gv), and [CVE-2025-61732](https://github.com/advisories/GHSA-8jvr-vh7g-f8gx).
---
## v3.4.40 (2025-12-17)
### etcd server
- [Print token fingerprint instead of the original tokens in log messages](https://github.com/etcd-io/etcd/pull/20943)
### Dependencies
- [Scripts/build-binary.sh: use `buildvcs=false` to avoid having a pseudo-version reported by `go version`](https://github.com/etcd-io/etcd/pull/20950)
- Compile binaries using [go 1.24.11](https://github.com/etcd-io/etcd/pull/21000).
- [Use buildvcs=false in release script](https://github.com/etcd-io/etcd/pull/21028)
- Bump [golang.org/x/crypto to 0.45.0 to address CVE-2025-47914, and CVE-2025-58181](https://github.com/etcd-io/etcd/pull/21022).
---
## v3.4.39 (2025-11-11)
### Dependencies
- [Compile binaries with `buildvcs=false` to avoid having a pseudo-version reported by `go version`](https://github.com/etcd-io/etcd/pull/20847).
- Compile binaries using [go 1.24.10](https://github.com/etcd-io/etcd/pull/20903).
---
## v3.4.38 (2025-10-21)
### etcd server
- Fix [mvcc: avoid double decrement of watcher gauge on close/cancel race](https://github.com/etcd-io/etcd/pull/20065)
- Fix [Watch on future revision returns old events or notifications](https://github.com/etcd-io/etcd/pull/20291)
- Improve [help message for --quota-backend-bytes](https://github.com/etcd-io/etcd/pull/20379)
- Fix [potential data corruption when applySnapshot and defragment happen concurrently](https://github.com/etcd-io/etcd/pull/20659)
- [Reject watch request with -1 revision to prevent invalid resync behavior on uncompacted etcd](https://github.com/etcd-io/etcd/pull/20711)
- Fix [etcd may return success for leaseRenew request even when the lease is revoked](https://github.com/etcd-io/etcd/pull/20813)
### Dependencies
- Compile binaries using [go 1.24.9](https://github.com/etcd-io/etcd/pull/20807).
- [Bump bbolt to v1.3.12](https://github.com/etcd-io/etcd/pull/20515).
---
## v3.4.37 (2025-04-15)
### Dependencies
- Bump [golang.org/x/net to v0.36.0 to address CVE-2025-22870](https://github.com/etcd-io/etcd/pull/19529).
- Compile binaries using [go 1.23.8](https://github.com/etcd-io/etcd/pull/19726)
---
## v3.4.36 (2025-02-25)
### etcd server
- [Avoid deadlock in etcd.Close when stopping during bootstrapping](https://github.com/etcd-io/etcd/pull/19166)
- Fix [missing delete event on watch opened on same revision as compaction request](https://github.com/etcd-io/etcd/pull/19251)
### Package `clientv3`
- Fix [runtime panic that occurs when KeepAlive is called with a Context implemented by an uncomparable type](https://github.com/etcd-io/etcd/pull/18936)
### Dependencies
- Compile binaries using [go 1.23.6](https://github.com/etcd-io/etcd/pull/19429)
- Bump golang.org/x/crypto to v0.35.0 to address [CVE-2024-45337](https://github.com/etcd-io/etcd/pull/19197) and [CVE-2025-22869](https://github.com/etcd-io/etcd/pull/19477).
- Bump golang.org/x/net to v0.34.0 to address [CVE-2024-45338](https://github.com/etcd-io/etcd/pull/19197).
---
## v3.4.35 (2024-11-12)
### etcd server
- Fix [watchserver related goroutine leakage](https://github.com/etcd-io/etcd/pull/18785)
- Fix [panicking occurred due to improper error handling during defragmentation](https://github.com/etcd-io/etcd/pull/18843)
- Fix [close temp file(s) in case an error happens during defragmentation](https://github.com/etcd-io/etcd/pull/18855)
### Dependencies
- Compile binaries using [go 1.22.9](https://github.com/etcd-io/etcd/pull/18850).
---
## v3.4.34 (2024-09-11)
### etcd server
- Fix [performance regression issue caused by the `ensureLeadership` in lease renew](https://github.com/etcd-io/etcd/pull/18440).
- [Keep the tombstone during compaction if it happens to be the compaction revision](https://github.com/etcd-io/etcd/pull/18475)
### Package clientv3
- [Print gRPC metadata in guaranteed order using the official go fmt pkg](https://github.com/etcd-io/etcd/pull/18311).
### Dependencies
- Compile binaries using [go 1.22.7](https://github.com/etcd-io/etcd/pull/18549).
- Upgrade [bbolt to 1.3.11](https://github.com/etcd-io/etcd/pull/18488).
---
## v3.4.33 (2024-06-13)
### etcd grpc-proxy
- Fix [Memberlist results not updated when proxy node down](https://github.com/etcd-io/etcd/pull/17896).
### Dependencies
- Compile binaries using go [1.21.11](https://github.com/etcd-io/etcd/pull/18130).
- Upgrade [bbolt to 1.3.10](https://github.com/etcd-io/etcd/pull/17945).
---
## v3.4.32 (2024-04-25)
### etcd server
- Fix [LeaseTimeToLive returns error if leader changed](https://github.com/etcd-io/etcd/pull/17705).
- Fix [ignore raft messages if member id mismatch](https://github.com/etcd-io/etcd/pull/17814).
- Update [the compaction log when bootstrap](https://github.com/etcd-io/etcd/pull/17831).
- [Allow new server to join 3.5 cluster if `next-cluster-version-compatible=true`](https://github.com/etcd-io/etcd/pull/17665)
- [Allow updating the cluster version when downgrading from 3.5](https://github.com/etcd-io/etcd/pull/17821).
- Fix [Revision decreasing after panic during compaction](https://github.com/etcd-io/etcd/pull/17864)
### Package `clientv3`
- Add [requests retry when receiving ErrGPRCNotSupportedForLearner and endpoints > 1](https://github.com/etcd-io/etcd/pull/17692).
- Fix [initialization for epMu in client context](https://github.com/etcd-io/etcd/pull/17714).
### Dependencies
- Compile binaries using [go 1.21.9](https://github.com/etcd-io/etcd/pull/17709).
---
## v3.4.31 (2024-03-21)
### etcd server
- Add [mvcc: print backend database size and size in use in compaction logs](https://github.com/etcd-io/etcd/pull/17436).
- Fix leases wrongly revoked by the leader by [ignoring old leader's leases revoking request](https://github.com/etcd-io/etcd/pull/17465).
- Fix [no progress notification being sent for watch that doesn't get any events](https://github.com/etcd-io/etcd/pull/17567).
- Fix [watch event loss after compaction](https://github.com/etcd-io/etcd/pull/17610).
- Add `next-cluster-version-compatible` flag to [allow downgrade from 3.5](https://github.com/etcd-io/etcd/pull/17330).
### Package `clientv3`
- Add [client backoff and retry config options](https://github.com/etcd-io/etcd/pull/17369).
### Dependencies
- Upgrade [bbolt to 1.3.9](https://github.com/etcd-io/etcd/pull/17484).
- Compile binaries using [go 1.21.8](https://github.com/etcd-io/etcd/pull/17538).
- Upgrade [google.golang.org/protobuf to v1.33.0 to address CVE-2024-24786](https://github.com/etcd-io/etcd/pull/17554).
- Upgrade github.com/sirupsen/logrus to v1.9.3 to address [PRISMA-2023-0056](https://github.com/etcd-io/etcd/pull/17580).
### Others
- [Make CGO_ENABLED configurable](https://github.com/etcd-io/etcd/pull/17422).
---
## v3.4.30 (2024-01-31)
### etcd server
- Fix [nil pointer panicking due to using the wrong log library](https://github.com/etcd-io/etcd/pull/17270)
### Dependencies
- Compile binaries using go [1.20.13](https://github.com/etcd-io/etcd/pull/17276).
- Upgrade [golang.org/x/crypto to v0.17+ to address CVE-2023-48795](https://github.com/etcd-io/etcd/pull/17347).
---
## v3.4.29 (2024-01-09)
### etcd server
- [Disable following HTTP redirects in peer communication](https://github.com/etcd-io/etcd/pull/17112)
- [Add livez/readyz HTTP endpoints](https://github.com/etcd-io/etcd/pull/17128)
- Fix [Check if be is nil to avoid panic when be is overriden with nil](https://github.com/etcd-io/etcd/pull/17154)
- Fix [Add missing experimental-enable-lease-checkpoint-persist flag in etcd help](https://github.com/etcd-io/etcd/pull/17189)
- Fix [Don't flock snapshot files](https://github.com/etcd-io/etcd/pull/17208)
### Dependencies
- Compile binaries using go [1.20.12](https://github.com/etcd-io/etcd/pull/17076).
---
## v3.4.28 (2023-11-23)
### etcd server
- Improve [Skip getting authInfo from incoming context when auth is disabled](https://github.com/etcd-io/etcd/pull/16240)
- Use [the default write scheduler](https://github.com/etcd-io/etcd/pull/16782) since golang.org/x/net@v0.11.0 started using round-robin scheduler.
- Add [cluster ID check during data corruption detection to prevent false alarm](https://github.com/etcd-io/etcd/issues/15548).
- Add [Learner support Snapshot RPC](https://github.com/etcd-io/etcd/pull/16990/).
### Package `clientv3`
- Fix [Reset auth token when failing to authenticate due to auth being disabled](https://github.com/etcd-io/etcd/pull/16240).
- [Simplify grpc dialer usage](https://github.com/etcd-io/etcd/issues/11519).
- [Replace balancer with upstream grpc solution](https://github.com/etcd-io/etcd/pull/16844).
- Fix [race condition when accessing cfg.Endpoints in dial()](https://github.com/etcd-io/etcd/pull/16857).
- Fix [invalid authority header issue in single endpoint scenario](https://github.com/etcd-io/etcd/pull/16988).
### Dependencies
- Compile binaries using [go 1.20.11](https://github.com/etcd-io/etcd/pull/16916).
- Upgrade [bbolt to 1.3.8](https://github.com/etcd-io/etcd/pull/16834).
- Upgrade gRPC to 1.58.3 in https://github.com/etcd-io/etcd/pull/16997 and https://github.com/etcd-io/etcd/pull/16999. Note that gRPC server will reject requests with connection header (refer to https://github.com/grpc/grpc-go/pull/4803).
---
## v3.4.27 (2023-07-11)
### etcd server
- Fix [corruption check may get a `ErrCompacted` error when server has just been compacted](https://github.com/etcd-io/etcd/pull/16047)
- Improve [Lease put performance for the case that auth is disabled or the user is admin](https://github.com/etcd-io/etcd/pull/16020)
- Fix [embed: nil pointer dereference when stopServer](https://github.com/etcd-io/etcd/pull/16195)
### etcdctl v3
- Add [optional --bump-revision and --mark-compacted flag to etcdctl snapshot restore operation](https://github.com/etcd-io/etcd/pull/16193).
### Dependencies
- Compile binaries using [go 1.19.10](https://github.com/etcd-io/etcd/pull/16038).
---
## v3.4.26 (2023-05-12)
### etcd server
- Fix [LeaseTimeToLive API may return keys to clients which have no read permission on the keys](https://github.com/etcd-io/etcd/pull/15814).
### Dependencies
- Compile binaries using [go 1.19.9](https://github.com/etcd-io/etcd/pull/15823)
---
## v3.4.25 (2023-04-14)
### etcd server
- Add [`etcd --tls-min-version --tls-max-version`](https://github.com/etcd-io/etcd/pull/15486) to enable support for TLS 1.3.
- Add [`etcd --listen-client-http-urls`](https://github.com/etcd-io/etcd/pull/15620) flag to support separating http server from grpc one, thus giving full immunity to [watch stream starvation under high read load](https://github.com/etcd-io/etcd/issues/15402).
- Change [http2 frame scheduler to random algorithm](https://github.com/etcd-io/etcd/pull/15478)
- Fix [server/embed: fix data race when starting both secure & insecure gRPC servers on the same address](https://github.com/etcd-io/etcd/pull/15518)
- Fix [server/auth: disallow creating empty permission ranges](https://github.com/etcd-io/etcd/pull/15621)
- Fix [wsproxy did not print log in JSON format](https://github.com/etcd-io/etcd/pull/15662).
- Fix [CVE-2021-28235](https://nvd.nist.gov/vuln/detail/CVE-2021-28235) by [clearing password after authenticating the user](https://github.com/etcd-io/etcd/pull/15655).
- Fix [etcdserver may panic when parsing a JWT token without username or revision](https://github.com/etcd-io/etcd/pull/15677).
- Fix [Watch response traveling back in time when reconnecting member downloads snapshot from the leader](https://github.com/etcd-io/etcd/pull/15520).
- Fix [Requested watcher progress notifications are not synchronised with stream](https://github.com/etcd-io/etcd/pull/15697).
### Package `clientv3`
- Reverted the fix to [auth invalid token and old revision errors in watch](https://github.com/etcd-io/etcd/pull/15542).
### Dependencies
- Recommend [Go 1.19+](https://github.com/etcd-io/etcd/pull/15337).
- Compile binaries using [Go 1.19.8](https://github.com/etcd-io/etcd/pull/15652).
- Upgrade [golang.org/x/net to v0.7.0](https://github.com/etcd-io/etcd/pull/15333).
### Docker image
- Fix [etcd docker images all tagged with amd64 architecture](https://github.com/etcd-io/etcd/pull/15681)
---
## v3.4.24 (2023-02-16)
### etcd server
- Fix [etcdserver might promote a non-started learner](https://github.com/etcd-io/etcd/pull/15097).
- Improve [mvcc: reduce count-only range overhead](https://github.com/etcd-io/etcd/pull/15099)
- Improve [mvcc: push down RangeOptions.limit argv into index tree to reduce memory overhead](https://github.com/etcd-io/etcd/pull/15137)
- Improve [server: set multiple concurrentReadTx instances share one txReadBuffer](https://github.com/etcd-io/etcd/pull/15195)
- Fix [aligning zap log timestamp resolution to microseconds](https://github.com/etcd-io/etcd/pull/15241). Etcd now uses zap timestamp format: `2006-01-02T15:04:05.999999Z0700` (microsecond instead of milliseconds precision).
- Fix [consistently format IPv6 addresses for comparison](https://github.com/etcd-io/etcd/pull/15188)
### Package `clientv3`
- Fix [etcd might send duplicated events to watch clients](https://github.com/etcd-io/etcd/pull/15275).
### Dependencies
- Upgrade [bbolt to v1.3.7](https://github.com/etcd-io/etcd/pull/15223).
- Upgrade [github.com/grpc-ecosystem/grpc-gateway](https://github.com/grpc-ecosystem/grpc-gateway/releases) from [v1.9.5](https://github.com/grpc-ecosystem/grpc-gateway/releases/tag/v1.9.5) to [v1.11.0](https://github.com/grpc-ecosystem/grpc-gateway/releases/tag/v1.11.0).
### Docker image
- Updated [base image from base-debian11 to static-debian11 and removed dependency on busybox](https://github.com/etcd-io/etcd/pull/15038).
---
## v3.4.23 (2022-12-21)
### etcd server
- Fix [Remove memberID from data corrupt alarm](https://github.com/etcd-io/etcd/pull/14853).
- Fix [nil pointer panic for readonly txn due to nil response](https://github.com/etcd-io/etcd/pull/14900).
- Bumped [some dependencies](https://github.com/etcd-io/etcd/pull/15019) to address some HIGH Vulnerabilities.
### Package `clientv3`
- Fix [Refreshing token on CommonName based authentication causes segmentation violation in client](https://github.com/etcd-io/etcd/pull/14792).
### Dependencies
- Recommend [Go 1.17+](https://github.com/etcd-io/etcd/pull/15019).
- Compile binaries using [Go 1.17.13](https://github.com/etcd-io/etcd/pull/15019).
### Docker image
- Use [distroless base image](https://github.com/etcd-io/etcd/pull/15017) to address critical Vulnerabilities.
---
## v3.4.22 (2022-11-02)
### etcd server
- Fix [memberID equals zero in corruption alarm](https://github.com/etcd-io/etcd/pull/14530)
- Fix [auth invalid token and old revision errors in watch](https://github.com/etcd-io/etcd/pull/14548)
- Fix [avoid closing a watch with ID 0 incorrectly](https://github.com/etcd-io/etcd/pull/14562)
- Fix [auth: fix data consistency issue caused by recovery from snapshot](https://github.com/etcd-io/etcd/pull/14649)
### Package `netutil`
- Fix [netutil: add url comparison without resolver to URLStringsEqual](https://github.com/etcd-io/etcd/pull/14577)
### Package `clientv3`
- Fix [Add backoff before retry when watch stream returns unavailable](https://github.com/etcd-io/etcd/pull/14581).
### etcd grpc-proxy
- Add [`etcd grpc-proxy start --listen-cipher-suites`](https://github.com/etcd-io/etcd/pull/14601) flag to support adding configurable cipher list.
---
## v3.4.21 (2022-09-15)
### etcd server
- Fix [Durability API guarantee broken in single node cluster](https://github.com/etcd-io/etcd/pull/14423)
- Fix [Panic due to nil log object](https://github.com/etcd-io/etcd/pull/14420)
- Fix [authentication data not loaded on member startup](https://github.com/etcd-io/etcd/pull/14410)
### etcdctl v3
- Fix [etcdctl move-leader may fail for multiple endpoints](https://github.com/etcd-io/etcd/pull/14441)
---
## v3.4.20 (2022-08-06)
### Package `clientv3`
- Fix [filter learners members during autosync](https://github.com/etcd-io/etcd/pull/14236).
### etcd server
- Add [`etcd --max-concurrent-streams`](https://github.com/etcd-io/etcd/pull/14251) flag to configure the max concurrent streams each client can open at a time, and defaults to math.MaxUint32.
- Add [`etcd --experimental-enable-lease-checkpoint-persist`](https://github.com/etcd-io/etcd/pull/14253) flag to enable checkpoint persisting.
- Fix [Lease checkpoints don't prevent to reset ttl on leader change](https://github.com/etcd-io/etcd/pull/14253), requires enabling checkpoint persisting.
- Fix [Protect rangePermCache with a RW lock correctly](https://github.com/etcd-io/etcd/pull/14230)
- Fix [raft: postpone MsgReadIndex until first commit in the term](https://github.com/etcd-io/etcd/pull/14258)
- Fix [etcdserver: resend ReadIndex request on empty apply request](https://github.com/etcd-io/etcd/pull/14269)
- Fix [remove temp files in snap dir when etcdserver starting](https://github.com/etcd-io/etcd/pull/14246)
- Fix [Etcdserver is still in progress of processing LeaseGrantRequest when it receives a LeaseKeepAliveRequest on the same leaseID](https://github.com/etcd-io/etcd/pull/14177)
- Fix [Grant lease with negative ID can possibly cause db out of sync](https://github.com/etcd-io/etcd/pull/14239)
- Fix [Allow non mutating requests pass through quotaKVServer when NOSPACE](https://github.com/etcd-io/etcd/pull/14254)
---
## v3.4.19 (2022-07-12)
See [code changes](https://github.com/etcd-io/etcd/compare/v3.4.18...v3.4.19) and [v3.4 upgrade guide](https://etcd.io/docs/latest/upgrades/upgrade_3_4/) for any breaking changes.
### etcd server
- Fix [exclude the same alarm type activated by multiple peers](https://github.com/etcd-io/etcd/pull/13475).
- Fix [Defrag unsets backend options](https://github.com/etcd-io/etcd/pull/13713).
- Fix [lease leak issue due to tokenProvider isn't enabled when restoring auth store from a snapshot](https://github.com/etcd-io/etcd/pull/13206).
- Fix [the race condition between goroutine and channel on the same leases to be revoked](https://github.com/etcd-io/etcd/pull/14150).
- Fix [lessor may continue to schedule checkpoint after stepping down leader role](https://github.com/etcd-io/etcd/pull/14150).
### Package `clientv3`
- Fix [a bug of not refreshing expired tokens](https://github.com/etcd-io/etcd/pull/13999).
### Dependency
- Upgrade [go.etcd.io/bbolt](https://github.com/etcd-io/bbolt/releases) from [v1.3.3](https://github.com/etcd-io/bbolt/releases/tag/v1.3.3) to [v1.3.6](https://github.com/etcd-io/bbolt/releases/tag/v1.3.6).
### Security
- Upgrade [golang.org/x/crypto](https://github.com/etcd-io/etcd/pull/14179) to v0.0.0-20220411220226-7b82a4e95df4 to address [CVE-2022-27191 ](https://github.com/advisories/GHSA-8c26-wmh5-6g9v).
- Upgrade [gopkg.in/yaml.v2](https://github.com/etcd-io/etcd/pull/14192) to v2.4.0 to address [CVE-2019-11254](https://github.com/advisories/GHSA-wxc4-f4m6-wwqv).
### Go
- Require [Go 1.16+](https://github.com/etcd-io/etcd/pull/14136).
- Compile with [Go 1.16+](https://go.dev/doc/devel/release#go1.16).
- etcd uses [go modules](https://github.com/etcd-io/etcd/pull/14136) (instead of vendor dir) to track dependencies.
---
## v3.4.18 (2021-10-15)
See [code changes](https://github.com/etcd-io/etcd/compare/v3.4.17...v3.4.18) and [v3.4 upgrade guide](https://etcd.io/docs/latest/upgrades/upgrade_3_4/) for any breaking changes.
### Metrics, Monitoring
See [List of metrics](https://etcd.io/docs/latest/metrics/) for all metrics per release.
- Add [`etcd_disk_defrag_inflight`](https://github.com/etcd-io/etcd/pull/13397).
### Other
- Updated [base image](https://github.com/etcd-io/etcd/pull/13386) from `debian:buster-v1.4.0` to `debian:bullseye-20210927` to fix the following critical CVEs:
- [CVE-2021-3711](https://nvd.nist.gov/vuln/detail/CVE-2021-3711): miscalculation of a buffer size in openssl's SM2 decryption
- [CVE-2021-35942](https://nvd.nist.gov/vuln/detail/CVE-2021-35942): integer overflow flaw in glibc
- [CVE-2019-9893](https://nvd.nist.gov/vuln/detail/CVE-2019-9893): incorrect syscall argument generation in libseccomp
- [CVE-2021-36159](https://nvd.nist.gov/vuln/detail/CVE-2021-36159): libfetch in apk-tools mishandles numeric strings in FTP and HTTP protocols to allow out of bound reads.
---
## v3.4.17 (2021-10-03)
See [code changes](https://github.com/etcd-io/etcd/compare/v3.4.16...v3.4.17) and [v3.4 upgrade guide](https://etcd.io/docs/latest/upgrades/upgrade_3_4/) for any breaking changes.
### `etcdctl`
- Fix [etcdctl check datascale command](https://github.com/etcd-io/etcd/pull/11896) to work with https endpoints.
### gRPC gateway
- Add [`MaxCallRecvMsgSize`](https://github.com/etcd-io/etcd/pull/13077) support for http client.
### Dependency
- Replace [`github.com/dgrijalva/jwt-go with github.com/golang-jwt/jwt'](https://github.com/etcd-io/etcd/pull/13378).
### Go
- Compile with [*Go 1.12.17*](https://golang.org/doc/devel/release.html#go1.12).
---
## v3.4.16 (2021-05-11)
See [code changes](https://github.com/etcd-io/etcd/compare/v3.4.15...v3.4.16) and [v3.4 upgrade guide](https://etcd.io/docs/latest/upgrades/upgrade_3_4/) for any breaking changes.
### etcd server
- Add [`--experimental-warning-apply-duration`](https://github.com/etcd-io/etcd/pull/12448) flag which allows apply duration threshold to be configurable.
- Fix [`--unsafe-no-fsync`](https://github.com/etcd-io/etcd/pull/12751) to still write-out data avoiding corruption (most of the time).
- Reduce [around 30% memory allocation by logging range response size without marshal](https://github.com/etcd-io/etcd/pull/12871).
- Add [exclude alarms from health check conditionally](https://github.com/etcd-io/etcd/pull/12880).
### Metrics
- Fix [incorrect metrics generated when clients cancel watches](https://github.com/etcd-io/etcd/pull/12803) back-ported from (https://github.com/etcd-io/etcd/pull/12196).
### Go
- Compile with [*Go 1.12.17*](https://golang.org/doc/devel/release.html#go1.12).
---
## [v3.4.15](https://github.com/etcd-io/etcd/releases/tag/v3.4.15) (2021-02-26)
See [code changes](https://github.com/etcd-io/etcd/compare/v3.4.14...v3.4.15) and [v3.4 upgrade guide](https://etcd.io/docs/latest/upgrades/upgrade_3_4/) for any breaking changes.
### etcd server
- Log [successful etcd server-side health check in debug level](https://github.com/etcd-io/etcd/pull/12677).
- Fix [64 KB websocket notification message limit](https://github.com/etcd-io/etcd/pull/12402).
### Package `fileutil`
- Fix [`F_OFD_` constants](https://github.com/etcd-io/etcd/pull/12444).
### Dependency
- Bump up [`gorilla/websocket` to v1.4.2](https://github.com/etcd-io/etcd/pull/12645).
### Go
- Compile with [*Go 1.12.17*](https://golang.org/doc/devel/release.html#go1.12).
---
## [v3.4.14](https://github.com/etcd-io/etcd/releases/tag/v3.4.14) (2020-11-25)
See [code changes](https://github.com/etcd-io/etcd/compare/v3.4.13...v3.4.14) and [v3.4 upgrade guide](https://etcd.io/docs/latest/upgrades/upgrade_3_4/) for any breaking changes.
### Package `clientv3`
- Fix [auth token invalid after watch reconnects](https://github.com/etcd-io/etcd/pull/12264). Get AuthToken automatically when clientConn is ready.
### etcd server
- [Fix server panic](https://github.com/etcd-io/etcd/pull/12288) when force-new-cluster flag is enabled in a cluster which had learner node.
### Package `netutil`
- Remove [`netutil.DropPort/RecoverPort/SetLatency/RemoveLatency`](https://github.com/etcd-io/etcd/pull/12491).
- These are not used anymore. They were only used for older versions of functional testing.
- Removed to adhere to best security practices, minimize arbitrary shell invocation.
### `tools/etcd-dump-metrics`
- Implement [input validation to prevent arbitrary shell invocation](https://github.com/etcd-io/etcd/pull/12491).
### Go
- Compile with [*Go 1.12.17*](https://golang.org/doc/devel/release.html#go1.12).
---
## [v3.4.13](https://github.com/etcd-io/etcd/releases/tag/v3.4.13) (2020-8-24)
See [code changes](https://github.com/etcd-io/etcd/compare/v3.4.12...v3.4.13) and [v3.4 upgrade guide](https://etcd.io/docs/latest/upgrades/upgrade_3_4/) for any breaking changes.
### Security
- A [log warning](https://github.com/etcd-io/etcd/pull/12242) is added when etcd use any existing directory that has a permission different than 700 on Linux and 777 on Windows.
### Go
- Compile with [*Go 1.12.17*](https://golang.org/doc/devel/release.html#go1.12).
---
## [v3.4.12](https://github.com/etcd-io/etcd/releases/tag/v3.4.12) (2020-08-19)
See [code changes](https://github.com/etcd-io/etcd/compare/v3.4.11...v3.4.12) and [v3.4 upgrade guide](https://etcd.io/docs/latest/upgrades/upgrade_3_4/) for any breaking changes.
### etcd server
- Fix [server panic in slow writes warnings](https://github.com/etcd-io/etcd/issues/12197).
- Fixed via [PR#12238](https://github.com/etcd-io/etcd/pull/12238).
### Go
- Compile with [*Go 1.12.17*](https://golang.org/doc/devel/release.html#go1.12).
---
## [v3.4.11](https://github.com/etcd-io/etcd/releases/tag/v3.4.11) (2020-08-18)
See [code changes](https://github.com/etcd-io/etcd/compare/v3.4.10...v3.4.11) and [v3.4 upgrade guide](https://etcd.io/docs/latest/upgrades/upgrade_3_4/) for any breaking changes.
### etcd server
- Improve [`runtime.FDUsage` call pattern to reduce objects malloc of Memory Usage and CPU Usage](https://github.com/etcd-io/etcd/pull/11986).
- Add [`etcd --experimental-watch-progress-notify-interval`](https://github.com/etcd-io/etcd/pull/12216) flag to make watch progress notify interval configurable.
### Package `clientv3`
- Remove [excessive watch cancel logging messages](https://github.com/etcd-io/etcd/pull/12187).
- See [kubernetes/kubernetes#93450](https://github.com/kubernetes/kubernetes/issues/93450).
### Package `runtime`
- Optimize [`runtime.FDUsage` by removing unnecessary sorting](https://github.com/etcd-io/etcd/pull/12214).
### Metrics, Monitoring
- Add [`os_fd_used` and `os_fd_limit` to monitor current OS file descriptors](https://github.com/etcd-io/etcd/pull/12214).
- Add [`etcd_disk_defrag_inflight`](https://github.com/etcd-io/etcd/pull/13397).
### Go
- Compile with [*Go 1.12.17*](https://golang.org/doc/devel/release.html#go1.12).
---
## [v3.4.10](https://github.com/etcd-io/etcd/releases/tag/v3.4.10) (2020-07-16)
See [code changes](https://github.com/etcd-io/etcd/compare/v3.4.9...v3.4.10) and [v3.4 upgrade guide](https://etcd.io/docs/latest/upgrades/upgrade_3_4/) for any breaking changes.
### Package `etcd server`
- Add [`--unsafe-no-fsync`](https://github.com/etcd-io/etcd/pull/11946) flag.
- Setting the flag disables all uses of fsync, which is unsafe and will cause data loss. This flag makes it possible to run an etcd node for testing and development without placing lots of load on the file system.
- Add [etcd --auth-token-ttl](https://github.com/etcd-io/etcd/pull/11980) flag to customize `simpleTokenTTL` settings.
- Improve [runtime.FDUsage objects malloc of Memory Usage and CPU Usage](https://github.com/etcd-io/etcd/pull/11986).
- Improve [mvcc.watchResponse channel Memory Usage](https://github.com/etcd-io/etcd/pull/11987).
- Fix [`int64` convert panic in raft logger](https://github.com/etcd-io/etcd/pull/12106).
- Fix [kubernetes/kubernetes#91937](https://github.com/kubernetes/kubernetes/issues/91937).
### Breaking Changes
- Changed behavior on [existing dir permission](https://github.com/etcd-io/etcd/pull/11798).
- Previously, the permission was not checked on existing data directory and the directory used for automatically generating self-signed certificates for TLS connections with clients. Now a check is added to make sure those directories, if already exist, has a desired permission of 700 on Linux and 777 on Windows.
### Go
- Compile with [*Go 1.12.17*](https://golang.org/doc/devel/release.html#go1.12).
---
## [v3.4.9](https://github.com/etcd-io/etcd/releases/tag/v3.4.9) (2020-05-20)
See [code changes](https://github.com/etcd-io/etcd/compare/v3.4.8...v3.4.9) and [v3.4 upgrade guide](https://etcd.io/docs/latest/upgrades/upgrade_3_4/) for any breaking changes.
### Package `wal`
- Add [missing CRC checksum check in WAL validate method otherwise causes panic](https://github.com/etcd-io/etcd/pull/11924).
- See https://github.com/etcd-io/etcd/issues/11918.
### Go
- Compile with [*Go 1.12.17*](https://golang.org/doc/devel/release.html#go1.12).
---
## [v3.4.8](https://github.com/etcd-io/etcd/releases/tag/v3.4.8) (2020-05-18)
See [code changes](https://github.com/etcd-io/etcd/compare/v3.4.7...v3.4.8) and [v3.4 upgrade guide](https://etcd.io/docs/latest/upgrades/upgrade_3_4/) for any breaking changes.
### `etcdctl`
- Make sure [save snapshot downloads checksum for integrity checks](https://github.com/etcd-io/etcd/pull/11896).
### Package `clientv3`
- Make sure [save snapshot downloads checksum for integrity checks](https://github.com/etcd-io/etcd/pull/11896).
### etcd server
- Improve logging around snapshot send and receive.
- [Add log when etcdserver failed to apply command](https://github.com/etcd-io/etcd/pull/11670).
- [Fix deadlock bug in mvcc](https://github.com/etcd-io/etcd/pull/11817).
- Fix [inconsistency between WAL and server snapshot](https://github.com/etcd-io/etcd/pull/11888).
- Previously, server restore fails if it had crashed after persisting raft hard state but before saving snapshot.
- See https://github.com/etcd-io/etcd/issues/10219 for more.
### Package Auth
- [Fix a data corruption bug by saving consistent index](https://github.com/etcd-io/etcd/pull/11652).
### Metrics, Monitoring
- Add [`etcd_debugging_auth_revision`](https://github.com/etcd-io/etcd/commit/f14d2a087f7b0fd6f7980b95b5e0b945109c95f3).
### Go
- Compile with [*Go 1.12.17*](https://golang.org/doc/devel/release.html#go1.12).
---
## [v3.4.7](https://github.com/etcd-io/etcd/releases/tag/v3.4.7) (2020-04-01)
See [code changes](https://github.com/etcd-io/etcd/compare/v3.4.6...v3.4.7) and [v3.4 upgrade guide](https://etcd.io/docs/latest/upgrades/upgrade_3_4/) for any breaking changes.
### etcd server
- Improve [compaction performance when latest index is greater than 1-million](https://github.com/etcd-io/etcd/pull/11734).
### Package `wal`
- Add [`etcd_wal_write_bytes_total`](https://github.com/etcd-io/etcd/pull/11738).
### Metrics, Monitoring
- Add [`etcd_wal_write_bytes_total`](https://github.com/etcd-io/etcd/pull/11738).
### Go
- Compile with [*Go 1.12.17*](https://golang.org/doc/devel/release.html#go1.12).
---
## [v3.4.6](https://github.com/etcd-io/etcd/releases/tag/v3.4.6) (2020-03-29)
See [code changes](https://github.com/etcd-io/etcd/compare/v3.4.5...v3.4.6) and [v3.4 upgrade guide](https://etcd.io/docs/latest/upgrades/upgrade_3_4/) for any breaking changes.
### Package `lease`
- Fix [memory leak in follower nodes](https://github.com/etcd-io/etcd/pull/11731).
- https://github.com/etcd-io/etcd/issues/11495
- https://github.com/etcd-io/etcd/issues/11730
### Go
- Compile with [*Go 1.12.17*](https://golang.org/doc/devel/release.html#go1.12).
---
## [v3.4.5](https://github.com/etcd-io/etcd/releases/tag/v3.4.5) (2020-03-18)
See [code changes](https://github.com/etcd-io/etcd/compare/v3.4.4...v3.4.5) and [v3.4 upgrade guide](https://etcd.io/docs/latest/upgrades/upgrade_3_4/) for any breaking changes.
**Again, before running upgrades from any previous release, please make sure to read change logs below and [v3.4 upgrade guide](https://etcd.io/docs/latest/upgrades/upgrade_3_4/).**
### etcd server
- Log [`[CLIENT-PORT]/health` check in server side](https://github.com/etcd-io/etcd/pull/11704).
### client v3
- Fix [`"hasleader"` metadata embedding](https://github.com/etcd-io/etcd/pull/11687).
- Previously, `clientv3.WithRequireLeader(ctx)` was overwriting existing context keys.
### etcdctl v3
- Fix [`etcdctl member add`](https://github.com/etcd-io/etcd/pull/11638) command to prevent potential timeout.
### Metrics, Monitoring
See [List of metrics](https://etcd.io/docs/latest/metrics/) for all metrics per release.
- Add [`etcd_server_client_requests_total` with `"type"` and `"client_api_version"` labels](https://github.com/etcd-io/etcd/pull/11687).
### gRPC Proxy
- Fix [`panic on error`](https://github.com/etcd-io/etcd/pull/11694) for metrics handler.
### Go
- Compile with [*Go 1.12.17*](https://golang.org/doc/devel/release.html#go1.12).
---
## [v3.4.4](https://github.com/etcd-io/etcd/releases/tag/v3.4.4) (2020-02-24)
See [code changes](https://github.com/etcd-io/etcd/compare/v3.4.3...v3.4.4) and [v3.4 upgrade guide](https://etcd.io/docs/latest/upgrades/upgrade_3_4/) for any breaking changes.
**Again, before running upgrades from any previous release, please make sure to read change logs below and [v3.4 upgrade guide](https://etcd.io/docs/latest/upgrades/upgrade_3_4/).**
### etcd server
- Fix [`wait purge file loop during shutdown`](https://github.com/etcd-io/etcd/pull/11308).
- Previously, during shutdown etcd could accidentally remove needed wal files, resulting in catastrophic error `etcdserver: open wal error: wal: file not found.` during startup.
- Now, etcd makes sure the purge file loop exits before server signals stop of the raft node.
- [Fix corruption bug in defrag](https://github.com/etcd-io/etcd/pull/11613).
- Fix [quorum protection logic when promoting a learner](https://github.com/etcd-io/etcd/pull/11640).
- Improve [peer corruption checker](https://github.com/etcd-io/etcd/pull/11621) to work when peer mTLS is enabled.
### Metrics, Monitoring
See [List of metrics](https://etcd.io/docs/latest/metrics/) for all metrics per release.
Note that any `etcd_debugging_*` metrics are experimental and subject to change.
- Add [`etcd_debugging_mvcc_total_put_size_in_bytes`](https://github.com/etcd-io/etcd/pull/11374) Prometheus metric.
- Fix bug where [etcd_debugging_mvcc_db_compaction_keys_total is always 0](https://github.com/etcd-io/etcd/pull/11400).
### Auth
- Fix [NoPassword check when adding user through GRPC gateway](https://github.com/etcd-io/etcd/pull/11418) ([issue#11414](https://github.com/etcd-io/etcd/issues/11414))
- Fix bug where [some auth related messages are logged at wrong level](https://github.com/etcd-io/etcd/pull/11586)
---
## [v3.4.3](https://github.com/etcd-io/etcd/releases/tag/v3.4.3) (2019-10-24)
See [code changes](https://github.com/etcd-io/etcd/compare/v3.4.2...v3.4.3) and [v3.4 upgrade guide](https://etcd.io/docs/latest/upgrades/upgrade_3_4/) for any breaking changes.
**Again, before running upgrades from any previous release, please make sure to read change logs below and [v3.4 upgrade guide](https://etcd.io/docs/latest/upgrades/upgrade_3_4/).**
### Metrics, Monitoring
See [List of metrics](https://etcd.io/docs/latest/metrics/) for all metrics per release.
Note that any `etcd_debugging_*` metrics are experimental and subject to change.
- Change [`etcd_cluster_version`](https://github.com/etcd-io/etcd/pull/11254) Prometheus metrics to include only major and minor version.
### Go
- Compile with [*Go 1.12.12*](https://golang.org/doc/devel/release.html#go1.12).
---
## [v3.4.2](https://github.com/etcd-io/etcd/releases/tag/v3.4.2) (2019-10-11)
See [code changes](https://github.com/etcd-io/etcd/compare/v3.4.1...v3.4.2) and [v3.4 upgrade guide](https://etcd.io/docs/latest/upgrades/upgrade_3_4/) for any breaking changes.
**Again, before running upgrades from any previous release, please make sure to read change logs below and [v3.4 upgrade guide](https://etcd.io/docs/latest/upgrades/upgrade_3_4/).**
### etcdctl v3
- Fix [`etcdctl member add`](https://github.com/etcd-io/etcd/pull/11194) command to prevent potential timeout.
### etcdserver
- Add [`tracing`](https://github.com/etcd-io/etcd/pull/11179) to range, put and compact requests in etcdserver.
### Go
- Compile with [*Go 1.12.9*](https://golang.org/doc/devel/release.html#go1.12) including [*Go 1.12.8*](https://groups.google.com/d/msg/golang-announce/65QixT3tcmg/DrFiG6vvCwAJ) security fixes.
### client v3
- Fix [client balancer failover against multiple endpoints](https://github.com/etcd-io/etcd/pull/11184).
- Fix ["kube-apiserver: failover on multi-member etcd cluster fails certificate check on DNS mismatch" (kubernetes#83028)](https://github.com/kubernetes/kubernetes/issues/83028).
- Fix [IPv6 endpoint parsing in client](https://github.com/etcd-io/etcd/pull/11211).
- Fix ["1.16: etcd client does not parse IPv6 addresses correctly when members are joining" (kubernetes#83550)](https://github.com/kubernetes/kubernetes/issues/83550).
---
## [v3.4.1](https://github.com/etcd-io/etcd/releases/tag/v3.4.1) (2019-09-17)
See [code changes](https://github.com/etcd-io/etcd/compare/v3.4.0...v3.4.1) and [v3.4 upgrade guide](https://etcd.io/docs/latest/upgrades/upgrade_3_4/) for any breaking changes.
**Again, before running upgrades from any previous release, please make sure to read change logs below and [v3.4 upgrade guide](https://etcd.io/docs/latest/upgrades/upgrade_3_4/).**
### Metrics, Monitoring
See [List of metrics](https://etcd.io/docs/latest/metrics/) for all metrics per release.
Note that any `etcd_debugging_*` metrics are experimental and subject to change.
- Add [`etcd_debugging_mvcc_current_revision`](https://github.com/etcd-io/etcd/pull/11126) Prometheus metric.
- Add [`etcd_debugging_mvcc_compact_revision`](https://github.com/etcd-io/etcd/pull/11126) Prometheus metric.
### etcd server
- Fix [secure server logging message](https://github.com/etcd-io/etcd/commit/8b053b0f44c14ac0d9f39b9b78c17c57d47966eb).
- Remove [redundant `%` characters in file descriptor warning message](https://github.com/etcd-io/etcd/commit/d5f79adc9cea9ec8c93669526464b0aa19ed417b).
### Package `embed`
- Add [`embed.Config.ZapLoggerBuilder`](https://github.com/etcd-io/etcd/pull/11148) to allow creating a custom zap logger.
### Dependency
- Upgrade [`google.golang.org/grpc`](https://github.com/grpc/grpc-go/releases) from [**`v1.23.0`**](https://github.com/grpc/grpc-go/releases/tag/v1.23.0) to [**`v1.23.1`**](https://github.com/grpc/grpc-go/releases/tag/v1.23.1).
### Go
- Compile with [*Go 1.12.9*](https://golang.org/doc/devel/release.html#go1.12) including [*Go 1.12.8*](https://groups.google.com/d/msg/golang-announce/65QixT3tcmg/DrFiG6vvCwAJ) security fixes.
---
## v3.4.0 (2019-08-30)
See [code changes](https://github.com/etcd-io/etcd/compare/v3.3.0...v3.4.0) and [v3.4 upgrade guide](https://etcd.io/docs/latest/upgrades/upgrade_3_4/) for any breaking changes.
- [v3.4.0](https://github.com/etcd-io/etcd/releases/tag/v3.4.0) (2019-08-30), see [code changes](https://github.com/etcd-io/etcd/compare/v3.4.0-rc.4...v3.4.0).
- [v3.4.0-rc.4](https://github.com/etcd-io/etcd/releases/tag/v3.4.0-rc.4) (2019-08-29), see [code changes](https://github.com/etcd-io/etcd/compare/v3.4.0-rc.3...v3.4.0-rc.4).
- [v3.4.0-rc.3](https://github.com/etcd-io/etcd/releases/tag/v3.4.0-rc.3) (2019-08-27), see [code changes](https://github.com/etcd-io/etcd/compare/v3.4.0-rc.2...v3.4.0-rc.3).
- [v3.4.0-rc.2](https://github.com/etcd-io/etcd/releases/tag/v3.4.0-rc.2) (2019-08-23), see [code changes](https://github.com/etcd-io/etcd/compare/v3.4.0-rc.1...v3.4.0-rc.2).
- [v3.4.0-rc.1](https://github.com/etcd-io/etcd/releases/tag/v3.4.0-rc.1) (2019-08-15), see [code changes](https://github.com/etcd-io/etcd/compare/v3.4.0-rc.0...v3.4.0-rc.1).
- [v3.4.0-rc.0](https://github.com/etcd-io/etcd/releases/tag/v3.4.0-rc.0) (2019-08-12), see [code changes](https://github.com/etcd-io/etcd/compare/v3.3.0...v3.4.0-rc.0).
**Again, before running upgrades from any previous release, please make sure to read change logs below and [v3.4 upgrade guide](https://etcd.io/docs/latest/upgrades/upgrade_3_4/).**
### Documentation
- etcd now has a new website! Please visit https://etcd.io.
### Improved
- Add Raft learner: [etcd#10725](https://github.com/etcd-io/etcd/pull/10725), [etcd#10727](https://github.com/etcd-io/etcd/pull/10727), [etcd#10730](https://github.com/etcd-io/etcd/pull/10730).
- User guide: [runtime-configuration document](https://etcd.io/docs/latest/op-guide/runtime-configuration/#add-a-new-member-as-learner).
- API change: [API reference document](https://etcd.io/docs/latest/dev-guide/api_reference_v3/).
- More details on implementation: [learner design document](https://etcd.io/docs/latest/learning/design-learner/) and [implementation task list](https://github.com/etcd-io/etcd/issues/10537).
- Rewrite [client balancer](https://github.com/etcd-io/etcd/pull/9860) with [new gRPC balancer interface](https://github.com/etcd-io/etcd/issues/9106).
- Upgrade [gRPC to v1.23.0](https://github.com/etcd-io/etcd/pull/10911).
- Improve [client balancer failover against secure endpoints](https://github.com/etcd-io/etcd/pull/10911).
- Fix ["kube-apiserver 1.13.x refuses to work when first etcd-server is not available" (kubernetes#72102)](https://github.com/kubernetes/kubernetes/issues/72102).
- Fix [gRPC panic "send on closed channel](https://github.com/etcd-io/etcd/issues/9956).
- [The new client balancer](https://etcd.io/docs/latest/learning/design-client/) uses an asynchronous resolver to pass endpoints to the gRPC dial function. To block until the underlying connection is up, pass `grpc.WithBlock()` to `clientv3.Config.DialOptions`.
- Add [backoff on watch retries on transient errors](https://github.com/etcd-io/etcd/pull/9840).
- Add [jitter to watch progress notify](https://github.com/etcd-io/etcd/pull/9278) to prevent [spikes in `etcd_network_client_grpc_sent_bytes_total`](https://github.com/etcd-io/etcd/issues/9246).
- Improve [read index wait timeout warning log](https://github.com/etcd-io/etcd/pull/10026), which indicates that local node might have slow network.
- Improve [slow request apply warning log](https://github.com/etcd-io/etcd/pull/9288).
- e.g. `read-only range request "key:\"/a\" range_end:\"/b\" " with result "range_response_count:3 size:96" took too long (97.966µs) to execute`.
- Redact [request value field](https://github.com/etcd-io/etcd/pull/9822).
- Provide [response size](https://github.com/etcd-io/etcd/pull/9826).
- Improve ["became inactive" warning log](https://github.com/etcd-io/etcd/pull/10024), which indicates message send to a peer failed.
- Improve [TLS setup error logging](https://github.com/etcd-io/etcd/pull/9518) to help debug [TLS-enabled cluster configuring issues](https://github.com/etcd-io/etcd/issues/9400).
- Improve [long-running concurrent read transactions under light write workloads](https://github.com/etcd-io/etcd/pull/9296).
- Previously, periodic commit on pending writes blocks incoming read transactions, even if there is no pending write.
- Now, periodic commit operation does not block concurrent read transactions, thus improves long-running read transaction performance.
- Make [backend read transactions fully concurrent](https://github.com/etcd-io/etcd/pull/10523).
- Previously, ongoing long-running read transactions block writes and future reads.
- With this change, write throughput is increased by 70% and P99 write latency is reduced by 90% in the presence of long-running reads.
- Improve [Raft Read Index timeout warning messages](https://github.com/etcd-io/etcd/pull/9897).
- Adjust [election timeout on server restart](https://github.com/etcd-io/etcd/pull/9415) to reduce [disruptive rejoining servers](https://github.com/etcd-io/etcd/issues/9333).
- Previously, etcd fast-forwards election ticks on server start, with only one tick left for leader election. This is to speed up start phase, without having to wait until all election ticks elapse. Advancing election ticks is useful for cross datacenter deployments with larger election timeouts. However, it was affecting cluster availability if the last tick elapses before leader contacts the restarted node.
- Now, when etcd restarts, it adjusts election ticks with more than one tick left, thus more time for leader to prevent disruptive restart.
- Add [Raft Pre-Vote feature](https://github.com/etcd-io/etcd/pull/9352) to reduce [disruptive rejoining servers](https://github.com/etcd-io/etcd/issues/9333).
- For instance, a flaky(or rejoining) member may drop in and out, and start campaign. This member will end up with a higher term, and ignore all incoming messages with lower term. In this case, a new leader eventually need to get elected, thus disruptive to cluster availability. Raft implements Pre-Vote phase to prevent this kind of disruptions. If enabled, Raft runs an additional phase of election to check if pre-candidate can get enough votes to win an election.
- Adjust [periodic compaction retention window](https://github.com/etcd-io/etcd/pull/9485).
- e.g. `etcd --auto-compaction-mode=revision --auto-compaction-retention=1000` automatically `Compact` on `"latest revision" - 1000` every 5-minute (when latest revision is 30000, compact on revision 29000).
- e.g. Previously, `etcd --auto-compaction-mode=periodic --auto-compaction-retention=24h` automatically `Compact` with 24-hour retention windown for every 2.4-hour. Now, `Compact` happens for every 1-hour.
- e.g. Previously, `etcd --auto-compaction-mode=periodic --auto-compaction-retention=30m` automatically `Compact` with 30-minute retention windown for every 3-minute. Now, `Compact` happens for every 30-minute.
- Periodic compactor keeps recording latest revisions for every compaction period when given period is less than 1-hour, or for every 1-hour when given compaction period is greater than 1-hour (e.g. 1-hour when `etcd --auto-compaction-mode=periodic --auto-compaction-retention=24h`).
- For every compaction period or 1-hour, compactor uses the last revision that was fetched before compaction period, to discard historical data.
- The retention window of compaction period moves for every given compaction period or hour.
- For instance, when hourly writes are 100 and `etcd --auto-compaction-mode=periodic --auto-compaction-retention=24h`, `v3.2.x`, `v3.3.0`, `v3.3.1`, and `v3.3.2` compact revision 2400, 2640, and 2880 for every 2.4-hour, while `v3.3.3` *or later* compacts revision 2400, 2500, 2600 for every 1-hour.
- Furthermore, when `etcd --auto-compaction-mode=periodic --auto-compaction-retention=30m` and writes per minute are about 1000, `v3.3.0`, `v3.3.1`, and `v3.3.2` compact revision 30000, 33000, and 36000, for every 3-minute, while `v3.3.3` *or later* compacts revision 30000, 60000, and 90000, for every 30-minute.
- Improve [lease expire/revoke operation performance](https://github.com/etcd-io/etcd/pull/9418), address [lease scalability issue](https://github.com/etcd-io/etcd/issues/9496).
- Make [Lease `Lookup` non-blocking with concurrent `Grant`/`Revoke`](https://github.com/etcd-io/etcd/pull/9229).
- Make etcd server return `raft.ErrProposalDropped` on internal Raft proposal drop in [v3 applier](https://github.com/etcd-io/etcd/pull/9549) and [v2 applier](https://github.com/etcd-io/etcd/pull/9558).
- e.g. a node is removed from cluster, or [`raftpb.MsgProp` arrives at current leader while there is an ongoing leadership transfer](https://github.com/etcd-io/etcd/issues/8975).
- Add [`snapshot`](https://github.com/etcd-io/etcd/pull/9118) package for easier snapshot workflow (see [`godoc.org/github.com/etcd/clientv3/snapshot`](https://godoc.org/github.com/etcd-io/etcd/clientv3/snapshot) for more).
- Improve [functional tester](https://github.com/etcd-io/etcd/tree/main/functional) coverage: [proxy layer to run network fault tests in CI](https://github.com/etcd-io/etcd/pull/9081), [TLS is enabled both for server and client](https://github.com/etcd-io/etcd/pull/9534), [liveness mode](https://github.com/etcd-io/etcd/issues/9230), [shuffle test sequence](https://github.com/etcd-io/etcd/issues/9381), [membership reconfiguration failure cases](https://github.com/etcd-io/etcd/pull/9564), [disastrous quorum loss and snapshot recover from a seed member](https://github.com/etcd-io/etcd/pull/9565), [embedded etcd](https://github.com/etcd-io/etcd/pull/9572).
- Improve [index compaction blocking](https://github.com/etcd-io/etcd/pull/9511) by using a copy on write clone to avoid holding the lock for the traversal of the entire index.
- Update [JWT methods](https://github.com/etcd-io/etcd/pull/9883) to allow for use of any supported signature method/algorithm.
- Add [Lease checkpointing](https://github.com/etcd-io/etcd/pull/9924) to persist remaining TTLs to the consensus log periodically so that long lived leases progress toward expiry in the presence of leader elections and server restarts.
- Enabled by experimental flag "--experimental-enable-lease-checkpoint".
- Add [gRPC interceptor for debugging logs](https://github.com/etcd-io/etcd/pull/9990); enable `etcd --debug` flag to see per-request debug information.
- Add [consistency check in snapshot status](https://github.com/etcd-io/etcd/pull/10109). If consistency check on snapshot file fails, `snapshot status` returns `"snapshot file integrity check failed..."` error.
- Add [`Verify` function to perform corruption check on WAL contents](https://github.com/etcd-io/etcd/pull/10603).
- Improve [heartbeat send failure logging](https://github.com/etcd-io/etcd/pull/10663).
- Support [users with no password](https://github.com/etcd-io/etcd/pull/9817) for reducing security risk introduced by leaked password. The users can only be authenticated with `CommonName` based auth.
- Add `etcd --experimental-peer-skip-client-san-verification` to [skip verification of peer client address](https://github.com/etcd-io/etcd/pull/10524).
- Add `etcd --experimental-compaction-batch-limit` to [sets the maximum revisions deleted in each compaction batch](https://github.com/etcd-io/etcd/pull/11034).
- Reduced default compaction batch size from 10k revisions to 1k revisions to improve p99 latency during compactions and reduced wait between compactions from 100ms to 10ms.
### Breaking Changes
- Rewrite [client balancer](https://github.com/etcd-io/etcd/pull/9860) with [new gRPC balancer interface](https://github.com/etcd-io/etcd/issues/9106).
- Upgrade [gRPC to v1.23.0](https://github.com/etcd-io/etcd/pull/10911).
- Improve [client balancer failover against secure endpoints](https://github.com/etcd-io/etcd/pull/10911).
- Fix ["kube-apiserver 1.13.x refuses to work when first etcd-server is not available" (kubernetes#72102)](https://github.com/kubernetes/kubernetes/issues/72102).
- Fix [gRPC panic "send on closed channel](https://github.com/etcd-io/etcd/issues/9956).
- [The new client balancer](https://etcd.io/docs/latest/learning/design-client/) uses an asynchronous resolver to pass endpoints to the gRPC dial function. To block until the underlying connection is up, pass `grpc.WithBlock()` to `clientv3.Config.DialOptions`.
- Require [*Go 1.12+*](https://github.com/etcd-io/etcd/pull/10045).
- Compile with [*Go 1.12.9*](https://golang.org/doc/devel/release.html#go1.12) including [*Go 1.12.8*](https://groups.google.com/d/msg/golang-announce/65QixT3tcmg/DrFiG6vvCwAJ) security fixes.
- Migrate dependency management tool from `glide` to [Go module](https://github.com/etcd-io/etcd/pull/10063).
- <= 3.3 puts `vendor` directory under `cmd/vendor` directory to [prevent conflicting transitive dependencies](https://github.com/etcd-io/etcd/issues/4913).
- 3.4 moves `cmd/vendor` directory to `vendor` at repository root.
- Remove recursive symlinks in `cmd` directory.
- Now `go get/install/build` on `etcd` packages (e.g. `clientv3`, `tools/benchmark`) enforce builds with etcd `vendor` directory.
- Deprecated `latest` [release container](https://console.cloud.google.com/gcr/images/etcd-development/GLOBAL/etcd) tag.
- **`docker pull gcr.io/etcd-development/etcd:latest` would not be up-to-date**.
- Deprecated [minor](https://semver.org/) version [release container](https://console.cloud.google.com/gcr/images/etcd-development/GLOBAL/etcd) tags.
- `docker pull gcr.io/etcd-development/etcd:v3.3` would still work.
- **`docker pull gcr.io/etcd-development/etcd:v3.4` would not work**.
- Use **`docker pull gcr.io/etcd-development/etcd:v3.4.x`** instead, with the exact patch version.
- Deprecated [ACIs from official release](https://github.com/etcd-io/etcd/pull/9059).
- [AppC was officially suspended](https://github.com/appc/spec#-disclaimer-), as of late 2016.
- [`acbuild`](https://github.com/containers/build#this-project-is-currently-unmaintained) is not maintained anymore.
- `*.aci` files are not available from `v3.4` release.
- Move [`"github.com/coreos/etcd"`](https://github.com/etcd-io/etcd/issues/9965) to [`"github.com/etcd-io/etcd"`](https://github.com/etcd-io/etcd/issues/9965).
- Change import path to `"go.etcd.io/etcd"`.
- e.g. `import "go.etcd.io/etcd/raft"`.
- Make [`ETCDCTL_API=3 etcdctl` default](https://github.com/etcd-io/etcd/issues/9600).
- Now, `etcdctl set foo bar` must be `ETCDCTL_API=2 etcdctl set foo bar`.
- Now, `ETCDCTL_API=3 etcdctl put foo bar` could be just `etcdctl put foo bar`.
- Make [`etcd --enable-v2=false` default](https://github.com/etcd-io/etcd/pull/10935).
- Make [`embed.DefaultEnableV2` `false` default](https://github.com/etcd-io/etcd/pull/10935).
- **Deprecated `etcd --ca-file` flag**. Use [`etcd --trusted-ca-file`](https://github.com/etcd-io/etcd/pull/9470) instead (`etcd --ca-file` flag has been marked deprecated since v2.1).
- **Deprecated `etcd --peer-ca-file` flag**. Use [`etcd --peer-trusted-ca-file`](https://github.com/etcd-io/etcd/pull/9470) instead (`etcd --peer-ca-file` flag has been marked deprecated since v2.1).
- **Deprecated `pkg/transport.TLSInfo.CAFile` field**. Use [`pkg/transport.TLSInfo.TrustedCAFile`](https://github.com/etcd-io/etcd/pull/9470) instead (`CAFile` field has been marked deprecated since v2.1).
- Exit on [empty hosts in advertise URLs](https://github.com/etcd-io/etcd/pull/8786).
- Address [advertise client URLs accepts empty hosts](https://github.com/etcd-io/etcd/issues/8379).
- e.g. exit with error on `--advertise-client-urls=http://:2379`.
- e.g. exit with error on `--initial-advertise-peer-urls=http://:2380`.
- Exit on [shadowed environment variables](https://github.com/etcd-io/etcd/pull/9382).
- Address [error on shadowed environment variables](https://github.com/etcd-io/etcd/issues/8380).
- e.g. exit with error on `ETCD_NAME=abc etcd --name=def`.
- e.g. exit with error on `ETCD_INITIAL_CLUSTER_TOKEN=abc etcd --initial-cluster-token=def`.
- e.g. exit with error on `ETCDCTL_ENDPOINTS=abc.com ETCDCTL_API=3 etcdctl endpoint health --endpoints=def.com`.
- Change [`etcdserverpb.AuthRoleRevokePermissionRequest/key,range_end` fields type from `string` to `bytes`](https://github.com/etcd-io/etcd/pull/9433).
- Deprecating `etcd_debugging_mvcc_db_total_size_in_bytes` Prometheus metric (to be removed in v3.5). Use [`etcd_mvcc_db_total_size_in_bytes`](https://github.com/etcd-io/etcd/pull/9819) instead.
- Deprecating `etcd_debugging_mvcc_put_total` Prometheus metric (to be removed in v3.5). Use [`etcd_mvcc_put_total`](https://github.com/etcd-io/etcd/pull/10962) instead.
- Deprecating `etcd_debugging_mvcc_delete_total` Prometheus metric (to be removed in v3.5). Use [`etcd_mvcc_delete_total`](https://github.com/etcd-io/etcd/pull/10962) instead.
- Deprecating `etcd_debugging_mvcc_range_total` Prometheus metric (to be removed in v3.5). Use [`etcd_mvcc_range_total`](https://github.com/etcd-io/etcd/pull/10968) instead.
- Deprecating `etcd_debugging_mvcc_txn_total`Prometheus metric (to be removed in v3.5). Use [`etcd_mvcc_txn_total`](https://github.com/etcd-io/etcd/pull/10968) instead.
- Rename `etcdserver.ServerConfig.SnapCount` field to `etcdserver.ServerConfig.SnapshotCount`, to be consistent with the flag name `etcd --snapshot-count`.
- Rename `embed.Config.SnapCount` field to [`embed.Config.SnapshotCount`](https://github.com/etcd-io/etcd/pull/9745), to be consistent with the flag name `etcd --snapshot-count`.
- Change [`embed.Config.CorsInfo` in `*cors.CORSInfo` type to `embed.Config.CORS` in `map[string]struct{}` type](https://github.com/etcd-io/etcd/pull/9490).
- Deprecated [`embed.Config.SetupLogging`](https://github.com/etcd-io/etcd/pull/9572).
- Now logger is set up automatically based on [`embed.Config.Logger`, `embed.Config.LogOutputs`, `embed.Config.Debug` fields](https://github.com/etcd-io/etcd/pull/9572).
- Rename [`etcd --log-output` to `etcd --log-outputs`](https://github.com/etcd-io/etcd/pull/9624) to support multiple log outputs.
- **`etcd --log-output`** will be deprecated in v3.5.
- Rename [**`embed.Config.LogOutput`** to **`embed.Config.LogOutputs`**](https://github.com/etcd-io/etcd/pull/9624) to support multiple log outputs.
- Change [**`embed.Config.LogOutputs`** type from `string` to `[]string`](https://github.com/etcd-io/etcd/pull/9579) to support multiple log outputs.
- Now that `etcd --log-outputs` accepts multiple writers, etcd configuration YAML file `log-outputs` field must be changed to `[]string` type.
- Previously, `etcd --config-file etcd.config.yaml` can have `log-outputs: default` field, now must be `log-outputs: [default]`.
- Deprecating [`etcd --debug`](https://github.com/etcd-io/etcd/pull/10947) flag. Use `etcd --log-level=debug` flag instead.
- v3.5 will deprecate `etcd --debug` flag in favor of `etcd --log-level=debug`.
- Change v3 `etcdctl snapshot` exit codes with [`snapshot` package](https://github.com/etcd-io/etcd/pull/9118/commits/df689f4280e1cce4b9d61300be13ca604d41670a).
- Exit on error with exit code 1 (no more exit code 5 or 6 on `snapshot save/restore` commands).
- Deprecated [`grpc.ErrClientConnClosing`](https://github.com/etcd-io/etcd/pull/10981).
- `clientv3` and `proxy/grpcproxy` now does not return `grpc.ErrClientConnClosing`.
- `grpc.ErrClientConnClosing` has been [deprecated in gRPC >= 1.10](https://github.com/grpc/grpc-go/pull/1854).
- Use `clientv3.IsConnCanceled(error)` or `google.golang.org/grpc/status.FromError(error)` instead.
- Deprecated [gRPC gateway](https://github.com/grpc-ecosystem/grpc-gateway) endpoint `/v3beta` with [`/v3`](https://github.com/etcd-io/etcd/pull/9298).
- Deprecated [`/v3alpha`](https://github.com/etcd-io/etcd/pull/9298).
- To deprecate [`/v3beta`](https://github.com/etcd-io/etcd/issues/9189) in v3.5.
- In v3.4, `curl -L http://localhost:2379/v3beta/kv/put -X POST -d '{"key": "Zm9v", "value": "YmFy"}'` still works as a fallback to `curl -L http://localhost:2379/v3/kv/put -X POST -d '{"key": "Zm9v", "value": "YmFy"}'`, but `curl -L http://localhost:2379/v3beta/kv/put -X POST -d '{"key": "Zm9v", "value": "YmFy"}'` won't work in v3.5. Use `curl -L http://localhost:2379/v3/kv/put -X POST -d '{"key": "Zm9v", "value": "YmFy"}'` instead.
- Change [`wal` package function signatures](https://github.com/etcd-io/etcd/pull/9572) to support [structured logger and logging to file](https://github.com/etcd-io/etcd/issues/9438) in server-side.
- Previously, `Open(dirpath string, snap walpb.Snapshot) (*WAL, error)`, now `Open(lg *zap.Logger, dirpath string, snap walpb.Snapshot) (*WAL, error)`.
- Previously, `OpenForRead(dirpath string, snap walpb.Snapshot) (*WAL, error)`, now `OpenForRead(lg *zap.Logger, dirpath string, snap walpb.Snapshot) (*WAL, error)`.
- Previously, `Repair(dirpath string) bool`, now `Repair(lg *zap.Logger, dirpath string) bool`.
- Previously, `Create(dirpath string, metadata []byte) (*WAL, error)`, now `Create(lg *zap.Logger, dirpath string, metadata []byte) (*WAL, error)`.
- Remove [`pkg/cors` package](https://github.com/etcd-io/etcd/pull/9490).
- Move internal packages to `etcdserver`.
- `"github.com/coreos/etcd/alarm"` to `"go.etcd.io/etcd/etcdserver/api/v3alarm"`.
- `"github.com/coreos/etcd/compactor"` to `"go.etcd.io/etcd/etcdserver/api/v3compactor"`.
- `"github.com/coreos/etcd/discovery"` to `"go.etcd.io/etcd/etcdserver/api/v2discovery"`.
- `"github.com/coreos/etcd/etcdserver/auth"` to `"go.etcd.io/etcd/etcdserver/api/v2auth"`.
- `"github.com/coreos/etcd/etcdserver/membership"` to `"go.etcd.io/etcd/etcdserver/api/membership"`.
- `"github.com/coreos/etcd/etcdserver/stats"` to `"go.etcd.io/etcd/etcdserver/api/v2stats"`.
- `"github.com/coreos/etcd/error"` to `"go.etcd.io/etcd/etcdserver/api/v2error"`.
- `"github.com/coreos/etcd/rafthttp"` to `"go.etcd.io/etcd/etcdserver/api/rafthttp"`.
- `"github.com/coreos/etcd/snap"` to `"go.etcd.io/etcd/etcdserver/api/snap"`.
- `"github.com/coreos/etcd/store"` to `"go.etcd.io/etcd/etcdserver/api/v2store"`.
- Change [snapshot file permissions](https://github.com/etcd-io/etcd/pull/9977): On Linux, the snapshot file changes from readable by all (mode 0644) to readable by the user only (mode 0600).
- Change [`pkg/adt.IntervalTree` from `struct` to `interface`](https://github.com/etcd-io/etcd/pull/10959).
- See [`pkg/adt` README](https://github.com/etcd-io/etcd/tree/main/pkg/adt) and [`pkg/adt` godoc](https://godoc.org/go.etcd.io/etcd/pkg/adt).
- Release branch `/version` defines version `3.4.x-pre`, instead of `3.4.y+git`.
- Use `3.4.5-pre`, instead of `3.4.4+git`.
### Dependency
- Upgrade [`github.com/coreos/bbolt`](https://github.com/etcd-io/bbolt/releases) from [**`v1.3.1-coreos.6`**](https://github.com/etcd-io/bbolt/releases/tag/v1.3.1-coreos.6) to [`go.etcd.io/bbolt`](https://github.com/etcd-io/bbolt/releases) [**`v1.3.3`**](https://github.com/etcd-io/bbolt/releases/tag/v1.3.3).
- Upgrade [`google.golang.org/grpc`](https://github.com/grpc/grpc-go/releases) from [**`v1.7.5`**](https://github.com/grpc/grpc-go/releases/tag/v1.7.5) to [**`v1.23.0`**](https://github.com/grpc/grpc-go/releases/tag/v1.23.0).
- Migrate [`github.com/ugorji/go/codec`](https://github.com/ugorji/go/releases) to [**`github.com/json-iterator/go`**](https://github.com/json-iterator/go), to [regenerate v2 `client`](https://github.com/etcd-io/etcd/pull/9494) (See [#10667](https://github.com/etcd-io/etcd/pull/10667) for more).
- Migrate [`github.com/ghodss/yaml`](https://github.com/ghodss/yaml/releases) to [**`sigs.k8s.io/yaml`**](https://github.com/kubernetes-sigs/yaml) (See [#10687](https://github.com/etcd-io/etcd/pull/10687) for more).
- Upgrade [`golang.org/x/crypto`](https://github.com/golang/crypto) from [**`crypto@9419663f5`**](https://github.com/golang/crypto/commit/9419663f5a44be8b34ca85f08abc5fe1be11f8a3) to [**`crypto@0709b304e793`**](https://github.com/golang/crypto/commit/0709b304e793a5edb4a2c0145f281ecdc20838a4).
- Upgrade [`golang.org/x/net`](https://github.com/golang/net) from [**`net@66aacef3d`**](https://github.com/golang/net/commit/66aacef3dd8a676686c7ae3716979581e8b03c47) to [**`net@adae6a3d119a`**](https://github.com/golang/net/commit/adae6a3d119ae4890b46832a2e88a95adc62b8e7).
- Upgrade [`golang.org/x/sys`](https://github.com/golang/sys) from [**`sys@ebfc5b463`**](https://github.com/golang/sys/commit/ebfc5b4631820b793c9010c87fd8fef0f39eb082) to [**`sys@c7b8b68b1456`**](https://github.com/golang/sys/commit/c7b8b68b14567162c6602a7c5659ee0f26417c18).
- Upgrade [`golang.org/x/text`](https://github.com/golang/text) from [**`text@b19bf474d`**](https://github.com/golang/text/commit/b19bf474d317b857955b12035d2c5acb57ce8b01) to [**`v0.3.0`**](https://github.com/golang/text/releases/tag/v0.3.0).
- Upgrade [`golang.org/x/time`](https://github.com/golang/time) from [**`time@c06e80d93`**](https://github.com/golang/time/commit/c06e80d9300e4443158a03817b8a8cb37d230320) to [**`time@fbb02b229`**](https://github.com/golang/time/commit/fbb02b2291d28baffd63558aa44b4b56f178d650).
- Upgrade [`github.com/golang/protobuf`](https://github.com/golang/protobuf/releases) from [**`golang/protobuf@1e59b77b5`**](https://github.com/golang/protobuf/commit/1e59b77b52bf8e4b449a57e6f79f21226d571845) to [**`v1.3.2`**](https://github.com/golang/protobuf/releases/tag/v1.3.2).
- Upgrade [`gopkg.in/yaml.v2`](https://github.com/go-yaml/yaml/releases) from [**`yaml@cd8b52f82`**](https://github.com/go-yaml/yaml/commit/cd8b52f8269e0feb286dfeef29f8fe4d5b397e0b) to [**`yaml@5420a8b67`**](https://github.com/go-yaml/yaml/commit/5420a8b6744d3b0345ab293f6fcba19c978f1183).
- Upgrade [`github.com/dgrijalva/jwt-go`](https://github.com/dgrijalva/jwt-go/releases) from [**`v3.0.0`**](https://github.com/dgrijalva/jwt-go/releases/tag/v3.0.0) to [**`v3.2.0`**](https://github.com/dgrijalva/jwt-go/releases/tag/v3.2.0).
- Upgrade [`github.com/soheilhy/cmux`](https://github.com/soheilhy/cmux/releases) from [**`v0.1.3`**](https://github.com/soheilhy/cmux/releases/tag/v0.1.3) to [**`v0.1.4`**](https://github.com/soheilhy/cmux/releases/tag/v0.1.4).
- Upgrade [`github.com/google/btree`](https://github.com/google/btree/releases) from [**`google/btree@925471ac9`**](https://github.com/google/btree/commit/925471ac9e2131377a91e1595defec898166fe49) to [**`v1.0.0`**](https://github.com/google/btree/releases/tag/v1.0.0).
- Upgrade [`github.com/spf13/cobra`](https://github.com/spf13/cobra/releases) from [**`spf13/cobra@1c44ec8d3`**](https://github.com/spf13/cobra/commit/1c44ec8d3f1552cac48999f9306da23c4d8a288b) to [**`v0.0.3`**](https://github.com/spf13/cobra/releases/tag/v0.0.3).
- Upgrade [`github.com/spf13/pflag`](https://github.com/spf13/pflag/releases) from [**`v1.0.0`**](https://github.com/spf13/pflag/releases/tag/v1.0.0) to [**`spf13/pflag@1ce0cc6db`**](https://github.com/spf13/pflag/commit/1ce0cc6db4029d97571db82f85092fccedb572ce).
- Upgrade [`github.com/coreos/go-systemd`](https://github.com/coreos/go-systemd/releases) from [**`v15`**](https://github.com/coreos/go-systemd/releases/tag/v15) to [**`v17`**](https://github.com/coreos/go-systemd/releases/tag/v17).
- Upgrade [`github.com/prometheus/client_golang`](https://github.com/prometheus/client_golang/releases) from [**``prometheus/client_golang@5cec1d042``**](https://github.com/prometheus/client_golang/commit/5cec1d0429b02e4323e042eb04dafdb079ddf568) to [**`v1.0.0`**](https://github.com/prometheus/client_golang/releases/tag/v1.0.0).
- Upgrade [`github.com/grpc-ecosystem/go-grpc-prometheus`](https://github.com/grpc-ecosystem/go-grpc-prometheus/releases) from [**``grpc-ecosystem/go-grpc-prometheus@0dafe0d49``**](https://github.com/grpc-ecosystem/go-grpc-prometheus/commit/0dafe0d496ea71181bf2dd039e7e3f44b6bd11a7) to [**`v1.2.0`**](https://github.com/grpc-ecosystem/go-grpc-prometheus/releases/tag/v1.2.0).
- Upgrade [`github.com/grpc-ecosystem/grpc-gateway`](https://github.com/grpc-ecosystem/grpc-gateway/releases) from [**`v1.3.1`**](https://github.com/grpc-ecosystem/grpc-gateway/releases/tag/v1.3.1) to [**`v1.4.1`**](https://github.com/grpc-ecosystem/grpc-gateway/releases/tag/v1.4.1).
- Migrate [`github.com/kr/pty`](https://github.com/kr/pty/releases) to [**`github.com/creack/pty`**](https://github.com/creack/pty/releases/tag/v1.1.7), as the later has replaced the original module.
- Upgrade [`github.com/gogo/protobuf`](https://github.com/gogo/protobuf/releases) from [**`v1.0.0`**](https://github.com/gogo/protobuf/releases/tag/v1.0.0) to [**`v1.2.1`**](https://github.com/gogo/protobuf/releases/tag/v1.2.1).
### Metrics, Monitoring
See [List of metrics](https://etcd.io/docs/latest/metrics/) for all metrics per release.
Note that any `etcd_debugging_*` metrics are experimental and subject to change.
- Add [`etcd_snap_db_fsync_duration_seconds_count`](https://github.com/etcd-io/etcd/pull/9997) Prometheus metric.
- Add [`etcd_snap_db_save_total_duration_seconds_bucket`](https://github.com/etcd-io/etcd/pull/9997) Prometheus metric.
- Add [`etcd_network_snapshot_send_success`](https://github.com/etcd-io/etcd/pull/9997) Prometheus metric.
- Add [`etcd_network_snapshot_send_failures`](https://github.com/etcd-io/etcd/pull/9997) Prometheus metric.
- Add [`etcd_network_snapshot_send_total_duration_seconds`](https://github.com/etcd-io/etcd/pull/9997) Prometheus metric.
- Add [`etcd_network_snapshot_receive_success`](https://github.com/etcd-io/etcd/pull/9997) Prometheus metric.
- Add [`etcd_network_snapshot_receive_failures`](https://github.com/etcd-io/etcd/pull/9997) Prometheus metric.
- Add [`etcd_network_snapshot_receive_total_duration_seconds`](https://github.com/etcd-io/etcd/pull/9997) Prometheus metric.
- Add [`etcd_network_active_peers`](https://github.com/etcd-io/etcd/pull/9762) Prometheus metric.
- Let's say `"7339c4e5e833c029"` server `/metrics` returns `etcd_network_active_peers{Local="7339c4e5e833c029",Remote="729934363faa4a24"} 1` and `etcd_network_active_peers{Local="7339c4e5e833c029",Remote="b548c2511513015"} 1`. This indicates that the local node `"7339c4e5e833c029"` currently has two active remote peers `"729934363faa4a24"` and `"b548c2511513015"` in a 3-node cluster. If the node `"b548c2511513015"` is down, the local node `"7339c4e5e833c029"` will show `etcd_network_active_peers{Local="7339c4e5e833c029",Remote="729934363faa4a24"} 1` and `etcd_network_active_peers{Local="7339c4e5e833c029",Remote="b548c2511513015"} 0`.
- Add [`etcd_network_disconnected_peers_total`](https://github.com/etcd-io/etcd/pull/9762) Prometheus metric.
- If a remote peer `"b548c2511513015"` is down, the local node `"7339c4e5e833c029"` server `/metrics` would return `etcd_network_disconnected_peers_total{Local="7339c4e5e833c029",Remote="b548c2511513015"} 1`, while active peer metrics will show `etcd_network_active_peers{Local="7339c4e5e833c029",Remote="729934363faa4a24"} 1` and `etcd_network_active_peers{Local="7339c4e5e833c029",Remote="b548c2511513015"} 0`.
- Add [`etcd_network_server_stream_failures_total`](https://github.com/etcd-io/etcd/pull/9760) Prometheus metric.
- e.g. `etcd_network_server_stream_failures_total{API="lease-keepalive",Type="receive"} 1`
- e.g. `etcd_network_server_stream_failures_total{API="watch",Type="receive"} 1`
- Improve [`etcd_network_peer_round_trip_time_seconds`](https://github.com/etcd-io/etcd/pull/10155) Prometheus metric to track leader heartbeats.
- Previously, it only samples the TCP connection for snapshot messages.
- Increase [`etcd_network_peer_round_trip_time_seconds`](https://github.com/etcd-io/etcd/pull/9762) Prometheus metric histogram upper-bound.
- Previously, highest bucket only collects requests taking 0.8192 seconds or more.
- Now, highest buckets collect 0.8192 seconds, 1.6384 seconds, and 3.2768 seconds or more.
- Add [`etcd_server_is_leader`](https://github.com/etcd-io/etcd/pull/9587) Prometheus metric.
- Add [`etcd_server_id`](https://github.com/etcd-io/etcd/pull/9998) Prometheus metric.
- Add [`etcd_cluster_version`](https://github.com/etcd-io/etcd/pull/10257) Prometheus metric.
- Add [`etcd_server_version`](https://github.com/etcd-io/etcd/pull/8960) Prometheus metric.
- To replace [Kubernetes `etcd-version-monitor`](https://github.com/etcd-io/etcd/issues/8948).
- Add [`etcd_server_go_version`](https://github.com/etcd-io/etcd/pull/9957) Prometheus metric.
- Add [`etcd_server_health_success`](https://github.com/etcd-io/etcd/pull/10156) Prometheus metric.
- Add [`etcd_server_health_failures`](https://github.com/etcd-io/etcd/pull/10156) Prometheus metric.
- Add [`etcd_server_read_indexes_failed_total`](https://github.com/etcd-io/etcd/pull/10094) Prometheus metric.
- Add [`etcd_server_heartbeat_send_failures_total`](https://github.com/etcd-io/etcd/pull/9761) Prometheus metric.
- Add [`etcd_server_slow_apply_total`](https://github.com/etcd-io/etcd/pull/9761) Prometheus metric.
- Add [`etcd_server_slow_read_indexes_total`](https://github.com/etcd-io/etcd/pull/9897) Prometheus metric.
- Add [`etcd_server_quota_backend_bytes`](https://github.com/etcd-io/etcd/pull/9820) Prometheus metric.
- Use it with `etcd_mvcc_db_total_size_in_bytes` and `etcd_mvcc_db_total_size_in_use_in_bytes`.
- `etcd_server_quota_backend_bytes 2.147483648e+09` means current quota size is 2 GB.
- `etcd_mvcc_db_total_size_in_bytes 20480` means current physically allocated DB size is 20 KB.
- `etcd_mvcc_db_total_size_in_use_in_bytes 16384` means future DB size if defragment operation is complete.
- `etcd_mvcc_db_total_size_in_bytes - etcd_mvcc_db_total_size_in_use_in_bytes` is the number of bytes that can be saved on disk with defragment operation.
- Add [`etcd_mvcc_db_total_size_in_use_in_bytes`](https://github.com/etcd-io/etcd/pull/9256) Prometheus metric.
- Use it with `etcd_mvcc_db_total_size_in_bytes` and `etcd_mvcc_db_total_size_in_use_in_bytes`.
- `etcd_server_quota_backend_bytes 2.147483648e+09` means current quota size is 2 GB.
- `etcd_mvcc_db_total_size_in_bytes 20480` means current physically allocated DB size is 20 KB.
- `etcd_mvcc_db_total_size_in_use_in_bytes 16384` means future DB size if defragment operation is complete.
- `etcd_mvcc_db_total_size_in_bytes - etcd_mvcc_db_total_size_in_use_in_bytes` is the number of bytes that can be saved on disk with defragment operation.
- Add [`etcd_mvcc_db_open_read_transactions`](https://github.com/etcd-io/etcd/pull/10523/commits/ad80752715aaed449629369687c5fd30eb1bda76) Prometheus metric.
- Add [`etcd_snap_fsync_duration_seconds`](https://github.com/etcd-io/etcd/pull/9762) Prometheus metric.
- Add [`etcd_disk_backend_defrag_duration_seconds`](https://github.com/etcd-io/etcd/pull/9761) Prometheus metric.
- Add [`etcd_mvcc_hash_duration_seconds`](https://github.com/etcd-io/etcd/pull/9761) Prometheus metric.
- Add [`etcd_mvcc_hash_rev_duration_seconds`](https://github.com/etcd-io/etcd/pull/9761) Prometheus metric.
- Add [`etcd_debugging_disk_backend_commit_rebalance_duration_seconds`](https://github.com/etcd-io/etcd/pull/9834) Prometheus metric.
- Add [`etcd_debugging_disk_backend_commit_spill_duration_seconds`](https://github.com/etcd-io/etcd/pull/9834) Prometheus metric.
- Add [`etcd_debugging_disk_backend_commit_write_duration_seconds`](https://github.com/etcd-io/etcd/pull/9834) Prometheus metric.
- Add [`etcd_debugging_lease_granted_total`](https://github.com/etcd-io/etcd/pull/9778) Prometheus metric.
- Add [`etcd_debugging_lease_revoked_total`](https://github.com/etcd-io/etcd/pull/9778) Prometheus metric.
- Add [`etcd_debugging_lease_renewed_total`](https://github.com/etcd-io/etcd/pull/9778) Prometheus metric.
- Add [`etcd_debugging_lease_ttl_total`](https://github.com/etcd-io/etcd/pull/9778) Prometheus metric.
- Add [`etcd_network_snapshot_send_inflights_total`](https://github.com/etcd-io/etcd/pull/11009) Prometheus metric.
- Add [`etcd_network_snapshot_receive_inflights_total`](https://github.com/etcd-io/etcd/pull/11009) Prometheus metric.
- Add [`etcd_server_snapshot_apply_in_progress_total`](https://github.com/etcd-io/etcd/pull/11009) Prometheus metric.
- Add [`etcd_server_is_learner`](https://github.com/etcd-io/etcd/pull/10731) Prometheus metric.
- Add [`etcd_server_learner_promote_failures`](https://github.com/etcd-io/etcd/pull/10731) Prometheus metric.
- Add [`etcd_server_learner_promote_successes`](https://github.com/etcd-io/etcd/pull/10731) Prometheus metric.
- Increase [`etcd_debugging_mvcc_index_compaction_pause_duration_milliseconds`](https://github.com/etcd-io/etcd/pull/9762) Prometheus metric histogram upper-bound.
- Previously, highest bucket only collects requests taking 1.024 seconds or more.
- Now, highest buckets collect 1.024 seconds, 2.048 seconds, and 4.096 seconds or more.
- Fix missing [`etcd_network_peer_sent_failures_total`](https://github.com/etcd-io/etcd/pull/9437) Prometheus metric count.
- Fix [`etcd_debugging_server_lease_expired_total`](https://github.com/etcd-io/etcd/pull/9557) Prometheus metric.
- Fix [race conditions in v2 server stat collecting](https://github.com/etcd-io/etcd/pull/9562).
- Change [gRPC proxy to expose etcd server endpoint /metrics](https://github.com/etcd-io/etcd/pull/10618).
- The metrics that were exposed via the proxy were not etcd server members but instead the proxy itself.
- Fix bug where [db_compaction_total_duration_milliseconds metric incorrectly measured duration as 0](https://github.com/etcd-io/etcd/pull/10646).
- Deprecating `etcd_debugging_mvcc_db_total_size_in_bytes` Prometheus metric (to be removed in v3.5). Use [`etcd_mvcc_db_total_size_in_bytes`](https://github.com/etcd-io/etcd/pull/9819) instead.
- Deprecating `etcd_debugging_mvcc_put_total` Prometheus metric (to be removed in v3.5). Use [`etcd_mvcc_put_total`](https://github.com/etcd-io/etcd/pull/10962) instead.
- Deprecating `etcd_debugging_mvcc_delete_total` Prometheus metric (to be removed in v3.5). Use [`etcd_mvcc_delete_total`](https://github.com/etcd-io/etcd/pull/10962) instead.
- Deprecating `etcd_debugging_mvcc_range_total` Prometheus metric (to be removed in v3.5). Use [`etcd_mvcc_range_total`](https://github.com/etcd-io/etcd/pull/10968) instead.
- Deprecating `etcd_debugging_mvcc_txn_total`Prometheus metric (to be removed in v3.5). Use [`etcd_mvcc_txn_total`](https://github.com/etcd-io/etcd/pull/10968) instead.
### Security, Authentication
See [security doc](https://etcd.io/docs/latest/op-guide/security/) for more details.
- Support TLS cipher suite whitelisting.
- To block [weak cipher suites](https://github.com/etcd-io/etcd/issues/8320).
- TLS handshake fails when client hello is requested with invalid cipher suites.
- Add [`etcd --cipher-suites`](https://github.com/etcd-io/etcd/pull/9801) flag.
- If empty, Go auto-populates the list.
- Add [`etcd --host-whitelist`](https://github.com/etcd-io/etcd/pull/9372) flag, [`etcdserver.Config.HostWhitelist`](https://github.com/etcd-io/etcd/pull/9372), and [`embed.Config.HostWhitelist`](https://github.com/etcd-io/etcd/pull/9372), to prevent ["DNS Rebinding"](https://en.wikipedia.org/wiki/DNS_rebinding) attack.
- Any website can simply create an authorized DNS name, and direct DNS to `"localhost"` (or any other address). Then, all HTTP endpoints of etcd server listening on `"localhost"` becomes accessible, thus vulnerable to [DNS rebinding attacks (CVE-2018-5702)](https://bugs.chromium.org/p/project-zero/issues/detail?id=1447#c2).
- Client origin enforce policy works as follow:
- If client connection is secure via HTTPS, allow any hostnames..
- If client connection is not secure and `"HostWhitelist"` is not empty, only allow HTTP requests whose Host field is listed in whitelist.
- By default, `"HostWhitelist"` is `"*"`, which means insecure server allows all client HTTP requests.
- Note that the client origin policy is enforced whether authentication is enabled or not, for tighter controls.
- When specifying hostnames, loopback addresses are not added automatically. To allow loopback interfaces, add them to whitelist manually (e.g. `"localhost"`, `"127.0.0.1"`, etc.).
- e.g. `etcd --host-whitelist example.com`, then the server will reject all HTTP requests whose Host field is not `example.com` (also rejects requests to `"localhost"`).
- Support [`etcd --cors`](https://github.com/etcd-io/etcd/pull/9490) in v3 HTTP requests (gRPC gateway).
- Support [`ttl` field for `etcd` Authentication JWT token](https://github.com/etcd-io/etcd/pull/8302).
- e.g. `etcd --auth-token jwt,pub-key=,priv-key=,sign-method=,ttl=5m`.
- Allow empty token provider in [`etcdserver.ServerConfig.AuthToken`](https://github.com/etcd-io/etcd/pull/9369).
- Fix [TLS reload](https://github.com/etcd-io/etcd/pull/9570) when [certificate SAN field only includes IP addresses but no domain names](https://github.com/etcd-io/etcd/issues/9541).
- In Go, server calls `(*tls.Config).GetCertificate` for TLS reload if and only if server's `(*tls.Config).Certificates` field is not empty, or `(*tls.ClientHelloInfo).ServerName` is not empty with a valid SNI from the client. Previously, etcd always populates `(*tls.Config).Certificates` on the initial client TLS handshake, as non-empty. Thus, client was always expected to supply a matching SNI in order to pass the TLS verification and to trigger `(*tls.Config).GetCertificate` to reload TLS assets.
- However, a certificate whose SAN field does [not include any domain names but only IP addresses](https://github.com/etcd-io/etcd/issues/9541) would request `*tls.ClientHelloInfo` with an empty `ServerName` field, thus failing to trigger the TLS reload on initial TLS handshake; this becomes a problem when expired certificates need to be replaced online.
- Now, `(*tls.Config).Certificates` is created empty on initial TLS client handshake, first to trigger `(*tls.Config).GetCertificate`, and then to populate rest of the certificates on every new TLS connection, even when client SNI is empty (e.g. cert only includes IPs).
### etcd server
- Add [`rpctypes.ErrLeaderChanged`](https://github.com/etcd-io/etcd/pull/10094).
- Now linearizable requests with read index would fail fast when there is a leadership change, instead of waiting until context timeout.
- Add [`etcd --initial-election-tick-advance`](https://github.com/etcd-io/etcd/pull/9591) flag to configure initial election tick fast-forward.
- By default, `etcd --initial-election-tick-advance=true`, then local member fast-forwards election ticks to speed up "initial" leader election trigger.
- This benefits the case of larger election ticks. For instance, cross datacenter deployment may require longer election timeout of 10-second. If true, local node does not need wait up to 10-second. Instead, forwards its election ticks to 8-second, and have only 2-second left before leader election.
- Major assumptions are that: cluster has no active leader thus advancing ticks enables faster leader election. Or cluster already has an established leader, and rejoining follower is likely to receive heartbeats from the leader after tick advance and before election timeout.
- However, when network from leader to rejoining follower is congested, and the follower does not receive leader heartbeat within left election ticks, disruptive election has to happen thus affecting cluster availabilities.
- Now, this can be disabled by setting `etcd --initial-election-tick-advance=false`.
- Disabling this would slow down initial bootstrap process for cross datacenter deployments. Make tradeoffs by configuring `etcd --initial-election-tick-advance` at the cost of slow initial bootstrap.
- If single-node, it advances ticks regardless.
- Address [disruptive rejoining follower node](https://github.com/etcd-io/etcd/issues/9333).
- Add [`etcd --pre-vote`](https://github.com/etcd-io/etcd/pull/9352) flag to enable to run an additional Raft election phase.
- For instance, a flaky(or rejoining) member may drop in and out, and start campaign. This member will end up with a higher term, and ignore all incoming messages with lower term. In this case, a new leader eventually need to get elected, thus disruptive to cluster availability. Raft implements Pre-Vote phase to prevent this kind of disruptions. If enabled, Raft runs an additional phase of election to check if pre-candidate can get enough votes to win an election.
- `etcd --pre-vote=false` by default.
- v3.5 will enable `etcd --pre-vote=true` by default.
- Add `etcd --experimental-compaction-batch-limit` to [sets the maximum revisions deleted in each compaction batch](https://github.com/etcd-io/etcd/pull/11034).
- Reduced default compaction batch size from 10k revisions to 1k revisions to improve p99 latency during compactions and reduced wait between compactions from 100ms to 10ms.
- Add [`etcd --discovery-srv-name`](https://github.com/etcd-io/etcd/pull/8690) flag to support custom DNS SRV name with discovery.
- If not given, etcd queries `_etcd-server-ssl._tcp.[YOUR_HOST]` and `_etcd-server._tcp.[YOUR_HOST]`.
- If `etcd --discovery-srv-name="foo"`, then query `_etcd-server-ssl-foo._tcp.[YOUR_HOST]` and `_etcd-server-foo._tcp.[YOUR_HOST]`.
- Useful for operating multiple etcd clusters under the same domain.
- Support TLS cipher suite whitelisting.
- To block [weak cipher suites](https://github.com/etcd-io/etcd/issues/8320).
- TLS handshake fails when client hello is requested with invalid cipher suites.
- Add [`etcd --cipher-suites`](https://github.com/etcd-io/etcd/pull/9801) flag.
- If empty, Go auto-populates the list.
- Support [`etcd --cors`](https://github.com/etcd-io/etcd/pull/9490) in v3 HTTP requests (gRPC gateway).
- Rename [`etcd --log-output` to `etcd --log-outputs`](https://github.com/etcd-io/etcd/pull/9624) to support multiple log outputs.
- **`etcd --log-output` will be deprecated in v3.5**.
- Add [`etcd --logger`](https://github.com/etcd-io/etcd/pull/9572) flag to support [structured logger and multiple log outputs](https://github.com/etcd-io/etcd/issues/9438) in server-side.
- **`etcd --logger=capnslog` will be deprecated in v3.5**.
- Main motivation is to promote automated etcd monitoring, rather than looking back server logs when it starts breaking. Future development will make etcd log as few as possible, and make etcd easier to monitor with metrics and alerts.
- `etcd --logger=capnslog --log-outputs=default` is the default setting and same as previous etcd server logging format.
- `etcd --logger=zap --log-outputs=default` is not supported when `etcd --logger=zap`.
- Use `etcd --logger=zap --log-outputs=stderr` instead.
- Or, use `etcd --logger=zap --log-outputs=systemd/journal` to send logs to the local systemd journal.
- Previously, if etcd parent process ID (PPID) is 1 (e.g. run with systemd), `etcd --logger=capnslog --log-outputs=default` redirects server logs to local systemd journal. And if write to journald fails, it writes to `os.Stderr` as a fallback.
- However, even with PPID 1, it can fail to dial systemd journal (e.g. run embedded etcd with Docker container). Then, [every single log write will fail](https://github.com/etcd-io/etcd/pull/9729) and fall back to `os.Stderr`, which is inefficient.
- To avoid this problem, systemd journal logging must be configured manually.
- `etcd --logger=zap --log-outputs=stderr` will log server operations in [JSON-encoded format](https://godoc.org/go.uber.org/zap#NewProductionEncoderConfig) and writes logs to `os.Stderr`. Use this to override journald log redirects.
- `etcd --logger=zap --log-outputs=stdout` will log server operations in [JSON-encoded format](https://godoc.org/go.uber.org/zap#NewProductionEncoderConfig) and writes logs to `os.Stdout` Use this to override journald log redirects.
- `etcd --logger=zap --log-outputs=a.log` will log server operations in [JSON-encoded format](https://godoc.org/go.uber.org/zap#NewProductionEncoderConfig) and writes logs to the specified file `a.log`.
- `etcd --logger=zap --log-outputs=a.log,b.log,c.log,stdout` [writes server logs to multiple files `a.log`, `b.log` and `c.log` at the same time](https://github.com/etcd-io/etcd/pull/9579) and outputs to `os.Stderr`, in [JSON-encoded format](https://godoc.org/go.uber.org/zap#NewProductionEncoderConfig).
- `etcd --logger=zap --log-outputs=/dev/null` will discard all server logs.
- Add [`etcd --log-level`](https://github.com/etcd-io/etcd/pull/10947) flag to support log level.
- v3.5 will deprecate `etcd --debug` flag in favor of `etcd --log-level=debug`.
- Add [`etcd --backend-batch-limit`](https://github.com/etcd-io/etcd/pull/10283) flag.
- Add [`etcd --backend-batch-interval`](https://github.com/etcd-io/etcd/pull/10283) flag.
- Fix [`mvcc` "unsynced" watcher restore operation](https://github.com/etcd-io/etcd/pull/9281).
- "unsynced" watcher is watcher that needs to be in sync with events that have happened.
- That is, "unsynced" watcher is the slow watcher that was requested on old revision.
- "unsynced" watcher restore operation was not correctly populating its underlying watcher group.
- Which possibly causes [missing events from "unsynced" watchers](https://github.com/etcd-io/etcd/issues/9086).
- A node gets network partitioned with a watcher on a future revision, and falls behind receiving a leader snapshot after partition gets removed. When applying this snapshot, etcd watch storage moves current synced watchers to unsynced since sync watchers might have become stale during network partition. And reset synced watcher group to restart watcher routines. Previously, there was a bug when moving from synced watcher group to unsynced, thus client would miss events when the watcher was requested to the network-partitioned node.
- Fix [`mvcc` server panic from restore operation](https://github.com/etcd-io/etcd/pull/9775).
- Let's assume that a watcher had been requested with a future revision X and sent to node A that became network-partitioned thereafter. Meanwhile, cluster makes progress. Then when the partition gets removed, the leader sends a snapshot to node A. Previously if the snapshot's latest revision is still lower than the watch revision X, **etcd server panicked** during snapshot restore operation.
- Now, this server-side panic has been fixed.
- Fix [server panic on invalid Election Proclaim/Resign HTTP(S) requests](https://github.com/etcd-io/etcd/pull/9379).
- Previously, wrong-formatted HTTP requests to Election API could trigger panic in etcd server.
- e.g. `curl -L http://localhost:2379/v3/election/proclaim -X POST -d '{"value":""}'`, `curl -L http://localhost:2379/v3/election/resign -X POST -d '{"value":""}'`.
- Fix [revision-based compaction retention parsing](https://github.com/etcd-io/etcd/pull/9339).
- Previously, `etcd --auto-compaction-mode revision --auto-compaction-retention 1` was [translated to revision retention 3600000000000](https://github.com/etcd-io/etcd/issues/9337).
- Now, `etcd --auto-compaction-mode revision --auto-compaction-retention 1` is correctly parsed as revision retention 1.
- Prevent [overflow by large `TTL` values for `Lease` `Grant`](https://github.com/etcd-io/etcd/pull/9399).
- `TTL` parameter to `Grant` request is unit of second.
- Leases with too large `TTL` values exceeding `math.MaxInt64` [expire in unexpected ways](https://github.com/etcd-io/etcd/issues/9374).
- Server now returns `rpctypes.ErrLeaseTTLTooLarge` to client, when the requested `TTL` is larger than *9,000,000,000 seconds* (which is >285 years).
- Again, etcd `Lease` is meant for short-periodic keepalives or sessions, in the range of seconds or minutes. Not for hours or days!
- Fix [expired lease revoke](https://github.com/etcd-io/etcd/pull/10693).
- Fix ["the key is not deleted when the bound lease expires"](https://github.com/etcd-io/etcd/issues/10686).
- Enable etcd server [`raft.Config.CheckQuorum` when starting with `ForceNewCluster`](https://github.com/etcd-io/etcd/pull/9347).
- Allow [non-WAL files in `etcd --wal-dir` directory](https://github.com/etcd-io/etcd/pull/9743).
- Previously, existing files such as [`lost+found`](https://github.com/etcd-io/etcd/issues/7287) in WAL directory prevent etcd server boot.
- Now, WAL directory that contains only `lost+found` or a file that's not suffixed with `.wal` is considered non-initialized.
- Fix [`ETCD_CONFIG_FILE` env variable parsing in `etcd`](https://github.com/etcd-io/etcd/pull/10762).
- Fix [race condition in `rafthttp` transport pause/resume](https://github.com/etcd-io/etcd/pull/10826).
- Fix [server crash from creating an empty role](https://github.com/etcd-io/etcd/pull/10907).
- Previously, creating a role with an empty name crashed etcd server with an error code `Unavailable`.
- Now, creating a role with an empty name is not allowed with an error code `InvalidArgument`.
### API
- Add `isLearner` field to `etcdserverpb.Member`, `etcdserverpb.MemberAddRequest` and `etcdserverpb.StatusResponse` as part of [raft learner implementation](https://github.com/etcd-io/etcd/pull/10725).
- Add `MemberPromote` rpc to `etcdserverpb.Cluster` interface and the corresponding `MemberPromoteRequest` and `MemberPromoteResponse` as part of [raft learner implementation](https://github.com/etcd-io/etcd/pull/10725).
- Add [`snapshot`](https://github.com/etcd-io/etcd/pull/9118) package for snapshot restore/save operations (see [`godoc.org/github.com/etcd/clientv3/snapshot`](https://godoc.org/github.com/coreos/etcd/clientv3/snapshot) for more).
- Add [`watch_id` field to `etcdserverpb.WatchCreateRequest`](https://github.com/etcd-io/etcd/pull/9065) to allow user-provided watch ID to `mvcc`.
- Corresponding `watch_id` is returned via `etcdserverpb.WatchResponse`, if any.
- Add [`fragment` field to `etcdserverpb.WatchCreateRequest`](https://github.com/etcd-io/etcd/pull/9291) to request etcd server to [split watch events](https://github.com/etcd-io/etcd/issues/9294) when the total size of events exceeds `etcd --max-request-bytes` flag value plus gRPC-overhead 512 bytes.
- The default server-side request bytes limit is `embed.DefaultMaxRequestBytes` which is 1.5 MiB plus gRPC-overhead 512 bytes.
- If watch response events exceed this server-side request limit and watch request is created with `fragment` field `true`, the server will split watch events into a set of chunks, each of which is a subset of watch events below server-side request limit.
- Useful when client-side has limited bandwidths.
- For example, watch response contains 10 events, where each event is 1 MiB. And server `etcd --max-request-bytes` flag value is 1 MiB. Then, server will send 10 separate fragmented events to the client.
- For example, watch response contains 5 events, where each event is 2 MiB. And server `etcd --max-recv-bytes` flag value is 1 MiB and `clientv3.Config.MaxCallRecvMsgSize` is 1 MiB. Then, server will try to send 5 separate fragmented events to the client, and the client will error with `"code = ResourceExhausted desc = grpc: received message larger than max (...)"`.
- Client must implement fragmented watch event merge (which `clientv3` does in etcd v3.4).
- Add [`raftAppliedIndex` field to `etcdserverpb.StatusResponse`](https://github.com/etcd-io/etcd/pull/9176) for current Raft applied index.
- Add [`errors` field to `etcdserverpb.StatusResponse`](https://github.com/etcd-io/etcd/pull/9206) for server-side error.
- e.g. `"etcdserver: no leader", "NOSPACE", "CORRUPT"`
- Add [`dbSizeInUse` field to `etcdserverpb.StatusResponse`](https://github.com/etcd-io/etcd/pull/9256) for actual DB size after compaction.
- Add [`WatchRequest.WatchProgressRequest`](https://github.com/etcd-io/etcd/pull/9869).
- To manually trigger broadcasting watch progress event (empty watch response with latest header) to all associated watch streams.
- Think of it as `WithProgressNotify` that can be triggered manually.
Note: **v3.5 will deprecate `etcd --log-package-levels` flag for `capnslog`**; `etcd --logger=zap --log-outputs=stderr` will the default. **v3.5 will deprecate `[CLIENT-URL]/config/local/log` endpoint.**
### Package `embed`
- Add [`embed.Config.CipherSuites`](https://github.com/etcd-io/etcd/pull/9801) to specify a list of supported cipher suites for TLS handshake between client/server and peers.
- If empty, Go auto-populates the list.
- Both `embed.Config.ClientTLSInfo.CipherSuites` and `embed.Config.CipherSuites` cannot be non-empty at the same time.
- If not empty, specify either `embed.Config.ClientTLSInfo.CipherSuites` or `embed.Config.CipherSuites`.
- Add [`embed.Config.InitialElectionTickAdvance`](https://github.com/etcd-io/etcd/pull/9591) to enable/disable initial election tick fast-forward.
- `embed.NewConfig()` would return `*embed.Config` with `InitialElectionTickAdvance` as true by default.
- Define [`embed.CompactorModePeriodic`](https://godoc.org/github.com/etcd-io/etcd/embed#pkg-variables) for `compactor.ModePeriodic`.
- Define [`embed.CompactorModeRevision`](https://godoc.org/github.com/etcd-io/etcd/embed#pkg-variables) for `compactor.ModeRevision`.
- Change [`embed.Config.CorsInfo` in `*cors.CORSInfo` type to `embed.Config.CORS` in `map[string]struct{}` type](https://github.com/etcd-io/etcd/pull/9490).
- Remove [`embed.Config.SetupLogging`](https://github.com/etcd-io/etcd/pull/9572).
- Now logger is set up automatically based on [`embed.Config.Logger`, `embed.Config.LogOutputs`, `embed.Config.Debug` fields](https://github.com/etcd-io/etcd/pull/9572).
- Add [`embed.Config.Logger`](https://github.com/etcd-io/etcd/pull/9518) to support [structured logger `zap`](https://github.com/uber-go/zap) in server-side.
- Add [`embed.Config.LogLevel`](https://github.com/etcd-io/etcd/pull/10947).
- Rename `embed.Config.SnapCount` field to [`embed.Config.SnapshotCount`](https://github.com/etcd-io/etcd/pull/9745), to be consistent with the flag name `etcd --snapshot-count`.
- Rename [**`embed.Config.LogOutput`** to **`embed.Config.LogOutputs`**](https://github.com/etcd-io/etcd/pull/9624) to support multiple log outputs.
- Change [**`embed.Config.LogOutputs`** type from `string` to `[]string`](https://github.com/etcd-io/etcd/pull/9579) to support multiple log outputs.
- Add [`embed.Config.BackendBatchLimit`](https://github.com/etcd-io/etcd/pull/10283) field.
- Add [`embed.Config.BackendBatchInterval`](https://github.com/etcd-io/etcd/pull/10283) field.
- Make [`embed.DefaultEnableV2` `false` default](https://github.com/etcd-io/etcd/pull/10935).
### Package `pkg/adt`
- Change [`pkg/adt.IntervalTree` from `struct` to `interface`](https://github.com/etcd-io/etcd/pull/10959).
- See [`pkg/adt` README](https://github.com/etcd-io/etcd/tree/main/pkg/adt) and [`pkg/adt` godoc](https://godoc.org/go.etcd.io/etcd/pkg/adt).
- Improve [`pkg/adt.IntervalTree` test coverage](https://github.com/etcd-io/etcd/pull/10959).
- See [`pkg/adt` README](https://github.com/etcd-io/etcd/tree/main/pkg/adt) and [`pkg/adt` godoc](https://godoc.org/go.etcd.io/etcd/pkg/adt).
- Fix [Red-Black tree to maintain black-height property](https://github.com/etcd-io/etcd/pull/10978).
- Previously, delete operation violates [black-height property](https://github.com/etcd-io/etcd/issues/10965).
### Package `integration`
- Add [`CLUSTER_DEBUG` to enable test cluster logging](https://github.com/etcd-io/etcd/pull/9678).
- Deprecated `capnslog` in integration tests.
### client v3
- Add [`MemberAddAsLearner`](https://github.com/etcd-io/etcd/pull/10725) to `Clientv3.Cluster` interface. This API is used to add a learner member to etcd cluster.
- Add [`MemberPromote`](https://github.com/etcd-io/etcd/pull/10727) to `Clientv3.Cluster` interface. This API is used to promote a learner member in etcd cluster.
- Client may receive [`rpctypes.ErrLeaderChanged`](https://github.com/etcd-io/etcd/pull/10094) from server.
- Now linearizable requests with read index would fail fast when there is a leadership change, instead of waiting until context timeout.
- Add [`WithFragment` `OpOption`](https://github.com/etcd-io/etcd/pull/9291) to support [watch events fragmentation](https://github.com/etcd-io/etcd/issues/9294) when the total size of events exceeds `etcd --max-request-bytes` flag value plus gRPC-overhead 512 bytes.
- Watch fragmentation is disabled by default.
- The default server-side request bytes limit is `embed.DefaultMaxRequestBytes` which is 1.5 MiB plus gRPC-overhead 512 bytes.
- If watch response events exceed this server-side request limit and watch request is created with `fragment` field `true`, the server will split watch events into a set of chunks, each of which is a subset of watch events below server-side request limit.
- Useful when client-side has limited bandwidths.
- For example, watch response contains 10 events, where each event is 1 MiB. And server `etcd --max-request-bytes` flag value is 1 MiB. Then, server will send 10 separate fragmented events to the client.
- For example, watch response contains 5 events, where each event is 2 MiB. And server `etcd --max-request-bytes` flag value is 1 MiB and `clientv3.Config.MaxCallRecvMsgSize` is 1 MiB. Then, server will try to send 5 separate fragmented events to the client, and the client will error with `"code = ResourceExhausted desc = grpc: received message larger than max (...)"`.
- Add [`Watcher.RequestProgress` method](https://github.com/etcd-io/etcd/pull/9869).
- To manually trigger broadcasting watch progress event (empty watch response with latest header) to all associated watch streams.
- Think of it as `WithProgressNotify` that can be triggered manually.
- Fix [lease keepalive interval updates when response queue is full](https://github.com/etcd-io/etcd/pull/9952).
- If `<-chan *clientv3LeaseKeepAliveResponse` from `clientv3.Lease.KeepAlive` was never consumed or channel is full, client was [sending keepalive request every 500ms](https://github.com/etcd-io/etcd/issues/9911) instead of expected rate of every "TTL / 3" duration.
- Change [snapshot file permissions](https://github.com/etcd-io/etcd/pull/9977): On Linux, the snapshot file changes from readable by all (mode 0644) to readable by the user only (mode 0600).
- Client may choose to send keepalive pings to server using [`PermitWithoutStream`](https://github.com/etcd-io/etcd/pull/10146).
- By setting `PermitWithoutStream` to true, client can send keepalive pings to server without any active streams(RPCs). In other words, it allows sending keepalive pings with unary or simple RPC calls.
- `PermitWithoutStream` is set to false by default.
- Fix logic on [release lock key if cancelled](https://github.com/etcd-io/etcd/pull/10153) in `clientv3/concurrency` package.
- Fix [`(*Client).Endpoints()` method race condition](https://github.com/etcd-io/etcd/pull/10595).
- Deprecated [`grpc.ErrClientConnClosing`](https://github.com/etcd-io/etcd/pull/10981).
- `clientv3` and `proxy/grpcproxy` now does not return `grpc.ErrClientConnClosing`.
- `grpc.ErrClientConnClosing` has been [deprecated in gRPC >= 1.10](https://github.com/grpc/grpc-go/pull/1854).
- Use `clientv3.IsConnCanceled(error)` or `google.golang.org/grpc/status.FromError(error)` instead.
### etcdctl v3
- Make [`ETCDCTL_API=3 etcdctl` default](https://github.com/etcd-io/etcd/issues/9600).
- Now, `etcdctl set foo bar` must be `ETCDCTL_API=2 etcdctl set foo bar`.
- Now, `ETCDCTL_API=3 etcdctl put foo bar` could be just `etcdctl put foo bar`.
- Add [`etcdctl member add --learner` and `etcdctl member promote`](https://github.com/etcd-io/etcd/pull/10725) to add and promote raft learner member in etcd cluster.
- Add [`etcdctl --password`](https://github.com/etcd-io/etcd/pull/9730) flag.
- To support [`:` character in user name](https://github.com/etcd-io/etcd/issues/9691).
- e.g. `etcdctl --user user --password password get foo`
- Add [`etcdctl user add --new-user-password`](https://github.com/etcd-io/etcd/pull/9730) flag.
- Add [`etcdctl check datascale`](https://github.com/etcd-io/etcd/pull/9185) command.
- Add [`etcdctl check datascale --auto-compact, --auto-defrag`](https://github.com/etcd-io/etcd/pull/9351) flags.
- Add [`etcdctl check perf --auto-compact, --auto-defrag`](https://github.com/etcd-io/etcd/pull/9330) flags.
- Add [`etcdctl defrag --cluster`](https://github.com/etcd-io/etcd/pull/9390) flag.
- Add ["raft applied index" field to `endpoint status`](https://github.com/etcd-io/etcd/pull/9176).
- Add ["errors" field to `endpoint status`](https://github.com/etcd-io/etcd/pull/9206).
- Add [`etcdctl endpoint health --write-out` support](https://github.com/etcd-io/etcd/pull/9540).
- Previously, [`etcdctl endpoint health --write-out json` did not work](https://github.com/etcd-io/etcd/issues/9532).
- Add [missing newline in `etcdctl endpoint health`](https://github.com/etcd-io/etcd/pull/10793).
- Fix [`etcdctl watch [key] [range_end] -- [exec-command…]`](https://github.com/etcd-io/etcd/pull/9688) parsing.
- Previously, `ETCDCTL_API=3 etcdctl watch foo -- echo watch event received` panicked.
- Fix [`etcdctl move-leader` command for TLS-enabled endpoints](https://github.com/etcd-io/etcd/pull/9807).
- Add [`progress` command to `etcdctl watch --interactive`](https://github.com/etcd-io/etcd/pull/9869).
- To manually trigger broadcasting watch progress event (empty watch response with latest header) to all associated watch streams.
- Think of it as `WithProgressNotify` that can be triggered manually.
- Add [timeout](https://github.com/etcd-io/etcd/pull/10301) to `etcdctl snapshot
save`.
- User can specify timeout of `etcdctl snapshot save` command using flag `--command-timeout`.
- Fix etcdctl to [strip out insecure endpoints from DNS SRV records when using discovery](https://github.com/etcd-io/etcd/pull/10443)
### gRPC proxy
- Fix [etcd server panic from restore operation](https://github.com/etcd-io/etcd/pull/9775).
- Let's assume that a watcher had been requested with a future revision X and sent to node A that became network-partitioned thereafter. Meanwhile, cluster makes progress. Then when the partition gets removed, the leader sends a snapshot to node A. Previously if the snapshot's latest revision is still lower than the watch revision X, **etcd server panicked** during snapshot restore operation.
- Especially, gRPC proxy was affected, since it detects a leader loss with a key `"proxy-namespace__lostleader"` and a watch revision `"int64(math.MaxInt64 - 2)"`.
- Now, this server-side panic has been fixed.
- Fix [memory leak in cache layer](https://github.com/etcd-io/etcd/pull/10327).
- Change [gRPC proxy to expose etcd server endpoint /metrics](https://github.com/etcd-io/etcd/pull/10618).
- The metrics that were exposed via the proxy were not etcd server members but instead the proxy itself.
### gRPC gateway
- Replace [gRPC gateway](https://github.com/grpc-ecosystem/grpc-gateway) endpoint `/v3beta` with [`/v3`](https://github.com/etcd-io/etcd/pull/9298).
- Deprecated [`/v3alpha`](https://github.com/etcd-io/etcd/pull/9298).
- To deprecate [`/v3beta`](https://github.com/etcd-io/etcd/issues/9189) in v3.5.
- In v3.4, `curl -L http://localhost:2379/v3beta/kv/put -X POST -d '{"key": "Zm9v", "value": "YmFy"}'` still works as a fallback to `curl -L http://localhost:2379/v3/kv/put -X POST -d '{"key": "Zm9v", "value": "YmFy"}'`, but `curl -L http://localhost:2379/v3beta/kv/put -X POST -d '{"key": "Zm9v", "value": "YmFy"}'` won't work in v3.5. Use `curl -L http://localhost:2379/v3/kv/put -X POST -d '{"key": "Zm9v", "value": "YmFy"}'` instead.
- Add API endpoints [`/{v3beta,v3}/lease/leases, /{v3beta,v3}/lease/revoke, /{v3beta,v3}/lease/timetolive`](https://github.com/etcd-io/etcd/pull/9450).
- To deprecate [`/{v3beta,v3}/kv/lease/leases, /{v3beta,v3}/kv/lease/revoke, /{v3beta,v3}/kv/lease/timetolive`](https://github.com/etcd-io/etcd/issues/9430) in v3.5.
- Support [`etcd --cors`](https://github.com/etcd-io/etcd/pull/9490) in v3 HTTP requests (gRPC gateway).
### Package `raft`
- Fix [deadlock during PreVote migration process](https://github.com/etcd-io/etcd/pull/8525).
- Add [`raft.ErrProposalDropped`](https://github.com/etcd-io/etcd/pull/9067).
- Now [`(r *raft) Step` returns `raft.ErrProposalDropped`](https://github.com/etcd-io/etcd/pull/9137) if a proposal has been ignored.
- e.g. a node is removed from cluster, or [`raftpb.MsgProp` arrives at current leader while there is an ongoing leadership transfer](https://github.com/etcd-io/etcd/issues/8975).
- Improve [Raft `becomeLeader` and `stepLeader`](https://github.com/etcd-io/etcd/pull/9073) by keeping track of latest `pb.EntryConfChange` index.
- Previously record `pendingConf` boolean field scanning the entire tail of the log, which can delay heartbeat send.
- Fix [missing learner nodes on `(n *node) ApplyConfChange`](https://github.com/etcd-io/etcd/pull/9116).
- Add [`raft.Config.MaxUncommittedEntriesSize`](https://github.com/etcd-io/etcd/pull/10167) to limit the total size of the uncommitted entries in bytes.
- Once exceeded, raft returns `raft.ErrProposalDropped` error.
- Prevent [unbounded Raft log growth](https://github.com/cockroachdb/cockroach/issues/27772).
- There was a bug in [PR#10167](https://github.com/etcd-io/etcd/pull/10167) but fixed via [PR#10199](https://github.com/etcd-io/etcd/pull/10199).
- Add [`raft.Ready.CommittedEntries` pagination using `raft.Config.MaxSizePerMsg`](https://github.com/etcd-io/etcd/pull/9982).
- This prevents out-of-memory errors if the raft log has become very large and commits all at once.
- Fix [correctness bug in CommittedEntries pagination](https://github.com/etcd-io/etcd/pull/10063).
- Optimize [message send flow control](https://github.com/etcd-io/etcd/pull/9985).
- Leader now sends more append entries if it has more non-empty entries to send after updating flow control information.
- Now, Raft allows multiple in-flight append messages.
- Optimize [memory allocation when boxing slice in `maybeCommit`](https://github.com/etcd-io/etcd/pull/10679).
- By boxing a heap-allocated slice header instead of the slice header on the stack, we can avoid an allocation when passing through the sort.Interface interface.
- Avoid [memory allocation in Raft entry `String` method](https://github.com/etcd-io/etcd/pull/10680).
- Avoid [multiple memory allocations when merging stable and unstable log](https://github.com/etcd-io/etcd/pull/10684).
- Extract [progress tracking into own component](https://github.com/etcd-io/etcd/pull/10683).
- Add [package `raft/tracker`](https://github.com/etcd-io/etcd/pull/10807).
- Optimize [string representation of `Progress`](https://github.com/etcd-io/etcd/pull/10882).
- Make [relationship between `node` and `RawNode` explicit](https://github.com/etcd-io/etcd/pull/10803).
- Prevent [learners from becoming leader](https://github.com/etcd-io/etcd/pull/10822).
- Add [package `raft/quorum` to reason about committed indexes as well as vote outcomes for both majority and joint quorums](https://github.com/etcd-io/etcd/pull/10779).
- Bundle [Voters and Learner into `raft/tracker.Config` struct](https://github.com/etcd-io/etcd/pull/10865).
- Use [membership sets in progress tracking](https://github.com/etcd-io/etcd/pull/10779).
- Implement [joint quorum computation](https://github.com/etcd-io/etcd/pull/10779).
- Refactor [`raft/node.go` to centralize configuration change application](https://github.com/etcd-io/etcd/pull/10865).
- Allow [voter to become learner through snapshot](https://github.com/etcd-io/etcd/pull/10864).
- Add [package `raft/confchange` to internally support joint consensus](https://github.com/etcd-io/etcd/pull/10779).
- Use [`RawNode` for node's event loop](https://github.com/etcd-io/etcd/pull/10892).
- Add [`RawNode.Bootstrap` method](https://github.com/etcd-io/etcd/pull/10892).
- Add [`raftpb.ConfChangeV2` to use joint quorums](https://github.com/etcd-io/etcd/pull/10914).
- `raftpb.ConfChange` continues to work as today: it allows carrying out a single configuration change. A `pb.ConfChange` proposal gets added to the Raft log as such and is thus also observed by the app during Ready handling, and fed back to ApplyConfChange.
- `raftpb.ConfChangeV2` allows joint configuration changes but will continue to carry out configuration changes in "one phase" (i.e. without ever entering a joint config) when this is possible.
- `raftpb.ConfChangeV2` messages initiate configuration changes. They support both the simple "one at a time" membership change protocol and full Joint Consensus allowing for arbitrary changes in membership.
- Change [`raftpb.ConfState.Nodes` to `raftpb.ConfState.Voters`](https://github.com/etcd-io/etcd/pull/10914).
- Allow [learners to vote, but still learners do not count in quorum](https://github.com/etcd-io/etcd/pull/10998).
- necessary in the situation in which a learner has been promoted (i.e. is now a voter) but has not learned about this yet.
- Fix [restoring joint consensus](https://github.com/etcd-io/etcd/pull/11003).
- Visit [`Progress` in stable order](https://github.com/etcd-io/etcd/pull/11004).
- Proactively [probe newly added followers](https://github.com/etcd-io/etcd/pull/11037).
- The general expectation in `tracker.Progress.Next == c.LastIndex` is that the follower has no log at all (and will thus likely need a snapshot), though the app may have applied a snapshot out of band before adding the replica (thus making the first index the better choice).
- Previously, when the leader applied a new configuration that added voters, it would not immediately probe these voters, delaying when they would be caught up.
### Package `wal`
- Add [`Verify` function to perform corruption check on WAL contents](https://github.com/etcd-io/etcd/pull/10603).
- Fix [`wal` directory cleanup on creation failures](https://github.com/etcd-io/etcd/pull/10689).
### Tooling
- Add [`etcd-dump-logs --entry-type`](https://github.com/etcd-io/etcd/pull/9628) flag to support WAL log filtering by entry type.
- Add [`etcd-dump-logs --stream-decoder`](https://github.com/etcd-io/etcd/pull/9790) flag to support custom decoder.
- Add [`SHA256SUMS`](https://github.com/etcd-io/etcd/pull/11087) file to release assets.
- etcd maintainers are a distributed team, this change allows for releases to be cut and validation provided without requiring a signing key.
### Go
- Require [*Go 1.12+*](https://github.com/etcd-io/etcd/pull/10045).
- Compile with [*Go 1.12.9*](https://golang.org/doc/devel/release.html#go1.12) including [*Go 1.12.8*](https://groups.google.com/d/msg/golang-announce/65QixT3tcmg/DrFiG6vvCwAJ) security fixes.
### Dockerfile
- [Rebase etcd image from Alpine to Debian](https://github.com/etcd-io/etcd/pull/10805) to improve security and maintenance effort for etcd release.
---
================================================
FILE: CHANGELOG/CHANGELOG-3.5.md
================================================
Previous change logs can be found at [CHANGELOG-3.4](https://github.com/etcd-io/etcd/blob/main/CHANGELOG/CHANGELOG-3.4.md).
---
## v3.5.28 (TBC)
### etcd server
- [Ensure the metrics interceptor runs before other interceptors so that metrics remain up to date](https://github.com/etcd-io/etcd/pull/21336)
- Fix [Race between read index and leader change](https://github.com/etcd-io/etcd/pull/21387)
- Fix [Stale reads caused by process pausing](https://github.com/etcd-io/etcd/pull/21421)
### Package `clientv3`
- [Print the endpoint the grpc request was actually sent to in unary interceptor](https://github.com/etcd-io/etcd/pull/21380)
### etcd grpc-proxy
- [server/etcdmain: fix startup deadlock in grpcproxy](https://github.com/etcd-io/etcd/pull/21356)
### etcdctl
- Fix [slice bounds trimming single-quoted args in Argify](https://github.com/etcd-io/etcd/pull/21403)
### Dependencies
- [Bump go.opentelemetry.io/otel/sdk to v1.40.0 to resolve https://pkg.go.dev/vuln/GO-2026-4394](https://github.com/etcd-io/etcd/pull/21338)
- Compile binaries using [go 1.25.7](https://github.com/etcd-io/etcd/pull/21405)
- [Bump golang.org/x/net to v0.51.0 to resolve GO-2026-4559](https://github.com/etcd-io/etcd/pull/21441)
---
## v3.5.27 (2026-02-13)
### Package `clientv3`
- [Remove the use of grpc-go's Metadata field](https://github.com/etcd-io/etcd/pull/21242)
### Dependencies
- Compile binaries using [go 1.24.13](https://github.com/etcd-io/etcd/pull/21266). This addresses [CVE-2025-61726](https://github.com/advisories/GHSA-gm9r-q53w-2gh4), [CVE-2025-61731](https://github.com/advisories/GHSA-xvqr-69v8-f3gv), and [CVE-2025-61732](https://github.com/advisories/GHSA-8jvr-vh7g-f8gx).
---
## v3.5.26 (2025-12-17)
### etcd server
- [Print token fingerprint instead of the original tokens in log messages](https://github.com/etcd-io/etcd/pull/20942)
- Fix [zombie members in v3store](https://github.com/etcd-io/etcd/pull/20995)
### etcdctl
- [Fix a typo of 'etcdctl snapshot restore' command](https://github.com/etcd-io/etcd/pull/20948).
### Dependencies
- Compile binaries using [go 1.24.11](https://github.com/etcd-io/etcd/pull/20999).
- Bump [golang.org/x/crypto to 0.45.0 to address CVE-2025-47914, and CVE-2025-58181](https://github.com/etcd-io/etcd/pull/21023).
---
## v3.5.25 (2025-11-11)
### etcd server
- Fix [`--force-new-cluster can't clean up learners after creating snapshot`](https://github.com/etcd-io/etcd/pull/20896)
### etcdutl
- Add [flag `--wal-dir` to `etcdutl check v2store` command to support dedicated WAL directory](https://github.com/etcd-io/etcd/pull/20886)
### Dependencies
- Compile binaries using [go 1.24.10](https://github.com/etcd-io/etcd/pull/20902).
---
## v3.5.24 (2025-10-22)
### etcd server
- [Reject watch request with -1 revision to prevent invalid resync behavior on uncompacted etcd](https://github.com/etcd-io/etcd/pull/20709)
- [Change the TLS handshake 'EOF' errors to DEBUG not to spam logs](https://github.com/etcd-io/etcd/pull/20751)
- Fix [Learner promotion not being persisted into v3store may be propagated across multiple upgrades](https://github.com/etcd-io/etcd/pull/20797)
### Dependencies
- Compile binaries using [go 1.24.9](https://github.com/etcd-io/etcd/pull/20806).
---
## v3.5.23 (2025-09-19)
### etcd server
- Fix [etcd may return success for leaseRenew request even when the lease is revoked](https://github.com/etcd-io/etcd/pull/20616)
- Fix [potential data corruption when applySnapshot and defragment happen concurrently](https://github.com/etcd-io/etcd/pull/20653)
### Dependencies
- Compile binaries using [go 1.24.7](https://github.com/etcd-io/etcd/pull/20665).
- [Bump bbolt to v1.3.12](https://github.com/etcd-io/etcd/pull/20514).
---
## v3.5.22 (2025-07-22)
### etcd server
- Fix [the compaction pause duration metric is not emitted for every compaction batch](https://github.com/etcd-io/etcd/pull/19771)
- Fix [mvcc: avoid double decrement of watcher gauge on close/cancel race](https://github.com/etcd-io/etcd/pull/20066)
- Fix [Watch on future revision returns old events or notifications](https://github.com/etcd-io/etcd/pull/20290)
- Fix [`--force-new-cluster` can't remove all other members in a corner case](https://github.com/etcd-io/etcd/pull/20339)
- Fix [v2store check (IsMetaStoreOnly) returns wrong result even there is no any auth data](https://github.com/etcd-io/etcd/pull/20357)
- Improve [help message for --quota-backend-bytes](https://github.com/etcd-io/etcd/pull/20380)
### Package `clientv3`
- [Replace `resolver.State.Addresses` with `resolver.State.Endpoint.Addresses`](https://github.com/etcd-io/etcd/pull/19783).
- [Deprecated the Metadata field in the Endpoint struct from the client/v3/naming/endpoints package](https://github.com/etcd-io/etcd/pull/19846).
### Dependencies
- Compile binaries using [go 1.23.11](https://github.com/etcd-io/etcd/pull/20321)
---
## v3.5.21 (2025-03-27)
### Dependencies
- Bump [github.com/golang-jwt/jwt/v4 from 4.5.1 to 4.5.2 to address CVE-2025-30204](https://github.com/etcd-io/etcd/pull/19646).
- Bump [bump golang.org/x/net from v0.36.0 to v0.38.0 to address CVE-2025-22870 and CVE-2025-22872](https://github.com/etcd-io/etcd/pull/19686).
---
## v3.5.20 (2025-03-21)
### etcd server
- Fix [the learner promotion changes not being persisted into v3store (bbolt)](https://github.com/etcd-io/etcd/pull/19563)
- Update [the RLock in Demoted method for read-only access to expiry](https://github.com/etcd-io/etcd/pull/19445)
### etcdctl
- Fix [command `etcdctl member promote` doesn't support json output](https://github.com/etcd-io/etcd/pull/19602)
### etcd grpc-proxy
- Fix [grpcproxy can get stuck in and endless loop causing high CPU usage](https://github.com/etcd-io/etcd/pull/19562)
---
## v3.5.19 (2025-03-05)
### etcd server
- Backport [add learner status check to readyz endpoint](https://github.com/etcd-io/etcd/pull/19280).
- Fix [performance regression due to uncertain compaction sleep interval](https://github.com/etcd-io/etcd/pull/19405).
### `tools/benchmark`
- Backport [add mixed read-write performance evaluation scripts](https://github.com/etcd-io/etcd/pull/19275).
### Dependencies
- Compile binaries using [go 1.23.7](https://github.com/etcd-io/etcd/pull/19528).
- Bump [golang.org/x/crypto to v0.35.0 to address CVE-2025-22869](https://github.com/etcd-io/etcd/pull/19478).
- Bump [golang.org/x/net to v0.36.0 to address CVE-2025-22870](https://github.com/etcd-io/etcd/pull/19530).
---
## v3.5.18 (2025-01-24)
### etcd server
- Avoid deadlock in etcd.Close when stopping during bootstrapping, see https://github.com/etcd-io/etcd/pull/19167 and https://github.com/etcd-io/etcd/pull/19258.
- [Print warning messages if any of the deprecated v2store related flags is set](https://github.com/etcd-io/etcd/pull/18999)
- Fix [missing delete event on watch opened on same revision as compaction request](https://github.com/etcd-io/etcd/pull/19249)
### Package `clientv3`
- Fix [runtime panic that occurs when KeepAlive is called with a Context implemented by an uncomparable type](https://github.com/etcd-io/etcd/pull/18937)
### etcdutl v3
- Add [command `etcdutl check v2store` to offline check whether v2store contains custom content](https://github.com/etcd-io/etcd/pull/19113)
### etcd grpc-proxy
- Add [`tls min/max version to grpc proxy`](https://github.com/etcd-io/etcd/pull/18829) to support setting TLS min and max version.
### Dependencies
- Bump [golang-jwt/jwt to 4.5.1 to address GO-2024-3250](https://github.com/etcd-io/etcd/pull/18899).
- Compile binaries using [go 1.22.11](https://github.com/etcd-io/etcd/pull/19211).
- Bump [golang.org/x/crypto to 0.32.0 to address CVE-2024-45337](https://github.com/etcd-io/etcd/pull/19154).
- Bump [golang.org/x/net to 0.34.0 to address CVE-2024-45338](https://github.com/etcd-io/etcd/pull/19158).
---
## v3.5.17 (2024-11-12)
### etcd server
- Fix [watchserver related goroutine leakage](https://github.com/etcd-io/etcd/pull/18784)
- Fix [risk of a partial write txn being applied](https://github.com/etcd-io/etcd/pull/18799)
- Fix [panicking occurred due to improper error handling during defragmentation](https://github.com/etcd-io/etcd/pull/18842)
- Fix [close temp file(s) in case an error happens during defragmentation](https://github.com/etcd-io/etcd/pull/18854)
### Dependencies
- Compile binaries using [go 1.22.9](https://github.com/etcd-io/etcd/pull/18849).
---
## v3.5.16 (2024-09-10)
### etcd server
- Fix [performance regression issue caused by the `ensureLeadership` in lease renew](https://github.com/etcd-io/etcd/pull/18439).
- [Keep the tombstone during compaction if it happens to be the compaction revision](https://github.com/etcd-io/etcd/pull/18474)
- Add [`etcd --experimental-compaction-sleep-interval`](https://github.com/etcd-io/etcd/pull/18514) flag to control the sleep interval between each compaction batch.
### Dependencies
- Compile binaries using [go 1.22.7](https://github.com/etcd-io/etcd/pull/18550).
- Upgrade [bbolt to v1.3.11](https://github.com/etcd-io/etcd/pull/18489).
---
## v3.5.15 (2024-07-19)
### etcd server
- Fix [add prometheus metric registration for metric `etcd_disk_wal_write_duration_seconds`](https://github.com/etcd-io/etcd/pull/18174).
- Add [Support multiple values for allowed client and peer TLS identities](https://github.com/etcd-io/etcd/pull/18160)
- Fix [noisy logs from simple auth token expiration by reducing log level to debug](https://github.com/etcd-io/etcd/pull/18245)
- [Differentiate the warning message for rejected client and peer connections](https://github.com/etcd-io/etcd/pull/18319)
### Package clientv3
- [Print gRPC metadata in guaranteed order using the official go fmt pkg](https://github.com/etcd-io/etcd/pull/18312).
### Dependencies
- Compile binaries using [go 1.21.12](https://github.com/etcd-io/etcd/pull/18271).
- [Fully address CVE-2023-45288 and fix govulncheck CI check](https://github.com/etcd-io/etcd/pull/18170)
## v3.5.14 (2024-05-29)
### etcd server
- Fix [LeaseTimeToLive returns error if leader changed](https://github.com/etcd-io/etcd/pull/17704).
- Add [metrics `etcd_disk_wal_write_duration_seconds`](https://github.com/etcd-io/etcd/pull/17616).
- Fix [ignore raft messages if member id mismatch](https://github.com/etcd-io/etcd/pull/17813).
- Update [the compaction log when bootstrap](https://github.com/etcd-io/etcd/pull/17830).
- Fix [Revision decreasing after panic during compaction](https://github.com/etcd-io/etcd/pull/17865)
- Add [`etcd --experimental-stop-grpc-service-on-defrag`](https://github.com/etcd-io/etcd/pull/17914) to enable client failover on defrag.
- Add [support for `AllowedCN` and `AllowedHostname` through config file](https://github.com/etcd-io/etcd/pull/18063)
### etcdutl v3
- Add [`--initial-memory-map-size` to `snapshot restore` to avoid memory allocation issues](https://github.com/etcd-io/etcd/pull/17977)
### Package `clientv3`
- Add [requests retry when receiving ErrGPRCNotSupportedForLearner and endpoints > 1](https://github.com/etcd-io/etcd/pull/17641).
- Fix [initialization for mu in client context](https://github.com/etcd-io/etcd/pull/17699).
### Dependencies
- Compile binaries using [go 1.21.10](https://github.com/etcd-io/etcd/pull/17980).
- Upgrade [bbolt to v1.3.10](https://github.com/etcd-io/etcd/pull/17943).
---
## v3.5.13 (2024-03-29)
### etcd server
- Fix leases wrongly revoked by the leader by [ignoring old leader's leases revoking request](https://github.com/etcd-io/etcd/pull/17425).
- Fix [no progress notification being sent for watch that doesn't get any events](https://github.com/etcd-io/etcd/pull/17566).
- Fix [watch event loss after compaction](https://github.com/etcd-io/etcd/pull/17612).
### Package `clientv3`
- Add [client backoff and retry config options](https://github.com/etcd-io/etcd/pull/17363).
- [Ignore SetKeepAlivePeriod errors on OpenBSD](https://github.com/etcd-io/etcd/pull/17387).
- [Support unix/unixs socket in client or peer URLs](https://github.com/etcd-io/etcd/pull/15940)
### gRPC Proxy
- Add [three flags (see below) for grpc-proxy](https://github.com/etcd-io/etcd/pull/17447)
- `--dial-keepalive-time`
- `--dial-keepalive-timeout`
- `--permit-without-stream`
### Dependencies
- Upgrade [bbolt to v1.3.9](https://github.com/etcd-io/etcd/pull/17483).
- Compile binaries using [go 1.21.8](https://github.com/etcd-io/etcd/pull/17537).
- Upgrade [google.golang.org/protobuf to v1.33.0 to address CVE-2024-24786](https://github.com/etcd-io/etcd/pull/17553).
- Upgrade github.com/sirupsen/logrus to v1.9.3 to address [PRISMA-2023-0056](https://github.com/etcd-io/etcd/pull/17482).
### Others
- [Make CGO_ENABLED configurable](https://github.com/etcd-io/etcd/pull/17421).
---
## v3.5.12 (2024-01-31)
### etcd server
- Fix [not validating database consistent index, and panicking on nil backend](https://github.com/etcd-io/etcd/pull/17151)
- Document [`experimental-enable-lease-checkpoint-persist` flag in etcd help](https://github.com/etcd-io/etcd/pull/17190)
- Fix [needlessly flocking snapshot files when deleting](https://github.com/etcd-io/etcd/pull/17206)
- Add [digest for etcd base image](https://github.com/etcd-io/etcd/pull/17205)
- Fix [delete inconsistencies in read buffer](https://github.com/etcd-io/etcd/pull/17230)
- Add [mvcc: print backend database size and size in use in compaction logs](https://github.com/etcd-io/etcd/pull/17291)
### Dependencies
- Compile binaries using [go 1.20.13](https://github.com/etcd-io/etcd/pull/17275)
- Upgrade [golang.org/x/crypto to v0.17+ to address CVE-2023-48795](https://github.com/etcd-io/etcd/pull/17346)
## v3.5.11 (2023-12-07)
### etcd server
- Fix distributed tracing by ensuring `--experimental-distributed-tracing-sampling-rate` configuration option is available to [set tracing sample rate](https://github.com/etcd-io/etcd/pull/16951).
- Fix [url redirects while checking peer urls during new member addition](https://github.com/etcd-io/etcd/pull/16986)
- Add [livez/readyz HTTP endpoints](https://github.com/etcd-io/etcd/pull/17039)
### Dependencies
- Compile binaries using [go 1.20.12](https://github.com/etcd-io/etcd/pull/17077)
- Fix [CVE-2023-47108](https://github.com/advisories/GHSA-8pgv-569h-w5rw) by [bumping go.opentelemetry.io/otel to 1.20.0 and go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc to 0.46.0](https://github.com/etcd-io/etcd/pull/16946).
---
## v3.5.10 (2023-10-27)
### etcd server
- Fix [`--socket-reuse-port` and `--socket-reuse-address` not able to be set in configuration file](https://github.com/etcd-io/etcd/pull/16435).
- Fix [corruption check may get a `ErrCompacted` error when server has just been compacted](https://github.com/etcd-io/etcd/pull/16048)
- Improve [Lease put performance for the case that auth is disabled or the user is admin](https://github.com/etcd-io/etcd/pull/16019)
- Improve [Skip getting authInfo from incoming context when auth is disabled](https://github.com/etcd-io/etcd/pull/16241)
- Fix [Hash and HashKV have duplicated RESTful API](https://github.com/etcd-io/etcd/pull/16490)
### etcdutl v3
- Add [optional --bump-revision and --mark-compacted flag to etcdutl snapshot restore operation](https://github.com/etcd-io/etcd/pull/16165).
### etcdctl v3
- Add [optional --bump-revision and --mark-compacted flag to etcdctl snapshot restore operation](https://github.com/etcd-io/etcd/pull/16165).
### etcd grpc-proxy
- Fix [Memberlist results not updated when proxy node down](https://github.com/etcd-io/etcd/pull/15907).
### Package `clientv3`
- Fix [Multiple endpoints with same prefix got mixed up](https://github.com/etcd-io/etcd/pull/15939)
- Fix [Unexpected blocking when barrier waits on a nonexistent key](https://github.com/etcd-io/etcd/pull/16188)
- Fix [Reset auth token when failing to authenticate due to auth being disabled](https://github.com/etcd-io/etcd/pull/16241)
- Fix [panic in etcd validate secure endpoints](https://github.com/etcd-io/etcd/pull/16565)
### Dependencies
- Compile binaries using [go 1.20.10](https://github.com/etcd-io/etcd/pull/16745).
- Upgrade gRPC to 1.58.3 in https://github.com/etcd-io/etcd/pull/16625, https://github.com/etcd-io/etcd/pull/16781 and https://github.com/etcd-io/etcd/pull/16790. Note that gRPC server will reject requests with connection header (refer to https://github.com/grpc/grpc-go/pull/4803).
- Upgrade [bbolt to v1.3.8](https://github.com/etcd-io/etcd/pull/16833)
---
## v3.5.9 (2023-05-11)
### etcd server
- Fix [LeaseTimeToLive API may return keys to clients which have no read permission on the keys](https://github.com/etcd-io/etcd/pull/15815).
### Dependencies
- Compile binaries using [go 1.19.9](https://github.com/etcd-io/etcd/pull/15822).
---
## v3.5.8 (2023-04-13)
### etcd server
- Add [`etcd --tls-min-version --tls-max-version`](https://github.com/etcd-io/etcd/pull/15483) to enable support for TLS 1.3.
- Add [`etcd --listen-client-http-urls`](https://github.com/etcd-io/etcd/pull/15589) flag to support separating http server from grpc one, thus giving full immunity to [watch stream starvation under high read load](https://github.com/etcd-io/etcd/issues/15402).
- Change [http2 frame scheduler to random algorithm](https://github.com/etcd-io/etcd/pull/15452)
- Fix [Watch response traveling back in time when reconnecting member downloads snapshot from the leader](https://github.com/etcd-io/etcd/pull/15515)
- Fix [race when starting both secure & insecure gRPC servers on the same address](https://github.com/etcd-io/etcd/pull/15517)
- Fix [server/auth: disallow creating empty permission ranges](https://github.com/etcd-io/etcd/pull/15619)
- Fix [aligning zap log timestamp resolution to microseconds](https://github.com/etcd-io/etcd/pull/15240). Etcd now uses zap timestamp format: `2006-01-02T15:04:05.999999Z0700` (microsecond instead of milliseconds precision).
- Fix [wsproxy did not print log in JSON format](https://github.com/etcd-io/etcd/pull/15661).
- Fix [CVE-2021-28235](https://nvd.nist.gov/vuln/detail/CVE-2021-28235) by [clearing password after authenticating the user](https://github.com/etcd-io/etcd/pull/15653).
- Fix [etcdserver may panic when parsing a JWT token without username or revision](https://github.com/etcd-io/etcd/pull/15676).
- Fix [Requested watcher progress notifications are not synchronised with stream](https://github.com/etcd-io/etcd/pull/15695).
### Package `netutil`
- Fix [consistently format IPv6 addresses for comparison](https://github.com/etcd-io/etcd/pull/15187).
### Package `clientv3`
- Fix [etcd might send duplicated events to watch clients](https://github.com/etcd-io/etcd/pull/15274).
### Dependencies
- Recommend [Go 1.19+](https://github.com/etcd-io/etcd/pull/15337).
- Compile binaries using [go to 1.19.8](https://github.com/etcd-io/etcd/pull/15651)
- Upgrade [golang.org/x/net to v0.7.0](https://github.com/etcd-io/etcd/pull/15337)
- Upgrade [bbolt to v1.3.7](https://github.com/etcd-io/etcd/pull/15222).
### Docker image
- [Remove nsswitch.conf from docker image](https://github.com/etcd-io/etcd/pull/15161)
- Fix [etcd docker images all tagged with amd64 architecture](https://github.com/etcd-io/etcd/pull/15612)
---
## v3.5.7 (2023-01-20)
### etcd server
- Fix [Remove memberID from data corrupt alarm](https://github.com/etcd-io/etcd/pull/14852).
- Fix [Allow non mutating requests pass through quotaKVServer when NOSPACE](https://github.com/etcd-io/etcd/pull/14884).
- Fix [nil pointer panic for readonly txn due to nil response](https://github.com/etcd-io/etcd/pull/14899).
- Fix [The last record which was partially synced to disk isn't automatically repaired](https://github.com/etcd-io/etcd/pull/15069).
- Fix [etcdserver might promote a non-started learner](https://github.com/etcd-io/etcd/pull/15096).
### Package `clientv3`
- Reverted the fix to [auth invalid token and old revision errors in watch](https://github.com/etcd-io/etcd/pull/14995).
### Dependencies
- Recommend [Go 1.17+](https://github.com/etcd-io/etcd/pull/15019).
- Compile binaries using [Go 1.17.13](https://github.com/etcd-io/etcd/pull/15019)
- Bumped [some dependencies](https://github.com/etcd-io/etcd/pull/15018) to address some HIGH Vulnerabilities.
### Docker image
- Use [distroless base image](https://github.com/etcd-io/etcd/pull/15016) to address critical Vulnerabilities.
- Updated [base image from base-debian11 to static-debian11 and removed dependency on busybox](https://github.com/etcd-io/etcd/pull/15037).
---
## v3.5.6 (2022-11-21)
### etcd server
- Fix [auth invalid token and old revision errors in watch](https://github.com/etcd-io/etcd/pull/14547)
- Fix [avoid closing a watch with ID 0 incorrectly](https://github.com/etcd-io/etcd/pull/14563)
- Fix [auth: fix data consistency issue caused by recovery from snapshot](https://github.com/etcd-io/etcd/pull/14648)
- Fix [revision might be inconsistency between members when etcd crashes during processing defragmentation operation](https://github.com/etcd-io/etcd/pull/14733)
- Fix [timestamp in inconsistent format](https://github.com/etcd-io/etcd/pull/14799)
- Fix [Failed resolving host due to lost DNS record](https://github.com/etcd-io/etcd/pull/14573)
### Package `clientv3`
- Fix [Add backoff before retry when watch stream returns unavailable](https://github.com/etcd-io/etcd/pull/14582).
- Fix [stack overflow error in double barrier](https://github.com/etcd-io/etcd/pull/14658)
- Fix [Refreshing token on CommonName based authentication causes segmentation violation in client](https://github.com/etcd-io/etcd/pull/14790).
### etcd grpc-proxy
- Add [`etcd grpc-proxy start --listen-cipher-suites`](https://github.com/etcd-io/etcd/pull/14500) flag to support adding configurable cipher list.
---
## v3.5.5 (2022-09-15)
### Deprecations
- Deprecated [SetKeepAlive and SetKeepAlivePeriod in limitListenerConn](https://github.com/etcd-io/etcd/pull/14366).
### Package `clientv3`
- Fix [do not overwrite authTokenBundle on dial](https://github.com/etcd-io/etcd/pull/14132).
- Fix [IsOptsWithPrefix returns false even if WithPrefix() is included](https://github.com/etcd-io/etcd/pull/14187).
### etcd server
- [Build official darwin/arm64 artifacts](https://github.com/etcd-io/etcd/pull/14436).
- Add [`etcd --max-concurrent-streams`](https://github.com/etcd-io/etcd/pull/14219) flag to configure the max concurrent streams each client can open at a time, and defaults to math.MaxUint32.
- Add [`etcd --experimental-compact-hash-check-enabled --experimental-compact-hash-check-time`](https://github.com/etcd-io/etcd/issues/14039) flags to support enabling reliable corruption detection on compacted revisions.
- Fix [unexpected error during txn](https://github.com/etcd-io/etcd/issues/14110).
- Fix [lease leak issue due to tokenProvider isn't enabled when restoring auth store from a snapshot](https://github.com/etcd-io/etcd/pull/13205).
- Fix [the race condition between goroutine and channel on the same leases to be revoked](https://github.com/etcd-io/etcd/pull/14087).
- Fix [lessor may continue to schedule checkpoint after stepping down leader role](https://github.com/etcd-io/etcd/pull/14087).
- Fix [Restrict the max size of each WAL entry to the remaining size of the WAL file](https://github.com/etcd-io/etcd/pull/14127).
- Fix [Protect rangePermCache with a RW lock correctly](https://github.com/etcd-io/etcd/pull/14227)
- Fix [memberID equals zero in corruption alarm](https://github.com/etcd-io/etcd/pull/14272)
- Fix [Durability API guarantee broken in single node cluster](https://github.com/etcd-io/etcd/pull/14424)
- Fix [etcd fails to start after performing alarm list operation and then power off/on](https://github.com/etcd-io/etcd/pull/14429)
- Fix [authentication data not loaded on member startup](https://github.com/etcd-io/etcd/pull/14409)
### etcdctl v3
- Fix [etcdctl move-leader may fail for multiple endpoints](https://github.com/etcd-io/etcd/pull/14434)
### Other
- [Bump golang.org/x/crypto to latest version](https://github.com/etcd-io/etcd/pull/13996) to address [CVE-2022-27191](https://github.com/advisories/GHSA-8c26-wmh5-6g9v).
- [Bump OpenTelemetry to 1.0.1 and gRPC to 1.41.0](https://github.com/etcd-io/etcd/pull/14312).
---
## v3.5.4 (2022-04-24)
### etcd server
- Fix [etcd panic on startup (auth enabled)](https://github.com/etcd-io/etcd/pull/13946)
### package `client/pkg/v3`
- [Revert the change of trimming the trailing dot from SRV.Target](https://github.com/etcd-io/etcd/pull/13950) returned by DNS lookup
---
## v3.5.3 (2022-04-13)
### etcd server
- Fix [Provide a better liveness probe for when etcd runs as a Kubernetes pod](https://github.com/etcd-io/etcd/pull/13706)
- Fix [inconsistent log format](https://github.com/etcd-io/etcd/pull/13864)
- Fix [Inconsistent revision and data occurs](https://github.com/etcd-io/etcd/pull/13908)
- Fix [Etcdserver is still in progress of processing LeaseGrantRequest when it receives a LeaseKeepAliveRequest on the same leaseID](https://github.com/etcd-io/etcd/pull/13932)
- Fix [consistent_index coming from snapshot is overwritten by the old local value](https://github.com/etcd-io/etcd/pull/13933)
- [Update container base image snapshot](https://github.com/etcd-io/etcd/pull/13862)
- Fix [Defrag unsets backend options](https://github.com/etcd-io/etcd/pull/13701).
### package `client/pkg/v3`
- [Trim the suffix dot from the target](https://github.com/etcd-io/etcd/pull/13714) in SRV records returned by DNS lookup
### etcdctl v3
- [Always print the raft_term in decimal](https://github.com/etcd-io/etcd/pull/13727) when displaying member list in json.
---
## [v3.5.2](https://github.com/etcd-io/etcd/releases/tag/v3.5.2) (2022-02-01)
See [code changes](https://github.com/etcd-io/etcd/compare/v3.5.1...v3.5.2) and [v3.5 upgrade guide](https://etcd.io/docs/latest/upgrades/upgrade_3_5/) for any breaking changes.
### etcd server
- Fix [exclude the same alarm type activated by multiple peers](https://github.com/etcd-io/etcd/pull/13476).
- Add [`etcd --experimental-enable-lease-checkpoint-persist`](https://github.com/etcd-io/etcd/pull/13508) flag to enable checkpoint persisting.
- Fix [Lease checkpoints don't prevent to reset ttl on leader change](https://github.com/etcd-io/etcd/pull/13508), requires enabling checkpoint persisting.
- Fix [assertion failed due to tx closed when recovering v3 backend from a snapshot db](https://github.com/etcd-io/etcd/pull/13501)
- Fix [segmentation violation(SIGSEGV) error due to premature unlocking of watchableStore](https://github.com/etcd-io/etcd/pull/13541)
---
## [v3.5.1](https://github.com/etcd-io/etcd/releases/tag/v3.5.1) (2021-10-15)
See [code changes](https://github.com/etcd-io/etcd/compare/v3.5.0...v3.5.1) and [v3.5 upgrade guide](https://etcd.io/docs/latest/upgrades/upgrade_3_5/) for any breaking changes.
### etcd server
- Fix [self-signed-cert-validity parameter cannot be specified in the config file](https://github.com/etcd-io/etcd/pull/13237).
- Fix [ensure that cluster members stored in v2store and backend are in sync](https://github.com/etcd-io/etcd/pull/13348)
### etcd client
- [Fix etcd client sends invalid :authority header](https://github.com/etcd-io/etcd/issues/13192)
### package clientv3
- Endpoints self identify now as `etcd-endpoints://{id}/{authority}` where authority is based on first endpoint passed, for example `etcd-endpoints://0xc0009d8540/localhost:2079`
### Other
- Updated [base image](https://github.com/etcd-io/etcd/pull/13386) from `debian:buster-v1.4.0` to `debian:bullseye-20210927` to fix the following critical CVEs:
- [CVE-2021-3711](https://nvd.nist.gov/vuln/detail/CVE-2021-3711): miscalculation of a buffer size in openssl's SM2 decryption
- [CVE-2021-35942](https://nvd.nist.gov/vuln/detail/CVE-2021-35942): integer overflow flaw in glibc
- [CVE-2019-9893](https://nvd.nist.gov/vuln/detail/CVE-2019-9893): incorrect syscall argument generation in libseccomp
- [CVE-2021-36159](https://nvd.nist.gov/vuln/detail/CVE-2021-36159): libfetch in apk-tools mishandles numeric strings in FTP and HTTP protocols to allow out of bound reads.
---
## v3.5.0 (2021-06)
See [code changes](https://github.com/etcd-io/etcd/compare/v3.4.0...v3.5.0) and [v3.5 upgrade guide](https://etcd.io/docs/latest/upgrades/upgrade_3_5/) for any breaking changes.
- [v3.5.0](https://github.com/etcd-io/etcd/releases/tag/v3.5.0) (2021 TBD), see [code changes](https://github.com/etcd-io/etcd/compare/v3.5.0-rc.1...v3.5.0).
- [v3.5.0-rc.1](https://github.com/etcd-io/etcd/releases/tag/v3.5.0-rc.1) (2021-06-10), see [code changes](https://github.com/etcd-io/etcd/compare/v3.5.0-rc.0...v3.5.0-rc.1).
- [v3.5.0-rc.0](https://github.com/etcd-io/etcd/releases/tag/v3.5.0-rc.0) (2021-06-04), see [code changes](https://github.com/etcd-io/etcd/compare/v3.5.0-beta.4...v3.5.0-rc.0).
- [v3.5.0-beta.4](https://github.com/etcd-io/etcd/releases/tag/v3.5.0-beta.4) (2021-05-26), see [code changes](https://github.com/etcd-io/etcd/compare/v3.5.0-beta.3...v3.5.0-beta.4).
- [v3.5.0-beta.3](https://github.com/etcd-io/etcd/releases/tag/v3.5.0-beta.3) (2021-05-18), see [code changes](https://github.com/etcd-io/etcd/compare/v3.5.0-beta.2...v3.5.0-beta.3).
- [v3.5.0-beta.2](https://github.com/etcd-io/etcd/releases/tag/v3.5.0-beta.2) (2021-05-18), see [code changes](https://github.com/etcd-io/etcd/compare/v3.5.0-beta.1...v3.5.0-beta.2).
- [v3.5.0-beta.1](https://github.com/etcd-io/etcd/releases/tag/v3.5.0-beta.1) (2021-05-18), see [code changes](https://github.com/etcd-io/etcd/compare/v3.4.0...v3.5.0-beta.1).
**Again, before running upgrades from any previous release, please make sure to read change logs below and [v3.5 upgrade guide](https://etcd.io/docs/latest/upgrades/upgrade_3_5/).**
### Breaking Changes
- `go.etcd.io/etcd` Go packages have moved to `go.etcd.io/etcd/{api,pkg,raft,client,etcdctl,server,raft,tests}/v3` to follow the [Go modules](https://github.com/golang/go/wiki/Modules) conventions
- `go.etcd.io/clientv3/snapshot` SnapshotManager class have moved to `go.etcd.io/clientv3/etcdctl`.
The method `snapshot.Save` to download a snapshot from the remote server was preserved in 'go.etcd.io/clientv3/snapshot`.
- `go.etcd.io/client' package got migrated to 'go.etcd.io/client/v2'.
- Changed behavior of clientv3 API [MemberList](https://github.com/etcd-io/etcd/pull/11639).
- Previously, it is directly served with server's local data, which could be stale.
- Now, it is served with linearizable guarantee. If the server is disconnected from quorum, `MemberList` call will fail.
- [gRPC gateway](https://github.com/grpc-ecosystem/grpc-gateway) only supports [`/v3`](TODO) endpoint.
- Deprecated [`/v3beta`](https://github.com/etcd-io/etcd/pull/9298).
- `curl -L http://localhost:2379/v3beta/kv/put -X POST -d '{"key": "Zm9v", "value": "YmFy"}'` doesn't work in v3.5. Use `curl -L http://localhost:2379/v3/kv/put -X POST -d '{"key": "Zm9v", "value": "YmFy"}'` instead.
- **`etcd --experimental-enable-v2v3` flag remains experimental and to be deprecated.**
- v2 storage emulation feature will be deprecated in the next release.
- etcd 3.5 is the last version that supports V2 API. Flags `--enable-v2` and `--experimental-enable-v2v3` [are now deprecated](https://github.com/etcd-io/etcd/pull/12940) and will be removed in etcd v3.6 release.
- **`etcd --experimental-backend-bbolt-freelist-type` flag has been deprecated.** Use **`etcd --backend-bbolt-freelist-type`** instead. The default type is hashmap and it is stable now.
- **`etcd --debug` flag has been deprecated.** Use **`etcd --log-level=debug`** instead.
- Remove [`embed.Config.Debug`](https://github.com/etcd-io/etcd/pull/10947).
- **`etcd --log-output` flag has been deprecated.** Use **`etcd --log-outputs`** instead.
- **`etcd --logger=zap --log-outputs=stderr`** is now the default.
- **`etcd --logger=capnslog` flag value has been deprecated.**
- **`etcd --logger=zap --log-outputs=default` flag value is not supported.**.
- Use `etcd --logger=zap --log-outputs=stderr`.
- Or, use `etcd --logger=zap --log-outputs=systemd/journal` to send logs to the local systemd journal.
- Previously, if etcd parent process ID (PPID) is 1 (e.g. run with systemd), `etcd --logger=capnslog --log-outputs=default` redirects server logs to local systemd journal. And if write to journald fails, it writes to `os.Stderr` as a fallback.
- However, even with PPID 1, it can fail to dial systemd journal (e.g. run embedded etcd with Docker container). Then, [every single log write will fail](https://github.com/etcd-io/etcd/pull/9729) and fall back to `os.Stderr`, which is inefficient.
- To avoid this problem, systemd journal logging must be configured manually.
- **`etcd --log-outputs=stderr`** is now the default.
- **`etcd --log-package-levels` flag for `capnslog` has been deprecated.** Now, **`etcd --logger=zap --log-outputs=stderr`** is the default.
- **`[CLIENT-URL]/config/local/log` endpoint has been deprecated, as is `etcd --log-package-levels` flag.**
- `curl http://127.0.0.1:2379/config/local/log -XPUT -d '{"Level":"DEBUG"}'` won't work.
- Please use `etcd --logger=zap --log-outputs=stderr` instead.
- Deprecated `etcd_debugging_mvcc_db_total_size_in_bytes` Prometheus metric. Use `etcd_mvcc_db_total_size_in_bytes` instead.
- Deprecated `etcd_debugging_mvcc_put_total` Prometheus metric. Use `etcd_mvcc_put_total` instead.
- Deprecated `etcd_debugging_mvcc_delete_total` Prometheus metric. Use `etcd_mvcc_delete_total` instead.
- Deprecated `etcd_debugging_mvcc_txn_total` Prometheus metric. Use `etcd_mvcc_txn_total` instead.
- Deprecated `etcd_debugging_mvcc_range_total` Prometheus metric. Use `etcd_mvcc_range_total` instead.
- Main branch `/version` outputs `3.5.0-pre`, instead of `3.4.0+git`.
- Changed `proxy` package function signature to [support structured logger](https://github.com/etcd-io/etcd/pull/11614).
- Previously, `NewClusterProxy(c *clientv3.Client, advaddr string, prefix string) (pb.ClusterServer, <-chan struct{})`, now `NewClusterProxy(lg *zap.Logger, c *clientv3.Client, advaddr string, prefix string) (pb.ClusterServer, <-chan struct{})`.
- Previously, `Register(c *clientv3.Client, prefix string, addr string, ttl int)`, now `Register(lg *zap.Logger, c *clientv3.Client, prefix string, addr string, ttl int) <-chan struct{}`.
- Previously, `NewHandler(t *http.Transport, urlsFunc GetProxyURLs, failureWait time.Duration, refreshInterval time.Duration) http.Handler`, now `NewHandler(lg *zap.Logger, t *http.Transport, urlsFunc GetProxyURLs, failureWait time.Duration, refreshInterval time.Duration) http.Handler`.
- Changed `pkg/flags` function signature to [support structured logger](https://github.com/etcd-io/etcd/pull/11616).
- Previously, `SetFlagsFromEnv(prefix string, fs *flag.FlagSet) error`, now `SetFlagsFromEnv(lg *zap.Logger, prefix string, fs *flag.FlagSet) error`.
- Previously, `SetPflagsFromEnv(prefix string, fs *pflag.FlagSet) error`, now `SetPflagsFromEnv(lg *zap.Logger, prefix string, fs *pflag.FlagSet) error`.
- ClientV3 supports [grpc resolver API](https://github.com/etcd-io/etcd/blob/main/client/v3/naming/resolver/resolver.go).
- Endpoints can be managed using [endpoints.Manager](https://github.com/etcd-io/etcd/blob/main/client/v3/naming/endpoints/endpoints.go)
- Previously supported [GRPCResolver was decomissioned](https://github.com/etcd-io/etcd/pull/12675). Use [resolver](https://github.com/etcd-io/etcd/blob/main/client/v3/naming/resolver/resolver.go) instead.
- Turned on [--pre-vote by default](https://github.com/etcd-io/etcd/pull/12770). Should prevent disrupting RAFT leader by an individual member.
- [ETCD_CLIENT_DEBUG env](https://github.com/etcd-io/etcd/pull/12786): Now supports log levels (debug, info, warn, error, dpanic, panic, fatal). Only when set, overrides application-wide grpc logging settings.
- [Embed Etcd.Close()](https://github.com/etcd-io/etcd/pull/12828) needs to called exactly once and closes Etcd.Err() stream.
- [Embed Etcd does not override global/grpc logger](https://github.com/etcd-io/etcd/pull/12861) be default any longer. If desired, please call `embed.Config::SetupGlobalLoggers()` explicitly.
- [Embed Etcd custom logger should be configured using simpler builder `NewZapLoggerBuilder`](https://github.com/etcd-io/etcd/pull/12973).
- Client errors of `context cancelled` or `context deadline exceeded` are exposed as `codes.Canceled` and `codes.DeadlineExceeded`, instead of `codes.Unknown`.
### Storage format changes
- [WAL log's snapshots persists raftpb.ConfState](https://github.com/etcd-io/etcd/pull/12735)
- [Backend persists raftpb.ConfState](https://github.com/etcd-io/etcd/pull/12962) in the `meta` bucket `confState` key.
- [Backend persists applied term](https://github.com/etcd-io/etcd/pull/) in the `meta` bucket.
- Backend persists `downgrade` in the `cluster` bucket
### Security
- Add [`TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256` and `TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256` to `etcd --cipher-suites`](https://github.com/etcd-io/etcd/pull/11864).
- Changed [the format of WAL entries related to auth for not keeping password as a plain text](https://github.com/etcd-io/etcd/pull/11943).
- Add third party [Security Audit Report](https://github.com/etcd-io/etcd/pull/12201).
- A [log warning](https://github.com/etcd-io/etcd/pull/12242) is added when etcd uses any existing directory that has a permission different than 700 on Linux and 777 on Windows.
- Add optional [`ClientCertFile` and `ClientKeyFile`](https://github.com/etcd-io/etcd/pull/12705) options for peer and client tls configuration when split certificates are used.
### Metrics, Monitoring
See [List of metrics](https://etcd.io/docs/latest/metrics/) for all metrics per release.
Note that any `etcd_debugging_*` metrics are experimental and subject to change.
- Deprecated `etcd_debugging_mvcc_db_total_size_in_bytes` Prometheus metric. Use `etcd_mvcc_db_total_size_in_bytes` instead.
- Deprecated `etcd_debugging_mvcc_put_total` Prometheus metric. Use `etcd_mvcc_put_total` instead.
- Deprecated `etcd_debugging_mvcc_delete_total` Prometheus metric. Use `etcd_mvcc_delete_total` instead.
- Deprecated `etcd_debugging_mvcc_txn_total` Prometheus metric. Use `etcd_mvcc_txn_total` instead.
- Deprecated `etcd_debugging_mvcc_range_total` Prometheus metric. Use `etcd_mvcc_range_total` instead.
- Add [`etcd_debugging_mvcc_current_revision`](https://github.com/etcd-io/etcd/pull/11126) Prometheus metric.
- Add [`etcd_debugging_mvcc_compact_revision`](https://github.com/etcd-io/etcd/pull/11126) Prometheus metric.
- Change [`etcd_cluster_version`](https://github.com/etcd-io/etcd/pull/11254) Prometheus metrics to include only major and minor version.
- Add [`etcd_debugging_mvcc_total_put_size_in_bytes`](https://github.com/etcd-io/etcd/pull/11374) Prometheus metric.
- Add [`etcd_server_client_requests_total` with `"type"` and `"client_api_version"` labels](https://github.com/etcd-io/etcd/pull/11687).
- Add [`etcd_wal_write_bytes_total`](https://github.com/etcd-io/etcd/pull/11738).
- Add [`etcd_debugging_auth_revision`](https://github.com/etcd-io/etcd/commit/f14d2a087f7b0fd6f7980b95b5e0b945109c95f3).
- Add [`os_fd_used` and `os_fd_limit` to monitor current OS file descriptors](https://github.com/etcd-io/etcd/pull/12214).
- Add [`etcd_disk_defrag_inflight`](https://github.com/etcd-io/etcd/pull/13395).
### etcd server
- Add [don't attempt to grant nil permission to a role](https://github.com/etcd-io/etcd/pull/13086).
- Add [don't activate alarms w/missing AlarmType](https://github.com/etcd-io/etcd/pull/13084).
- Add [`TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256` and `TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256` to `etcd --cipher-suites`](https://github.com/etcd-io/etcd/pull/11864).
- Automatically [create parent directory if it does not exist](https://github.com/etcd-io/etcd/pull/9626) (fix [issue#9609](https://github.com/etcd-io/etcd/issues/9609)).
- v4.0 will configure `etcd --enable-v2=true --enable-v2v3=/aaa` to enable v2 API server that is backed by **v3 storage**.
- [`etcd --backend-bbolt-freelist-type`] flag is now stable.
- `etcd --experimental-backend-bbolt-freelist-type` has been deprecated.
- Support [downgrade API](https://github.com/etcd-io/etcd/pull/11715).
- Deprecate v2 apply on cluster version. [Use v3 request to set cluster version and recover cluster version from v3 backend](https://github.com/etcd-io/etcd/pull/11427).
- [Use v2 api to update cluster version to support mixed version cluster during upgrade](https://github.com/etcd-io/etcd/pull/12988).
- [Fix corruption bug in defrag](https://github.com/etcd-io/etcd/pull/11613).
- Fix [quorum protection logic when promoting a learner](https://github.com/etcd-io/etcd/pull/11640).
- Improve [peer corruption checker](https://github.com/etcd-io/etcd/pull/11621) to work when peer mTLS is enabled.
- Log [`[CLIENT-PORT]/health` check in server side](https://github.com/etcd-io/etcd/pull/11704).
- Log [successful etcd server-side health check in debug level](https://github.com/etcd-io/etcd/pull/12677).
- Improve [compaction performance when latest index is greater than 1-million](https://github.com/etcd-io/etcd/pull/11734).
- [Refactor consistentindex](https://github.com/etcd-io/etcd/pull/11699).
- [Add log when etcdserver failed to apply command](https://github.com/etcd-io/etcd/pull/11670).
- Improve [count-only range performance](https://github.com/etcd-io/etcd/pull/11771).
- Remove [redundant storage restore operation to shorten the startup time](https://github.com/etcd-io/etcd/pull/11779).
- With 40 million key test data,it can shorten the startup time from 5 min to 2.5 min.
- [Fix deadlock bug in mvcc](https://github.com/etcd-io/etcd/pull/11817).
- Fix [inconsistency between WAL and server snapshot](https://github.com/etcd-io/etcd/pull/11888).
- Previously, server restore fails if it had crashed after persisting raft hard state but before saving snapshot.
- See https://github.com/etcd-io/etcd/issues/10219 for more.
- Add [missing CRC checksum check in WAL validate method otherwise causes panic](https://github.com/etcd-io/etcd/pull/11924).
- See https://github.com/etcd-io/etcd/issues/11918.
- Improve logging around snapshot send and receive.
- [Push down RangeOptions.limit argv into index tree to reduce memory overhead](https://github.com/etcd-io/etcd/pull/11990).
- Add [reason field for /health response](https://github.com/etcd-io/etcd/pull/11983).
- Add [exclude alarms from health check conditionally](https://github.com/etcd-io/etcd/pull/12880).
- Add [`etcd --unsafe-no-fsync`](https://github.com/etcd-io/etcd/pull/11946) flag.
- Setting the flag disables all uses of fsync, which is unsafe and will cause data loss. This flag makes it possible to run an etcd node for testing and development without placing lots of load on the file system.
- Add [`etcd --auth-token-ttl`](https://github.com/etcd-io/etcd/pull/11980) flag to customize `simpleTokenTTL` settings.
- Improve [`runtime.FDUsage` call pattern to reduce objects malloc of Memory Usage and CPU Usage](https://github.com/etcd-io/etcd/pull/11986).
- Improve [mvcc.watchResponse channel Memory Usage](https://github.com/etcd-io/etcd/pull/11987).
- Log [expensive request info in UnaryInterceptor](https://github.com/etcd-io/etcd/pull/12086).
- [Fix invalid Go type in etcdserverpb](https://github.com/etcd-io/etcd/pull/12000).
- [Improve healthcheck by using v3 range request and its corresponding timeout](https://github.com/etcd-io/etcd/pull/12195).
- Add [`etcd --experimental-watch-progress-notify-interval`](https://github.com/etcd-io/etcd/pull/12216) flag to make watch progress notify interval configurable.
- Fix [server panic in slow writes warnings](https://github.com/etcd-io/etcd/issues/12197).
- Fixed via [PR#12238](https://github.com/etcd-io/etcd/pull/12238).
- [Fix server panic](https://github.com/etcd-io/etcd/pull/12288) when force-new-cluster flag is enabled in a cluster which had learner node.
- Add [`etcd --self-signed-cert-validity`](https://github.com/etcd-io/etcd/pull/12429) flag to support setting certificate expiration time.
- Notice, certificates generated by etcd are valid for 1 year by default when specifying the auto-tls or peer-auto-tls option.
- Add [`etcd --experimental-warning-apply-duration`](https://github.com/etcd-io/etcd/pull/12448) flag which allows apply duration threshold to be configurable.
- Add [`etcd --experimental-memory-mlock`](https://github.com/etcd-io/etcd/pull/TODO) flag which prevents etcd memory pages to be swapped out.
- Add [`etcd --socket-reuse-port`](https://github.com/etcd-io/etcd/pull/12702) flag
- Setting this flag enables `SO_REUSEPORT` which allows rebind of a port already in use. User should take caution when using this flag to ensure flock is properly enforced.
- Add [`etcd --socket-reuse-address`](https://github.com/etcd-io/etcd/pull/12702) flag
- Setting this flag enables `SO_REUSEADDR` which allows binding to an address in `TIME_WAIT` state, improving etcd restart time.
- Reduce [around 30% memory allocation by logging range response size without marshal](https://github.com/etcd-io/etcd/pull/12871).
- `ETCD_VERIFY="all"` environment triggers [additional verification of consistency](https://github.com/etcd-io/etcd/pull/12901) of etcd data-dir files.
- Add [`etcd --enable-log-rotation`](https://github.com/etcd-io/etcd/pull/12774) boolean flag which enables log rotation if true.
- Add [`etcd --log-rotation-config-json`](https://github.com/etcd-io/etcd/pull/12774) flag which allows passthrough of JSON config to configure log rotation for a file output target.
- Add experimental distributed tracing boolean flag [`--experimental-enable-distributed-tracing`](https://github.com/etcd-io/etcd/pull/12919) which enables tracing.
- Add [`etcd --experimental-distributed-tracing-address`](https://github.com/etcd-io/etcd/pull/12919) string flag which allows configuring the OpenTelemetry collector address.
- Add [`etcd --experimental-distributed-tracing-service-name`](https://github.com/etcd-io/etcd/pull/12919) string flag which allows changing the default "etcd" service name.
- Add [`etcd --experimental-distributed-tracing-instance-id`](https://github.com/etcd-io/etcd/pull/12919) string flag which configures an instance ID, which must be unique per etcd instance.
- Add [`--experimental-bootstrap-defrag-threshold-megabytes`](https://github.com/etcd-io/etcd/pull/12941) which configures a threshold for the unused db size and etcdserver will automatically perform defragmentation on bootstrap when it exceeds this value. The functionality is disabled if the value is 0.
### Package `runtime`
- Optimize [`runtime.FDUsage` by removing unnecessary sorting](https://github.com/etcd-io/etcd/pull/12214).
### Package `embed`
- Remove [`embed.Config.Debug`](https://github.com/etcd-io/etcd/pull/10947).
- Use `embed.Config.LogLevel` instead.
- Add [`embed.Config.ZapLoggerBuilder`](https://github.com/etcd-io/etcd/pull/11147) to allow creating a custom zap logger.
- Replace [global `*zap.Logger` with etcd server logger object](https://github.com/etcd-io/etcd/pull/12212).
- Add [`embed.Config.EnableLogRotation`](https://github.com/etcd-io/etcd/pull/12774) which enables log rotation if true.
- Add [`embed.Config.LogRotationConfigJSON`](https://github.com/etcd-io/etcd/pull/12774) to allow passthrough of JSON config to configure log rotation for a file output target.
- Add [`embed.Config.ExperimentalEnableDistributedTracing`](https://github.com/etcd-io/etcd/pull/12919) which enables experimental distributed tracing if true.
- Add [`embed.Config.ExperimentalDistributedTracingAddress`](https://github.com/etcd-io/etcd/pull/12919) which allows overriding default collector address.
- Add [`embed.Config.ExperimentalDistributedTracingServiceName`](https://github.com/etcd-io/etcd/pull/12919) which allows overriding default "etcd" service name.
- Add [`embed.Config.ExperimentalDistributedTracingServiceInstanceID`](https://github.com/etcd-io/etcd/pull/12919) which allows configuring an instance ID, which must be uniquer per etcd instance.
### Package `clientv3`
- Remove [excessive watch cancel logging messages](https://github.com/etcd-io/etcd/pull/12187).
- See [kubernetes/kubernetes#93450](https://github.com/kubernetes/kubernetes/issues/93450).
- Add [`TryLock`](https://github.com/etcd-io/etcd/pull/11104) method to `clientv3/concurrency/Mutex`. A non-blocking method on `Mutex` which does not wait to get lock on the Mutex, returns immediately if Mutex is locked by another session.
- Fix [client balancer failover against multiple endpoints](https://github.com/etcd-io/etcd/pull/11184).
- Fix [`"kube-apiserver: failover on multi-member etcd cluster fails certificate check on DNS mismatch"`](https://github.com/kubernetes/kubernetes/issues/83028).
- Fix [IPv6 endpoint parsing in client](https://github.com/etcd-io/etcd/pull/11211).
- Fix ["1.16: etcd client does not parse IPv6 addresses correctly when members are joining" (kubernetes#83550)](https://github.com/kubernetes/kubernetes/issues/83550).
- Fix [errors caused by grpc changing balancer/resolver API](https://github.com/etcd-io/etcd/pull/11564). This change is compatible with grpc >= [v1.26.0](https://github.com/grpc/grpc-go/releases/tag/v1.26.0), but is not compatible with < v1.26.0 version.
- Use [ServerName as the authority](https://github.com/etcd-io/etcd/pull/11574) after bumping to grpc v1.26.0. Remove workaround in [#11184](https://github.com/etcd-io/etcd/pull/11184).
- Fix [`"hasleader"` metadata embedding](https://github.com/etcd-io/etcd/pull/11687).
- Previously, `clientv3.WithRequireLeader(ctx)` was overwriting existing context keys.
- Fix [watch leak caused by lazy cancellation](https://github.com/etcd-io/etcd/pull/11850). When clients cancel their watches, a cancel request will now be immediately sent to the server instead of waiting for the next watch event.
- Make sure [save snapshot downloads checksum for integrity checks](https://github.com/etcd-io/etcd/pull/11896).
- Fix [auth token invalid after watch reconnects](https://github.com/etcd-io/etcd/pull/12264). Get AuthToken automatically when clientConn is ready.
- Improve [clientv3:get AuthToken gracefully without extra connection](https://github.com/etcd-io/etcd/pull/12165).
- Changed [clientv3 dialing code](https://github.com/etcd-io/etcd/pull/12671) to use grpc resolver API instead of custom balancer.
- Endpoints self identify now as `etcd-endpoints://{id}/#initially={list of endpoints}` e.g. `etcd-endpoints://0xc0009d8540/#initially=[localhost:2079]`
- Make sure [save snapshot downloads checksum for integrity checks](https://github.com/etcd-io/etcd/pull/11896).
### Package `lease`
- Fix [memory leak in follower nodes](https://github.com/etcd-io/etcd/pull/11731).
- https://github.com/etcd-io/etcd/issues/11495
- https://github.com/etcd-io/etcd/issues/11730
- Make sure [grant/revoke won't be applied repeatedly after restarting etcd](https://github.com/etcd-io/etcd/pull/11935).
### Package `wal`
- Add [`etcd_wal_write_bytes_total`](https://github.com/etcd-io/etcd/pull/11738).
- Handle [out-of-range slice bound in `ReadAll` and entry limit in `decodeRecord`](https://github.com/etcd-io/etcd/pull/11793).
### etcdctl v3
- Fix `etcdctl member add` command to prevent potential timeout. ([PR#11194](https://github.com/etcd-io/etcd/pull/11194) and [PR#11638](https://github.com/etcd-io/etcd/pull/11638))
- Add [`etcdctl watch --progress-notify`](https://github.com/etcd-io/etcd/pull/11462) flag.
- Add [`etcdctl auth status`](https://github.com/etcd-io/etcd/pull/11536) command to check if authentication is enabled
- Add [`etcdctl get --count-only`](https://github.com/etcd-io/etcd/pull/11743) flag for output type `fields`.
- Add [`etcdctl member list -w=json --hex`](https://github.com/etcd-io/etcd/pull/11812) flag to print memberListResponse in hex format json.
- Changed [`etcdctl lock exec-command`](https://github.com/etcd-io/etcd/pull/12829) to return exit code of exec-command.
- [New tool: `etcdutl`](https://github.com/etcd-io/etcd/pull/12971) incorporated functionality of: `etcdctl snapshot status|restore`, `etcdctl backup`, `etcdctl defrag --data-dir ...`.
- [ETCDCTL_API=3 `etcdctl migrate`](https://github.com/etcd-io/etcd/pull/12971) has been decommissioned. Use etcd <=v3.4 to restore v2 storage.
### gRPC gateway
- [gRPC gateway](https://github.com/grpc-ecosystem/grpc-gateway) only supports [`/v3`](TODO) endpoint.
- Deprecated [`/v3beta`](https://github.com/etcd-io/etcd/pull/9298).
- `curl -L http://localhost:2379/v3beta/kv/put -X POST -d '{"key": "Zm9v", "value": "YmFy"}'` does work in v3.5. Use `curl -L http://localhost:2379/v3/kv/put -X POST -d '{"key": "Zm9v", "value": "YmFy"}'` instead.
- Set [`enable-grpc-gateway`](https://github.com/etcd-io/etcd/pull/12297) flag to true when using a config file to keep the defaults the same as the command line configuration.
### gRPC Proxy
- Fix [`panic on error`](https://github.com/etcd-io/etcd/pull/11694) for metrics handler.
- Add [gRPC keepalive related flags](https://github.com/etcd-io/etcd/pull/11711) `grpc-keepalive-min-time`, `grpc-keepalive-interval` and `grpc-keepalive-timeout`.
- [Fix grpc watch proxy hangs when failed to cancel a watcher](https://github.com/etcd-io/etcd/pull/12030) .
- Add [metrics handler for grpcproxy self](https://github.com/etcd-io/etcd/pull/12107).
- Add [health handler for grpcproxy self](https://github.com/etcd-io/etcd/pull/12114).
### Auth
- Fix [NoPassword check when adding user through GRPC gateway](https://github.com/etcd-io/etcd/pull/11418) ([issue#11414](https://github.com/etcd-io/etcd/issues/11414))
- Fix bug where [some auth related messages are logged at wrong level](https://github.com/etcd-io/etcd/pull/11586)
- [Fix a data corruption bug by saving consistent index](https://github.com/etcd-io/etcd/pull/11652).
- [Improve checkPassword performance](https://github.com/etcd-io/etcd/pull/11735).
- [Add authRevision field in AuthStatus](https://github.com/etcd-io/etcd/pull/11659).
- Fix [a bug of not refreshing expired tokens](https://github.com/etcd-io/etcd/pull/13308).
-
### API
- Add [`/v3/auth/status`](https://github.com/etcd-io/etcd/pull/11536) endpoint to check if authentication is enabled
- [Add `Linearizable` field to `etcdserverpb.MemberListRequest`](https://github.com/etcd-io/etcd/pull/11639).
- [Learner support Snapshot RPC](https://github.com/etcd-io/etcd/pull/12890/).
### Package `netutil`
- Remove [`netutil.DropPort/RecoverPort/SetLatency/RemoveLatency`](https://github.com/etcd-io/etcd/pull/12491).
- These are not used anymore. They were only used for older versions of functional testing.
- Removed to adhere to best security practices, minimize arbitrary shell invocation.
### `tools/etcd-dump-metrics`
- Implement [input validation to prevent arbitrary shell invocation](https://github.com/etcd-io/etcd/pull/12491).
### Dependency
- Upgrade [`google.golang.org/grpc`](https://github.com/grpc/grpc-go/releases) from [**`v1.23.0`**](https://github.com/grpc/grpc-go/releases/tag/v1.23.0) to [**`v1.37.0`**](https://github.com/grpc/grpc-go/releases/tag/v1.37.0).
- Upgrade [`go.uber.org/zap`](https://github.com/uber-go/zap/releases) from [**`v1.14.1`**](https://github.com/uber-go/zap/releases/tag/v1.14.1) to [**`v1.16.0`**](https://github.com/uber-go/zap/releases/tag/v1.16.0).
### Platforms
- etcd now [officially supports `arm64`](https://github.com/etcd-io/etcd/pull/12929).
- See https://github.com/etcd-io/etcd/pull/12928 for adding automated tests with `arm64` EC2 instances (Graviton 2).
- See https://github.com/etcd-io/website/pull/273 for new platform support tier policies.
### Release
- Add s390x build support ([PR#11548](https://github.com/etcd-io/etcd/pull/11548) and [PR#11358](https://github.com/etcd-io/etcd/pull/11358))
### Go
- Require [*Go 1.16+*](https://github.com/etcd-io/etcd/pull/11110).
- Compile with [*Go 1.16+*](https://golang.org/doc/devel/release.html#go1.16)
- etcd uses [go modules](https://github.com/etcd-io/etcd/pull/12279) (instead of vendor dir) to track dependencies.
### Project Governance
- The etcd team has added, a well defined and openly discussed, project [governance](https://github.com/etcd-io/etcd/pull/11175).
---
================================================
FILE: CHANGELOG/CHANGELOG-3.6.md
================================================
Previous change logs can be found at [CHANGELOG-3.5](https://github.com/etcd-io/etcd/blob/main/CHANGELOG/CHANGELOG-3.5.md).
---
## v3.6.9 (TBC)
### etcd server
- [Ensure the metrics interceptor runs before other interceptors so that metrics remain up to date](https://github.com/etcd-io/etcd/pull/21329)
- Fix [Race between read index and leader change](https://github.com/etcd-io/etcd/pull/21378)
- Fix [Stale reads caused by process pausing](https://github.com/etcd-io/etcd/pull/21417)
### Package `clientv3`
- [Print the endpoint the grpc request was actually sent to in unary interceptor](https://github.com/etcd-io/etcd/pull/21382)
### etcd grpc-proxy
- [server/etcdmain: fix startup deadlock in grpcproxy](https://github.com/etcd-io/etcd/pull/21354)
### etcdctl
- Fix [slice bounds trimming single-quoted args in Argify](https://github.com/etcd-io/etcd/pull/21402)
### Dependencies
- [Bump go.opentelemetry.io/otel/sdk to v1.40.0 to resolve https://pkg.go.dev/vuln/GO-2026-4394](https://github.com/etcd-io/etcd/pull/21340)
- Compile binaries using [go 1.25.7](https://github.com/etcd-io/etcd/pull/21393)
- [Bump golang.org/x/net to v0.51.0 to resolve GO-2026-4559](https://github.com/etcd-io/etcd/pull/21440)
---
## v3.6.8 (2026-02-13)
### etcd server
- [Postpone removal of the --max-snapshots flag from v3.7 to v3.8](https://github.com/etcd-io/etcd/pull/21161)
- [Revoke the deprecation of the `--snapshot-count` flag](https://github.com/etcd-io/etcd/pull/21163)
### Package `clientv3`
- [Remove the use of grpc-go's Metadata field](https://github.com/etcd-io/etcd/pull/21241)
### Dependencies
- Bump [golang.org/x/crypto to 0.45.0 to address CVE-2025-47914, and CVE-2025-58181](https://github.com/etcd-io/etcd/pull/21037).
- Compile binaries using [go 1.24.13](https://github.com/etcd-io/etcd/pull/21266). This addresses [CVE-2025-61726](https://github.com/advisories/GHSA-gm9r-q53w-2gh4), [CVE-2025-61731](https://github.com/advisories/GHSA-xvqr-69v8-f3gv), and [CVE-2025-61732](https://github.com/advisories/GHSA-8jvr-vh7g-f8gx).
---
## v3.6.7 (2025-12-17)
### etcd server
- [Print token fingerprint instead of the original tokens in log messages](https://github.com/etcd-io/etcd/pull/20941)
### Dependencies
- Compile binaries using [go 1.24.11](https://github.com/etcd-io/etcd/pull/20998).
---
## v3.6.6 (2025-11-11)
### etcd server
- [Reject watch request with -1 revision to prevent invalid resync behavior on uncompacted etcd](https://github.com/etcd-io/etcd/pull/20707)
- [Change the TLS handshake 'EOF' errors to DEBUG not to spam logs](https://github.com/etcd-io/etcd/pull/20749)
- Fix [endpoint status not retuning the correct storage quota](https://github.com/etcd-io/etcd/pull/20790)
- Fix [`--force-new-cluster can't clean up learners after creating snapshot`](https://github.com/etcd-io/etcd/pull/20896)
- Fix [duplicate metrics collector registration that caused warning messages](https://github.com/etcd-io/etcd/pull/20905)
### Dependencies
- Compile binaries using [go 1.24.10](https://github.com/etcd-io/etcd/pull/20901).
---
## v3.6.5 (2025-09-19)
### etcd server
- [Remove the flag `--experimental-snapshot-catch-up-entries` from `etcd --help` output](https://github.com/etcd-io/etcd/pull/20422)
- Fix [etcd repeatedly log the error "cannot detect storage schema version: missing confstate information"](https://github.com/etcd-io/etcd/pull/20496)
- Fix [etcd may return success for leaseRenew request even when the lease is revoked](https://github.com/etcd-io/etcd/pull/20615)
- Fix [potential data corruption when applySnapshot and defragment happen concurrently](https://github.com/etcd-io/etcd/pull/20650)
### Dependencies
- Compile binaries using [go 1.24.7](https://github.com/etcd-io/etcd/pull/20664).
- [Bump bbolt to v1.4.3](https://github.com/etcd-io/etcd/pull/20513).
---
## v3.6.4 (2025-07-25)
### etcd server
- Fix [etcdserver bootstrap failure when replaying learner promotion operation due to not exist in v3store](https://github.com/etcd-io/etcd/pull/20387)
---
## v3.6.3 (2025-07-22)
### etcd server
- Fix [v2store check (IsMetaStoreOnly) returns wrong result even there is no any auth data](https://github.com/etcd-io/etcd/pull/20370)
- Improve [help message for --quota-backend-bytes](https://github.com/etcd-io/etcd/pull/20352)
---
## v3.6.2 (2025-07-09)
### etcd server
- Fix [Watch on future revision returns old events or notifications](https://github.com/etcd-io/etcd/pull/20286)
### Dependencies
- [Bump bbolt to v1.4.2](https://github.com/etcd-io/etcd/pull/20267)
- Compile binaries using [go 1.23.11](https://github.com/etcd-io/etcd/pull/20314).
---
## v3.6.1 (2025-06-06)
### etcd server
- [Replaced the deprecated/removed `UnaryServerInterceptor` and `StreamServerInterceptor` in otelgrpc with `NewServerHandler`](https://github.com/etcd-io/etcd/pull/20043)
- [Add protection on `PromoteMember` and `UpdateRaftAttributes` to prevent panicking](https://github.com/etcd-io/etcd/pull/20051)
- [Fix the issue that `--force-new-cluster` can't remove all other members in a corner case](https://github.com/etcd-io/etcd/pull/20071)
- Fix [mvcc: avoid double decrement of watcher gauge on close/cancel race](https://github.com/etcd-io/etcd/pull/20067)
- [Add validation to ensure there is no empty v3discovery endpoint](https://github.com/etcd-io/etcd/pull/20113)
### etcdctl
- Fix [command `etcdctl endpoint health` doesn't work when options are set via environment variables](https://github.com/etcd-io/etcd/pull/20121)
### Dependencies
- Compile binaries using [go 1.23.10](https://github.com/etcd-io/etcd/pull/20128).
---
## v3.6.0 (2025-05-15)
There isn't any production code change since v3.6.0-rc.5.
---
## v3.6.0-rc.5 (2025-05-08)
### etcd server
- Fix [the compaction pause duration metric is not emitted for every compaction batch](https://github.com/etcd-io/etcd/pull/19770)
### Package `clientv3`
- [Replace `resolver.State.Addresses` with `resolver.State.Endpoint.Addresses`](https://github.com/etcd-io/etcd/pull/19782).
- [Deprecated the Metadata field in the Endpoint struct from the client/v3/naming/endpoints package](https://github.com/etcd-io/etcd/pull/19842).
### Dependencies
- Compile binaries using [go 1.23.9](https://github.com/etcd-io/etcd/pull/19867).
---
## v3.6.0-rc.4 (2025-04-15)
### etcd server
- [Switch to validating v3 when v2 and v3 are synchronized](https://github.com/etcd-io/etcd/pull/19703).
### Dependencies
- Compile binaries using [go 1.23.8](https://github.com/etcd-io/etcd/pull/19724)
---
## v3.6.0-rc.3 (2025-03-27)
### etcd server
- [Auto sync members in v3store for the issues which have already been affected by #19557](https://github.com/etcd-io/etcd/pull/19636).
- [Move `client/internal/v2` into `server/internel/clientv2`](https://github.com/etcd-io/etcd/pull/19673).
- [Replace ExperimentalMaxLearners with a Feature Gate](https://github.com/etcd-io/etcd/pull/19560).
### etcd grpc-proxy
- Fix [grpcproxy can get stuck in and endless loop causing high CPU usage](https://github.com/etcd-io/etcd/pull/19562)
### Dependencies
- Bump [github.com/golang-jwt/jwt/v5 from 5.2.1 to 5.2.2 to address CVE-2025-30204](https://github.com/etcd-io/etcd/pull/19647).
- Bump [bump golang.org/x/net from v0.37.0 to v0.38.0 to address CVE-2025-22872](https://github.com/etcd-io/etcd/pull/19687).
---
## v3.6.0-rc.2 (2025-03-05)
### etcd server
- Add [Prometheus metric to query server feature gates](https://github.com/etcd-io/etcd/pull/19495).
### Dependencies
- Compile binaries using [go 1.23.7](https://github.com/etcd-io/etcd/pull/19527).
- Bump [golang.org/x/net to v0.36.0 to address CVE-2025-22870](https://github.com/etcd-io/etcd/pull/19531).
- Bump [github.com/grpc-ecosystem/grpc-gateway/v2 to v2.26.3 to fix the issue of etcdserver crashing on receiving REST watch stream requests](https://github.com/etcd-io/etcd/pull/19522).
---
## v3.6.0-rc.1 (2025-02-25)
### etcdctl v3
- Add [`DowngradeInfo` in result of endpoint status](https://github.com/etcd-io/etcd/pull/19471)
### etcd server
- Add [`DowngradeInfo` to endpoint status response](https://github.com/etcd-io/etcd/pull/19471)
### Dependencies
- Bump [golang.org/x/crypto to v0.35.0 to address CVE-2025-22869](https://github.com/etcd-io/etcd/pull/19480).
---
## v3.6.0-rc.0 (2025-02-13)
See [code changes](https://github.com/etcd-io/etcd/compare/v3.5.0...v3.6.0).
### Breaking Changes
- `etcd` will no longer start on data dir created by newer versions (for example etcd v3.6 will not run on v3.7+ data dir). To downgrade data dir please check out `etcdutl migrate` command.
- `etcd` doesn't support serving client requests on the peer listen endpoints (--listen-peer-urls). See [pull/13565](https://github.com/etcd-io/etcd/pull/13565).
- `etcdctl` will sleep(2s) in case of range delete without `--range` flag. See [pull/13747](https://github.com/etcd-io/etcd/pull/13747)
- Applications which depend on etcd v3.6 packages must be built with go version >= v1.18.
#### Flags Removed
- The following flags have been removed:
- `--enable-v2`
- `--experimental-enable-v2v3`
- `--proxy`
- `--proxy-failure-wait`
- `--proxy-refresh-interval`
- `--proxy-dial-timeout`
- `--proxy-write-timeout`
- `--proxy-read-timeout`
### Deprecations
- Deprecated [V2 discovery](https://etcd.io/docs/v3.5/dev-internal/discovery_protocol/).
- Deprecated [SetKeepAlive and SetKeepAlivePeriod in limitListenerConn](https://github.com/etcd-io/etcd/pull/14356).
- Removed [etcdctl defrag --data-dir](https://github.com/etcd-io/etcd/pull/13793).
- Removed [etcdctl snapshot status](https://github.com/etcd-io/etcd/pull/13809).
- Removed [etcdctl snapshot restore](https://github.com/etcd-io/etcd/pull/13809).
- Removed [NewZapCoreLoggerBuilder in server/embed](https://github.com/etcd-io/etcd/pull/19404)
### etcdctl v3
- Add command to generate [shell completion](https://github.com/etcd-io/etcd/pull/13133).
- When print endpoint status, [show db size in use](https://github.com/etcd-io/etcd/pull/13639)
- [Always print the raft_term in decimal](https://github.com/etcd-io/etcd/pull/13711) when displaying member list in json.
- [Add one more field `storageVersion`](https://github.com/etcd-io/etcd/pull/13773) into the response of command `etcdctl endpoint status`.
- Add [`--max-txn-ops`](https://github.com/etcd-io/etcd/pull/14340) flag to make-mirror command.
- Add [`--consistency`](https://github.com/etcd-io/etcd/pull/15261) flag to member list command.
- Display [field `hash_revision`](https://github.com/etcd-io/etcd/pull/14812) for `etcdctl endpoint hash` command.
- Add [`--max-request-bytes` and `--max-recv-bytes`](https://github.com/etcd-io/etcd/pull/18718) global flags.
### etcdutl v3
- Add command to generate [shell completion](https://github.com/etcd-io/etcd/pull/13142).
- Add `migrate` command for downgrading/upgrading etcd data dir files.
- Add [optional --bump-revision and --mark-compacted flag to etcdutl snapshot restore operation](https://github.com/etcd-io/etcd/pull/16029).
- Add [hashkv](https://github.com/etcd-io/etcd/pull/15965) command to print hash of keys and values up to given revision
- Removed [legacy etcdutl backup](https://github.com/etcd-io/etcd/pull/16662)
- [Count the number of keys from users perspective](https://github.com/etcd-io/etcd/pull/19344)
### Package `clientv3`
- [Support serializable `MemberList` operation](https://github.com/etcd-io/etcd/pull/15261).
### Package `server`
- Package `mvcc` was moved to `storage/mvcc`
- Package `mvcc/backend` was moved to `storage/backend`
- Package `mvcc/buckets` was moved to `storage/schema`
- Package `wal` was moved to `storage/wal`
- Package `datadir` was moved to `storage/datadir`
### Package `raft`
- [Decouple raft from etcd](https://github.com/etcd-io/etcd/issues/14713). Migrated raft to a separate [repository](https://github.com/etcd-io/raft), and renamed raft module to `go.etcd.io/raft/v3`.
### etcd server
- Add [`etcd --log-format`](https://github.com/etcd-io/etcd/pull/13339) flag to support log format.
- Add [`etcd --experimental-max-learners`](https://github.com/etcd-io/etcd/pull/13377) flag to allow configuration of learner max membership.
- Add [`etcd --experimental-enable-lease-checkpoint-persist`](https://github.com/etcd-io/etcd/pull/13508) flag to handle upgrade from v3.5.2 clusters with this feature enabled.
- Add [`etcdctl make-mirror --rev`](https://github.com/etcd-io/etcd/pull/13519) flag to support incremental mirror.
- Add [v3 discovery](https://github.com/etcd-io/etcd/pull/13635) to bootstrap a new etcd cluster.
- Add [field `storage`](https://github.com/etcd-io/etcd/pull/13772) into the response body of endpoint `/version`.
- Add [`etcd --max-concurrent-streams`](https://github.com/etcd-io/etcd/pull/14169) flag to configure the max concurrent streams each client can open at a time, and defaults to math.MaxUint32.
- Add [`etcd grpc-proxy --experimental-enable-grpc-logging`](https://github.com/etcd-io/etcd/pull/14266) flag to logging all grpc requests and responses.
- Add [`etcd --experimental-compact-hash-check-enabled --experimental-compact-hash-check-time`](https://github.com/etcd-io/etcd/issues/14039) flags to support enabling reliable corruption detection on compacted revisions.
- Add [Protection on maintenance request when auth is enabled](https://github.com/etcd-io/etcd/pull/14663).
- Graduated [`--experimental-warning-unary-request-duration` to `--warning-unary-request-duration`](https://github.com/etcd-io/etcd/pull/14414). Note the experimental flag is deprecated and will be decommissioned in v3.7.
- Add [field `hash_revision` into `HashKVResponse`](https://github.com/etcd-io/etcd/pull/14537).
- Add [`etcd --experimental-snapshot-catch-up-entries`](https://github.com/etcd-io/etcd/pull/15033) flag to configure number of entries for a slow follower to catch up after compacting the raft storage entries and defaults to 5k.
- Decreased [`--snapshot-count` default value from 100,000 to 10,000](https://github.com/etcd-io/etcd/pull/15408)
- Add [`etcd --tls-min-version --tls-max-version`](https://github.com/etcd-io/etcd/pull/15156) to enable support for TLS 1.3.
- Add [quota to endpoint status response](https://github.com/etcd-io/etcd/pull/17877)
- Add [feature gate `SetMemberLocalAddr`](https://github.com/etcd-io/etcd/pull/19413) to [enable using the first specified and non-loopback local address from initial-advertise-peer-urls as the local address when communicating with a peer]((https://github.com/etcd-io/etcd/pull/17661))
- Add [Support multiple values for allowed client and peer TLS identities](https://github.com/etcd-io/etcd/pull/18015)
- Add [`embed.Config.GRPCAdditionalServerOptions`](https://github.com/etcd-io/etcd/pull/14066) to support updating the default internal gRPC configuration for embedded use cases.
### etcd grpc-proxy
- Add [`etcd grpc-proxy start --endpoints-auto-sync-interval`](https://github.com/etcd-io/etcd/pull/14354) flag to enable and configure interval of auto sync of endpoints with server.
- Add [`etcd grpc-proxy start --listen-cipher-suites`](https://github.com/etcd-io/etcd/pull/14308) flag to support adding configurable cipher list.
- Add [`tls min/max version to grpc proxy`](https://github.com/etcd-io/etcd/pull/18816) to support setting TLS min and max version.
### tools/benchmark
- [Add etcd client autoSync flag](https://github.com/etcd-io/etcd/pull/13416)
### Metrics, Monitoring
See [List of metrics](https://etcd.io/docs/latest/metrics/) for all metrics per release.
- Add [`etcd_disk_defrag_inflight`](https://github.com/etcd-io/etcd/pull/13371).
- Add [`etcd_debugging_server_alarms`](https://github.com/etcd-io/etcd/pull/14276).
- Add [`etcd_server_range_duration_seconds`](https://github.com/etcd-io/etcd/pull/17983).
### Go
- Require [Go 1.23+](https://github.com/etcd-io/etcd/pull/16594).
- Compile with [Go 1.23+](https://go.dev/doc/devel/release#go1.21.minor). Please refer to [gc-guide](https://go.dev/doc/gc-guide) to configure `GOGC` and `GOMEMLIMIT` properly.
### Other
- Use Distroless as base image to make the image less vulnerable and reduce image size.
- [Upgrade grpc-gateway from v1 to v2](https://github.com/etcd-io/etcd/pull/16595).
- [Switch from grpc-ecosystem/go-grpc-prometheus to grpc-ecosystem/go-grpc-middleware/providers/prometheus](https://github.com/etcd-io/etcd/pull/19195).
---
================================================
FILE: CHANGELOG/CHANGELOG-3.7.md
================================================
Previous change logs can be found at [CHANGELOG-3.6](https://github.com/etcd-io/etcd/blob/main/CHANGELOG/CHANGELOG-3.6.md).
---
## v3.7.0 (TBD)
### Breaking Changes
- [Removed all deprecated experimental flags](https://github.com/etcd-io/etcd/pull/19959)
- [Removed v2discovery](https://github.com/etcd-io/etcd/pull/20109)
- [Removed client/v2](https://github.com/etcd-io/etcd/pull/20117)
- [Removed v2 request and apply_v2.go](https://github.com/etcd-io/etcd/pull/21263)
### etcd server
- [Update go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc to v0.61.0 and replaced the deprecated `UnaryServerInterceptor` and `StreamServerInterceptor` with `NewServerHandler`](https://github.com/etcd-io/etcd/pull/20017)
- [Add Support for Unix Socket endpoints](https://github.com/etcd-io/etcd/pull/19760)
- [Improves performance of lease and user/role operations (up to 2x) by updating `(*readView) Rev()` to use `SharedBufReadTxMode`](https://github.com/etcd-io/etcd/pull/20411)
- [Allow client to retrieve AuthStatus without authentication](https://github.com/etcd-io/etcd/pull/20802)
- [Add FastLeaseKeepAlive feature to enable faster lease renewal by skipping the wait for the applied index](https://github.com/etcd-io/etcd/pull/20589)
- [Bootstrap etcd from v3store](https://github.com/etcd-io/etcd/issues/20187), see changes below,
- [Stop loading v2 snapshot files](https://github.com/etcd-io/etcd/pull/21107)
- [Initialize confState from v3 store on bootstrap](https://github.com/etcd-io/etcd/pull/21138)
- [Remove flag `--max-snapshots` in 3.8 rather than 3.7](https://github.com/etcd-io/etcd/pull/21160)
- [Keep the `--snapshot-count` flag](https://github.com/etcd-io/etcd/pull/21162)
### Package `clientv3`
- Allow setting JWT directly by users, see https://github.com/etcd-io/etcd/pull/16803 and https://github.com/etcd-io/etcd/pull/20747.
- [Function etcdClientDebugLevel is renamed to ClientLogLevel and made it public](https://github.com/etcd-io/etcd/pull/20006)
### Package `pkg`
- [Optimize find performance by splitting intervals with the same left endpoint by their right endpoints](https://github.com/etcd-io/etcd/pull/19768)
- [netutil: Refactor IPv6 address comparison logic](https://github.com/etcd-io/etcd/pull/20365)
### Dependencies
- Compile binaries with [Go 1.26](https://go.dev/doc/devel/release#go1.26.minor).
- [Migrate the deprecated go-grpc-middleware v1 logging and tags libraries to v2 interceptors](https://github.com/etcd-io/etcd/pull/20420)
### Deprecations
- Deprecated [UsageFunc in pkg/cobrautl](https://github.com/etcd-io/etcd/pull/18356).
### etcdctl
- [Organize etcdctl commands](https://github.com/etcd-io/etcd/pull/20162) to make them more concise and easier to understand.
- [Hide the global flags](https://github.com/etcd-io/etcd/pull/20493) to make the output of `etcdctl --help` looks cleaner and is consistent with kubectl.
### etcdutl
- [Add a timeout flag to all etcdutl commands](https://github.com/etcd-io/etcd/pull/20708) when waiting to acquire a file lock on the database file.
### Metrics, Monitoring
See [List of metrics](https://etcd.io/docs/latest/metrics/) for all metrics per release.
- Add [`etcd_server_request_duration_seconds`](https://github.com/etcd-io/etcd/pull/21038).
- Add [the following metrics related to watch send loop](https://github.com/etcd-io/etcd/pull/21030),
- etcd_debugging_server_watch_send_loop_watch_stream_duration_seconds
- etcd_debugging_server_watch_send_loop_watch_stream_duration_per_event_seconds
- etcd_debugging_server_watch_send_loop_control_stream_duration_seconds
- etcd_debugging_server_watch_send_loop_progress_duration_seconds
================================================
FILE: CHANGELOG/CHANGELOG-4.0.md
================================================
Previous change logs can be found at [CHANGELOG-3.x](https://github.com/etcd-io/etcd/blob/main/CHANGELOG/CHANGELOG-3.x.md).
---
## v4.0.0 (TBD)
See [code changes](https://github.com/etcd-io/etcd/compare/v3.5.0...v4.0.0) and [v4.0 upgrade guide](https://etcd.io/docs/latest/upgrades/upgrade_4_0/) for any breaking changes.
**Again, before running upgrades from any previous release, please make sure to read change logs below and [v4.0 upgrade guide](https://etcd.io/docs/latest/upgrades/upgrade_4_0/).**
### Breaking Changes
- [Secure etcd by default](https://github.com/etcd-io/etcd/issues/9475)?
- Deprecate [`etcd --proxy*`](TODO) flags; **no more v2 proxy**.
- Deprecate [v2 storage backend](https://github.com/etcd-io/etcd/issues/9232); **no more v2 store**.
- v2 API is still supported via [v2 emulation](TODO).
- Deprecate [`etcdctl backup`](TODO) command.
- `clientv3.Client.KeepAlive(ctx context.Context, id LeaseID) (<-chan *LeaseKeepAliveResponse, error)` is now [`clientv4.Client.KeepAlive(ctx context.Context, id LeaseID) <-chan *LeaseKeepAliveResponse`](TODO).
- Similar to `Watch`, [`KeepAlive` does not return errors](https://github.com/etcd-io/etcd/issues/7488).
- If there's an unknown server error, kill all open channels and create a new stream on the next `KeepAlive` call.
- Rename `github.com/coreos/client` to `github.com/coreos/clientv2`.
- [`etcd --experimental-initial-corrupt-check`](TODO) has been deprecated.
- Use [`etcd --initial-corrupt-check`](TODO) instead.
- [`etcd --experimental-corrupt-check-time`](TODO) has been deprecated.
- Use [`etcd --corrupt-check-time`](TODO) instead.
- Enable TLS 1.13, deprecate TLS cipher suites.
### etcd server
- [`etcd --initial-corrupt-check`](TODO) flag is now stable (`etcd --experimental-initial-corrupt-check` has been deprecated).
- `etcd --initial-corrupt-check=true` by default, to check cluster database hashes before serving client/peer traffic.
- [`etcd --corrupt-check-time`](TODO) flag is now stable (`etcd --experimental-corrupt-check-time` has been deprecated).
- `etcd --corrupt-check-time=12h` by default, to check cluster database hashes for every 12-hour.
- Enable TLS 1.13, deprecate TLS cipher suites.
### Go
- Require [*Go 2*](https://blog.golang.org/go2draft).
---
================================================
FILE: CHANGELOG/README.md
================================================
# Change logs
## Production recommendation
The minimum recommended etcd versions to run in **production** are v3.4.22+ and v3.5.6+. Refer to the [versioning policy](https://etcd.io/docs/v3.5/op-guide/versioning/) for more details.
### v3.5 data corruption issue
Running etcd v3.5.2, v3.5.1 and v3.5.0 under high load can cause a data corruption issue.
If etcd process is killed, occasionally some committed transactions are not reflected on all the members.
Recommendation is to upgrade to v3.5.4+.
If you have encountered data corruption, please follow instructions on https://etcd.io/docs/v3.5/op-guide/data_corruption/.
## Change log rules
1. Each patch release only includes changes against previous patch release.
For example, the change log of v3.5.5 should only include items which are new to v3.5.4.
2. For the first release (e.g. 3.4.0, 3.5.0, 3.6.0, 4.0.0 etc.) for each minor or major
version, it only includes changes which are new to the first release of previous minor
or major version. For example, v3.5.0 should only include items which are new to v3.4.0,
and v3.6.0 should only include items which are new to v3.5.0.
================================================
FILE: CONTRIBUTING.md
================================================
# How to contribute
etcd is Apache 2.0 licensed and accepts contributions via GitHub pull requests.
This document outlines the basics of contributing to etcd.
This is a rough outline of what a contributor's workflow looks like:
* [Find something to work on](#Find-something-to-work-on)
* [Check for flaky tests](#Check-for-flaky-tests)
* [Set up development environment](#Set-up-development-environment)
* [Implement your change](#Implement-your-change)
* [Commit your change](#Commit-your-change)
* [Create a pull request](#Create-a-pull-request)
* [Get your pull request reviewed](#Get-your-pull-request-reviewed)
If you have any questions, please reach out using one of the methods listed in [contact].
[contact]: ./README.md#Contact
## Learn more about etcd
Before making a change please look through the resources below to learn more about etcd and tools used for development.
* Please learn about [Git](https://github.com/git-guides) version control system used in etcd.
* Read the [etcd learning resources](https://etcd.io/docs/v3.5/learning/)
* Read the [etcd community membership](/Documentation/contributor-guide/community-membership.md)
* Watch [etcd deep dive](https://www.youtube.com/watch?v=D2pm6ufIt98&t=927s)
* Watch [etcd code walkthrough](https://www.youtube.com/watch?v=H3XaSF6wF7w)
## Find something to work on
All the work in the etcd project is tracked in [GitHub issue tracker].
Issues should be properly labeled making it easy to find something for you.
Depending on your interest and experience you should check different labels:
* If you are just starting, check issues labeled with [good first issue].
* When you feel more comfortable in your contributions, check out [help wanted].
* Advanced contributors can try to help with issues labeled [priority/important] covering the most relevant work at the time.
If any of the aforementioned labels don't have unassigned issues, please [contact] one of the [maintainers] asking to triage more issues.
[github issue tracker]: https://github.com/etcd-io/etcd/issues
[good first issue]: https://github.com/search?type=issues&q=org%3Aetcd-io+state%3Aopen++label%3A%22good+first+issue%22
[help wanted]: https://github.com/search?type=issues&q=org%3Aetcd-io+state%3Aopen++label%3A%22help+wanted%22
[maintainers]: https://github.com/etcd-io/etcd/blob/main/OWNERS
[priority/important]: https://github.com/search?type=issues&q=org%3Aetcd-io+state%3Aopen++label%3A%22priority%2Fimportant%22
### Check for flaky tests
The project could always use some help to deflake tests. [These](https://github.com/etcd-io/etcd/issues?q=is%3Aissue+is%3Aopen+label%3Atype%2Fflake) are the currently open flaky test issues.
For more, because etcd uses Kubernetes' prow infrastructure to run CI jobs, the past test results can be viewed at [testgrid](https://testgrid.k8s.io/sig-etcd).
| Tests | Status |
| ----- | ------ |
| periodics e2e-amd64 | [](https://testgrid.k8s.io/q/summary/sig-etcd-periodics/ci-etcd-e2e-amd64) |
| presubmit build | [](https://testgrid.k8s.io/q/summary/sig-etcd-presubmits/pull-etcd-build) |
| presubmit e2e-amd64 | [](https://testgrid.k8s.io/q/summary/sig-etcd-presubmits/pull-etcd-e2e-amd64) |
| presubmit unit-test-amd64 | [](https://testgrid.k8s.io/q/summary/sig-etcd-presubmits/pull-etcd-unit-test-amd64) |
| presubmit verify | [](https://testgrid.k8s.io/q/summary/sig-etcd-presubmits/pull-etcd-verify) |
| postsubmit build | [](https://testgrid.k8s.io/q/summary/sig-etcd-postsubmits/post-etcd-build) |
If you find any flaky tests on testgrid, please
1. Check [existing issues](https://github.com/etcd-io/etcd/issues?q=is%3Aissue+is%3Aopen+label%3Atype%2Fflake) to see if an issue has already been opened for this test. If not, open an issue with the `type/flake` label.
2. Try to reproduce the flaky test on your machine via [`stress`](https://pkg.go.dev/golang.org/x/tools/cmd/stress), for example, to reproduce the failure of `TestPeriodicSkipRevNotChange`:
```bash
# install the stress utility
go install golang.org/x/tools/cmd/stress@latest
cd server/etcdserver/api/v3compactor
# compile the test
go test -v -c -count 1
# run the compiled test file using stress
stress -p=8 ./v3compactor.test -test.run “^TestPeriodicSkipRevNotChange$”
```
3. Fix it.
## Set up development environment
The etcd project supports two options for development:
1. Manually set up the local environment.
2. Automatically set up [devcontainer](https://containers.dev).
For both options, the only supported architecture is `linux-amd64`. Bug reports for other environments will generally be ignored. Supporting new environments requires the introduction of proper tests and maintainer support that is currently lacking in the etcd project.
If you would like etcd to support your preferred environment you can [file an issue].
### Option 1 - Manually set up the local environment
This is the original etcd development environment, is most supported, and is backward compatible for the development of older etcd versions.
Follow the steps below to set up the environment:
- [Clone the repository](https://docs.github.com/en/repositories/creating-and-managing-repositories/cloning-a-repository)
- Install Go by following [installation](https://go.dev/doc/install). Please check the minimal go version in [go.mod file](./go.mod#L3).
- Install build tools:
- [`make`](https://www.gnu.org/software/make/): For Debian-based distributions
you can run `sudo apt-get install build-essential`
- [`protoc`](https://protobuf.dev/): You can download it for your os. Use
version
[`v3.20.3`](https://github.com/protocolbuffers/protobuf/releases/tag/v3.20.3).
- [`yamllint`](https://www.yamllint.com/): For Debian-based distribution you
can run `sudo apt-get install yamllint`
- [`jq`](https://jqlang.github.io/jq/): For Debian-based distribution you can
run `sudo apt-get install jq`
- [`xz`](https://tukaani.org/xz/): For Debian-based distribution you can run
`sudo apt-get install xz-utils`
- Verify that everything is installed by running `make build`
Note: `make build` runs with `-v`. Other build flags can be added through env `GO_BUILD_FLAGS`, **if required**. Eg.,
```console
GO_BUILD_FLAGS="-buildmode=pie" make build
```
### Option 2 - Automatically set up devcontainer
This is a more recently added environment that aims to make it faster for new contributors to get started with etcd. This option is supported for etcd versions 3.6 onwards.
This option can be [used locally](https://code.visualstudio.com/docs/devcontainers/tutorial) on a system running Visual Studio Code and Docker, or in a remote cloud-based [Codespaces](https://github.com/features/codespaces) environment.
To get started, create a codespace for this repository by clicking this 👇
[](https://github.com/codespaces/new?hide_repo_select=true&ref=main&repo=11225014)
A codespace will open in a web-based version of Visual Studio Code. The [dev container](.devcontainer/devcontainer.json) is fully configured with the software needed for this project.
**Note**: Dev containers is an open spec which is supported by [GitHub Codespaces](https://github.com/codespaces) and [other tools](https://containers.dev/supporting).
[file an issue]: https://github.com/etcd-io/etcd/issues/new/choose
## Implement your change
etcd code should follow the coding style suggested by the Golang community.
See the [style doc](https://go.dev/wiki/CodeReviewComments) for details.
Please ensure that your change passes static analysis (requires
[golangci-lint](https://golangci-lint.run/welcome/install/)):
- `make verify` to verify if all checks pass.
- `make verify-*` to verify a single check, for example, `make verify-bom` to verify if `bill-of-materials.json` file is up-to-date.
- `make fix` to fix all checks.
- `make fix-*` to fix a single check, for example, `make fix-bom` to update `bill-of-materials.json`.
Please ensure that your change passes tests.
- `make test-unit` to run unit tests.
- `make test-integration` to run integration tests.
- `make test-e2e` to run e2e tests.
All changes are expected to come with a unit test.
All new features are expected to have either e2e or integration tests.
## Commit your change
etcd follows a rough convention for commit messages:
* First line:
* Should start with the name of the package (for example `etcdserver`, `etcdctl`) followed by the `:` character.
* Describe the `what` behind the change
* Optionally, the author might provide the `why` behind the change in the main commit message body.
* Last line should be `Signed-off-by: firstname lastname ` (can be automatically generate by providing `--signoff` to git commit command).
Example of commit message:
```
etcdserver: add grpc interceptor to log info on incoming requests
To improve debuggability of etcd v3. Added a grpc interceptor to log
info on incoming requests to etcd server. The log output includes
remote client info, request content (with value field redacted), request
handling latency, response size, etc. Uses zap logger if available,
otherwise uses capnslog.
Signed-off-by: FirstName LastName
```
## Create a pull request
Please follow the [making a pull request](https://docs.github.com/en/get-started/quickstart/contributing-to-projects#making-a-pull-request) guide.
If you are still working on the pull request, you can convert it to a draft by clicking `Convert to draft` link just below the list of reviewers.
Multiple small PRs are preferred over single large ones (>500 lines of code).
Please make sure there is an associated issue for each PR you submit. Create one if it doesn't exist yet, and close the issue
once the PR gets merged and has been backported to previous stable releases, if necessary. If there are multiple PRs linked to
the same issue, refrain from closing the issue until all PRs have been merged and, if needed, backported to previous stable
releases.
## Get your pull request reviewed
Before requesting review please ensure that all GitHub and Prow checks are successful. In some cases your pull request may have the label `needs-ok-to-test`. If so an `etcd-io` organisation member will leave a comment on your pull request with `/ok-to-test` to trigger all checks to be run.
It might happen that some unrelated tests on your PR are failing, due to their flakiness.
In such cases please [file an issue] to deflake the problematic test and ask one of [maintainers] to rerun the tests.
If all checks were successful feel free to reach out for review from people that were involved in the original discussion or [maintainers].
Depending on the complexity of the PR it might require between 1 and 2 maintainers to approve your change before merging.
Thanks for contributing!
================================================
FILE: DCO
================================================
Developer Certificate of Origin
Version 1.1
Copyright (C) 2004, 2006 The Linux Foundation and its contributors.
660 York Street, Suite 102,
San Francisco, CA 94110 USA
Everyone is permitted to copy and distribute verbatim copies of this
license document, but changing it is not allowed.
Developer's Certificate of Origin 1.1
By making a contribution to this project, I certify that:
(a) The contribution was created in whole or in part by me and I
have the right to submit it under the open source license
indicated in the file; or
(b) The contribution is based upon previous work that, to the best
of my knowledge, is covered under an appropriate open source
license and I have the right under that license to submit that
work with modifications, whether created in whole or in part
by me, under the same open source license (unless I am
permitted to submit under a different license), as indicated
in the file; or
(c) The contribution was provided directly to me by some other
person who certified (a), (b) or (c) and I have not modified
it.
(d) I understand and agree that this project and the contribution
are public and that a record of the contribution (including all
personal information I submit with it, including my sign-off) is
maintained indefinitely and may be redistributed consistent with
this project or the open source license(s) involved.
================================================
FILE: Dockerfile
================================================
ARG ARCH=amd64
FROM --platform=linux/${ARCH} gcr.io/distroless/static-debian12@sha256:20bc6c0bc4d625a22a8fde3e55f6515709b32055ef8fb9cfbddaa06d1760f838
ADD etcd /usr/local/bin/
ADD etcdctl /usr/local/bin/
ADD etcdutl /usr/local/bin/
WORKDIR /var/etcd/
WORKDIR /var/lib/etcd/
EXPOSE 2379 2380
# Define default command.
CMD ["/usr/local/bin/etcd"]
================================================
FILE: Documentation/OWNERS
================================================
# See the OWNERS docs at https://go.k8s.io/owners
labels:
- area/documentation
================================================
FILE: Documentation/README.md
================================================
This directory includes etcd project internal documentation for new and existing contributors.
For user and developer documentation please go to [etcd.io](https://etcd.io/),
which is developed in [website](https://github.com/etcd-io/website/) repo.
================================================
FILE: Documentation/contributor-guide/branch_management.md
================================================
# Branch management
## Guide
* New development occurs on the [main branch][main].
* The main branch should always have a green build!
* Backwards-compatible bug fixes should target the main branch and subsequently be ported to stable branches.
* Once the main branch is ready for release, it will be tagged and become the new stable branch.
The etcd team has adopted a *rolling release model* and supports two stable versions of etcd.
### Main branch
The `main` branch is our development branch. All new features land here first.
To try new and experimental features, pull `main` and play with it. Note that `main` may not be stable because new features may introduce bugs.
Before the release of the next stable version, feature PRs will be frozen. A [release manager](./release.md#release-management) will be assigned to the major/minor version and will lead the etcd community in testing, bug-fix, and documentation of the release for one to two weeks.
### Stable branches
All branches with the prefix `release-` are considered _stable_ branches.
After every minor release ([semver.org](https://semver.org/)), we will have a new stable branch for that release, managed by a [patch release manager](./release.md#release-management). We will keep fixing the backward-compatible bugs for the latest two stable releases. A _patch_ release to each supported release branch, incorporating any bug fixes, will be once every two weeks, given any patches.
[main]: https://github.com/etcd-io/etcd/tree/main
================================================
FILE: Documentation/contributor-guide/bump_etcd_version_k8s.md
================================================
# Bump etcd Version in Kubernetes
This guide will walk through the update of etcd in Kubernetes to a new version (`kubernetes/kubernetes` repository).
> Currently we bump etcd v3.5.x for K8s release-1.33 and lower versions, and we bump etcd v3.6.x for K8s release-1.34 and higher versions.
You can use this [issue](https://github.com/kubernetes/kubernetes/issues/131101) as a reference when updating the etcd version in Kubernetes.
Bumping the etcd version in Kubernetes consists of two steps.
* Bump etcd client SDK
* Bump etcd image
> The commented lines in this document signifies the line to be changed
## Bump etcd client SDK
> Reference: [link](https://github.com/kubernetes/kubernetes/pull/131103)
You can refer to the guide [here](https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/vendor.md) under the **Adding or updating a dependency** section.
1. Get all the etcd modules used in Kubernetes.
```bash
$ grep 'go.etcd.io/etcd/' go.mod | awk '{print $1}'
go.etcd.io/etcd/api/v3
go.etcd.io/etcd/client/pkg/v3
go.etcd.io/etcd/client/v3
go.etcd.io/etcd/client/v2
go.etcd.io/etcd/pkg/v3
go.etcd.io/etcd/raft/v3
go.etcd.io/etcd/server/v3
```
2. For each module, in the root directory of the `kubernetes/kubernetes` repository, fetch the new version in `go.mod` using the following command (using `client/v3` as an example):
```bash
hack/pin-dependency.sh go.etcd.io/etcd/client/v3 NEW_VERSION
```
3. Rebuild the `vendor` directory and update the `go.mod` files for all staging repositories using the command below. This automatically updates the licenses.
```bash
hack/update-vendor.sh
```
4. Check if the new dependency requires newer versions of existing dependencies we have pinned. You can check this by:
* Running `hack/lint-dependencies.sh` against your branch and against `master` and comparing the results.
* Checking if any new `replace` directives were added to `go.mod` files of components inside the staging directory.
## Bump etcd image
### Build etcd image
> Reference: [link 1](https://github.com/kubernetes/kubernetes/pull/131105) [link 2](https://github.com/kubernetes/kubernetes/pull/131126)
1. In `build/dependencies.yaml`, update the `version` of `etcd-image` to the new version. Update `golang: etcd release version` if necessary.
```yaml
- name: "etcd-image"
# version: 3.5.17
version: 3.5.21
refPaths:
- path: cluster/images/etcd/Makefile
match: BUNDLED_ETCD_VERSIONS\?|
---
- name: "golang: etcd release version"
# version: 1.22.9
version: 1.23.7 # https://github.com/etcd-io/etcd/blob/main/CHANGELOG/CHANGELOG-3.6.md
```
2. In `cluster/images/etcd/Makefile`, include the new version in `BUNDLED_ETCD_VERSIONS` and update the `LATEST_ETCD_VERSION` as well (the image tag will be generated from the `LATEST_ETCD_VERSION`). Update `GOLANG_VERSION` according to the version used to compile that release version (`"golang: etcd release version"` in step 1).
```Makefile
# BUNDLED_ETCD_VERSIONS?=3.4.18 3.5.17
BUNDLED_ETCD_VERSIONS?=3.4.18 3.5.21
# LATEST_ETCD_VERSION?=3.5.17
LATEST_ETCD_VERSION?=3.5.21
# GOLANG_VERSION := 1.22.9
GOLANG_VERSION := 1.23.7
```
3. In `cluster/images/etcd/migrate/options.go`, include the new version in the `supportedEtcdVersions` slice.
```go
var (
// supportedEtcdVersions = []string{"3.4.18", "3.5.17"}
supportedEtcdVersions = []string{"3.4.18", "3.5.21"}
)
```
### Publish etcd image
> Reference: [link](https://github.com/kubernetes/k8s.io/pull/7957)
1. When the previous step is merged, a post-commit job will run to build the image. You can find the newly built image in the [registry](https://gcr.io/k8s-staging-etcd/etcd).
2. Locate the newly built image and copy its SHA256 digest.
3. Inside the `kubernetes/k8s.io` repository, in `registry.k8s.io/images/k8s-staging-etcd/images.yaml`, create a new entry for the desired version and copy the SHA256 digest.
```yaml
"sha256:b4a9e4a7e1cf08844c7c4db6a19cab380fbf0aad702b8c01e578e9543671b9f9": ["3.5.17-0"]
# ADD:
"sha256:d58c035df557080a27387d687092e3fc2b64c6d0e3162dc51453a115f847d121": ["3.5.21-0"]
```
### Update to use the new etcd image
> Reference: [link](https://github.com/kubernetes/kubernetes/pull/131144)
1. In `build/dependencies.yaml`, change the `version` of `etcd` to the new version.
```yaml
# etcd
- name: "etcd"
# version: 3.5.17
version: 3.5.21
refPaths:
- path: cluster/gce/manifests/etcd.manifest
match: etcd_docker_tag|etcd_version
```
2. In `cluster/gce/manifests/etcd.manifest`, change the image tag to the new image tag and `TARGET_VERSION` to the new version.
```manifest
// "image": "{{ pillar.get('etcd_docker_repository', 'registry.k8s.io/etcd') }}:{{ pillar.get('etcd_docker_tag', '3.5.17-0') }}",
"image": "{{ pillar.get('etcd_docker_repository', 'registry.k8s.io/etcd') }}:{{ pillar.get('etcd_docker_tag', '3.5.21-0') }}",
---
{ "name": "TARGET_VERSION",
// "value": "{{ pillar.get('etcd_version', '3.5.17') }}"
"value": "{{ pillar.get('etcd_version', '3.5.21') }}"
},
```
3. In `cluster/gce/upgrade-aliases.sh`, update the exports for `ETCD_IMAGE` to the new image tag and `ETCD_VERSION` to the new version.
```sh
# export ETCD_IMAGE=3.5.17-0
export ETCD_IMAGE=3.5.21-0
# export ETCD_VERSION=3.5.17
export ETCD_VERSION=3.5.21
```
4. In `cmd/kubeadm/app/constants/constants.go`, change the `DefaultEtcdVersion` to the new version. In the same file, update `SupportedEtcdVersion` accordingly.
```go
// DefaultEtcdVersion = "3.5.17-0"
DefaultEtcdVersion = "3.5.21-0"
---
SupportedEtcdVersion = map[uint8]string{
// 30: "3.5.17-0",
// 31: "3.5.17-0",
// 32: "3.5.17-0",
// 33: "3.5.17-0",
30: "3.5.21-0",
31: "3.5.21-0",
32: "3.5.21-0",
33: "3.5.21-0",
}
```
5. In `hack/lib/etcd.sh`, update the `ETCD_VERSION`.
```sh
# ETCD_VERSION=${ETCD_VERSION:-3.5.17}
ETCD_VERSION=${ETCD_VERSION:-3.5.21}
```
6. In `staging/src/k8s.io/sample-apiserver/artifacts/example/deployment.yaml`, update the etcd image used.
```yaml
- name: etcd
# image: gcr.io/etcd-development/etcd:v3.5.17
image: gcr.io/etcd-development/etcd:v3.5.21
```
7. In `test/utils/image/manifest.go`, update the etcd image tag.
```go
// configs[Etcd] = Config{list.GcEtcdRegistry, "etcd", "3.5.17-0"}
configs[Etcd] = Config{list.GcEtcdRegistry, "etcd", "3.5.21-0"}
```
================================================
FILE: Documentation/contributor-guide/community-membership.md
================================================
# Community membership
This doc outlines the various responsibilities of contributor roles in etcd.
| Role | Responsibilities | Requirements | Defined by |
|------------|----------------------------------------------|---------------------------------------------------------------|-------------------------------|
| Member | Active contributor in the community | Sponsored by 2 reviewers and multiple contributions | etcd GitHub org member |
| Reviewer | Review contributions from other members | History of review and authorship | [OWNERS] file reviewer entry |
| Maintainer | Set direction and priorities for the project | Demonstrated responsibility and excellent technical judgement | [OWNERS] file approver entry |
## New contributors
New contributors should be welcomed to the community by existing members,
helped with PR workflow, and directed to relevant documentation and
communication channels.
## Established community members
Established community members are expected to demonstrate their adherence to the
principles in this document, familiarity with project organization, roles,
policies, procedures, conventions, etc., and technical and/or writing ability.
Role-specific expectations, responsibilities, and requirements are enumerated
below.
## Member
Members are continuously active contributors to the community. They can have
issues and PRs assigned to them. Members are expected to remain active
contributors to the community.
**Defined by:** Member of the etcd GitHub organization.
### Member requirements
- Enabled [two-factor authentication] on their GitHub account
- Have made multiple contributions to the project or community. Contribution may include, but is not limited to:
- Authoring or reviewing PRs on GitHub. At least one PR must be **merged**.
- Filing or commenting on issues on GitHub
- Contributing to community discussions (e.g. meetings, Slack, email discussion
forums, Stack Overflow)
- Subscribed to [etcd-dev@googlegroups.com](https://groups.google.com/g/etcd-dev)
- Have read the [contributor guide]
- Sponsored by two active maintainers or reviewers.
- Sponsors must be from multiple member companies to demonstrate integration across the community.
- With no objections from other maintainers
- Open a [membership nomination] issue against the `kubernetes/org` repo
- Ensure your sponsors are @mentioned on the issue
- Make sure that the list of contributions included is representative of your work on the project.
- Members can be removed by a supermajority of the maintainers or can resign by notifying
the maintainers.
### Member responsibilities and privileges
- Responsive to issues and PRs assigned to them
- Granted "triage access" to etcd project
- Active owner of code they have contributed (unless ownership is explicitly transferred)
- Code is well-tested
- Tests consistently pass
- Addresses bugs or issues discovered after code is accepted
**Note:** Members who frequently contribute code are expected to proactively
perform code reviews and work towards becoming a *reviewer*.
## Reviewers
Reviewers are contributors who have demonstrated greater skill in
reviewing the code from other contributors. They are knowledgeable about both
the codebase and software engineering principles. Their LGTM counts towards
merging a code change into the project. A reviewer is generally on the ladder towards
maintainership.
**Defined by:** *reviewers* entry in the [OWNERS] file.
### Reviewer requirements
- member for at least 3 months.
- Primary reviewer for at least 5 PRs to the codebase.
- Reviewed or contributed at least 20 substantial PRs to the codebase.
- Knowledgeable about the codebase.
- Sponsored by two active maintainers.
- Sponsors must be from multiple member companies to demonstrate integration across the community.
- With no objections from other maintainers
- Reviewers can be removed by a supermajority of the maintainers or can resign by notifying
the maintainers.
### Reviewer responsibilities and privileges
- Code reviewer status may be a precondition to accepting large code contributions
- Responsible for project quality control via code reviews
- Focus on code quality and correctness, including testing and factoring
- May also review for more holistic issues, but not a requirement
- Expected to be responsive to review requests
- Assigned PRs to review related to area of expertise
- Assigned test bugs related to area of expertise
- Granted "triage access" to etcd project
## Maintainers
Maintainers are first and foremost contributors who have shown they
are committed to the long-term success of a project. Maintainership is about building
trust with the current maintainers and being a person that they can
depend on to make decisions in the best interest of the project in a consistent manner.
**Defined by:** *approvers* entry in the [OWNERS] file.
### Maintainer requirements
- Deep understanding of the technical goals and direction of the project
- Deep understanding of the technical domain of the project
- Sustained contributions to design and direction by doing all of:
- Authoring and reviewing proposals
- Initiating, contributing, and resolving discussions (emails, GitHub issues, meetings)
- Identifying subtle or complex issues in the designs and implementation of PRs
- Directly contributed to the project through implementation and/or review
- Sponsored by two active maintainers and elected by supermajority
- Sponsors must be from multiple member companies to demonstrate integration across the community.
- To become a maintainer send an email with your candidacy to
- Ensure your sponsors are @mentioned in the email
- Include a list of contributions representative of your work on the project.
- Existing maintainers vote will privately and respond to the email with either acceptance or feedback for suggested improvement.
- With your membership approved you are expected to:
- Open a PR and add an entry to the [OWNERS] file
- Request to be added to the and mailing lists
- Request to join [etcd-maintainer teams of the etcd-io organization in GitHub](https://github.com/orgs/etcd-io/teams/maintainers-etcd)
- Request to join the private slack channel for etcd maintainers on [kubernetes slack](http://slack.kubernetes.io/)
- Request access to `etcd-development` GCP project where we publish releases
- Request access to passwords shared between maintainers
- Request cncf service desk access by emailing
- Raise cncf service desk ticket to be addded to [cncf-etcd-maintainers mailing list](https://lists.cncf.io/g/cncf-etcd-maintainers/directory)
### Maintainer responsibilities and privileges
- Make and approve technical design decisions
- Set technical direction and priorities
- Define milestones and releases
- Mentor and guide reviewers, and contributors to the project.
- Participate when called upon in the [security disclosure and release process]
- Ensure the continued health of the project
- Adequate test coverage to confidently release
- Tests are passing reliably (i.e. not flaky) and are fixed when they fail
- Ensure a healthy process for discussion and decision-making is in place.
- Work with other maintainers to maintain the project's overall health and success holistically
### Retiring
Life priorities, interests, and passions can change. Maintainers can retire and
move to [emeritus maintainers]. If a maintainer needs to step down, they should
inform other maintainers and, if possible, help find someone to pick up the related
work. At the very least, ensure the related work can be continued.
If a maintainer has not been performing their duties for 12 months,
they can be removed by other maintainers. In that case, the inactive maintainer will
be first notified via an email. If the situation doesn't improve, they will be
removed. If an emeritus maintainer wants to regain an active role, they can do
so by renewing their contributions. Active maintainers should welcome such a move.
Retiring other maintainers or regaining the status should require the approval
of at least two active maintainers.
Retiring maintainers must:
- Open a PR and move to emeritus approvers in the [OWNERS] file
- Open a PR to be removed from the [etcd-maintainer teams of the etcd-io organization in GitHub](https://github.com/orgs/etcd-io/teams/maintainers-etcd)
- Remove their access to `etcd-development` GCP project where we publish releases
- Raise cncf service desk ticket to be removed as a [cncf-etcd-maintainers mailing list](https://lists.cncf.io/g/cncf-etcd-maintainers/directory) admin
- Request to be removed as a member of the [etcd-maintainers](https://groups.google.com/g/etcd-maintainers) and [etcd-maintainers-private](https://groups.google.com/g/etcd-maintainers-private) Google groups
## Acknowledgements
Contributor roles and responsibilities were written based on [Kubernetes community membership]
[OWNERS]: /OWNERS
[contributor guide]: /CONTRIBUTING.md
[membership nomination]: https://github.com/kubernetes/org/issues/new?assignees=&labels=area%2Fgithub-membership&projects=&template=membership.yml&title=REQUEST%3A+New+membership+for+%3Cyour-GH-handle%3E
[Kubernetes community membership]: https://github.com/kubernetes/community/blob/master/community-membership.md
[emeritus maintainers]: /README.md#etcd-emeritus-maintainers
[security disclosure and release process]: /security/README.md
[two-factor authentication]: https://docs.github.com/en/authentication/securing-your-account-with-two-factor-authentication-2fa/about-two-factor-authentication
================================================
FILE: Documentation/contributor-guide/dependency_management.md
================================================
# Dependency management
## Table of Contents
- **[Main branch](#main-branch)**
- [Dependencies used in workflows](#dependencies-used-in-workflows)
- [Bumping order](#bumping-order)
- [Steps to bump a dependency](#steps-to-bump-a-dependency)
- [Alternative: Using the update_dep.sh script](#alternative-using-the-update_depsh-script)
- [Indirect dependencies](#indirect-dependencies)
- [Known incompatible dependency updates](#known-incompatible-dependency-updates)
- [arduino/setup-protoc](#arduinosetup-protoc)
- [Rotation worksheet](#rotation-worksheet)
- **[Stable branches](#stable-branches)**
- **[Golang versions](#golang-versions)**
- **[Core dependencies mappings](#core-dependencies-mappings)**
## Main branch
The dependabot is enabled & [configured](https://github.com/etcd-io/etcd/blob/main/.github/dependabot.yml) to
manage dependencies for etcd `main` branch. But dependabot doesn't work well for multi-module repository like `etcd`,
see [dependabot-core/issues/6678](https://github.com/dependabot/dependabot-core/issues/6678).
Usually, human intervention is required each time when dependabot automatically opens some PRs to bump dependencies.
Please see the guidance below.
### Dependencies used in workflows
The PRs that automatically bump dependencies (see examples below) used in workflows are fine and can be approved & merged directly as long as all checks are successful.
- [build(deps): bump github/codeql-action from 2.2.11 to 2.2.12](https://github.com/etcd-io/etcd/pull/15736)
- [build(deps): bump actions/checkout from 3.5.0 to 3.5.2](https://github.com/etcd-io/etcd/pull/15735)
- [build(deps): bump ossf/scorecard-action from 2.1.2 to 2.1.3](https://github.com/etcd-io/etcd/pull/15607)
### Bumping order
When multiple etcd modules depend on the same package, please bump the package version for all the modules in the correct order. The rule is simple:
if module A depends on module B, then bump the dependency for module B before module A. If the two modules do not depend on each other, then
it doesn't matter to bump which module first. For example, multiple modules depend on `github.com/spf13/cobra`, so we need to bump the dependency
in the following order,
- go.etcd.io/etcd/pkg/v3
- go.etcd.io/etcd/server/v3
- go.etcd.io/etcd/etcdctl/v3
- go.etcd.io/etcd/etcdutl/v3
- go.etcd.io/etcd/tests/v3
- go.etcd.io/etcd/v3
- go.etcd.io/etcd/tools/v3
For more details about etcd Golang modules, please check
Note the module `go.etcd.io/etcd/tools/v3` doesn't depend on any other modules, nor by any other modules, so it doesn't matter when to bump dependencies for it.
### Steps to bump a dependency
Use the `github.com/spf13/cobra` as an example, follow the steps below to bump it from 1.6.1 to 1.7.0 for module `go.etcd.io/etcd/etcdctl/v3`,
```bash
cd ${ETCD_ROOT_DIR}/etcdctl
go get github.com/spf13/cobra@v1.7.0
go mod tidy
cd ..
make fix # This will update the bill of materials, Go modules and workspace, etc.
```
Execute the same steps for all other modules. When you finish bumping the dependency for all modules, then commit the change,
```bash
git add .
git commit --signoff -m "dependency: bump github.com/spf13/cobra from 1.6.1 to 1.7.0"
```
Please close the related PRs which were automatically opened by dependabot.
When you bump multiple dependencies in one PR, it's recommended to create a separate commit for each dependency. But it isn't a must; for example,
you can get all dependencies bumping for the module `go.etcd.io/etcd/tools/v3` included in one commit.
#### Alternative: Using the update_dep.sh script
> Note: Please use bash shell version 5.x or higher.
As an alternative to the manual steps above, you can use the `update_dep.sh` script to automate the dependency bump process across all modules:
```bash
# Update to a specific version
./scripts/update_dep.sh github.com/spf13/cobra v1.7.0
# Update to the latest version
./scripts/update_dep.sh github.com/spf13/cobra
```
The script will:
1. Display the current version of the dependency across all go.mod files
2. Warn and prompt for confirmation if the dependency is purely indirect
3. Update the dependency in all modules that depend on it
4. Run `make fix verify-dep` to ensure consistency across all modules
5. Display the updated versions for verification
This script handles the correct bumping order automatically and ensures version consistency across all modules.
#### Troubleshooting
In an event of bumping the version of protoc, protoc plugins or grpc-gateway, it might change `*.proto` file which can result in the following error:
```bash
[0;31mFAIL: 'genproto' FAILED at Wed Jul 31 07:09:08 UTC 2024
make: *** [Makefile:134: verify-genproto] Error 255
```
To fix the above error, run the following script from the root of etcd repository:
```bash
./scripts/genproto.sh
```
### Indirect dependencies
Usually, we don't bump a dependency if all modules just indirectly depend on it, such as `github.com/go-logr/logr`.
If an indirect dependency (e.g. `D1`) causes any CVE or bugs that affect etcd, usually the module (e.g. `M1`, not part of etcd, but used by etcd)
which depends on it should bump the dependency (`D1`), and then etcd just needs to bump `M1`. However, if the module (`M1`) somehow doesn't
bump the problematic dependency, then etcd can still bump it (`D1`) directly following the same steps above. But as a long-term solution, etcd should
try to remove the dependency on such module (`M1`) that lack maintenance.
For mixed cases, in which some modules directly while others indirectly depend on a dependency, we have multiple options,
- Bump the dependency for all modules, no matter it's direct or indirect dependency.
- Bump the dependency only for modules that directly depend on it.
We should try to follow the first way, and temporarily fall back to the second one if we run into any issue on the first way. Eventually we
should fix the issue and ensure all modules depend on the same version of the dependency.
### Known incompatible dependency updates
#### arduino/setup-protoc
Please refer to [build(deps): bump arduino/setup-protoc from 1.3.0 to 2.0.0](https://github.com/etcd-io/etcd/pull/16016)
### Rotation worksheet
The dependabot scheduling interval is weekly; it means dependabot will automatically raise a bunch of PRs per week.
Usually, human intervention is required each time. We have a [rotation worksheet](https://docs.google.com/spreadsheets/d/1jodHIO7Dk2VWTs1IRnfMFaRktS9IH8XRyifOnPdSY8I/edit#gid=1394774387),
and everyone is welcome to participate; you just need to register your name in the worksheet.
## Stable branches
Usually, we don't proactively bump dependencies for stable releases unless there are any CVEs or bugs that affect etcd.
If we have to do it, then follow the same guidance above. Note that there is no `./scripts/fix.sh`/`make fix` in release-3.4, so no need to
execute it for 3.4.
## Golang versions
For all libraries that exist as independent subprojects (e.g., bbolt, raft, gofail), we should always stick
to the oldest supported Go minor version for all branches, including main. It's up to the users of these
libraries to choose which [Go version](https://go.dev/dl) they want to use in their own projects.
For other subprojects that produce binaries or images (e.g. etcd, etcd-operator, auger), the main
branches should use the latest Go minor version for development, while stable releases should use the
latest patch of the previous supported Go minor version to ensure stability.
Suggested steps for performing a minor version upgrade for the etcd development branch:
1. Carefully review new Go version release notes and potentially related blog posts for any deprecations, performance impacts, or other considerations.
2. Create a GitHub issue to signal intent to upgrade and invite discussion, for example, .
3. Complete the upgrade locally in your development environment by editing `.go-version` and running `make fix`.
4. Run performance benchmarks locally to compare before and after.
5. Raise a pull request for the changes, for example, .
Stable etcd release branches will be maintained to stay on the latest patch release of a supported Go version. Upgrading minor versions will be completed before the minor version in use currently is no longer supported. Refer to the [Go release policy](https://go.dev/doc/devel/release).
For an example of how to update etcd to a new patch release of Go refer to issue and the linked pull requests.
References:
-
## Core dependencies mappings
[bbolt](https://github.com/etcd-io/bbolt) and [raft](https://github.com/etcd-io/raft) are two core dependencies of etcd.
Both etcd 3.4.x and 3.5.x depend on bbolt 1.3.x, and etcd 3.6.x depends on bbolt 1.4.x.
raft is included in the etcd repository for release-3.4 and release-3.5 branches, so etcd 3.4.x and 3.5.x do not depend on any
external raft module. We moved raft into [a separate repository](https://github.com/etcd-io/raft) starting from 3.6, and the first raft
release is v3.6.0, so etcd 3.6.0 depends on raft v3.6.0.
Please see the table below:
| etcd versions | bbolt versions | raft versions |
|---------------|----------------|---------------|
| 3.4.x | v1.3.x | N/A |
| 3.5.x | v1.3.x | N/A |
| 3.6.x | v1.4.x | v3.6.x |
================================================
FILE: Documentation/contributor-guide/exit_codes.md
================================================
# Exit Codes Reference
This document provides a reference of exit codes returned by the etcd server.
etcd server explicitly uses three exit codes: **0** (success), **1** (general errors), and **2** (argument errors). When terminated by signals (SIGTERM/SIGINT) on Linux/Unix systems, the exit code depends on the process type: PID 1 processes exit with code 0, while non-PID 1 processes re-raise the signal, resulting in exit codes 143 (SIGTERM) or 130 (SIGINT).
## Exit Code 0 - Success
| Scenario | Code Reference |
| -------- | -------------- |
| Help flag (`--help`) | [`server/etcdmain/config.go#L131`](https://github.com/etcd-io/etcd/blob/e0a72cf470756149f4f602bf89284038e6397549/server/etcdmain/config.go#L131) |
| Version flag (`--version`) | [`server/etcdmain/config.go#L144`](https://github.com/etcd-io/etcd/blob/e0a72cf470756149f4f602bf89284038e6397549/server/etcdmain/config.go#L144) |
| Normal shutdown | [`server/etcdmain/etcd.go#L176`](https://github.com/etcd-io/etcd/blob/e0a72cf470756149f4f602bf89284038e6397549/server/etcdmain/etcd.go#L176) |
| Graceful shutdown on signal (PID 1) | [`pkg/osutil/interrupt_unix.go#L74`](https://github.com/etcd-io/etcd/blob/e0a72cf470756149f4f602bf89284038e6397549/pkg/osutil/interrupt_unix.go#L74) |
## Signal Termination (128 + signal number)
**Note:** This behavior is specific to Linux platform. On other platforms, etcd typically returns exit code 0 if it exits without error, and 1 otherwise.
For non-PID 1 processes on Linux/Unix, the signal handler re-raises the signal to the process itself ([`pkg/osutil/interrupt_unix.go#L77`](https://github.com/etcd-io/etcd/blob/e0a72cf470756149f4f602bf89284038e6397549/pkg/osutil/interrupt_unix.go#L77)), which results in the kernel setting the exit code to 128 + signal number.
| Signal | Exit Code | Code Reference |
| ------ | --------- | -------------- |
| SIGINT (Ctrl-C) | 130 (128 + 2) | [`pkg/osutil/interrupt_unix.go#L53`](https://github.com/etcd-io/etcd/blob/e0a72cf470756149f4f602bf89284038e6397549/pkg/osutil/interrupt_unix.go#L53) |
| SIGTERM | 143 (128 + 15) | [`pkg/osutil/interrupt_unix.go#L53`](https://github.com/etcd-io/etcd/blob/e0a72cf470756149f4f602bf89284038e6397549/pkg/osutil/interrupt_unix.go#L53) |
## Exit Code 1 - General Errors
All server errors exit with code 1:
| Scenario | Code Reference |
| -------- | -------------- |
| Failed to create logger | [`server/etcdmain/etcd.go#L60`](https://github.com/etcd-io/etcd/blob/e0a72cf470756149f4f602bf89284038e6397549/server/etcdmain/etcd.go#L60) |
| Failed to verify flags | [`server/etcdmain/etcd.go#L69`](https://github.com/etcd-io/etcd/blob/e0a72cf470756149f4f602bf89284038e6397549/server/etcdmain/etcd.go#L69) |
| Discovery token already used | [`server/etcdmain/etcd.go#L141`](https://github.com/etcd-io/etcd/blob/e0a72cf470756149f4f602bf89284038e6397549/server/etcdmain/etcd.go#L141) |
| Initial cluster configuration error | [`server/etcdmain/etcd.go#L155`](https://github.com/etcd-io/etcd/blob/e0a72cf470756149f4f602bf89284038e6397549/server/etcdmain/etcd.go#L155) |
| Discovery failed | [`server/etcdmain/etcd.go#L157`](https://github.com/etcd-io/etcd/blob/e0a72cf470756149f4f602bf89284038e6397549/server/etcdmain/etcd.go#L157) |
| Listener failed | [`server/etcdmain/etcd.go#L172`](https://github.com/etcd-io/etcd/blob/e0a72cf470756149f4f602bf89284038e6397549/server/etcdmain/etcd.go#L172) |
| Failed to list data directory | [`server/etcdmain/etcd.go#L201`](https://github.com/etcd-io/etcd/blob/e0a72cf470756149f4f602bf89284038e6397549/server/etcdmain/etcd.go#L201) |
| Invalid datadir (member + proxy exist) | [`server/etcdmain/etcd.go#L221`](https://github.com/etcd-io/etcd/blob/e0a72cf470756149f4f602bf89284038e6397549/server/etcdmain/etcd.go#L221) |
| Unsupported architecture | [`server/etcdmain/etcd.go#L252`](https://github.com/etcd-io/etcd/blob/e0a72cf470756149f4f602bf89284038e6397549/server/etcdmain/etcd.go#L252) |
| Generic fatal error | [`server/etcdmain/util.go#L34`](https://github.com/etcd-io/etcd/blob/e0a72cf470756149f4f602bf89284038e6397549/server/etcdmain/util.go#L34) |
| Invalid listen-peer-urls | [`server/embed/config.go#L821`](https://github.com/etcd-io/etcd/blob/e0a72cf470756149f4f602bf89284038e6397549/server/embed/config.go#L821) |
| Invalid listen-client-urls | [`server/embed/config.go#L830`](https://github.com/etcd-io/etcd/blob/e0a72cf470756149f4f602bf89284038e6397549/server/embed/config.go#L830) |
| Invalid listen-client-http-urls | [`server/embed/config.go#L839`](https://github.com/etcd-io/etcd/blob/e0a72cf470756149f4f602bf89284038e6397549/server/embed/config.go#L839) |
| Invalid initial-advertise-peer-urls | [`server/embed/config.go#L848`](https://github.com/etcd-io/etcd/blob/e0a72cf470756149f4f602bf89284038e6397549/server/embed/config.go#L848) |
| Invalid advertise-client-urls | [`server/embed/config.go#L857`](https://github.com/etcd-io/etcd/blob/e0a72cf470756149f4f602bf89284038e6397549/server/embed/config.go#L857) |
| Invalid listen-metrics-urls | [`server/embed/config.go#L866`](https://github.com/etcd-io/etcd/blob/e0a72cf470756149f4f602bf89284038e6397549/server/embed/config.go#L866) |
## Exit Code 2 - Argument Errors
| Scenario | Code Reference |
| -------- | -------------- |
| Flag parsing error | [`server/etcdmain/config.go#L133`](https://github.com/etcd-io/etcd/blob/e0a72cf470756149f4f602bf89284038e6397549/server/etcdmain/config.go#L133) |
================================================
FILE: Documentation/contributor-guide/features.md
================================================
# Features
This document provides an overview of etcd features and general development guidelines for adding and deprecating them. The project maintainers can override these guidelines per the need of the project following the project governance.
## Overview
The etcd features fall into three stages: Alpha, Beta, and GA.
### Alpha
Any new feature is usually added as an Alpha feature. An Alpha feature is characterized as below:
- Might be buggy due to a lack of user testing. Enabling the feature may not work as expected.
- Disabled by default.
- Support for such a feature may be dropped at any time without notice
- Feature-related issues may be given lower priorities.
- It can be removed in the next minor or major release without following the feature deprecation policy unless it graduates to a more stable stage.
### Beta
A Beta feature is characterized as below:
- Supported as part of the supported releases of etcd.
- Enabled by default.
- Discontinuation of support must follow the feature deprecation policy.
### GA
A GA feature is characterized as below:
- Supported as part of the supported releases of etcd.
- Always enabled; you cannot disable it. The corresponding feature gate is no longer needed.
- Discontinuation of support must follow the feature deprecation policy.
## Development Guidelines
### Adding a new feature
Any new enhancements to the etcd are typically added as an Alpha feature.
etcd follows the Kubernetes [KEP process](https://github.com/kubernetes/enhancements/blob/master/keps/sig-architecture/0000-kep-process/README.md) for new enhancements. The general development requirements are listed below. They can be somewhat flexible depending on the scope of the feature and review discussions and will evolve over time.
- Open a [KEP](https://github.com/kubernetes/enhancements/issues) issue
- It must provide a clear need for the proposed feature.
- It should list development work items as checkboxes. There must be one work item towards future graduation to Beta.
- Label the issue with `/sig etcd`.
- Keep the issue open for tracking purposes until a decision is made on graduation.
- Open a [KEP](https://github.com/kubernetes/enhancements) Pull Request (PR).
- The KEP template can be simplified for etcd.
- It must provide clear graduation criteria for each stage.
- The KEP doc should reside in [keps/sig-etcd](https://github.com/kubernetes/enhancements/tree/master/keps/sig-etcd/)
- Open Pull Requests (PRs) in [etcd](https://github.com/etcd-io/etcd)
- Provide unit tests. Integration tests are also recommended as possible.
- Provide robust e2e test coverage. If the feature being added is complicated or quickly needed, maintainers can decide to go with e2e tests for basic coverage initially and have robust coverage added at a later time before the feature graduation to the stable feature.
- Provide logs for proper debugging.
- Provide metrics and benchmarks as needed.
- Add an Alpha [feature gate](https://etcd.io/docs/v3.6/feature-gates/).
- Any code changes or configuration flags related to the implementation of the feature must be gated with the feature gate e.g. `if cfg.ServerFeatureGate.Enabled(features.FeatureName)`.
- Add a CHANGELOG entry.
- At least two maintainers must approve the KEP and related code changes.
### Graduating a feature to the next stage
It is important that features don't get stuck in one stage. They should be revisited and moved to the next stage once they meet the graduation criteria listed in the KEP. A feature should stay at one stage for at least one release before being promoted.
#### Provide implementation
If a feature is found ready for graduation to the next stage, open a Pull Request (PR) with the following changes.
- Update the feature `PreRelease` stage in `server/features/etcd_features.go`.
- Update the status in the original KEP issue.
At least two maintainers must approve the work. Patch releases should not be considered for graduation.
### Deprecating a feature
#### Alpha
Alpha features can be removed without going through the deprecation process.
- Remove the feature gate in `server/features/etcd_features.go`, and clean up all relevant code.
- Close the original KEP issue with reasons to drop the feature.
#### Beta and GA
As the project evolves, a Beta/GA feature may sometimes need to be deprecated and removed. Such a situation should be handled using the steps below:
- A Beta/GA feature can only be deprecated after at least 2 minor or major releases.
- Update original KEP issue if it has not been closed or create a new etcd issue with reasons and steps to deprecate the feature.
- Add the feature deprecation documentation in the release notes and feature gates documentation of the next minor/major release.
- In the next minor/major release, set the feature gate to `{Default: false, PreRelease: featuregate.Deprecated, LockedToDefault: false}` in `server/features/etcd_features.go`. Deprecated feature gates must respond with a warning when used.
- If the feature has GAed, and the original gated codes has been cleaned up, add the disablement codes back with the feature gate.
- In the minor/major release after the next, set the feature gate to `{Default: false, PreRelease: featuregate.Deprecated, LockedToDefault: true}` in `server/features/etcd_features.go`, and start cleaning the code.
At least two maintainers must approve the work. Patch releases should not be considered for deprecation.
================================================
FILE: Documentation/contributor-guide/local_cluster.md
================================================
# Set up the local cluster
For testing and development deployments, the quickest and easiest way is to configure a local cluster. For a production deployment, refer to the [clustering][clustering] section.
## Local standalone cluster
### Starting a cluster
Run the following to deploy an etcd cluster as a standalone cluster:
```
$ ./etcd
...
```
If the `etcd` binary is not present in the current working directory, it might be located either at `$GOPATH/bin/etcd` or at `/usr/local/bin/etcd`. Run the command appropriately.
The running etcd member listens on `localhost:2379` for client requests.
### Interacting with the cluster
Use `etcdctl` to interact with the running cluster:
1. Store an example key-value pair in the cluster:
```
$ ./etcdctl put foo bar
OK
```
If OK is printed, storing the key-value pair is successful.
2. Retrieve the value of `foo`:
```
$ ./etcdctl get foo
bar
```
If `bar` is returned, interaction with the etcd cluster is working as expected.
## Local multi-member cluster
### Starting a cluster
A `Procfile` at the base of the etcd git repository is provided to easily configure a local multi-member cluster. To start a multi-member cluster, navigate to the root of the etcd source tree and perform the following:
1. Install `goreman` to control Procfile-based applications:
```
$ go install github.com/mattn/goreman@latest
```
The installation will place executables in the $GOPATH/bin. If $GOPATH environment variable is not set, the tool will be installed into the $HOME/go/bin. Make sure that $PATH is set accordingly in your environment.
2. Start a cluster with `goreman` using etcd's stock Procfile:
```
$ goreman -f Procfile start
```
The members start running. They listen on `localhost:2379`, `localhost:22379`, and `localhost:32379` respectively for client requests.
### Interacting with the cluster
Use `etcdctl` to interact with the running cluster:
1. Print the list of members:
```
$ etcdctl --write-out=table --endpoints=localhost:2379 member list
```
The list of etcd members is displayed as follows:
```
+------------------+---------+--------+------------------------+------------------------+
| ID | STATUS | NAME | PEER ADDRS | CLIENT ADDRS |
+------------------+---------+--------+------------------------+------------------------+
| 8211f1d0f64f3269 | started | infra1 | http://127.0.0.1:2380 | http://127.0.0.1:2379 |
| 91bc3c398fb3c146 | started | infra2 | http://127.0.0.1:22380 | http://127.0.0.1:22379 |
| fd422379fda50e48 | started | infra3 | http://127.0.0.1:32380 | http://127.0.0.1:32379 |
+------------------+---------+--------+------------------------+------------------------+
```
2. Store an example key-value pair in the cluster:
```
$ etcdctl put foo bar
OK
```
If OK is printed, storing the key-value pair is successful.
### Testing fault tolerance
To exercise etcd's fault tolerance, kill a member and attempt to retrieve the key.
1. Identify the process name of the member to be stopped.
The `Procfile` lists the properties of the multi-member cluster. For example, consider the member with the process name, `etcd2`.
2. Stop the member:
```
# kill etcd2
$ goreman run stop etcd2
```
3. Store a key:
```
$ etcdctl put key hello
OK
```
4. Retrieve the key that is stored in the previous step:
```
$ etcdctl get key
hello
```
5. Retrieve a key from the stopped member:
```
$ etcdctl --endpoints=localhost:22379 get key
```
The command should display an error caused by connection failure:
```
2017/06/18 23:07:35 grpc: Conn.resetTransport failed to create client transport: connection error: desc = "transport: dial tcp 127.0.0.1:22379: getsockopt: connection refused"; Reconnecting to "localhost:22379"
Error: grpc: timed out trying to connect
```
6. Restart the stopped member:
```
$ goreman run restart etcd2
```
7. Get the key from the restarted member:
```
$ etcdctl --endpoints=localhost:22379 get key
hello
```
Restarting the member re-establishs the connection. `etcdctl` will now be able to retrieve the key successfully. To learn more about interacting with etcd, read [interacting with etcd section][interacting].
[clustering]: https://etcd.io/docs/latest/op-guide/clustering/
[interacting]: https://etcd.io/docs/latest/dev-guide/interacting_v3/
================================================
FILE: Documentation/contributor-guide/logging.md
================================================
# Logging Conventions
etcd uses the [zap][zap] library for logging application output categorized into *levels*. A log message's level is determined according to these conventions:
* Debug: Everything is still fine, but even common operations may be logged, and less helpful but more quantity of notices. Usually not used in production.
* Examples:
* Send a normal message to a remote peer
* Write a log entry to disk
* Info: Normal, working log information, everything is fine, but helpful notices for auditing or common operations. Should rather not be logged more frequently than once per a few seconds in a normal server's operation.
* Examples:
* Startup configuration
* Start to do a snapshot
* Warning: (Hopefully) Temporary conditions that may cause errors, but may work fine. A replica disappearing (that may reconnect) is a warning.
* Examples:
* Failure to send a raft message to a remote peer
* Failure to receive heartbeat message within the configured election timeout
* Error: Data has been lost, a request has failed for a bad reason, or a required resource has been lost.
* Examples:
* Failure to allocate disk space for WAL
* Panic: Unrecoverable or unexpected error situation that requires stopping execution.
* Examples:
* Failure to create the database
* Fatal: Unrecoverable or unexpected error situation that requires immediate exit. Mostly used in the test.
* Examples:
* Failure to find the data directory
* Failure to run a test function
[zap]: https://github.com/uber-go/zap
================================================
FILE: Documentation/contributor-guide/modules.md
================================================
# Golang modules
The etcd project (since version 3.5) is organized into multiple
[golang modules](https://golang.org/ref/mod) hosted in a [single repository](https://golang.org/ref/mod#vcs-dir).

There are the following modules:
- **go.etcd.io/etcd/api/v3** - contains API definitions
(like protos & proto-generated libraries) that defines communication protocol
between etcd clients and servers.
- **go.etcd.io/etcd/pkg/v3** - a collection of utility packages used by etcd
without being specific to etcd itself. A package belongs here
only if it could possibly be moved out into its own repository in the future.
Please avoid adding here code that has a lot of dependencies on its own, as
they automatically become dependencies of the client library
(that we want to keep lightweight).
- **go.etcd.io/etcd/client/v3** - client library used to contact etcd over
the network (grpc). Recommended for all new usage of etcd.
- **go.etcd.io/raft/v3** - implementation of distributed consensus
protocol. Should have no etcd specific code. Hosted in a separate repository:
https://github.com/etcd-io/raft.
- **go.etcd.io/etcd/server/v3** - etcd implementation.
The code in this package is internal to etcd and should not be consumed
by external projects. The package layout and API can change within the minor versions.
- **go.etcd.io/etcd/etcdctl/v3** - a command line tool to access and manage etcd.
- **go.etcd.io/etcd/tests/v3** - a module that contains all integration tests of etcd.
Notice: All unit tests (fast and not requiring cross-module dependencies)
should be kept in the local modules of the code under the test.
- **go.etcd.io/bbolt** - implementation of persistent b-tree.
Hosted in a separate repository: https://github.com/etcd-io/bbolt.
### Operations
1. All etcd modules should be released in the same versions, e.g.
`go.etcd.io/etcd/client/v3@v3.5.10` must depend on `go.etcd.io/etcd/api/v3@v3.5.10`.
The consistent updating of versions can be performed using:
```shell script
% DRY_RUN=false TARGET_VERSION="v3.5.10" ./scripts/release_mod.sh update_versions
```
2. The released modules should be tagged according to https://golang.org/ref/mod#vcs-version rules,
i.e. each module should get its own tag.
The tagging can be performed using:
```shell script
% DRY_RUN=false REMOTE_REPO="origin" ./scripts/release_mod.sh push_mod_tags
```
3. All etcd modules should depend on the same versions of underlying dependencies.
This can be verified using:
```shell script
% PASSES="dep" ./test.sh
```
4. The go.mod files must not contain dependencies not being used and must
conform to `go mod tidy` format.
This is being verified by:
```
% PASSES="mod_tidy" ./test.sh
```
5. To trigger actions across all modules (e.g. auto-format all files), please
use/expand the following script:
```shell script
% make fix
```
### Future
As a North Star, we would like to evaluate etcd modules towards the following model:

This assumes:
- Splitting etcdmigrate/etcdadm out of etcdctl binary.
Thanks to this etcdctl would become clearly a command-line wrapper
around network client API,
while etcdmigrate/etcdadm would support direct physical operations on the
etcd storage files.
- Splitting etcd-proxy out of ./etcd binary, as it contains more experimental code
so carries additional risk & dependencies.
- Deprecation of support for v2 protocol.
================================================
FILE: Documentation/contributor-guide/prow_jobs.md
================================================
# Analyzing Prow Job Resource Usage
## 1. Introduction to Prow
[Prow](https://docs.prow.k8s.io/docs/) is a Kubernetes based CI/CD system. Jobs can be triggered by various types of events and report their status to many different services. Prow provides GitHub automation through policy enforcement and chat-ops via `/command` interactions on pull requests (e.g., `/test`, `/approve`, `/retest`), enabling contributors to trigger jobs and manage workflows directly from GitHub comments.
When a user comments `/ok-to-test`or `/retest,` on a Pull Request, GitHub sends a webhook to Prow's Kubernetes cluster. Visit this [site](https://docs.prow.k8s.io/docs/life-of-a-prow-job/) to further understand the lifecycle of a Prow job.
This is where you can find all etcd Prow jobs [status](https://prow.k8s.io/?repo=etcd-io%2Fetcd)
## 2. How Prow is used for etcd Testing
etcd's CI is managed by [kubernetes/test-infra](https://github.com/kubernetes/test-infra), running Prow.
When a pull request is submitted, or a `/command` is issued, the CI of etcd which managed by [kubernetes/test-infra](https://github.com/kubernetes/test-infra) uses Prow to run the tests. You can view all supported Prow [commands](https://prow.k8s.io/command-help).
### Jobs Types
The jobs [configuration](https://github.com/kubernetes/test-infra/tree/master/config/jobs/etcd) for etcd. Please see [ProwJob](https://docs.prow.k8s.io/docs/jobs/) docs for more info.
There are 3 different job types:
- Recurring jobs that regularly run etcd performance benchmarks, specifically targeting the put API, for the amd64 architecture.[etcd-benchmarks-periodics.yaml](https://github.com/kubernetes/test-infra/blob/master/config/jobs/etcd/etcd-benchmarks-periodic.yaml)
- Presubmits jobs: Run on pull requests before code is merged, ensuring new changes do not break the build or tests. [etcd-operator-presubmits.yaml](https://github.com/kubernetes/test-infra/blob/master/config/jobs/etcd/etcd-presubmits.yaml)
- Postsubmits run after merging the etcd-io/etcd-operator code: [etcd-operator-postsubmits.yaml](https://github.com/kubernetes/test-infra/blob/master/config/jobs/etcd/etcd-operator-postsubmits.yaml)
- Periodic jobs are jobs that run automatically on a fixed schedule (such as every 4 hours, once a day, etc.), regardless of code changes or pull requests. They’re designed to continuously check the stability, compatibility, or performance of the project over time. [etcd-periodics.yaml](https://github.com/kubernetes/test-infra/blob/master/config/jobs/etcd/etcd-periodics.yaml)
- Builds the etcd project for all main and release branches before merging any PR. [etcd-presubmits.yaml](https://github.com/kubernetes/test-infra/blob/master/config/jobs/etcd/etcd-presubmits.yaml)
- This file defines jobs that run automatically after code is merged (postsubmit) into the main or release branches of the etcd repository (etcd-io/etcd). [etcd-postsubmits.yaml](https://github.com/kubernetes/test-infra/blob/master/config/jobs/etcd/etcd-postsubmits.yaml)
- Test pull requests for the etcd-io/raft repository on certain branches (main and release-3.6)[etcd-raft-presubmits.yaml](https://github.com/kubernetes/test-infra/blob/master/config/jobs/etcd/etcd-raft-presubmits.yaml)
- Checks markdown formatting for website changes [etcd-website-presubmits.yaml](https://github.com/kubernetes/test-infra/blob/master/config/jobs/etcd/etcd-website-presubmits.yaml).
- This file contains jobs that run after code is merged into the etcd-io/protodoc repository. [protodoc-presubmit.yaml](https://github.com/kubernetes/test-infra/blob/master/config/jobs/etcd/protodoc-postsubmits.yaml)
- This file defines jobs that run on pull requests (before merge) for the etcd-io/protodoc repository.[protodoc-postsubmit.yaml](https://github.com/kubernetes/test-infra/blob/master/config/jobs/etcd/protodoc-presubmits.yaml)
As an example, `pull-etcd-e2e-amd64` is one of the [presubmits](https://github.com/kubernetes/test-infra/blob/b21a1d3a72d5715ea7c9234cade21751847cfbe5/config/jobs/etcd/etcd-presubmits.yaml#L193). The job automatically runs end-to-end (e2e) tests on the amd64 architecture for every pull request to the etcd repository targeting the main, release-3.6, release-3.5, or release-3.4 branches. This is an example to its dashboard result [graph](https://prow.k8s.io/?repo=etcd-io%2Fetcd&type=presubmit&job=pull-etcd-e2e-amd64).
Refer to [the test-infra Job Types documentation](https://github.com/kubernetes/test-infra/tree/master/config/jobs#job-types) to learn more about them.
### How to Trigger Prow Running Tests
These tests can be triggered when you leave a comment, like `/ok-to-test` (only triggered by an etcd-io member) or `/retest`, in PR [example](https://github.com/etcd-io/etcd/pull/20733#issuecomment-3341443205). `/ok-to-test` allows Prow to run tests on a pull request from a first-time contributor. `/retest` tells Prow to rerun any failed or flaky joobs on the pull request, useful if a previous test failed due to a transient issue.
You can find all supported [commands](https://prow.k8s.io/command-help).
## 3. Navigating Performance Dashboard (Grafana)
Test-infra's Prow exposes Grafana dashboards to provide visibility into build resource usage (CPU, memory, number of running builds, etc.) for the Prow build cluster’s Kubernetes jobs. It is scoped via organization, repository, build identifier and time range filters.
- GKE Dashboards: [https://monitoring-gke.prow.k8s.io/d/96Q8oOOZk/builds?orgId=1&refresh=30s&var-org=etcd-io&var-repo=etcd&var-build=All&from=now-7d&to=now](https://monitoring-gke.prow.k8s.io/d/96Q8oOOZk/builds?orgId=1&refresh=30s&var-org=etcd-io&var-repo=etcd&var-build=All&from=now-7d&to=now)
- EKS Dashboards: [https://monitoring-eks.prow.k8s.io/d/96Q8oOOZk/builds?orgId=1&refresh=30s&var-org=etcd-io&var-repo=etcd&var-build=All&from=now-7d&to=now](https://monitoring-eks.prow.k8s.io/d/96Q8oOOZk/builds?orgId=1&refresh=30s&var-org=etcd-io&var-repo=etcd&var-build=All&from=now-7d&to=now)
It is useful for a few reasons:
1. Tuning resources: By drilling into each build-run, you can determine realistic memory & CPU requests and limits for that job‑type. This helps avoid waste or avoid failed builds hitting resource limits.
2. Spotting anomalies: If one build suddenly used 8 GiB while normally this job uses 1 GiB, it may indicate a regression or mis‑configuration.
3. Capacity planning: Seeing typical and peak usage helps cluster operators plan node sizes, scheduling, concurrency of builds, etc.
4. Debugging performance issues: A build with unexpectedly high CPU or memory might be stuck, looping, or consuming resources inefficiently.
### Panel: “Running / Pending Builds”
Shows the number of builds that are in Running vs Pending states over time.
Use it to track build backlog or concurrency — e.g., if the “Pending” line rises, builds may be waiting for resources.
If the “Running” line fluctuates a lot or remains at some steady value, you can infer how many builds typically run in parallel.
### Panel: “Memory Usage per Build”
Shows memory usage over time for each build ID (each build listed in the legend at the bottom).
The y‑axis shows memory use (e.g., in MiB / GiB).
Use this to spot builds with unusually high memory usage — a spike indicates one build consumed many resources.
### Panel: “CPU Usage per Build”
Similar to the memory panel but shows CPU usage per build over time. Spikes in CPU usage may indicate heavy compute jobs, inefficiencies, or need for resource tuning.
### Panel: "Resources"
- Memory panel
Green line (“used”): how much memory this build’s pod was using at each time point. Orange/Yellow line (“requested”): how much memory was requested (i.e., Kubernetes requests.memory) for that pod.
Red line (“limit”): how much memory was limited (i.e., Kubernetes limits.memory) for that pod.
Y‑axis: shows memory (GiB, MiB) over the build runtime.
X‑axis: time of day/date.
If the green “used” line is close to or hits the red “limit”, it means the build came close to its memory cap (risking OOM). If “used” is much lower than “requested”, you may be over‑allocating memory (waste).
If the “requested” line is much higher than “used”, it suggests the job’s request could be tuned downward.
- CPU panel
Similar structure: green = actual usage, orange/yellow = requested CPU, red = CPU limit (if set).
Y‑axis often in number of CPU cores or fraction thereof (e.g., 1.0 = one core).
A green line with spikes may show bursts of CPU usage (e.g., build or compile phases) while idle periods show low usage.
If CPU usage consistently saturates the limit, the job may be throttled or delayed. If usage is consistently far below request, tuning may reduce cost.
## 3.1 Prow job categories (robustness, integration, static checks)
- Static check:
- Description: Fast, deterministic checks (build, unit tests, linters, go vet/staticcheck, formatting, license/header checks, generated-code verification) that catch style, correctness and packaging problems early.
- When to run: Every PR as presubmits; quick feedback loop before running expensive tests.
- Example job patterns: pull-etcd-verify, pull-etcd-lint, pull-etcd-unit
- Tests:
- Robustness:
- Description: Long-running, fault-injection and chaos-style end-to-end tests that validate etcd correctness and availability under failures (node crashes, network partitions, resource exhaustion, upgrades).
- When to run: Periodics for continuous coverage; run for PRs that touch consensus, storage, recovery, or upgrade paths.
- Example job patterns: pull-etcd-robustness, periodic-robustness
- Integration:
- Description: Functional end-to-end and cross-component tests that exercise real client/server interactions, snapshots/restore, upgrades and compatibility across OS/arch.
- When to run: Presubmits for PRs that change APIs, client behavior, or integration points; periodics for broad platform coverage.
- Example job patterns: pull-etcd-e2e-amd64, pull-etcd-integration
## 4. Interpreting Metrics
Some Prow components expose Prometheus metrics that can be used for monitoring and alerting. You can find metrics like the number of PRs in each Tide pool, a histogram of the number of PRs in each merge and various other metrics to this [site](https://github.com/kubernetes-sigs/prow/blob/main/site/content/en/docs/metrics/_index.md).
================================================
FILE: Documentation/contributor-guide/release.md
================================================
# Release
The guide talks about how to release a new version of etcd.
The procedure includes some manual steps for sanity checking, but it can probably be further scripted. Please keep this document up-to-date if making changes to the release process.
## Release management
Under the leadership of **James Blair** [@jmhbnz](https://github.com/jmhbnz) and **Ivan Valdes Castillo** [@ivanvc](https://github.com/ivanvc), the following pool of release candidates manages the release of each etcd major/minor version as well as manages patches
to each stable release branch. They are responsible for communicating the timelines and status of each release and
for ensuring the stability of the release branch.
- Benjamin Wang [@ahrtr](https://github.com/ahrtr)
- Fu Wei [@fuweid](https://github.com/fuweid)
- James Blair [@jmhbnz](https://github.com/jmhbnz)
- Ivan Valdes Castillo [@ivanvc](https://github.com/ivanvc)
- Marek Siarkowicz [@serathius](https://github.com/serathius)
- Sahdev Zala [@spzala](https://github.com/spzala)
- Siyuan Zhang [@siyuanfoundation](https://github.com/siyuanfoundation)
All release version numbers follow the format of [semantic versioning 2.0.0](http://semver.org/).
### Major, minor version release, or its pre-release
- Ensure the relevant [milestone](https://github.com/etcd-io/etcd/milestones) on GitHub is complete. All referenced issues should be closed or moved elsewhere.
- Ensure the latest [upgrade documentation](https://etcd.io/docs/next/upgrades) is available.
- Bump [hardcoded MinClusterVerion in the repository](https://github.com/etcd-io/etcd/blob/v3.4.15/version/version.go#L29), if necessary.
- Add feature capability maps for the new version, if necessary.
### Patch version release
- To request a backport, developers submit cherry-pick PRs targeting the release branch. The commits should not include merge commits. The commits should be restricted to bug fixes and security patches.
- The cherrypick PRs should target the appropriate release branch (`base:release--`). The k8s infra cherry pick robot `/cherrypick ` PR chatops command may be used to automatically generate cherrypick PRs.
- The release patch manager reviews the cherrypick PRs. Please discuss carefully what is backported to the patch release. Each patch release should be strictly better than its predecessor.
- The release patch manager will cherry-pick these commits starting from the oldest one into stable branch.
## Write a release note
- Write an introduction for the new release. For example, what major bug we fix, what new features we introduce, or what performance improvement we make.
- Put `[GH XXXX]` at the head of the change line to reference the Pull Request that introduces the change. Moreover, add a link on it to jump to the Pull Request.
- Find PRs with the `release-note` label and explain them in the `NEWS` file, as a straightforward summary of changes for end-users.
## Patch release criteria
The etcd project aims to release a new patch version if any of the following conditions are met:
- Fixed one or more major CVEs (>=7.5).
- Fixed one or more critical bugs.
- Fixed three or more major bugs.
- Fixed five or more minor bugs.
## Release guide
### Prerequisites
There are some prerequisites, which should be done before the release process. These are one-time operations,
which don't need to be executed before releasing each version.
1. Generate a GPG key and add it to your GitHub account. Refer to the links on [settings](https://github.com/settings/keys).
2. Ensure you have a Linux machine, on which the git, Golang, and docker have been installed.
- Ensure the Golang version matches the version defined in `.go-version` file.
- Ensure non-privileged users can run docker commands, refer to the [Linux postinstall](https://docs.docker.com/engine/install/linux-postinstall/).
- Ensure there is at least 5GB of free space on your Linux machine.
3. Install gsutil, refer to [gsutil_install](https://cloud.google.com/storage/docs/gsutil_install). When asked about cloud project to use, pick `etcd-development`.
4. Authenticate the image registry, refer to [Authentication methods](https://cloud.google.com/container-registry/docs/advanced-authentication).
- `gcloud auth login`
- `gcloud auth configure-docker`
5. Install gh, refer to [GitHub's documentation](https://github.com/cli/cli#installation). Ensure that running
`gh auth login` succeeds for the GitHub account you use to contribute to etcd,
and that `gh auth status` has a clean exit and doesn't show any issues.
### Release steps
At least one day before the release:
1. Raise an issue to publish the release plan, e.g. [issues/17350](https://github.com/etcd-io/etcd/issues/17350).
2. Raise a `kubernetes/org` pull request ([example PR](https://github.com/kubernetes/org/pull/5582)) to ensure members of the release team are added to the [release github team](https://github.com/orgs/etcd-io/teams/release-etcd).
On the day of the release:
1. Verify you can pass the authentication to the image registries,
- `docker login gcr.io`
- `docker login quay.io`
- If the release person doesn't have access to 1password, one of the owners (@ahrtr, @ivanvc, @jmhbnz, @serathius) needs to share the password with them per [this guide](https://support.1password.com/share-items/). See rough steps below,
- [Sign in](https://team-etcd.1password.com/home) to your account on 1password.com.
- Click `Your Vault Items` on the right side.
- Select `Password of quay.io`.
- Click `Share` on the top right, and set expiration as `1 hour` and only available to the release person using his/her email.
- Click `Copy Link` then send the link to the release person via slack or email.
2. Clone the etcd repository and checkout the target branch,
- `git clone --branch release-3.X git@github.com:etcd-io/etcd.git`
3. Run the release script under the repository's root directory, replacing `${VERSION}` with a value without the `v` prefix, i.e. `3.5.13`.
- `DRY_RUN=false ./scripts/release.sh ${VERSION}`
- **NOTE:** When doing a pre-release (i.e., a version from the main branch, 3.6.0-alpha.2), you will need to explicitly set the branch to main:
```bash
DRY_RUN=false BRANCH=main ./scripts/release.sh ${VERSION}
```
It generates all release binaries under the directory `/tmp/etcd-release-${VERSION}/etcd/release/` and images. Binaries are pushed to the Google Cloud bucket
under project `etcd-development`, and images are pushed to `quay.io` and `gcr.io`.
- It is advisable to do a dry run before the actual release. This will create a `/tmp` directory. Do **NOT** forget to remove this directory before the actual release.
```bash
DRY_RUN=true BRANCH=${BRANCH} ./scripts/release.sh ${VERSION}
```
4. Publish the release page on GitHub
- Open the **draft** release URL shown by the release script
- Click the pen button at the top right to edit the release
- Review that it looks correct, reviewing that the bottom checkboxes are checked depending on the
release version (v3.4 & v3.5 no checkboxes, v3.6 has the set as latest release checkbox checked,
v3.7 has the set as pre-release checkbox checked)
- Then, publish the release
5. Announce to the etcd-dev googlegroup
Follow the format of previous release emails sent to etcd-dev@googlegroups.com, see an example below. After sending out the email, ask one of the mailing list maintainers to approve the email from the pending list. Additionally, label the release email as `Release`.
```text
Hello,
etcd v3.4.30 is now public!
https://github.com/etcd-io/etcd/releases/tag/v3.4.30
Thanks to everyone who contributed to the release!
etcd team
```
6. Update the changelog to reflect the correct release date.
7. Paste the release link to the issue raised in Step 1 and close the issue.
8. Raise a follow-up `kubernetes/org` pull request to return the GitHub release team to empty, least privilege state.
9. Crease a new stable branch through `git push origin release-${VERSION_MAJOR}.${VERSION_MINOR}` if this is a new major or minor stable release.
10. Re-generate a new password for quay.io if needed (e.g. shared to a contributor who isn't in the release team, and we should rotate the password at least once every 3 months).
11. Bump the new etcd release in Kubernetes, refer to [Bump etcd Version in Kubernetes](bump_etcd_version_k8s.md).
- For etcd 3.6 patches, bump it to Kubernetes 1.34 and all newer minor versions (including `master` branch)
- For etcd 3.5 patches, bump it to Kubernetes 1.33 and all older supported versions
#### Release known issues
1. Timeouts pushing binaries - If binaries fail to fully upload to Google Cloud storage, the script must be re-run using the same command. Any artifacts that are already pushed will be overwritten to ensure they are correct. The storage bucket does not use object versioning so incorrect files cannot remain.
2. Timeouts pushing images - It is rare, although possible for connection timeouts to occur when publishing etcd release images to `quay.io` or `gcr.io`. If this occurs, it is known to be safe to rerun the release script command appending the `--no-upload` flag, and image uploads will gracefully resume.
3. GPG vs SSH signing - The release scripts assume that git tags will be signed with a GPG key. Since 2022 GitHub has supported [signing commits and tags using ssh](https://github.blog/changelog/2022-08-23-ssh-commit-verification-now-supported). Until further release script updates are completed you will need to disable this feature in your `~/.gitconfig` and revert to signing via GPG to perform etcd releases.
================================================
FILE: Documentation/contributor-guide/reporting_bugs.md
================================================
# Reporting bugs
If any part of the etcd project has bugs or documentation mistakes, please let us know by [opening an issue][etcd-issue]. We treat bugs and mistakes very seriously and believe no issue is too small. Before creating a bug report, please check that an issue reporting the same problem does not already exist.
To make the bug report accurate and easy to understand, please try to create bug reports that are:
- Specific. Include as many details as possible: which version, what environment, what configuration, etc. If the bug is related to running the etcd server, please attach the etcd log (the starting log with the etcd configuration is especially important).
- Reproducible. Include the steps to reproduce the problem. We understand some issues might be hard to reproduce, please include the steps that might lead to the problem. If possible, please attach the affected etcd data dir and stack trace to the bug report.
- Isolated. Please try to isolate and reproduce the bug with minimum dependencies. It would significantly slow down the speed to fix a bug if too many dependencies are involved in a bug report. Debugging external systems that rely on etcd is out of scope, but we are happy to provide guidance in the right direction or help with using etcd itself.
- Unique. Do not duplicate existing bug reports.
- Scoped. One bug per report. Do not follow up with another bug inside one report.
It may be worthwhile to read [Elika Etemad’s article on filing good bug reports][filing-good-bugs] before creating a bug report.
We might ask for further information to locate a bug. A duplicated bug report will be closed.
## Frequently asked questions
### How to get a stack trace
``` bash
$ kill -QUIT $PID
```
### How to get the etcd version
``` bash
$ etcd --version
```
### How to get etcd configuration and log when it runs as systemd service ‘etcd2.service’
``` bash
$ sudo systemctl cat etcd2
$ sudo journalctl -u etcd2
```
Due to an upstream systemd bug, journald may miss the last few log lines when its processes exit. If journalctl says etcd stopped without a fatal or panic message, try `sudo journalctl -f -t etcd2` to get the full log.
[etcd-issue]: https://github.com/etcd-io/etcd/issues/new
[filing-good-bugs]: http://fantasai.inkedblade.net/style/talks/filing-good-bugs/
================================================
FILE: Documentation/contributor-guide/roadmap.md
================================================
# Roadmap
etcd uses GitHub milestones to track all tasks in each major or minor release. The `roadmap.md` file only records the
most important tasks for each release. The list is based on the current maintainer capacity that may shift over time.
Proposed milestones are what we think we can deliver with the people we have. If we have more support on the important
stuff, we could pick up more items from the backlog. Note that etcd will continue to mainly focus on technical debt over
the next few major or minor releases.
Each item has an assigned priority. Refer to [priority definitions](https://github.com/etcd-io/etcd/blob/main/Documentation/contributor-guide/triage_issues.md#step-5---prioritise-the-issue).
## v3.6.0
For a full list of tasks in `v3.6.0`, please see [milestone etcd-v3.6](https://github.com/etcd-io/etcd/milestone/38).
| Title | Priority | Status | Note |
|--------------------------------------------------------------------------------------------------------------------|-----------------------------|-------------|--------------------------------------------------------------------------------------------------------------|
| [Support downgrade](https://github.com/etcd-io/etcd/issues/11716) | priority/important-soon | Completed | etcd will support downgrade starting from 3.6.0. But it will also support offline downgrade from 3.5 to 3.4. |
| [StoreV2 deprecation](https://github.com/etcd-io/etcd/issues/12913) | priority/important-soon | In progress | This task will be covered in both 3.6 and 3.7. |
| [Release raft 3.6.0](https://github.com/etcd-io/raft/issues/89) | priority/important-soon | Completed | etcd 3.6.0 will depends on raft 3.6.0 |
| [Release bbolt 1.4.0](https://github.com/etcd-io/bbolt/issues/553) | priority/important-soon | Completed | etcd 3.6.0 will depends on bbolt 1.4.0 |
| [Support /livez and /readyz endpoints](https://github.com/etcd-io/etcd/issues/16007) | priority/important-longterm | Completed | It provides clearer APIs, and can also work around the stalled writes issue |
| [Bump gRPC](https://github.com/etcd-io/etcd/issues/16290) | priority/important-longterm | Completed | It isn't guaranteed to be resolved in 3.6, and might be postponed to 3.7 depending on the effort and risk. |
| [Deprecate grpc-gateway or bump it](https://github.com/etcd-io/etcd/issues/14499) | priority/important-longterm | Completed | It isn't guaranteed to be resolved in 3.6, and might be postponed to 3.7 depending on the effort and risk. |
| [bbolt: Add logger into bbolt](https://github.com/etcd-io/bbolt/issues/509) | priority/important-longterm | Completed | It's important to diagnose bbolt issues |
| [bbolt: Add surgery commands](https://github.com/etcd-io/bbolt/issues/370) | priority/important-longterm | Completed | Surgery commands are important for fixing corrupted db files |
| [Evaluate and (Gradulate or deprecate/remove) experimental features](https://github.com/etcd-io/etcd/issues/16292) | priority/backlog | Not started | This task will be covered in both 3.6 and 3.7. |
## v3.7.0
For a full list of tasks in `v3.7.0`, please see [milestone etcd-v3.7](https://github.com/etcd-io/etcd/milestone/39).
| Title | Priority | Note |
|-------------------------------------------------------------------------------------------------------------------|----------|-----------------------------------------------------------------------------------|
| [StoreV2 deprecation](https://github.com/etcd-io/etcd/issues/12913) | P0 | Finish the remaining tasks 3.7. |
| [Support range stream](https://github.com/etcd-io/etcd/issues/12342) | P0 | to be investigated & discussed. |
| [Refactor lease: Lease might be revoked by mistake by old leader](https://github.com/etcd-io/etcd/issues/15247) | P1 | to be investigated & discussed |
| [Integrate raft's new feature (async write) into etcd](https://github.com/etcd-io/etcd/issues/16291) | P1 | It should improve the performance |
| [bbolt: Support customizing the bbolt rebalance threshold](https://github.com/etcd-io/bbolt/issues/422) | P2 | It may get rid of etcd's defragmentation. Both bbolt and etcd need to be changed. |
| [Evaluate and (graduate or deprecate/remove) experimental features](https://github.com/etcd-io/etcd/issues/16292) | P2 | Finish the remaining tasks 3.7. |
## Backlog (future releases)
| Title | Priority | Note |
|----------------------------------------------------------------------------------------------------------|----------|------|
| [Remove the dependency on grpc-go's experimental API](https://github.com/etcd-io/etcd/issues/15145) | | |
| [Protobuf: cleanup both golang/protobuf and gogo/protobuf](https://github.com/etcd-io/etcd/issues/14533) | | |
| [Proposals should include a merkle root](https://github.com/etcd-io/etcd/issues/13839) | | |
| [Add Distributed Tracing using OpenTelemetry](https://github.com/etcd-io/etcd/issues/12460) | | |
| [Support CA rotation](https://github.com/etcd-io/etcd/issues/11555) | | |
| [bbolt: Migrate all commands to cobra style commands](https://github.com/etcd-io/bbolt/issues/472) | | |
| [raft: enhance the configuration change validation](https://github.com/etcd-io/raft/issues/80) | | |
================================================
FILE: Documentation/contributor-guide/triage_issues.md
================================================
# Issue triage guidelines
## Purpose
Speed up issue management.
The `etcd` issues are listed at and are identified with labels. For example, an issue that is identified as a bug will be set to the label `type/bug`.
The etcd project uses labels to indicate common attributes such as `area`, `type`, and `priority` of incoming issues.
New issues will often start without any labels, but typically `etcd` maintainers, reviewers, and members will add labels by following these triage guidelines. The detailed list of labels can be found at .
## Scope
This document serves as the primary guidelines for triaging incoming issues in `etcd`.
All contributors are encouraged and welcome to help manage issues which will help reduce the burden on project maintainers, though the work and responsibilities discussed in this document are created with `etcd` project reviewers and members in mind as these individuals will have triage access to the etcd project which is a requirement for actions like applying labels or closing issues.
Refer to [etcd community membership](https://github.com/etcd-io/etcd/blob/main/Documentation/contributor-guide/community-membership.md) for guidance on becoming an etcd project member or reviewer.
## Step 1 - Find an issue to triage
To get started you can use the following recommended issue searches to identify issues that are in need of triage:
* [Issues that have no labels](https://github.com/etcd-io/etcd/issues?q=is%3Aissue+is%3Aopen+sort%3Aupdated+no%3Alabel)
* [Issues created recently](https://github.com/etcd-io/etcd/issues?q=is%3Aissue+is%3Aopen+)
* [Issues not assigned but linked pr](https://github.com/etcd-io/etcd/issues?q=is%3Aopen+is%3Aissue+no%3Aassignee+linked%3Apr)
* [Issues with no comments](https://github.com/etcd-io/etcd/issues?q=is%3Aopen+is%3Aissue+comments%3A0+)
* [Issues with help wanted](https://github.com/etcd-io/etcd/issues?q=is%3Aopen+is%3Aissue+label%3A%22help+wanted%22+)
## Step 2 - Check the issue is valid
Before we start adding labels or trying to work out a priority, our first triage step needs to be working out if the issue actually belongs to the etcd project and is not a duplicate.
### Issues that don't belong to etcd
Sometimes issues are reported that belong to other projects that `etcd` use. For example, `grpc` or `golang` issues. Such issues should be addressed by asking the reporter to open issues in the appropriate other projects.
These issues can generally be closed unless a maintainer and issue reporter see a need to keep it open for tracking purposes. If you have triage permissions please close it, alternatively mention the @etcd-io/members group to request a member with triage access to close the issue.
### Duplicate issues
If an issue is a duplicate, add a comment stating so along with a reference for the original issue and if you have triage permissions please close it, alternatively mention the @etcd-io/members group to request a member with triage access close the issue.
## Step 3 - Apply the appropriate type of label
Adding a `type` label to an issue helps create visibility on the health of the project and helps contributors identify potential priorities, i.e. addressing existing bugs or test flakes before implementing new features.
### Support requests
As a general rule, the focus for etcd support is to address common themes in a broad way that helps all users, i.e. through channels like known issues, frequently asked questions, and high-quality documentation. To make the best use of project members time we should avoid providing 1:1 support if a broad approach is available.
Some people mistakenly use our GitHub bug report or feature request templates to file support requests. Usually, they are asking for help operating or configuring some aspect of etcd. Support requests for etcd should instead be raised as [discussions](https://github.com/etcd-io/etcd/discussions).
Common types of support requests are:
1. Questions about configuring or operating existing well-documented etcd features, for example, . Note - If an existing feature is not well documented please apply the `area/documentation` label and propose documentation improvements that would prevent future users from stumbling on the problem again.
2. Bug reports or questions about unsupported versions of etcd, for example . When responding to these issues please refer to our [supported versions documentation](https://etcd.io/docs/latest/op-guide/versioning) and encourage the reporter to upgrade to a recent patch release of a supported version as soon as possible. We should limit the effort supporting users that do not make the effort to run a supported version of etcd or ensure their version is patched.
3. Bug reports that do not provide a complete list of steps to reproduce the issue and/or contributors are not able to reproduce the issue, for example, . We should limit the effort we put into reproducing issues ourselves and motivate users to provide the necessary information to accept the bug report.
4. General questions that are filed using feature request or bug report issue templates, for example, . Note - These types of requests may surface good additions to our [frequently asked questions](https://etcd.io/docs/v3.5/faq).
If you identify that an issue is a support request please:
1. Add the `type/support` or `type/question` label.
2. Add the following comment to inform the issue creator that discussions should be used instead and that this issue will be converted to a discussion.
> Thank you for your question, this support issue will be moved to our [Discussion Forums](https://github.com/etcd-io/etcd/discussions).
>
> We are trying to consolidate the channels to which questions for help/support are posted so that we can improve our efficiency in responding to your requests, and make it easier for you to find answers to frequently asked questions and how to address common use cases.
>
> We regularly see messages posted in multiple forums, with the full response thread only in one place or, worse, spread across multiple forums. Also, the large volume of support issues on GitHub is making it difficult for us to use issues to identify real bugs.
>
> Members of the etcd community use Discussion Forums to field support requests. Before posting a new question, please search these for answers to similar questions, and also familiarize yourself with:
>
> 1. [user documentation](https://etcd.io/docs/latest)
> 2. [frequently asked questions](https://etcd.io/docs/v3.5/faq)
>
> Again, thanks for using etcd and raising this question.
>
> The etcd team
3. Finally, click `Convert to discussion` on the right-hand panel, selecting the appropriate discussion category.
### Bug reports
If an issue has been raised as a bug it should already have the `type/bug` label, however, if this is missing for an issue you determine to be a bug please add the label manually.
The next step is to validate if the issue is indeed a bug. If not, add a comment with the findings and close the trivial issue. For non-trivial issues, wait to hear back from the issue reporter and see if there is any objection. If the issue reporter does not reply in 30 days, close the issue.
If the problem can not be reproduced or requires more information, leave a comment for the issue reporter as soon as possible while the issue is fresh for the issue reporter.
### Feature requests
New feature requests should be created via the etcd feature request template and in theory already have the `type/feature` label, however, if this is missing for an issue you determine to be a feature please add the label manually.
### Test flakes
Test flakes are a specific type of bug that the etcd project tracks separately as these are a priority to address. These should be created via the test flake template and in theory already have the `type/flake` label, however, if this is missing for an issue you determine to be related to a flaking test please add the label manually.
## Step 4 - Define the areas impacted
Adding an `area` label to an issue helps create visibility on which areas of the etcd project require attention and helps contributors find issues to work on relating to their particular skills or knowledge of the etcd codebase.
If an issue crosses multiple domains please add additional `area` labels to reflect that.
Below is a brief summary of the area labels in active use by the etcd project along with any notes on their use:
| Label | Notes |
| --- | --- |
| area/external | Tracking label for issues raised that are external to etcd. |
| area/community | |
| area/raft | |
| area/clientv3 | |
| area/performance | |
| area/security | |
| area/tls | |
| area/auth | |
| area/etcdctl | |
| area/etcdutl | |
| area/contrib | Not to be confused with `area/community` this label is specifically used for issues relating to community-maintained scripts or files in the `contrib/` directory which aren't part of the core etcd project. |
| area/documentation | |
| area/tooling | Generally used in relation to the third party / external utilities or tools that are used in various stages of the etcd build, test, or release process, for example, tooling to create sboms. |
| area/testing | |
| area/robustness-testing | |
## Step 5 - Prioritise the issue
If an issue lacks a priority label it has not been formally prioritized yet.
Adding a `priority` label helps the etcd project understand what is important and should be worked on now, and conversely, what is not as important and is on the project backlog.
|Priority label|What it means|Examples|
|---|---|---|
| `priority/critical-urgent` | Maintainers are responsible for making sure that these issues (in their area) are being actively worked on—i.e., drop what you're doing. The stuff is burning. These should be fixed before the next release. | user-visible critical bugs in core features broken builds on tier1 supported platforms tests and critical security issues |
| `priority/important-soon` | Must be staffed and worked on either currently or very soon—ideally in time for the next release. | |
| `priority/important-longterm` | Important over the long term, but may not be currently staffed and/or may require multiple releases to complete. | |
| `priority/backlog` | General agreement that this is a nice-to-have, but no one's available to work on it anytime soon. Community contributions would be most welcome in the meantime, though it might take a while to get them reviewed if reviewers are fully occupied with higher-priority issues—for example, immediately before a release.| |
| `priority/awaiting-more-evidence` | Possibly useful, but not yet enough support to actually get it done. | Mostly placeholders for potentially good ideas, so that they don't get completely forgotten, and can be referenced or deduped every time they come up |
## Step 6 - Support new contributors
As part of the `etcd` triage process once the `kind` and `area` have been determined, please consider if the issue would be suitable for a less experienced contributor. The `good first issue` label is a subset of the `help wanted` label, indicating that members have committed to providing extra assistance for new contributors. All `good first issue` items also have the `help wanted` label.
### Help wanted
Items marked with the `help wanted` label need to ensure that they meet these criteria:
* **Low Barrier to Entry** - It should be easy for new contributors.
* **Clear** - The task is agreed upon and does not require further discussions in the community.
* **Goldilocks priority** - The priority should not be so high that a core contributor should do it, but not too low that it isn’t useful enough for a core contributor to spend time reviewing it, answering questions, helping get it into a release, etc.
### Good first issue
Items marked with `good first issue` are intended for first-time contributors. It indicates that members will keep an eye out for these pull requests and shepherd it through our processes.
New contributors should not be left to find an approver, ping for reviews, decipher test commands, or identify that their build failed due to a flake. It is important to make new contributors feel welcome and valued. We should assure them that they will have an extra level of help with their first contribution.
After a contributor has successfully completed one or two `good first issue` items, they should be ready to move on to `help wanted` items.
* **No Barrier to Entry** - The task is something that a new contributor can tackle without advanced setup or domain knowledge.
* **Solution Explained** - The recommended solution is clearly described in the issue.
* **Gives Examples** - Link to examples of similar implementations so new contributors have a reference guide for their changes.
* **Identifies Relevant Code** - The relevant code and tests to be changed should be linked in the issue.
* **Ready to Test** - There should be existing tests that can be modified, or existing test cases fit to be copied. If the area of code doesn’t have tests, before labeling the issue, add a test fixture. This prep often makes a great help wanted task!
## Step 7 - Follow up
Once initial triage has been completed, issues need to be re-evaluated over time to ensure they don't become stale incorrectly.
### Track important issues
If an issue is at risk of being closed by the stale bot in the future, but is an important issue for the etcd project, then please apply the `stage/tracked` label and remove any `stale` labels that exist. This will ensure the project does not lose sight of the issue.
### Close incomplete issues
Issues that lack enough information from the issue reporter should be closed if the issue reporter does not provide information in 30 days. Issues can always be re-opened at a later date if new information is provided.
### Check for incomplete work
If an issue owned by a developer has no pull request created in 30 days, contact the issue owner and kindly ask about the status of their work, or to release ownership on the issue if needed.
================================================
FILE: Documentation/contributor-guide/triage_prs.md
================================================
# PR management
## Purpose
Speed up PR management.
The `etcd` PRs are listed at https://github.com/etcd-io/etcd/pulls
A PR can have various labels, milestones, reviewers, etc. The detailed list of labels can be found at
https://github.com/kubernetes/kubernetes/labels
Following are a few example searches on PR for convenience:
* [Open PRS for milestone etcd-v3.6](https://github.com/etcd-io/etcd/pulls?utf8=%E2%9C%93&q=is%3Apr+is%3Aopen+milestone%3Aetcd-v3.6)
* [PRs under investigation](https://github.com/etcd-io/etcd/labels/Investigating)
## Scope
These guidelines serve as a primary document for managing PRs and review policy in `etcd`. Everyone is welcome to help manage PRs but the work and responsibilities discussed in this document are created with `etcd` maintainers and active contributors in mind.
## Ensure tests are run
The etcd project use Kubernetes Prow and GitHub Actions to run tests. To ensure all required tests run if a pull request is ready for testing and still has the `needs-ok-to-test` label then please comment on the pull request `/ok-to-test`.
## Handle inactive PRs
Poke PR owner if review comments are not addressed in 15 days. If the PR owner does not reply in 90 days, update the PR with a new commit if possible. If not, inactive PR should be closed after 180 days.
## Poke reviewer if needed
Reviewers are responsive in a timely fashion, but considering everyone is busy, give them some time after requesting a review if a quick response is not provided. If the response is not provided in 10 days, feel free to contact them via adding a comment in the PR or sending an email or message on Slack.
## Verify important labels are in place
Make sure that appropriate reviewers are added to the PR. Also, make sure that a milestone is identified. If any of these or other important labels are missing, add them. If a correct label cannot be decided, leave a comment for the maintainers to do so as needed.
## Review policy
To ensure code quality and shared ownership, this review policy applies to all pull requests (PRs).
### Default rule
PRs should get at least two approvals (/lgtm or GitHub review approval) before merging.
Notes:
* Approvals should come from a maintainer, reviewer, or submodule owner familiar with the relevant code or area.
* If there’s disagreement, maintainers should discuss and agree before merging.
### Exceptions for Less Impactful PRs
For low-risk changes — such as:
* CI workflows
* Documentation
* Comments
The rule can be relaxed:
* One approval is generally enough.
However:
* If the author is a maintainer, they should still get approval from another maintainer, reviewer, or submodule owner, even for minor changes.
================================================
FILE: Documentation/dev-guide/apispec/swagger/rpc.swagger.json
================================================
{
"swagger": "2.0",
"info": {
"title": "api/etcdserverpb/rpc.proto",
"version": "version not set"
},
"tags": [
{
"name": "KV"
},
{
"name": "Watch"
},
{
"name": "Lease"
},
{
"name": "Cluster"
},
{
"name": "Maintenance"
},
{
"name": "Auth"
}
],
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"paths": {
"/v3/auth/authenticate": {
"post": {
"summary": "Authenticate processes an authenticate request.",
"operationId": "Auth_Authenticate",
"responses": {
"200": {
"description": "A successful response.",
"schema": {
"$ref": "#/definitions/etcdserverpbAuthenticateResponse"
}
},
"default": {
"description": "An unexpected error response.",
"schema": {
"$ref": "#/definitions/googleRpcStatus"
}
}
},
"parameters": [
{
"name": "body",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/etcdserverpbAuthenticateRequest"
}
}
],
"tags": [
"Auth"
]
}
},
"/v3/auth/disable": {
"post": {
"summary": "AuthDisable disables authentication.",
"operationId": "Auth_AuthDisable",
"responses": {
"200": {
"description": "A successful response.",
"schema": {
"$ref": "#/definitions/etcdserverpbAuthDisableResponse"
}
},
"default": {
"description": "An unexpected error response.",
"schema": {
"$ref": "#/definitions/googleRpcStatus"
}
}
},
"parameters": [
{
"name": "body",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/etcdserverpbAuthDisableRequest"
}
}
],
"tags": [
"Auth"
]
}
},
"/v3/auth/enable": {
"post": {
"summary": "AuthEnable enables authentication.",
"operationId": "Auth_AuthEnable",
"responses": {
"200": {
"description": "A successful response.",
"schema": {
"$ref": "#/definitions/etcdserverpbAuthEnableResponse"
}
},
"default": {
"description": "An unexpected error response.",
"schema": {
"$ref": "#/definitions/googleRpcStatus"
}
}
},
"parameters": [
{
"name": "body",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/etcdserverpbAuthEnableRequest"
}
}
],
"tags": [
"Auth"
]
}
},
"/v3/auth/role/add": {
"post": {
"summary": "RoleAdd adds a new role. Role name cannot be empty.",
"operationId": "Auth_RoleAdd",
"responses": {
"200": {
"description": "A successful response.",
"schema": {
"$ref": "#/definitions/etcdserverpbAuthRoleAddResponse"
}
},
"default": {
"description": "An unexpected error response.",
"schema": {
"$ref": "#/definitions/googleRpcStatus"
}
}
},
"parameters": [
{
"name": "body",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/etcdserverpbAuthRoleAddRequest"
}
}
],
"tags": [
"Auth"
]
}
},
"/v3/auth/role/delete": {
"post": {
"summary": "RoleDelete deletes a specified role.",
"operationId": "Auth_RoleDelete",
"responses": {
"200": {
"description": "A successful response.",
"schema": {
"$ref": "#/definitions/etcdserverpbAuthRoleDeleteResponse"
}
},
"default": {
"description": "An unexpected error response.",
"schema": {
"$ref": "#/definitions/googleRpcStatus"
}
}
},
"parameters": [
{
"name": "body",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/etcdserverpbAuthRoleDeleteRequest"
}
}
],
"tags": [
"Auth"
]
}
},
"/v3/auth/role/get": {
"post": {
"summary": "RoleGet gets detailed role information.",
"operationId": "Auth_RoleGet",
"responses": {
"200": {
"description": "A successful response.",
"schema": {
"$ref": "#/definitions/etcdserverpbAuthRoleGetResponse"
}
},
"default": {
"description": "An unexpected error response.",
"schema": {
"$ref": "#/definitions/googleRpcStatus"
}
}
},
"parameters": [
{
"name": "body",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/etcdserverpbAuthRoleGetRequest"
}
}
],
"tags": [
"Auth"
]
}
},
"/v3/auth/role/grant": {
"post": {
"summary": "RoleGrantPermission grants a permission of a specified key or range to a specified role.",
"operationId": "Auth_RoleGrantPermission",
"responses": {
"200": {
"description": "A successful response.",
"schema": {
"$ref": "#/definitions/etcdserverpbAuthRoleGrantPermissionResponse"
}
},
"default": {
"description": "An unexpected error response.",
"schema": {
"$ref": "#/definitions/googleRpcStatus"
}
}
},
"parameters": [
{
"name": "body",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/etcdserverpbAuthRoleGrantPermissionRequest"
}
}
],
"tags": [
"Auth"
]
}
},
"/v3/auth/role/list": {
"post": {
"summary": "RoleList gets lists of all roles.",
"operationId": "Auth_RoleList",
"responses": {
"200": {
"description": "A successful response.",
"schema": {
"$ref": "#/definitions/etcdserverpbAuthRoleListResponse"
}
},
"default": {
"description": "An unexpected error response.",
"schema": {
"$ref": "#/definitions/googleRpcStatus"
}
}
},
"parameters": [
{
"name": "body",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/etcdserverpbAuthRoleListRequest"
}
}
],
"tags": [
"Auth"
]
}
},
"/v3/auth/role/revoke": {
"post": {
"summary": "RoleRevokePermission revokes a key or range permission of a specified role.",
"operationId": "Auth_RoleRevokePermission",
"responses": {
"200": {
"description": "A successful response.",
"schema": {
"$ref": "#/definitions/etcdserverpbAuthRoleRevokePermissionResponse"
}
},
"default": {
"description": "An unexpected error response.",
"schema": {
"$ref": "#/definitions/googleRpcStatus"
}
}
},
"parameters": [
{
"name": "body",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/etcdserverpbAuthRoleRevokePermissionRequest"
}
}
],
"tags": [
"Auth"
]
}
},
"/v3/auth/status": {
"post": {
"summary": "AuthStatus displays authentication status.",
"operationId": "Auth_AuthStatus",
"responses": {
"200": {
"description": "A successful response.",
"schema": {
"$ref": "#/definitions/etcdserverpbAuthStatusResponse"
}
},
"default": {
"description": "An unexpected error response.",
"schema": {
"$ref": "#/definitions/googleRpcStatus"
}
}
},
"parameters": [
{
"name": "body",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/etcdserverpbAuthStatusRequest"
}
}
],
"tags": [
"Auth"
]
}
},
"/v3/auth/user/add": {
"post": {
"summary": "UserAdd adds a new user. User name cannot be empty.",
"operationId": "Auth_UserAdd",
"responses": {
"200": {
"description": "A successful response.",
"schema": {
"$ref": "#/definitions/etcdserverpbAuthUserAddResponse"
}
},
"default": {
"description": "An unexpected error response.",
"schema": {
"$ref": "#/definitions/googleRpcStatus"
}
}
},
"parameters": [
{
"name": "body",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/etcdserverpbAuthUserAddRequest"
}
}
],
"tags": [
"Auth"
]
}
},
"/v3/auth/user/changepw": {
"post": {
"summary": "UserChangePassword changes the password of a specified user.",
"operationId": "Auth_UserChangePassword",
"responses": {
"200": {
"description": "A successful response.",
"schema": {
"$ref": "#/definitions/etcdserverpbAuthUserChangePasswordResponse"
}
},
"default": {
"description": "An unexpected error response.",
"schema": {
"$ref": "#/definitions/googleRpcStatus"
}
}
},
"parameters": [
{
"name": "body",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/etcdserverpbAuthUserChangePasswordRequest"
}
}
],
"tags": [
"Auth"
]
}
},
"/v3/auth/user/delete": {
"post": {
"summary": "UserDelete deletes a specified user.",
"operationId": "Auth_UserDelete",
"responses": {
"200": {
"description": "A successful response.",
"schema": {
"$ref": "#/definitions/etcdserverpbAuthUserDeleteResponse"
}
},
"default": {
"description": "An unexpected error response.",
"schema": {
"$ref": "#/definitions/googleRpcStatus"
}
}
},
"parameters": [
{
"name": "body",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/etcdserverpbAuthUserDeleteRequest"
}
}
],
"tags": [
"Auth"
]
}
},
"/v3/auth/user/get": {
"post": {
"summary": "UserGet gets detailed user information.",
"operationId": "Auth_UserGet",
"responses": {
"200": {
"description": "A successful response.",
"schema": {
"$ref": "#/definitions/etcdserverpbAuthUserGetResponse"
}
},
"default": {
"description": "An unexpected error response.",
"schema": {
"$ref": "#/definitions/googleRpcStatus"
}
}
},
"parameters": [
{
"name": "body",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/etcdserverpbAuthUserGetRequest"
}
}
],
"tags": [
"Auth"
]
}
},
"/v3/auth/user/grant": {
"post": {
"summary": "UserGrantRole grants a role to a specified user.",
"operationId": "Auth_UserGrantRole",
"responses": {
"200": {
"description": "A successful response.",
"schema": {
"$ref": "#/definitions/etcdserverpbAuthUserGrantRoleResponse"
}
},
"default": {
"description": "An unexpected error response.",
"schema": {
"$ref": "#/definitions/googleRpcStatus"
}
}
},
"parameters": [
{
"name": "body",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/etcdserverpbAuthUserGrantRoleRequest"
}
}
],
"tags": [
"Auth"
]
}
},
"/v3/auth/user/list": {
"post": {
"summary": "UserList gets a list of all users.",
"operationId": "Auth_UserList",
"responses": {
"200": {
"description": "A successful response.",
"schema": {
"$ref": "#/definitions/etcdserverpbAuthUserListResponse"
}
},
"default": {
"description": "An unexpected error response.",
"schema": {
"$ref": "#/definitions/googleRpcStatus"
}
}
},
"parameters": [
{
"name": "body",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/etcdserverpbAuthUserListRequest"
}
}
],
"tags": [
"Auth"
]
}
},
"/v3/auth/user/revoke": {
"post": {
"summary": "UserRevokeRole revokes a role of specified user.",
"operationId": "Auth_UserRevokeRole",
"responses": {
"200": {
"description": "A successful response.",
"schema": {
"$ref": "#/definitions/etcdserverpbAuthUserRevokeRoleResponse"
}
},
"default": {
"description": "An unexpected error response.",
"schema": {
"$ref": "#/definitions/googleRpcStatus"
}
}
},
"parameters": [
{
"name": "body",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/etcdserverpbAuthUserRevokeRoleRequest"
}
}
],
"tags": [
"Auth"
]
}
},
"/v3/cluster/member/add": {
"post": {
"summary": "MemberAdd adds a member into the cluster.",
"operationId": "Cluster_MemberAdd",
"responses": {
"200": {
"description": "A successful response.",
"schema": {
"$ref": "#/definitions/etcdserverpbMemberAddResponse"
}
},
"default": {
"description": "An unexpected error response.",
"schema": {
"$ref": "#/definitions/googleRpcStatus"
}
}
},
"parameters": [
{
"name": "body",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/etcdserverpbMemberAddRequest"
}
}
],
"tags": [
"Cluster"
]
}
},
"/v3/cluster/member/list": {
"post": {
"summary": "MemberList lists all the members in the cluster.",
"operationId": "Cluster_MemberList",
"responses": {
"200": {
"description": "A successful response.",
"schema": {
"$ref": "#/definitions/etcdserverpbMemberListResponse"
}
},
"default": {
"description": "An unexpected error response.",
"schema": {
"$ref": "#/definitions/googleRpcStatus"
}
}
},
"parameters": [
{
"name": "body",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/etcdserverpbMemberListRequest"
}
}
],
"tags": [
"Cluster"
]
}
},
"/v3/cluster/member/promote": {
"post": {
"summary": "MemberPromote promotes a member from raft learner (non-voting) to raft voting member.",
"operationId": "Cluster_MemberPromote",
"responses": {
"200": {
"description": "A successful response.",
"schema": {
"$ref": "#/definitions/etcdserverpbMemberPromoteResponse"
}
},
"default": {
"description": "An unexpected error response.",
"schema": {
"$ref": "#/definitions/googleRpcStatus"
}
}
},
"parameters": [
{
"name": "body",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/etcdserverpbMemberPromoteRequest"
}
}
],
"tags": [
"Cluster"
]
}
},
"/v3/cluster/member/remove": {
"post": {
"summary": "MemberRemove removes an existing member from the cluster.",
"operationId": "Cluster_MemberRemove",
"responses": {
"200": {
"description": "A successful response.",
"schema": {
"$ref": "#/definitions/etcdserverpbMemberRemoveResponse"
}
},
"default": {
"description": "An unexpected error response.",
"schema": {
"$ref": "#/definitions/googleRpcStatus"
}
}
},
"parameters": [
{
"name": "body",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/etcdserverpbMemberRemoveRequest"
}
}
],
"tags": [
"Cluster"
]
}
},
"/v3/cluster/member/update": {
"post": {
"summary": "MemberUpdate updates the member configuration.",
"operationId": "Cluster_MemberUpdate",
"responses": {
"200": {
"description": "A successful response.",
"schema": {
"$ref": "#/definitions/etcdserverpbMemberUpdateResponse"
}
},
"default": {
"description": "An unexpected error response.",
"schema": {
"$ref": "#/definitions/googleRpcStatus"
}
}
},
"parameters": [
{
"name": "body",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/etcdserverpbMemberUpdateRequest"
}
}
],
"tags": [
"Cluster"
]
}
},
"/v3/kv/compaction": {
"post": {
"summary": "Compact compacts the event history in the etcd key-value store. The key-value\nstore should be periodically compacted or the event history will continue to grow\nindefinitely.",
"operationId": "KV_Compact",
"responses": {
"200": {
"description": "A successful response.",
"schema": {
"$ref": "#/definitions/etcdserverpbCompactionResponse"
}
},
"default": {
"description": "An unexpected error response.",
"schema": {
"$ref": "#/definitions/googleRpcStatus"
}
}
},
"parameters": [
{
"name": "body",
"description": "CompactionRequest compacts the key-value store up to a given revision. All superseded keys\nwith a revision less than the compaction revision will be removed.",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/etcdserverpbCompactionRequest"
}
}
],
"tags": [
"KV"
]
}
},
"/v3/kv/deleterange": {
"post": {
"summary": "DeleteRange deletes the given range from the key-value store.\nA delete request increments the revision of the key-value store\nand generates a delete event in the event history for every deleted key.",
"operationId": "KV_DeleteRange",
"responses": {
"200": {
"description": "A successful response.",
"schema": {
"$ref": "#/definitions/etcdserverpbDeleteRangeResponse"
}
},
"default": {
"description": "An unexpected error response.",
"schema": {
"$ref": "#/definitions/googleRpcStatus"
}
}
},
"parameters": [
{
"name": "body",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/etcdserverpbDeleteRangeRequest"
}
}
],
"tags": [
"KV"
]
}
},
"/v3/kv/lease/leases": {
"post": {
"summary": "LeaseLeases lists all existing leases.",
"operationId": "Lease_LeaseLeases2",
"responses": {
"200": {
"description": "A successful response.",
"schema": {
"$ref": "#/definitions/etcdserverpbLeaseLeasesResponse"
}
},
"default": {
"description": "An unexpected error response.",
"schema": {
"$ref": "#/definitions/googleRpcStatus"
}
}
},
"parameters": [
{
"name": "body",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/etcdserverpbLeaseLeasesRequest"
}
}
],
"tags": [
"Lease"
]
}
},
"/v3/kv/lease/revoke": {
"post": {
"summary": "LeaseRevoke revokes a lease. All keys attached to the lease will expire and be deleted.",
"operationId": "Lease_LeaseRevoke2",
"responses": {
"200": {
"description": "A successful response.",
"schema": {
"$ref": "#/definitions/etcdserverpbLeaseRevokeResponse"
}
},
"default": {
"description": "An unexpected error response.",
"schema": {
"$ref": "#/definitions/googleRpcStatus"
}
}
},
"parameters": [
{
"name": "body",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/etcdserverpbLeaseRevokeRequest"
}
}
],
"tags": [
"Lease"
]
}
},
"/v3/kv/lease/timetolive": {
"post": {
"summary": "LeaseTimeToLive retrieves lease information.",
"operationId": "Lease_LeaseTimeToLive2",
"responses": {
"200": {
"description": "A successful response.",
"schema": {
"$ref": "#/definitions/etcdserverpbLeaseTimeToLiveResponse"
}
},
"default": {
"description": "An unexpected error response.",
"schema": {
"$ref": "#/definitions/googleRpcStatus"
}
}
},
"parameters": [
{
"name": "body",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/etcdserverpbLeaseTimeToLiveRequest"
}
}
],
"tags": [
"Lease"
]
}
},
"/v3/kv/put": {
"post": {
"summary": "Put puts the given key into the key-value store.\nA put request increments the revision of the key-value store\nand generates one event in the event history.",
"operationId": "KV_Put",
"responses": {
"200": {
"description": "A successful response.",
"schema": {
"$ref": "#/definitions/etcdserverpbPutResponse"
}
},
"default": {
"description": "An unexpected error response.",
"schema": {
"$ref": "#/definitions/googleRpcStatus"
}
}
},
"parameters": [
{
"name": "body",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/etcdserverpbPutRequest"
}
}
],
"tags": [
"KV"
]
}
},
"/v3/kv/range": {
"post": {
"summary": "Range gets the keys in the range from the key-value store.",
"operationId": "KV_Range",
"responses": {
"200": {
"description": "A successful response.",
"schema": {
"$ref": "#/definitions/etcdserverpbRangeResponse"
}
},
"default": {
"description": "An unexpected error response.",
"schema": {
"$ref": "#/definitions/googleRpcStatus"
}
}
},
"parameters": [
{
"name": "body",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/etcdserverpbRangeRequest"
}
}
],
"tags": [
"KV"
]
}
},
"/v3/kv/txn": {
"post": {
"summary": "Txn processes multiple requests in a single transaction.\nA txn request increments the revision of the key-value store\nand generates events with the same revision for every completed request.\nIt is not allowed to modify the same key several times within one txn.",
"operationId": "KV_Txn",
"responses": {
"200": {
"description": "A successful response.",
"schema": {
"$ref": "#/definitions/etcdserverpbTxnResponse"
}
},
"default": {
"description": "An unexpected error response.",
"schema": {
"$ref": "#/definitions/googleRpcStatus"
}
}
},
"parameters": [
{
"name": "body",
"description": "From google paxosdb paper:\nOur implementation hinges around a powerful primitive which we call MultiOp. All other database\noperations except for iteration are implemented as a single call to MultiOp. A MultiOp is applied atomically\nand consists of three components:\n1. A list of tests called guard. Each test in guard checks a single entry in the database. It may check\nfor the absence or presence of a value, or compare with a given value. Two different tests in the guard\nmay apply to the same or different entries in the database. All tests in the guard are applied and\nMultiOp returns the results. If all tests are true, MultiOp executes t op (see item 2 below), otherwise\nit executes f op (see item 3 below).\n2. A list of database operations called t op. Each operation in the list is either an insert, delete, or\nlookup operation, and applies to a single database entry. Two different operations in the list may apply\nto the same or different entries in the database. These operations are executed\nif guard evaluates to\ntrue.\n3. A list of database operations called f op. Like t op, but executed if guard evaluates to false.",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/etcdserverpbTxnRequest"
}
}
],
"tags": [
"KV"
]
}
},
"/v3/lease/grant": {
"post": {
"summary": "LeaseGrant creates a lease which expires if the server does not receive a keepAlive\nwithin a given time to live period. All keys attached to the lease will be expired and\ndeleted if the lease expires. Each expired key generates a delete event in the event history.",
"operationId": "Lease_LeaseGrant",
"responses": {
"200": {
"description": "A successful response.",
"schema": {
"$ref": "#/definitions/etcdserverpbLeaseGrantResponse"
}
},
"default": {
"description": "An unexpected error response.",
"schema": {
"$ref": "#/definitions/googleRpcStatus"
}
}
},
"parameters": [
{
"name": "body",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/etcdserverpbLeaseGrantRequest"
}
}
],
"tags": [
"Lease"
]
}
},
"/v3/lease/keepalive": {
"post": {
"summary": "LeaseKeepAlive keeps the lease alive by streaming keep alive requests from the client\nto the server and streaming keep alive responses from the server to the client.",
"operationId": "Lease_LeaseKeepAlive",
"responses": {
"200": {
"description": "A successful response.(streaming responses)",
"schema": {
"type": "object",
"properties": {
"result": {
"$ref": "#/definitions/etcdserverpbLeaseKeepAliveResponse"
},
"error": {
"$ref": "#/definitions/googleRpcStatus"
}
},
"title": "Stream result of etcdserverpbLeaseKeepAliveResponse"
}
},
"default": {
"description": "An unexpected error response.",
"schema": {
"$ref": "#/definitions/googleRpcStatus"
}
}
},
"parameters": [
{
"name": "body",
"description": " (streaming inputs)",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/etcdserverpbLeaseKeepAliveRequest"
}
}
],
"tags": [
"Lease"
]
}
},
"/v3/lease/leases": {
"post": {
"summary": "LeaseLeases lists all existing leases.",
"operationId": "Lease_LeaseLeases",
"responses": {
"200": {
"description": "A successful response.",
"schema": {
"$ref": "#/definitions/etcdserverpbLeaseLeasesResponse"
}
},
"default": {
"description": "An unexpected error response.",
"schema": {
"$ref": "#/definitions/googleRpcStatus"
}
}
},
"parameters": [
{
"name": "body",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/etcdserverpbLeaseLeasesRequest"
}
}
],
"tags": [
"Lease"
]
}
},
"/v3/lease/revoke": {
"post": {
"summary": "LeaseRevoke revokes a lease. All keys attached to the lease will expire and be deleted.",
"operationId": "Lease_LeaseRevoke",
"responses": {
"200": {
"description": "A successful response.",
"schema": {
"$ref": "#/definitions/etcdserverpbLeaseRevokeResponse"
}
},
"default": {
"description": "An unexpected error response.",
"schema": {
"$ref": "#/definitions/googleRpcStatus"
}
}
},
"parameters": [
{
"name": "body",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/etcdserverpbLeaseRevokeRequest"
}
}
],
"tags": [
"Lease"
]
}
},
"/v3/lease/timetolive": {
"post": {
"summary": "LeaseTimeToLive retrieves lease information.",
"operationId": "Lease_LeaseTimeToLive",
"responses": {
"200": {
"description": "A successful response.",
"schema": {
"$ref": "#/definitions/etcdserverpbLeaseTimeToLiveResponse"
}
},
"default": {
"description": "An unexpected error response.",
"schema": {
"$ref": "#/definitions/googleRpcStatus"
}
}
},
"parameters": [
{
"name": "body",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/etcdserverpbLeaseTimeToLiveRequest"
}
}
],
"tags": [
"Lease"
]
}
},
"/v3/maintenance/alarm": {
"post": {
"summary": "Alarm activates, deactivates, and queries alarms regarding cluster health.",
"operationId": "Maintenance_Alarm",
"responses": {
"200": {
"description": "A successful response.",
"schema": {
"$ref": "#/definitions/etcdserverpbAlarmResponse"
}
},
"default": {
"description": "An unexpected error response.",
"schema": {
"$ref": "#/definitions/googleRpcStatus"
}
}
},
"parameters": [
{
"name": "body",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/etcdserverpbAlarmRequest"
}
}
],
"tags": [
"Maintenance"
]
}
},
"/v3/maintenance/defragment": {
"post": {
"summary": "Defragment defragments a member's backend database to recover storage space.",
"operationId": "Maintenance_Defragment",
"responses": {
"200": {
"description": "A successful response.",
"schema": {
"$ref": "#/definitions/etcdserverpbDefragmentResponse"
}
},
"default": {
"description": "An unexpected error response.",
"schema": {
"$ref": "#/definitions/googleRpcStatus"
}
}
},
"parameters": [
{
"name": "body",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/etcdserverpbDefragmentRequest"
}
}
],
"tags": [
"Maintenance"
]
}
},
"/v3/maintenance/downgrade": {
"post": {
"summary": "Downgrade requests downgrades, verifies feasibility or cancels downgrade\non the cluster version.\nSupported since etcd 3.5.",
"operationId": "Maintenance_Downgrade",
"responses": {
"200": {
"description": "A successful response.",
"schema": {
"$ref": "#/definitions/etcdserverpbDowngradeResponse"
}
},
"default": {
"description": "An unexpected error response.",
"schema": {
"$ref": "#/definitions/googleRpcStatus"
}
}
},
"parameters": [
{
"name": "body",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/etcdserverpbDowngradeRequest"
}
}
],
"tags": [
"Maintenance"
]
}
},
"/v3/maintenance/hash": {
"post": {
"summary": "Hash computes the hash of whole backend keyspace,\nincluding key, lease, and other buckets in storage.\nThis is designed for testing ONLY!\nDo not rely on this in production with ongoing transactions,\nsince Hash operation does not hold MVCC locks.\nUse \"HashKV\" API instead for \"key\" bucket consistency checks.",
"operationId": "Maintenance_Hash",
"responses": {
"200": {
"description": "A successful response.",
"schema": {
"$ref": "#/definitions/etcdserverpbHashResponse"
}
},
"default": {
"description": "An unexpected error response.",
"schema": {
"$ref": "#/definitions/googleRpcStatus"
}
}
},
"parameters": [
{
"name": "body",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/etcdserverpbHashRequest"
}
}
],
"tags": [
"Maintenance"
]
}
},
"/v3/maintenance/hashkv": {
"post": {
"summary": "HashKV computes the hash of all MVCC keys up to a given revision.\nIt only iterates \"key\" bucket in backend storage.",
"operationId": "Maintenance_HashKV",
"responses": {
"200": {
"description": "A successful response.",
"schema": {
"$ref": "#/definitions/etcdserverpbHashKVResponse"
}
},
"default": {
"description": "An unexpected error response.",
"schema": {
"$ref": "#/definitions/googleRpcStatus"
}
}
},
"parameters": [
{
"name": "body",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/etcdserverpbHashKVRequest"
}
}
],
"tags": [
"Maintenance"
]
}
},
"/v3/maintenance/snapshot": {
"post": {
"summary": "Snapshot sends a snapshot of the entire backend from a member over a stream to a client.",
"operationId": "Maintenance_Snapshot",
"responses": {
"200": {
"description": "A successful response.(streaming responses)",
"schema": {
"type": "object",
"properties": {
"result": {
"$ref": "#/definitions/etcdserverpbSnapshotResponse"
},
"error": {
"$ref": "#/definitions/googleRpcStatus"
}
},
"title": "Stream result of etcdserverpbSnapshotResponse"
}
},
"default": {
"description": "An unexpected error response.",
"schema": {
"$ref": "#/definitions/googleRpcStatus"
}
}
},
"parameters": [
{
"name": "body",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/etcdserverpbSnapshotRequest"
}
}
],
"tags": [
"Maintenance"
]
}
},
"/v3/maintenance/status": {
"post": {
"summary": "Status gets the status of the member.",
"operationId": "Maintenance_Status",
"responses": {
"200": {
"description": "A successful response.",
"schema": {
"$ref": "#/definitions/etcdserverpbStatusResponse"
}
},
"default": {
"description": "An unexpected error response.",
"schema": {
"$ref": "#/definitions/googleRpcStatus"
}
}
},
"parameters": [
{
"name": "body",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/etcdserverpbStatusRequest"
}
}
],
"tags": [
"Maintenance"
]
}
},
"/v3/maintenance/transfer-leadership": {
"post": {
"summary": "MoveLeader requests current leader node to transfer its leadership to transferee.",
"operationId": "Maintenance_MoveLeader",
"responses": {
"200": {
"description": "A successful response.",
"schema": {
"$ref": "#/definitions/etcdserverpbMoveLeaderResponse"
}
},
"default": {
"description": "An unexpected error response.",
"schema": {
"$ref": "#/definitions/googleRpcStatus"
}
}
},
"parameters": [
{
"name": "body",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/etcdserverpbMoveLeaderRequest"
}
}
],
"tags": [
"Maintenance"
]
}
},
"/v3/watch": {
"post": {
"summary": "Watch watches for events happening or that have happened. Both input and output\nare streams; the input stream is for creating and canceling watchers and the output\nstream sends events. One watch RPC can watch on multiple key ranges, streaming events\nfor several watches at once. The entire event history can be watched starting from the\nlast compaction revision.",
"operationId": "Watch_Watch",
"responses": {
"200": {
"description": "A successful response.(streaming responses)",
"schema": {
"type": "object",
"properties": {
"result": {
"$ref": "#/definitions/etcdserverpbWatchResponse"
},
"error": {
"$ref": "#/definitions/googleRpcStatus"
}
},
"title": "Stream result of etcdserverpbWatchResponse"
}
},
"default": {
"description": "An unexpected error response.",
"schema": {
"$ref": "#/definitions/googleRpcStatus"
}
}
},
"parameters": [
{
"name": "body",
"description": " (streaming inputs)",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/etcdserverpbWatchRequest"
}
}
],
"tags": [
"Watch"
]
}
}
},
"definitions": {
"AlarmRequestAlarmAction": {
"type": "string",
"enum": [
"GET",
"ACTIVATE",
"DEACTIVATE"
],
"default": "GET"
},
"CompareCompareResult": {
"type": "string",
"enum": [
"EQUAL",
"GREATER",
"LESS",
"NOT_EQUAL"
],
"default": "EQUAL"
},
"CompareCompareTarget": {
"type": "string",
"enum": [
"VERSION",
"CREATE",
"MOD",
"VALUE",
"LEASE"
],
"default": "VERSION"
},
"DowngradeRequestDowngradeAction": {
"type": "string",
"enum": [
"VALIDATE",
"ENABLE",
"CANCEL"
],
"default": "VALIDATE"
},
"EventEventType": {
"type": "string",
"enum": [
"PUT",
"DELETE"
],
"default": "PUT"
},
"RangeRequestSortOrder": {
"type": "string",
"enum": [
"NONE",
"ASCEND",
"DESCEND"
],
"default": "NONE",
"title": "- NONE: default, no sorting\n - ASCEND: lowest target value first\n - DESCEND: highest target value first"
},
"RangeRequestSortTarget": {
"type": "string",
"enum": [
"KEY",
"VERSION",
"CREATE",
"MOD",
"VALUE"
],
"default": "KEY"
},
"WatchCreateRequestFilterType": {
"type": "string",
"enum": [
"NOPUT",
"NODELETE"
],
"default": "NOPUT",
"description": " - NOPUT: filter out put event.\n - NODELETE: filter out delete event."
},
"authpbPermission": {
"type": "object",
"properties": {
"permType": {
"$ref": "#/definitions/authpbPermissionType"
},
"key": {
"type": "string",
"format": "byte"
},
"range_end": {
"type": "string",
"format": "byte"
}
},
"title": "Permission is a single entity"
},
"authpbPermissionType": {
"type": "string",
"enum": [
"READ",
"WRITE",
"READWRITE"
],
"default": "READ"
},
"authpbUserAddOptions": {
"type": "object",
"properties": {
"no_password": {
"type": "boolean"
}
}
},
"etcdserverpbAlarmMember": {
"type": "object",
"properties": {
"memberID": {
"type": "string",
"format": "uint64",
"description": "memberID is the ID of the member associated with the raised alarm."
},
"alarm": {
"$ref": "#/definitions/etcdserverpbAlarmType",
"description": "alarm is the type of alarm which has been raised."
}
}
},
"etcdserverpbAlarmRequest": {
"type": "object",
"properties": {
"action": {
"$ref": "#/definitions/AlarmRequestAlarmAction",
"description": "action is the kind of alarm request to issue. The action\nmay GET alarm statuses, ACTIVATE an alarm, or DEACTIVATE a\nraised alarm."
},
"memberID": {
"type": "string",
"format": "uint64",
"description": "memberID is the ID of the member associated with the alarm. If memberID is 0, the\nalarm request covers all members."
},
"alarm": {
"$ref": "#/definitions/etcdserverpbAlarmType",
"description": "alarm is the type of alarm to consider for this request."
}
}
},
"etcdserverpbAlarmResponse": {
"type": "object",
"properties": {
"header": {
"$ref": "#/definitions/etcdserverpbResponseHeader"
},
"alarms": {
"type": "array",
"items": {
"type": "object",
"$ref": "#/definitions/etcdserverpbAlarmMember"
},
"description": "alarms is a list of alarms associated with the alarm request."
}
}
},
"etcdserverpbAlarmType": {
"type": "string",
"enum": [
"NONE",
"NOSPACE",
"CORRUPT"
],
"default": "NONE",
"title": "- NONE: default, used to query if any alarm is active\n - NOSPACE: space quota is exhausted\n - CORRUPT: kv store corruption detected"
},
"etcdserverpbAuthDisableRequest": {
"type": "object"
},
"etcdserverpbAuthDisableResponse": {
"type": "object",
"properties": {
"header": {
"$ref": "#/definitions/etcdserverpbResponseHeader"
}
}
},
"etcdserverpbAuthEnableRequest": {
"type": "object"
},
"etcdserverpbAuthEnableResponse": {
"type": "object",
"properties": {
"header": {
"$ref": "#/definitions/etcdserverpbResponseHeader"
}
}
},
"etcdserverpbAuthRoleAddRequest": {
"type": "object",
"properties": {
"name": {
"type": "string",
"description": "name is the name of the role to add to the authentication system."
}
}
},
"etcdserverpbAuthRoleAddResponse": {
"type": "object",
"properties": {
"header": {
"$ref": "#/definitions/etcdserverpbResponseHeader"
}
}
},
"etcdserverpbAuthRoleDeleteRequest": {
"type": "object",
"properties": {
"role": {
"type": "string"
}
}
},
"etcdserverpbAuthRoleDeleteResponse": {
"type": "object",
"properties": {
"header": {
"$ref": "#/definitions/etcdserverpbResponseHeader"
}
}
},
"etcdserverpbAuthRoleGetRequest": {
"type": "object",
"properties": {
"role": {
"type": "string"
}
}
},
"etcdserverpbAuthRoleGetResponse": {
"type": "object",
"properties": {
"header": {
"$ref": "#/definitions/etcdserverpbResponseHeader"
},
"perm": {
"type": "array",
"items": {
"type": "object",
"$ref": "#/definitions/authpbPermission"
}
}
}
},
"etcdserverpbAuthRoleGrantPermissionRequest": {
"type": "object",
"properties": {
"name": {
"type": "string",
"description": "name is the name of the role which will be granted the permission."
},
"perm": {
"$ref": "#/definitions/authpbPermission",
"description": "perm is the permission to grant to the role."
}
}
},
"etcdserverpbAuthRoleGrantPermissionResponse": {
"type": "object",
"properties": {
"header": {
"$ref": "#/definitions/etcdserverpbResponseHeader"
}
}
},
"etcdserverpbAuthRoleListRequest": {
"type": "object"
},
"etcdserverpbAuthRoleListResponse": {
"type": "object",
"properties": {
"header": {
"$ref": "#/definitions/etcdserverpbResponseHeader"
},
"roles": {
"type": "array",
"items": {
"type": "string"
}
}
}
},
"etcdserverpbAuthRoleRevokePermissionRequest": {
"type": "object",
"properties": {
"role": {
"type": "string"
},
"key": {
"type": "string",
"format": "byte"
},
"range_end": {
"type": "string",
"format": "byte"
}
}
},
"etcdserverpbAuthRoleRevokePermissionResponse": {
"type": "object",
"properties": {
"header": {
"$ref": "#/definitions/etcdserverpbResponseHeader"
}
}
},
"etcdserverpbAuthStatusRequest": {
"type": "object"
},
"etcdserverpbAuthStatusResponse": {
"type": "object",
"properties": {
"header": {
"$ref": "#/definitions/etcdserverpbResponseHeader"
},
"enabled": {
"type": "boolean"
},
"authRevision": {
"type": "string",
"format": "uint64",
"title": "authRevision is the current revision of auth store"
}
}
},
"etcdserverpbAuthUserAddRequest": {
"type": "object",
"properties": {
"name": {
"type": "string"
},
"password": {
"type": "string"
},
"options": {
"$ref": "#/definitions/authpbUserAddOptions"
},
"hashedPassword": {
"type": "string"
}
}
},
"etcdserverpbAuthUserAddResponse": {
"type": "object",
"properties": {
"header": {
"$ref": "#/definitions/etcdserverpbResponseHeader"
}
}
},
"etcdserverpbAuthUserChangePasswordRequest": {
"type": "object",
"properties": {
"name": {
"type": "string",
"description": "name is the name of the user whose password is being changed."
},
"password": {
"type": "string",
"description": "password is the new password for the user. Note that this field will be removed in the API layer."
},
"hashedPassword": {
"type": "string",
"description": "hashedPassword is the new password for the user. Note that this field will be initialized in the API layer."
}
}
},
"etcdserverpbAuthUserChangePasswordResponse": {
"type": "object",
"properties": {
"header": {
"$ref": "#/definitions/etcdserverpbResponseHeader"
}
}
},
"etcdserverpbAuthUserDeleteRequest": {
"type": "object",
"properties": {
"name": {
"type": "string",
"description": "name is the name of the user to delete."
}
}
},
"etcdserverpbAuthUserDeleteResponse": {
"type": "object",
"properties": {
"header": {
"$ref": "#/definitions/etcdserverpbResponseHeader"
}
}
},
"etcdserverpbAuthUserGetRequest": {
"type": "object",
"properties": {
"name": {
"type": "string"
}
}
},
"etcdserverpbAuthUserGetResponse": {
"type": "object",
"properties": {
"header": {
"$ref": "#/definitions/etcdserverpbResponseHeader"
},
"roles": {
"type": "array",
"items": {
"type": "string"
}
}
}
},
"etcdserverpbAuthUserGrantRoleRequest": {
"type": "object",
"properties": {
"user": {
"type": "string",
"description": "user is the name of the user which should be granted a given role."
},
"role": {
"type": "string",
"description": "role is the name of the role to grant to the user."
}
}
},
"etcdserverpbAuthUserGrantRoleResponse": {
"type": "object",
"properties": {
"header": {
"$ref": "#/definitions/etcdserverpbResponseHeader"
}
}
},
"etcdserverpbAuthUserListRequest": {
"type": "object"
},
"etcdserverpbAuthUserListResponse": {
"type": "object",
"properties": {
"header": {
"$ref": "#/definitions/etcdserverpbResponseHeader"
},
"users": {
"type": "array",
"items": {
"type": "string"
}
}
}
},
"etcdserverpbAuthUserRevokeRoleRequest": {
"type": "object",
"properties": {
"name": {
"type": "string"
},
"role": {
"type": "string"
}
}
},
"etcdserverpbAuthUserRevokeRoleResponse": {
"type": "object",
"properties": {
"header": {
"$ref": "#/definitions/etcdserverpbResponseHeader"
}
}
},
"etcdserverpbAuthenticateRequest": {
"type": "object",
"properties": {
"name": {
"type": "string"
},
"password": {
"type": "string"
}
}
},
"etcdserverpbAuthenticateResponse": {
"type": "object",
"properties": {
"header": {
"$ref": "#/definitions/etcdserverpbResponseHeader"
},
"token": {
"type": "string",
"title": "token is an authorized token that can be used in succeeding RPCs"
}
}
},
"etcdserverpbCompactionRequest": {
"type": "object",
"properties": {
"revision": {
"type": "string",
"format": "int64",
"description": "revision is the key-value store revision for the compaction operation."
},
"physical": {
"type": "boolean",
"description": "physical is set so the RPC will wait until the compaction is physically\napplied to the local database such that compacted entries are totally\nremoved from the backend database."
}
},
"description": "CompactionRequest compacts the key-value store up to a given revision. All superseded keys\nwith a revision less than the compaction revision will be removed."
},
"etcdserverpbCompactionResponse": {
"type": "object",
"properties": {
"header": {
"$ref": "#/definitions/etcdserverpbResponseHeader"
}
}
},
"etcdserverpbCompare": {
"type": "object",
"properties": {
"result": {
"$ref": "#/definitions/CompareCompareResult",
"description": "result is logical comparison operation for this comparison."
},
"target": {
"$ref": "#/definitions/CompareCompareTarget",
"description": "target is the key-value field to inspect for the comparison."
},
"key": {
"type": "string",
"format": "byte",
"description": "key is the subject key for the comparison operation."
},
"version": {
"type": "string",
"format": "int64",
"title": "version is the version of the given key"
},
"create_revision": {
"type": "string",
"format": "int64",
"title": "create_revision is the creation revision of the given key"
},
"mod_revision": {
"type": "string",
"format": "int64",
"description": "mod_revision is the last modified revision of the given key."
},
"value": {
"type": "string",
"format": "byte",
"description": "value is the value of the given key, in bytes."
},
"lease": {
"type": "string",
"format": "int64",
"description": "lease is the lease id of the given key.\n\nleave room for more target_union field tags, jump to 64"
},
"range_end": {
"type": "string",
"format": "byte",
"description": "range_end compares the given target to all keys in the range [key, range_end).\nSee RangeRequest for more details on key ranges.\n\nTODO: fill out with most of the rest of RangeRequest fields when needed."
}
}
},
"etcdserverpbDefragmentRequest": {
"type": "object"
},
"etcdserverpbDefragmentResponse": {
"type": "object",
"properties": {
"header": {
"$ref": "#/definitions/etcdserverpbResponseHeader"
}
}
},
"etcdserverpbDeleteRangeRequest": {
"type": "object",
"properties": {
"key": {
"type": "string",
"format": "byte",
"description": "key is the first key to delete in the range."
},
"range_end": {
"type": "string",
"format": "byte",
"description": "range_end is the key following the last key to delete for the range [key, range_end).\nIf range_end is not given, the range is defined to contain only the key argument.\nIf range_end is one bit larger than the given key, then the range is all the keys\nwith the prefix (the given key).\nIf range_end is '\\0', the range is all keys greater than or equal to the key argument."
},
"prev_kv": {
"type": "boolean",
"description": "If prev_kv is set, etcd gets the previous key-value pairs before deleting it.\nThe previous key-value pairs will be returned in the delete response."
}
}
},
"etcdserverpbDeleteRangeResponse": {
"type": "object",
"properties": {
"header": {
"$ref": "#/definitions/etcdserverpbResponseHeader"
},
"deleted": {
"type": "string",
"format": "int64",
"description": "deleted is the number of keys deleted by the delete range request."
},
"prev_kvs": {
"type": "array",
"items": {
"type": "object",
"$ref": "#/definitions/mvccpbKeyValue"
},
"description": "if prev_kv is set in the request, the previous key-value pairs will be returned."
}
}
},
"etcdserverpbDowngradeInfo": {
"type": "object",
"properties": {
"enabled": {
"type": "boolean",
"description": "enabled indicates whether the cluster is enabled to downgrade."
},
"targetVersion": {
"type": "string",
"description": "targetVersion is the target downgrade version."
}
}
},
"etcdserverpbDowngradeRequest": {
"type": "object",
"properties": {
"action": {
"$ref": "#/definitions/DowngradeRequestDowngradeAction",
"description": "action is the kind of downgrade request to issue. The action may\nVALIDATE the target version, DOWNGRADE the cluster version,\nor CANCEL the current downgrading job."
},
"version": {
"type": "string",
"description": "version is the target version to downgrade."
}
}
},
"etcdserverpbDowngradeResponse": {
"type": "object",
"properties": {
"header": {
"$ref": "#/definitions/etcdserverpbResponseHeader"
},
"version": {
"type": "string",
"description": "version is the current cluster version."
}
}
},
"etcdserverpbHashKVRequest": {
"type": "object",
"properties": {
"revision": {
"type": "string",
"format": "int64",
"description": "revision is the key-value store revision for the hash operation."
}
}
},
"etcdserverpbHashKVResponse": {
"type": "object",
"properties": {
"header": {
"$ref": "#/definitions/etcdserverpbResponseHeader"
},
"hash": {
"type": "integer",
"format": "int64",
"description": "hash is the hash value computed from the responding member's MVCC keys up to a given revision."
},
"compact_revision": {
"type": "string",
"format": "int64",
"description": "compact_revision is the compacted revision of key-value store when hash begins."
},
"hash_revision": {
"type": "string",
"format": "int64",
"description": "hash_revision is the revision up to which the hash is calculated."
}
}
},
"etcdserverpbHashRequest": {
"type": "object"
},
"etcdserverpbHashResponse": {
"type": "object",
"properties": {
"header": {
"$ref": "#/definitions/etcdserverpbResponseHeader"
},
"hash": {
"type": "integer",
"format": "int64",
"description": "hash is the hash value computed from the responding member's KV's backend."
}
}
},
"etcdserverpbLeaseGrantRequest": {
"type": "object",
"properties": {
"TTL": {
"type": "string",
"format": "int64",
"description": "TTL is the advisory time-to-live in seconds. Expired lease will return -1."
},
"ID": {
"type": "string",
"format": "int64",
"description": "ID is the requested ID for the lease. If ID is set to 0, the lessor chooses an ID."
}
}
},
"etcdserverpbLeaseGrantResponse": {
"type": "object",
"properties": {
"header": {
"$ref": "#/definitions/etcdserverpbResponseHeader"
},
"ID": {
"type": "string",
"format": "int64",
"description": "ID is the lease ID for the granted lease."
},
"TTL": {
"type": "string",
"format": "int64",
"description": "TTL is the server chosen lease time-to-live in seconds."
},
"error": {
"type": "string"
}
}
},
"etcdserverpbLeaseKeepAliveRequest": {
"type": "object",
"properties": {
"ID": {
"type": "string",
"format": "int64",
"description": "ID is the lease ID for the lease to keep alive."
}
}
},
"etcdserverpbLeaseKeepAliveResponse": {
"type": "object",
"properties": {
"header": {
"$ref": "#/definitions/etcdserverpbResponseHeader"
},
"ID": {
"type": "string",
"format": "int64",
"description": "ID is the lease ID from the keep alive request."
},
"TTL": {
"type": "string",
"format": "int64",
"description": "TTL is the new time-to-live for the lease."
}
}
},
"etcdserverpbLeaseLeasesRequest": {
"type": "object"
},
"etcdserverpbLeaseLeasesResponse": {
"type": "object",
"properties": {
"header": {
"$ref": "#/definitions/etcdserverpbResponseHeader"
},
"leases": {
"type": "array",
"items": {
"type": "object",
"$ref": "#/definitions/etcdserverpbLeaseStatus"
}
}
}
},
"etcdserverpbLeaseRevokeRequest": {
"type": "object",
"properties": {
"ID": {
"type": "string",
"format": "int64",
"description": "ID is the lease ID to revoke. When the ID is revoked, all associated keys will be deleted."
}
}
},
"etcdserverpbLeaseRevokeResponse": {
"type": "object",
"properties": {
"header": {
"$ref": "#/definitions/etcdserverpbResponseHeader"
}
}
},
"etcdserverpbLeaseStatus": {
"type": "object",
"properties": {
"ID": {
"type": "string",
"format": "int64",
"title": "TODO: int64 TTL = 2;"
}
}
},
"etcdserverpbLeaseTimeToLiveRequest": {
"type": "object",
"properties": {
"ID": {
"type": "string",
"format": "int64",
"description": "ID is the lease ID for the lease."
},
"keys": {
"type": "boolean",
"description": "keys is true to query all the keys attached to this lease."
}
}
},
"etcdserverpbLeaseTimeToLiveResponse": {
"type": "object",
"properties": {
"header": {
"$ref": "#/definitions/etcdserverpbResponseHeader"
},
"ID": {
"type": "string",
"format": "int64",
"description": "ID is the lease ID from the keep alive request."
},
"TTL": {
"type": "string",
"format": "int64",
"description": "TTL is the remaining TTL in seconds for the lease; the lease will expire in under TTL+1 seconds."
},
"grantedTTL": {
"type": "string",
"format": "int64",
"description": "GrantedTTL is the initial granted time in seconds upon lease creation/renewal."
},
"keys": {
"type": "array",
"items": {
"type": "string",
"format": "byte"
},
"description": "Keys is the list of keys attached to this lease."
}
}
},
"etcdserverpbMember": {
"type": "object",
"properties": {
"ID": {
"type": "string",
"format": "uint64",
"description": "ID is the member ID for this member."
},
"name": {
"type": "string",
"description": "name is the human-readable name of the member. If the member is not started, the name will be an empty string."
},
"peerURLs": {
"type": "array",
"items": {
"type": "string"
},
"description": "peerURLs is the list of URLs the member exposes to the cluster for communication."
},
"clientURLs": {
"type": "array",
"items": {
"type": "string"
},
"description": "clientURLs is the list of URLs the member exposes to clients for communication. If the member is not started, clientURLs will be empty."
},
"isLearner": {
"type": "boolean",
"description": "isLearner indicates if the member is raft learner."
}
}
},
"etcdserverpbMemberAddRequest": {
"type": "object",
"properties": {
"peerURLs": {
"type": "array",
"items": {
"type": "string"
},
"description": "peerURLs is the list of URLs the added member will use to communicate with the cluster."
},
"isLearner": {
"type": "boolean",
"description": "isLearner indicates if the added member is raft learner."
}
}
},
"etcdserverpbMemberAddResponse": {
"type": "object",
"properties": {
"header": {
"$ref": "#/definitions/etcdserverpbResponseHeader"
},
"member": {
"$ref": "#/definitions/etcdserverpbMember",
"description": "member is the member information for the added member."
},
"members": {
"type": "array",
"items": {
"type": "object",
"$ref": "#/definitions/etcdserverpbMember"
},
"description": "members is a list of all members after adding the new member."
}
}
},
"etcdserverpbMemberListRequest": {
"type": "object",
"properties": {
"linearizable": {
"type": "boolean"
}
}
},
"etcdserverpbMemberListResponse": {
"type": "object",
"properties": {
"header": {
"$ref": "#/definitions/etcdserverpbResponseHeader"
},
"members": {
"type": "array",
"items": {
"type": "object",
"$ref": "#/definitions/etcdserverpbMember"
},
"description": "members is a list of all members associated with the cluster."
}
}
},
"etcdserverpbMemberPromoteRequest": {
"type": "object",
"properties": {
"ID": {
"type": "string",
"format": "uint64",
"description": "ID is the member ID of the member to promote."
}
}
},
"etcdserverpbMemberPromoteResponse": {
"type": "object",
"properties": {
"header": {
"$ref": "#/definitions/etcdserverpbResponseHeader"
},
"members": {
"type": "array",
"items": {
"type": "object",
"$ref": "#/definitions/etcdserverpbMember"
},
"description": "members is a list of all members after promoting the member."
}
}
},
"etcdserverpbMemberRemoveRequest": {
"type": "object",
"properties": {
"ID": {
"type": "string",
"format": "uint64",
"description": "ID is the member ID of the member to remove."
}
}
},
"etcdserverpbMemberRemoveResponse": {
"type": "object",
"properties": {
"header": {
"$ref": "#/definitions/etcdserverpbResponseHeader"
},
"members": {
"type": "array",
"items": {
"type": "object",
"$ref": "#/definitions/etcdserverpbMember"
},
"description": "members is a list of all members after removing the member."
}
}
},
"etcdserverpbMemberUpdateRequest": {
"type": "object",
"properties": {
"ID": {
"type": "string",
"format": "uint64",
"description": "ID is the member ID of the member to update."
},
"peerURLs": {
"type": "array",
"items": {
"type": "string"
},
"description": "peerURLs is the new list of URLs the member will use to communicate with the cluster."
}
}
},
"etcdserverpbMemberUpdateResponse": {
"type": "object",
"properties": {
"header": {
"$ref": "#/definitions/etcdserverpbResponseHeader"
},
"members": {
"type": "array",
"items": {
"type": "object",
"$ref": "#/definitions/etcdserverpbMember"
},
"description": "members is a list of all members after updating the member."
}
}
},
"etcdserverpbMoveLeaderRequest": {
"type": "object",
"properties": {
"targetID": {
"type": "string",
"format": "uint64",
"description": "targetID is the node ID for the new leader."
}
}
},
"etcdserverpbMoveLeaderResponse": {
"type": "object",
"properties": {
"header": {
"$ref": "#/definitions/etcdserverpbResponseHeader"
}
}
},
"etcdserverpbPutRequest": {
"type": "object",
"properties": {
"key": {
"type": "string",
"format": "byte",
"description": "key is the key, in bytes, to put into the key-value store."
},
"value": {
"type": "string",
"format": "byte",
"description": "value is the value, in bytes, to associate with the key in the key-value store."
},
"lease": {
"type": "string",
"format": "int64",
"description": "lease is the lease ID to associate with the key in the key-value store. A lease\nvalue of 0 indicates no lease."
},
"prev_kv": {
"type": "boolean",
"description": "If prev_kv is set, etcd gets the previous key-value pair before changing it.\nThe previous key-value pair will be returned in the put response."
},
"ignore_value": {
"type": "boolean",
"description": "If ignore_value is set, etcd updates the key using its current value.\nReturns an error if the key does not exist."
},
"ignore_lease": {
"type": "boolean",
"description": "If ignore_lease is set, etcd updates the key using its current lease.\nReturns an error if the key does not exist."
}
}
},
"etcdserverpbPutResponse": {
"type": "object",
"properties": {
"header": {
"$ref": "#/definitions/etcdserverpbResponseHeader"
},
"prev_kv": {
"$ref": "#/definitions/mvccpbKeyValue",
"description": "if prev_kv is set in the request, the previous key-value pair will be returned."
}
}
},
"etcdserverpbRangeRequest": {
"type": "object",
"properties": {
"key": {
"type": "string",
"format": "byte",
"description": "key is the first key for the range. If range_end is not given, the request only looks up key."
},
"range_end": {
"type": "string",
"format": "byte",
"description": "range_end is the upper bound on the requested range [key, range_end).\nIf range_end is '\\0', the range is all keys \u003e= key.\nIf range_end is key plus one (e.g., \"aa\"+1 == \"ab\", \"a\\xff\"+1 == \"b\"),\nthen the range request gets all keys prefixed with key.\nIf both key and range_end are '\\0', then the range request returns all keys."
},
"limit": {
"type": "string",
"format": "int64",
"description": "limit is a limit on the number of keys returned for the request. When limit is set to 0,\nit is treated as no limit."
},
"revision": {
"type": "string",
"format": "int64",
"description": "revision is the point-in-time of the key-value store to use for the range.\nIf revision is less or equal to zero, the range is over the newest key-value store.\nIf the revision has been compacted, ErrCompacted is returned as a response."
},
"sort_order": {
"$ref": "#/definitions/RangeRequestSortOrder",
"description": "sort_order is the order for returned sorted results."
},
"sort_target": {
"$ref": "#/definitions/RangeRequestSortTarget",
"description": "sort_target is the key-value field to use for sorting."
},
"serializable": {
"type": "boolean",
"description": "serializable sets the range request to use serializable member-local reads.\nRange requests are linearizable by default; linearizable requests have higher\nlatency and lower throughput than serializable requests but reflect the current\nconsensus of the cluster. For better performance, in exchange for possible stale reads,\na serializable range request is served locally without needing to reach consensus\nwith other nodes in the cluster."
},
"keys_only": {
"type": "boolean",
"description": "keys_only when set returns only the keys and not the values."
},
"count_only": {
"type": "boolean",
"description": "count_only when set returns only the count of the keys in the range."
},
"min_mod_revision": {
"type": "string",
"format": "int64",
"description": "min_mod_revision is the lower bound for returned key mod revisions; all keys with\nlesser mod revisions will be filtered away."
},
"max_mod_revision": {
"type": "string",
"format": "int64",
"description": "max_mod_revision is the upper bound for returned key mod revisions; all keys with\ngreater mod revisions will be filtered away."
},
"min_create_revision": {
"type": "string",
"format": "int64",
"description": "min_create_revision is the lower bound for returned key create revisions; all keys with\nlesser create revisions will be filtered away."
},
"max_create_revision": {
"type": "string",
"format": "int64",
"description": "max_create_revision is the upper bound for returned key create revisions; all keys with\ngreater create revisions will be filtered away."
}
}
},
"etcdserverpbRangeResponse": {
"type": "object",
"properties": {
"header": {
"$ref": "#/definitions/etcdserverpbResponseHeader"
},
"kvs": {
"type": "array",
"items": {
"type": "object",
"$ref": "#/definitions/mvccpbKeyValue"
},
"description": "kvs is the list of key-value pairs matched by the range request.\nkvs is empty when count is requested."
},
"more": {
"type": "boolean",
"description": "more indicates if there are more keys to return in the requested range."
},
"count": {
"type": "string",
"format": "int64",
"description": "count is set to the actual number of keys within the range when requested.\nUnlike Kvs, it is unaffected by limits and filters (e.g., Min/Max, Create/Modify, Revisions)\nand reflects the full count within the specified range."
}
}
},
"etcdserverpbRequestOp": {
"type": "object",
"properties": {
"request_range": {
"$ref": "#/definitions/etcdserverpbRangeRequest"
},
"request_put": {
"$ref": "#/definitions/etcdserverpbPutRequest"
},
"request_delete_range": {
"$ref": "#/definitions/etcdserverpbDeleteRangeRequest"
},
"request_txn": {
"$ref": "#/definitions/etcdserverpbTxnRequest"
}
}
},
"etcdserverpbResponseHeader": {
"type": "object",
"properties": {
"cluster_id": {
"type": "string",
"format": "uint64",
"description": "cluster_id is the ID of the cluster which sent the response."
},
"member_id": {
"type": "string",
"format": "uint64",
"description": "member_id is the ID of the member which sent the response."
},
"revision": {
"type": "string",
"format": "int64",
"description": "revision is the key-value store revision when the request was applied, and it's\nunset (so 0) in case of calls not interacting with key-value store.\nFor watch progress responses, the header.revision indicates progress. All future events\nreceived in this stream are guaranteed to have a higher revision number than the\nheader.revision number."
},
"raft_term": {
"type": "string",
"format": "uint64",
"description": "raft_term is the raft term when the request was applied."
}
}
},
"etcdserverpbResponseOp": {
"type": "object",
"properties": {
"response_range": {
"$ref": "#/definitions/etcdserverpbRangeResponse"
},
"response_put": {
"$ref": "#/definitions/etcdserverpbPutResponse"
},
"response_delete_range": {
"$ref": "#/definitions/etcdserverpbDeleteRangeResponse"
},
"response_txn": {
"$ref": "#/definitions/etcdserverpbTxnResponse"
}
}
},
"etcdserverpbSnapshotRequest": {
"type": "object"
},
"etcdserverpbSnapshotResponse": {
"type": "object",
"properties": {
"header": {
"$ref": "#/definitions/etcdserverpbResponseHeader",
"description": "header has the current key-value store information. The first header in the snapshot\nstream indicates the point in time of the snapshot."
},
"remaining_bytes": {
"type": "string",
"format": "uint64",
"title": "remaining_bytes is the number of blob bytes to be sent after this message"
},
"blob": {
"type": "string",
"format": "byte",
"description": "blob contains the next chunk of the snapshot in the snapshot stream."
},
"version": {
"type": "string",
"description": "local version of server that created the snapshot.\nIn cluster with binaries with different version, each cluster can return different result.\nInforms which etcd server version should be used when restoring the snapshot."
}
}
},
"etcdserverpbStatusRequest": {
"type": "object"
},
"etcdserverpbStatusResponse": {
"type": "object",
"properties": {
"header": {
"$ref": "#/definitions/etcdserverpbResponseHeader"
},
"version": {
"type": "string",
"description": "version is the cluster protocol version used by the responding member."
},
"dbSize": {
"type": "string",
"format": "int64",
"description": "dbSize is the size of the backend database physically allocated, in bytes, of the responding member."
},
"leader": {
"type": "string",
"format": "uint64",
"description": "leader is the member ID which the responding member believes is the current leader."
},
"raftIndex": {
"type": "string",
"format": "uint64",
"description": "raftIndex is the current raft committed index of the responding member."
},
"raftTerm": {
"type": "string",
"format": "uint64",
"description": "raftTerm is the current raft term of the responding member."
},
"raftAppliedIndex": {
"type": "string",
"format": "uint64",
"description": "raftAppliedIndex is the current raft applied index of the responding member."
},
"errors": {
"type": "array",
"items": {
"type": "string"
},
"description": "errors contains alarm/health information and status."
},
"dbSizeInUse": {
"type": "string",
"format": "int64",
"description": "dbSizeInUse is the size of the backend database logically in use, in bytes, of the responding member."
},
"isLearner": {
"type": "boolean",
"description": "isLearner indicates if the member is raft learner."
},
"storageVersion": {
"type": "string",
"description": "storageVersion is the version of the db file. It might be updated with delay in relationship to the target cluster version."
},
"dbSizeQuota": {
"type": "string",
"format": "int64",
"title": "dbSizeQuota is the configured etcd storage quota in bytes (the value passed to etcd instance by flag --quota-backend-bytes)"
},
"downgradeInfo": {
"$ref": "#/definitions/etcdserverpbDowngradeInfo",
"description": "downgradeInfo indicates if there is downgrade process."
}
}
},
"etcdserverpbTxnRequest": {
"type": "object",
"properties": {
"compare": {
"type": "array",
"items": {
"type": "object",
"$ref": "#/definitions/etcdserverpbCompare"
},
"description": "compare is a list of predicates representing a conjunction of terms.\nIf the comparisons succeed, then the success requests will be processed in order,\nand the response will contain their respective responses in order.\nIf the comparisons fail, then the failure requests will be processed in order,\nand the response will contain their respective responses in order."
},
"success": {
"type": "array",
"items": {
"type": "object",
"$ref": "#/definitions/etcdserverpbRequestOp"
},
"description": "success is a list of requests which will be applied when compare evaluates to true."
},
"failure": {
"type": "array",
"items": {
"type": "object",
"$ref": "#/definitions/etcdserverpbRequestOp"
},
"description": "failure is a list of requests which will be applied when compare evaluates to false."
}
},
"description": "From google paxosdb paper:\nOur implementation hinges around a powerful primitive which we call MultiOp. All other database\noperations except for iteration are implemented as a single call to MultiOp. A MultiOp is applied atomically\nand consists of three components:\n1. A list of tests called guard. Each test in guard checks a single entry in the database. It may check\nfor the absence or presence of a value, or compare with a given value. Two different tests in the guard\nmay apply to the same or different entries in the database. All tests in the guard are applied and\nMultiOp returns the results. If all tests are true, MultiOp executes t op (see item 2 below), otherwise\nit executes f op (see item 3 below).\n2. A list of database operations called t op. Each operation in the list is either an insert, delete, or\nlookup operation, and applies to a single database entry. Two different operations in the list may apply\nto the same or different entries in the database. These operations are executed\nif guard evaluates to\ntrue.\n3. A list of database operations called f op. Like t op, but executed if guard evaluates to false."
},
"etcdserverpbTxnResponse": {
"type": "object",
"properties": {
"header": {
"$ref": "#/definitions/etcdserverpbResponseHeader"
},
"succeeded": {
"type": "boolean",
"description": "succeeded is set to true if the compare evaluated to true or false otherwise."
},
"responses": {
"type": "array",
"items": {
"type": "object",
"$ref": "#/definitions/etcdserverpbResponseOp"
},
"description": "responses is a list of responses corresponding to the results from applying\nsuccess if succeeded is true or failure if succeeded is false."
}
}
},
"etcdserverpbWatchCancelRequest": {
"type": "object",
"properties": {
"watch_id": {
"type": "string",
"format": "int64",
"description": "watch_id is the watcher id to cancel so that no more events are transmitted."
}
}
},
"etcdserverpbWatchCreateRequest": {
"type": "object",
"properties": {
"key": {
"type": "string",
"format": "byte",
"description": "key is the key to register for watching."
},
"range_end": {
"type": "string",
"format": "byte",
"description": "range_end is the end of the range [key, range_end) to watch. If range_end is not given,\nonly the key argument is watched. If range_end is equal to '\\0', all keys greater than\nor equal to the key argument are watched.\nIf the range_end is one bit larger than the given key,\nthen all keys with the prefix (the given key) will be watched."
},
"start_revision": {
"type": "string",
"format": "int64",
"description": "start_revision is an optional revision to watch from (inclusive). No start_revision is \"now\"."
},
"progress_notify": {
"type": "boolean",
"description": "progress_notify is set so that the etcd server will periodically send a WatchResponse with\nno events to the new watcher if there are no recent events. It is useful when clients\nwish to recover a disconnected watcher starting from a recent known revision.\nThe etcd server may decide how often it will send notifications based on current load."
},
"filters": {
"type": "array",
"items": {
"$ref": "#/definitions/WatchCreateRequestFilterType"
},
"description": "filters filter the events at server side before it sends back to the watcher."
},
"prev_kv": {
"type": "boolean",
"description": "If prev_kv is set, created watcher gets the previous KV before the event happens.\nIf the previous KV is already compacted, nothing will be returned."
},
"watch_id": {
"type": "string",
"format": "int64",
"description": "If watch_id is provided and non-zero, it will be assigned to this watcher.\nSince creating a watcher in etcd is not a synchronous operation,\nthis can be used ensure that ordering is correct when creating multiple\nwatchers on the same stream. Creating a watcher with an ID already in\nuse on the stream will cause an error to be returned."
},
"fragment": {
"type": "boolean",
"description": "fragment enables splitting large revisions into multiple watch responses."
}
}
},
"etcdserverpbWatchProgressRequest": {
"type": "object",
"description": "Requests the a watch stream progress status be sent in the watch response stream as soon as\npossible."
},
"etcdserverpbWatchRequest": {
"type": "object",
"properties": {
"create_request": {
"$ref": "#/definitions/etcdserverpbWatchCreateRequest"
},
"cancel_request": {
"$ref": "#/definitions/etcdserverpbWatchCancelRequest"
},
"progress_request": {
"$ref": "#/definitions/etcdserverpbWatchProgressRequest"
}
}
},
"etcdserverpbWatchResponse": {
"type": "object",
"properties": {
"header": {
"$ref": "#/definitions/etcdserverpbResponseHeader"
},
"watch_id": {
"type": "string",
"format": "int64",
"description": "watch_id is the ID of the watcher that corresponds to the response."
},
"created": {
"type": "boolean",
"description": "created is set to true if the response is for a create watch request.\nThe client should record the watch_id and expect to receive events for\nthe created watcher from the same stream.\nAll events sent to the created watcher will attach with the same watch_id."
},
"canceled": {
"type": "boolean",
"description": "canceled is set to true if the response is for a cancel watch request\nor if the start_revision has already been compacted.\nNo further events will be sent to the canceled watcher."
},
"compact_revision": {
"type": "string",
"format": "int64",
"description": "compact_revision is set to the minimum index if a watcher tries to watch\nat a compacted index.\n\nThis happens when creating a watcher at a compacted revision or the watcher cannot\ncatch up with the progress of the key-value store.\n\nThe client should treat the watcher as canceled and should not try to create any\nwatcher with the same start_revision again."
},
"cancel_reason": {
"type": "string",
"description": "cancel_reason indicates the reason for canceling the watcher."
},
"fragment": {
"type": "boolean",
"description": "framgment is true if large watch response was split over multiple responses."
},
"events": {
"type": "array",
"items": {
"type": "object",
"$ref": "#/definitions/mvccpbEvent"
}
}
}
},
"googleRpcStatus": {
"type": "object",
"properties": {
"code": {
"type": "integer",
"format": "int32"
},
"message": {
"type": "string"
},
"details": {
"type": "array",
"items": {
"type": "object",
"$ref": "#/definitions/protobufAny"
}
}
}
},
"mvccpbEvent": {
"type": "object",
"properties": {
"type": {
"$ref": "#/definitions/EventEventType",
"description": "type is the kind of event. If type is a PUT, it indicates\nnew data has been stored to the key. If type is a DELETE,\nit indicates the key was deleted."
},
"kv": {
"$ref": "#/definitions/mvccpbKeyValue",
"description": "kv holds the KeyValue for the event.\nA PUT event contains current kv pair.\nA PUT event with kv.Version=1 indicates the creation of a key.\nA DELETE/EXPIRE event contains the deleted key with\nits modification revision set to the revision of deletion."
},
"prev_kv": {
"$ref": "#/definitions/mvccpbKeyValue",
"description": "prev_kv holds the key-value pair before the event happens."
}
}
},
"mvccpbKeyValue": {
"type": "object",
"properties": {
"key": {
"type": "string",
"format": "byte",
"description": "key is the key in bytes. An empty key is not allowed."
},
"create_revision": {
"type": "string",
"format": "int64",
"description": "create_revision is the revision of last creation on this key."
},
"mod_revision": {
"type": "string",
"format": "int64",
"description": "mod_revision is the revision of last modification on this key."
},
"version": {
"type": "string",
"format": "int64",
"description": "version is the version of the key. A deletion resets\nthe version to zero and any modification of the key\nincreases its version."
},
"value": {
"type": "string",
"format": "byte",
"description": "value is the value held by the key, in bytes."
},
"lease": {
"type": "string",
"format": "int64",
"description": "lease is the ID of the lease that attached to key.\nWhen the attached lease expires, the key will be deleted.\nIf lease is 0, then no lease is attached to the key."
}
}
},
"protobufAny": {
"type": "object",
"properties": {
"@type": {
"type": "string"
}
},
"additionalProperties": {}
}
},
"securityDefinitions": {
"ApiKey": {
"type": "apiKey",
"name": "Authorization",
"in": "header"
}
},
"security": [
{
"ApiKey": []
}
]
}
================================================
FILE: Documentation/dev-guide/apispec/swagger/v3election.swagger.json
================================================
{
"swagger": "2.0",
"info": {
"title": "server/etcdserver/api/v3election/v3electionpb/v3election.proto",
"version": "version not set"
},
"tags": [
{
"name": "Election"
}
],
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"paths": {
"/v3/election/campaign": {
"post": {
"summary": "Campaign waits to acquire leadership in an election, returning a LeaderKey\nrepresenting the leadership if successful. The LeaderKey can then be used\nto issue new values on the election, transactionally guard API requests on\nleadership still being held, and resign from the election.",
"operationId": "Election_Campaign",
"responses": {
"200": {
"description": "A successful response.",
"schema": {
"$ref": "#/definitions/v3electionpbCampaignResponse"
}
},
"default": {
"description": "An unexpected error response.",
"schema": {
"$ref": "#/definitions/rpcStatus"
}
}
},
"parameters": [
{
"name": "body",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/v3electionpbCampaignRequest"
}
}
],
"tags": [
"Election"
]
}
},
"/v3/election/leader": {
"post": {
"summary": "Leader returns the current election proclamation, if any.",
"operationId": "Election_Leader",
"responses": {
"200": {
"description": "A successful response.",
"schema": {
"$ref": "#/definitions/v3electionpbLeaderResponse"
}
},
"default": {
"description": "An unexpected error response.",
"schema": {
"$ref": "#/definitions/rpcStatus"
}
}
},
"parameters": [
{
"name": "body",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/v3electionpbLeaderRequest"
}
}
],
"tags": [
"Election"
]
}
},
"/v3/election/observe": {
"post": {
"summary": "Observe streams election proclamations in-order as made by the election's\nelected leaders.",
"operationId": "Election_Observe",
"responses": {
"200": {
"description": "A successful response.(streaming responses)",
"schema": {
"type": "object",
"properties": {
"result": {
"$ref": "#/definitions/v3electionpbLeaderResponse"
},
"error": {
"$ref": "#/definitions/rpcStatus"
}
},
"title": "Stream result of v3electionpbLeaderResponse"
}
},
"default": {
"description": "An unexpected error response.",
"schema": {
"$ref": "#/definitions/rpcStatus"
}
}
},
"parameters": [
{
"name": "body",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/v3electionpbLeaderRequest"
}
}
],
"tags": [
"Election"
]
}
},
"/v3/election/proclaim": {
"post": {
"summary": "Proclaim updates the leader's posted value with a new value.",
"operationId": "Election_Proclaim",
"responses": {
"200": {
"description": "A successful response.",
"schema": {
"$ref": "#/definitions/v3electionpbProclaimResponse"
}
},
"default": {
"description": "An unexpected error response.",
"schema": {
"$ref": "#/definitions/rpcStatus"
}
}
},
"parameters": [
{
"name": "body",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/v3electionpbProclaimRequest"
}
}
],
"tags": [
"Election"
]
}
},
"/v3/election/resign": {
"post": {
"summary": "Resign releases election leadership so other campaigners may acquire\nleadership on the election.",
"operationId": "Election_Resign",
"responses": {
"200": {
"description": "A successful response.",
"schema": {
"$ref": "#/definitions/v3electionpbResignResponse"
}
},
"default": {
"description": "An unexpected error response.",
"schema": {
"$ref": "#/definitions/rpcStatus"
}
}
},
"parameters": [
{
"name": "body",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/v3electionpbResignRequest"
}
}
],
"tags": [
"Election"
]
}
}
},
"definitions": {
"etcdserverpbResponseHeader": {
"type": "object",
"properties": {
"cluster_id": {
"type": "string",
"format": "uint64",
"description": "cluster_id is the ID of the cluster which sent the response."
},
"member_id": {
"type": "string",
"format": "uint64",
"description": "member_id is the ID of the member which sent the response."
},
"revision": {
"type": "string",
"format": "int64",
"description": "revision is the key-value store revision when the request was applied, and it's\nunset (so 0) in case of calls not interacting with key-value store.\nFor watch progress responses, the header.revision indicates progress. All future events\nreceived in this stream are guaranteed to have a higher revision number than the\nheader.revision number."
},
"raft_term": {
"type": "string",
"format": "uint64",
"description": "raft_term is the raft term when the request was applied."
}
}
},
"mvccpbKeyValue": {
"type": "object",
"properties": {
"key": {
"type": "string",
"format": "byte",
"description": "key is the key in bytes. An empty key is not allowed."
},
"create_revision": {
"type": "string",
"format": "int64",
"description": "create_revision is the revision of last creation on this key."
},
"mod_revision": {
"type": "string",
"format": "int64",
"description": "mod_revision is the revision of last modification on this key."
},
"version": {
"type": "string",
"format": "int64",
"description": "version is the version of the key. A deletion resets\nthe version to zero and any modification of the key\nincreases its version."
},
"value": {
"type": "string",
"format": "byte",
"description": "value is the value held by the key, in bytes."
},
"lease": {
"type": "string",
"format": "int64",
"description": "lease is the ID of the lease that attached to key.\nWhen the attached lease expires, the key will be deleted.\nIf lease is 0, then no lease is attached to the key."
}
}
},
"protobufAny": {
"type": "object",
"properties": {
"@type": {
"type": "string"
}
},
"additionalProperties": {}
},
"rpcStatus": {
"type": "object",
"properties": {
"code": {
"type": "integer",
"format": "int32"
},
"message": {
"type": "string"
},
"details": {
"type": "array",
"items": {
"type": "object",
"$ref": "#/definitions/protobufAny"
}
}
}
},
"v3electionpbCampaignRequest": {
"type": "object",
"properties": {
"name": {
"type": "string",
"format": "byte",
"description": "name is the election's identifier for the campaign."
},
"lease": {
"type": "string",
"format": "int64",
"description": "lease is the ID of the lease attached to leadership of the election. If the\nlease expires or is revoked before resigning leadership, then the\nleadership is transferred to the next campaigner, if any."
},
"value": {
"type": "string",
"format": "byte",
"description": "value is the initial proclaimed value set when the campaigner wins the\nelection."
}
}
},
"v3electionpbCampaignResponse": {
"type": "object",
"properties": {
"header": {
"$ref": "#/definitions/etcdserverpbResponseHeader"
},
"leader": {
"$ref": "#/definitions/v3electionpbLeaderKey",
"description": "leader describes the resources used for holding leadereship of the election."
}
}
},
"v3electionpbLeaderKey": {
"type": "object",
"properties": {
"name": {
"type": "string",
"format": "byte",
"description": "name is the election identifier that corresponds to the leadership key."
},
"key": {
"type": "string",
"format": "byte",
"description": "key is an opaque key representing the ownership of the election. If the key\nis deleted, then leadership is lost."
},
"rev": {
"type": "string",
"format": "int64",
"description": "rev is the creation revision of the key. It can be used to test for ownership\nof an election during transactions by testing the key's creation revision\nmatches rev."
},
"lease": {
"type": "string",
"format": "int64",
"description": "lease is the lease ID of the election leader."
}
}
},
"v3electionpbLeaderRequest": {
"type": "object",
"properties": {
"name": {
"type": "string",
"format": "byte",
"description": "name is the election identifier for the leadership information."
}
}
},
"v3electionpbLeaderResponse": {
"type": "object",
"properties": {
"header": {
"$ref": "#/definitions/etcdserverpbResponseHeader"
},
"kv": {
"$ref": "#/definitions/mvccpbKeyValue",
"description": "kv is the key-value pair representing the latest leader update."
}
}
},
"v3electionpbProclaimRequest": {
"type": "object",
"properties": {
"leader": {
"$ref": "#/definitions/v3electionpbLeaderKey",
"description": "leader is the leadership hold on the election."
},
"value": {
"type": "string",
"format": "byte",
"description": "value is an update meant to overwrite the leader's current value."
}
}
},
"v3electionpbProclaimResponse": {
"type": "object",
"properties": {
"header": {
"$ref": "#/definitions/etcdserverpbResponseHeader"
}
}
},
"v3electionpbResignRequest": {
"type": "object",
"properties": {
"leader": {
"$ref": "#/definitions/v3electionpbLeaderKey",
"description": "leader is the leadership to relinquish by resignation."
}
}
},
"v3electionpbResignResponse": {
"type": "object",
"properties": {
"header": {
"$ref": "#/definitions/etcdserverpbResponseHeader"
}
}
}
}
}
================================================
FILE: Documentation/dev-guide/apispec/swagger/v3lock.swagger.json
================================================
{
"swagger": "2.0",
"info": {
"title": "server/etcdserver/api/v3lock/v3lockpb/v3lock.proto",
"version": "version not set"
},
"tags": [
{
"name": "Lock"
}
],
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"paths": {
"/v3/lock/lock": {
"post": {
"summary": "Lock acquires a distributed shared lock on a given named lock.\nOn success, it will return a unique key that exists so long as the\nlock is held by the caller. This key can be used in conjunction with\ntransactions to safely ensure updates to etcd only occur while holding\nlock ownership. The lock is held until Unlock is called on the key or the\nlease associate with the owner expires.",
"operationId": "Lock_Lock",
"responses": {
"200": {
"description": "A successful response.",
"schema": {
"$ref": "#/definitions/v3lockpbLockResponse"
}
},
"default": {
"description": "An unexpected error response.",
"schema": {
"$ref": "#/definitions/rpcStatus"
}
}
},
"parameters": [
{
"name": "body",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/v3lockpbLockRequest"
}
}
],
"tags": [
"Lock"
]
}
},
"/v3/lock/unlock": {
"post": {
"summary": "Unlock takes a key returned by Lock and releases the hold on lock. The\nnext Lock caller waiting for the lock will then be woken up and given\nownership of the lock.",
"operationId": "Lock_Unlock",
"responses": {
"200": {
"description": "A successful response.",
"schema": {
"$ref": "#/definitions/v3lockpbUnlockResponse"
}
},
"default": {
"description": "An unexpected error response.",
"schema": {
"$ref": "#/definitions/rpcStatus"
}
}
},
"parameters": [
{
"name": "body",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/v3lockpbUnlockRequest"
}
}
],
"tags": [
"Lock"
]
}
}
},
"definitions": {
"etcdserverpbResponseHeader": {
"type": "object",
"properties": {
"cluster_id": {
"type": "string",
"format": "uint64",
"description": "cluster_id is the ID of the cluster which sent the response."
},
"member_id": {
"type": "string",
"format": "uint64",
"description": "member_id is the ID of the member which sent the response."
},
"revision": {
"type": "string",
"format": "int64",
"description": "revision is the key-value store revision when the request was applied, and it's\nunset (so 0) in case of calls not interacting with key-value store.\nFor watch progress responses, the header.revision indicates progress. All future events\nreceived in this stream are guaranteed to have a higher revision number than the\nheader.revision number."
},
"raft_term": {
"type": "string",
"format": "uint64",
"description": "raft_term is the raft term when the request was applied."
}
}
},
"protobufAny": {
"type": "object",
"properties": {
"@type": {
"type": "string"
}
},
"additionalProperties": {}
},
"rpcStatus": {
"type": "object",
"properties": {
"code": {
"type": "integer",
"format": "int32"
},
"message": {
"type": "string"
},
"details": {
"type": "array",
"items": {
"type": "object",
"$ref": "#/definitions/protobufAny"
}
}
}
},
"v3lockpbLockRequest": {
"type": "object",
"properties": {
"name": {
"type": "string",
"format": "byte",
"description": "name is the identifier for the distributed shared lock to be acquired."
},
"lease": {
"type": "string",
"format": "int64",
"description": "lease is the ID of the lease that will be attached to ownership of the\nlock. If the lease expires or is revoked and currently holds the lock,\nthe lock is automatically released. Calls to Lock with the same lease will\nbe treated as a single acquisition; locking twice with the same lease is a\nno-op."
}
}
},
"v3lockpbLockResponse": {
"type": "object",
"properties": {
"header": {
"$ref": "#/definitions/etcdserverpbResponseHeader"
},
"key": {
"type": "string",
"format": "byte",
"description": "key is a key that will exist on etcd for the duration that the Lock caller\nowns the lock. Users should not modify this key or the lock may exhibit\nundefined behavior."
}
}
},
"v3lockpbUnlockRequest": {
"type": "object",
"properties": {
"key": {
"type": "string",
"format": "byte",
"description": "key is the lock ownership key granted by Lock."
}
}
},
"v3lockpbUnlockResponse": {
"type": "object",
"properties": {
"header": {
"$ref": "#/definitions/etcdserverpbResponseHeader"
}
}
}
}
}
================================================
FILE: Documentation/etcd-internals/diagrams/consistent_read_workflow.drawio
================================================
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
================================================
FILE: Documentation/etcd-internals/diagrams/etcd_internal_parts.drawio
================================================
3LxXk+xIlib2a9qMfJg2AAH5CK1FQAMva9Baa/x6Anmruqu6i7vD5YwZjXlvZiA83B3uR5/vOOJvH7o7+TkaS3VIs/ZvEJCef/swf4MgGIXR5+VtuX61QBAO/Gop5ir91Qb+s8Gq7uy3xt+7bVWaLX/quA5Du1bjnxuToe+zZP1TWzTPw/HnbvnQ/vmuY1Rk/9ZgJVH7761ela7lr1YcAf7ZLmRVUf5+ZxD47ZMu+r3zbw1LGaXD8YemD/u3Dz0Pw/rrqjvprH2p9ztdfo3j/m8+/cfC5qxf/zMDKG7j8nsfWwsOP6BgOiU1/QdE/Jpmj9rttx3/ttr1+p0Ew7a2VZ/R/6Aw8LcPlQ/9Sg/tMP/0+Tz/uPe2VDFHaZX987N+6LO3e9W2f+jO4CgKfp72ZZ2HJvuXzmm0lFn62432bF6rhx9KFGetMSzVWg3981k8rOvQ/aED2VbF+8E6jE9r9Nu75FlL9sxNlWvXPu/B39b+m6SB0O/vf9vve8toGX9tNK/Odx3UOFTvLOz+TLb8NsnDzvEd0J3FK/p/j44F/nvVL2vUJ9k767/z5zeWvQvOzj80/cYvPhu6bJ2vp8tvn/4Hhv4mPNfvDTj6d+RX0/FPccR+F7ryD6L4j8boNxUo/jH/P6XkufhNUP4fCM0H+F8Lze/kSa5HdtIfDhxltWbWGCVv+/EQ7c9ciYft6Zgq8T8aoqQp5rdV/yWBv1P+R2Z+18a/EKNHwPIcBwDgv4gLKIz/HUL+zAjwdwP1BzbAxF+w4YP9t7EB/Dc2ePND4qeJLLMofV6VoXj+/h8eqfyf/8ahZ/frnznwbzrzr6rVVWn6DqfmbKnuKP6HxvwoyM/+EOpvCPPOta2Prt6/8+yPKof9F7EFBqG/Y58/sQUk0H9w6g+MAf+SMf9tfIH+1+rxR6r/q7X5S5v4D+8B/GcY9csG/ovR/VA4w/2V6SqSEfr7XmXH/2irZf2vYM0HQ//+Oxl+Zw30F4YLhMG/Y8i/8wb8vfG/njmf/7ztSqM1WtZhzv7XxuvfDNDnQxB5/m/WCv0XtvzuHv8b1APB8b8Df1aPv2AA9FfUx//Bq/96+sP/efpX3U9wRv28kr+ryV966P/NKOEv/EaMYMBfO5l/USfg5+cvmPfbsn/E528f8tdbiBv7xxbTlUvp5gHIfDGQz49mOSXrFM+V+v5hRJoMnld69WQ0ezvQPiV6vvpcYezzRz9J3pUPOH5HVE7Lfl0ThrZPmkOli5wfAbfTcROtL/VtrGKoRItuLatqLZP9Ai1HsVbVVM4wwLILhKfpmXbzlZ1BZFmrt55Oo1hJXFV+TdpxUNrD5qp0z950RrsRhy74fiWlWC5MfPbTb/nSr9ejEBCFI8R99/3DfSrnzlNW7Q7JQuy+/Z+mEv+y5p//NamE2I33pcMPSW+UfzDXMdCiWZgZecOCcEiHVlc5GXr0XDW5jJHkQV+3t5nhgnOm43IiVEIHqYgRrBRsKtfiWAFC3HOTmuiguzhNOJNiJxaxIPZMnm4mR08L/eGLUlfuZ1UXu2jYCjA7OS+UEpC5NW5XuNfKNxIe30kB+qYnEUwdh/PsVgdvmJztQJIlxsgO8i7PovtCPSayDOMiwpaR+eo+HSWpRW/q5uHlnF/BpjIIZvshxve+LxTXY0KO9XE+EQ6bZQhtxIiCNcQP9gyl+KjzK9O260WsGxcTbuYWnzkpMHsJ/nFNyJL8RTTOPkCFQZ0V+2NQdW28RJ6t6jtrBWMzg1Qza7lgWB+m8xjvaUK4wdp8v8QBgoKNTjQRPgTgDlmPCGwaJ4kH0kNohcMXws+E25s+F/k755hV5ordjVwXqRPiuqErc7+hQ2al6qxxT25HCXsUrXlCq8+EyVkim54PT/NkabNZUgMJro9DoQofD5+XkjyQjmmufVWMg1b1o9IzjxamBrVWaoCjHLcycK2Qm32k+JkwKIlGGVihCIeNSlYsGw9Z1bsxYr45brQQRD/55jR6cLpq+CUEPC83UqvlC3/5siwZPsPMncyUm6NBJiR6oaKcrfEs5PmfPPO/ohvk3jJ6tHX5jhmHVOHIrAEN3g3kqR5spr5PdryLTNFh0mK8A4juixOfQk9dxFQ8UgX96w5R+OVQnA/Dc2Pc3mlkJv06BhErPlWAhaEd6rdF3IF2FrhqnsbksOsSdkSVDr/5ISS777nu8sFdDnjJN+xe8sGFD4ud4M8y6bbysUSh/JPCp0MBSVSN9+eW975Hz0ul6/kagtO63jfBRNjirFlhNU2Ap1ST1V+DW9LU3jP9woh7A4zNxJ2XTySoGfP00IP7Khz5NAzxKK+nfk9ZC90CKjx/H5UL9wP+RDclSgQr0IfJdo6OAolMMHvx+gHu+dXuT2rWjyWZTO2ZaH08CCVHdFE5RhxLnB5jM1Hv6Jfc9L3A3xHOYElMKn9NIY7IZMDGb2L5mnP4BEZuyweVuTqUv8w4Gqjiz91spTFD3+g78lWKWyua5xpC3Q9/e4JoYa+u+KS0Hgvge2DskDn/fXuAE10iwMt4DQaY80PcAohytC2W34MrX4cGUZQQyzkG1dr+AaXUMVwQfnfwqRvAsgqKKg/QRmQYW4eCl2lUeud91Q4hLkgTjC0woJotUupWI0HDWsI2eIsPGdC2O/WVt3EI7nIszueyz5/+pA+/lKOZ+Du/GqI2Tg4L8Nv1/qFOoeGSQbwsxlR5i1eiy/F3RBGHVY3Yr7jNo2elOFYTpZw8pKCOGAEFzONenOMr4Y+r5epMCAt8b/BXSiS0jhR3RxNby/s6flomJxVT7Kqb2LdUsBf6EPiAq8MBS0bfnazBD0VCky/zGttfRZ7R7MotmnrJKvXpayelLsj0BoOgo4JK5qPkJrOYFqRfRRY7/fBu6KBntQIb346QeGIWD9ye3b55EdfgzLXG39tevkML8/NOAIYaLLxLBtwlP56zV4RPMjpjXvI3VqwRBNItFQAtPn3qcSVVnOqVEjh9CEcOkKWQr+CHJbqqeKS/NjzZjYpJgJkPvSNfciv7YJ2qv7RaD4elq562kzH7vkTvzzsJgyEnbt+HV+Rmpk8oz2YGd67CEKuwBWj4cI8jd9gta/Jyhzx19nXr+9ubP7CFmgcY5vtlbG0nE9/vtHWvCVsVsCcl+SWDblEidbMKZeTRNzC2bno6IIDG9bpuY7uHY9srVqhI+n4V7k7fCq9qj3gJ1YKm1OKSNx5ukphWzlk78Kh1r9OUOFc2M0yldNo7mFxF9wOFOjRmbaKOQLuR+mubpM51JZWlOtXKq6DJPV/+Xt3CLlnIWhAroPLHe9frN5GoEPOjdZQRYKiQjl/XltLzCXm46NZcFoYDtMcEV6H3Pre0ft6HFGop99v24SufMguLFy/fFLOD7Mfz+37qCAjUX48Tvirls1srBbY1O/krUqpO3nvTw1Qw2tqzBKqu0DoXbZ6wPiHE4JiThcYy5h1+f03ldSM4Rr90CneXf1eM4lnjyzPADdBxWbEHcHHA4aQyc08wlDVCKMzMp8DOFXq8S0wwBX8ztKZEPvFac8Opo92tyg4X4lTv0wapOC+sFmmFL6hzLpb3dOnHdxnC682HDDwLGP4o0s68OmUkDME7rylghKI/0StHbh0x3zCZiPnPncXLvTyf8iXp4FNMnu2IZ69FHx7TXMyXvcS56LFkPlDEXS4tXOlEbOffkHhdqoshFKrX5BxjBsDtwhUYbtlOuVoaTyxOiSkuelpZwhux7KhhEx+Na2e/H2/RkSiiDlerxpr8E36w3HymU9P6Ry/mNwCidTuMJI1+Sa6AHDzZEG5iKBHHiMYLuUxuwRY658122GdU4FfX311hhFSLIhR5bXKB5WsAFiKcu5eWnwmi+eJJUNLH3lJe1A9n9n21wAksWmVMDhfa1FfeWVAMOkLhjOEzq6lbXlTw8WbU9gqQPZnlCjfeqYvzILV8rTe+h1eJqbTxGH3v8rt8VWVzQBbzAOd7ba/13pF8SU/EXp3F4YFXwW2X9O7riWQzsLACpWYfqyuEvKU13d5WFGm0nGuhO3olsH7S1uYlp0yOvMdkSher5OJ3qbu7vjF9ywhfCmjFAh8zclSDAhBHrMnTlelkwQigzxQBrRVexQmuUu5SZ5CjPl85mwxQgfTpGlo5SG+hvGuUIRMrfvIADu90nYQsY2ppa6UF4Y7OOWKhTNIvpSdjYX0il2Y2QURQzQGUZavdEmkk9hpeJ6/pLWp4SU/0rQeM4wSyg5XeV//6cTgNLVpCLy3eYoPuhw12QKfHfLIpXyk1RHSAoiDN8DHRUUaRnUpak4rqD58zel8a24ciNbVDXaXAKz2Zpn4b3hOaMa4XruzGgQ2xWNUy72wdYypiJsK12nMHOEd66FdOJa8XTpOu+whe1fmyvb23VV8BedRsArR91KErHUJ099bAtdxxY+F3SBFNzoU8EQwrxDAuuCpecnw3X2dj1GVfhK5njzzLwAnu2+hzy+FomnFaJEgNHNOVvDWyMO+rvmFN9eWB1mxH9TXY4oGMwLfh3FY/d9LJ2McQ8r0c1TJPJTciWPJ4+eJVtvokNG3s1jr4Aene1xTve0e2al+PqnGWbLq8c/KJuuDyNJmr7J3P3Wj8jZpcB0RXY7qjl7WhfsxFjVOGHJ9vZFUIuPNukM3o+RVM+HF1RKqtHOhH22kFxrvIUT79GjYcrX4Npfshj6jJT1MKYT8RKO3rCZTaKuoQNDO6kusJc3Qo+yF5Sr6bHAexVEWEujpTbPJdNY2byq9tiuBSC0HL1PK2/sJKVcCj1GgHd0jFyxNlFUb6W5f+sZHw9JqEr6r9oqL1pH0cdVjglLQ0jBhqrhJiA2gBI5pt5cB2NcCSOJds4QuVKDHDUl7v6uqG+yZs0ZTAYglq4giU5DI3ZeIqFZAiOk2T4wrIt9BJdO6e68dbJ2vj6PWgT2HXu/sgCufBuckTRxvzHpHe8TrBcbZeAdpaSv8J17jXFlOf46UvwD9OLA/rooPULGMJThE6sNhMq1Nh5g0Ufbbpklw6i0TfETKZgLxj4ULXI3IFPbzpk559MpEQpzYu+jQfc1S1PvaPnPeqNpJh/qCfa75qPRkm/QTr9Zzyo8F78mafD6XsO6iTU7UyEz5ehEopNVFVuXiGKgP5BIyMiK5yK45NB8FXSsKMd5qJM5ufEqnfXI1eqwYerQrKmkKX0MKBrrZgJkq/eSY4ZdvktibRUJ03XzEkX3M987InbgMpqIH0bPv7ceCuQXcgOGxevyx4d5qCICpul+wiI/F3BC37otENp07BFAq6uNOZRMcL4aQKJ8tVPTzqj+NB6IS5yZ5tKqer5kpAgKP/qFI1mce+LEHPJCQYzZD8VcHSWCElwogBqJ1i8MXCFgTn7FiEszKxPzqUfzkERtMlm+o8fNV6w48DJ6sx5sNE2sr43TZ/oAW1lMWmdlKZ7ZbcqOW30mOYy7jQc+woX537K9cdfjxmlrO61H9ViIbnjf4u00AadyDW06MRj2Rf3Zs0Fj+fF2uryZwkf8KS4AHhsUo9JXquj4E3ZWHxKLlSRCSwu8tMwZLvnG/yTuKnjU8eao1kGOXyp/QOi6tACZ4zNn7Gy0uYdUgxJa5AS++AXuVsiPXkYBClRS9TFCvoR1cvTsqDPcQYeoFNKb7u0sHHjfx60cBsZJC4Pi0qSqI1kOabpDIYLGn+K0LCmg/HDfiV/rjg33RVilT9RYJI0nJc3ZQROhDFF4z6LwIMsX8DDPG/BG2Bv8IMgf82xBD5N8SQGtqVof7/V9FAUODvyH+qooH9NxU0/gdg+DxD3o+/TW1NNrTjP+z/+E/UM7K0yH4voQ7zWg7F0Ect+89Wav5V3vuNvP/sowwvEvtD0jpb1+s3mr7U/jMvk23ef8b/ew3wn+XWdx1/ov5fMGQZtjnJ/mcSh/3quEZzka3/k444/tf8fLKzaK32P6/kr9jy21DjFbY/VrY+wN8hjPjHD/YvxUcUgf9OEPA/O/z5Dr/W/duk/8L7f6zy/4VCYv+fhvD/31Ve/vfAe2d5oXr3F3jPmIRWgs+FPPMt8wUp6QuohSNIe9i1S/h9zLfYue2ROi+M1ViFQWAAoNHWCwOtBB2KEkw7AKv7TB3SbEU7PClSpyhzp+km9DVUA11dLun6Y1JGS8RUw0STucN6pOemilZ+MzOBKSHPf/LMVLvfvKB7t6H3PvHmYy9ymOheQpNSL7NFZQVeQzp7PR4dubvg2LjMV9OeKHEgC7IhPUgRoqigWAcRE7zweyL99DmFkmGepo20Rr9N+dxtFxejHGeWKkTxW29es7jq1xigxYUuSqUA0qXd6smfVA6/jzSwxu9kkiX1k0gbR2Lj54HRVw3E6psiJ9xG02p/SFqg8CxO0t/0ZDElUJM6ceqNlvZTt48XYVqKO2FHyUtAilcbhaHInEzEG8+684N7L1wA7qoncCpXrdktaH1nerh+EVqjnhH+IbLz1/q9lrfNnISJUGKtUsOoSgoFbHthO6AbczXXavJQ+i7JQiJ8hrSUxH0g5roB4qoQ5ImQ9sR92Knj0t7rEtw759H3ZoPexAHUCIVwE6xzWyaz2HAiY9P6ajFbxWBDpw4/IRSOifa3EnodFh3hWWfkDPVevDlNDNGwQwX7jLwgC+FT+ZUNs9GPCvTxW8Q2hIwQze+L6gmh993BN2To1DIsxyZTIp07P/Xh5arygiaiBro9u30F2oaQK8SqDBx5S3hsVi9SWHdoQImXcLP0KIVQG0aSb2BVGucgkkhM0mPBeDCT2MzwiV4MPaY8dKa8jd2Nik2pgUFtcauTtu8utPLCMvRI8QCwRxJxO4YwnaDpnlfW0hoK+rgc4U2bRFm7v3oBmuXYBqqi1cAEDFhJSvccBBvL+zrU+QHNsyTrPIF1K5IoQAb8QZoBSe0FyV3fqgdJkSw4xyPbQFTZhpRZkdIXEllJoSrI7SdZc0hFPlzuS8Io06Ak15OefzD+l85Q0t9JkRGpEqcktTDSgh7EQmAtXpbIJyYUxu8SfSnHEnqaNC4Sl8tJLni6yOBGrSIKAihrIumJtVCydF7rCtB98JDZzop4KyiNrJ5M7lXz+yZlaXkX+vGKM2dSqXgRwmdCnBxgoUG5FzPikoxME4bLyVf+qDcDopSEznP65QelZJTIOZQ4ZOG3uwqTM7GuJFvBnJjKYRWHagN8+rZewbfUOpGY46dfMipvkA5AziFIYCX7iMwgjWn0G4j6gcnqFaUk4LG5Yf51Ce6GxL55lfNAyHMqy7pmIL4Kevbk1Uv1u0BsDncs2ptLdYVPIvyE5BNvS2KmPx5/bpydWNvpYYKwfKGgIqoN4xiIbRJmylgVDxC4kE6FO1ClID6kkKjpIfjFufN5Yiow4x9cXxo5Y2U18ZOv4ooPJz+o9hus12+k39HaIxsBi4uSQIrRUWB06LHaIq1HTZVVVTIgnaBso0tVYmonL1Uv7dgJt4VzDiy2lCK2T0yLDjHWHNqqMlULvBxeslEzZFteYnIrGWVxFTXHjkcxayjUuiKbSIoN4ctL7xthC0O8y6+3Tsjxj3Fvpiu0G0sPxbYZIOvFBMR9FCtLb2U1Mj2rcxXO6dJmHlbNQrRGi+x01DrrlY2vMr5+QSSA3bBaprF5nw5NqUmbzbnS+wuFDldl84tJPjk3ImitDUwZ2EKVKFfRY0Zl/pa5ZmgRa2w7cWrRVg9B1pweFwGvwYKKVjKHl+MrY9K61/ixO+TRDm+X3kTRsZE4l33C2ptfBnGB5YhsdGrCKG1w+NJ8sUxmGKaquiRffcGe04k5H49Vy05aRnbacQVlBSi1dlVkE2+V1tZd5gkN3BP0BrmAp1hy8TZUJwZ0S9cy3EDzwKlJ3fhZ6jR1cxY1Ekiig+8GsYukfgpyhvsWlea8RQhb1Cs4simAqObEEWrKgfRzIlsAHkcXKuxumqLPE7/flh754Wh7+xgF7hSv5gT2XWGvWR8l6Yit6QZKny+QGicIbr2D3rcjZbWVmhrwitsKoQL2BYhMAbRtAlHxM70YyXATtgG+0L2fQyep9SRkMd6gKmVfMV7DX0Eo1BYeHN/YM5dHgJzoY0d87UFqlgRCHS9+YMahkon71NZODIjPiLLpNCtmpFl+3fMUxnW4qj4Eenx6ErqSVvfi5bd+a80CTbeJhTQyY90L5ULx/eL+IsQ1X/+Jleyqr/wQWNXrxBQXnz13MX0fHRk39zZ5fd0D6G7P3lAcWp/4ojuiWHWXFDrrWP0p1uqjk5kaoe/P/hdfBy+MxV4Pn0ufJXhLYjn6yoaL61KBSEzv6AOwA+2H9l0aDpPVvEKmyRLzPIa653QKhPie8VI+QNz7NfQJwUFwXDpbcBwM1Uk1r6suinRzo/jSmhjxHm4oAWzbZCR7et+feMFD5kMb7ID3/ThlSYiB/apnXIvNuYc+jDmJ3c+w9GO4vH51sa/SJhH3vxUK3c8p7dULR0gxZry0uvFSQJ6tcvNro+sXjn0NCp5QWyHghRSSrFqoH8qAJfZdscz4A4j6wehwioUkL9Z/B4Zf5gnriJJnutYLSYkv1sS4wFuiqGpVVA/HLbHHBsL4zkQ4iZtYyXPsVzery/DNT8jLTn4O7VQnqVACrm82+IK3Gxhr8/1krH2Gz/CrX9V2uesZTPFxZZmHuK48e/Kqnx5ViZ0yBTN6kG3jzmobhN0ZYqQd1zGUv2ZHqY+1V/hMRV9zJX54PQnc8wknOF7H/b06Tzlh4cE6+3bbH4eCAvZ+GgrLAB1lpa4E4I15BcD4Es8KWkbDPR9+DRo+TkTqhhO2cE9QgB8OPeXZqx4g2ZUzRk6MM8ZNGkV24/EDuFQkwT5BPauO8G3VL6lCOOz6pbPeOgbXfcCyWqE47CRtuX9V6DMQVfxlzT6zpqWrV2dwP7+w30WGw1uxu3iivNdeuxI4ADMeWiD2MUBxPq4L0XfH8tnXxlyiZHUvAgpe3NBiwOOV/yyUkOOnBjJ/kfj6EOy7oxpbvC1G4oCBMKz9AVx2bixcjnJxKMGyfFd7/KeqnSMvHpTVEZprd6Nji7LD25slQPqvpW9y+B1vGk6g7b3v+Gn22PtlT5/AGXM3CHMesXEVNN/eRiBHmWDGarBQUcr/vmF7S1sOIUVoW0feFIbri+euRyPoB4IwyhCE15rLNP6GCUIW5PuLE7Eo0F6lC3IAJyzAiznX97Uzcv3iYqNBpJ7sJq4rQCvSJgZI2r4vd+AcQauDfmLvLer61UsVoa7e6my6ZfDPhjjBo0adF+HPVX+d/iB7szJflTmN20oJ5fNW6N5zCx0PYFN/IcNg573mGHqEDJ33+FgKnGoPq9+oPiTmWNCmKK99Ys2nBVl7K/kofTrCfb/ghPEB3toGrjRmInC4s58tHEcu9rrXrTxfDqSoO7oZOu9jtvuYhhPz4yhjocw2HXy8GJctCnEiVUyEzdbbj+VUsjW245+InYidMDXcXNuhdFvS7V7Xj7Oh74mYBCbAC0U/2AeqP93+8LzQsG1/rQT1iw4f5U028u21AahhND8LNcbyXPTRPJc8OLHeQF6WOrvpEF7PYfBrO7UDCxjpiNOfKI+w7gaK1JJYGcUglF+zM/I7KXLjO1+yAWpCIK8AmFT3kAT4Fwir+2nhPTW/xxhAo7sxyacm4hccTj0dUEi6xL4EnxgJeCsu0A666Ct0Hx1tP1j4RHkvT5MRQ39uJBCvcWzru8rREf4YLILwj8y+BQoFd3MmfufIc4Mg+gIN/TE6KPkKm4TiFKXYmIL8tF/my4YdvQwpKdYK0sWTFwivAytyld1Lw2DSzw0PGql4Yld2BFkHtKNH9E0zYtPE3s1VbM12xxi58J38ovCCGWjNFvkbqTMH9soLkGK/dvk4osN+bgO/ze+pCQSPKvkliJ3jSrE67GvPl3Z84lySpLzQLaWwATWS/snXRZLkhehLsj8Y6k+Xn/b3XJ1VFqTwXrFvPs9axZP4/rOfsxXk8Yf3kvPr0tUYy2neiezgI7Whh9wRTwAQE5zR2y8NR5crAY8FVb3T9thCypAnqtBC4Nj/7dbMCet9uSY82KY8W2Q8uMS9imYMUAWeuQedg77vY88FAguvRKFAnz5Hyi+E2JaaQ1NM/NxbZIJNpeFDrGFZpMnCEKQmrEfLZIN/zJl0ZmdY0pAK5qFX+J5+0kfvklvpiCu88CeXbhDlJi/lFq/HFzdhBd6PwwECv1if8fXvc/9hfjb0tTrp2mc97R5X1BXyARp40p76X0J8QtLf+//++/taQr8cwxqoIsEEEmbYFQh51sE1MSS1yg+tiDbj20652WdfeGUUwx/nwX+NH7+RX7Yx17KRB7Zxr40x9Jos1f4iBv1rBr2lxox3F88xx4eKWwI9K/3DTn52w7tw5AWow7dw6J2BybfrDzc9YjNq9VbtY//zHct/uSNd/WGfzPnbnd079CUmhsB3NsBoX1q576gh/nxxBfp1V9fX2qR59vwz4/ePszHH/nJNb8w2gbQr8ingZ02/qIErn39w+R1VH3vGjI9sUWvom6VRTHLYkeEjn49M/zos+uR7KZWQrYn4e3TdUsiLdnrlG9ZYJKyjx6bfdDDvIvyr3E+Vnl8euUy2CX0pygRT3EJbQSVctM0aoaZ+OKDn/V9Fuc8bGFJ5tntY/JppA9md9wSTv2KjZL857OxM2TyhXW/kb71fcGFr5LloeYS4O9mPTcTm66vo4fts9VrFb2Wd3a7o1nRxbYfmbrUVo9rd8Gpree6SiBgIPf+utbMO3zaNaTpyoZtHubYRBpqsJWHmpVazJ3EH9HEyqgnA94jU6EyRJl2XYfOYkJCNG1YxQe9ROAeRMunLF8XId+soU24st9qjAmFF+kI3ZU3cvnEl5ijMFa22QvAZz4KH5qPEKBqkPc6UREqiVQAN08Noly4GJlRt2KS2HRCC1BIfIBnHACLuBY1I+TZxyXYtYQm75Uht/T32QUVJQzQ2B9ZeTJeBZdYnTV1nmbPJAn9lQ4tHu2XU8OoKVxjnINkYsuv1xdtf7028qWGnf3l/vIRNwOtERnyeX3VDPr/hcC4+i5UR9kXCM3Iun6tRcq/G69HF0zH5Z/9ruO5HeF/lrvjsMYA3zfIY1EWP4M/UYCG++0le0eCpAOghut2Zmpc+UBlEZln8hFLRZBq1vEuSRpuNouUaIXOiGPFn2Sa1eo4SaBoruzuhg83v+TubjECzuNgdbuS1G7aI5lLNSM1mfIzJ1TLfTpjZ5vgJ58ero6svfptJHAJ0pMkW555VeEwKqpVuKIbgk/gbrh6PoGYpOyV/aV0/mSdWVrfiOt3rcVQfwY1ElbBWQAz977Uvyas+1EaOO99iRfglwjv4JKY4Qswx1ZOavWADVwvmDSB2ikD4NvpKp44DLD+Sh7RLxYH38HioXvdOHIHMTvxu/ZNc8iagWjc9nS6qxcrWRcFhy4KkTCBkmuUuYqPKqRCgTFZUt0j9ppVNfVWvt6OLRG2/BOvU4tmpupfYngGAw5vW3C7BQB8BAloZcMZW4T5BkiDLpg7szZyOat+G8/Fzl0ZGTHYolwOdJypsubjk/XpSRt6hoIWez7QDt2/NxJ3iiFlt09M3/T5ZH+qCXsaVfEckH/MNhL+e7Ahaj2NUcQTFNfLy3EwYIUcW6ZYy4p220usObuPwy6Hos6wgaJJfMaxX9Ot+S+xqWQdgPfPd30mFrhbCE6Bb984jLxwTnfA0U1PZmaIaFkgPx53vBJXXFIm2E7E8FiL7rcZh0p+4AXBC14KPfTknPnRQLqfmXN3ca6wrXwBGM/b2aYBBJ0veYN+qUBUewFNCjfCaGbo+ASmvTf9UHDZWustzEu+3NeAAn6/f+0nds96O5QAoCAq3PkihFf4kDNKnM/dyKRBmma94KXMbUZwervpR4lFH+MaLYPl4M5KmT1SFROgu1wIuU23uEth9FGkt1RvfzVmiaMGCWsdrrnfAN5+oRd+jrFDlmNEX2pUzKek9u9nKH9SbUwp1Ej40OojH42yyuU+iKreCVi6T2q6+58U0n8AEOFdMlg392E21PKh5GAQS1lTVlgAFoWXizs83oguOtlfZo6J7plO2BV9/6vSh+ZkpWzE0W3ysXtPmnhxFyDffQxwXZKSmQ/kkhCkprdRqkS9x1ipQp5ov+Q0wraPu5ueOKWj7WHmZFdUejc4AF8pmpswb3KvmSYRb0NWhYgW0NtVSYeJlX20i1LnbkU7VIjLoH2ylP+EASY5Q4DkOaTD+Q9SKVE4WOz6J853x8p7ySghfkvAN2+rjq5/L/7ryRiasxbgDU5qlj4EOlPSRdE627u4FyX8/ookMhEivqkQ1alxkXHex2DynfH1TA8pd5VH0qYx6p6nO9kgKOk+NRH4QmKDK8NW3ifwi8Jr5oifoeLPhC0YBCZB1rvrkdp/mdMgPycSVoeNdsGqEcIYtZRNflJbfwgLAiFwRMgerSqpe6pV+t7Yzjr7t8aH1PbkBU0C0ve1DUxkLHzilqxluxjj7nLfBuTxwNDnXVo6tAYf3uNvoUnRQyBwXwUByHnKuafB35bW41df3c1avDQbiZuqCp+FeyC4TEZ35jJ7X6dLCaTGIrh8opxT+HA7O9Iav+9ld9AmQvJrqhcBouiqItxWdL8gLVJP0FHhpPUT9wT6gqXMQy50aO/Bs2feUsMAhjspgukFRXU5oEBiiYYYh4NhXrCk1M3TGyiOwJnblEJhjtWkrMNDfkGGILzqlXZhSjLdQ+jbdardGo4n0g7Cp0RdtJyJQOn16LJOvTKlzaXYUqRtxe7Ck7FPabie3E4llgE7dC/gd+6dzZvxo9U28zTOobO2ID5FG1h/mBbnsUCk4J7y9NIgSBO7M53rK5zUGG8oo+cLG2wCkrYvZfnKglVJcI74HMg1eIRDwUnQj3bSEJ4TgbtpXhikuuEggey7xGLiBGnpkshXUuydFDeOFvlELTESKv2PU27xHXkpI8rppr5xKXT+ZYIU2WJalSagvcoTJIg/WJe9eoPWmq2NNNUf+LQu9pdlCLcTvQtZo112UuFG57mUS6ZRvPlTFNe1KTLB/RZRapouHvalOzmUqCXVCV4GeX7y8YPhEOj6ZNMNXAhRzzKd9HYZ7DX/dvYcfd6GnHJ/zrnAIhf0xWnqSC3Hj3UyoHqumTiOdDYrw9aVREbRkgo8+y8/xJdRIdD0KOIT4CglpIdo5LaT9/Q41BIqHW3dF0uVVSvJ4b3al/cHVz8oUVQ/egCk+sszHlllK4nvsdaJ4FfCWgnd5jX1dihpJY7qom2nzhF2relJ81EfiGNMx9zb2SJmmgegkPNB6D8ZP5iVXaBgFSG+jgGGAxqriuPXGRZCkgEDP3u61OxTpfJA1syrDbzNo0c6P+o0wh8DaD/2iZ/bqfVK1yuRBUgt6aiYPccpIfgQqnhWPYwFuOQhDXNei6hLVMJnHe8j2a0/vC/MHAYNSNXyhZG5eFNsqIReyZUSV8MFpnuA01ntV2OrGk3VxzhH26LZ1I4uKMJCXU+PNHB9O5GZSFDiT3B+Pctpn82a4sItSETIZIV1eMba8Qd+tmf6TUa4mo1wIlbQ9nyqf2ZyjyvYTdjnseLYog+6/PHhZPIy+iIPBTNdKM9kO4MRqrUnrpDYS6QkVyC5oy64vcDq6zZ0Xgs3ALJfzJFKEjbLJGR4W8yIPXiXNx2SDK5kecyDaMgW0rM1lvjUjD1kwpb1g6QeNeI3gT2AvNzvkfL93TZcLmZ7DnJbNgPB1kXjfVCGphPbwb0nIvSkQuILn2EcGZnVRZ64Boq8tDsXX3Vb4NqaIoZILKm1+0NcRGeDoZX1KL0+61Xr9Juu3OZuT7rqgw8h5cDlSIGq0ilU/GN6KcPcdWiAfBP41cVJGDtC3Y180sqarQ6bMltUQnSb2VI0gD07DF+s5/LY6M0EKVt8cu4/vGgdcQHKJjZz79VquIWYZlH1U/lzySR1SdkLDuI6qaZA6kw0U4Jp1KUraqyI2hKM1uChcH2e6YPNxO4uEatXlJ3+2NAw7x8aQXWSW5bJmeFdctip0VkAZlzhLbqGlNuNLar3qMUHV6n5yl1VxdFpeACyw7TS5v6qrElyiIfO2PEmKvEQUdD9S0wuf8OsZc3OUVCNLSZJ1VLAaHB8knSXFct11a9kcDtc86aJwBshNEW7DaIMZh5XuKdbE2C9MhyDT6DMV4/cSXq93R4hrA+UEu0XUPmirHWweVJqT6A7OUU/TMqtB83iOtbkBCcsCfFnnDHZ6QlEhuFDD+clRM9PXkI+bC86NWMowgD1kLbdDKcD3iDVK5jMGZcumEQNmZHFCvOaLo7d8r6kgFWwbyFNnPS0mcXKDr/JFHxALV34eUnzBV7+lLQIVeeGSFp4WYbMkXeZiScnxXG/K6oR8S3CR6Td5Es0207zyIPivrINfD4tI4z1GP6/RVoBKHHDy5Te3XsSJ2XJlX5UH6rmuth2Vw7vrIBPgBKBVN6gG8wRmNd9l+ucyaOnoY3FpQzcSdkCd4tYFa94U4XRQpF0ySTd411vxAPotj7grfeFJqD1nYBC5zNUJf2twoh81r+JvIxZgcaxDnXBWMK5NIHFlYQ2ilMrzJB9+DOfOCN89SkfhBpvDa5jo1MFd5gRGwmWLsXxs0JWwtF7VKLcXI2CyoDnR19dcxtMXT0CfIt1tvsrqpLnj1DKlO3U9voGqkqEG4M7t1iFhRyXHx6xlR62P3OUDUhshF1yGhIM0dLihErob3tmucX6460mO41J1PA+5MV6SO5HOudQ0YtO4xZr3ZcZOH3H7nDKf7hsABl84vPBUV25jZ7/gBqdc95s3foOGOH0xwOu86+kFGQxYGDtGRt/YOkncCMbEILYWTXlUfDItFlA0ISk4sfQADuTA/PTn19HmsS63XBspVW0D0Qq8CGTfc1VtmPr3ibmfjKWeBrHkGCDB+Cd9VX6ynV//0So1Un4QuzBp8PAdihlQY/+Uj9CvgISBK4MDPuE9y2q0O3ivm4NmOhyUjP4wO0iORiMIvVPzh9IfGxMYBrOVsP0E7DLWcpWsZebgn9H3lzFt38PADkKW/bdMNq98VCqE7ZpYmycQnyFoD8hLmLZhcgqU9yfMr2QC1237APNfUZa7d2i7JHiGSx//rZVR3+RNLSWucPUFZpOYlkFJcc719sRog2TQTdToPI/w52hCLiBxkgjaQriuNVU/eesShRxoXJamXst4WnUWmMVu8Dwm5MZH9SjUHkt/iuNh8z+C0mwLliDB+dYSv/vdGy41Zq+P+NYsItsaN70kilvE941v2BmslMVBy2Uub3Ceq3xKH4HDBYM6rVI08HuP/GPWdvYIXGFLDxVx1cAqgLcs0nt5nqhbGbHLB2JXVUjfY+GZkfen/bLuwPtu5QE6e5KCajaAUV9DuLKecOcetAMQmppqx4HkEBCxaQA2oe16cXVzmmA3vG8YsDXdOcabIArQmKw1SzVDc8wJMbWd8pLeCvjP66LrUvF3WsN5yfrMmQmxM79a46W6uPx83AgpT3anIG8Ea22OQXIChHvlEjHXQZNwL8RrvYw9pFa6DHTH9JYN3prLt3oRNhHhODd8q5uDfsOgjvZrsL0QWz4S7cHR50zlr8/hgSHO0B7zAOp+H/GtZadCUDlB7M7J5pGAhqt9xSP7eVTrwMHZHJCvHfixxMe1g7RRV76il+ecAd9935cF0E6QYxkCI3knyPbzxCRDa5lW4KfjJLxwXjuCoDz66jqu9Q0qx7UGsvaF+SW+CcW9W3hV3sfL8IahiI/vAz/P6Eqq8OlmhYGOfBNOxO7RcN3q3nWsyKFvCK2twd/Whn43aUp4ZGH6DmJUKvl5x/Y6vkhAMWSc/UgPHdMm8MZU3konAJOl1lY0MWdTCM7QkQ5vr/gqvEtbM7vTUWS7q2yhABqBYeAn2JMPNgiIXvzw/RQHc62Ijyta/IFKbUDA68NkjfY4k66P+2JuXNJDn2CEfSVPcm8v53vN/ZYNzLbYSbgyjkts9JZQ1eFMSDC0ik9uBCXg0rKVaq9FsUTtcLNctw0cBxGsRD6etyxqd7byE0h4rBRMGHCEy1sMxRIfg1Hd80ufOfJOQE4345jXfWCfw35zQU/ZnhDgCNX3+6G487hkfwD6CHS6131gdkF5fDNdIvRVh51uWkz9DJTZ01N9A6CcjZATIDiSStf5ChBnBaI5Il+oCp80If0esFpfc9/cear2FgP5sNimLHJqu2VRDs6LgTnGfMQRonnDMXkOL2tbI4KYGK+FyDBYxgMg55RD5i0KO42LUxxYc1IwHl1piuudxqIBHnsUx2YEffzrXgyn4+FMA0Hgc3PgXMcMj2ltyWUtim4NxgU7ABuOhOYo8+zBexL9tyQVM8U97zQR56lQPrbLSV/Y2GB7W4ky7kSjmWU7JqO6HDC7yHmRbADL2bAyJRIkojmn+EaOFSscodN4TaD781glqXmIiXUeo/s8P8Ab2Bq7ZIuXa0QNUnL9NZaEP4TX+Yl4pzlgbIgROND8J2qZCEwsyAwEANO5OhIPZja8+S8VcgkULuzN77RzTbCiBwoEgseQ5ys4z3Kayg2BnPM4l9fKIne2b9W0TATij6s09JF3TUBxuqv3xsNkbvRmlm1621kObR1PHMrr0bY5cZDv6lryaDOsj0EkgyBTmaoWsyN7GLkIjAkQwVd4bQQMMJewk+Fs8NiCF9E3hJDbhrV2rgpolAozXaAmNDPKHjOyxAwFbBcfZPNWmRUgD2QbpcIeEQfKk0skrY8uEhM/85laN1Fex3MOQzX9UIvguBoQe+5h8CjouoG0obsmzHc8d/weQoC/rnL0GFwhCL59/xnQW3nruUTPlFqacTT6fiGBtJ7XCr6slR15BfKvKiTY8K3X2JGY3BERqPP2iR+7c1RQ8MxNrR6tkpN0b1oSGrZyF+VD6X3InBhvDJS/UDP9PFenI63w2oka996j6gqgI9NggZUYC809sBJErBAZPdF1JE4XhsFPVJw6AgznroWtVJoWaJ4TeVxUZ9nli7ASR7L3cUp4HGFkVijZgqVH1DVBdRS4fvuqbr7guFJrBLEI/RtSUKBKoz5NFSt71ET5nmDgmDHF0PWNCrBSirUGPXqJez1nafc9kpGY/+nHeTMESMelWq2+j4Q35McsFW0Iis+FxPHpvccWiMXIZqc2bB3IuTMDDt0wqHljdeDzEbh+01HPgwFhFNoDymsCklACAq11lR7TZLjNZ17aF+oy+zjmkEz7eZId9hmCOOpYUbKtAdpbItFellO4qoC30OyLIv14kLG9UB10+Y5/nzHdMuVYO9834QxGHadMBHicUOJ46yKkZOMz34MLu+4xktLa6G8OCr0OyU9LVaj8WRZa8kBoNw2t+dJKItoT4ihQycS7L1jo1SsZSPxQ3zF6BkG8J8nzCavcg9F4lOCGBaoVM6S7b5XxC9BZwjtjeXgaBvQtEfQ0rB6PszQRIdi4G0b3vO3fKTdeKEHHLd8zdJSYf6Q5zt5SCPZmjXlb/hy78hs2aYukqrTXKw7BzeyGXif4UtCM7HSyUTkxnn2wi4zanyfG5Vwvsu199JbLtTy2v5wmfsGv+B5PO+biIrSZzPNMkRLd1TSkv27ske3doCdly6mBzDoNFY3NpAYDCc+vymBTrniaOKTEOFn34zYktQetdKMOgHcAJuKDGhN6hL2IdmP4NM8aD0aVeT5KcS0tFQAQOFKdTTf17spq84u8cMSgAANgD0ggorGly7E0znnVvOcwXAGl39ypdQWhhHVQ68fiRbYHLs/GwtlLQtjXsnzdVSbUw5LkjICjMfdBCPDJJG06z/D8ixshYRvB+7wVFx9T5iBPgjZmCHytxFuWGYBP23zesw2FTMdGGwkPSWvmKGWPOl3Vv8y64HJwsqGIiZywlyUar8X3EFSu4CiuCgq2ADNFMT3xjY3v9TgB32KGWMC7q6mzF9Cp7USc7s/lcWXqDxgY0sOvIwlc+3O+E63gUKFIAITpBTt6bPCunEmzxaTg+G4tXHOZGw7qdYSbmxLK8UL6++N35t5F2ufhVZ7PuA+pqAtH9UUfmpbVuoHCq1bJyWgzN8SmC4m+aqocKgjPP4dt5Ka5Wxs7xlZ9tHg2TZ2P8zwoJi68AMc8vcpH1fsD80nguyDdkTSBSS/y4LWz8sTlmwD9fHmPYkPz8KT05RmeP8+LBEK1cUwCFxqvDKJRnLl/Lbrf38PXcZNYf8hhr3emrevaNXD2nsQgDsz4QM76SuaTqIe1hGufi3uDAwaYlJByw1TDt+yGCqj8qhxTFS/H9mVhDfjHbGcCgaHFlnG3nSeHQKvMx4rSqPA6SplQQUBcyQW3kOL5Q+UZeIccboU/g4B1iPHzTRphSTWo+d2svNMMVgCWCJfTIhH4iw1RXF8wpLAuHmu7bYPVh2KqUQHazn9trYJjTp/VBNWJVlbSXL2xJ0MmpM6dC/7XMZPusULzYSP7+/U2XFj3J6fVEQUL+E4JX95TCePLVoGzxhi83pJygrJbNDUSkFTGdvTE0GGLvpJSn/DQrFrRKyCIf2Z56Is3jpB/viij1UP4sffFIA9K8TXyJ42wiXsQ44O4wgkq5G/PXl0qMD0f3p2SpTnwFr85YkZYrxMjSj02wxIBvc0uSKhQ6LENCWGFQb7SsUmNljMiYYsxy/0+rpFfgIobhl0vhKL54tFzm+weZuElIQ9+6yZzmX221deCcfD7XNDjqMVU9adW15zpPXU6TY0fSCefPlkQa5TUlRNqeLyJuSmLJYKeTMt9ho8QRRqEwC6w5z4Lh284Fmz2263uVTdugpj++htV4FxMqaqyN3a+mm1KoMjdXiOQRr9S1rc2ACeKxteujwY7Cn0ZLUrDewqMxPKx+OwahVHDsKKFal9xAwqCnWHWePsG21JC5FvTZ4LwBx577fTR2ofG0AxhZuNj0OAjji/n47PBWXft+1RJmucf7PFdvKAReIL8XzRd1dKsSLN9mv8el8sGGm3cucPdnac/1LfnTMTESOy2InPlWmm1jY71QZ3LEXMdph9yu04UDoKqO3RjKzBYOk4hn5/pwxskAHjQIBficQi3cw6GNr55YoyWE+EfigEJc00xCmR6ctjVxs8IN4KfcsNXJVjuC6L/j3GWQkgrjjjyiIm5fycwzwfnvSECwCtXjsB3tD3lVa6wf9yDhpzuxSkco03pP4GftkGAw9dI6CTR8TWl1k+m7m2kfn4M9n3s3ud10GeR5aJ5Dpkvt376A8rlXyPjnxJ8z1ydeR4t2t4ajLH9nQRzuzMMvyS8r1LXT0Q3NMvMgHkBbY4gRpmFTqnHXXp+tc3ypij2PODq44VI7/FUCC/XzBffiZWMPxgD+AEJUsFw6nnRKm968LMnuZarQRqj9X5osMzoUNJ2WZJPEGN3lcu3c+n1IG3RFmPi25hTII0iMR/TKIFyddUOfQjF6L+pl3+92a9oH9oOO1cITN9w+Dn24Lg8xv58rspNhZ59oy+PYh3BJy3BQjNtzR/AAmVp9OqsyuSKEl/VMG4n+hqDIv7XpPpQSkEuVN3OhmSjRhZZn78GjjPPO2iJY/MM7eACcoEKxRAUFYzlJ9NIvaSkqesoAGCxBwTm5xtFbVHEdBSr7UKzKf5cFj5KU6SfzA5uG57gr1+K0pdsPiS2NPDgHM1JwJQzLOhL8iJBZGCf958y5SviE67e5/NKAxU0Oievwj5+a55zH2qSMtJRqPNJWDeb05uTt2G+kXWnXzddnOJVC9uJkwbSJCelNr6qbAh0IdmMliI5nvEJhNpAAQvH6idVnjGp9jtPES0qp23KrEi10MJuAmNC0UDMO69VgkEqdmtyql5dLZtbWCZPC34J6WMwWEeFClJTbeg5xbLDWtCETscuj7207Dz8YUlCuNIT7SMq0ms2FwrajZwhVXYBI7a5OVT5Md2dP2Sjxr5rm7sFWzP1viv+4mDko4nxkkHH4bwqaZF8t5waRwbO+TWPl09MpSA2BMqX8Fd0BWEbV0NkC0o2XRzWSZJRcgCWQp+GyHgfskn8VXRHfVaqi9IFpNI5xK/6mJpNFCgFOMsFnb5Mr1QLe79p7nn5KmA4vP65+u57kdQvOQMMnruKKB8r2fMPBDGTln3WU611/OmdCUpIE/owOOCQmYYe4DP1XXfsrUQ/dx0pBPkBjdjMVRGOo5WhzuEe/7XijwVeVooALiSM/pqwxKkgi60/fMfd8MLCiV+XQQvzPdNY2s9shgj8QNH0BWTJyOhfIybzI08qBuHc8C7yRiANaSETZLUc40duGkY9IdLiqJAbyPE86e+voUvdwkLVtOZziNbQafxHCGYh6T7qnxg9j0NP4lTFu7+29gpN0eqb+JfTC/jwUahHzfNC+71HCYIueEDjsu6eINATZ+vRs9Ed9MX25tc3EmWHfxttWBGDUNGo83UvllfXgRL9Syqe8uEwB+laFoljxGOzzP9i1yQpl84Om3zVp9sPVQ8IEE+fp3nOSCJ/kbv7UHJZHQSWoighV6+ePZA48UJOqe7paaNGgV65ADqlwWYjS9+/mTHF/qNJN/yFkaAM9mMpp+hIRIOZcv7izDb/XQa5Y9UatnAFFZULrCSXEpufc8M3xe1HPd6/Q1euScHS2EnO5y/eklA7sLywj4JaWarpcozSKC4HX3SQNGQg9i6kWYIpoAsZMZJ0As8QcZyyj2bT8xzGP2EQEu0+H4jqf2w+aF3hi+kMTHLy6+ZLkGd9mGXbdmJkkvTdEqjtdPfY03lCeyZlsAbFG7Q1LHkZGOw2r/gENif21G1sIfgdSM1BLwn0O/jint51twIpgzFCGmnQBRQkmwsy67oFIj58SbWcKDSF86G5YQl4vv+iUEJwBpvYrA7Fx7Xu+nQYx1Fk2L+gz6BwBnzg1js3rrdDpb4Y6Y0XYdA7mdOIM2Iw5s6tjAs+pS3G9ws0pPCGPf9xmAkrRLvUQEuy4+kAflUI4fCaLb9Wn939a9hokChxNKNGraabvu6Y92oVWzDSZ12sYfaoIiEEHUSCv21T3NeNKlUSF2syEEysnj7MK6NExFPxiM9BhPBEbJM7oEQhs4jRpiXWVz7vatGGcdyf1T/YfNL0BY+Y5DmsaxRO8gzM9ps9opz+YHU39p2S2bzajbyH6nC+JFW9yTlxrOl7AAunEK9qKmLXRYM0iK3GTI6bPyv2fDrkA/J80fkylsKzsPvBSeQy2cfPq+5l7upkB0Yhn76Ye14MNT37zR8tIUmMzosAwSxo7WaBMk2o46yui3TxUY9hD3JJNUrHuQOx2TJvFPPolcZ10nVrwBAG++otCHCG2LD6m7cLXV8ZBLaRMQA5E4ZwOzT5GUUuAMwqiu8X5GqwvzzFJbaAQx27na9bMafrT/Rx6nsIIPfknbYJmm2YGuS2gyRWi3+Rlxe7SvXUVxih7UZ/wzqxRb+eAbfE5SyQt7X7EcwwAjMiZJZbnOsSx89P3PKxJfHQJutxw8BHPHTTc58zThSHVgs9/G0J4ZvPSVhZ8d1x3DXkLCeQc5gSVg+giSuJWDdqtiNnbqpBBUHRCCa2jy2YwZTGeh0KoCAgqINnVb5nZatsw0iQjIn46XXUD9hTSsly34Gkqp009zUd7saE0fuwov6c7e25blNraU3s8nbmMipZ6PeM1KyK24Q4PJAPOsbKXA5LOTCrGbCDNHzoFX+p0WvseR8vEm8SId7Z3AhQ1T8Nl2YRxwWnwNVh+grxeWOkz2E/eMFM0rACzvlS9roOX3Wru7grlmTPcDpy+WNyz88OQ3zPqrcqM38LU2Y5NfzVBj/l9INKa+9eVSWl0LI6nr8dHfF6hQX2A0K9bVW1Bqocka03SDljvqQVdOqIjkMT0Q9KBIgG5RMYK8iHNhw3cmEiTj85+X5s1+gQ+in+8i/4bIOJY5LeUildpE+0L9xsfweJ467yafEQyvkenHqYYbxX5selIAc3i3rIK/7GuXDxunvOaUaCSmHw0rZv8x1PVlInDw71Jr32j8Bv164seCC4JdhuyRBzkjRRExduWJoEMBnUSHs4gEDLmt5/bI2s5iWIW5Eg6eBCbSS7yqYufSWdB0eM/cLSsCPGwZ4GxrqPDY0Nt91U7fuea4vQeEsEHKjp/h6MwbLcwHUJ/zePzqskunEklO+rm7qEmQnLSR0KjQ57UpaFRMERvLMF4NGoFnq2vgdJESFH1aHNMPkmI+JtgCJ5F2Fuog25MvX1/RQKcFUxmS9G+DbNVEwspuJOiLBbJIXO5/2PoTVQgRZScJTfkJd8a5psO25RWNmZHKyP4juKT2/xbIfdWw87eHBeA2kutA+qrZ/DywReyvUEm1xzhX1BTE+V37n3QcAkOmyIBQ9j4VKxOUG15cKzR63wqWMm7e3vO3v+AQUhWrpSVwyXOYIq7jjf+UK7xe5fv670wm+RK7LwPvk3HuvCDQ13X++Gn2o5sfdPz7F+uOyzIK6zlWVhTx+aof455dpPTe98EM8q8follQtrTaMhKv6VqLJmiQcn05jGC9Q1FMkASiUoTdgQZMGZuD8sukp6/4NIE/6ABRHM82H3kUmFv3oXMcFH4xLSh9ELBmW46F9wD86CXGkUH09EzxNGG7ZPr1MOduiSRbrXwJSgrKgmMA6RRdAvwCROgiL7XDeFFt1e9EodOIhDcWfkPd1Klq1gzpfaa/rFbmx1ah8Zc52F4HDWray2FyB1ERfy5Hx1PTdxjMxi3k9/6XXkJrcPjNv9JM+EFqk4kf5np8reOLT9tlejll/Hmki2ObVJEdMMwAONOQncHXYIimk9TWduDpvP7E1H4oq4Z4Tw37KpBccSHuepQPjBs/jNP4HdaP61SHpElQx32XH1d6LHN+jZIyPgI34fg5GNjGCCbAMI+NBpdSbh2itWhvc4xl6SSCE3CvKThzMUTF6qnbApGO+brP15da9YzmNhACvuDVBSFkXwhSPGmnyRdR4FUodcBucLWU3rdqL7woMHNaISPqz2n7QGtCx5XdLV5lyUysTeMNC7tAKiWah6/qwa+Wmk6g1Okfse90zoI0uPUdvocJqCTG3g4V6Nn4GVYo9SB1hyDGh9U1FzY9e37qDIVClZrL8HROn63kKUcT/ua7Rx0KP193QYatkBoP5mnvaSVUFCPS1HQWjW5W4lC66XbTL2epUwSu9vOv6WGEYTynm1Mr0iuXRapsff/nacBabwG6ECMfoE3+i5o2Goqo6oj8elnfx7PYJbceRSS75IY0dJmTWpJ/PWxV8b10Tz+BsAlzHH+S0EpCkfnOMyFphkt/E4dkMPR3CxVr8UNuQ8DFmYIOuMIaVRzASztUwvOREBSfEenhR1fj9GXJfMuZiCcsJharJU9qKfJw4fwBGPI3gK47u8ZPxp9aSpyTFATMP4iKLOBRdGkg/1WVbUFh9etR+Sex2Pwp89lmakjCcdz2+MVH0wbmSynIxpOAkA1Gj5AtsUVMSVXdaNZfWSfq/EPTUq8UrQhYdMqC8PqdLP8va0jn6mpWoMSGeZVUYPmVWCtdtqkPI7WznKOcRa7xdaCdS4mzUdCuT3aqoJ/IILlbFXfHcl6ZqKEkuR7VW/DrCcqPxeXMJK5i0KiGTmCeIsBQighAteSJ25yDMi+j6CZkwfHVgc3NmcfXIDjPb9dBksl+oY3H/z1twbjgeJUyhEjlP427r9/bUWOgzLr/DOuIJ/NFnlBOpgIvhzD8hfQoyAes1kaB9DjUHV2Av8ffmx1e/39U5vrH1c8/LpxK5rMNT/NqHoEer5dIp9iB0khIgAu8ZDPZzwN+a/yD/RE0si7kOx33BU8VIfBob3CQ7e+YVpXOGjAACnYfylO3iY4Fh6BPFT94cWfzo/1vyjQNyghicdhU+9WYCWuLFuJtbuLkP3N+L9JtbcqocaIrk6Ri+/7H0+n5neb+KDWmnouhjaLkhSfiKWK3XuDVGGHO1We55r8Cc3v8xwf3R7mHC4ahwJFb4crMuxBQlRQBgQ7AuBmBuyuhvFT0ZNMaT8Je8LaaXCEi+mNrWVUKbd0PmCFC3t9aL01FQfGBK57DGWqiW+fjXcHm5F7J3amKaWAqOKBqhLFjVuXixp8X+hFnayeQOe3RSV8g9rGDkHz1/99TKO6qqr99Ms+88WtSJbxsusQv2akbT2UlFUihwpadfJ7m3D2gQVVP4yynjpEp3nEIIRCy+C8/bidHgfXA2wzz1UAmQJ/ReCKd1Pnx/dM8dLwa9t2C3BO2Fl2G+Q7/+mRoKH0/GpCdKqONlrvz/SPpWLI0z2WxgQdJV4OaKCO7l25PtJN6DopxXkz+7QvI9+rgVam6Xf00ODuVt7pTpxaveHiFKEcJYkooJQHoWoCs6y5yoh+6q67sJ5Oq5I4pTHD/2OH5ljYvIvLd39SA+pmOcuGYGl8aJYWYaiTDCIonUDs+JPsLzelZBB0GfptYjbWcVkMQxgyTCjwlFxL3g3pMhJzIf3lA94BLvMf1BdWyn3gstaNS04Tl1ussKm21d4Dv3Xfr3eKFLpr/fuMdvNyPWSM+k429wF665XfLL0M4bPwC4QvOo7VSNwMFp2qUDuTnGWgTquEpUgE+zOTzz8dCq5mmoajgtvXkifua0tJyyFx57B3zcmW+7bMCe9WyCbyF/6JJTRGUfxvTaKCYeQ21xDXNO/mXgZDZx5+6qA7LWTRogPdI3Yf7/aK2Ku486f9xvpRmUR8sid5z8Xnr0fmcM9Eo0R4zgmN1LRNNjRC6YoHhd/VRvpeb/LJzmho0lixktxtB4o17tJtGSTvc5sVSfqXH0iNSwkYzgpCPDfmcOG6tiL4hBq8TDmRf/dSFrez+A1+qT3V1NjVy9trfrir3lRFKMHZZgS8fco1587Ff81zmXkzMOdAJRSV+YhId2tyAWRi0bZ9kAB7owR4tkTRc5iFv6TVAz2fUEQoZ4fdwqsm+K32pRDzFRpS/VCqdm/tD5flP10zCz9fD7cg79JeG95bqv9xNpXR5Su+rjucStnL6NgX2R3W5PtKscEFkUBfYmgMJUYOV29zwA3YmfeDZ27wPITnpK5565+GD/5R817B7spR601kxJFcgb9EkGfnS0nniXp8kJI17+dQzd4pSM3u2sKHHBOJ4vk3RQ7+zVvKOYIaHOlSZWuMgw4jfhI4RSJZrlhoS8mMVIkpvXvxFQlor+zoE9DRRqw9HuSlPTKLtByb8h5IUKa6MI9k2Z3ASddAP5iKcrwvKhcrU1tXBaGqBUvBKst5pIbDrdewMwPHYZOjnztxqD5aI+EEbJ/taG0a0gFv92Yf0hFGUo3FNuZhMOkO/ZwzUr5yrb9DSE8OhN456onKCzzKbfs+0EzNdTb4UiwHzk0aWtM4WYDNrf+4nFnuU+BwNWiB0RnqMFXTU6/XOxmzq8YxeohRD7DIJ7IB1HlEgP7F+JQDdA5TVxeh7AKP8BtBfzrwNhI3OzBkqYtfxVnbW/njC4QQ5uu2rdIdaa9g0kZesSrDrNmHT4Hq1SVYtZimfMgqdBfY61BlReBJr2P9ynkIxYIWiRrMUlzZAC4A7s3pm7YmGdcc+ZvNMISBYd/mxSRtc3lOJ9pDKU8nSs5cGxykzqzRmYWJO/aGbjKgvkNBvKNOXMiErk3IJq1mwIdGtyJsfc9KQtldywjl+JxjqRnKVwhO49lHzb6HA0CYgpeinWAs2L33Pu+Y/2u53eaTshEwFR8i6ahtuzIXZupzdVhKe9fUPtq8qndXxWvwLU2P4zc30K100qGQx5LaJ3rio6pQHzwrH04QlFY/dX6OJFTWOlMAsfXlp59kYfTXShmvn7i5QhSs8XLzrv7wuYcud2Gmv08o7EOxaGwyzhvihvfHtbpJWtFmAyDBhVZwvoiLlaTwlN9Jx2pSaCH4F2P9dpjlyCppiK6Qtck/zWY1vepy5zblBMXuKhJyWcGXeD0OFUp7K9z3vaeQToivpLgb41ZRcU+R2f+tc5FBIPhofF+Lb49SVvsVDd2AaInSFdZhU04XLx9nR9+Z7eVkq/MrI4/hiS37uKB5JRMKyCBNv/tZIAw5SJUeRrVOkb6Omi1n2b4vRtQkyAtNvcrpfNWj0GBq/0CV3hAbRpfEd5F5i1wqxF7qPOEBdrppurYi60o3nKm1WR/55WXi9gkA6u+GagSaFUtQ1T/Rum9XPku6LlMqTwsAW3ixYojRhL79UUfo1whxl6zeHz4wiYifgPceeIfReKsydGVz02pP5QxbU3dxpUqiLEEzUGned7htu0HSXSWiQVXNFuqIIhMqKBd6s9SudJxWf9AjlSh3sAZAa/++kIlg/Rb2B925QBDzrS9oPmVyJ1z5BJ0oEguYw4zGIK7Cna0RbbIwI9zxgzc9Ykf/pWhuzLMade1xCNBJ4tJPw5A5EMqluwe6gNdsVvF+RwMJQzYpazbhSqDn/Pq98INhawTjuijcGElaJvmGSIE+2CUft5LjSE7sZ0rX6m0T51JJ+rbfh3ia2jJwBy4k+QqW459/NCW//KGqOKgblsk0NCn8zDBegDlEYTUmv+TcfN3gOoW/eoTNPitZEyDV8H80AxdgU/vA84fXDNSsBCJ2QYRD5tQvCoxyYuMRiAIbk5rBNw+Os/TWS5umXnKN7xT50liXL+W1ZOKSIx/6U/7cH4GtGcdm3upnKVymvHZaj+egcg62UEgxDMjMY+8umSVEtK9bPHqrcawu40KgyfI9w1CA6U98ednV6+edEcu082t+0lP47nW4st/9SPZ3C4i6LfTipRbwE4EIZDeh6HVaAUCy3FkvT3OG6JT9oX7PEjSkKkFdzauh7gtZ/ed0rSMhrwD6dLz89lVBalg2M7htgXjEvzvnuZE9aRbsJD9UO1iXVfUtypMGO0lTY+c5kv4gN/Ti/w28U4ETiIxvtgpPHzpO0RO5FRbsqqvoFKrKcNBFxWfKTvmIlUIB9LuiJlJ7Ty8gURX773fErXsVGzQnBR6zWHTpL40+TI+m5LppwNHD5YN+oDE2qJFtqsV1yI1y21KX8i7tN41R/rLe7iiq7JZ9StRt5/FifGD4UUmsQA/JwPd3j2mUs6aTBLG12HlryoF7HaSkoOBWIZk/7wl53EaulHfxWkS917p/5k/IPCqbrdlWlAcx6G8/x4Bmw/X5sUAEl+Nu4D1Y6inhu3NJXiAazWXuCLHz63CEz0WuD7Pby0YJLISF7QYu0vgR+ATjnJfbk1T+of72bugdIgKxv9cPCAoFIKvYAe2udjn1FyTEwhl94IJ0u9i8bcs5tUGTM7g3jemixVMSMSIjoyL9DU1aGI1iRA8SGDzxdAKJb0IbU0fjMdeOK1S1hwYJDJ6Yndcp9ZM5UeaoL9TW+vLEzaXikiaPus9L0IApBZ5bV+69K9AaOvuntxSMJoHqUbmVHv3L/+v/M77MYy1ghNFlmIjJqdvkWRpWAAx0+t6PC9BQsd5SnwjG+EG6BTyiFSK4bUx+XAIbO43yg4rjsMbl++f1x6Q5dbbAxRcb2erfjR7GnKXRvYRHjqfFGcY9tj2K7ef9u9xRnOSkELrsdSWwVG/e5j9SfE8Xxgu1oUBa6vhforcvjOTO/OzhyRhv19fegXwPSavXKBd+8KcEG8fzhXIVUJxlvcjdRL7y14ONcJG1lyYNP/sOlagh2HxeMcTn2DaiVotLHirWhz7uk+TgdgWbfXfKhwaqTZN3AA7BU7MPEfyQH9dK/qG/KheWmGC018d/kxBu5SgWvjkpsJCPhwA6KNEsTX+yQAwIkPXgzSIYu0yvc1vqI8bPX9uJHE8wdGG7RxHmZztENGYEY1R17YWFfhUWBP1mAB1KynxXBE7f39raj83+jTTNBJY/YHewFg51ISWQcLLz+c73dqsRraPHJCRxTn2N1JFyM1LGpNmL130HnSt/vF63oeyLLE4xbMe+I42fIxO3fa21H20y6Ts5Pvvu98HZ58nccGa9cHXZb68L1vi7Sj3Q8lwOJJtpzK5fERxSUFTc4C0Dvl3wdSWcAnl5MfHN2p6wMTfliCiT7E5qYZMv0kftPiIKKgZVXuahz9lVi1v6Tskz5PXVK69CKDDkVPQq8ZAoGWQKdy7ELKOrMjmSm3w0oH524Hq2udTB87p2AfIAevlox8y5o2kO/M/OoYqvIoCXj7c6aVX4BWzvfxGmWkKrgmShg5cLiSWVgiypncM22IrRhv7+6s8Yp8K7vm9Llg8brB6ijdiXXPw2qZlCvHufGNGs2/7R2ynEa1xY7iq7uMTW0fvCMoW6GuUoBa1/1wKMHCLMLSr+XhlwB32VLVrwphPblMf8GhfVdf9zv4AUjocMcj3RP1jUU6RFn368t55SVgc7aTwpsNbuP14EIO/LiTBWVCSvLKclwqgUpM4fZYSKk4C4jdVkJOdbbmNK6asI8Yd0BomuxsFmr9GLz5F//wtvtFsMtg0kBy+vuby2ByAqA6JPFjQdOE1q4xpQ0IVMoDZxQjhc1GkFEm+MDiEsn1tOkUVCo6QB3hhNpFZwWiRTHwpmpWqS1wIe8CrY/R1wAVY6jgnjhllq9JD4wsSZhNoyeGNlyABX3Pu3RtsffQILcfd9Hic0aQUPdgHLNCP4ideQTSyiDuQzd/uDuTVh6gBCsXbixr0b2nmzxnT4Jf9UJpVXVsuafUTIYAk8D7w2uBlD72yHVYyRpr41+UqrV9SKPV5gJbNBXNCThHdKok3qGef49fIi2Hpgr+NzjIZtHO/BHKV+8n3hjnOaAJMz+6lcKCpqU2ek4pWoKwCeizRX2l3mj+Hd6/V59l0EQOIQuH8j5z0KOMeEqjsb4TJvZs7/Q0SVsMSI75Lvq6FssYxEU6cv4Gev52gSui/2l+zNaoqDMGrEBqTQBQ6QoU91qypReiO+AgJxg+OhZwkggXQdswGBw0A3wUyMZr1vNbBvqSIwYBNHM/9ALjhgS0mxagSY+4SK0/exGMetLUcnuen3jyL3SYeoingSO7QbUPDf3v0RIQLxBrZxD0OTocQC7ulL9NdsHkUtPIoBIKAx3vjTR7dxio39vGeDEFyf1rcjW58L00V1N2fd0rrEX+lBcL6RPDSkz88PDyJ8OzYMbHJ+HewIC26H0BcQNWY3pNFlCw4T+9vpM15JnjgEEf5Jbri/Fom9Y1Ptbgo6AJtseLQh5TSfX828LpPC7HD/8UjhdcAaStK8xrms+duP6vFmsytWqF+L3a08xT7t2bMoegrAWm1qzVguCO75WkS31/kIHBZ9OLh89MWWaRCetW+rlQYM1uEe7f7U5e6OsQf85jC8d0ITW1AE/UbddcsAlkGcI8Ak4EpYrdakr4uElXTFsZAl7+BF5w+oxedUknzLfJYpPawjHk7aLBHkxH3enhtc3kxFIYDQdOig6663zH9NAR1I/Qw8RFrkeJ1JYu4joMizLvbkqT2jSWeGqoAFkUuh8x5sKsQ64TjLoRWwDFB8bTg43EQFspamE+ZdVsabyBWjtv0+V2HbjW1aKQPSkXFcPh6Np207yPNzeC2qeeA1n3+ag3ahsVTrZuVIRuaOSsyv2Oy/bfTlXY+sRhHA6BU4rcrVtSyzqV4ARd1tcQVXZYypbOFToTCydsXewIHmhkyMTb4u+PNt5Cs1eQINWQg26+fo6dkIQ9XsRNZAmOgiS7up9dS1AMPqGH8af3DO6KiLfAv4HoJ7kryYemfOPsZWed4XwRWHAFPPLAEPUmUAPfX8Rt3FMbfICcS/K1i5OE3ylC7kJg/h221uTG2Fwcmm4GML/g1gUHnqKHR7h7R6dXJqUWBUsBDH/l3S4KvWnsq4wuCy/CjBhqc9Df4UT8P5Khfdm/BrX7shhZdbIt7icu9UNyZsdqrDHzo5qSt8wcqnhYmqSgYhhbLRW5BERhvyUZ4kF1htmmZdA4Tv5Z5fJdcPTo8q2zBdZljrmslW8Go88gwOJWa53Iy9EFoTGj7mSUUg9TQAu01/0oR//uvHZhREu1rbbuEkJEDjNhRrIdjv5ixKl9Sw/FJTLGBau728f9kXYweVb0GqOB0g0iDMD0kgaBO0cuxNUy0LseC//f/ja9c41w/D/pbPAikGH51vZESXC2MwW7x06mTz4cPLM+6YFNpqSgcp1LCLU8Uhei4aPPlnEo5gLeyP3hRDCKiC+6itOBzPz9pTZQqb2va0eU+8CbXZpSBqD/H1BrH3+2rTo+CKIyTkvjsP5qe0GdCptxT04+PgGXgt2ioO5RPxN/8s5oYI850Ay9ttBMADNPUGY3iU5aXZhlRdzlT2hMOwTLAgCUvUiylBAU6UdipuZMqLqrZ0NBOQyXxc5IK5hjypo2+Fq92hf/A2QCvu+U8cr67jSPms8BjJzweBudy8pGbac9qshyGcSIjeBbqii6FAuf7an1q0DLw0mUZ8l+GGRr3g57LV24Hd+JYX/mY8zn/ThqZIrZErc4OOtBH84PKy3katOBVNdqH1GXZE+XZHs+0RYDYpBKMbJI7XSnxlJJBL06hwzCWa4DnQAn218YNgu8NKMpCfZcq27FumObGzIsm9P2crTyYUvWTwX4nbsY2l86G6GB02Q7Vou57ehKS+LeA3USSm0vOa/xPOajIy6hxDKoHAAGcoav7YzvLqu2TJ89I1SQfheiukcJm1jjVgmJFN8bVxFLrv1XhRgphvC4PNmwcwpLKp0sG/E+YlnrFLAubk59/2133E5pPH/VjGEihjYKKd/xyfZCjWhQRH+/5gPaXKb5aNf+bEitxQTArfPL4IgC1WwMktX+hOvz4l8UJBh4FFLjDgyH/CESueNE+KntlOdtvEE1GcAdIK6oRqtw8rKNWSdGvXlOhcghQbYI9t6SPUMbxNBAo1/Df34IOn/D982OkakWdPalOWcJ0IRNOwZ3Y2Af9ao/3B2WVW7/PEjhTxm0JpkRDWfniyG3UWDWzHqadUo7wdMuG6owTzHOH+juz9Fdg7NYMvgDnBts2zTkOIJOfSgcob7NEb5dCpkqJDad31yKUzazOyF44yN3MAlVKGxgQWXYSoiAT6tIzoNtQFd8gIZJmSufkRR0H26xLUTC++6vT8I3UbbQ0bGsqfzfIMmZwP85g42ISKL8WXoc4WhsT/fAdBBGQtoZrSM82hUc4SX8SmcsFSiEo3aNpKr31I1WpYEjTHlHL+66PJoox1fDCUEDo9WKczniVvW9x+otOq/1sIujMgOkjVo0dPboCw/NnoSgxIHvS/mkhFg5N3lzIzvxotE7dy0s1PODLvUu9NNyxuayodgtATiHl6gMLTu2wEDPAjkVTc0GVQ5Tr/CThvH7G1VCZFJ0AztNJLQVJX+0kJl9GrSLQ56lm4L86dbAxTo9pMLLlbnd94GyUflOCJEAURZwXJ1Y84YyNjPOI2VJaXUKKPvd5SsaA+rG/aMCFUBRFvHaKq2tVgpKRPkSpMvjqxgcMJ1szp+jL2xZgVv8m9wGRev8Or8pfAeK9ZwMQkf+UQQyZBpRFXNnWKJBnxG1Jj3lrrv3ZYFKXxjL5rJqWlEp6kiFRf585NeBAu9AHxr2H4fYUco5ijDDRzj4JaF3SZ80gl47lzI8Hxmpuy8h9RV4TsSLIj0gKAXQqJjTK0oKPwLjyaNrzvDQ0NJ3yIxGg5o0nrPwlKPv+645dyhimoiMu1QcJw8cxoQn1UIuE10+apzKUeali0IB1IW6m8TBMDz27dzvieZ3P5SKPGuofZXn5iqlLFPxDhYtuCrFlbwx6Pb0/zYNzgt4Uo6ZDdPd21NWg6JF8g/Kx13GabSIf96ymYf6KBekE9tdD2B7EfPFM52+RQbTzAt1unOeJwrK8vtpHKM4QbldMdCVQuKqvROS8Zg0OxhFX+SC7rFgKEefA6gFjd9cbupX3u1tcEXTlPdezWN7hZefy+bAlxrAiRSkdbyFL54CU2Y9NZScizav6deDxiiE13p05DFzlfyFnWHCMojDocoQODJqBVBDz7YYurUhsaCZrBEZQZCgO5MvLU/6SsZ8g5B9MHftBcg0eGcyfP3UpaP2lJq4jjiWmJT9T2aswDpgQSEActyyJmfz3RSKjYf5iW1bEEjKZPqXG4QoduHiE/9hABzIRVN0KDEMkH31OX5wlxMaQqNxvUOyrhwV9bC9ub7W8oUqjigOXwe8zygg2EtG2d7S2OsmGv+/DLXaNkQxeZZeP2hUUWbwwknxYvkyfKufKxSYzv+i28c8y9FSAuROcwhdOjwxlXKc9vIs6fc5GP/ODOt4nFX/qCAjufXpOAH3jFAo9yv8J2gF6AV6SeChzdBTDkx6vePb3A+v1lOrkVZi+ZjFHQ4X0DWZbAZcS8wzrEfb9UaMj01t/TfZA7111xE49soENnZ5mWeDM7wuOyi79JAuvpRjEQrnQ/mVI8fzbXCUOWEnMPVpsf49rEMnHSX+/Z4FCfiuD0dfOXwp9DtnDY7SBbu76DakPJ/qeNS7tuuQkcwhNomYW5Q7VPIrA0bjFSfISeEOQuyH4YbMnNISuQlQJLzBlQ0tlUdzOJ5frGxSaRfIC5FYF2i3C/SZ1XyEJjxAxxv2BAYMhJuVfd8OjPh3NKTTDUF3DNc7w5wxNpKvLqtEdOvlKsyxXH2FoQ65SKNdJJ5Dk//3VRpYyi4i/zYe8lu3ETnxHe6XvVbbyOc/Yebl1/gnmlrzqJpteyeioZ2bMVVenfZRzIn5XfYYR98NeWIklOOQTrLEHNo2Hr3xVkDJ3AOO1qdxWoTDc4/NOa0SXqw20uqMpFOVNl8J9m43gojDG7Cgaj9SB0euJXW5FtxImc4TDJi5rVl+OQqOFEUHkUP+IOhA380ATb7UhT8nF6MTLs3rV2hNRJ2K/rA02vmaHJ8vyJA3CNxl9muGwpfoEieXG2YG3O3pSZNOGR5mTzN53sq92tuat8zeEF1yIEdKIWpjN5fTAPsdybSuRnis8/QZyPHB4DjTZ8+uAWlKhSrsjyaI+mg5wLyh3wf2BS4iZRw/8Rg++e2sYap6gf946SlHyOxcR3kjL7tsY5FPQN6aBfx5QP4y0edG7m8ZHSmGflsv39Hn05dNtZT4eySJnvHxjYhEf0f0cOvPdg5Td6GscocW4qqCnqhXoA8y4oppG3tjE1N66Oko/0FbcJOWuC7p+n5WXLf1giHqcOzoRD4qG1CnEjhSaOEI+N2W5Ct8uP9Hbk/4yLDvnhIZo5P9J0vS3UwcR8MtV+L06QuSvySIoIVCMmBNRQAM3/JHUvL/kj+8JavvHPwlYRqD5ZpCm1Ye5ld0y44kODuDpZVuAQDiJd9uUfULzoUwMGlw19SlIqm4OmVSun+JUQe0q2He21D0i8kCebHl/FLiPjXValn/1Dr7MZC8IB3+1rZr/q2yA/MAn+9GmanXyEUSI/gzCP0Um+7RoSmmCVXj/36oiuvNsc0z63F2ewkNxvLub/V7z0fE8SOmcmBqppklRfoLPZpT+xoKDIGi29ei/6Sx9dvf8fNRTk60v79ZdDnfJk45GlicnMZTn0iJpAaElMuaEcbLOMsff3lLo+9KEqQ7+7lviguZpmUXfr5evy4p8wijyMfj94K59UfE/a2I+MOxVlzqWQjNG/SMfr2gXeNzXOPxXfzXMZXY9EteRA1RJrMSiyBYIlp/lFbo/FsvIIXv5gABfOD194EFqIajPov6DQoCmepvTiZBqTl6h+nPOQa++fz4UbKcluP+Yb8RjqOjAyEe8KWTQwYOO5+UdgIAyc4q0rI9RWJS+0hsAFyrv57UpXpnC3WvbtsncjOZTX1xWldklzDdcfg2MfEaIQrTGOfffS8xs2SNaEge1Fw4SgISgBS7V1b91gN5o0xbI/cTVDoxSN/iW/Mtwr8aX1vBGKpcaBdyswUNTw4EijErrlCvCUo7KjWPnqwhyh8S/fA1lQo2aoH1AN6HcWSkyYegBKNGPxMllh8HA0+g4xWyHVCy86NxyWLdPCj+WNQxb/BoV8GrLsPDNnFI+Nj2IWyBkXiQZ/waotslaTXSbJ8Dt7Z7eiEs9mK9yeYbRZB6D6CEVko+0wwmhis3fJ88isx0m6Q7tnHJeVVlaP2BIDZJO8QnB8JWNsoDDS4JSmLDlUZZqcWl94ow/070T2ttNIL/u0/moH5Jr5mrE9V9lCUNEnxgjnig8Q7nSA/vI+1SD8VB3pf4RDN6GtbTFTLX5DjRsVG63knGjFLKtxdYqVw39Hb6KB599IxbqrE7UsdpI/J6hpVv0/GnTLhQuTgRke1JPQr2GYZDl/GsfxfG3/YOCFuolezmdYyZDN1vzm8SuVSqD84M6I3YL4Ck0Gq/G+yo6OVsV5MyRoRBBlPVDAWtBb6ORrlMUqxNjsa/RTtlzFFobpYgKlc5h3x5PtxsZNDBz08olmuKT/EofdbpGeqgZXIJkkELDJ4Rz3/83/k6lt/YAfjUbKFBmNOk05PvRgoen2RvLu3moGtzqoNdTweAaKAowVLrdxfpMmVWDciWU5f+AZfbRzKQdGorQXAWGICtd4wZ50GdZ74o1sOlHsnQdXI3KVNiT6UuPNi9HI/UC0ETS7CHMvPYREIKMOp+qsS+ZdEdFXau9Y9R9tobZ0sC4rO2Ls8ZKbt5tCNVRnflLjYWWjFy89dg8WNZ8cPJEtzS5yfZjeyce9eT7e3peGT75J8MM6dccajI3OfpXrsGSQp7VQzDP1jO1YdcyqkQaIFual+TeJCefCHYwoDOMJy0Bo2ln1UmPu6haym6CHP8KIlhyYoNYF8KrewD+0yeZ6UNQqVh5UXxRps3zp5bacGSA+FsDUZ4K7dAYCzQY9qBHYtsQBF1ZfN1BhleoLaJSiYlbeJpWYvU5o8coosbiVVM5P6c+21zoq18t5rTtGVWjhQzEuCQfU9zbvfYjLVQDfuLyrz+NQYvv3Xk+OG6BIrQB98ZLtFAmx3Xmdvt+Jqq/ez1Q0gLPOrnobn708GXZPwqnh9+lfc77hP+kE3tzEa6+JPfvzh46Swk32qud277jp9I4UkqjvwtMquDrFCJRfI57gRgCUqCBa0T+VfUE9ENr/G/C82qIRVKNBNeLSvcaGrpH7TK2ui65LlTa65QqSX8B8nM+Iqt8icf1Y5Iv85i/h4+uko1J0hLiMxj17BROEtfxsqWK2ynoIepd/NsTrO2AwuRPBn7al/4krrrlRrAfKPnoj546i/F0UsShCmioz8WAyA5eB6rWmlxX/CczJIyKn2X/AnKqT0+BnS+FAw+bp7grYLP6Ndfy+R0zcUgOnzi89qvQPGr6X9ChA2qiaw/eMfSwRLzXj7XeMRUfktYLjbtFbEisjteXpgm3z8CWEDtRqVMhhlSUD/ba1ux/HS4OplbinPxvgI3IzxVf1K/y6ZT2Ex2eeNsdZwWT8Y3LUU/AUFcM0pSTVJ6BAPU3+OrS8UqnyIiamrji3noE0GRVDil7HHngyxtO/qZClJLjEErS0eicfsEwGBwhm3PxSyHPGMSasdxH8e2o0Y1ByjSQgXsQ601ix3Swlmf9gkFQzjfalMo3yMoXjFSbVwtuKP7t++QV95rjSYzZFurHUiG/kQNlbKX1L4P0+fxOzEidpNCuUCeLIc3yi3FgSGcLV65+CW4nM/g0x5yUrPqhUhgHj1SaUr+/FRI7I6b/oEngSklxxvJ5YCpN2KfbRBJXBLi/bJX4NIcYcDsYPWHGstL/qlzK+KJ0rJdIkIlfTuNQMJK0fFd1na4L7N1sjSRY0J43QucHPUc2/qlPKdMwVbaNCt2rpD3J01HoziAuCnu0gpiaC23dvIWY5P3MlbD7QtdeukMyJBlcarc6ebNJaSqb6zx1KA4j7PfFeTzLWtAv6Y4mQBld5Y7bqmdco2sfcWyL3zy4TGR1NgiTz5owKJupmBVrNNhxuhCex8csx4XlJUYefWEtFPRcZiAx7Cj2idGwpiycWHJIXx8B3+tIXxzuKGalEcXry1bH7LC3k7QJJiAwo7fkwrl/RezgHRwSHmI7C65tjKj4MI4tPEQ2ZyKaPN63D7+5uY54nhNx3tqClnv0W0s/1OKJlgPpHNDQyUvDhF/98JrEb7ww3AY1jw/xCiRfL+57YRFUmTfKcUVlmyF4z8AaOj6X1CDdZLoi2vPQJRZcL82Ud8CdtREUAQ85Rvq3D8rSaZH+lk1psRHolxhpjVqk7/G7FWczkJfl8ktrg3W7vN0RuBMcemQMe7Z4oO8+uV0cpYW5tGOZCl5uCNJ+VTYiZlS53Bm/tIAwA6TWErlWGBUqdhX8JER7ev4mj99l4x/6dZtDdX1haV6PJSDxPSaW1Kt82yj+10FHvCja1enQ9iHNy1LT5WyOOtWgTcXQhnFuvkEfII9NUiNyzf3KqZBLFIXPBB4dTdjTeAkTRl9YBlWBuHuuE2hp5ls8SNqDKLPM6U2rg/+upf1Vg0eoeXEWCDo6BweKdnL52CJHT7UvaLKrEzzr5RLod+o+fyv4BhIkLqRKnQgFh2YmlWsVv1QxQY+KPa2Llo3IM0/xdzo/hloRf8j3V57qynZjqiKaumz38y5fjaTlq6CO97eoUY7tlde8qUWIKKmAVik3N+ULEVB9VGEyNQNyNtO6f54HPeGYsbU0vBvVfc0yluO6it53xmsCJDCgAOaHybXllfP+sA4lHo/NWXUZsbmUtdfpab+jJ+R3Jg5CkTuCQIFxWPGS9d4X+1mVTFysg0VfZHu6iH/g1vLrENAMUalVwtjYFztBeD4Lvv67aKCZJuJGFjXod7L4W8cSJ2hPF8eDoZIsohNp7Bxzy2lWtE2YputW9cooiiH6Ru9wnmgU+TfSxoxVqm5mGtzUVC7n7meX0ZyVFb5MAfCln7YF8DcewX6SSQ/Dv1uQn0h9OeUXB4gFiNtNUwxnxNwgcgxyehmSDEEQqaVUNx03qBVrfFP/gzTXGLdsxyKqNRjFsywYMSgLSFFEqJFnEIjDGBYcFIc16C0JsCeRYQ9nXtCKQouYmRvuYqGQR0yhBntLJ/ZHCo8RuLKXhv360LNFDv0olVTeS41NK4UNw10lWMH3ZqC8/KS8Gj12zioIh7nmFEkiu0B/mVya0CyTAd28aWo3HxlQfgJ/7f9foGgtQyOfJp/VdOSMN2BXmS9kWq6SezywHC8fyybJSKh/xGCKxJ2jCQq8hW+ugZ+yDU2iM+6/+n/evSbOkVMqnXEX1PJe0eFvT9/rpIkmWMleTuPEdCaUt0ImOzlwKe86/2gnIKbD1dnqYV0XPTEwTdXwAVWZlJGqnsSmADjb5yiKZsYXCQSNwEuBdn2jzRkiQGaGmjHEgAIv+V+R7y+RHei61sBsByXRUR1+ZWJ+Eg4MrWoISJUIIc6d1KuVGGSwo1yG4g2UmLn8O59sQAoi07zk6Q6onindBdObwSIeAUJR26LMQ31qPBkf28gQnJ2Es4WxzKGboogT+sNiioGiwdraUzlf5usLETn+2Fz7Sj6g7GC33NUGAZxMWFBymKI9JM0MCs8gWB0pH0YTVPb2L6REmAxFASXJZvIx9KJJPCjPNcFEPNw6uuEvTS5C4WhOCaYpDNeqAQ5OhMZztEHQYY+XRlmdwl1+jiFpsawhfi38TYpp/i/wMR2dnBSkhbXnMgJU77i/ZnMK03U1Aht2/upl4lMVOaUJFJ693yp9uW3Z55L8s/6PuPfadh1p0gOfpi/VC95cwoOEd4S5A0B4783TC8lzStPdI/0XktZMraram9gkkMyM/OILkxGk5muqousv10hz/Lrt7qvnBZaacPlIwu4oYDA5lrvFYJrmNPBZoGPQaWleoh53jwwmunIOn/GtdedSsKVY31kS/A0+SodC65Gmv1pkKssfIDxMU51Xa5FbgOCj+fVU5E2TjHx6Co1pbQBCNu2cp3S/fNCe/4Y6Xm2GKd+a2Oz89sF43nmPMhymA5SmQPEkTvHrz6pKhiyjZcvfodybqybd5sqICJWYmI5l2x9g6SDcxbsaG0LRCO6p9yl1vucJceNlDTH1jQSvIpji7K1FlHoQPUzTiC3JZZBNFoT0HHnWb9BvOwgVoNLIvL95+eIHFXZN7aVL3pGD9qvfNKzg5GOXaoj3PnpzKZUJ1tHjaYLhvxPlYE2qF3SO8TAYwmOg1JyToHemZeIO7lwQBAoXijYJWOaWXnDnHaCsVByDjADkoShzsn/1hGOqjB8YIA3IuKbonRr2Wcta8xq7S2Fku9ROoJ/FOAXP5R+AyfvgmrIQxiMsoa7fOVZzvwn4OFOSKJhBTld08qbXicVQpSOkDBhCAByLjrjqSQRvyqvd8u8IjLYUsSbSUfl02e+avk0+WSgKVrHLhDKPRZn+he9ILOw91tF0sZ22jV8VdXP1Xb0eRfw7D8b7WXEIDUa95q58jO19E91Tyx3qm66wgaKonW8PdpRXg+E3SMUnvyH+1TDX8bl3ZZMHDrKuDp0iq8fA+fLQSelkeqpq7YsHz/pKaEUuDxgU7KVfISGvIwfVhp1rgA5BwydjEwd8RiYCptvtepHW7lxH996aL0rsZeEN/e5XZcDRWtDAx10lQD108avGPRq0o1CiXtKjo8KSNTfrUYkz2UA2f3kUEMCKnPyumyCe+BVqHdlXC2ioPJhuFHid33T9+2W28qN9C6IW34sb9XNCnVg1fT+PzVZgb/hEKxlocdGxxONj/HxZzlrEbs5WDX7SkGVWJnAVwToCNlvfRxPlGkVoF7ziqh+XRwW+lUv5EYeBfsWyYdP07ywDn8EkgnXFIALjoA5N4INmhSzP+F9f7XbPeQ/8QicOL38HPIFTnC9g5WMEgh7FZ4RmYF+BmnBnFE4aRpUFgmpohA/3GToXiLcAjQOkTCHPEH2Up/fGGaPJ37zUlSq8XbXdAecgKwxgHwvCmTuE7+X4DLrwsbt8XQR/3u+APJY05/zomyW7F/LqAoRHy2fGVgY3dCwMEpIFQrj6z17XhyhFY04tjVyiqiVVmKYYcZ9r30tkki2KKM06VBOlUXRkg1AFR/vX6N6n9mUZ4AvY1O/3YCzNLN/mDuaHRAsKyuFz15Q+4d+BskCm97fEH7Fnj0EPVJe0GD1BPFv5XdezJESHYsQoYcUYwTBSBm68rgedw199I+4T5mEc19TBqwITmnya1ukbpTitrflz/50rLdo6mwOZ7hNVzouINhSMO/Y31z87O5oRdsulMzq3XxvcABcNwUnY+ELpdpbO9/3mGLvFtn4WgocdWWScRNlBGa1YhBDeAfNDk76i8MvcOUPrXbtuMOHndcxX712332tdjsRFmICU7o3NdcAKMBJ8U+O+yiTzh/quZQ6ti6+OrCsW3KstcQp2BSTqGGnTYypcQNbGXamVq2n1JqW+o9w61nAX3YJtX8q3Zs7fN7W/Rgwh0QfDkUdHR8vwyyyvrZyDDkOXsk/f0AP8wSn3Xo3gdUD7TfO89KmtW87ioD9DkuV+YstDAdkXUb8wsLXTBD8dhlVyfv5ejxdxojmGAIovssLntuRtLucHmwxmwOOXvz9aaDP87zcbm6zUDp0UDcZzlGY3RfIq5fDL6HIhym6QTxIsmYQujFoVxp9liNqjpWbE+NVQj9MUt2GZnHPPOSbtlh+LY0VguLbSPUaalw5bRbGuv6POQdB+iVECHpD8V3eI+5ZGfJAAshRdgDaN6C5H5j7v9+atyoFV3fGp/Jat6gtShhdwThm5kXevv7II51wL3O3nJ9NDv8N3lhZgVHYMrb0N3sz/tEd+KMaavEoMHT9BlfVBjKBFkMd6cuJKpvBar3f3QUVx8CIqErC2+y6njmT5zpkYQYTxSTH6czKfKaMwBT92xAD2/vVaFrVfNC3jdzXRqYGzC1b3b1hT9pEbwxFwN6RBYoN9ZTGoolipzV8euQnosOrLUOCWTz3f8884dY4/fokOBk1nHWVjh769RYNFEYvQAEYMFkT5e3G9QAInTyqPiaQKLH0MGbk21K6EM5bnPZBZpu7oFWQRYVuWEOyithzlYqZaJu6vAfr9AgsQFCWW+V9JAfTM7KvUbHQdmd/opECVCNUq/KlgCOP1C2djNLeGQ3f54I45ds8Xvn+Y4VD9gpqalRGfsn23lfANzErnsxBL3wY4UMaKe7sCHm9UAf/QV2PzYmaaRyGeqbKGVlasSKHhHkqXyxbFX0cbDr2IMN+yazmJeRfqYw6RBcBACniYQhMlZjgkN79OIcF3uLc2cKcOAS/7nG/xL9sdKi75mVcgdPJaFh3wVXcvBszzizR9qTc5f1rQ/Cr0whLm0keUQY8FYebll7ToVB4TAgKgMBAA3gguUbZkWj0j+/XDHAzFs1IVLbgaIWicfmRCT6RHn+M2vl2fG2bKN0WRZq+q6IGzrl7Oq5B2wTiTj7UyYhP+PbT7K7gij1qwtJChI3VDlY1oEAcq9l3AemgCkpgEJjfWRWZSljcEL10r1N69MJCWVw+mXo79o8Drn6ZEjFQFfrVxGZ23v5ofb7JCpkjbvV2AAdl1YUx9+zJAkFyM9O1GVd0Wd79LqNo9RtEUD8qvd7P+1WkHLieibsQon8vmm3/IpHd8UQkx23FvqWGGrCxfq3JyCDlc5dR+DI472P7KtwuEpM4+o7+vgMADObxgtfUUzNJuxkkr78zWkrRp+WMxRpEo4xvqUg7XdDvAT2TsSwUAL12mYnLL+yZHzovaIYnXX1dX2jAh0x56znv5EOyEmSvPv6fFN7idBmtNOU6yt2XuXUFnOrVRPMC7y4GYjmIeUwtIcdKPKr6ow93SjoRf2OuZgRDDqImZ60kInW7ql+usmV9aAjUOSUP97Y9lBqfGw/ID2a1s0SisE2mtR+P05hPxsC2TPeT6zBWaRYNj9HpEn32TbZUAWc0FnEus3d33keVZmPPrQ+ayRnNcz8BHSZkK4ZX3ozQJJ9+BbsnN/JfIh7kRXY09ibJEi3vRydtkNoUsOidSk/Y2l0UzhwBvj6xZs+6YtSdYFqYB7kj0p76Ym8bPKCEmcWNCVSEQPvZI2c/qCh6a4CmyZbnq+5DbwxcqUyWQ8/6O+YSaKl9k7UoBVKobWds4mk/TL7tX5sQS+wskkwcWrTXMY9AfHHyfwKOB0SCvU/gRezKA7F3CPkpR7vKXRCHbTWR8pHHbCd13am2DItRf4Ewd0cfEcUlx6YqzYzBdqlNcN+51P935Mor8q+hYW/8w7R/Yp7Sxl08yo7JHxorNSo/LGu+sOKsFjfRfr+W+sUo0Vw5rKVAr1F4yZ33pX9o4k4zJeoEn400q6ANUjuWFic2nZ75GgpMyREGcgstkMV6OjTYR2Yk9dJWxEKP+i+siafQaf8R12R/HeRPTg+K6F/d2QDKry51ycX7kZyOhFE6pLlVPLzRttPcCw4PinkWTpibZm1grf0rPooFfDnXxNhO+v7Bp/9CFtIBSNJFkvOTgQOk4BHqn1e8wYGzUaKOaKuZFUOK6sh8+jDF5FG/p4IdpBFyFHCEgomxaYliAZ3wiTbsJL6AdTUuvcHGBo4Nkv78lLd1cS008Sh+kfgSOLdFd3ntQpN29J9SHxYEyAhKwwcgB8NdMzNwTSHGE56wpM6Zvf9WnkY3yNLyW7Ml8CyD54bt8H57Ag2cO7KckXyckxFlM/LlblBxFQOHqfsszitfl+Mag3WAOzKa1cRinwxy+W9G72DeXzROXgoxI15p5SVSG9JP/4WFaWqxf4reO3K+klmJjkhgc4RisS6VkVvYoioz6eFVQgnQGmXiQpdhBQ5x2euj4+OCeoAMNHmCY/m2pK0+oIPIeJiPP7rUSvuJVsgibdTyWjhm8lVSJPgPBxc32iYa8bImEhZOhEI0BkLCy7T+oB4m63BqiC6CC2WAU+1IoIABr2pvIpEOBqIA3a7SsAsePuf3l0729bbLKumksT0XstJscQymLMWdOeDtc4NesAs7aWXaeHAy0u0SInfSF3QEv7nvTiu1qQwaEvM6bPjkZdpV61ifql2G99Oaq7wSSifB5Ht++MjONpF8F0IaHEHXdrtrgMIvN9tCGFSlF5988JA7tA+NETJK/ZgBmHkCp+RJzMqxGtdW8s/ZIP3AyhZyx/VGVD0ymDnr50qaIcXzhhWgOcCkMwnieyAu721OX1/l9mZIAuXeJT4LKgtwGladbWr/RD7qK2dWlj/WKb0ehO7OxbhsNYDejFKdHlzxbK2Vz3vZ4tO+KkV1eVD6kX32cwITH5bQHy7cA6/F9MpCaIyuiNgGPcGgk1rpn220G8KG3x3EETAbJfHOmuwTB9r3LcoYqjMxcWZqiODYFsTQVk4ETeUYnybEuNIXWMTKnU3BSYvfA1XhjxY5VoD3FgyquM9GCYC7vWBESM1YyrRCDcPGJT0GHASNTxhvGNNbqkkFlYgXATvijeJ1JDy5hfIfgu5owDOk2z1YEKSHKz4S8affzGQn7aN4+KqLqPaZz3gdbNnsoUt5Zg+PQJL0Ef2WOHeYDhagHInvHBRtpIU6mXFCNn95tPVzUkInH3uvVvjAdOq6z00O16pZApVYaWwN0N2RzX351SUUdxnfzswbrPHRj0FV1wvhxkU+A9oBehqJP0FufkXCxXL2W1bTEcfAAzU2ak1IYXOdKQYGGYgH1MctD/n5Z8xKBsS62ylpxD5SjDVOhcv9H+nMFeDKJ89mpG+KYsMVSRv83hwmDCHVKzYRCo/PepY4nrVd4jPrPWAwF7HjUU5NklMbprm0gqFzyPjQKGWhJNtDzSZkxGLDeQIDI8XYeUl+NTd6Bw7WUbpKodk3utqvUL1df2qfjoWb8knzwqdjI6d5b/8U2RCNkzPn9QuWfETf7XQB8fTaFU2ZmlGX05AJwjDr6I364EgdpKapO8l5DqWVKzQ8pEgssv/R4zBAes6bw9VFlCYmQJle9jQthoE2XGJdtYWNltdtN3UAs8DVPxrZFs+7Sa2PEyW1FXtweSz5Aspn+kh4ua75ABh5mb7X0ydpjW/1LRn2a0EHjPZbGug8BDJoDd33gm54zWUlNOBWAohaHNne8NzGtPxf8Nu6bZ5/vZQxl50G65WFCgoRZNo2/SH7B1uy3q3uOKYi9+kQOIdpU1Oee9cHknI5jkkVgouvjCu54eh4q3SzO4vjqX8gJg7oGxxvyZF2P63diH0F8y81u1bDXjWZ1ctiarNasu3nU38a983Gz1GBucu/OjwlMOPGiGeCNb9Sz4+8X46iaUV8YdWHx50ZdcSofGFa8eyGcto+EQYM/+cHa7+NUaBDyD1IlZPjoa159Oll3CThxqVrGeOSmGJodOn1b+Fc7vSD80t/4mQgiX37M0oFXGZESbybU51EXnPt1RoE4dap+J48IDGM6Eqai8kac4Uq/fkf4+ugYPLPnTqT07S47nNlwB2dL/5hqeyvVb2PUDw6aT50GAQBWpTtncGLgv9Q19dAxLgC4XA65Zr/QTJfn6BB0Zg+Wu0icztiC2X0w4Ii2VZ9FD3p1rFJJn3sWgcFFak2/WglB5RwDd8WVpB9ngK8NfukPYYN3WJKaT+tAHLFSNk9/fnGI7CMGD2pKzevZFtkxDeQdTFq+H1lbUdd8rtstDa9JCQzGblz9jSOakLx1ir5CTcUioe+XyiV10nVDSZWp/IPpcTmhtdPB6XAaIkoaMbWVdc+ndr3dcO/O2Ieg/fOyV219lMCfKPeiNHl9bfl6/bLujWXz0oehuym8WiF1sj1F6zw0CLq+uXGTK64+RPkgQsl6eGm+YHrwnTZudW6oSd3qJNFQFqOYp/u3DsHwrIPSuKJweNOJOCJbHKIYF5w1ftZOBEKC+r7i2PMtVI91b4f5un4+H+l4T1MrbeY3lfjgw3dezDrKIT4ma8a+35qghSLSvUoik2hvUPUBBMYQMwJ1pVkdq7wiXiOei0TlrKt7TNhfS5EYmn07yV8nx6Pc5IEcjc15Af9AnFARLNXVRS/n5gm13nnfcbV8AeRNiN6EHMri8DbtFi7I21iLdc4Z8TsLMXNzC9Lztf+RvngcaNDyrudl5AMd1AFmpZ0uRRQZh+Xt+x7cxyFoQSeGSxHWYolzk6aewStE5l/qJ3511vYojN/AACbv94y5oGiVmMzZSRMTkFftlk5FJt6Z/jr7j1HEDRcCXau3r4fkXVKvvX91TK+OFQ7N+ywDxm3hLgic2WtpbbfgCMAL1bULk5QughWRaE+VodkPAYlyHVyvHKQ+LXZN9zbAsAU96OfrSXBpq351zGWKITGsce5zv0FPQgk4094t2FaIPhz76/o6Eqw7/ZzDgQji+sO3irfwHq9PxXEMN0/T0O7TNW3Hjt6rmjaE+B1KZoDOJKa66UGw+IiJjM0eqZ963AydayZM99FL9zcDLFfkgttVI4TkvvL8EbB3SgLvihwr1acdgdXpGZUVyCs75e+GybLPgYqAOcPfV5lKbNJAcbDXkVPJb3CgIwY1GURpkZU6zEqiPEl1apFRYb4sPx1cg1ZOcxz9BFWSFAxQO50jdE63VA0PEFd82VnTq6KGbivLpoYpnytOeYk2BIHngxcOs20M26qhoILlcDed0Pq6lugzR2ofn4VEJXFf32TEUVuf48vXE8taih57gD1NBBPvASVZu5/dg+pzsY+4MhtTSTQelV8NUxhVr56LaIFp48Rpv3NEfGsnMQj/7pjEcjFSHr+htmsPtUbZh+R3YVrnNfK5K9bLtvnXlmpRpR7RKLgw5kE/dDFMgeNH0FeF2OL5YZqo4ItCOLoy770FRG7lLbeRCyGwFU4Wlro8VxITE8XfeMFUvEITqNNEjOs4YsDNAnAj0vQLbq1KhaFyuATowzbx1DAt60fxnEJjCRq4svSDvRNnu4WH19QUdrG0xMflNmnr9PGIopP+2CQ7AcdjffKubC+bTdzQV9J1Q/Ppm/OzSP2Ge7IzObJ/xNwu1ZeiHZBlns6OPLvTknBX/vbQQ/riylAaIN+QdbUYadh6u38zLF9ry9I+J06liQVPbFRR9E2j7rLtUervRWHs8gs3eA6ft1CNXzdPlQYCd1cL1bqJJpoOVFqbaqVaul+z4+CWUMl0/MplOoIEPfp4b6/M7miKHburyTbsSlQ6mj+obA3qtZWOMIcOV5RiX8ON4vI66OrSmpoiLnMRf9xQbngdz/FQYjd1PSKFFrIhzjmHloNBlzQ4eE1bxOGegi0x8zmTSO1GL2iHO9nQ/XiJGcn5sowMSTe/lqG8Htsbn02tU2fAS/YG/J+bUEsDLOtUgCsRP9r11i41V4ZWtLL0HNJ2jHEAUs3Y5iG6lyPrOrOMw6/BQx5udYlj4gpvk5+mD25JdV0McVzUwdetUKitCW7tPvPzUWtGJu3yaV1cTtqJxdCOIwgTtICX1LHu2V0f0yGTT6z9WAOy1dd3hWoM2+Tq9HbemwlUzcxRuqgGcZYddbimCvFKWnP9m+5LGTFIgR/eWdUsa0Yy/vCqXyyFwvHGnpZXrbiJ46A8CEWqws+Ps0PdQ2QcEmJ07L4TcqlRWUVNL3ItnKntGBiyGFdkNpExi9FF8UF4co8o6P3YaEPrFO7C9ns90kWk11MU4rtqNMfDNp/Pve+qES0peDZ7Hai1/zXbaJ6w/XxbO3TMzfQd02xhuiHWHHModzENl9jVXMpdPn5ZNKT6GR/Ua16Ojemq1WED6tRnsn8Dc3cD+lfXJMA0MpppmswQhnKVMFn+tEpiW3+H2UWf4LW+AkmeWdPskdwbwxB6Jdnu4M5EAAJ/YQRtqDX80UqrqO3h7NVrXII6gmYWoHwO9Av2JYYwu3aKI++DKeuFZVuC5zUDoK9hs2hT8nMSSJck2A/OLV8FGMiUqcwa0Fbz/BnB5rN9YpnqXIJue1zSqM2czzvzj0IV99kkxYVjejOKXpm7KrP6KfyaA+Ei44UswOmUhQbo98VehOVJTUMrjfn1cuBKFPh3qvChaQeP7QRSvb0bl8mMyfdxefbjepbAb8J8jV0Nah8YJG1yEdCvV1L/9T6Bcj0CL6LZC9HYFC06nmrziNTAItqDiAnt2TYImnhvraZOFbotC6caT2L4zj9LTu6Viag5crCQh1KtwI+29GKD4EOiDQ+0DRn9UIfjSrBtlmTD5GCn+4LSAuzMNEo6STfb/0ZCP9C6owgWhn4vhhDnvVz8XaxjR8sD3DowPG3ygR9yirzljvsbE03oEicIVFPePglftTuCi/g686XRm3wNUTgcTpiQm9dwl6Qc7DNyAa1KXPCuzUTrKl+DkkKMMgzXJU0zwPnM6hYZN5l7WRN38FwTyfZSCx/zE+NfRScMo/6y3FhilqC/Y2zsgM/lUwCuMdqi/1LO/muF20DdvdFJKyPBdQG+YWpk4gtm21KKdlEvFEqAs+4jKZPwkZCXmUQWUjNcKM4fT5Y2pSOGISknI+SdrOWQvOxM4OR5/zpnTAw9fN6T23DKqngtceVKP7qKJVz6LK3wTVRHBMFilLJf715BCi1YbXS/guVFGR7IBOPd47AMmoTTnXPbdZdyEo5AGrMldpvpI432SfJ7vtqVjMmv81LlIdiX5IG22nIZ4NJzWIavspWGyZ4kiUyJ26+vDRcVOqRHkR9uBnBanT7nrJIzFgrExns4ARe8PU6q+U5sD2aN5dWEyOezMOE0F6WrdtF7QpZU0qgBgRLYUBRZt96DPFfSw1RZdeHjj5gVj/EaEc16VNGFrBiHMrrOZlSZNtBu/9oSiq3fmEunbKUuXZZTfjjlhbTiBYCcZgtWHWw7ZI1GAUZsn/CkXHaCqErSG37lXWnJkkeUW9zZOvTNVsyOzxQxsDMMk7ald5eBnDGB7BC/DHjNh8cGpHeUJA16h2A0UcSpikAqlpZVKxQqCN8V7gyKiccVX6CdtZIk2N353JDWsiqk02dss3PzL0HFBJ2CgvhIP+R93shiUHdSYHr+tkHiF8tU7jFGXmub1dZoQ4XDjLcLoPf5JAATRJI/dnTSUaeuS8a9bOaXVtx/e5PBYoBEa+EvVDFmzPaFqEBYn6lrke5e36hd6mRxr2WF/pquohZm66cVcvLOfZRyXLHH3t4MleaGMrmN7faXAvFVpn1bF/BhzKAkjljYeYRubm1I5Nq1Wcrl2XFH0j53wNYWSdIWDKSDAj5AQML2C1jLJluUD+9Mo6MZU1598Sbc8v7C+uWFl/5ptkCW1pr5HDXSb6JLomWSGb9Os1AcstZ5UAyTuaxJtXdMRTHhbO080NrNG+VyNjY+ECKAUfTjmTs5q4jTBIGcfbXRUkFO1OtE4oYxqJUJZLHSGNbH7WGFGanDMqIOUmHmUwyV4qAKTtIhVc/z7embX6TPmu1spwmGmIppWcXqdIJlXfP2XWzZJ7o+OTKayIut8var//JvtrpNwyuy4iMIoSDpOMarhV1EA1SU2y+vL8+M/HYzGt/YUnqATNaKs4HtjyIdsvKFLoJ4v8/7tD/lYcNo5z3mCmiZxeqrZBBFvu/Tuu7AUkmVWNryEnt2JIDl1MRELDAB9GJy6egEW2yLwpvRvEHnthTTgjnNH0/UjhSMp+H6EA8a+8DWHnwOqw5iycVyTQwxC8nX5GoOfcfrNH94uFb0Y3qjXRHnzFDV0T7Bd+nfn5Pp63IKt5gp/PSSZwYM1P+GpoJu5ps775ccMwTgkTUXHZpEPutJQ0k4ZhJ/O1p4hK8/PcqWi0RPCUH6qjY4pwveirkz9h5LGFQl+9r3+5o95sPVbD+1QJKkvsu5/KUqlwjtqBo3L8s2Vcvf7iInSR6NnSyrydd7bh+HvXnhWhwajHrCTJdlbwPMUgoCKBKESPa9gDNvrBUwPjCfPond+t85Y2rSbpIIJ06lTozRsU9oZ5CBUoboMZfKT+mh3vgVYpc6qX3UCHTRpiCJXhC4sYZmfAmlw8XLGX6E2YR/8mWs8Zf1yhjSEo/JR4GBLCf9YI+qUry+dANDIe1vj1aFYrwILYHWeE7cNJiMu34kcUTfszciHfsaqWssURPymUYmXuIRwvb75nvvUAFxmpJcYQSE++URdLWJ479O12Jz4rpnCD2yjim0DS6x9h3puk6km06DIO9270RjGb/AzCHxA6P7QZAX7p36lxS4Y2v3djklMKuE0y+poLnqrUtm9GHTtEyr9ISL9oeEHXW40Zaop+GLt0Sb8+pYftQXrX3Zezf8IJTHugsySMOPFMGnjRoKXDxBFN4Mek4MH8aB1lEsaBdBmvpNH4xewgiCrBttMAfUWUCPBS58ThhzSsy8ah/iQbfg0MwwrzGedba3C/ODhKNCmuXQO7/27dxn8S7LscyxxKHYtsIOLzsCp7Ip//V2/lZUrO7p9YqoutLeLdaS+/dEJ+Bd2sIrqttE3ZQMAchOWaG5fXhH6exaxFFZuM8vZmZv4Bd+Vq7JX1zYMwW1JwGHy6/DMp0HobpgSG6Ph9WDyo5qmUEwgy2E84oq1ObaS7Zv3gdmPIqXWa7CMFyce5YTJ5Z9ZFkEqfXU6mhj3xio046/CsKEXGcYWQgu6J0h/QpKy+hi2cmReA51Cyn5euVfYltktf4IaOZ8FXlNVwqiHTa7hEF3RdOu3eXGCLUm7O5cNXPskymW7h151HN/9z7X8ZImU0k6gLx5nn95/A2r5n4qE+BF56fiQoWqtOl9XYUrv7vFbRBMaa6R8Vsez0Q1E7mxaBoM8n0hK4u+0ums24hfkbqiJLsrodkBx/nDM+D9dwy3kP33yg+TtiQB/KM3axD8ylhnX5CBwvpyxlu+HJaKlE2MrHFtBFXOo2vvDt2u42ISIxJf6IHWw/VhPJpCMw31ZOEiGnVj8je1GaHrnIuOphHt3JNU+Mw+v4zDMfmFhSFjK7MXZYm46VNAL9vLXoO45HtiJG0Mpfch+4HuMV9YtcaoP2NeCsmUqzHmc+mmNemFYL7nAQV+uTAGgH2RJ5SdeK+SSYa+s4/32RwZMjWulhfTfeHmbX5nBgDcNc9F0wcBy27m1PnXIb6OKU5axyVZcxLKk6Qc7OQ3SDswq5qrbxO9C2UPx0d7tMfN3Rn1mIPGyjUGF++4J5nQtD+EQQggePH4xsKUmyfv1TWu8i7cnChOEpxZDPbOyUbfi6xqe6/r3cnJry7QmhurrSdJi8a+dzhch3ijDfGVMGnS1Ctw+wanDgZJnaCg63prncqP9DHNBORzFROZJCuMyA+BShXDR45MX+mjgNhUB7kX7FgRWWsarPYluS0jfZ/SPtwZCwTYeTj62NEFpiWfOveUD+bS9px9u/QYsShAIihF5ZqT7trzKbbAJlQwyWhTsKSS4vPz2NHJNX24qXzT1JUDg9meRHTmQLqL2s1tKcMm68olZ1/HO27WKQzU4g789wR/d/NjaWYnozUHhw6UPTarS1Cz7mybRATTjBZfDfkwXanx0UhkxDLf93uALXgbZ7v3Yfd7wWt4RuSjXaeZ6PhbY24wBHJ/hnzm8S96EjEvjQak32SEV08Zwa2+73HQWQ2GGGFN8H6PtQ9C9NNnTtxO+0bvDr3vVpM3yFuh84REHY8nrlvHs1R1/9vJj9ncKp0ZETCfBs/EF/yMGocl29o+P8ZKelaBpPICMzdny9aaRaeaJL44NJKuQQDHS1+QIw3GHhCfsqA1abcCrXUGzLNBzkn+5Z0vJsx0cyW/0+yQYSKS4GEWFJKbM/B34nlNiDZKjyLta2pWzzE/QlVLWMngM1x87jWlnaDPj9F70ZUmW91L/nJTl/pQZphtlLvCK2PZgC1Eky2NEMU7m8EPZXDk/mqZRz1H86IftFtFnR69SmF3pWZa5Hc7NeUkHb49l5k4+yc96Zpv9UalWUhLT+GKSa3ZlKPxy2n4de8idKcXbm10SvEtjM0slZouLvfdLEauSkZxMZEiGRHcIWmC6s5jzr6TJHCkr4o1Q4hFWBEUaBzuY5WVGjzS7qj2hcs7YfQ6j/tkh9GN9FZfEciYB2bl342Q7FHI6TSkPSt8unTZyMRrg3s/o2x6WK0k6Zo2fgNgjczUH0xW6C+v7hhlBkGsVIIYfMBG8r2sm4ke/sD3wuaUu5wmA2v075CtEiKXyPDhgliMaTj07MpBB76EKXxGZsQEfBttFEHKJ/E+JKqF7jBcLz/bYtkI42OhqVheFlGaNiEfSNh4AZk4tKvYuxtV3dkzXzYiaJ9inJ1SyUAMUoirF8XEUDL336DEzpWqHeHXiteNQlDIGDhT0jpX2VX7Hkxnc/hdrndgOcOVaXH8SaQm8f0KGP6vQX4sH2jAC5iWw+KzVGrV6q/aU19X/MhFhy5kNmWT1Fu3Svh8SApExII4pgGRFttSkl4losbF/qeeA8tz6ymxu604yzGwUpjhkEojjDGtj55Jwu9uydO2RHYUust7bhrRDnRzaHF8fgePIuDZurdjObKlQfRYfoLgF+eXrUbTy1AB06tc9PEL0XyZ6t7hysSSFp6M604iLXP0EZeR5c+7L64JitLSPlvsNGxkO9HeMFbdLi02A4RCWR1FT3P91SESIvjMZzUNZWq1/rR4Qf44l7wGSj5c5KB5gdfeiUbI4aXK0ww9vMSbvjY/D8sYYZqxZiDQokbvVdkh33IiwmOgEn5Mui3mGtp9DZdT1dRmS9ASBys243IVo1/RlOj5fjUGqV9JbHK5yxzK3BMRftv45KwUEYrCyWNHN6zCqfVbQeDdUqAyw4sdiS49A985ta2zDaFqy+LjQ420+J7azpRaebORh1WswpFoBy8TGioWPtlmUrYtK236mzrBBzs0Z0hAuY5yvCTQyQsOKU1597ZY07eqadZC3ZGTKiz+cw59XzX9IkW4w7SqJpNGo+iRnxSF3eHWvm2/4IOV7b74OOWOPQ4G2bgeki4TWiC8NspJdd3rCc+2FZScrIW2/vGhzuCIKClFVZy0EaP9Zws1QpkVxoA+/MA9az27HjvOq6G7k5SQ+0UXYFjD6KrpYtgo4Hmkn/sH+aohyVE7omtUTdFeC6FJbvHcWxWW0X8zI2L083foCK2WH9wjIJiEGo0fHPs9ABukj2Emht3eXNT3u8o/iAGLVRwKyENjJCll5QcSEalD8XTyoOgdn2lhoMdK4zYbIJ+m374h/bG7h1sm8ch03OH6rPWRQGyYUl6Xd7ttN8kPL4lAZmvoXFRvcBg8No+RrLcvaccPqXLuYH2sPBgBNjx+QUpe3OtkS82WlHkcV7X2mHh8o9bBG2/hqmqmVWFGHWJ17Cy+ouCLrh8QTRMnQvBsRLL/cMOzdRgbWeGj43q7p2o5e9vco7HVdami90ey7CYqEVTxFQ2cOLBV47p78yF6KxJ2y5K+0QTC3jS79x703MCxzfZdb9Y9vi2xxoD3mxZcThrqVgZ9SUWCM7iGV2LgmHqVxgZOsbOzr1sqjSuJa0SfD9Yv2omJMxcDkATR4xM1t6QwLsLF0RXm8iv63owKH3Xau9vco8su0jzbuHBpTj0tqMb6WV/KRs8EwWXq7mcgPnIMwfT9/tJR8M1R+PdseZr6qiX/U0k1JJfM4WLrXSCDxNCfN9VP0RUMyJhoGfOi449R9XFd+b+ivdkbGk2KE8nxsX5vHSUcQm+wwECLBAcyWNnfTKfW2tZK+R5OV4ZQ7GrOAX1t5PyiieWdyddRT3ULwKZoK68mIZ1MvghHAHdyMU1feRUGb2E76VcB634EPNxepOYb4X7oe8dEXV60/MgHUIjp9n0f6kVFzhu71WWhpQak4hf18AsaUZlD1DZBUJnNNnarHVm7Zp9Xgr1U3GULmD3ZIya41X1TSLdLnuThH98bM5E8kzr/RpNIqEXanOeDLXwIAfotGUIhtX1g3a5iBPAj6W56aEFKMvFKoFF5uvcvN61kSkMSf/nb2sE8/7Bv28OFuXkXRfFvKP/7l/035OGW0BjPWb+CKwgC/7m0Z/Oanf/hEir8G8p1p5QNXbbO1/OWv3/9bwTx9zPXPxco8t9B/U1w7ai+a/n3Njjy7yTy53KZVUX594kE9e8gAx5cjpc/l4r/8RQQbf7zbGDCn1zWtv8M5fc7AlXfP59hxU3M731sHSxCYdn2Snb6byj19+vE7Zb9ed+fC8t6tX8vzMPWfzNwF+iZkWFey6EY+rhVh2F8LsLPxTpb10dZ3OAT8bYOz6Vy7dq/f82Hfv37Rxh7Xi/rPDSZ//ebP1PHPrM7XwF4wr9jKPrPhfB3AYLofy7w599B/Hl1/cdXZjZXz5xk89+L/8uVW4ZtTrN/MSfY3zeu8Vxk67+avL9zlX2L7F9Kwpy18Vrt2X8ax/9sLf9+1ByqZ9D/jwThCP6fJYiG/vM9/gz178f+i0j8j3H870vJP1L5r6QETILz92U/9M8P9v9YcP7fgvJfRen/g1Um4f/bq/x/tGP/Gfe/WouljEfwa9XFYMzs7yezjFm6/l2M+J8XeXWCFWIBnlXpszZx8tjYw1Kt1dA/f0+GdR26//AGpq0K8IcVrOHfFeKGdph/j0ZRlKYf+/m/rh0B1q5q23/e+VdC/uNykv8M9Hnxjdf431Dmz0tEHPtHG3HVhzXsA1KkYgB4rTteKXjF8xv4j5F7jgmfn7wBZZ8vwwix0rSC9bExZLtpFj9bH/b6Gvg3H6UtddfczRux3vcOcY4VWfbhC5IrawRbWN+8mjGjGjrGkLpc2MtVsL+ONQyM5ohyQdnay5V3EBoWP2QviyRKwAUOY1BP6nQ/o6YxfzaUnJBtnvCUoLY+0z8wWcN9jwbPVtlIuMXZYhELjS20cxx44WCkgnpevwsGXGMfcy5kVCbkwb8xD5raHj5zSMzRMc+P/1/f+7pHTWwEXkuUY8p31FsRhPcSLNZFqY91qoZdpZTR4fMGcayiKWu2oFRECKGWs475/SsuD47ope0dQICio8HbQ1n5zVWP/dg+tqXWchPG9YGYYCG1K/g+fpdYJfmmC49PnQoVxrO+OFDoQ80Pntf5oly0Tvcr3FS/6WcWCiR7WDSnzvSbizzLWhtnacN3aG0tr/heaBfdyMk9JU3z1dMfbiT7I08p1jMIYefKRgTHbXs44rJXVNLteFI4EXyviHpn+CvOrU86duTY9ZlC1vVDgjJ4TXgX/2fGWK5/DwNp6t8vmqeudL9FVCVfIQwXKsXu3YwUnxnLVwVShQIUIGKAR3aHGOLzUpzlo0hIC4vWZ15dTmVGrjWx2udcKIdAFL+YRTCzKi6HqEfft1AsIxM2b5CNYOngbEywnaNygihfu5DtnFEVQa9K3LVIyheUIBSaGH5dwPF6GWmVFOX3jcroMYhfjuxz1yfOlYK4yOgVBlMGrO3PHGWEfLXQOvHvfH35gvMymOY9HKBuLvuBBrZ01GpzDpvleKESk5HO+EwIP4/pCxswl+0jabVnh6TmPyOg+xohVo8YSPI24clPPZuVn7ldi644bl7v1FgzgUdUfiD+/IDG2GxAugVISLE5wJ0xumHiRpSZ1KYN8QXqs4wIpOLtxYsgTU+8+76Dxiwc+67R2FJj2E5/UwHwr7FmrnTmR4dKXW41a1M8J1k7AhYzmjTnnn0hW6aFX9ERxpdrHVpBVWy5rTjrcfTEusYhmqaBklsAkw7cV/GYfgqNAXs6FFyaSuP4+tWevaxiP1Fuw20lhNay67kviVZ5nT/go/loZxRVKHkac/FrQyluJdQlX/Svx57u9ct9LFbfTDsnrLmQeXZiSilvLDs6JQMBAHHkdrpHUc1d/JiOpd1YCDvbsiU4X4VraZKuMHG0vyLUYIQta6IAB+fDQ6/n7xRE+bCuPZ/Nzzz7WtmUKEKwvunXJKdcmBKHknqVo2Rs8Jg1eLLKXEQchVUx7SjJnM5AHjf4HLV64jvnoFcKcmlkn3il58H+1liV/DQ4GpqCj939viG0gilVTlYJqheTjbBfRzWr4xlNQAk0ML8vJxDAsvHpTY96Bc4xLAKmzuLzzV2GCSeYHHkiyROhrg0ULT5xVK4vZsLUrqH4NkwwaEk6mSokKaT0ixsv3lR+hQ6KXj0XL48bmi44V/rN5XQ4fYnqO02AdAAHEqhkbDun2knHaG6EX58n+p2deGHJKmgptdQ88iACtOj1nIAc3LN8V781F8PwHg4iQY3Pn9gpqlAnAZNRtYou9kEQYe5vgdfZ5lDDvauyBUefsecF8StPh/Nu2O1Lb/2+5WP6KQ1liPphZvy1iR+6QhJ3NRbg2DOdkTqj+134A+OlpokbBGH1SAfdIKPl6/n5qRfSD62Xr4Whf8/wO2Pt38fU7pXmV1f2tUYshkneiagj+LAFuqheWaRx8Vh3WMFRYMuzXnlyvi+8tDfzMk5HrqdcpPUg2KcvhALf6IaHtz6F1/kZXGAvf0xwDpbFnKWIinpJmpppSz84swUcISxlpj+GclMauzgaprjl/bPWWPZa8wa9k86Wd9hThs/ZSUz8DP2XRnkOv3PEuNUucVpq30Wkco1BqwyY8Dyr1n+wBPM1f4fllMv4jUkOrwioE5O0usler0vsk4wJi0TYl1hAjShGod94EsbAPEuKwu43HrCfigokPH8ZE1OLB4Nz9EuVkcFLdkFZZ6PWL113PvU1OFH8elRR9k4trFkW77dwcb2t753JeZlRMdUSEPiGofxCtIGE1EOVvIURr3ZRtK2e4tAAfuV+t9QiP5oSXSgnqk7UYUM2PAUgRWQ6jy6mDr8DXzyF/EryBP6InBgGTqqLZ6FqtovirgRmvQVpA5Jb8LsHzbASAzCsr0AHGsk3tl9Tw48y2Rr4aBEwX+pGslvkX5IXu4wQUgduKoH7QZgVrz4hsxOxp1RViy6QE90MGyoHfALcdJ2pT5g3JqSStYFlL1++yeBkSJ03DU7CoG9uWMNb3N6HaKmULI2053Ro86bYglnY8A7BCd7Zm0bj4CA8McswJT8irxWG6Ii/LMxbQKoo/x0iSUxWYtTM80baee5Svx+OAu7i/u5CPnfxDwBq3MITWdQV1sRLzXzox7uUeYzlCgR6sJfPGIOLvW14j1/bz6tHqAtOa+Mby6HwuWQ+simF3/PbFwjCfA8hhO0P4Wm37GEgkYi4TErJ24TILVsQqvBC1DG7EUaEXhXIQmTzt2ScR4J/zkYHk8CArJTUkby86p1f0s99nE0IOnSIr05RITFF3tKyMr3j2w+WTti1tovK4WbLH6D5JUilkbjy6AMQ0KVDb3u/UvSFo2vcM/nlCMarMhFBEg5eStsAeDJO+ZF9BtvKOoCwHbk8YVu1+IUXDyIc6iyUcQyyB+OliuUaCITbB6+7CbLz4Rl3gFzLrzrnUp4g7a99U/CfmR5A7pgL1wRtyuK+J9Fg3de+vd4bFa/5gMKOvc/NGvG9n4o3pwWHSVnWfvgm2vzG54Ft9QBh8KDsximEhtd8k7l0ASdmIPMv8UsHslDz3merLQl76EIF5unZM7ym38WrAaVzxJ/0ycpW/YE0LG5l4atxBSYEmqmYyOc9rb9A8/hz0JUIs8O60n90EPLsGezBMUk7RZM2FSUgGPPm0rJ69EOPBPh+P6saC/2SCN36Fc0fYolr6CqVGx2A/bL1ywbVgV7QNEYHT6EklyrRw5Xf0VLYKkMOIcYhLyc57MO6epi9DgUwjrM/AIKRFEfxF9NeBLpYjS3KbDmzCOZ0IhlEt2sSOc/ujDF+HTX0QuOnP3mxP3Oad6Z2YyT6O3eG5ng9jBCplzMgaUPUeC9UA91xBiaKVUNvixBOhPczvHEEJQWE6TLYwkzE6uT0GMImiqd4AKwiSrQ13wO0iywdU9oip2tYzAGxrLEbtPpiqZEq36/jC/GfeoAudw9bznVOvPichUR5R33a/3zHHQHKH6Vkiqd54MxUpoIL+eHwgL50O9l5KQIjb+aHSOD+frV0zxcofB/yiw84R5B81vrtyHQBbm0m5eWWUMIbUSqM29NuKWsNHCp9lNDbpN8eHzxAOg9IrfvEpRmhxFNMzlVALEov+LHwCrQDEo1DpvB2InDSnTFqHNpt5tJLZZ1KeCiP3L31AI7uw0sKFhttvlKHjfOsjjqS8NG1FDJ+XXyNE0pIJqe+nV551xoOjdwF4uAbM3NNaICvbj60AnvhDjHQkQisKelXkiKnquK1MPSEt0Z2TTWhjdH4hq8PyFD45WMp5Mqkqk8c1EgD6/R9OkRbAdsy/RaiJu7INoIpWajlT0ObMXVayLkl7sb3R8Z4W7KWyGsgpEgmldFOD1W1c1FKxuDR0gZehoN4A2Ya7j/EvK1ffz2BoR1Em0zGT6V0sta0A0V32GbJJ8/rNs03Rs0H+35YzRlrSrPwxcLgYYr/gKv3n1VCl8uZQsItvODL0rW1s5boOu9GWtdA9E1hV60Q6K93YWGjtnBVyNRvH0idnAKsEVOmg5uYpnKxcc6WMxl1UWtlv/RyIHYp6oQFQNHVCobS+8w5C9DNfrD8yu+NcYibAl11RdiJtpspI6BRg/xzijWuggFSo6hO7Gtjwe6NfqXM3UPGNJTweSwkEIxdHQMrF6FWcsh07CbnwueJ7nnaTt2n/qt4EMNsFRPXnalJGJ1koLFIuu5wScYOfbNQYtXWv0oSUr8cKu2/83Rdy7IiO/Zr5h1vHvHeF/YNKGzhPXz9kPv0TMTtuBG9q4siU1paS6mUDFtywC5KY89wp9gWNwA+kxHxX/ePGPLjF0P2FyKdhjqZrQ6vP8cJLr6Q59g2cIAoaU0N1FHZxaOyWKEF1s0iDAvAovX6LL2J1dYvX/uGRyePcIMp4HfiGvC1gcluUf0AvDS0sIJXyahjoWbNCfsbk9ONb+z3Xwz7kswIRH/XZVdGp4ZsMK2uwHhZgRpJ8aaUkgkhoCHVki8r/kUm+d2BV0LVOghnoBUfSzG64pmDIWbSoQScQZZP5KGYfp3RyXjMWv6FGTR/w6NXQpCazvUprCxz1r7HgT8hm7q8b/IJq06ZL+0v95CAsouIsk4ARu8KxNYCELRaryo87VH2m+xY0hfQGFt5eU5xv/YN4pT1R77FlUNXzwnF7tSK18P1i20JlIIxqO9gQPRPCTzFSiIqpKPXl3jAq9luLZlYz9rqWgz9jaGtBudk+ctf1pGvLLBbuwOSa6QYiwkqCdra/MPlNMW+MeCRBlt6Xpy2/z7KM8Xnr9e9pLAtIyprm0fsbhn134yL8BT0v3sFvLbF+uH/xm/yCK8G442oBPGrBoWBIrar1r6yYsXGSjif4nAOjLSq7vR1kldxfBgj5bW/zuYcDhDoZY1XePAKQ/Ez8GTJyT/g2+IRrStRnT2uh5sXG1mF/QukQL/gJ188kxpHUsvT9oS1M8ge1KXDMO+PbZachWssZZIr8Ga49T8vCxTdv1btAOnT8LL09u6qEGnrjs7+IOE6NQXtbTaTEZ85qrXT58A7NYDtRA488AbPVNW4FM+XG0srJssgB0QFcai4zIdOaie9vsLBHrHbPSC6uONw/1nR2uQh4o7CMj6/l0Kgs29840Kqpg9ZFe+D1yx6bakMvrqSAXdLsFv8lXh5RowG/7zm3LdaxOInMH2rAyDUgFMz6jdmArrGAoeYG/ZQqlIS7pUzQqX+rSssOTT65N5njR/++LV7qh7t5+wMve/rNt356AzXEn2jGCje6l/jWjikA2rsKK+AOxTltS3eCQ48PnOTQVcRUyKGXmkA6zJTnTezO6Y8yHmMtva51vaqvkaknerA4uZ/FYcgsC3kUxgfK9MEWlBHr5Z+ozaoKsH51bwDoL+WOg9EBBUt1gxDjdIrReyyFOribsL2IhV/UjeDys+GG+QgH4bxJZ1acPx8KnGMXhZoywZPSpMSQ0m5QQylpIvyJfpDc+TFkMd2krJdHI8VJNxiVELaAVQJ8Ity+WQv3hunwpwszxxr/Muk5JdeaKlgH8wCKJxz83XYNJiz+7JKDSgU5YO4Q4NP34QajKRsU/Tuqd/fQMvGZUCynCall4Ws3LdaCpOsXaaMgbcETWDx4PoOW8KCbUtLH3lpnYtVpzLMgBicxAiYOjLqaS18U8hrRjdgwT87nqmpeIasX2m5n/5Ovkem9Y3oPJupCPUZ60v6JiAlG1SB1T7Aj3l/oedixTa5NGOe7ekqGZWNC1eRPSSSweyeMz+OAlf5Hn2xkS7IKZHAN7SeBaItCEzwC+XgBsgXX+wzaZn8x3SKcmku8+qlk/dldmkWSBybc2147rkmzteZ9axYJ4LgvoA+A0j+iVFEeJUPli4oeTqmFbVCLQ6zx1xhoIJXx1jrJheKbDqTMYqTSqha/gB1oYo6Qv2Bt5lNzm3ISMeJhtj2r+F1mWEPQUEY5ta/Ing+ksIxmNTYXCVOQRzfuzYVH7t9SU7dW2f6ZXChql27qB4CxGccMoLlEukIIJfOjX+Pe4XtNUwYlv1W5aofmntgsPG/YuNPU4AW8Fl394IalA5yPtjSuyCgv3FV9dJ4jjuV9+mAMfAJw3SS1xt4aeYXo3w3xEBK80vjff1Qu7+mHvAX0kZZjPhptTocLA2QzAUL/zFi/v2f4ib2aYe5ilHQBSKxDXV1nvOkdUJk0e8OWmpB/P+fz38G3cXJEBIQ9homqCSdckGIjaiCZX4fKs471u/2uX7G+wv3Wwqf9VWA8vPDf8dKv0T8U1mA6YjK956w0CeeJ0pC4ZTeaHHiGvNh0j4t0bSJZSN8OVBzWhaTVPwHBNffMHkv45+odrBpKX4xttMqQvaEKTWjok5rbrY78TAQmFc/rPqZ3s2nV4kioRoQH9ugb/qNiRp+5o9NBHGm38nDhcKEe6kyJy+qZRdFEKy6tMkarUxEJYn7MTorBGkQ2vFMXHh/pArsbMui+SN0Un+1SOBEE/3Sr7EAddiWG2VHemD4YLESn2B27dD110DQcQGRuGsNNbbCeThSv65SGDFEmeYfgSVR7iKYW6Gg6RW83URCfs8sRIoSZ+Mvvtim45j50J8G3MKQmbYFUBLvCS0TLuPwVYviumDr0XCqk99udfxo2+jEKEALLnnWd+X/CDY4aOii+6/MYjU5G5CZV5VwoW93KPxtxs25ALNAr85UykeBu1xeYRdLJoXy3YANzZ+fbuJWKuTvyuaF15Zw7y9Wsm48cpaDdJgHVjG1SEge4krz3etMeBKQwQSlxzg5O/6Q5pKBfTF4sFmJnToQPd9fuf91i/9TAWSryZh1fU2GONpg/3OAoX6MV9i1yj7ggQgia7wQ68Sa6ZBxygQnHh42ppAFKOf8Sm/cCTsnh8FpBBshlcJ6JGDJ0Q4c//WsrsduhIovxfdMErUIGTPdY+Mi7SnpDZp2Slo0n0eYwO2Dg4IJe/gxOKeITw4jg4Z7O994VxG6HtgGIfobZuAv1gePbc030M83LDgl5jLic+4tkrp5iZJBNa9mg2s89sjAfXi8Kp2Dog/ptRX3JdeAAp1RtrUF2a72uy+8BqImi1DKfcoD6gBawzYODM48OGeHp5+grV+NI7EQJhJLco3ZnzgeQmEnYF3Z/wVfsaFJtCKX+6tiHu4K47Jzz4+m/qbXpuDAg5Y6DLoF7MU7lqiSo/dEs3z0l/LjrKaD+sMgQm4bXLp4pf5VkfEHqY3wcmosCL3pgoMD1M2V8HevF/M79ndwBZYogiuVcxo3UVR/Q9ODWivnLsERDBGpCQCI7xuZPetCPB47V4KJU14P68OaFADFQlphWiJpmDo3i8FPVsW+wJHvtfhrR+sVow56i7VGjMfz8LTK2GgnSiB8TmUcbHbJmoqWhp32pIHW8izK8nHEmvzyQWAx4i0KFZcRNJBst84GMpJ4fjdmTHNLolFwKTUVKMr//JvX1dek9nhsavZWsM1x3yh9YAR+21pYol5uy4l/yllEl0fkwGIsf/1j4/4Xfl+8XFeikIXmc35aRzS8+PTtEcOjvKBzQOPaNNTIOGXy2vkM3sDpHhBadmo1cG/QqHr/qIQq7o2h7S1Nx79UYUK7QUwYsN3BoNj7sKPrUH93TiFwoNkp9P3K7179nrMhXOVwzPrg3M0za1kBHR3F78ctnR8a9reYaJwn/42ORgva/htupW/dAOwD6NFYVpW5MfAzkw96v3XgeiwvxMFray+cp96WVi/LZhU/HPyQiUFq7rWiVoyxz0fgwY8buaQgCa62dpcaKw9EoGCy2iNxZuxdcl+M+VZDdRQUkJUu/HJFrTp8xaPPMDBl+3yfLqIvzrNU3kPBv5Zm4vwG78ctrLQttWP13E6aWG8cZEK0/yIW54viTowtbt9HK7n8M9ruDz6cqR8hGX7pJT+82gG57kFXhHDu0rGAoJat7uYCt4umtUIxiB2U3ZhxORjcB9U+zIWZpucp4e9+eTkfDbD1KIBCAVXBP05H8FPK/B3mRpxyFstpbr8BPTTqqorXq52nR4P+w4hyh4wxQ7d512ASOFhdhhEnym4ee08dpNQSz/T2YQRQZpfulSTYib7F943Y7QcxHDE21Gcbwhad7I5tj3owpPRqjJq5Ayp+9QvXYZUfv8KwYiXiJAOvTUDu54l/VOHgGuwHj8e8zODJ7qFRxJkNGPgwr7Rlv5vO2dvsKkaCJc8Y/5Sw6cDtVHYRyzBY3RumHvS10o9uUJXTi8ilCItqQC7YvnBbaJGU+kRVR2UQ22UUkFoJ84kA1ZL338RrUmbaiifyG6enJtjjaetijh8gfoDBwZSH3TjUh1VfStTYAH241TOMVt9lZrLIv/KVPhZs/wQhpBhheXyK4VDWy4Csz0Z0jbU1TOMPyhL5b1TVdOjSYNgIPan0FBkwT4NAt91RXiayXF/M5BK5Y2p15GLyb2ZPS1tnU+0+wfd/2GfjH0AHAef9klVNHyiFrxDfhLkbyGyJ5MDTnbgSgMPU9bsPckoY+No/ps/KCyjum9UIUdyMQMxPIP9B/J8+MC8HbSJxWqTuRSu/5VQooo4TyA3wqbX564P6e1KI0Vi/DfuSL0FlZ3+vJRTsryDZW8EqGqMjwUgCriUtvvprYpli6Y42W/9KMncfYo/CAyI9XoTQf2jcSpnyuWFAUu3iwaYLd/CDCkha7o/WysYvYf+kYhmQJNe35geZK+KgHyZ7oEk+G1WPLusBTdGlR5+CDe1yzMTProU4PvNxfaKeIW8Zg3qoEhTpiVgYkUvP8IYFYRbe0mtvJzt/TK0hxXLmCtZTMGlb6QFtaOW3hAF+kPun6dTqsWWFTVE3ZLN4qyvWo0dvWQxz/c0wHMKaH0h1ikXJ7x6cgww6GxyDi8V1lb57MWrQa7m4VtRPN1xsar9qnwlYOBtbZs8gYb5AZvVXI8rc/XwMSjxeOy8VNSzX6mxtY3fUBe/WGpfndXRazgpoqPWNH9B4QCztV5FSWPI5yb40+1enolISvTKGnuqzVFoSl3/d2XCYEH7vhmA8nqm0ZTOVMN6ycUo1GPq0j7w+2dzquU0VcfrwJetnXK98YeUUmFSM4odwvwtKFr+mkZTxjd0mW4eHOrKNN1/uhsVVYnNpi2WhJ37Or9cM97h+GvEAnbeU0/6LLstX3LwZyFI8AkvE7jaLqdEqBd/Potps9NepT537YeVYjfwJK3YcX+Fh8OcYhMxEl/nVAkHwGAmKSfEIunNGJtsGarnHfXjJBUg5orT1OoQZ/LrVlB9baPlQ/jaGvQoHJhoMi6VzsJA5pCi/+6ANt358fArac3NW9uI4ei+dLzrCwMKlSJ86raDEHGSMpzA+H29Hny5ZBt4Zr+mlT8+j2VKkpVMCw9W/iCBN+gXOM1MPInB0tItffYQ2yXW9KudaNkeYOIofOIL1PXis3PlQ36KUQaBj643m1Ip9rvWVT0pu3DTZ5/1fH+/eZDlDEzBa5tDaeeZ7zjQF2Q9/BIocvRcfHIb9sA6FkPQefJulkIGoqnYImq1m/Hwop6TYOD1fmKXDA+fvqjmNJ91gHMqHy3k6peh0mFEigj7f9cdMMiqqAm7ERg6OzdS9ZZPepBNlWw3xCyX2h08/VfDPHLvnS30RKnk5pMWxeXb+S46gb+ywxZLanrql3mghtR3EQhKoyvmIbBUGrqvfY7nYEbscE3blHMnCEfELVzKpWuLIHn/RiSIYG3vTnNEiJJ81Lwtgk53DV/Ob8dD2/3IRCYzkuvmTpcD7UbrXkVORU4QQ5oykQXM+l4/wGZyeDAXokIC2WDTU6+I1Ipg5LPqSyAGamPMqwKu3hlrn0IHnRUIN6j69R/jy+3cgMmqjP1KuO51XannjuIriNYvTm/zdxPdsFC8qpnNLh7IeL/CgduojjJOkxPWjfWgtInW5vpajIhjDFtkMoKQec/f5ikpmR3oZ2koIG4ifFfdFK5gAn+ExUCuEn1dV5IZitwrMlXlq3F5sgBsINsf4/U7p9R4Q2uDVqdZWy7A7I/I34Aom38rGTaZGg9P2l3/8Tgu/XqjhvvBqqKvzgRrf8ekW6pBWDAkGCsIIoRG3yaPg/DVijqXjNJrXR7ifo4y7rl/yQTVvLENkA26yrQ28KWESbLaH9s1t4KREGz/7gVdaRmcCphyM0SQ9lOR6oA8lu31ihYjd2OLI6HVQR6T4ovdpXPID3sHE52Y/D4+wuqXWewfZZdotU2XPRNGb8kjAPAoYF43Zyz62ovBsNt9HZUk4E5Xa1NYKbM8GFv9HabJfy3+3k3tOR4oJ7FdMmRY2hw7OOu8mOLjnEL8hN4fwuLyW4ny3TJ5I0HjVu48N9ajPJwAX/9lulPtJHz1zGT0GeIT9FLnqqs4yuDrzB+Rf+GU9t7wgoWduTbGm39Xraz5/Wa7Sw6K5xtM0CX8MZdvD1QKeXUs2LUCRZogWMyTtveP1rfvQrlfXMPtqfU8u9ELcaMzecWRrbeTR5tFrkXUcUgjf76A99BNGJZxquJVgwwcycMN7oRXmO2dum9VrMW47lnh3cQj03+lw8ry7n20FmbVbU2vjIZ/A07uGUlpY5vxX5gMCU5fZaDfD6sAu4ITvFd8hWIfPjbwiqQUVT58QqasXj3+UI/sYkIinDLHn0yAdan1L2TcEs6k61SnwcfFaL0nQ36boSa8r09RzGGageeJjbFeLMSkIj1zwocu/HP/eK3hKfNzHs+VVC+QCafMJS0+cr9pX8AxsDTc9qUy/U/cLE5kCPz6i+pc5Pto61MUTYalyASf1tvtq50IOd2GIoPL7Vx5jOct+aUjhuFIy1MficOQ8X3NwgdZfAU69DCM7UR3U2hx3+rXax42Oo9/YkWm9OCfKEYeq/pulv/onhppGX3pRUB5JR/6avfx5adThZxW/l3A4vtlPGiUHgjuW+kEUK4rcMf0iY1lH3F8IB8CN03znu6OkWB8GypNYA5oy+TDcjG3td03gHn2BslOVnM7p6mHh1PPHWh6q2s//pEg6jVACBq+zrfU1YaXAKulQqHHNWF+apqBQkTl+aRdF1clo7GokacDxJjh7N2vZ72fyPa2IiboNCTd/RCHVoAPW7su26YXc/wqcbgovivxJXpUE7UKlo8BUWIFqQK9Oz+qz5Jnt4Ow+v1Okm0sS3wBZqiN4jg4CvpxZVettV7Ij2PF6WSidVJ+fcq1LsgpIZtMEpEodTHeUMMjI9hRVSX6t1U6zlFD4iszNCN8YYTDWZPzN6duogQPtm1h3x193vZ/6RebC+BgghzHR8mkXf6PHf+jwN4HdLzLp7uhnz1AXmrSRmqS1MB6kkN6Fj0RbxT+OWnwR5fy/JKWF/5iPYV9nDmQ/ii3gX5KDr2M2O2ueo4/ozzO6Y9GFSfwJ4XZ6HBnvr711BTM6Xfaxdmwh5/ts8ZLBuVunKy+/LUWjEtvcI7t3EmzywNuYA5E8TNy5HLyY6PenvSGHab/kgIM0hV9g1jS3L8zJT1BXD0+tCJYdBUR9U/+iDGjnOYevcaHdB5GPrxNk+kBO8/0/v2GCYzIN19iM+ABFGJAbzyiFvfQwiyjEYdc6LmZ39te6y3tQ/QdNz0uUM0lq8ldvfu8DqJDnX/pwkCnpb5BN5UrsPAcUrEacvcZ30OPM+ZHsJhestZvDq64G5i7kNWnqnELRMjaBzYBq5oVB4291F/gDXxMHdpudMumr3a4i8JfgCZ2GUqDuOXbuyqdadiNenG+lT3GZalydnwqX+X1CTpJua6Ai/rLDWVrBxsjwQGENXOCeH7cgP18sa7ySQkEqaBruEqiohP+ovCp/uQn/4ENxPQ9k1z/9Q/G+l/BmbuqLRT6/eU2GxSKe58IQy1pKlwAyEt/Wok1A8NtzCp5RNxjCVwoqG8pnxkuGWwG9mGdojcnVCH+WR2r4tQxoQi/S+/Kxy7s5NKn5q/cARwcaCbKCW0sVfxYHWAEUIynmOZMm+N8VUb1fdQj8adIfVobmMvYxBShp0EBYJHpmFRlKvOzn3lUcCsQquRxESkff0cuJyTIl7JZUsxJTvIIzijZD4VRfkAHycKlffWS0ZDFTS0cvXyDDyVDMnljTwx5ZMuqFrfv6+MajQfHby8rqvaQdhkA63gVJh2wP6wsKGe43KhaMa8pn+xCdP/Oe06lm+wHJXTbfxVeTfSPljmjXRIt1wW34yaZ+FIzMG8ajc7BrrvGGzaXQ/DWWRsq44t3BPa/n3ynNVZ36z25qUpRqa5rcUk5qmORujNTXsGiPG9XRUBnW+6XIbmBQYnLzzTT5lkXI6ximupx4+9KSvXX6Ioa++zL0Ux/+8PS0VSX8WV5+hd9aupGWo5FuDY+kD4M5lS4yekhWKh+aQmsJQu0ghyTTJuS8GUyzKMx72F3FITiHRqihFQrxKdV/9Hf48rY+3xNQ9CHmH85HoORHD3KueC2/b85CgWy8wdVBNOC/dXLwZlIH/OVI6zoX+e22xVpy2KN5+acahDQi6iJHvQnXweVItimvtSPhD06F54hf9deh+bK1uYK26vv40feq+AYKb+Jwb1+QHXKHjblMTvKCc2oPxfN/fquOH/yWZfKSnY0fXBSxwYgeNjjfaN0Bcn14/EW1F1xCGZIqc4MEBI8GnlAndl24R14c4gZe0SAFD8cDVd/GwWl358uk2m/6Bc9U0nrXYuU8wJSt0uA7HTudTrs+QKK9K/ysbItil56mGE9C2uZ35WuZfbrzHIZgWvoB0j6xxwT3T7bvosaClb7Y6By7p+dAXmSz1mIqccer8OIjBtAAwvxSXJEcpwjudA2mzbGOaBQPdBbrpvvdnPdBqVdn3SAtKwXhINJIZMBn++0NJ4ebVgtavwlaw1gk1ZgtkCNlnnyc7158Yvdbm/PhYHrmdBcGaY+JLb9kXSVkg/iZXlv1xVJkQcO85+YhzpPCjIL0/KwOL5NI+W1U/epybAhCaZf8QAy3Hy8Yg6uh4AaGj9UaUpb6AA0TXULzRR22To2Uq+u/fzKzfPK5mkniTp08e23g7Ioks2lkCb9NpH/5TG69BR2NBXwd+UEuLhq6O5JbZ7o1GCbvif5rZi4GWDI/RRKPO5GulHb5demr4jeTqSM86JcuGsRVwl+8PZDLunX9KcIIlSNUMgU4/nFfAmAtTdQiOEDR3mghe/kRqQlHKp+BCsz+787Kz1nbUV+Ov6GCFEu8eFCn1clnnw7FzNyy1lId/m7dzA56egNT2de3W51ZrRiJMlbQAIm1hhsB91z+JjHzmOE/Ceh5DKhFnUla47zPLu0SctKkgjAc4CgLbsngAcE4lewZMrXS+zPp4EyzlsMhG5BG+WD26oPPnuCejTTeDOOzrJfGCwid8amd/UT8HYoS4o9V1/9iDZO284yfkfvXRYDbsxaUX5H04q8sasGo0JKzc5QJ3koeDA7lHtLK1iyOTFjyfn9j4MPp530gBBGaZU0pSugXQcKk/F4ZtEUdDlkCP1nUETrfn6rDUHRsIzfcIJsihUdHutFZx85B+7RTGt9TTpq/EWkUzFUREUVjoUUl0RwKD07t2f/mmPuUI8xYN8Kv+aR4wsyWnFqd4+nYIDqsFbLNKdbwAjMNl/53UgeSfcWJAnbdM8ML9NHSRPaxSvZlw8xrGNewMFK3XCES58qKm+JNQz6EZo32AumhyPLhJk3LPMyNXdzO/Mu/ihQpkV8W44fzt/jjpuHyd4P6hJ9Jw0xx7uUq7xuN2d94p1/KanmcDU45fyZFZ0NddnCxH8eEBOaHzwmjXc5XvQmb8wL4bHaaouMpYMUpofmXbkaQLN/SxwMrUc8rFGHjiSmv0mJSX1AteC2+EOb7hdNXYvYMzITxHPM7wvhLIWf6++sQLmAGSUtmQ8muDX8+LN0+p8EbqRbE2mXxz9/UL9muEvQAp97nxiLUqpShntH88ncHOl3doZeZ3xmn7DCfRPICm62ZOsWkZBWu0hsc1D2kLOYSHN/7nQbKA3DSYkSG/A0mnyFrSEBEWVCmb+NOKYWKmPpS2wwWoLxv0JC4HBy5L5/ueurCy47Po4ZtFh56SPKjEm/STRRnnh62MrxGVwBumbjhofK6IvcyVn3z2zwFqDsQrByz8mJRur45wsky5nykptOEP95G3ijwCb6QkZxbBd0pD52K8P6vAxsYPC9KxcsmS9e43RyC9G/MUi+PIuwHSEy2jlr7jVVX2LSfA3x6mX+KJ8tiHbgZqVPayrAyneJkUTYK138kg0R/eUCT0F87uuRdXMTH+JexDRJ3KECrqaz1IA0hEDT+VRj1NGbA4ju0LpwEnNRidwBMGn4NFST4h+MgbqR06RiQWndD8YTgV1V1vjWuSqZj5Mehg/hEybkJQTz33bks2ZSLu+N6dKbW59W/Vt6knH95dJBc7WkVEb6xpd6hOUhnj8UFRFDDxuhenmq8oEb+Lp6X+AXz/i4StDwhHel1rM5E8Kz7Wy5mq8CPtkD5GP19CoOyQYUovYEXqDMK/pSmHGgyY+DFpdTgRbYjM0s2y4YDus2BnJmH/4DvYD6b+/EDCuXgcymDc1Z75QWmYYZMuqhHL2V7CWV/nDqOGs0hMG2W39d1PxRXg8OEfw0wZfCG7zpZ8XfSDbwVyJ/B36/91Bg7kovgSo5fMxO44oe1s0OEgnEcmmnZEJ1hwucDK4QyWheqQ4z3AwCpchiSDOVxrLkQ60NDW4Nv9aHNgYOL4QBBRGpGcwnjxm4ReRAfQqUbEKo68XlegwnMlLU15IgoXDIQuvWh2DHJGBln6Cdc6CUuByGXDiz0fMfALcURIM0Cl/H18l+BNJWULHNT4HnvJwiBffq3U7tPoJIt1f3NXH5iyz6HV2lX8dktUHTKEgVijP9FS4t+NXkI0of/rhOxu172nQo2h3zQB67ZAJBqP2dO3iLJS9QTIKY4emzpGxWIpveyqIGvBPNRSNufLzxqVjc014XiNISAFCTlu+7OOnWlTOZ/mM1gRZXJBl8RVfQU9ccRJvUTiJfWxcJpxAUToNjwAZG1fN8X0aEzTm561RXFSZcXgodXjS8TpZ6yzv+AZYXf6OPTwsEk+16ugV71XuzFN1DNIqgJECXSjwasiGbABME/esh9X4X0lP+OA1laICBzCz9/pZcH7ZH5u0HAbNTKK3DE/CnjkR25hcGvCbiGf7baVDDJYCtgn/3KBcNcXhc2TLK8uoptw2hlO+v7P//GIZKv4AApB1bikrGw/dfyL9625QLg9/uLVWeVTP+wSmdHKd0ApzX82ivBEiFboPsSGTb7SRH7d3EgeOJ0FAxF6V2xEFZH+kNKsX9uZgdH/O8m7bTjSFpX+wHGr6C4YbAnUCH0wnsUbHW+fa2r1RHmjT+ItycMdz4Lv+Gpr4wv5oU2YuEO1KtV39U0bZXg5DYFYfrvhtDnR+h/hQaAOcRZqurpnkHi8AqPZqt9UBCsIZUV4grz/UbF+5IMh+fkHd/x1X5gIFrrAlyMYivc+md1XB6T5siB50SNEz8d4USnaJJZjvJD+xhs+C6bjUZTCz1/AxiBBAMRER6nktN1cK9OnMm1+GdfgNFEs/6cL7sbAhxs6UYi9JhR2v4p4HERYAeSCee3fG+SRp3J+MJ5maX298YQADWZ8VIPhqXP9WgJkNZk+UxE+d8Wv3shFROru/xdvaK1r14mTjyINnkHts+56CGSqyplhUj3srKvijTAegBYIzrdoXRUwoxefeMO1dnU0v/qoSblYaUpVoLt0r215qiNT7E9NvjVV6+EEQmCNQeP8d+J9jd144mrZV8tqlKmzOAD33MvMItdJ9Z8wjbYZ26L+4pZrwOR28Sh0oRvugWTNZAz4od8bcyPJlxGVcW71a6m3q7GV47DuFdsM0GW1L2KRPOD7idTL63Iww/FKlNh/HyohrKfBK9ultbcd0it3fW2iG2olHtGkK0TWUVQz3hOLlJ4qWFJYjTJ/5llweWsp/3bnVZE8eDa0RLHQWVEa0WYtPQhl1rORWBH6WQLyGmcVa2ybC1Tk+NxC0oVCO9B2CYb7REB0vDihVLXrg9y/Kz+wIiEocOHq7YJ/0wUYQUY3pRFFQHo325O+IgSuhHstu4/f7UIsUEq+ntqUw/nDkFCZk6AzSckukIEmWa9H8+fPG0SdxPw0u3HH6RE2j9QTTSKfZgv0a05mrX6+BzW6NVZtJ6r9MuMwTe/Mm6y2udWyhuYtau2RTSyPC8XZVgfsmsxqp/Js7+jU6mguQHySNegcQk+OcdIfR/0VKOExvhXMvLgZcN+jqHbmtHP50a+jxGQqJxviaBNePB3+h1drg9xslykKsXEMuXBAJoob1nQ1cz4Xsm4SJcfXjVeDcfWvEPRX9j+oKMlz9YHsFIaihTC/sVJzdmKmyBc6sUsXbBfWQF/fh9MJruLLqONwRsSox8+GW/4G3LUmAy/eKn24mzCHtMMkFyTfzlIEBVf9PMXToAXa9CwkNhJJmARcVRLhg40rxQ3Tb10Zs8+18sQgSKkxL9ZugAfGO/7NXyuG+gBoM43CtF4I8OnN4C7dzOrOlwSnVZhR87lcWmucbyCtj0AXIqgJjtiLcph/c/WM+rtpuQGWmKwu1n+tXhNkuvl7Rfq5VQXLMQVx5akqwF5b2Z0FC67K3mj+TymZQH611I7i7BXHC2UKjiwGRvKzxwK/ypSoID2KDogCzdBVL0P8PG8lPyOrwS94blQUkalYGNzmokMse2LJvT9sO0THCKHQfokCwE65bDbKMgb8jTpyxtz+GhzzRGOXHIP3uiL/Xy0/obW6Yx3wKZLG96O85Usxcj778JBmOtqGr88sj2W9vx4OnyyL2Scc7E3OSjtbVwAoDwsKyefr7z+NFvWIjpAKaEQjUVXvp/hFfivsL1Kkj8oC6QGk1c5IgbwV9SKZF4Scgz0LBGvTuPTI4+orX12n67QDACmJTL31KJvoOHDPwQjvy6MZMgsLyQ3V8fTc5vlWPLLKTbfMIwpler1cz4tCppPUh8apFMtCafh6G+seQcyiu79rloeZHa71ohk0oIt/vrPy3RzzGUQx7fCZAa5V3HOrrHZW7Qo7nMWvau09e1vrueoD+RDIPK1rcwdg5xTX85FSTz9jaNCzyibyQg4MB3dqfEon/V64UJeW2yUR784TMvt+i9hoRanW5XVhSsBmg8pAfIS66fQ3TLZ3AGbPiMOO5/Y05zfRVNv5LED+ZFrf662EMOl7bJSAnTHFgdid6aEcUivf7HkrzHY89Q9zShy8yA6+de+Wd4bmuKtTODsC/sU3Dz4Q038Q9NXNlntK0saap0u5HxkNR7NlvD8H6fNRVchbi1A/le3YdukrA5QDkzTq539DA4LhTY4/OsrLmX8byRAFaGvt8+5fewD+92KZv9rGlbDTr1KW5cU+ZFMzjnc0D9y88pR6GTEWvomr1RYbXXQNTmQcSo+KQGwhsdP24cSbdp39ikGs1BYzuK/4Gx0jMO1nCFlE3QFdzZ2IuGciUPOu+TL+QDrOlIcKJTP38VOIWPMC4HiJlsNJEnr8/zdbOArGi/YL/OOsl3Gj1gdI0dL5bwAd+fE6sXnAmaXmPUGtwqD7/RXC3GAo6od8IZKlZKKpe1nq5kmHsH2RkRFWZtHm3YYCEdmxLG/UicV/E3WAgW1kZ78+ghc02RXrOAwfVDxv0kgubxbiW1qic0kktpyxf0Vr8/HM8LIjTrFzwdzlF6PBbqgLfJCvdRTVGZvPTygIof2iEu0ZKhPJQQApYif/lK0VKxBZWvs3SwMOLXTUOFF5s6D2D56nHu8xmNXCls5KURYHBOd8LceaSIDz8S7aqHqer/5iMIahAox7R66D5Fm/KQcbo9hWHcLEbmG+LdZhnf4Wnr1xoTa2LlIOoQobGs747I8f7GPb6Dga2SVoVSx2o0IcYon+O00INllJR3qIs0jsJ3VwAKiXxpE93YOdE/vpE/D+jST4YDLCImm1BcKFa/b1JXW/nYhliuq/a40CJlVOYxsTUjWTWwpm7MINgfTG7P+xszo+PRyw8pf5pcvPblFKsPluNUv43dYA4x3LK/wc8CtbqX60qBg49YT+wLhc8Ufl6ZS+0y7rRc/UBFaEzjDXmpsVoulY9mxTjN7TtGoLRAzlfZZIVYk//WL6CbtyKHVg5KbLXeKt4az/iUKsKyUPNjELhMxY5kFpClAHdIXiOutg5Tl342wC6ZqvFHP+97veflCW88+yfM+6tL06WDKjuGn4kl/k5Ro8D+cgTCIPAxbs7xH8Lrs5MKGnPTouDbgt3jm1vlHaqehgyh+K2F068jU/B7GKcax7n13m+WDFRAIhgErZ7ixDibNs3tpPTixFxcVAmTkAImIfV0WBjbAEi2OgMws9Gy4AgxE5II0hHJPrGWPUx4T/qbOyMydzdJ0VMnWfoIxSy8x+6gFan0Lcp9YjVwGfTt/V1qqjixc+aHEpdGj2Fb9lVA5BkzJ/Q+Ei/hr54Nfh5EGQc1vkLK++W5Ef0uJXzXMvbOCEJmd7caAUqSm/3enk/JPKxnDvWkVsipEOcLs9u+LP0zcVSNSTb0GIuQGk6u9qCh3WQzrZ8Mdb2XtegfD53rsnWfJDXXfxEj0NKYOeDctUS6RUaoNi5lCOPnLUKKb+nlh0fwMB9fEBP95XS86dnlhD6eKYmaa+YDolHxb+G1lOsxEB4By9nZD5/5s0JDXsCz2Jh5aqlRl+PFTP6WrZhZ8OMg3YWDCvOY9WpisTezY+XQI5kpetbszwf9i4zjxKCobChAiGy4VOdpE/fIdKED/WiXglq6SCrmkPVRLaDWFTasZXtYGBuY9wJJwryLqyXA1/GV1qLxy1x/DBn9SM1GVmEnUkk2BqfGQ5OpXwn95GsXwSovcrztTdzYC/wVp4Y3O3M0b25eXSQl/nAzwbwDPLwePTCKm/vqho19IGPUVTk9k032uufaQZNa/XOm4lXrAtfiNQwQ4W2qIviN6T0TZORg5BLMtq0EdpCWky2QSGbpcpmt2DLe0cBz0sxdTpRgfYp9WMiRa9seSUd55WnGcLXb9KLp8urWtGDcPiPfR+nlWGg2D6kygBO7yF9RkWo8hDsNB6eR/d0//CdH8OJSmODNxyl3lHqCAgCAcZBhARoTW51ZjQkrWLTI3PRT7u3PulWGLpviP0dUP/DdoqBnOgy/Vdc5tpowRwT8QO/FeZh2v9GBjMzI9OIuHsg5OsFJf27JNovcTZFRXIh2Up0uscCr5GvvamKtXsGKYMTOjVb3m924CftSaJJv71y//BZ2f/0hHkWR/5xn6iOJjFl+iNQh0VMFjOawwhZs+yn7+7dNmlldqEZfMWtn1KJfUwN9aV/yq7PSa2AHwvj5L9Qss4SrCj3/X5FmTJqn1PDwHEy6Ksq8pcl9ZB5INPcOsAj9Z0VItlY8B++xtw3wefjLwtQDuCrE8MBLqFBS1YCPkJRC+AEjN31wfoSsv6vnr6hJ3X5CKag7L3Va276ZffmLSReujdE08TTGjFpifuxlIIq5m4Rv/ZmurMu9v7hV3dXr+/Zs3EkEm/0XNrClc/OeCftDPPva3jnwIvXOez89ywFIc+qWR3mYawccwU7sBzIsuyBrGeCnW56+qE4vXGlFlFfhBX09CQVjP2JYBv9IA7b2M2v9km1HEQOHUnt9OCUz4b1RGahszcJg1/J/WV3Ns+3Ys8tbzrcCp2xwa0rd8wvvy/AFUxrwsHXezzOkaxQ/mv9u7u/85Mm6IsIaB/tqupN7dSIrQ7k/aaOgSUi8Jcq+q/7MbYLr731ftyWiLpH0kO0F5VhvCyzIVXHDcaIFlF6n/spWDW21d18mW/yI+NqqMe7De2U+a+N/Y+1eyp+fLb4cNeAW16q+s7v/SKvIpI1axSRG7brwPYxAto9BB/P4Op4LX0v4UW3evmU2091RHf/fV0eX+QDR0YQSZf/XKv5FKWNGCZp3fzaO0pJEIZszfSW/YFS8PqGATy58SJ5Id+FWfF/ndn8GXkVl9VaVjDUMD7fAUOnyXi7dtc9Rhk03QNNYGB5j2pxcAI/k6sDLMHfW+mKz85fDbFagJAvhQfdXvWz+7LWC/X453/EjlKBBxKZczLwzav+nj+d3pPOGa8GdrGvn0K0tbKV+z/pBYUWZaQzz/Vv+NPFNKDCyGn4k2l2af8/LiBoBuLMir4sXvTneKBhNrifrBS9S+nheIevhyUvvRLJwiy4mZhSnF26yEl9+hmi6ZfL6r2vzVey9rGD3s51WM9dgbx0sQHK6DLAOaOMbbBrglsEkl4E1pMarPVZrEh0f8rvMAYr5oQ/rWqdjZpWraN7sxcWFsDnjEgAQcu5BT5yQ+f1Qk8ndFGjsFjMWdtUy0hz48+7d/D+qR+8nl1kP6LvgpRAf4Yqg462HnNx3Tmo5aenLyZeWLZM05U8KivUrg2Qlcfkl8EKKQfwdE5O08WBUWF4w+PnqsJiXXCRXuIP8CNcDnHzbSkRp3oxjZpPaBaiU6cagQM3KS61Y0KbIYdVnluvXwveEbBfD2ile8Isk2dy9ntCqzSXe0GOSe2KAJ1HLnuLhEQEH+dUnQyFge9oOnsKXg6RjMngJRaN77mbnFgovNGed4LGuhMBUpSxcw29i5BK5Pkq1ph5irSpRB1GA1tUpSUcmLv2M7ZIpP5YzOzwvZUSvsygjUNtw9pp/m3pnxJfpFbFTlCflCrLKGH9cCdP74gHY54sI6wY/vrxJAddCM+EPcXQo7/JQpUkScG8QqlUyAaDWROg38sQ+IFg+bOv0F14oUzsFI3VYU/OpMFIVYpK9ahu5VKqSMS49+T2KdNtSCvkqpXMsb4WDfPmd58MDjV+RHHyUdtFeAz3qEPrvQ6oBjsq5dqUJxTf7fZPooL6w7MKwyNu/wxPWI488GSvFGSb7aKLkdR/rMQRonhv0M3rM/RZXI+incto8JPVCfzJ75gSGnzW8ZQaZIV2SwmD0SI7qiE+CLeQ9Vui37EIjFkQI2iapPdf3uC0rlt8EJ7eZBDMfv5KmLiWoFVsHEIUiMWBn0HAUHcKzFCvpnfkI56nCcwRHw46sywzQ62uH3GzUqpUP66yJBQ5Nd8K6SQJbe/VVBfxJx+b6eZtwHb+o3iHRi7mg9i7aa7W4lavCze32tYVlVIFILpcg7V8HprdymogEFjEV2miwsyvz2AWWb1kGzZvhIgXJgX5iwCa76opnG3C/9vq8mMAVqyUiInO1O8sy2xVFExROtDrbEHfsyttrCXCYCNz9gR6CybC5LJ+/+H/pIxgolzKkGLzOW7Usr4oDD8aDaG7QmmEWGLGsmSILHUdkaj+jU3CHmfBF3WgFYkU+7e+h77ix71t38jjjoVpQwLYBebD4v2vT8BWoC55OVQdr9JGqQE6DrVnfh31YyXxHVIPF33ZsB0d/l2gusLd4HcjnLxwea3CQl/41tMIHipk65AoIr1eDVLn9mZg8Yinlb0RnBfFQieRAbAe9XRq8EoiUJlEH5nq7DOnT5YUrvOqL8+8cpAV9ih1E7VSk8RoIgpvqnuS3/a8voLQ3h2FzetVzuxqPPgqH7qOUwpRmvDCGUAM5fooM0R4nOYH8RV7wiOLOwTunj9EeggN4EyN3g8liQEUIev/KnBt+zgf1P9C1aROG99TensuG2ao/qpun0wN/8R/rNtlkGyoSref4hpBK/Qiv65WQ/DRWbyHFughBPLllL9VsRFI4g2ILNdWc8YYJLMPPgxp4rs5M8E1C2PemmoAsm5egpGFPR6054YxxEaobzE3+sqLG+wxB/fW44Ir+G1JDlSs1Aqg4yi3ZBqoT/QZENqBxeMJ5cSVC64zS/s6zF1NBnmOM9GnVGtxcAkFJmxg7mAaqeRxLZHTF9grjQfscF+KswEPeOEFsQTl2s/fGrbZzSE86XnzSmv2Enomi/a7WBFBFQI06zJ5PAx3GIsZlJsVP1tS5vEdsz2NHqdkqHEM6alkX76n9iEN1xi7TFiCeIGtJa7QlRISlzxCSFE1/J2+uWPH9Mde7vseTjq/e+nlsG7uSov4VQOzc7/DFlhF9965vbY4ub08XPAUjPJ+a5sw1PaMSd/Jr4AakBzMR2rwsh0Z1rrZvBIqA0ABGKxfHtd5WMAaEMRfIXyvwgVL+1erDt8yIwgWxXuoDSpGb8+X6F5u5FHvcq0dnNGPPkoO5AsYJYUIzL6osY0ol/vZX6m3wgYCWYWwZ6wIolO/xR15d75H5pU8gXhrshdEGyCnuS2cbuL1I+iH68YYF9VppSRJ56DZ4cSZDHzsJoR7arCBgiWLiJJm3djP6uOYf1L1adfzWpxXUkx0upJIgUwaEMfRtsf1wU9q06nJksw2Vtjb4j+XtzdIqY+TkTDRkHy/asV+xnaCo1c/3UGf6Tvp/IbwlxiFkHv24IcbQlBFKeqdscPjWIE2n3Ymx38hq3FaX289dfrhnMOhwAQh4vb7txDUH3+q+BL19acKwDrzv/okTRp2mroBDHETT9Ka/zJZ0nymiI/PRW+OCXvToESZmJV05yOuwr+HpT13JkgzQQz4kQ6gP/V+td7kbVXnzsm0y2k4xATkDmc8uOccxe6BW5Of1UbSx7YJleCFBxkkMROWPLC2cgYYQD/pN0UyB/LWHIt+bCAfEHtwoCu2RYzrObKzFC0esOGllUOrGoXn2Byi1WhFgTZELzWAXUv6x3i4CVfoNlq1ryNJLpW7p4TaMGOhZm7q+MAlDUHBFueWTnu7JLetHoQLr6bRnn/ChL0qX+Bo9F9+f36v87aFzwStEKG57/jMs897SopafxERLljV5/41Mbi5FKpEHJQL+nJf1ZEr2ms6lV6sjPTF4zrsRA6/HjrgA2EtuEyC2TwkbJKMjnPoLpyWdAt1+dfM22OFKa+GswaOrf9ryYb5U0ZQUKx8a5WKNdxXdQ1sKm5XqL046Bn5uptjTaVIi+cA/OAbXipZgWG308eEQH6NM3OCxtLRSIRTkW06exZHnsnoNkPojVrG4fEjdYwswR5tflKRCTRDr3YianPMLA850DTrXFJdib8VJziHCZqfgT5bf2V93c0z8V/+QJ9YrgmBZHcid8dfAeaHP2YHFnD2N/5QcXxq/8+qhcLgLOaONDmuj9jJrYXS+5atN8eQIDe40aJn6v2Ffmi97J/oX9L/ylMXjr/zpO3WSpZOBoMsF/ZkLO0ZdPzYJrVd+2gcydmp5/NxSpXQ+nFOQEhw7qz5vF0KytRBYy6d1PQhsXMBxV/H7sSZY9kX7KcOXNxUHH+UXMvcL9IKRAYQXIN1aE8cHV5ZDcs1Pmv3nNlJdvItuf6NW/seAiooGMj7IyQXaZUSsxLcoJibcUBIf6kzi08L80XcWW5Miy/Jq3F8NSSjEz7ZSiFDN+/VVUz9tMn6lToFS4m5s5BXJ/l7BYPB+GGXaauRdOJTV0nKTDJ7b13cTYu8yWv5DEtnGfsZj5K+Wd1yZinJ7HhRmTG8ARmCDISQSQ9hBg99IXXrTl8C53w8RQkj/tK32Ara9pfx179jfvOF53EWcv4zcT70W75aIpriCrKJrVHVvlcA9x9Bsu0rBCeSLAYb2TUTfuyKPjxREf3zIzW4ZfAwbuOd0GF64BM4l0czEc2AADMdLU0vnou6TBWQ0CdCKyEFUoC39gieL9kqPiACpUcR/NXypvhN+0tMhvn1WR4PQei4p7VXXdv+pqsHl0OjjPkD9WKyXZh+dLL4xvPUm7vrpjibXHvSeeWlV3PYqphQDh8lyJ3dDbVP0mX+6IKhAEtp0+SQ/WPdWZCkXlC+GMgA8h5ktfC72gCoyAqOWi5iuLoqVCFz9jkr1zPt/za7u3rnSBQfvD3Ws++opm5CMi4LOry1gGvIVF/NzNmDlQhx0yKVpgybmAEMCjTdG48yWryXquu3ES3QbK4BEIgiqCZYXb6Q+8ImTCTiL0mcD0vJ+hh755DFPwIjQ8BwWgFRhQDkoujanVIvRFFpK+uQ3oFcrD2i9MzOL+Gewup0NE39jiF6To2IA/5tIqWRKZ1fUeyv+0b/5KMHiZfqBd83DrDZ+Wp1glQ7hOiiq9JN6pj5ZlzU7u5/Q18VmpmlfaKj43fxXieH75QV8F4iHuRAaOmk1nL5YtZhh+DxmGY5+3ulF8n3S+t2C3BESYGvNc/FWvyKfntj2lYVTYNutBd9iYJpYD7gYQ4m5BPryqBSE3TEdSX6uq0S9rcyG3XB4vbsMx3VkcybXo5T6E8SGVbQKxSDpi3lHnEepvJEqFK8X+olBr9T88J52IL3gVD08igO20UYWs5gOvcdDPSGjFaTa7hKOVrDD3H8AFItvpnNqI3MsqpgevEpqfFvx6Bt/Cj66rfmpMcMS94LFyMmaNUU/X0hrKodYlQYm90bRowxRKD+T6t6wEZD/zsLLIVwh8ezPq7q5VPWx/5rSu5h79aH5Nzfnx42dn0o/dMFTIXJ5eTlmQLs9XeSuHcK+g38NUJLUOy6SRZjUzIU2HDV6uKDTdDxtkNINYIwlU8c8iFpBbukdrVdclWn+z5tw2gnw6HveiePS93fsE2Jr9HDoZa3Xt5jqgg4qocYLVv4qqkLLgjv2ag4Z5lp4U5ZVFDZHfkMbkhm5F5Z95HAtyVcGr4dEdyw1rwPsAAwFRjYYQMTYu/A0YQOkf6fN9ztQiqPezWzX/da2gn6W4m3jdxHspY1oBdIA9WYDVWJdfgLlRzU3IVjU2AiY1YAuXkOXIsbFKxViV5UfLvQf3l7vBH7HOTdFQq3josy6Beklai4kTYc+aQJCvYv8YC5rOLkRQEfV9GuzoNeMe6kN4rbshCzjH+ZpMpnuJgtTH1UhbkiY+ogJTK4OIZBG8WgeO429+5lNxc+mCQ5PZ0K6iYGUnSCE8wKR/Ifj56vaGbvsyoDYId0mVZ40W//5Wz9lzYBIOPXFLsenrlw3FUynNkaijy4jfiA94z5aYr1wkfcvQLLnLT8ryHw1h1iHdiZ/DO69Gez+e/Kxo3MEL8GO+6BCd+itfBr3etHOh9U+Bs/K+hZsL9Xhg2pj615bt9n83O8aon8ZMN32QD7V/QAjRPfQVB6WXeZXQ12TZJBHJ1TXQGKCHAkR6+EOhLYXGOkHQ+cSl9PrFSMo0p7kS9KITTOUFyPEv27S6iW9uesRDuvriElkePIiCKZpFPjTFAYuVJhV+xd8xS3UB2xhQAgcXyHz59ymgbGGpy8/79+EdJe7Csp5MZZmZAahjF4WMMDnStTN/jVC40ccQJhVFyQN3Bq1DEirGWrmCpJzAeRuG73SElT1pxjxuyRDEzo+CgX9uJcdQIhe06K/ar9EhMYU7fCPUL/ssO93EUpUJCAnxn37ZtuDnMXdDP8Fxjn97dx8hXpYN+iZi95O1lXyAeoO6xZb6tsaZ39GlzwGX49kAs876K3b+8vEkzVOPWf7AuNIMdkW+5K5sLlX/HSSdwIjWTzJVS1q3xzIKenG28TYj8laqXID0bKZLSVi/6d2ZB3bd8EzmRR6szItwP2lm8wpKlljbtGu9CXfr0HCjHY8K9sWckN/9RcJbTVThU7/YFbe6BLRbCfmLKxG0cXVC2d9NKAaVPM4vtS6t4duxnTuuL1ABJzh2FusMgUXs0t8Qpbtz2rPLSylQPYeXyMfoU6MJUA9ybt8autKrrZcXXfIhXsNn/ZaUxEVrOGmVDQSitMpPOO3ZXpqHHYPcgHCyBBOm3IPrPtSBp9tKOostw+Rz9p4RcHI//19+hlt/OhhrFVLJHMdQD+HcWtShvr5ftxrJ7MhLkNdIyZK8E+iYcZtDF3qzHF+ktpzq4Y6tIvb9jD0sd+Ls6Cp0WMka+mUizB5UfoMZq4O254b7nEAONdKawkr9KpCk3d8eBRY5dlatVv1L9I0MB5qrvi6y5doCcqprqR/CkjfkkVN5j+6ngm1SuvUmdGixyLbVEtMWTwe09MgROyfe97hIA0QwHseCEFcLZn25wnfYd3R/lTs8KRWH2vmegElQFvLM3VOnrWu+v5DTiRMIRUGk7uIkzr+VSINRsxJBTfNMlo8mf89XJTVBFBi188f5heLUMdBl4Jt7Si2T/50yyl9BYXr525v+hY4qd4uPLB/H8ln9vd3F9L4XlzK7Zc4iL3qd7lruL945KW8OaMftyNWRe3Z8P/2pE3veLvSNeA84szQZpj19xQ6FcTEEp1lhVqx912yPrW4Xwk55arg12MEg1A4+Jfqj5cbwdLiE0ix2uRCLFV5laN5vB7SItEJvFn/ARxqN/dl8h4/lUS0DrDMmwTUvBqEIBdhe+O1M9UxgrAm+iP+YVuPQhIQTIO4wM9L35deYP212pMoRGYV1LiPk68HrX/ofJEleh0Q6CnEUxa3rrULvH0pxWCsD7PtXmzOccswfegEH8MAvRbrELnA1AY1G8jllckX4Sqz8tV4ucXWveNSbMDiJQ9nuMR41pGlBzNMAiwW3C6iu+Yv72MsQxesIynN7Z8rs6eXQxLOTMcIlg5KeG7oRP927SLIN/6Z1wa2HQifYAwq6gcJn+gYd6szzYH0Q2440NHdRf1R7SPmAz8X6zf+3CoMinQ0cvCe/jPrz/XxUNFd6jHooWi5E94TRLx70cB2fNECqtNS+UvA9/IhkKyRqagUtwGcXqCsz559rjHo3XpE5oisCeQSNo4MX6Y//F4FQFfTpYCizoWqMjG23nvN+9wJVgPykBtxbCPVa7SYCjX043yGUuNLAMYvcVHGKQ+jAjhj9M8s+zi7yj/3FEjhJrc2NmnCTRKLZ/JWFkL5xOx57MNFALet8Y6E48lACH6INi0lgImlKOUYK0E34NLjMYgnrRyqZL9uxjw6lR0thuAE1+9K2MeQrFJwF/1sIVoMR4+sFOn9GH8Q4rL/xGDHmA0DU1wCVNirItvSNjrVe/vADaE0I3SAUpx4mrT+X3HqDDdVlm43XF/xQ8AgZhKkB0FIaY6GUvfjgjfIDUR/9lFMvZfxbF9zniSsq9eZT45GNLe90PzKlQOqnPVQVWnQyLP4yVDMRgtOljD/d7OjbyDUdQrgi7UON2O14UeGksfi2E53zC9XBfOqq8OXPOFqs7+/6Yh//W3d4o52FkbUY1+OlRLvCLCoBQXbohlD8gwP81VM/Yj4dmNv8Sc7WO7ZaI9XExUibmSjFEeH9w5ZzLzbrQ9o/41nS/Qt3Dmo/wxPrwdQ4/9UV1balvQBXT6fZTjJE//xzP47rGztZONFs9trqeCdYU4AKgcWqn5QG1gRSIS2V3AeleQf9ncAMj9B96lhZYgAQzwRm5y2ktL0RGLwmqQFovGiNDi3htg6Deix6IjnHnzuiM3+9sAaBVOU1fWEv408I9FZ93Mlq2cemkBQwUofTv75amJIJBm12eG19Wl/dA8rC33amZiYie156r8T/S8CmljgmzicYpzfGUiBlT25o4AoaZGMLXnAEgimPjJcWqtMRW3gT7vG0J9E91nPQDJPxR+4U8DHKv8K/PqZU+Tc34hV7uv5t8ivm+cCUglKJOb68T8IDrZriKOkDRBCXCcZBgon6YVcIrTFpl8k/nULuKHvAmCQQLl8picMD8lf8jmKPGcFuGDHbLYQf6WgR1PizcCPeyY9P/GQdkDaAWyOoemdGoXaELbhI8kLC+wVayqPAhqZJveG/1TMY2mKAx0GFYNcT3AMgIvYZAV9bCdIgM/8bwSQ8c92WY2ZbCq3mdsGnw+r6Uj/mnCFGVOzqgrSpmf57+JJwwPV17C/afblbQLIzrpF6SbZUjfAYziaNizyj9IYBBVEt61hllZ1McM4IPKT2SgzPV9Svft3NSRnLZu2TxsRCEggVzx9ilWBasyte4dBr8pu9A5kSFq1xA+1V5Bc02Jda77qGKaU96LmLfiWDpNowYB3wyxLkkPZka+xxuEfJOoxvicF6mZt1wmQyAv3NIPh6w55EFtuKXdufzNyorSua6h//FoLkL3lQmvH0tY5Kgqwkc+snqwFYrvUp3HKIl77xdd3inuOp1IgZfSX2HPbnmJUZahDeeZvY/qrCD1/ijvW3CSrnki1yJnH6evIssrrlkDl4fT0r5PS9nZiRguuFAK3QjEdMn5Wf/zZ/dmidb3ReCKXr+ZIZbiCvcfhUtrVQkiEhpxgMgK9rHVDPNbpHQGiXK58EPgxpir5yb8zfA7VJW/a14xOD9s+NnP1q8BXR4PcjsB5O4ewO3a3gl+A8p4Xrcd1UcbTqPVjOEdkv6ZASIDmmKGf9/Vh+Xwjn44Pz6b/Okpj2sZ4Rg9GUSCQW5HSCB3y5CKwbo5bgGLMsOctPCxD3pkW8fyhp1xtItmNJSBpHGP5Jg9AFjOwp6gM150brN3+iKWhe4q+XORvSbzlKp+dcm5V0uh4eqkdAILWwh75FvsgPh/pM28irVMrjBWoPRnDPBTR2Zh1Q9smvaebRCvKQr5pw5hCXK/sTCGbBMfKjBqsLIXIpf0hKcyJnd/IHzg6DMO4nJ1X6PV+WvaPOm+NU7CkOdepGd1as8uGPV7+/V6g/r2XvFpTfkjA2ttzXtAWT0qPB8pzcAWeH8QBZtLFQ5UWCg16qNeNYiWeIuS/kv3pdkXfkhy3JapAAjg3xz0srivxHHY7PfJTrwm17StfgNgRgCQzHNpQ4f9VXCRWrsC1AR/4IBTdmnhKKbHaovzb+MZsiQfkBcr2OuVytfu9Ci3rTNKmIeY9pZh6KhdkddAsejh1IGmvph+jCJTEJXCsd1yR3rN7DdSkkfyxbvqUMzZDqUf59OSMze7ogcWmOSAc1xBKUSARSQbbSHR4PKwBREjQ9mjOjmpRrRxlE9cGSUkGHuKgnht3eIjAcJYCvzVGazSJpsBFiXR970sgeAr9g9tmmhfq67wUTlAeVXkbC3+Q6DP59Q8D0SQAJdV49qmnGsNAFb78KfpTys1HIaTO50GHQ9DgeC9HA3+LzpyhRh93Pan5OXX/DibCwvVZmotmC3lc2hl7qtoETiOju1NgVljEdo6zycA40WShxSdNppANTMgyzAtP59BTERCAImy/MJ6fIXLTfRVrM7mPPrmbgkWd1iXlYL6cH7mCE3xw+Z0ky0S/h1tXYpyfMRSf+JQLfTBOjSRoy1ZaN6GL9e88SS/+tei1yhN7fwGWn8jaN6eV8C7E8ogS9u7s/u/QupowUosRutC83To7Bf7HBz5JP6gYfx+N1O9lKL8Qp7b+gvuZm8Xn/jpXWjZxPai26vrt6Ntq5LyQnhDo4vJY6WticxxvKq3yn7MKW56WW1B/+1Q2UBDh0s1FIFpUSG4IlS+tl/PXJJ1m2Zww/KjYzaBPk3ACprZBiF/7BoJFdXPMCCA7S2cBX07+e3qyZo3JVm/YQ3GwO8tCduPHSfZ18MPPH73y2qTfu254YytrfnQkqtq/tTRm5eudk8lcwBgKOT26sUMx0FmxV2Qw1Cw1Sfg+J6ItLHP6NA972+OWhJM70Bslm9gD7A9jvS9ueLaCCdYXmujydwox8m+hJ+ggAMflULxHuKxvZmtB1H0zGOWsqR7U624/CEpmKVILURqgzLFBZNDrEO5eFQ9U+XkZCLH/DHuDjSt0g8LKsPXXQkQYhh3/XspQFTbES1SXogZR9gSwBKCzS4CXt9G7qKluZPWv9PuRTx/jGKB83hH7SKcY1yxCMnbkazuh6lCMaOafPMgU8QedI35Hf9QEUFy0SJAlDJIGhCSHyV4Jae9+ieYcDgMrx//JSf4ty2scDExOXv/Wb/NkNp/5O0haz2guyG6JHZt/Vrkllmc4zlhjAhYm/er68Rjb69kj8XAqH5bdBEiAexizDveG7p+ZMWPFpxRUTBF+QqpADXekdI7+t75fGPtbXrAkvWOBjCXBUGx20U/VFqT/a83K8x9dd4T51fy1aeIJ8shzbzGos8J7ybtpE0UNL9HlezTGiQPALnvPZNjazsINw74pbn0FuxYpQPo7bGRBtsCLakLZ44I8emPehQqKK2srU+Z9GWF75WkBNWohCYK1QpanP/mOD69v99Pqz+Q99T6BP/qo4Rhc673kqxmJO0NhQ4gxT5+v3VB7JQanYZGpbhu2Hk1eenaZZ0dZY/FTH3SaX18xst0TrFYqoZP9t17CsixV0ljQuLGkkbG3KBtuQo3pdXZ+D8ek/DGtdUyGVtIvLjK3B99PBxEB1AP2170rfxUafKYgqryT7GxIFRbeq/IsiiCNveKN/Uj3RNNdH2bY4oyC6zUaLLSZGnLvbHb0i5PhHM/BIZDAovyW9uXyefarTTfOg1FJcbkZZ2WBHBJJrPTw3vbO2i3XRMKttfPFLZROwwmV3s7UbapBiwZVL9jdF7bl/0ARUWnhdm1wS5j0ckSG5zD2Pie1WQMw+6Gop/ymB5a4ZyzwOwStMz9pMVQwX/UiAGffJA0Fasyz0mLLdFG87K8cxeH+0/snywI+SiV+rJRcTPOqLexws0NRTVbA/5QzXkk1n/CO654vx2BQmjWvjeEuLINwW2w2hH5vzQbWfpgiIMiyr6JV/N48I1zJQSbXd7DJf2J1Z5/vu+uBDOaiVmsSu1bxg1r+fBO1zpL00iLjgQPXJocSzxOrjsWdFKGxPCIfvrMexlmqHKjZ+T9KBsnB9482ABC2t7c1eVQZe9t+kmT9mbryix6juvTf7k3ROmQu/mBJMNd2TD3SRmXZdK34eoDJ/QBUHOsHH+H710nJ4OBHabyiCmG6fhUvBo9HSMKZMsIV3gmlgRrPHIkLzlzBmEf9wtYrwjC1gv+79sPdYgdYw5/WH9x/z4lb1sQOGHdZQOYsrjICPB9EHfJMrD/WzVnXbaUcl1n+kjD1NsrSwuwZYA9ltA+Vrz8qiFQzT1l3pCwyklozeoGyKiWJXSmB9mN+BqvL3+sqKv/7t0Ed/lFjRhlNsyplh1o5+6jTeizdi+RE1KgYPrgJn+VxIdpGHhClhDJgGmnYWt8SisdXNy8A7qDaF2OLZivbrO4vNT0KQhoXgZAMpQktkAET5BNXXVqU8B9YmKDxIvxoPzfnVJw0G9UdNT7SG7SWS6rjZfVfg8/iRou/BaR6Flb2WAyggKh2CKwpDy1+Vw4AFI3Kns4MRqhUwP3Q7RBoBJOy1c4b51H8i7sOqcO/RJQqXdqorRGpwiIUeEWbAK2vWmuf/kB5gKAIbX3t62tXOMz1ePNOw3JG2YxmZf9zHiR8+ztCuZxfD9oOw6naXhkaUDD5cDnMTzzQ5b5MIA1X0Zr8y+290chcknGAlrbQaXH/5TJ+Ch0J3+I2JNB1GvtY1W7Jxhpvjc7THJ9/XlIdtzIZ/I0jxNFMWCQoQYJspZd6+5qT/lB/dy1CHgghfzDl5GuRHyl1p3dSu6X42vGmGbZq21vJ2wrQu6Jd4YyuI8g7Tu2Hh89h48fUuUSUSRG8IhCxG00xjo3vdGcsOzr2hEXDNk2b8lm7LBNc9CbUFkjSH/ruFVPpu7mE6a3gX96Lh6KAo5dDYEUnQQxgNtqW29rr6KTk2n2860bJxBDkUN8YjdedY95fLXE/Rpw/j52gwyR7KXo51MzIzjKIC1VRtFs0IvX/u18RRsH/qWVsNtw10n/l+5OXJNlu5uiyAav7L9XQUlCvyd420aPSafCpDJPwtCUU4b/K0irKOqZD7wNwa8jufk0KKO8x8Rk4ZaOwY25T5U1FCrCziiqLl2XP09/U6faUEl5FbrvmRerMjB7rAC7Eivehkro5p/eDAqOQPT3zVE/QPzuzE1QADknpF3WsQMJIxDMSGj2jO8KWqlmZiK8WfUjv0MXOdmvRcfYAioWecmfgjTafbtGD0All2lkLHe7uPv+jXm6U7ybAOeOBvnjyyVaehqZ1Xyzq5d3BncmG9jL50dBh3ByE8U5O3yKTpNIAAc9h+YeWE2KZM+OETyu9v67hHvfxoVKlDal4R0MW/ESMoawhexVO7ysQZdpRcWnmnuTbYZmMYsvormm/2WUbsjMwzmYK/G2iedsoxkCMIdnN82Rk4is2n0M/NJj7M3TjI/TgbClk/M9w9fnVN3IKJC9WXl5+WJNnlpyahtym1FcIQ9/GiKluDvmP2KSEt+ZKY9NFTXirJFP+y39mwUuk8J6+5amHqPOlb2yNDqpwsOfGH1b6NnNSipKdN4kA4l8+NzSLr3a9sSpzN3DdtuNlneneRvK8IVrqZZT7SAM/RJXTZBO5OEPpeLiMH2ofcviwfyygmSW0PcpaFa1x/bCo0plCXEC+6V3USoecDwezkdTCyHMo3jij5vmnEGjdf0t7sOZ2bErpRD/p8uaamxusRiqRrXTj2V726pWHY4/WvH07n4abUy+LimiSIqo4EVLLxzC9WCngOQSUiBVd6mkyGl2mRvj5tf6BvC8ohwid+smMm1xIeUMyFjwt5/4AkJOLw4b1FinJfbGtr9EQ/pxyhN/hTrHK1n6NE69WVusL0DbV4AnI2yzeRWWYmRYSOid+rX09tR4yaAeS2CTnlpWLoFVUtEAtvLL3yITxm+fykyInTNH7ca8DK2ziDMMZ5xIO3nLmVYZMgVR3n9jG7xgV4K7JMzoAvWRLOPkiUrhdWek0JE1ayp0bvfBNfeJ0o2VUlkZhv/PyY4ST+rgcKmLHW+jTyfgiK5NWnSkPJ/usG03mrKciSHmFYDO8VaY6r5HuQukZpOlI1lvlg/jmRANnsTJWOdoLLuWNlEPM+1/BJH/4lsylIi0s7lDkfBUa39vx1CPWKXh1i7VaBTZxnJqPaOt50DJ2LGEnlvhhR4pTfDy6dV2vduC/vBDKxQYN7umGzIXAsgQQg1sxbHQ+NyiWCDWZof19vzzhw976scL1DALcW/ZIQUk+gSj1NGAW4EsazJUe3JCDRyDaERsuBCL6VTrn822QWREwlhoqhtulOpcNEOVByMXbh3A1XmVi+jxF7lQ0jX2oys4qItHxjSyRkZzJu4kV0DvNAxIDzBdlhtWIqDH0WOparUNGA9EE20vVua9RFmVpfVLLIIEc+GWgsbxdhl2L9Y7zv91VEXeNMXPwTQm9MuXE1oB8E/X6J+FMfkSpncPZcD9Ey0uziDBKl13lgqqJJdBHgsCkemS2s9AtiQbkXIE288D2PeZJtsmHYIZJxFV7XV1u5vIoDSIWssPCrxQUEUqxumuXSd2vJwJ0g/d00QKAt+sX1R1FYL6IEJB186JEW+g3WFvntUV/3WtFVOraLmMp71U3bBu2PuVdXb7LXJxNv+mRPK7jQ8irGl+QGC2LIzPm5zh+uG5V4gPNhQp5jkrZhcBDqKausCAtnPw75a7UZjDvwtVNhYhWvH9Z4A+LiQY3CvPrzBYhUawbL9hPVS1P3XPye7VA/oiPA0U0D/TqvAvbp4DCc7aaWR15Dck0k+RA/Q7+unmG+FNM1iwg1cNfNGPr59AeYwzI6eisLEzuejnFqgX0ZSFpZCxftiLeAgKImudy76/NtNt+D5lKyrSYwVa3UU/qELQiVyfWFZITXP+GF2JBv9nKVTfxS/+CE5cDgihusTvLX26IvFRc8XVfFzIeJrUUjz8xSP8iGlBBdKK90Le08eQ7MaB/MG29hdOstV+lqu/pW/z6F6lMuRygunvpAz7PF2TyDuNGsZUAjtHnabP2t5Mj9o0+qWFxCL1JPTdf0O/pUe21LRdJPAUgQRPAoIrvzhXUqv7FSZsTMiP9KQpNsuIF68R2sebMDIiRkjYX22i+PrGDbMMtKIAyYJySJ5ygTeh7O5j1+43J4jtIubLR2f10fmU9IQS0T3k+vaGZ586RcvWod2p1kKd1ULFsdicgSZfX3Z0FclFBcP/nPQ/MgcxMbOyHF+8f/xkob/1YKwQdhM7uKTW4eX776dZLbCB+e/2JW7t/U0INViRl3W9XrgwC/QPoIiNIeKKpKeR/ry2/aJTBCjFuapUnaEMStO6P+In4jdg23kklfgRhkPm6Y+cJb1ZiAHAWcDlNef4kp9xuSaQWV+a5U2Y5G2spdlX+obOupwRzqihEPd06mxyM871fC/FZvSudGuCpjGqbX6dAbayWvRA8sQpj7XtqVb4OGDLK8cncxvMizt+ub9N7AJdZzWDwVrVQW70w/OwOMaul+HZDkEZMXLsN9tPbJ/20Vxu7n2w7Zga19/RgPwYmOlh6vvPMDEv/Ijhi8gcjHpf6e3dVkLdfyOin89nR+f1VODWW+s4IvioTmDxnMT9T+bT+TcTvmnJuGFMzkt4/NYE29vqzWpYIanT28Ty6FL8sfNA1O5tz1G783K0qnLZmjb5vMca3cpiDuZMyEkXxoQlOp6nMzCROmOptv9bkQom9aj3CzLiyxSOeA60zZR2Bs08kDEs7SQ9ZYnn3PvbrjFX+A4cvV30ad1frN1jDZzCfpmBgS2b+N3lbHFGM0fx7V6fBQbVsblNg1tcGtS20Gd0rx390SLy/kv/TXGJJHTjKR/RZJ1ZSNjpUIRDUm0kB4ThBCJ5VDXN9tf/Hu2iKXsQh+6AUf8o/2lwmqQoz7hYnjXNxX4AQjGbcNWikt6w5Rc8r8x/94lVI6ERTgv8qGDBCGvfpDHw1IAiBlDKDjuW0G522lyms/DpUu6MTodHazWOyVkc2sBd/7d3FpBy5HZdHGl6T1I9gg9LDa5wDH1ezd7Qmt+Opd9UOYTIGLNia06Z5iiWEaFAFtiu4Fs9Pv6Y97lCeu4vju2Lxtj8bqbHx5hXEAeAz4rXF92fqpTdzvCA5+d9yy5IaPzjG94WW4Vw+oSnwctmrjqWu/EIfR+j7/Db/U6mc2mbL7BQNE46GMcd7wInoDrsYxv7k1UZ0ll+6kCsrfRUUAHCHCdblwLk8s0ircURNcLIN2cAVLmEHHi3quxCwfHbGEAJMqo7aR/PC8XWhWUZTjiW/drGzKXSvEtfFYXPS84KVi43ZB/RRj8FYFsGMxjHUITc1/KirAxWx/FMQee2By8Cn2N8uLIbGBO2zZ4JkM6m9JYin0T+++J+7/7VCCVRJIBNOm2NHecca4QHgDP4HG+41/NtXvBrOPFKb9+X7y836LgX21UYWe9sT4TMU2f8eyHquVQCvnSV0Hll6Xj+AIRRuEh6pnTAW7ZAcGlljRVe1EabObcvUPZXpeJQMDalm5LmRsWL8Kv/Os2JY1W45ZR84SlI+PDQIqW9kS8qWOINGL4oWGvwQ0+feswsaD/2nmsPEv71dbn165IGh+So4HTO+FACqTekZelpiwv5tOE+eExcRlZPeLXVb5Oh+7l0DZzLVn0aIcPUwLNU1yHJMbr6wGH3vE2F47mVa1NWr23QkD6oSqfOhrdXYUoR7/BMmyLcG0xEAX+ud8GE2FZB3D9Z8QLeslnJctMmeopcMFahvht9MMthrzL+DDxhsoyOlvDV/erRXysxpUvz+shWWfRrkhdksd0pWM9CzVrKJY19hK14L8EyTw6XYE9QEhJGFlTX7XqvJ5uNcqtU4T7CHI6jK6oPFKRROWA5oKmWw0G/DmLT9+VSWWjtmzOt9TIrcuDVNzjB2jrFhxkAUYaRz1AIOE7HhwhEp/YaezLq+2GUIz+ePR27b6HQi5i31dVlKjo3qL6VYoS8navAjVCPY3dYpz/txQ1wHx7Ore5NJQLCoIGHGkE3D7nHtAQEa/gqGvZjtRu78SVL3hZzVqZ1VTDAmWWrOOrLMjZonRq4surLa+nzc0/Q0aSIsgBkr14j/pW4nTZteV767NMKK3eyzPLOADR+TDpr6RwMBu7H/mw45W+cH+Hi0QnWNQql4zhmabdj3VWwuzAOSaHua4QwYaBIRQh1RHlwwq0VgYOuCXB8xyjv+NCe8/uUPMm2VO3TSv3t3753myG0ttuUQXE/TK5lYf8zw7MomoUXOI30H3ssas3QMQniO/PySwqYH5g5KGlSboaNzr+Vki3kIhbYqt46Cc4xzQ5bwW6JyB9tJ09Zf27BNCPkvUxw1jj+9gB/GXHRtmN2NPWV3L/Rm+D2w7uBbFjfYKYd72hSIE4Vulh5cPYt+gwqHWTbwL1DUaBdS9AcZHfMtiBGawsUkVfsCcjN3KfdjV5xsErl2zmcrdX6F4naCPnR0w+4ybRTJMsK7guDUQ6ViqmvqE07e9lvHk1XAhLoJr9ohfytO9E2SlNfswziCWZxqH2xBMPFbVmK1JodM1nERduymDo1gkTJfTGlcsa0dxasXmFHq5AkhwanmCwPdtIw2C5cGcvnMXRmdxpzLLNtczHQvV7xmllXtRu5CUtT9iA6McDB+sb4bCrGho2PLEfzPGFR6kr2NI+N9OuTJlTZN5aSSBz0Qi+5mrZannf5hx/yp05B4b+7cfgYRemotJVQmDrmbh+JzSk6bP5o/AR3FfYKJtdpEqTQ1t+gDZf61WcD/D4MCcwP8VZ2YGWAByIN882uC/yi+1u2dFQCunqKfOFAoWZc1rZoAjMAOZhYzMZkpdekm2c5N9ih+ZZ7YGhT9/u4oA1Ov3ISrpd7vYuAJPRjEYvFe34se5WKNkBhSIYG7H16mVp15UbQrw/ropWlXsIk6X0k6RG9LdLLVcZw7qzbkLYlVXz/4pBvARR3+yyxUIlP+qis6xr0F5r8wxNZdmmvfgXzI2uQ2yhi/TaDZ1ofZiSXAvBt141muPdnqFd563uqGRa5h29xWeh5fRRMByioPCe0+FvEimAQ+JHr20MEPQlJaITx4NdMdMfUfDtfxGg7uLNwi3xQKiRPZfQBMgVSx4yPrMCk3vjPXF28xtb1BihL364KdQ+MtbB/buX0vkDDrz893eQl46EpRd1PzNLqKi9pORzyv5D7bHb+jgIgaiRqfaunDbkHwP5egcPzbORNz2l+iwU96ZIHgAprFfrCicz2XKA3Dtq7/yTpk/i6IUcoxPVf3qHnN6eSKLqRjMIRVB3JArQ6+DoMEv+yk5zUUiiSZ1NEqdGbNgtpmNcDG0740UmlbUPR3qSday90MszXwzxeQj1qN86olcURaLgQFvqf2WlOrsg0M591iRKjbLrVzR2aVqWFl1hnMJV6bdpabc76FbKeA1Cr8SKG0Ixav1769hl5w88EfG1CaEbdzDDgDEywtgxQrhYO6H1Y843Q0ttAvzd+vdiekcou+je+CpdW9plaOVm9wk/HcjOeyIvM8NLzNTzZgii7jRO/GAfXqi5mR/7cNIMAx2FFjZBlTM6vdbuGAu9vvIaqZo5ZyHen4lGZDL4Rf8Zcof4Vx2dJAZm2NS7qtZHf3kAb6m+vX70VJ3UkIJtkQK6atSTb+d+5Feg5UcXKlJf55+PVeuiekrvzexVb7o/cMb5NcsGV6byO0n62G8sflvValkwOTGbvrHVr8zlYUU/KuLzAT1igLI5o48cv7wVkjcLFc/zQHB9sZUx1Hun12CjPG5R5Y+j/EKdNGbdzlTfyJEx49N8/4pMOPDt9OvokynfYrxaQdyeb6Fy0E6LSFsP5sxT5pA7H3ZMOD/1rVYg7/NYuh2KyOSHSVd4vc656D8aQ0nUq8GN9PHBxsg2CKeTfHk+wHIYcP7c4kkqs3OhrFfEaqlT1G/FdnWYsR8PHyh5ny1x/LjBVk/QI+2dUgToYtNoR1hgTTDsdydELb8QFrQsK5PkphojNB0GkEHoZJM8efwu+wsP5MMuzTVoabyJJqc7mhtbyLBw1z86sKynplahavAfOwk7TD3FEEK//1o3CCLNi7X2D03f21tSTWO2gRzJMjPU1pF0dxWkvFJyTpggEQaj+MeI+XVRQCobCs6AGWAafildSGemW0hNVn6K6AiFfsKbBMRDFH0dAzT7jXILzTxOn4FPS9L6vDeetmPdOv2arj2tZrcxRY1HhWt+uNtKD+h31wJyYxmh2C+QhBAvENfkLB+ek2m2F2/rWBqq2EionjW8X29fkoKlNBHMglrV1K9cmJ6CNHXs/Ll8rCFboJnVDtExUfih/z0v8vIcT+LDzP5YSeuMdfe+Joa7aLP5vcBaYb8nU6S16LAKSAJeKf4K1h5pjPHDM/kD7C3qRi96gv927wsbJKai39TaFFqjapcgVJkKiwacCoEIJhZRCcUZTJzJMqGPQyi4zkmtnWI6ev2y3eHnfU4bASSgbARFcaEUS08FmSW+sx1OeuKsIG7aAHn76kK+xOPBu13fUo7fbwJeDCMKtdDCyhL62WI032z8tHHVHT3KrqwNxYgBp4BscVZpTYuCoAFsrKo1XyK+rr7S9op7gTC7SChqwri50bavtopZzp2eEYvDajVbBkEB0ZUZRlShfARhdhpzxsKrIjxptleYxKr+UQEbl8NtSTz76O4QStEOK5HoX+GhXxnaT4nouwIhA2M+RSUsDHBrSJsl2dbE1YEYJtyMPxddMDU3f6J0PQL2oletXu5HZAuoZ+LmjEN2i6++Fa0lquofmMt0eesbeWzcJalZU6hrbUFMgTXG7CpKOGR5kYevKYBU8CpidbWC7V4PRHzBIzw6B++Vasksh82yFumLyAny/yaGz8etcQ+Y7L4UV+gkRMkBT/SLuAs43Pz37L1mlr+79/W7hhLJibrPUmMXk4+H5CRBJ41deI8q7+S8JtDSvq677p1nyh9YC5f2Av7UJ8k5U/ETAfrCrmK+PYvySvrn7/SWe5J/SwkXzqn2AeK3mj9a0uzhS5/Z69KGEaW+favQ+T9ZSEgR+CaM1+rD3gkqSOa4g1p9AfkP/6oscuyKutvBPcC+yqeFnjsvOcZtrqa3sHucCYC+zt7687O1wi6/VmkbFCufYGWbrcT0S8/rDnA6HvPPyjszpnpQclUZdpo8mnY+go0TP81zV0a+7pF/1fMh1hL6gkvg0f1OPCvuB7RoOVc/9e6QzAVFJBVheI6jNzSqihkpVUMZvxchufq3ZN/rq1C+6cxU1l5A80tlcfriAJGuAFIX3/l30FtWxL5rhGEz3E905Y3v7/D+SvLzLwy4dqNm+AIB1hZfkEHzeoZ+o9WQx5PjPOlW/QNbWdqDciSQ58lLFaWgWvXTx4EUvAfuEgWHbnqkOwgSe2p3Od7Utbl9/xJvdciV8cytOLzUU+4uDkh9dqsbj6D5bYgZiTuse+rb/5YjDQYsfZP886JdXnSL7VPc8EeNgHoOLsHGu/X1UXJv459Y4LfrC0YoHshpgSey39ENrN40WDDIZi0biIdQrlBBvUhj8z0HMBcTfpXaPT6B9iOkbRkGqVgL76gGgAnhqQuQrQh/ricXmSoatjs/L7t3n/UL/Lb+3xzkS/TMicvmV4JzEmFSpqJdScIi8KhEOuA0rsTFdXpTr97wyGWf8xs9mKNX4hoJMxia9h0X5MrDXc5DS7PAys1w/Gv7xzYBIdQ06iI2EbnUURR4fL4hjHElo9pZWxIVK2rPL5EbCYFQwoSS8zSfkw9JK4OF0Hj0Xj4ZiTDUNa5oWt+qj0rr97rdY9bJhLuJtVz+5tn7XmDP3TuDg/pLlzcAavWz/n5uq34CsjfCYN8T9L5czs/4V1tJKFPHStk5O8IviXpuWqBBE5aTbUtVQuD/VoPMVGo8eEcNMF/rdfsXVoSQwFJ9k+FSDkDs8HOrLNo2ZI9j+rrk5mk30j/qgyk35AZev/zt0fROL912k53HfzU+54vOgyRZRkx4z5vwzsvETbDtudP0fy7JIMq+vdVBvSFdXxO1Ol6dYkuYucezN6PAekqRu4YLRYZheUiTpTwj+Q5OEQ1Zdp58VNjYKMZKyEDOHpCG2nJchzuWUv0WFxYCDoVhpS6GGkPZM/H2BARQgleP1AJK0Y7RR1/NUiXteQFjW3BTkSDJYKONva6wUb/l4fs5mf08SRGUbSXb15k/7bMBr5nh1sM0zZIYcjMFDpc1ZZzrzwitDZhVAuN39lsHW9sp//GtW5CAi7bjotSbOg752MXOTQypWYTR251Ywx+7bn77CQ0Bi3gUXZgaZx4SqpytxsJhTJHr8DI60txOIBC1Chuq4Qhdc+HiYRroNeMG/MmcufMx0DJM1n7Z5Zq6xUz4gcZHojEe9skibri2wZk/UDGaz2RfCEtXtA9jAZfVL4Pl+JlRKj3nnfHFJTQilkM8rdMIXnBuj8hVYJmkrv8v5tn6yPKVmL4s4DqE0yB4i1ck6O8lXKGRLpuanzs+L7mb89P+KC9QJjRoJe9Mo3ozNf0VHZDKbbpCWKEoThlzlqnsn6QT/2qnwQjIthbxNLs0VpHfE6Kq89/QOiFaeSEvCT+NEtGjiZUi9dFB0bsQKPFV5C20oCLMG5SaLvPviNvaPibMrMGdWR6ppswZ56kRbT3LvbPr+6rMYfjgmQT365wvbB0s7gpPP5RXeIHvWdwDrgWejFl0QR3p93Pq+JZwXJQ+WX9l45KxTcrxyUcXAxwnEjA/xTlPQKO2u1f9of/UkfyGPnTbq0s3iv/gDdBd7eabYYTJj+j/RKvvExUorEBuKET8YzGs5RIPEjh0qfd2OrgfXRs0L+sNSIOUVN9eOoBoesZaZYm1M0PKlXDYE9/Fxo48fS4CvEGXu9Ku142w6CstAVxiMyf/qq9I/8xRumJCXenZFT1GbVGLHoIjOcS3nBONsjlkcngfuCPO0C83pN3qocK02/A+l0aajI/l7DLisMA5hcSXMog2BnXq0vOdXjK+pxMD46NNCNjK2kbfQiFMdOGV00lOSbUsvCvwKG3DoW78UDHM1f3hDqQg/n1L+krbwV+/mv6baqxjuh5U3V+7jjdd1Wf6fgI0+OjivIiVaSfGPfHVLHhWiQMhXtHeY3VGvebIRtnVkoLsw17DROkdGCeDfsOSlx7Qrd0TZgkOGKxlZJNKht5Kb5O/ORl8dR89kwWznzYv82mmr6DShwfmWZBOZJIhXrAM8wGwndxPngfE7M4gJ4AR4fl25PUOJlGlnfSgMMeAzIK58Ezxm6rls2cHPZD6Ivnbt9r5IBJbXxlhO5FC5TAKiQ11ELTXpNp6yZ6SIr3rA7ySCa+WHCSkbFZ5I6raX6jCAooRmsBRXGmSBpBMpnOoK3iJkDF+ybCdcv8wASVOMWoZysNQw1ZRLr9dItQW/D60Ex7RPHa3+VLfMptqd0TymMyGwN3GL0LWudgKWmJYT6mTfchY38oW3KgvwtQzlpWWR28gaB2c4fe9lLlAWtNhi3LrhestTbhfk8cLETl/kZSUV5y3nY9vDILtFTBL+9HTZ/FE0leDHfdPMLpiUi7f+WIjJo4DLEaZFBpXft6CsNLHPf0i3qNXqyfsBLwFES9NryrCBzTIuMgTnx5uPfGSxXct8J2/idREAshjwjXTRrmV12vjYhmJolgfrHr8hU/0vsdMze23R9bujGGCfQlmFbil8lm+Z4iVP+GcZBPPzvOJC3jriO+cfrIJ3CjB/fWmSHYDsM9KDM1tZHrId+pu+q9YTy/fIKNatwPoXSAFOloC43S1BIQlUrvkS0rzlPIqIMgpQKRAR5Gg61uLDWynp26t0Gi4dLOW/sR3yArgCYx9vWugRqj5Cy0jNMnUZc+8fx5+NqVdLggJOX2DXwEDMWwsdk0CHurfcbWMe3x8rMaP9vD6BXl3R+2NtSw/hua/ycBBClJWa+tXniSMKdpU98VyzgX/NAX+3lSQ0T82zP9hvFB/YxzGkPdE5N/mhGGxc0tAQuHP/V1mBE4wwbXfaPxWA5mIf13r9y6wLlQwLY0/vof/cYfOV1LRZCzOulU9sclR90e/rRUygTvcatn/cC6wuhETdnVRdXED3UTx7exrZET71MfH/AuaxhxzQE7/u6jz/4y4H7c2HG21LTkBkTvbiK1mcXYfWEz2q9QbuOcGwXGVT2lCpGALcSi3h148/Hyfo1IeU6agKTo5GrxmhCfcgR/8wul4NvXd+Zbh1UbmGlfZb+K+4kXneg/x/uKVwLl9p0QWP3XDAst4oM+vMo03b24d5cidNfn+BNgsEv0wyKmUQlAvEmqStDJBvvESWTqhy+7meRqNs8oAmkiBvMERnFD7C1Veq8/Cv+StiOgcS7Nfqpr2IhruTC4SEQKhNC36YdURuMHxWS3tRacfz5QlrHt9ncFOp9bbfQvFZKYXtiD50RF2wc1ZOGNG9CT5Q6ktv/j6TqWHEd24Ne8O7050hvRG9Hc6K3onfj1y1JPvIidjY3eHlGsQgGZCRQQNeoNLc8J3kH1GgrDnscN5Fp0eLbwOiJtbWWdXDLhyX3+mnl0xWfsGFfpxpGUh2N++D+h3MCNwwrbi/GS7l2hBKnoaKM2Nl5L6NiLWUN1BkmwWTvhbE+B/3AXFOUF2P70uEbexnBHXIAGk5S8MckKKEmTP2yLvPLDUIiVzptTNiikDk1c48tvhp+OFeqrUcABhJ7sjRCauqxH/rviLvVHm40MkZXgtWXX/4pDBliVZ0Zym8pV6yZtImRLtD2+Tl979yu6ljMagtBRBVD3AMyiwaVBUJ7BOpsC967yivvrcwfjdBwoPJUmDDTCMGId47X14vqbzhn7ljQc4XU1WCLmr3aB3oKKbrzjYZeCLAQH6v1Z9woc3Z+M1VHbSIJjuXo9jCnJtG6B9Y5lgmRcKkWOSkQ/kluI19f+GSQHDlwHNyL2hfEDxjRSlppjLfp5Ww2KwdAMShJ7Q2VmR5FUMr6ursQUKL5pMUjV56htEy7qLGMbtzqb7KclNa5VVeIUHAWPNd73ouI76n0NPJdos73wm/x04SfJXJvndfQDB0+kDK2lCqdI82PCs+NI3K+zC3wBOvQzeDPcDFYMDfYqXRt3SKuOUSxLOmdIYDiHZYyDMDLgVdtD+3NHDP4grSoL0tGZtvd6qVLe506UL++5gIC3PLDW0EomGlDRGd5j7NOmxdY18xVw68zELQ6cLloDf+vuT7PDseEdzNA/5KaeX9lkr1+j9WVR6PSzoBDJMsYWCNO6YGcM3k/51IAdF4Pt4YbP/kd3BHebQmeHdptv57iU7abVdAkhklmsKCO3KRxIdBKmKegtr5S9hoswbMzGF9U0aDUVQ5lg8Kc00OnYeALxQf4zqe5gbr4usC42lmnpPmSGsDyz+fV4j/uUp1M2bc0HNFGV2AHoyUTTZO28SBnwUcsCTGMLxy4VzDAMdi9j3z5xj45nG6YRkNbqaZolAfQ3xgYxhIrAgQiOHPHshtL3eM288c0ZSFmTeNP6KA28NVjf6bY4LHY3ED2eNnJ9Nd56z5fvpF9UFjqYLT4q2rlRxX4Mr3txc04rPk4EHubdbzRCnTntPnqln5FOFN+VGJU+C+OGXkJk6jCUxi1v/Yz9sjULxNqOCOPyCFNqcdCAZ5dYYIMQDqrnrxcm2M4av9OXlupwPOkUI1aWHYA8j6PONH2Xm9413T39ruQjaaq43eYcdfIWNg8aGCA/mPm+u1K7lEoUyknVCu61VZ+pO7suk+ztKn4Ry0BHC7Wdug8fcq524VZFTPdyVkrdSRbiPKJFT8oqHOBAr0PJB7fJFFQp5xvhn12LymfFjDkl9hmi6AtWgv0JYtHLN6oN/zjD59VPDRTAITeH5kXxXo4TrkoOs+EY1wNe1i8mrF/phYEg9uIz/g8LTS9RfyWTaQBRl//GUYAucib1Sy318LpYoMoyXgbW8jWcrVBh5EPqG/PYZUTg+mheGt0CgUhJlraU7UYjfryPZbb7vmHR1yl/TC8Dd8ZFWsnAQz4QxNQlkDJ9NqkUmGNe4HemfovCZFibKmW9fjI0ZWjy18vT1hM0FEJ/KGeQ7X5rtybzW8eMyRnIQjkzD59hg77JE8PpJMA3SbC5pnseyuzfl6Z+Cwl7kUkbD6slttTR+6YkvTVkHyR3L9nwjaRSxZ5VI/DUHlG9qlSK7t0zf928EfwpmEyK+UzTgOK5/TVh5L3vz+FF+ux4uJpTy1JimQfJMhgpxd20jZPno/n2/rX7z89fTLy/1ZyCCSqs3mhDbaA3inBjLDg/WLaMDqHrbid3KqdSkk7uHCZcvzoSOMQjSKgVUZXfOxzoUFiAalaiwg/y2lKBaU704R/oSx8+n6GZUmVU8VNsUEcHqtckZY4JdTa7EY3DfcVfFcvuKMu7G35lmPfZgt77LOOMNVk36ZjAWN8k43w30iYwqWnI0tg/wSM2jy2XP63aYdGuX6pHyPdPN/5a5ChdqM9i8jczHcoWip+3LQlcxH71mCklK3dBFXrQlReYfPnJFIsJuq8sc8G1P97FxFc4mI2E+NPATBKBeZQAwi2DDr3TFyOsDYuswlwHelX9aj+4x4uDugvSkolkKbBkePF891FuH5oRhdvf0kT45oYGbKXrL9k3W1EKLXQt1vTN3dmDJHpZ6DmmJbzKNe1PMdELTcXxzoONthxVQnSlNIY20LBmf60rmPnCaslsD0qVpkhLsGfGk+Z1OX3Z2yi4p1D+BksQa3ty8YN2pFg95+WjSQ8ulkIe2Y/fWvt27InG+zUGZys5H1JYnG1WQr1TMmVRkDOT7+RV7bYIflk8hH6Rorh9oSkk1yAZAL4HcwffJpzMgPxI76Q0XlozN0XMD7Nq15rgUQrUKZNZVBTNPijDubw8ZkV+0PZFSgHyyjb7xGO0PftK5VlOM0h+2P7s/Q13ckl5d6nJzZGs+CtyZsEA+mA8gjCh7/SIupHnP3R8+7ydfFBc7e3c7zSxp8SWHBkpfArJ3yahG/GVsHp4pV38gOjU3oN3rP86jqqsi++XZfxOcVcLyMmJMDdz9TgyTC8K9jhhbfz+6syQvkVD9+1fY3nJmEjn+2lgfM0i60Ds46F9GbITzeuFKhgYU3IGGos8Hm+MsuKDT2yjf2VjmTzVr+FAmci6cPHX7bH466eTX8Tuv1/pYFk23VC9IoDEVV/nw0t6jzzWcjMMUb+Zj+ByH30A/ViMd7Ku3frFgQWbG6B38ZitVb86zc7tmgmeQNHx2RYmwOIssSM2b0eVJH49WB11M7Bu5mQddw0JYigwMOT2Q31HU6ZcA3gOWjxrFpiTQpSUpre4ZvLYW6iLPBN1KyG+XsNUBHVyg9Ttxoj3QXvZ3yXJfI7pQ6N1hgndRZVmLlPYt+Avv9W+vxdM32s7YjktfAbumyX74x9eZVrpRN7rznYWQtZTi+H1xHspcp17pWFQEgncUr8eNOp8dm1f11MuEkvQ767ndl0tBZPhxStOO6jvxLZjAnNZWER1tPx88X++LMacsRk7pR6NfUXSFS+F67wQoPGMoP6keC/b0kdfDumm9zRiX32DFM56e0O4LfGYQzg+4BSDVQliT8JZkUm1qlYsxZ9NiaSven9jNxdGbzU04hO0+9CZ/tjTzX62wOTrSi/FU3qBkVisvhzTKm4j6T74LI8o0u4EuLhLZmcdYZctzU8H7L1VAbdz5pGuqiyD/TJ1sbnOCNluS7DNgrfBDXLsjWR/R6dtgNT2TV0Ms2OEYR6U/fxRXlegE/PC9WIYYFcjzp8BeVzYeIUfXBx8ICRwyZs8ZFx/YnVTogxITtlGK7FIXuWZ5wvxt31YWxDLrRZfSCRX9v7vVrtdFVeUBz5egxaiWy/3HF3rwhkmFq+QlFqSD4gF9Brv69/FCQU41vTFfHy6Z6WsUCKySMm2dLdCasMvYdqrt5QQ9GuKGlJ2LPtImIaibjTvaWHFyqw4prGF2XeMPyCxTLn6BlE9ZHWrt1M9rnN5QuwQY63nDLqufAu+D7NH9Zgc8WL6naihPpo5+6iu6gXB5sp1AaJJiOpX0uB1FVUQxHttjpiwieSUGMS2DdFFZSryYyZw08rwqh9b0iu5pxi9hb3Ye+DhOqgZy2jKqadvfHlFiqSXHwBLn3CyrHh9ZXIWQx5dHvyJlXT6Xt+M5KwETG9yETI1k20YvmkkSkJgNjdrKglTcTySPltz8h3iRTujPqGuP8f6NyaEJPJKLm5/KD6Wi86KcACuqSawH6iNKhFkWaUI+eofXFsVGuprsIp+2ESw73a6vsjeHsybYkZEvyW2SRxhSy0YwyCMcLDg09sxw1SuuFr8O4Yh5qz1FGHe9yy/0hiNL529fcf83J+Vt7JGiG0XCPov0RCg/AR3PnFfoa80hLQ5Z4wWQmxlSxXFeyPjx0k3421t4mecG8S/kHNyLFQXYqg20lvRJ66+o1YVMjFQ3rhfwO0Cv7D5m0T3Cm4nXywWmpCmaJfRzqxirAsbfiNCHbu7wlCtlPTu21/vrvskkvEBdGHOi8g/HzoI+ewQIiTs2AHuSKHOTWrnkZYf8q5+XyrTnsUAjlVmvVrICMWapfDV+zP28axvbKwMuFfeKq1+45PGvTekAig+TUXNXjupmtMkPjEZVMR5nP69WujEgTOeh9it+nkjnFc8dQHPwyQ+SSRyFA0hJ7LHbez+hy7s7rEP0i0q8/UKGT9Eqghz3TtpuAfmVX5UvkGbQbGY5Ej95tXnhEDPu4+hh/ZXnZNxA0aAbUj0962L5OMNcczzPey4n2SFjX7xv9MZXDh3aJLxzeooHzJjTE2SbWJz+6j1eX6Ui7CX5CWb5Leurc8GP/95rvNixB67fqRNdT/u9bISfaZJ2do3/sNHqbAKwL/ZPU7mxYuznP2GfM5xEUKqoHHip03EHjRiILnFRjt1Eeln5rDMX/xxY4cD2dSs2RnfTwYAP4pbfxiTKACUnx3+FWsfwvH6v1VCZVaL6yF06jQuqVVbadYGDZDZFE6/03VTsC3bMB8r1HK3CNB4votyPfjotF3cNJRle3CEzKdZMb/MezdADCBZpLCHo3zJqFFJObKPZlYO88srRKRj3838a3hFEJw7nFGEpGQewDkXspSGOtpBU2B6teg8LhuwvVZ/wSLq0V/PPYN6+NWSkc1C1xRqnAy4qiZ935rpVDozfjgGAqeHzX9h/qXcJpdVUpnyEv+yQgwr83SVNbIlhAAhsyZ6NRq3vBpihIhxEp9VLBlG4Fw/PtCrAtB4w9pEQreRVjib8rP760PVftmrXysubaoEXlG188kjrhZq8iIzTqGaJWpOgWpNV0yCSND98K3yXXB8Tl+hzDFCqcC0vddzzKwAf+W2T5qKBngKe7wMOFiRyInSFURbEUKeEK0zBv6eDIeIAlXhyi8Lad1baZWXr1pDv3a6yHY18l5n/DuUPuZyOjxcdHtHwfGG202KOjr5tGjSf5q5RBU/UWr9tAtO2XEainGxsqHtczKAVlxJOgby8hcQGtV94sb4G3tD71TQ4TJRXlrMcuTofJlbpug/E1KpijvjM0G/kvmEDb/loamQaB6IZwXK8FVorrqKbZP0HUKZd+OOD+0GBuMMRWrmQNLroXWuvsFRhB4mFqDycmt1bTxIo4bUh2L5i0x5576VaQsz21aYc8zhTUrBaqAYMgsp95qn+zwL33Po0mbT7FY/Ix6cXJ2zqt2kidNWJN5SvVCsWgS+bFswBpWXfr2577Y7vNaBu+95tqDcYIb+Mvngn151LdwF21OaF43/+kIhleGGE15l38eJ0XYQN0Y3qvpkjI9bcPs0EPCshzll+bKKFOdhW+fW80cexnK5aSZu0PXNJphkNtdHeDYKzvWIfKdF7Aa5VfGgGI40q3L6GwEbwBzcrp4x7ion718INqq3TO+5NAbgEB/y2XAME7Ehmi7oZ6KmCZr+tdATo/UieHfV6IkHOWVLvTBMX5Xgc1cwJJzfadA5YQAy/YXmIXhr57YxJlTPFWkzjuEYIsQeRPmnvq1TWdbm9+tPo57hTOOo4CEJjdM2jJEphHaKbszJ8KHDl1JEOgMTpwwi7Kf8jTYeMaSss3Kr1ycYMJWIuFoeig9fWD+8ARFikgGTimj2ktRPNuEgd/cbdlANK5eSNoVksJp+oLTp0eCByezJ3WqN8uPdltbKGJF0jaC6JKmmVM43PfCpvtW96fXta+H4UU3HLtQ3YtSCHRd01k+0GiSdD6Y0fF5337S3TQKhKA+glaJKRZpitj6HuL9rqv9140go+FO/u4kWb1eUYEVLdICasrdgtIUFTYI3LSOoyPNaqHEopCxe2faapq++aG6YdZQetPTnIVdo/qCFnIvoZm1h9IYQUmxOVuMTHrc0jLIe+lH5YB/nF9cojHXsFydbxwh/mbgtDlCF7q7MqTi2bjU59Ow+SR8+w4EuCKlmlix0L7LN2itOja/DotWL6uWB+rEbA9wUgVm3G3XrKKn3/Bpnh6vsYOHi9vbecCxEQewEvShqv8GOhlP9LldenGWhUHGgNyaVLc24o0VI4MjYjSraSo6+FRve4fIOp/elKl32lp848Hpjm13vcwEgLMGA3zfzq75FFEzEYTvWttIXX4Q4LH5hwY9eZhx8aCwIwlr1dE+VF1Lp5O0q2uO7whY3oqTapbA9oPcKNdGe26SL8sw+a1dkr+930kyAu4TSKtl9vwzNUqIdo9gPQiPYB4sqtzZfrKDmFc9VjdMErsKkqBHWqZ0zPFHPpY3wIlP5/Gp+huorjQjU1UPgC4EXWYo/2LinHD9I6q6mvURw7HVd5UFbjHn5RK8kju/kJm8ZtpBFQmnkPuemNRQepCPPl3pipvWSfEhhR1BNKW3/IEuBpOZpnFM3NKK53ymIw+yLac677S9MBY6Spw6M4Zz7EO07o6+9v/Kj3K2tMEKtxRJykgOj4W4+5aMaAxWyTSjpg9amcCQtSW1bZDLwi+ftx7M0peZwEVDLw0r6c+ZsiJ8Eas/yiUeV7E6OCb4agb7fr5ePg4gYbm2XJdkF5WuGl+RFNAHgLKz2OxxxZ2OSfVaSvTlklCZRp8ZzTYuPexTVAIzvrDi1TBv4M9QoGUQ8b2cIyJ+rVUt1o8Ay35bc8KDe21rOY3LJiImDoBD70oPWW6/BkKjTUWIqhWF1t8QjyHEtd1tegH7dKAJrmacCh6ImbsYmmuPa/borXCSCDn3b+MF3A4cXhHMzDzf5nu3X7pqFSzzX8zxHt5VQ8syQtKV4T/LCWJMv/cGX6yIgFzjAr+R8jgZ5O3Zl3Zuh5rfLYtMqw+guZ6F4r/Ip82Qak1k+d3nS7EgC4z7b0WZYDOGbMS1kDFdphcr9UsC6ly0lBHaC3ns7monuPKYFwkvRgJVu8yOpWLt4xWpYv61sCR50DxTY+9MB5Jpp5Kl3WSwYVK6fWQm9RaslkS4vqpFnlij3LGtO2UThUzwzDvn+sH9mVqbR5dAf6n4tIL8Gwey/2ZbJcZwrucXB7sZz+ZFfOgtOelLesobTHGjfp9it8g2YahQXZ+kJoCUhglwMS1Cde8h+29wjrhLUHpw73GiXzbGKrewbEGTe8vaQLfs7vNzh5Xj1qbEN5NdaJMdlRJ4OL12f1Ibu/dXumJPLvjUS+dAasaxbnK8EUhV5ElOxg/JWsvfb93Gca6B4nWD/JJsimojhvnOpCYsMqVMzWm4d6WfnjSHKK4caAGRWiXN604PkGQq8OYyW2aU/c3gfIAIE1Gat2RDfIkcgNGV9eM7vW0PlmjjzkYc6MGTUMy/B7WxjiX4InUhiilQpkyXjQIyZHYIySWSYdwpT7RjiMmQUpnmeqBaBcxRZ3v0ldX8xAe9njSicTIAnZIGgCQm72VMdQYsnzucY9oUNf5fgBtTDq4jBv6RKoD5nm4r40Z7FtsrfwJ3K1Gey0KBPNJRhlSd/+qsY0NT5Rezqo3EGG93KpHQYcLzWkElWeSjR5Ijtv6mCEZBvBl8F4woxClqkXog8N6U0o7QUSf4NANX93tj2ETdnLCuQt3SRInpINY37VGbP2Rrn2qwJUEkIFUgmC8J7hN+BuFtBgOYNuV8vsY5Gpft+X1hG1o5WbAeQrw39kKEfjAAzH9rnvcwoBii/uAbLyim9pYowe51fuBdHo/j+hMfb74uotvIHxSsw9D7Nk13zj8u6qn8QrqOi9A+mzeP4ACl/DKVRsJlr0MfoMm4XfLwoTP6d42gB+3Wb8NI0PeHiPG/ceLgY9gqGSRIOu7chul6XX/1sFPPbLsWx7N99i0KcC7gV9DlfiZqG8NrSdY8+fHhpwRF2EspDkGrdjrfzgtQJYiDJqdU5ktM+wHRvX9G8Ez7ZTrtgU36Xix+7zy7qtaCYruYLPMF1SvXMyYH/WZQ8XwSpzHBZigv18pv498nuCxN0VraQoaV06gHMIOz5ey28IhwUcP6GSoDFrady04TSFoV29Crqw8A/Bc2yGhBUKPn8sq7BClFi/1p+uK/n/UF+KKsAaKKeeHZm1JWEzc2Vpav+Zt2bxw1xDR2sv04ekSiy6acrOTM73g/rMdf1WKZ7KfrcJcYd61+6dnBfIIoprImiqZLCF73ZoujwNUrgzozC+ugMj73V9weaaoyiVxsfeDS0fUfrRQlTOtY5BfYKq3RCa8uqutnZt+cwXQMhfklY2Pw9oI7X+LnX2YVjY415setku3klJlZMKWVUDVARty8yf+oN4ptvqnHtK+zfeWXGpDfohUUhj/Vo8O6KzCsC7S41K7NoPgkBGtUSp2B0aOWPPldNYtysHk5CLf7s0ExOERPkmiTn/fYxKKT7Nfx2xrqnIjRbdxb5bAX4UUoAvcvDr9nL702DjspKrbE0dD75S2+z+dSGa2mFjMKueklTs5zcDMbdd6hh7AtKcYpmsbbKyqLSXPNDSUK5AB3clhXWfvydXhY3iuDTg4Pma2GM8Jr/MQKi+ZX2/8K6wumtGs0FYBFwcf4Gu4dhlp8aispcOIj2KMAU0urqbDwseDqJ8u9D5rLWu3C93jyVRuUs8g9gS9paT3JomWEL8uCINeFFYbnmWgP+N4L7pJbV8rGljB4+Ala16cfFevArx2CZACCEqdUA7MvrCkHnB0P9N8r1Lra6BLV8fdFMnK/hzlRwnyoOelmJmfJpR7lJf9e8mKsOIX1qymz9jdtRk+eBMo6Ci0wpcIq6ItlhnahlQRDUcM1ybflIqcH4l3uvK2/vj2tEjuO9jj/ad/dkv60z17VwxpkMY6V6GQlVH4QQdCDuFwQMq5Jx3vHwXp+Kr1OPx/nnT8sdAnffoDeRVEmY8tvbm4s8OblYLe4jZznMNWzj7qpGsT0vp+9Of0BzlmE/gwkyhjXJ4aRXtDj7Si4jhlXOj2w1FvnlSeTh28HbBHBrhEUCD2C1bCGVJhiDNeXxBZ6MQokn2CYP6uLbFwjyVFI9bmc1utV/yxWqnrLzeHrsBR8HC4uq19kMJcKBR3f3/CWL0y4Lky8xmY3yYEfERXfyhyGWmy951+GY2xHUP6XHfcPsx6jH1/UwjSZBUe2lOQ2iuahes76pCXUeP6d8uh+2KeVyrEctMCJg5wOVX7cX244u7rofsdbzaib04g3gu/PPwrXwoqdeuRC0fEGrsue4vpIhmUxC2cdJb/6aG3iYvLWHNPUIBvNvoe4/xwlOKnkzN/ZWy67mMhM+9h4u27SASgdpV3fobueLXW9Rirw7tR+M5xfWeaRxjdIR4eE3ed2pBvTGlzJ3O3gQuoz2RyOwz5cq/5LKYqfJsU0/u1ySqZl07b0u3IeYhvdQEPURK+NY1abByEbTcAnmDejXzq4eXxX7+TV90a+AeA6sRVMuzoXqKmKMrbOds4E5huJ4g0IatDq9NQmd4iuy7wEj6rKct1iPc3g8A+aLASdvHR0/Zaiht3HFctqRV3+Dvtjb3c8HiMDXi1W8z0qH5ylsmocAvvprItuIrv+5LhaD4sNE3rVj15rxYCtzvP+J0N1Ihx8Q6MCeZJqvOgSNrRb28Q8zchtsv+AHZqghJFdu3E642f1mGgwXOOLtLo2pkc7y19aVOCMDoYuPSZGsZRU/geVowc4aZmieDajsFJFh78wX9nClRt3oYMOtL8zFv3TpXCb4YekZxUrhuS0ydD5m9ZaQsa6V11TeEd7XlGCZYvDQH4h7rCiOEKR70duQhe0Dv83L3KUyXilS817hsNMpCshI4X98sIQIraTTuptf6oxYY9ZgSCMuFdf+Ki5jp4C6UXF1tQYHnJCJhMKbrYzGBYfGyGQ5O6o+OoRTz7af5qbUixqcN+xzlciCTLQ5jMcHPpDiZE5907qPTw8sqwoqUEv1keFDci5z62ytGPkA1Yrc/qlPbSKSFuYx7Re7fYMkafO+b48z3KhRPzh2agZNYGCFwH7RyC9GOUtmvwzeec6sdz/YS6ZxSqOh6bSimBzuL59GSxH1D0VDOhWUNkAQCV+YDx7M1Yv+UMMDOExK/pVlXeFbuVHWBn5lIlhQ4PYqZsuljeFtRdrgLV5F/ySQ6UVt4mBxLzvaGNQ5i/vzyp7FnxKaw5DlkN9RsmNK7SSfY2m+VjzTV1uCmp7tE99pTZvGCoEZtyKU7P7V6HfQ76WLl4pQoQbG+GkM6lB88TXYpU/nLiYxJ+U9ADzlPSbufXDh8214GuPamhS3HzZvtCxaNj91flEdZIMq9U7FVIiluYJQkqBz8WtWQZW29gCSwn0+NTA0uerNzg9LUWlzS66WVnupdBtw5wyyf43FUmK7kHVCUm/+do8zOnMcXsgmzWo/omzAaHl2wCUeuv7OojB8TcPim6tevLfSMPZGlWhabmi8JgzrZDvvaU2qtguQjuSFfdWo1fY8pUP6Jb+p7M8cfnM2MSp/vLZD41xRn59ESrJ0uX4dqIGWECXyRFRkaRCnezugh+0sWKiykul2aOIQ8/7YJa2dUuf9982Y6UGhWfje467WVtS6FElaO5/UPnHltSXqmy7mm3bJMZ7ahGgt04SwL6Pb6LJd1x/UX54Q3l47cOkkmVPlRdAU9Rv+4zfOKMXhG+qEUL2Zumy+Spvh/MPWlxzZYaeWg8imJ5D9yfgHHOWfB/i2PmZHcHtWWmeW1sCJUly3DCkTWSxJZAGtLTDFc0XVYHooV6rcL29Kv78By4SW5Yzmd/NDvpPF+bUk0osHZXwElFA4Po8S47ORD7/Kx+7aWkhWIbT8Mv6DEeA1JbNbdGbm41fu9TqOVnqTthnCGPVYeTEj1TGbNl4Ej5tz+hg+RFmN6k7/nVOeJpdq3ggR+iNguCr5TCENbebZJ8NxL2zJJbp1qEjU+eqlaGlsfiV9ypLzcfvq5/oNTj2AuyhjFDK/jlBg3WU8S4AsJKUhaRAQfEjptY2B4gJWAERDrNg05yMwXEc04jUK/6yANF4cqjk5U15CQjevrbpGlLSOxj43v/gd9BgDTPOCCuT8vOgMCp5dJ/q7EZLVY6J6zIzzDvsTDcOUskj0UK6Xeu3vTPDr4H49lF5/WOQ5tjV8TANUq2p1xWeEa7f+rtE88DmXgqVG1SJUkyjWm+BNTaaAfvdEUOEj3EOP23/BZUZnJ1yOnusix0viQ5Fd3k/QpxbRbTgpNm9J+S7cEjIt0JE1dqTLpA+TRNVY5gV5OjMP8ABizl/PniEAFs86avzV8XgMNyofY3BX05KJuU8VisImSqlFTXPfTMP4IE80s6/3Ht4q7d3ifZkeS+AckCsosL5HoGayNf56Cn0cG0ACpO9yGpWNh0iP2vPZLLhsKmphx9SM+JuVTAQk/3jah5u5EEoAhpU93q34O8NjSuUg1Vk6Oxcu9I7zFGF0htn5b6RzDs8oShFzLbgAEybZgujNLZawE/5AGq4KWWnTKwW0/NUtTSz5yess+QDgLRNn5gnNK/Gtc6AOmw+dJRuqMGcRHu0OEJ+LtgKACdHD3HdURtEos6kqQ2ub0+CWj9A2YUiU/BEBB+GzwqhBWGA95HEy9JXKRUKH32WE2OUWXyDmWKZBuwcM11xNedGaQfA9gazFFsS4uIGOLHlvB37bz1sSvRBejx9XL65ZbKIBDuVHfOuwcf7CavJOu0ErxnKYbnFO0p3STLW1lvjh+QH+Bj613RNMeMsFJ6yC9pWWcyN4K7KcJv8lBkk59Pz96nbwBnB4IVEF0lfgOPIQEj68y1o821ME+LyldOnCGCZRra75M3yrlKb7DMu4E/C1BjmTZI0Gzvf9nUQIp9ET/JgT+8S/Q6bT+fYrgp/wRr82xEKuSHMf/AmKx8SDLVC/zdF0vL75MPtmQUjOG6coU/zpdyk63HLYWiaMc1g2SVFVt+x+Dwc6Jsi7M6dXGI/4Keln3KteD2Lr8tEoa+P9KDaFB6mOxDgWkBRYUrIO9/Mgz2WI8soA43JN/1+RNPuReTRZX9KtDrMzX7ZL5ENRlACbTa9iwHR3evfB1EVbyiLpaVal5EYqOpfQ2345HXEomGj/Whh2yZClCbBkfM4tVGhmawzPiFNska1T+5TsxZY3kHpYCYS1J5VqWYKbbD1hZB/VYzrdpqCU7kE1nXV6XQ3tFPRUb18GVfA3RJBvsqkAbwO9eWlv9PHEPV665Nhrtvi0VyI8znTf5kFi+2+QGDhAPha8zuk+ceZ7fJuAPEJs/mKwbiodZz6uxF3S/dt3dJU1ZDFGnxPubaXW3YJd8B2f+sikSLMNtIkioDk8RfMDAMLYVU/ARI4CABHzKFVDZ0oK4xNsKDjd6Xgg3O4ecS9FEQIlCV0RBbemYbDpM4mHgFdrE/nbAfCrUckRijjmUBR7vcVfLfmVDF1ulbsTe488m5AOFQLGgZhHX3/WyqIDOvJ8TcweVNmbQbSwYTzsFCCPFr8F/u/jN5vkZsu8E/w241Zu8wQhuQ2gRMLtgpeePeZ5YrRxG2LyAXqPC8HANmSS/t1YWOAC2OOyuAevHii0ydwQeEVIjYRK+rk0Auey9iwp3s4Nas/ovCshzOEXlvFIFz5hDkwLFQtsxvqtbHABx/svrzjyz+cZI+FMRXfoAyRYv/ulX/XXuLbnG4U0T6i/vEH5QjDvjBGPvD+T+5JULjGSqFcZ5tjy5c4ZaAtN0CqLLYo5Avd4SrjlPH2QaqAlwXwgnKk4ycZUlNu7TYUth4gTeujMOrRHaQdZTx1YNJtQJL3KCdDmF0eyocqyrb+XbSuS+wjXdDxUPXAxSYCD3sOuwNSqstT4s98725+fibxr7oNKoOm4iPCfP/0+gD65xop3FDzYwcCWYNglbAIZdJLfAlki3CdyJbEHqrJdf85kk9Nr68hd6XGNrQTWF5rVcXnBd0CitLbpRmJQcIO/Iva+CwjAc3Ze7cSUbuVadIVLAZvvP6fEmc1BDnELY5hgHuf6oNnEVlQjGnXiQKh/1WySBf/AuSSZcsqgElYeB4+m1GsfYl95cMuHupWE9W4858y/AHW8f9XdISrrYXmAHxYvjuf9wJ6x5ZcZ56OHMNf+0vo8+bvyDN+WPS764JHlZBzQ9RuKctrsHxZiZzGcg+sMIGPvnd4ClUEs82t/cS+0HCGsL86z8lUgyRmErYj3eZ4ePPYb+PaR1Jmsa3Om976X+9YnD4sgwst/X0E9NorVFyH+4hZ9BEFxSsL4JLXA1xZq7F49KuPXLGZvmwN7i1HngX2B2y5HlYI/GKTqEPGKFFj4DZ2F47Kz1VAWOwzVEa/Pw/aipwbaBYJhayfLZ9Hw+bi84sWVLj3VGFQAoXy9nQmuNlaJvv4H7T68r8acuiY7NZzPVyN/bToNbiLwJybux26SYLTcfoZSlhQfA/Q2lfjmu+IapsWxINdu7Rmg9EmfIRJfc1UZWtRQUlAQ89mywyxhTK7rB5wRYBUrXWMjgRll8jp+o9Io4nNXHAN2MQ2IzhR5IQLcyHxbmO7bqmlQlrf6wiIu+fe97x9RHixzqBHBp0wwl1m0xTx0Lf+rBMoGDG74ln2Yiu2woMBFcXaJwDALar1/I+1NJQhRFAHdMRgKqqFLghENOKsBfRdmdIiETiDDx7yoVlgkwv+u94sooqJ8L3XEtDDthhii5KUYYdH2YL+WN8j6X6uyG/yrjUma4v61I1mQHHbjLNIB+GHtKi/yF1eMa/PWmvWKb36hazGnDHe19/nuLCMcZEIZj8f++G3fwy4k3pP/LmxvfknQ8R7NCMyQGMfQUr0YaswEJ8NhjIzf1C3m72vgKNBN6xssaj6G9AOZmL3ZFSxrWEOOzOhTGlX6UMeWAZ7Wiak8Q+Pp5aTZ9qICj0SZU8L1/WHMmTW5Lf4s2RV58/bw5zuGu9u0ZAVuk0tRGtcxjgT5VjK2T0bqMJhgt8vDokrGCehNUNAg4yrFJWO56Pz+C0+iYw6HJedv8aUPWiBqnzqBQ4X1uNXe8l4Z7LzEWQvSEIN3/94HWFc0J5fHyqhwJ6H01TGVR/rPkd+PZ/EZ08QBjLpWLDODBa3cEWeLi0C3khmq+vWgfr88B3motleWik5jSOMpyx6ZVs7val9rptSWrThGJYN1YETz4WFbN6QduRDuS8B0/xsps7k6d5f5oW4zjIvSGYw3RsvyPXFQVjhtnAEx60SkNnO1jUqFcR22sdrnthiWhmSBFEevAQqVVs7/+ju9UbkgQutA35vBH9YUX2N9yc6aEI4Kwoh+ww9Y5ozvyylR3U4dCCRY2C47qTjm/K/H2JGT1lnYN1u83cj4eFgC3/aLPoqcKXaTfjGoekLunt02V6jQoqiKJof6tOumzFUsSH7iGfBxWYZgkFH6n5tJTlfNCQaLPl2Mzw6+GMdLfcJv6oK6nv2gybpJGun4FS2FA22XU5HOaSstxy76QBeeflKNcBrtxBqDrTujbnKf0vNHjbrcYYEJcS6y9PFYdKtmXOFGyhecTV2xeYRa6lXd27UB4E/sss/AtwXCokB3E7p24tzQUZHAn4kmBstx8OBpWQLmLLdwIRjkUaXFKDXZz/9rn+xQa56J0FKDyxyGxKQGh7MqyvLYkEveCTkkJZ7ZiFus1pGrXq+AInmTL0VF7gWVm91fL+z5tbFGVB40U96Vg1HN5UmlHjfZoPsyiVL9soDiCBZLwNzYl74tig3oP7soJo/COPPWr0+96ojhhrDxxP4tYqjIzMBbnipfn76dXHtoKKDOCzAfywtk9sYx7TpNhEhsn4SC6ZIhFpUxn0d+eh/9Y+EEj5glOxeG/TB98mXsX5Ip8ucr1jqWf8HtMxkQtNK2vpITsyvpjgvABjx+k1j8u7j6mwAq5mCYLnuNa14JTKUshOtrqFWRPBRRvzaDQF4lfoLsnX2+kqq3CPocemkc2+Fe0jbainPcg7NaE9kseG7g3VfNWCzBNG+zeDBHLTT2siQFwi8PHt6ib55isiPr1+vLaWdlnjAq51DgHT1AJlQ5UbscZxckfEd78cWQdPO90vVTkaxuVYk4nzg7lpL3MvYvMhnnELdO4wUCEisQZULwIbSOLpdci0lG+UXqRunodaNGYXAHp95q57nBOBiIzfpMxTkEoNm2r03croGFac9zaUPK64FLXS6izwvnEiWUi2BObqnZPjpKCz4H3sUiTeL0sp04DMku54wrfK+USSZhhz+0Rvycf67s12/EFT5K1HT0R/j8cpduhFXJYp0yKz6kwGLnLGkylMtaUVv9qqTs/LouFMss42X7ojPNMKQ7RvoWTbMEQfA0ixYnGGk7UyV2Fzj7YsIG2suKAjVIv7Yqw90I03Wjoz1FaVrII2UZNIL95oz+RieV7RemKQlND+1VPPgCiACfY6GRHu9OabCWagEnDQVwrdFgcu4+HxMWjirrqvEFkxLQW0vyzbuYu40l3S4psY6K/moCnhncW012qNhntqgTnH2z6S17/bcpyuNkVW2urRKk4Me7m3ql4ZsyHw4j9I1fbH0OJgWpklqG0aeh6w/A1oF1GB46nGDDzDAik99AlmGGtq+b580LdDoSN3C6kqIrr/shJRTncLfy4Fp+f/wJ0ATAOD7gqz69jLhef9a9H9mkoo2oZTPMzktrVquyyJxjmnZMgOa3aOqlDl89HvbjzO0qOBXkhEIh9ZvuV0y0jcJC+tDyexeX+wN75ZRV9tprcRvp6mgYXNlHuLBURPTNWtXrjN1rl5z4p74W7PzE63a21gcJcn+gNLppCh3Zneb1CztnK7nfMK/7uPiCHdKAj/1c6/TLA+XHNVib54RmnKjSJO706wzisF+zuWXJ8ylnwX1JFn7hEEZQhTuJ46jTrnLWERlXkC4nh8SnKMX8q5c1XjDrfwTIGMfF+4XAQe0VFRPNsuD/IeYcgCelfu2O7jjOR9sH3Xy4ytsbKOPLzD0mcIx7hMb6jQSaogiWeythqI/JuryvorwwarCXwdONU2o8ly6PpX/5smMCleQNTRnhKfq1jw4m0V7Pg56eHn1PE/ziYnkuN78389oLyG/3AdEAgMy/NEkvt+PWj42lCwPbdB8AwHAPPb07BFvu18MFfOjZ259AB3+FE3bABTzmZEzUfLA4Kkd0M5An8ctitgdIRIOwfUjzG/4OxVXjlZ8DP3WyUj0XPsiRtN9fxgk7wWdNdNrf3hzD3Ptx5GRZdX/rWgRAFgWgQixOwvA8GPKxGLbnubFck//6DJyEIJLenAADgupIjnvoimO/Y8KkFBC39ySi6JHoY+P66McmTGjUAhwtyi0JkkG08O1MBE9+/ccoXLh4rq0fqze2lGw8+GtVcFf1UZgcd9TFWCadyQ3cwNiaKwlx5MdTYAkARBHH2eOE7LywaS1IgfDwBM7YYCbwGAakwapfHpAayS4O3u6x6Sguf2nLtIZgYSHd08c5SpedKGxjFI1kegwepNGtV+srB89hy8NtRodVASqXrpbFIBJL3nn/vHRNEevyFdY4u1UhxJGwahvdmDQIACRvWQ+3yNMBCC1PxAcbJKv8OxRM3z8P+Ikivx6Qw2dY3aQZzrKiOOKnU4m/qfKnWONkuoSUSAcHVbWVYMpqm9GMynWQcc31Qrnal0OP8wg+ZlOaqanzP2R1MWH5hDWT74gwdmnUJMu0bbfhOVvaLICmeqIT656I1wRFYrqFBRtYoxMYEE1SA/UCfrtkgNsS98uiDMyyRlsTyWOK6cTQbVg15W/Vb5odj6tzQQQizDS9yD5ZzA07Al1pfCkWjytf9wwmJ6EGh/hitfotDGmpuTPgXeSbcUyJVeGAHnTSHBqGaPE8PG604rLrMch9yF73Tz2exIlYlmAkxR4mbq91DVk6uByI1KfhgG/5wNmYXKDoFARncm0Aimiw5MfpbmU5ucLORjPnfElI6MBrxj7HBECehvH04292tmPJrw+5IaTo3huBhxrtXV/iY76GdbcoOBQkYPGonHyHoIQFIiz6A9IbLmBNlrBPnKKA7QtW+sV6UmnV1i4o+gFHkOWUbP6qgAWbcHu3VKd5I//d+FcTHTnx81RQA8Gcgw3xEP+aXwF6/Uv/nFPibBI7lLg9a7EzLK8mfC0Ih4zICjM2uHEgXjDJLMpUbGkChKSwpYFORDfOA2Fa75UVVSm/coU04jTfLZpEuyktW5Gdcak5P/9iv6GWpnJbhKpXNLxXDzG2nZh2BQQW5rqyzcfS/hU4PuGGmbMnwitvTZxzc8N1LsSdWvj6EMxmnqvUdFgTJRALoNbH3b3l37u8ZGWKDo8HWMKS1hyNrEzbEazS5N6/Pk/NEL2YHzznf50AMZGXyiy+VmJT3utHHiIplery4Q9/cIWlO/MYD49w9sUTDB/k/MSjKJl/KQLtgwpCY7DJklyuZk8rVmPtFHEOz9BtTT7+mQiecHZGjEbauuYNoR1UsO6r0WftsL3zKrelV8v48mI/VfuniwjLw6hynsHDu6J5M/yUI162a7m0PViTFVL614C16HK9qhJWGUqCTLDVCSWvW25/twYEGTJWL0Xz5CVxF0bjOb/dcwI+6GXtp5C9qWUD68Zcv7JF94f8bnCgYjaOHjRdGNy19YPCbrofJaacvNKUblSf56+4XtPsL9Cwf2gYLBKNtqiqiPsHd4UF/ZYGRmggXEzfL/+bWCWUvfAcbWDgJSWm5EFBUPu6sfiTO/IkcctvevhvdkCTlOZFDNvy9gk4Fj8O9O6t+nNSpRC3Zq0ztOOL2RsOB32upNZpkjmKXGy6M9mpUF+p45dRYrsqH9YX+DgxXMhu4iv33JwqyWF1zps+ZtgY58fLxKBLOxCS6BNoYZAegKhMSylWnTMDiYLqStyAt5ZupGiTJ33xmw0p053slf/qms5g2OT3aQsut6DWsJdfoVThicFx74OWdcwkuEJC9KQdDp1dGnZKJ4JhKMMvXNKmfSKd2sGMBHyIwo/Fzb6DZu1l7qmgw8ZWWCJsYhViOkmmMzk9plD5gGxi3KqSue0kEFKKGbd/rRMc/NtHdH4obtwjf/sjZav+d9LFOjvA+rN2AuJuTHwUjb1/E1S9i3nbeEwHpwJCuOBeErEJqbtcTDWl0uMfbgjEPTE1R0whU5pSZ4/6wnejZT93TvvLvU5RNonpFkVUBTB90SqGRlJbVExpO956xIXHQeaFxf/0vXcaoqcVHmKYUir6+9GNHjLzU0IzSXCSvUb/XmtHyyh0IHWUxyuYUl/iw7aagTytDjVhEgbZj59OZXcVXkJEKrljcEgGFaXW+1wwjZMMHaQd/XvGjNuK4pAhSvi8rK3ZnUxtt0W4jNd+3Hpk4sfljTRiFICGc7+UHb6PzVDwsqefAafMSvJPx/Dt+ID33uSYV2JWcTixN/NeoRF0bv++CglrKlIJeWhYxOVrEgNVKpWRDaK/fz/IjPqzDnzIgNVIAXIunsw2eCaPMIBxDRthD94/QrtzxDVwFaQHPOCjh+ZY5768Ha1PYleLvi8OOQMzt/amu7Ov7SzcMSqgTtEzQAZUb0MolEd1qgDhnWESMT432Eq2Bbt9P3QGdpPDHyf5AU7d0K7m9/HogC+VRpE0afg+wd2GT5luiYQU+iB1bI6VlrQckPp5N9NHxcoTcmmwvsDiAOkijAuQx/ILAgf2q+am6QGTH/rqfs7/1zf23076ocqvr4rFG4+5U2crgS4L47Kbe8XezpvAAeC6BBTSdb5aTY05ptEAGVKEfE/SigYkG9A05cCnMCM2G3+qNeVFlTGj9UHhCFEpUmzDvWXVFgmJuBuQXA/I/iRVM6X1g1YsCPRTcv6s7X+/W5E0/Wy8iBK2iDPVg3et97c6WafrKvk5wl9nsmaKvwlnThRFqFOQgEqLCd9IyIhIYQ9/U3tm4H6+kJwMPjsWD4IjS9invYM+lpZTNJCJoDPGnN1BGwmi0Wt9fBiUN717BYNlAsyuQVIyK2bBKOBdgV95e9AnbnWTfdzU2+ND0+V08rim3irPkt/yreI+99a4TANk65dhw+tjnq2NaP/RdBULjipR9JdwWeLuzi5YkAT3r39Uet5iNj3pNFRdOeeq3VQOk7OQzfBQBQoYsva7kzflq26Mi7+oB/oFMmatUocJ2G+eOCBlLI9atkQUKJWv9I0HI7r8qtYswvtSpBntLpqWnItaI44RPzYIhOC1vYQQLxmOazeu0YOeNBDmMFqj0B3sbaiWs18NA2o+BIki1IRWuSgn7bNK6uStWZoFTFrllFK6bGO1dpgElrJYJS/1R33x8FkaOHrGLph7zeKvfXc+IpuuPgajV3etwCn14/U6jr23Vj6H6rI3Mrpsuab3kAfbsr8OOmyeKfVj7zW+fe785Kx3Mlo1ugUCU5Loa/ncBUUr8KSeeJbCRMgAi+RZRfUdaRVcLqF0cfGV8VwpXq+AyjPrSL7DWgjASFTKDWFtjCLSfZoQ7/u/DNwjjLncU0nCy1VDiumUVR5qJiIC6WYl7R0E0cSMCrNoOzwtDscAVjSIDufXr+B4jBwXPgKyowaNFpomTu6nrZfWen48WllnaZ7ZhprpZdoDhgOJJE9avph9P+Sr3H8RcZDyr2ojnBwhjdSChMg3GIUmBifpnf8mDGjIg3nQxnO8R0Pa5RW0+jVVIGlnlran5mgrR3+fFESxIQNvifQo/VpGi1OMKtsO6+ngChCdM1o7Ct2+lA3GhHHiJBTF++CQRjUcxeUktO/24P1S8eeZLJx+O/pDaZ2vuwuUC03mEmCikB7Gv3IZzYKl5M/SiNSVdUlbtqME+iSApcH8kd7ATIjHOXb0J/Skv09uIXpI/BuGQrUmHnm90aDQiqGI2TPNtrcNNT7/Z5VYwPW2v8A7y3w/oxbLVhPExc18sjtPPhw95lrKDZpxSsB+00mkuvJ1qBAPEc2QXUzI2GkPt7+JPyayeuH5+EuAgU5KwXb34z6e52Vx7VvZhmXHClTELUbRhm8cYpz3+sZxuzlDOZ1tVREgMFd9XhPT1nUzJMH2LgvFnKe7gi/0Sn30XtRwO6jnGK0HNlzo6s+0SM8M0zbzEqP6A3S6H+666RN952H8m71chg+J8tL8E+qzulxQeM143TBYyggNB9Ws4nnvRgB7vpzfrLK373jCd3jVik8pTueUrZFgNJHpxnbp4RqR8DgSxG9CA4VstfgiJySF0dzqfbjN0BVHSRy6c6SyCAGmZUd2LAY7izCs40HotLQpbF/OJxXaklPFM/O8GlYbv9uo5VVhV7UNZlJpmW0vSRHiiDoMCo/xbzb9LLnPJuQuYXH8slVV1KpSjn9reBwC++orbrs3J12L16+ifT7wpyBVErySE6WERJUm2BryfVyDmtzYErfM2uqsh7xin//Xn/NGDEYuft3Eoiu0ZKYSuUgXMKI1rukfGAhcDSpFtXBR/jZQZGR/APVICn0ScwGEZNjjNN/DesVvz8Ep0WCTX18ntbphHFerHstVC1I4fAKGhj0qVZVohxCyk5KvdoBZpk7W29B4mzw8vgpN4VXuxotGZX5OR1+jyS5gWWFQMukBf8EjYpl85/xD5rlFNyrCGz7K3dPXEljmLkl9i8O1avpkkg+1onCBtrb9/EGcUICSHqvOD1I5ZPdtfMMrtOkcuDR+OHw/Nrv8G2ckiknnWzBdIUS6nzjJdYPjyoBQKWXMv7et/Hjb5mpZ9YDbUDfhSI4vAoPoD2x3Uy34TMmImOf1r96G7CZIbUmZLkK+By9EpmlsfHn0ZMxrrefXqpY21Dfh/xqbmVX93tFJjfqFSvNJYELyHaWKJ2O9hbZ1rVus56ULfJSDv1HDck4v4aGCzPO3qfHXi5/mAa6NtBNPI0uuMsVsGWv88DVLEbIGYrnd2w4DQ6noImTG30rejVQOi8ey+ovXXWU3aMe6Gl/bSF6VDswpwtFxip5m++bPMDwsG66PD7B/8eHBI7hQRtnim3n7q6+VCN0OB0DlYomFBGVUAtJ8lv07h7J8fkhPIeRhUguIP/n4Ct+SBIFaALSvZ+PUfGxp3vIy7OeKgxIUUaZAkJR2rZL9xPBitauZwbjJMmPyYISM52EEDJMS78vMfRHJDWMvZTVbgSvqrXkp/yAb+1oe3YaAnQqCMKE1jbI+XuBxCgiF9m7nn6TvLfhLatMQ5pvxAzycrB6B6jxg+GBHUccz+35xX+dIz5ZuLROvhXJnh+i19dl6am4YnBWVCE07K3HsCtE+Y+QNX80IYEddxBhxjth62vI8L9XDVqx3Hu8uCLPIMZYTIhNNd12aEqMw7gsWZZyGX/chl3Aw0nIKj1gmd4JjZvGIKreC3C/F/1QcLqGyyGelaNukoQNAI8KpwBSPByUB5KNY7/gxOfTBndL1qdoyfS3O924IOut6//NyZY1COj06GICosZJfUp6ygYhcAe4xLGeHHG1QVvgZ6tS4CtByEii5TmFtb1z4h8PimjWUPGnxQRJp1cPeAmxqFomUSOv7uEcAQJ4sID6Bk+/QQZoI+3YH5y37lAM5AgigVp26dkSv+40tVIS5S5HCKaC6rZaJv5Q0hb5e83JeMP1tYkSmEOs34tnaQ7po4A30g8tMaBf4D9ohjWy3Dg+EU9BRLFQYeYVJ0M5elcoVgjjHlCHHfodvKDcE2vJyTWvMzjeXCJF2ScRUIukDyKgt99ErWOBRsUi8x8f6jFLloBHBLBEFVKRtXfpK36eWQfBS827ELBRdv/Lh3XIfKv0tAkIdyp79brOIbL/4U/qQ6PsbhG0p3H5sIYYRcA5I9LB1rC3fl61J4NWy+m1P7NypIPLfZzxbhCP9SiEsaFa+vyIsf8QA/jVaipmbavlHWWZTFip0BJNqQScpy4A6/F/3DVZr/hK1+LRm8uOBq8PG155+wOX35LjxtUcrSiBme0phjZDTQ5gFLlovRpu0epJYRw3bNBvYaJjemRAKyqYVb27TbP0jBQ/bvhLXXawD+XqRq1ZSTbtVJhGKfwVZ3Bz1Lmkq2+e47P9KpgF6f+OkX7yhxN3NwHPQbdtomwmFx3g//2ngsvUDTcE9QtT7BsU9vxo2HJIn1CP8toFHQK2cD6QhMJ35ymGq2DbnJs1XhNXOeuaCkINNPmaO+D/hgXZxQF68wZwq1nPXriDSjG5DNkgDoQ6w2XbDwGsKVfVOIy6kMdgUOi/CPr28YkdIOYnm8XVmPNAoRM3j1xL5l+/K02BDTFA5bPvwtYdhn0IzFi+OqUhGOa1m26SVo2WQfLVvNAmGCfkaL/uAHzq6AR9MMM23TxVyN2YYoJ+QBAL2MmRGf9Px2wbB8Dbr2rywusssPBF+5VnjCYTlAD/r8xT+hqtNZ0RUQpZtY+n8a6D7XHd0veaVq37h+1Sy8MpjSc28sj0WDoCE6XUu4rEKg9a80NItalx2ClRFNQisI2GFujJuBU3T1Hp/mYKFfANL+HQ3UQ7TUZsNxpSJFiqrXvWYhwYZ7nv66mpniX3F587U4BAUU3ZuyJBWak9wsU3QDAstPQiKhvmNwLFTaXLFwivTeLtluX5dMBxDHK3AUNXOHFcnBoDOKIEzoDR8eh1QD6I40Q76U1gU5y5LY92PlAGI7AZdo0HNqkNzjVLZc0mGJ9CUcjwQaqjZ9/Jtg7FjnU0jHJmy5MjBfUFzRi7SfMawdB2HSGnqvykTxowmbebwS0Tw7o7IDDRSt2XssbBOZ2G5YGb+FnW/teNEy3/kLPkU6MzkAkT6A7d4xQr4lMqOcotIAH+PsMBoXJ2t7NnNOJU/IJUs+5ljuJ5w+dwPHROSE1b64Gx20o+B0rhdsMXHOKBi89ssAwjO6aAr6vCPwlvnK44xDiFJbDaQJiRlGCREi7fx+XIctsphFvgMjU2BUMxypwFe+1mU5oqIuCKGLIzMPMtS3WhyY0j+zWGKvMe+KDkODtlB993/iOG0r1uSPC+B0+iu3MAll2h1XI+8P/Scmeo1ZC/zNyoAvdB2gNJCJdY5JYzkQZI/bIveLW8+QPCYPl8sq3ucIj/hEPr45Xi7XmSAk2SUzGXf9gKvSgjxm5YWc6u+y6BlWfu2v+mnPW7HYYY0UcM+j4e1I3Kuy71PLczEa17N801Re/Kbeh3MwBFYvxYD+iHHGbs9XuqCkurs/RPRKbz4gsCIZFMh6ZKjfZab8evWhgZHsu6RDpMVeT8EArjF9gUqMaIPWJwnOl4ZdXl2Cm704dmVC/CQUSkdOcEfFjukdcFJ7/JO0K7DYUKQgNItYQXrzFjQyBvjlK2rMDt4EMvJb2EvP7WEb+pCffnvOEiWpPidL+LEWGbNpT0I0Yv5OMiX/INhXY5iyWrblHeCZ+qxq4DQA6S77zezbLmCczNNsDdBRg+q69VU7jPsinv/tiFdXF/SYYlHDcLhBfYSl9uuTRYYW8Wviwf4ttQ1JpRF5R6e8DkjA4DCz9/7GrLYkzvf5g8S3R4s2QVmyClz+673h9GTiPWth16t9p++Cg/GmfIysadep+fOYUSuEsBfsScVRFBY0oQh5UMtvvFJJO7V7BmVtuBwgW0l+jn91O8UvJ/sVGnpuz1NOsiQ3Ibqh4HnlYMV/2O/LsQCHayOk9/2kBEKMitYVsT9+FFPpBVTlxxe7+SLT8pMXLnvBmI0ukcFr21c2MPRVRm/CP31OHlnVELiLR2ZRQSY/etGPl+jxrwfiz28QbKMVRgQwLx6aibNHW3SxwwHY3+S1Ehhcj9Inxwa3vVjBBSU1qLew2dPcPWR4EzSengh8RAvgE3FmphufBFBFXDIwoHoA27FJvhcvXBWGHGAgvgsL3IY1/gLA++aItoQli3mses4EeNHrgrE4P3gPA6JuAcVMSSnE1zXWI02a3v94/N2GcwVW+5c4atTKQsOz3e8h1e+L/Kc4XmFgnT24gGfbFuXF9krGvIr3h7ItmRLvNuBqy7bbsgxZV/4cyxwiROGIhkjCCB8Oxuv+nnjT23OyYYq+GQXa8WQ/Y1bV7LwdgzbYyKv7ZVgkd9OaE7DvxnmnTDxqRypbDwcJu77sodZfV0NPDy+k2/SpZSxuGd9Mqe4CiABtsmrXnzMC9IvlCskBxZ86ML9pg/r92B1w7Ul6civxQ17qE8uKRU50G2iHYuaWLimOYAG7jTFkEWrIHZ1AILCi6cgugBW6KtG8p17epbFKCzvpVQJ7N/71QcR3JL0uecH0+rlnKEu28Wo0s/LQZsmxEfSdR6J2ggrF4sK4aoGblvOoDlmqX7NxfoY3xGXDujlq0TpnEQJNSwbhXQokr9xCLQwpX07oK8puWrgJIrumJtgUdDqyxtbi9HT5q5FPA169kD4SgTGcc0bOPrVQigv8Jw8BPqH/lppI4gWyGmYb0p7mZUlz7ZgKy8QzPo6rf0bokx/PsNs3n0xVwdsLoSFwzjGoxF8r8xdhD3bLcH93PmE6MxpLq9p0xSrmvt+c3ipqlTdeGOlRpiDy7u8hGSYcB6/EYYYNKtX7GUQI+rx3U6eRP1obZCAte6sV9TiEr7n81s0BnFtqeWasQh9bptufanJeoLkUBkKxbizd6JUW7/rB9kE4XqK+67dRc0z3r8SFnKcM6MclxHI92897med122Rg40a/O12GbIv4dgEVaaIGZGD7pyHw+4ci+ytBSVBFORabu96mRo+Eepf9Hw3B4jBPGRUA3Ba55CyVZHBG3SxHqf8IzhiQ2ghKXzcqjVdHfXy8WO7BCHXeGS8CMqf5js5Qw0pxUInO9cByy3FraO/BtQssy5hr1kPO/30KEsxpA8U96RpgsNh47jInS7n9NLp9ZWw0uc2WowYEODMSYCUiq4NBjV4a9wML6AKcQz16tzmu8UDZ9sEcluIa5JUKK9JI043yUCFozvbLxEr4nObmjj6NbRzRwcBJPxrvDIifymTj+JuRWXOmZ01HYLiH55yluRfWPHhAq+LlOlt31mQj5qF9NjqN1FrKhPiOacoB/eaAMFZruPNgI0S4gNS1UGF6PBAJtuJsuhWefmOEh1qCoPMOFCoI8Jw0JLMYeXM+/n4ZaDmad+55glZ/ZWhCdlylTXyOzRND5QhcVeE139tpSzNq7v5PCJ1soxgkFehA/0VbhA0rUJKjXV41NYSX3egDCAT8ioUUyjCUBgMWHmIOEkrHE3tP4J745v2C0BjUZsEp31AdmXvCFPfmgUJGpha+RyDN175lILKhN0/k1Bifm21QKMarL/IsMIe+xPon9ylkGBipOBO9PvzdVskvVUw6p09C7IzS1A+aZBU0g1l9it1aqbBe/t1D1Uuo2PMBkRv0GHtBJDjhbW0OipQRkT2TpJ7n5i3NlzGZ8f7iURnSi4MuZIuZWFeKhDeHUHMCsB6bHGF/OURGCOP/TBMzZv5LdRKINumL3EzHMAKbRQ0R7DWzQOZYK/SlLEqEJCBLisrhjv93iZ+5REqKCZRO/j+CN+WAHvMvG122Jx8vShFMZCMHZoJ5Li+NOF3WDvfUYEVri4uhdEdsHfg39xSe1eu1/R4ab01o5fORXt8A6sM8mcN/N3ta78R7vzq4gSHZ1fo0gnBnOg46pUTVbOkdA6weDTCmnDdv7XlwKYjoPVy064B0MFiuh5rzzjxldkyBm3ZXcDb940LV7/R1rGhYpWccHTy34ejZBRnvVjJcL8YSbSp7j6/E/GF5HUjwoZZk6T0QxPEC1eKPYk/I9Pl6xYdX2gPZTbCSidcESA7blixurC+wR5VVpYbD0IWEXtFalVB3E2siKOrM5ktdlcKzMFE8IQo6PMI7ZqTUJSiLF7ZPK6+puzvoC4aJG+svyG1IsRPzej/ClcvQhYSA8WuTCEwQsN/izMMvOgeuVDfzPl9uVYD8OWsXDaGfoOQdmWeKRGX4Qz4FjjIXo5dKOXjmOLrzK8aDk0Vkt4GQGbVb7tt9iFwphIU4/HPdcm4UQR/7d5fdE1TDyqJdK8U980A8SFxti4DegvE6ze+9ohULxOw6NNofbxZyVfKwjWWyCvSx9XApOOFzZXglwoWkMhDzSYRAXFsfjxcjD97yDN5GFNr6v2SoJZZ0h72X8T2CYgX2tK3i1XmRpHsRHXr9XN6TSBaYd8aEcy9ni+unHNB5dwI49DEUkBf9RdM7QGEFfbiaypBPs9F9ukru90NbBV877/oJgiB89Uu9+LWjL/k3p3hfjOYXZFExpyI8m/VS7122HiGOSrvcVoA2DafhrGD0EzkuHvzNnbEZJMYq4DNQQtE+tJDVz5gk0Hbc0oMaTNSKLLfluuUGpa/qY/A6efcYOdtABPR+FqOsukcCi1DCJ/EHplAxdFtxEfRPtWkcxZJ9OWv2pCmH2bU1GElSq9XzjZnZR8GhMYPtEMTLAGBNhImttxlBiWng0FRLU8q1yksArR4DlM5Lzlq6/AewRaq5kfMX5afQF98RlCrngWAJvbjMh19uro8OR/gfHfd9I7xdKjypghY8XT725hXYCEVNC77w7PXkWj4UXEbHdstj88AfGc//zbd16aeUnh/2jQKZ+axt80D2VyBMVpGu/rGeo6rPwe9HQxfOOg7gIyx1yRpH2c5btXHWxnnK4cTC56ukoVCj7AR3f9kdSkKIlFN8YcuPX/gK5W2b4ZSiMra7OXVg7SDIpZkOk1J3nOvxKSAFUmd6JNkpttri2AhxYDwkjzu/lxg44iylOTEN1AHaVR9DDJAWLVf7F46cAfauNeCmhLKMi9g2eRwvpB+r5FsrW+OKu0PIm8Hp39udS4kxnfDsfT/pfdOVMEDa23Ctz+WMTzttRwXvXQLStSPmm28vursSqxzta+TA2S/fRGM8aJN5zx7w4iKlJ5+UU/FhkW2Salv95vvA8BsYk5UJORn+ulmtQp/GW4qk6WoIbbJsLeDqLhaKV4jhsZsUUKydVFUTAxis4I9pOz2ofNjDHZCUR3+vXKahT/HWqUCPm370Y6dAPj951TURvVxBL22B5IKk6HYs7tglPqxljei120hDVyac3vEv3zzsxZBKZ+eVCNl8bAfQTVn1wzNgMitT2haWww/DCeQNx8Q7/YR28Y1vnzzuptDBoLTyaH4kcqwW9dwMndTHD2hURJHYvbPlCgr9cme7/DCmiQfBlcRSGaBgqB6XCd1qC227R1dhC5dsQ3gAjUZU80TueDCyTVHKKrYBI2nIt+nb7SAZkVWsUvz0MZIGPZAuF6lSrmI+KVZrPf0kvYGYKNzyMnxEQJSeUS1x1ysvtBmOzdboksIz9nzYwZYhNs006gsF74PBbO3HC5BjCO5e8lza2DOMfUjOrDLkxGEBorcFFP4IYQ9BXj3Ax8jvEzoihwBmsSFcufcYC0iA+p9K+Ss9ttdDesL5cHHEbel847F17VzVIzZQ9MAz+49fgE+AWXx4IevSbupsvfPd/BIjyCHChQg7fnXdTt9u8Zw1Sh0gjOxfIdAIze/2dQPQeZcTrLyeQNgcnemIESoHn3lCHPkoehsjR20MVuypu1G5Q6npc7PA1ysuQyJqzk5+8skliiJgVTvnZbHGcIOslLrAzXWtVFaaa6qzXKQ8xa/WHInyZfHY7D2PB9pjDH8xJO+A15wAYepAZfx23KF9rH/ITCRypmXyIQED8e+Vxx9DimT60pk1OAJgT4IjBIaa5sUEG3ls2vnvHLbGEic6IwbjfcCkVS8x7T5XA7QN99A+SXTCZyKskk7BoKamLguHNpKqJIPAmBaX4Z4yPyIDgt79fr9yh3RKNpcM5bpdDRTxzL/pXvKiXYScCeT6mH8FNAseltUfU0TDrvlwUELx/lexMiMjHbGt2VVXbMS45qXxzqqnhEFrczkd33NKqBsa6/jMyvQ3fDYsW2C8qylJAR0hrPrz76y4H5QhBVQvje7NKkgtpQmBGbPkNW2h9xaBpqoiLauR1nBdeAU3sMsl+FIqKr7fl93RFWWiV4oqGF/OSH0DhjNFkrO/S2C0DWe1ky1vLjOG7QFy418D2L++wjJeP9yxOSpgNWb6WN8Yp3O3OEYKgJFOpGCR2mb5TRg7jqeWnHYFJ/OVlDl9qacDGBfG6HXTqosCV+rKAiKIrn0bFIM5ss2HHnuaRuk/UIiSVrpDR32dxJMNVZMPn4NI2cdeLB+HdGU1u/jesbivPJY73jsyyWxfL0vSWR19dsOo50cTGBFNkzTc23gXyOJHM7B8rIqcgkzV0SZfjy1HE8AID46SCF4YXJqrRHiHvLyfJmN730zYXKiq/6iSJ/RGmJ9G48v885BbE+7XJU+oba3PhlnN8c3eY7dhY9jaWmK+uYWdHlUkk5BdkZgnAvRhl6tyc3zLd5PJC7G7l6rpP5d66nFJ/72iHhu1wpXRG1Bcn0d9S6w9x6Cn0zx9vVHc9Uj+9BWWrcUMY4SYsGRr3bIumyNYRhIX+tLtPemRtFSGoa8JwO/pXnU9xycz0amHbEXDyPd+Iv9SAjnUpIv3OhKzVr0Gwrp/+q6BTw0Z1ByInJheqS3sYSPscYjiqawHHh4nZheH9cMAufB3iNWD8jjb+vJST84Sv1c4A3q40WGTBph0s3vDgBv6snPZ0zgdgy2zIWQDp1hINhhCsdTbr35pTRZ1O5BxnfMwzISd0PLe9K13wgsw+cbu1xsd+U1xp/h1F2ke2yVWjp45ls8WHCzm0YNrn7IYWwOh5PDjRvqdKk29Jtv9g+J+TC55uyWlo163zKbkxWCHtJadGFpzeCajB6phEEzhQae3klXhfn+7eSCMYONZgKnVAmL57NFXzx2IGLIHiWLkluVpcPlXH/dWbUg6zP2Sj+f7zLjtO8w0KvYFPXgnQ9/WoQG1sWFolsk+WRlTldPd5Q7svzLvk9NwzvxgzH5ppTV6WV7oPouKiJVbudYozHX+XWdUdCYbH1NuLJQHfQbl+BfVIYNl5euEC+rSC/oz2q6jMCpcRjHqPBtg0cZv8vVZPxDgUT1CyhI9OuheVkpvuMYfNMsDVPrWkh8i2B+M5EudWs12evjR20Sq/g4E6rZQ/jwDRcALkJSOyqgPKKg7StLl1s+u7J0c6Tz2ewiMRp/kOLw9Su0cBMWs+sVs+cvdtdFfeeI3EG3iCXdIyfJgiEZD0XhKoUDCX0OS56WXr/8dk/o33YIQ8/LJHRpfJbmgPJzKszNtvfTj294h6FCWQuRwof7tOwHCc6W49TZVEJnN+8Qx2yeeGhyMuC0R3uHCn0ZYFBPdQEVvr9GzXHyBUTnzozrQ+JEOebWjSyhAG/VNS6Lu96TNmB6B2SQ5seV+Cpp1D1yNy7tuQMWcRMYjSdSnesIzYP2ru97LsnAP9OseFMXZDutlUbO371UgGqwDymFDJFudvgj9tRv011ut0S2nXDrBTpASubJo/OQp5Tmw8sK78eM0vthjB/khXo9cxSZ6HAtiZItZS5k27L+6QihGJBR5H0y6AjVpBbB8QkPCcBuLnzJZnDqyspO0ZK9qDMxpuWzSaLNBzRyDQdTzcXG78/Jd/ZxSa8bq9kmB3hc39HJaIY1rEZHETYbGyGAmupdU7bWcolK1nbSXb/WW8eo/ugDLUlyZTJI8oTur2d4Sb/nk1ZhJbyjcmloNWpA4WBfKDGE7X5l/eJ9Uqs/qF/D405k1K+T5Le6w3wNpBcD99P1/Ov0mCKGw7JgnDB0wj7K5Fb2j1Vnp2t6LWwfnxfzklT3LWCoJ6Byl2tXWxtMWwlRmR5YBGaQs6T9uAQQZWPZb9layHxNjTNGKpdJ1YeKD6rjNDw3e9tpwOVAtPpvQIAOVP2sHoP4+EiuA1MXBf91Ba78+URUkWUAuku0heNs9HDzEbV2HanZ5KsaJeO8EanpV4TfEvCt9dmFoPCGKM5UffxEeULjtEh4z7goz4VyNFgscrThR2wTmmrOyfi0CoitUf3+5Y2pQAxG96wa5Qb/gKSbMDEyjd92ANXVnSDfFMQYFoc0bxePhIJTQcY5XR7zg23a7Oe+ghAFTLaDJDJgEcdDg98ftgoevRF5bJiWxJyDxRVvw3bfsLQbX8tlg4jU8hNUQLPM41TfX7KoI49Ts+6FZRUBqUbGqNg7qbqcNtJdEBd5n9ULdaDDrvrd1FhcA4vs5Y7R6GqcbQIiid1lOMEQY1YcYrcsjL7TTrBGGQw0ZEf76onGn355T4ULgkBZINf/xOx2rs4vUjovVY+4hPSavbuzT+ZoUga8bAhLjDh8t0vfiWkVHEmjmkFvNmevr/wtjddUf4nRHufX25lpUoQM+Ye+a0JwwsJHF5K/o1K/tnv4ag8olqZ+ChmGf9g5MVEJjfDQ8RVepo9dxqu0BjgjC4l2ZhI9+oaqzGofMg+qp8RnL2p+Z3lulSC3ZK8kMD9y/4uniiCyR29n9vkJV0LNXdJ8PAL9wEPCIa8zvgyGimWEe9BEu+jziAApaIttIPzh9YXsfaoEtLvapheC25A+izdU5X61x8SsEQd7jinzXZXc6ObJoorINoioE8r7LErB9ZEk/xIMKTlFQsl0sveZZAgKU6vQnSF09u2WqNH3fVgpi5S6B/jNDz2OyDwG/yAm0wRJXlxGzIrT/WflmuMk/SPX9SJdYiRO4qBMiJq+FAbOYustnYsqWh8Ca9Q1lMyK9GBTfCvf/LMcoKKpRBWHf7SOEWRfOJtfxLOit0OI4jhBEkR/vx2XPSgYspNFCzKtmZhpD1sFpNSQBPW6cbSmD0tA1T2/zbBpt5r2cJWI9t/U24IdKRJvmShmP9/0saUavebb/aO/h7ugSEY8r4uIthIEujJWPVFzLwLmbuytLpXNoxO7xQcYfcUyyJiW0Tv/3NWgk8AP2ARoSxWnEy3CHqalOIke+tKrk/vbWZeLJd9Bgjdt8pvs+5nQCz8E5VPR7NqsY/in2Ub4gqSjLcxU3V7dJbV2z0ouj1DsYpTyWpAVGq3rp54YLUE/BWx/1G3XT35XFDoAgLY/3fQclr1yLhl1rsx2wHv/2L3ZClgFIJ6MmzRKA5xY5/fCUcawBctprWIcVCzDOBKy6QHza4nVvPK3AsThwbzxljvp5QpMc7h2qH+EMKmhSHnL7rIj5nm5qZJI72NTpqnRFImNPdPmNra+AGzoyWXbpoiRPu82PqRixzJcfJhKq3CQTGzcep1791AParn4/gYOAGO+NVqruosg8t6nGsx2nzQ61gJZfAWgJ8olbB1180JBX+8tLUr34fGe3L7iN7pmoUWa450xjLdirx2HWWzmzumYxnpUNS/pvmue8fZIViUC4b+l8pApPrylKYvXXmFRZZNnSbbjskYYrRxpab1UmTLcnj41hk9XewCeKhkTorBngzBFde4qaScfQLetqNzI+74jbmuLLv/dIQOUi7R0GsL+r3aB8FhV6Iosxo/JwUppdyiqfZn6Tcgg2x/cenUzC8MKDxSmUiYCQUmErzeOrmQlMn8D/TnVp/RfkSHRLmI7VK40aBv/wxrm3kgpIpos13K/De/A79DLGUYv4v3mwNeRQbZDMe9ZXlf49MOIx25F6jP6NbfmujulBPeamsBeX0FylEzQKAXCHDWMDH3mNVgVoCZm8e8HHeyHmCIxiFirvDQ1AyBalIRS+CRlUHkub/MzuRAsci7EKmKDB3AagfjsBu0Ydg9137zlcivoHfvACZosZd3R3m+KHYvVOr1XyrHe9rp3DqAsj13Ovf3XIOeNL6fxdxriNJ+SERnQ6Yd7SH6Ug9gZJV0DZIOzgo1fd2qbt4wkGeHPQdp3sgdpehSmkL6Ms2BD8qrb87ukINAultW/CXdHoE4rfhp07JTcb4Imo8Jmgh13kSM/DnVRBOumqbNsA+e1Kqi/oCfuYXmLHXO564i/UW19H4ho+wCGNC78O6PMEpGuKKu3T7VDJajQZNuT2l1cdvnOv7F7Q9Ha/9dUKpgotnOlntS2vJoTrf4C825kyK3hListV7elKgOW6yWC2gpfIqrBgbxawOoVvrLY+gJyhxkKQsENyb38F4zOr1DMzT2TrDabEd3fvwaWzxq4mnchfA+P207JzPGihQ7TPmorPnWfYs+AVezf8K1GAgFm0UdZOPv6Pc2HblhVuGdHFUjukfpdP9jCDxaDgvF3IsEkSR8vgPWW5PuacmLZ8rkGORFxPTskut7p8FsXgorhID3kYGiu1uaNrB+uVHGDiFPTdN/by3qP8NLxDOEy5ztpEnQQTx7x8ejI0LHRj0fYgXyov7kA8i2cS4Tac1aDCEh50oWBJsmm1gQ+Q3IQVuj9qYr7vlgTIzZ6T9Z5YUMm+BYORx2r5qnN1OMX8igMTZqYCO5kMTEUfb+k7yyD6NL0inwKhNbTLzuDYiExXxRHvXm4n3beUwtgBB+eAI7r5X7ywCpo5rdgs0Qwxw9pwpqB2tAfZDbLBCloQIF/ja3+lZNN0T7GoH7PAxigsfKOklnH4wu3vwA8FUVcur722w8a4yjAFFARFLOIZf9QQP/dGsm5lhZK0vVPMWD2jJRvh2kjD8Xf+OB8fXZJa6qMrTnfoiV/G2Gk10RS9fEwTUAteXVrIXe6+ZcwsKyM9O67hEhu4mZhfFVVSM49jWxpC4DNheXOB6+lP/1JQYX7F66p2vIimPZ7f9kKLvQcgmk/B6U451aNfQBrcCNIzFxffXNodgDH8Me6Y7hv9WaYwWBAtvnVZBr7hw4+bAjJrHVz6iKOQsNTGWH8dOVG84qr/LUlFVTqCON8yyS5Qyv3ea11EDZ7tcgqbsSDszcsdjCuvToLEvAwROkIwsvz4ASy/pjqk8/pSJiRhmFnkkCYAIgtjZDANz38PpmWvEaKslRo7YIUmnfjzctM9+tLK5wbjsgEj8gQXIqoUcT6TGmj+kxc/ET3OvUWCdxGb0tMg3dky9o9rYEmXGGPHndT9hCGNth3wvogTB7/vsDLb5zcnpuljmwJyL1Ab3lZ2QDYSN2vQf0NxwhQ8Cv3gY0eCM641rRCUtZB7mzZvCXjAxeNWxBq2AD5fH6MeO/660MzO6Gt89tcXGGjmXo2NbeNHvqnkb85EM5+sJZfUpU+RBXJ6s97ZZyK2DYTTK31fYARgE/ZVjuj+dni0cKbzWAEj9WNfac/YTsobyNORdf0Op31geHiFjGUySz/LYsByFv2abu9IcLYj9I+WrepmPSB232cmn5kJ5G3VzF47/aDHtnYp32DaRtGtR8TNpJlR6UMuwsCeWQ2/4Z2+FWGgUVtuga283FwUjPQkFLUkIzZfWeenh9TFVAQyQ8hzcX/RkpA1f6OZjvLYBROH3RAnqRlgpJD0BkoBmLoNutz428R3+6CFRnDFXSqA9A3hlpCbn956l5OtYKMu34BduFY7YiKcTXkNmSPfIVH0seAM4NqZolbrAokl6YnwGKBQq77RqdeRWalp7IAOW605s4HylFozuVfwILAOTsZ4pRl63BYZuAOKdR0ZvQV4Y57k/0ZA5tBOFe3eHHXErzKAckQ8J1V3ia8OaHoshFBY3CXX/zKXon8PJEBf4Mgt/r3FWjVuijplimTMK69HvhKqxJX+RVBlQVpOTvKmYU7jByyhO+9LL/5fC3r9M1xzsTgbxVB/vrFSFnLHs7ST4pE8pl8xdCuEI6YHxXdTzpdUKEXWrLZxVo9iw8OS0HKmTcAriZlPy3z9/8zpr0+kWWpHk1lpquOwA1WFEvdtYjWRUmuF7gw5COP4l/mhqq5aFxVQzJh6jIqmP9+yEqlbVMWZTLZrglqyj04ju1XAbsR1jDQbNDg8+cBV3cMAB2fzDEgoN/HYjICk6A/fe9SieEndvwKOwyoRtmKXy/Q64ZPwL4y8TIUckgO4cskbmS79t4qP8cD/aJNPImXcuo/TIkJNMgnsUtYIdbUy7N0OgAyQAEOlULH4x3hnnVS1i0qPiCROnD9yYpGL3f9m8QKwo4tejA25/mqSgdJzBgYwNZQ07cHgD5zR0cZYtPXdwoHblUE1NiIcwdUG2K7GN/bRKFVRzgpbrqRL8/tsbXYS20FxK9i9jcY7g0FR3dpk7Z1SdyX8M/+skwV6eP2IEm3U97q2iRGNxHNliEYTBsJrWvEwGMZNWAhYSqN+XmsElqhQMFzOFOi4aU6zMXLiXQIgv8Y15Qp66UX7mY8m+dc0pTa7ASaLYfGbPmo+iJxNRRWMQgdZNs8b6YAloNtAjjja+GUOKycK4F+fadiAzUO7A7LwUCirJDdpvYTHt33upbIo+t8GMQrY7/+IH1eQEmZguw5z4hZJwDxApBXzL6XZFHWpM0bFMvyWXK/xQxb8uly/bdajBpm4l3u/JuRH9yEBm/nrfrR+pukggPNy5hUF1GCgCFapOXkN9LHp4OD9Kh39CaYyHUcguS9PL+8KDUP94t9ZceCWuq3NQv6hFVovzWNL1DCsIby6kmS/FF+EAMmlAEODRTQvPFt86W1X90IHNVInG+WYLxQLWKbL7zO7GoGSY6ctNtzgXnz03LFQ4RgZjm/AqgPAjK80hhtlfuge0s7vBkb8jOUTLsWXSh7vzscEAsHP0WEBGwBh/gT1ncFAA4mFb17yI7DiCvclkBRBPJqc19AbA4F4EgUoJf5mSvUkl6qmKDtBBpg+PtCxG6WkUZNlMbyCjscwnBljteLH9Ws3F7vyUGQCtF0OSc/0m9YyJ00bjxVHclJ7qcQBvpCwNwmcQWvsOXZciUfBdPD5eYQ0xGa+pSsBoxOweVm2oj1/Tx4os1MDqdb0VIARhSuFQff20I8le2kpYJiVuaaRYaGuLIZMsfiWTvd8AUPgz2rDakJ+EccEbs9FqTJ19SJ5EYb8iXoz98ezJz5bHtpgISsRQDwej0+abRFU/Im9vX2h5WSP9SmUsP4Qu3KaYhmfh3DrJZGwFVEUFg3Tc5IQJjVOE77/DGQAhAUk0+iSWdrzU92SY9JSOKm89qD6SKKSfYgX5Ks8Z+dNB4qYL9KTA49OJV1vVv0ieAWurs12Qx4zn4ObodO74501NR1MclkZ4VwFPFAu5RaN1kMNzj1vqA5Ka3ZSXxJDuELSm3J9iGkA96HHrI0F/um2ndCVtHySxtLgzoOZ8U9wEbrSl6K5JHvKyZ5P1CYiRnk++H3VhjOX/LNtlz7DYvwJSX3usKYfEGUxiqYlp7nFzq454kWJJ1sEjhcXLdJagUY+Ldm9uMojCYdJfdO8nJLyHbeQTyt3WcM14H5ZCneCcvzfGwO2KclKmSijxTMDRMHjR0+iSPtpk1ttGeYEkZWoTueYO8oJ6j0EQB3kWVdccGfC1oFBM/dXnJXPCuq93fBGhveYDYNQrrkfQnBPG5NC75YPFEIzRB6cOYiu07XcFN+l9tKI9eXH1oJJfaJ2veBGF7jtzRJnFbYI8e+91l38y7BN5edChgznaIwP48o8CSgHCXoLITztD5uafrVVMWRIHY19DLI3lBNry6IDIJlwa8MdapOO0vBNYrgrIVwiLduX8RBpmdytPbYsGcS8x+iRjMBhDeHXeqOu7PRjThehiaDbdOTCNLZv+zRpXCmH1pf7v46bjWZw8U/HFWa0t/wEHsawUwzmepvqu7ShwKDpJ+vidfD60o7AGIrkN8kjS6AimjwmHS013LrLHpUPI+13zv5QpPNFt+PMV25QWP8u1pOULwKsNqbAqJkgiYLItth9eiOcyF7wNt49iQ38qOhlZTKxybDGDnEKKY054PrW2krizp5BVDUGpErR3UhTmYk3+ti0+OoP36MDmJR8SktxZkQ1OOxMS9Z0eYaEbTRjjxQ63V999EkCY7aFSzgzSUro8X2PpO5lhXijf5nRr13233Ae2UT4DcWRdcbGqNHn+Lfd4hGykMsWZzgAbW79BXzt4+zbQfrx9Iejy9SjQEywKKXhSGKbC29qAr7cVK/iagHiioSQHnc91/9Gr8odptD02UbBymCxQk8uPURM2kcT8/1wqGHK4OgxMWCuqSP0XSvaXjQHJiaEslYGcrM4ZhAdqy/Oza/tt1/7YeU564AERBv2eBPptNJklgPpW9i/Oa1CyP9QvwCOQCV/dY90pRMDq3iuazukVa7/r6PJfugU3Pnbd0LT32/UHhH1NGGPV7+fCICfcCroVb5lyBG6du4UkN6UFqwzxFBlknOtbsqk/jfQHvWCXua2hlb/1DHO6HLto7yiMqXPibK6Nogk7WKeyhbKqP4L02ICNxfXbULUagan1ppH/0QhGZyBLvE2WX8KszjR2F1DxQf5BU0B7HRyihAJ84jPSZg1NpQGL6gvh5Yu+3kQEq/aNEcQ792H+CcsVw/hjjh9ahU7gKZ0JOPHXY2yu2gwkBxubX7hVRpv/jolLyBUyPYVDacLkqS4y0wge/lIzAxD3kO7E1TldtAUIB5FTGpxrcnzU2hohNa795JmB2cRAdR3w01vzjs8Id3XW/iw6q6h6NM53aSq0VHOggagGjol22hyXLhs9EZeKuQ27IlOm+OllUtFihjA4w025c79ls+nTVO+4rbunOV2Mrt5ILJpdzZEVaQqhLnvs1fmOmV5nsJf31wOjsXvAn3WatKepGiy1nCiQStmjdoHb3T5S8UxIamQ+77XBb2r2NhzxDRWeJWnVyOxIzXq7RvXrAPaNlnyih6dEoGr7se9wWM/s4U87182eNmHDSG7ensqkp/+xlJwGp5L0DtCjLI1kV3xnj2Qo/oRsVZXoR5yCysjVqCneNCbY+t6fzOTwgjmtZypne10ewHQhceVRi628PL45ND2GinZbWP8FNxMDl8F7oWrBbDGS+vHqF8CXMFZ1jGrnNKGv/GWc+be4l1BtAxqt1n7CUes9AtigsesTmxX0QgcnGQbxCfY62sQGQbB3fU3+wr+9jX/Q4nB9jJ31bciglaxkQYjEqz0sSnzfU0o28aKx2k2GWdvoVs/LVs7xUC84LZbNg3Znr4RpKohPLmrhn5G2cm/moKgRd2O7I4Q4/jDdxIE0OM+y/xSJVeDoS680FOo1gc7lltUKice/6OUnxGXnCd64E5Lni+RJwTK5RfccYX01b+Pn49vcrQ6NBBD9p3iMdL/Ki2uIiv4Ey5r1A3kcsMSavOeOd141WhX8ZJ6kaEC05JMQ/OqFOIcYongaReXZ42Rf8ruPLpvJTWYh5mIaSM6GvzN4Xw9KGBt8Ke+8W3/Mun+cMpl+NO+5xkKbczUtBwK657tScEVgolWRImK0BoAqC4xTMuJMZJVaVLOSy1mkzQNFD5FqNtan+8z3yDzZgulrOD7zP4IHnB5wG3ogP7DrSh91F+I28Xw6DNHusdccJtv89LAG0FojNBI0WUxhHpOVGW4ldeCg3SbeU5VFfFM2LnPAGJMzY1bqidTXWICUvrf3GOcria/ZWchC34a3ma/lJWZUyYENzegJaOn+hsGeaGbJ9OTZhCgAa9jFrU3xnvJgeeGw7vZoJ5fsbHnxnv7wUrM9eyq1xUbqmVdMIO7+h1Fdrbn6bhJUVqAeECnnn3F4dIcdU+rRE/orLpLcnIo3/rju6iHG2Cap0sW8a8k3j3OfgTkmNTr3O79uJQkve3ayfrDDEjhIfB7XOvNhR12A6Kcl5n1Gam3+rhNjf7+Gj8aU+nZZQt6mZxVGyJi1zq8vO4adduS7Sjq+d9ITNW7hTSEFB/dE+KWXtAIwisKzCMQs1Q62u6eh9aG7AwfNJmnVkX5ofp8NqIr9ToLJRWkdGax8XlCZoVNflv8/SOsh83gDkDObuszc73B8NSYPvoAd4snqpDtlvDvVypbkl2RGIgP3gAyYK5g0F2Sb5XaSlyEN6Rww61h+cKd7If9r+affWe7AzrrDzr99ft04jWlKbPYhCRWBuFv5XPO09eL2m+BlMeh6EJqNIG8TlTTghNGerVHt3JPkF8vGmi/qHEQuO9e7H9tfMLN4H1D1iPBYfwPc/Hif6XJG5xoaaEX0zY8jGSXe7NjdyMVgQmKr23gpI3bb1urFGcC3nMZbHWGfkfTde1LKmSA38JGv+I9x66gTe8956vX+rM3ZiIiTEnuqlCJWWmVNKylM7eV3U9EByrPBHHB1wKf5+JQlSWpy0d1OEKZ7HW6xuBHEGk/9rpvl8qyvKkaS7GXVeYyciBvmSltZ7fJEUIZDQyzHRdIK/tNYdQp6xOnXVj5aig8Xr0oyupJJLAFa/r9cGQqZ/QHyRn/KvyEunlQbjEfmZb6CbNX7S4Lcmrl+GWKJzRi5mwKDvp9hPYfUN/qsIIxHh3X4fmPDx/U6NQETikaqtmbVhKTtIzDkP9k9+Zz3+gifrdBLvH27N/YP6DmXQGwkvSAB5NUbZ1m+zwfHZo8BDEKet7DTjUgx5NOnuUhIc/QR/ND1o229wDmy4on/Wc4w3Iq5JwjRaUIH+DWHX7dDv8kD75OshQsnQ9N5lemOxpKurgVJXo63BB/nVA6uTlIighblhV5Gj0saRtv8tps6FnLHgMxcRE1gFWarW2v8/xp2GzCXK3OIlpnvxVqF0X7pLTnK8YAQf5JP75oUJsLkPjwJXRZ2xhTYRPvtRH+ul1s/Dkfzd0BL519U62+7vaxMIIfF1f88FZThFZygNShOIriyxazyHDYfMN6kKfHeFqvNnO12fOPeMKWUEnVenRMn5o4p4YH32UbZo8/wSVCifhHkJ3HKyZ1YLSlUu6NaMH6olt5gE7finaIk7cskRMB/n747ZfXuHlL70pjKOKH+lMbghxHpNkUHHwa4XB9LYUw3iHrunUjyvX1xJWgRLD3s538RXy04yXFmBpOMfqH7Sbbk1qyGxg686VOcJFjSeFd01o/8q6ijy1W0WrW8oyU9g4QL5tOSnLOvwaDKL8sij+m9VhuK+FHDymFijLbifV43mCv//GjaLMatLwFn7G2F2ZOjO/iizFFa2UsB2hn902gPQeVFj7KJhWPRhI60nzvGbGOopZgd7JSOXm60NuIOScDsCJLONH6XDeRoOcykspP9Uqf34e24Aa7eIK7b9km2YI9RzESBP8TbXBtNrjzTNcycRGbl9LjEwugLMoYDdpPtVG2RlwKYniBoq6YnnPBwCB75/jiqri6cNjq0j8jReI9hkbiSKGcnLtetQRC/Vi0ukoZxkjpmKhDeE7LHlXVD6K4WWh1nv6sBq7remWkHbakJsyK7FqWXbY/jD9B1l4KTtd0ih1vaZ1T+aF6Z5bLCYVeV6s3XGkhzhfpNjwxUxu39b3wv5Q16IatlMTc4uretwr3gjDozhfBKOkP298BodPteYF50crh0HgoK7CahRpf+KvX/Fz/Zv5mkl/1V5TebAaZ/nFfinffAz/G1uL/tfRKBZfphAU+jB9GYxiZnBTk6lVW3LjVf2ry3z4v5lIgMxSAao+oZRtBDFDdBSNLq+L5t4nZ/43ZWDSbPFvEEn8MpPcI63foaU5sjTTAvEhSFnp4E49ILV/rZmWDjmrqRZTj1DvbQD+xJZiQbKs10jEMje7TkMtQgs0WO8c8oeBnKJUOt/M2WQWnGQ5Pyicmj8TuWDQX8st5w1h0F8/nNo6yIw+RP2RaEMUfcbnZ1q2KkJnQqAB7+debcMt9S/mG+GmGaMXWAatW2Qe4VMh54ArP5AfD86g+Vy3Dsb7Bx3UdOmw41tfvXIEP+Dhzqmgv0rlUYZ9gVRhmr64GKkvGP4ji1NGFV4voJSfK8NHKVsWJlPEkKqTzBgl2apPX2l7rzRp1qF1zbONFi9QUOa1prAd9Hf1KhRlI1of9cwsAcJ0PCtnrzX1iSbIARaB+N2p9Qw0k/QA/Ff0sqCeJU5ZsbW0Ma1sCRAtUMGSRIU3vDGFybvHJOLayCs/4ARi788qNm4AeAsxgldk3Wia0S94Iy0EOLKxwas9tsPHmeFnxVtvIbbsItzD8b7iY5nQMBeVSBUdNDhfIYhK4l4I9Jv99J6RsefDQdHxesx4DBIMQxIT3Gz40H5Ie6XZALP5FnK49CDF5leLBG+5OL9QSDmcj55dO1LUuUtKCR1IoTrNThxfvtQFpm2Qp0wt8MGfHPfZy2hUh94cTJeJlrhWg3XC+3Sz9Z++/yrVK39HDHumyTSmOCJXyjXLarJT9RH35USvoEeq7FjPKB1DREqeKiqhyL58N8cuV9s7d5Qfo4cjJ+gECcTrTnC/ZR58oGOg0Hn2bSW0l5Pz73z4qz5vDQ5MvlxzhRVijoytSR63qHrC4gLOOz8U+K4+Mmx6kiyCnC0mOeteUAHEO4/zd5kHM5LHYk5j+zb330w4er1z3jKTv8FQLZuaQwBsNZ+kVTnLlXtUFt/O4u8+Of0o3yUXFrUKe2/9jpdSxzYaRiqOH+WQcBNeTg3GXpoWl/I0QtkXGSUuMjOXaj5FV6R6YoEE0QtE1zmdI+uNy3zUMJlnLLmN+OTO2jp9DfrWO3TLvudhS8u9KMNwnV42iV3sFMfp+O52WeVfiGFATHe83Wcn4aJQKfBn7prImZOh+Dwn4hhchWQj097gFo8r+QelX7flGw395RZalnco8xLAi5j23JzwdWaZ4GvdHZSs27F0iXYrnteHxi2nnyfl+rlUSV+f2TP+OtuGyNeOwiBOm+sUG1qvYgKcLzNbhn+pj/+oLcTqifSphaGqDjFo26i5EaDIsI3b4LuGtBDca9JyZVATHleHix++rEt1FA8CvFbmc/7Wc2U3+ktQ1Evd/8aTAOGulR702x7X0Cgww+lUYTa6vdnKSQpl1EyR70AExH4S1ICooraQALGxwC6XyXhYnf67v5rIYX8Dze7oFYp6+S99+DzNVQTW4X+toJmBS2zhkl5o0eT6VH9pDR5KrS5vDYYgmNLu2lbsttfO/SfABi/BubmYQWJjclamJJt253VntNEB3yMUdfmibak9c4muRddxzNsYPvpfjg1EhWSJpOZr2ZPsy6W6PFNtqDeW1XbrOZPJL+poer0im7bLlrKBATjFSRiFVnnefP66jGzeaLXlTXWclryW/5fYLcJdRMjaX2560mSvt4o3itLp+OXpkDj3/WMLy0LUi2G4v+sFCrTPVOempRbJFfhPbRjanftFHqbb7CNLGsHkeCKgrLZXVCt4AQjYylD/YTqng8JDTBBo5rxUZc/12O0kOvlebnFYiPrpciBfwH6mb/X1sst+21CZy1ISQA8WGZevFg4YGrFma6U5ZerdbgcvV/3rP0rSeyyqfZlRch1E9nfvTkrrVoLGony1haQ3Lc3+Cn4iCPh+WlbpOIRwZcHftf298BeMSZRzm0vaIxCLMX6+mQ3EYcdlnxK5XlDHBaqtFxZFS+JgdjymiStgvtH11+obpJpQl/+7leb4TJEMtXgBBJVYKeUxft1W7TJo5Cb+Ar+hKjetp2V0cAAgSXcIQUjZLMhjcE0LpKiqmJGNwEEwiHtQL6yhX7deOSUIOnwtHWOrsyGVqLxCd1/rkQbKiZ1/RKSOoV535a+tbsHUr+ce3OGKpy8+G8/1F+r3zs1lPiaCsvkRYJjv+kP+bt4X8/MHdRFVnKKaEUpIqNn0aHx9zdY5eyfAt3UgKikxSoXngtuQtuyq7X6h1j/cwpAfR7TUELkp7/WzHwJG9LFknJBTE9IIib9qdeRAyLTnje2CkwAMEvEuv8WSr9mrOsiKLm3iJwVUmDeGV+HNsA9XnLb7wqTv+veit2cEbQbed0Jbdu3sfy2NVWhkh2Ip1nisHgNqRqPVeyNFfeGva/h3+ombOn6npkR+cBuLs9rcealf17M9Sg/ielI7XYRClX3/Ri3XzfgzJ+nrdxgsVGtbjuFRo33QdZ+B2GtFsK84HrhpVtoTMIAZhq5XAa3m+xlF7HBUDK8jLSrJL/BINWpzP0i5aYf11YKYQJfIF/2NcW7UY3y0aNJglgAnDcgyAr7IoAkjPBrZ6VULkmVvBNGYn0IOorosY1b5h8X11bxKt8b/GsrMa3YUXnz8Q2Oyo7pV+Lwne2dSu/uljzjMzbkMF/C8I+8oQWr5b/AD2GwC4Pzc73X6OdJh67Wtp3RdApejMK9HA+ZpnZGn4KDnrCN5+ppV0Or4lx4Ss5KRNyIwHh65p9k8nVEh82yL4hNmlx4ZFcxEP58yumYEvlpOgoc4Zv/19+I8ChgCGmDAzyWEn2flgi+Qq9YaOXGZZo6hym3MspBWQ3c9LaGwzk0OIXguuazt/+IjePgqEIjly/DeZYKZ6ExSnVaA8LixwPX7YS+WuWNvjYl8Tu8o9fiGlj60HqwWcOwKmhyDDZ1OiZst9OXP2hPQQ9mRtOb4v/r4/Lc3ykgtL3Q6I5aVxRn6isw4Vz73UMu3DH8aubCP2Xq6NPmWfm5TUMde+XfDrdxfp4qR1jrfo6EwWleqP+mexeX4SPpP9IIWzRNVtY+rX/GhYVoWd2qhBsnsZjIlrXviOjQby8aXX3DHV7v13P8H8LK9TBVKqvkv5FjlGtp4gB2xy4b5sKu+NgsGLglYGlik808B1fuZjvq0UVADNjvhwkGyRgkC6FK252y+rkFshBKiVoEdnUqEVkQAr0N/tQ+WUaUeASXcrNy2dDqcuz9dSzG+x90nfw1/s7qtH9kbPkWhWZT6P7umBfesQBNsmFdO1Kp+rxllVSKNt5KOmCRE8ze46Cb8Cl3ErTHkdK4T3A3mlBSGN7O6LeNaGFEj1GtXh2iRQjY6WdfaaQe6eiSWOGil4fjwhCL7ImSstxC++QKThRplANSk4XsPOrjlIkjOiRQp4zgj4oJqRKkPQHhKofBiX+vmMudMxgWM0aMZB44so1aN1ISxSrJt3DuNq2dpqCn591nvUOeobGlZZSsQNFtuUVFs03zkmlbLyTYZ1Ptr2cBcVlbY0FMle/0xzTeOYHlN75Bi+8fB+IEzq679+jQOdqJ0NSaWI+LnDZWAyv8WeP60dsEScjOUIMUpQKIkUBDTt1jNL3+9NfgoEjuf/EoA7+zKb6a7KQwp4ghwtTye86j38i/15MTJpAuVodjAtHASTu3/eoKPnwOBDF0NjHKQNKJlrkD/nao7eoYrjVlAfJ9gI9+39bPJm95YSdSQgZTM2k0qITohyGkc3w7pl07V67KyvBSRTVyk/Y/dVY5fM1XWm9UFRS0E7v7kx1RtsQmxO2IGSoUBhrLCttEnt5bc9QtyRP13URJe+lUlCbSuKCjsENbY4EixQqmJoNVSfTMESHU6RoAlkQbwn7jJFvaOAaND6NNyWkYcZa7KFyUuCWkqu30opJS2D3me0AwcH3N+yMDsDk+33Kn4gVjSwTulipGe/k4TXPe7GEYrOFamA99Y7QsWJZcCgo7cshcDEQx/fGefFbee7v2Z8XaFWIrYPNScI4N+ZIjohhk10L6zcd+DN9NtLxYoq88f+wY2Whgd9pfl3CibKKRKUTnQEkiwrABlQH9CIKDRbYTVLh0f0KbsT/oNa2AKll0MDkiEgg06kPcww1Tvo7klfSH+6lkWvOZalaiDUQxI8rYRnVMxT62J8O9YAd8KgiatcV2pXKscfbalFtqv4G6Jx5vvanlFUB1Va2q6Kq5sVfx0etk08os7cydTh+4gDT44YpTXPj4Udy3s9CsmRlWnH2lW55LNYHtAaMsxBYHgTswjy7QoBkh9qvGHIx+0EGo8aQgwAvP9Vl8r4e+XDDyGvM7OOojh8f+Ui79+7nJNmFAubtgHiDFicTYIgVpXNjw/sS3Av7lY2EQcevSwkXWe9bH5EQAS+7AK+4tD/ym0FLRhh2GNocnSgsTb6lcgS1+hHN8HIUXAdIMI3XUb64g/dPLbii18y3TJTFpfHtDPLgERXE2S/boJr+g0nHnRjNXY+669KeYvs4rr6aujyEc++SYU97CQnua2Prgsyl6UkVbZNdftgQi/rMdJVJeamnsa0SVp2dk/x86lJlXfIEHMXKsDAadLD7vT1I/w1x1oA4iNNm7Qbh2cF/kjysmKyWqPQ+mSfjJThOM2WpgiBLIgWLik5M4zaaAUbW3eLStXUhD49gjiBzk3Ir2s+n3wcgGZd+aIR33qw4GRSe39Qe+1f1tO/e6cSFKc8YuneNQordfoHPk77Mv0kKF4f0AspnBSE61LQlA2p7oLr7xIGufaHwPay6isPg/zdzIZ7IMNKNrqCKtTgty7XjT730weIV9gmPnCDnvRH2shT8jFPhhPetl+xdL7vvRV/YObSCPrJHM/v1kccSB3zljvUW0l0kXrzchfv3A96oyGeJ2snHgrwzFRe6aSndsb9bgJxFvf0hrLPI2GBrZam9luzonjlmRNYWChv1ovWti+burIpSV+EfFFBu0lX0W2HzT9rI9Z3ulsm1dYjm781GYkCmBAKpflxQ/iHiZ0yoqlXl+KvE5eakiV6S+KfUJP8XSzGP2DqMjk8SIfAwkQVUK+acJWefQ3F/bsqBRSYV2MRG4Uy86m3b92oI3ECzkuxGDNQI7nHg7VtszqncEjnW8HdCVmD8yB1kX5FNldqFkL+WjsHJnf3ERgL6shMyRBeBT6dZobpRhF8Ldh8pelBWkIUAzFcP5A6X8D0qqaMAzCin4MYDpAnChqvaFpg6GtsCg6qazyLXJdmrOvTKH1gkmeqEEBwMqBGS84jEvi1DpGJdi+8MUtEH0ssUm1rsNfOmWzCWJPmR8LwGBnuqbv2KmgwyX1p7DWuG+IgsMWjlXkxckeJZsQVPfbEec6CjHuuffUjP8rJ9F1CQzyZM7CGuHrevecZZLXu9MnnUiretXO/at60fa2lBnBA1YGtZ6Rk/UYh1EwyZV131YBQ8TKKDZkatVUz4NMcLgGbh+VcPpRSyhSTWOAvpREFNO7eEiWLusMwDAQ42+8KrhyaGN9TObuMZ1rGRmUaiHzhv1N/e6ySbvyENT0CvaQtEM/MzN+7N/d0w9JSYoEr1LcrP+6zgKQUg7s/gw2IzI1zmRd6/RXZuR/N1rol61vq/Q4SxIneud8pYp8A9xP/4SWu3JmoPdhjqe3X2le7OTs/eFLx/6W4H1PSZpr6KkZveAIkwsMJ0HwkwVq6oqga1ihv1h9l7nEMUs9yftpa0TOOMr6iDJ7jEcpNMadTzbI6Y8lQCx6MA1nAhPRT/ksmRoYD1mqOpltsNAGWhmLSatrXduZn2X9thSBitBtwBm9NiWRaLhhBOc3+o6lPcm36nyS/qffuekIEURGFaWWvGmev55FGBJJd+hloiw5HjL12t9jNAlxn1MYF668r6Oq3nS2IVIqDcH18LWjf2tJCkNOQWDGYJl/nvGxeUx3cuG7wUr2uvgfvf8Kjn6yymVojOgzITB77ZrBkK0x+D3xUVsJw2PFo4283nAQJ0+jPIjNoyOLbiXAsZFc6tOBIWwHEAvLaWNDvlW/DpFcTe/Hw+Uyn9f1uJDD32krL1cIFaAcCN63aCz/qjuZ9DS0hQjCwosLJB4izP8GWf16pihtNAvtm4ucHGDoO//5qeLlqh9+NEZUBMmXka9Hc19dyVpU4csZwpz0F8Lg1ybtpenBgUA/QFJZOaekRftySo9Cu2iysCwK9VtnTnPe8/K+Y5kHop1n3OAi/T+SX1Irlqaj3NIYvcG+97umbfPiruscPCmEcNb2nBBfzOAJn559fm7dNh96bj4SOvjwDhs+fFLDWM7nOAl/rZCZhi/3g80K1uUwmpzfMEKHOKhWQQJ/AqxwCA5Yf/4qnTLtr/jPz6oox/VFFCnBvmEWIooe3HKS0u/5+XGyRZPGxJ9vlNPLeLqg4zSaDcdEKLxg9Bcq/VrkMifbLk3YPZckLau58I0KTzGCc7BvDqaHWrU8w8ODviACdsvQNNsCo2zh38ic6kuAajvzdv/GuMxx7hl5VDqlzrwxyAeqwxUepIUAfAJSakwZmuUWGw7sh5yAQkm8pnqYBfTAHxlJJyIGspRS/3CaFR0wGXx67Nf7jd6rEuYvBJr+TTLARB1Wxg/jRlJiPhRDoFI3nvqgIkrvVQ3iiOEYDEn4l71rrDQzQkFmmTHImHrsSVv8hOb3y/idjzkVojkUCCJJQPtSqI1xqOxrSweehBT11OK6uvirnHtFwZo35HV3uTn9osvL6Lxo1o3corwpDXM2oy2BNiNvsDd4wYf+y0LBssTBWCtA0PaS45oZQ1KxE+Z1MTQ/7o5kD77gj2jx+jOFdx6h8mgEFjhFAvKDXPoOW162LK8Msp57E+anEX7QsNDN5nu7o9DsJIrHdbKh6lcsDGf9oBxdx06D4B8HUCIbpoBNPJMdKDyhfwy+VT5VvgtTfqJfsrmL18Lp97RTG4yMFp/ztmlg+jbXmi6tJKk5MM0QhQvXczs6QKGTx8QMBF5Zk9NaqTJK1MS7YOvnPWzg3LB+/W66b1CmRZgUivr34fQXKcGSgxKe0FJSZ0R5QXBAVxdOHP65HFEkXvBED8Kx+os0d5/tdZCZwYUXzRK+ztGGwi5V+XfPtfx9YgO9+VFvHaXcgqVah6HMhoaOSfE+1BHzZOqL06KX4SjDV68JdoTnsBMbGRXJ9SrbMNaAi8ULasXpgZ3W2xpyraLD18O/UTydcDKpMK7s+8W6tZfAnYB+qF7uSW7SWoZoBSVvv1tWnlxMn5uw1uDFa8ice2Yh3UPxN21bFW5KgZYP+aRRr5UWFxqXDchogRf8KgcocoZIg/drrFukCaaDAgV1HivXVEDDNQGjO5qI1TS7flbykHzpw+V7gkRmjPP2+3UqOtnWSPocRQ/SmWVA1oBKEIMxsFHzvtRT52wQksjiEvG/6VrbswKBsXJEWg5jsfL65PsFmljHmdGHd/r3098zWucUUKtOUti2DCh6PEGiVnWToC9AZBf4e/pGdur+voK+uMeqNWWu1X3B9mv2LNHmw3GuikH5HGkBmFgI8/R/z77/gOpu5p2HfnMEb73xryMah8mTP7Af4vfNAm0saf3v0xUkLjA9mmcAzKGFBhtIubIUWhUlBi5V+aQcNbz0qEpIg6ePxWYBXqlxrPbengmbeJKzwNhSplG/Zgbw28RPtxjUuRfQYL1itTYf5DMLUw3q7QE3HBrdOtfJpclLGYxEDp+/x+Hdm89of04qHKmvdxOTRMDnFMQF5Jjna0ch1/EC+9+ndtyBfFx2EidkVfiwqXiaFjF0uY3Kj0L7/rdC+zqaZV7mXWsKQvPC+MRs2jCLVlDnmOqzF8T/2+dSZbiZnWOELeGAXMXwKr0Xgb6YyTj86ocNtf/GY/CjrB1pf31gKfwJeV4QUbjy3h2N5b8E1CeH1+XQwWfqisIgBdGO64z9ivw5X7zOqAxuF8ABCcAxQrXb/vtc+SoJmLRHumE5qpIblmZDlZ3/sipYP+OTxoo2eMmhbccFoUf1DP4zaDbiM2yrwYchnbIYRoww51GDc/J/K5NJBCcpGtqSdo7IqEteF8bpFBAIYmUC58J6GIWw4OonvOFQrEpRZFrOzTyb8SWbIiSeuG6RcXIo9LaC/uQf2zKwN348VjH2htNUYiPMJrDzfN/EIba9Ua01rgJfT2mIs1AOZBu6/ddcM4w9oWmzj6h+w+czY7JUPZ88edDE172rrNd6zKuXtRSRLM93RN4oSda07AGceu65QAeVtiFHhOR5GfOF/8PtpIJRufYZGHcNe1m+0D70JKJgRgzGCaohdDddZ0s+w5FEbZurpo6nXZ8JgyFDF86p6J7c8MZU7RH+jUVmJI7m0oxga7qMJWu01dFvgrS9aqm6Y9OJ1SmHBCnZMmayBLLB1G8/+Bl/2oxxF2Jpm1KGTUCzN5q/dp4SodtndX5i2M6C7ndvw9/werOxw4ZWYfkDJpcLH31ZgJBQFv//Ztns6CUmqpocmtV7gaBfHGRuNSCZxyZXHbYZZTzGA3yAoRBcGd2RDCh78QNJMb73/c413zhcquIM808fs+Daxowo3bBu0iiyxs5l9tFrXYYVQ5Ey4d+U4FV/Iwh/nJgMCfxl4od0bRRdtx3vncQad7bhoVABHEuCdP4ARAvpO/s1HZQ/Pvw1dt7PM9EXwsCw9DwPSo+omepXxCK+pg5Zm01n3iSOhNmP7+sBHgFGkZqcKrzJEWdfPoDqFPzzBm11E4Y5DZ0KMIpP+BjKXzVGr/Mjo0NzhD2Gj3i9T9s0pB/85/peOTa1GIOLK7hjs70nBejQadBa6ZG9wVIsWQe9+KDK6Qd6qLRJSGGvgUsOvO+BAUwdxZJtyFQoUPUV+oxIAL9GmVAuoO6QYx/+AlhL6MQ1ltpNubQb7Xm82Al85yMSMnaxhFGy9+Ifr9BoSva+AEezGCm4NKIpPzgD1uaJym60PyVjnZFR8Pm9FHKjYe/+IhiIOK/3/aaa0hh/U2jcSww5gDlt2rcvREQzUgu56c81JrwSPCDysMsv5hrLOh0RuFpgVFnH1cA7g+MpJgtI0ko685DiERtUV2aKjjSFzcz6HR3d9N3s5Ge8SAr1bGWLkwaK7PbDjSeyUz78ERTUHk6Jz0RrDJrQ1NEfVwOoUVaryvQ4iPDkJsw1cG+dQxHrBSecHdI5yMRguDp9EZOlldACnlmHOTnIsvP3m1zYTP2du9FdK/U4luVuIDSIPUTg4bW2I16THRC9E8s3LEiz0AdHLNXzSF1RF8VAF4DIEk/puT6DJsnEJNlgeK28y5OBypyv1pllEPF0ZtBqfzs9MLHO1O1clwTHJwp4HsurKxQ/w5zypOzZEzKWQGkZY7bWG/d0aztWSPptv4MJYvyGfjSPao7z+bKX2/mQQq5SKHKjVDnZ9bklLV+aAoTVuQo/m4omuX5gv5dIHdRNwns2/JmZxbA8d6Dq/RT0eBTyqjAcf2aM/iJjlMtS2LK+BA+6XTPF6L0fQQ5gv6mWAKevkedmAXotiWMUFf8mxTD3xYChe8+WzsVbaMbprL9ov2DEzGMmvC45HoD89S6bB+MH+jmFoTx3VR6bBuMdnrmcU+TT4UEFJwJ0rDZkDYkQqN9wwp98qovdT06SGEm0iPArq85H8dlFMABEmHcB7DaJBEUsCFjgc4Df/yvGsJVGGSLxM4HEiCv2Nh1NQR7qljWxwR56aPGGWBtCaLqGP9FEUm5cYbvFK638Uy6X/UkvZKJ1tHuRP71kmv9B5sjFDkvHXPliRVUVpafmQWufr92ek/6uldP+Rm1yPF3u+uHa500LbQdr2ietpW/6qyF16e3W7Fn9DYEZ/kAeJH3RT5K/7EMm1cgV3xdk6+ffjeMt5UUGyWbQu4XwewYe12F1+dlsBKTSCjqNQuz+cDVK+3f4vas65RdR0kL+fjr6cwVdB1F96bRin6H8gqbnZM/6g5UguO1oESv5L2RBV+EcJjUtS65yGmbhqmiz3+b2Hz3GMqWASD90t7+JhSFu/CloUyTIJHQZtYuhvG707ad0is7l8oMt0qRsAB0X2la0ynr2eVFmU33Yss+l7R2QwS2dCKZUifTWFXDuIB2qvo/rdn+wN/8wy9UvRNpJ9iHDJgZ4zEZiTEo8NqvZL9sK3IWnv85USbJTV3KY7NgpRyjxkbKvVLW8ImGZXTU/8yYXg5I0UKOKpC87+9MIi0apeol/4QRdTwITPU25Usn0LORHjS40EKqSRzCMeWGf7yJu6zGXoRzMrIWIpTr8OUdUTanj1ttcCYhNbc9imsqdaNjK+EB2n0A3PaquyzvRZPIZFuZo9VlUxbv6CdPZljt2sSM+aNYb+/0r/EuueKLCc2Q5Rtr9u4cKzpjkc3ejIdVoMtadwlHXJe3O+bB4/Q0lX3ZyuH5X/jKnCg39cgm6KSjPMP4mz/F3HWA10Uyq/K+bvhw0IYoJCJgCYTpOgPqS/4WnSLfw9pFdAA50rtYVZMFJAxlVJlokzfgqP4VXfbdzeC5IfaNjXPzQ+IyrsxQoBoJRfekKanTvTEySJqpdlsteiFdsgDMhKTV9AkhBXx6SE7r8K7NyH/JypbCF+JODT1Uy3/xBHYzWQse8as17KjwUyKXxBNRkyEWOb3oiJoJMO/vBPYK2z6XyfnEFos1XaxWvtilqGvuwZQuhQP1lxleAqQyEW23n04wnKPwXth+nRzaB8Nh4JciANmV+m43/yI2jJKUkz1Mb4218DubP0Ts7LWlMDuLEAZLD9zHSL1615QuIspwqXPEq9kve/uqOzw1Z22rldhqvLOwz6uoeQA3wnsuyqob7eQ/cSpncWlwOIydmiaIcen6gC4ALpeCctVLIvUevZ+5C3TQeF5qV4p+St5/Aw/H7ILDu5lzWrkufR3DsmR0mR9QCrT2aA4RcriK47oxFn9uYVgvhQLejbAkhr2r4CDOXl1I2k++5vY3WoNmKqHKTr8ToLG4cKqWty7yAxG9PI1W30t2FOPjaU8ql4yZ/suR7oLX85MMS/rhOtHNu5msKB9/7oBJ5LNGNoJ/7gXdLQtdh4r196rztkF2zX6uA2gh/JKGXYX1HcMG4iban0KqPHuIQc3AGXk/Qz9YUMY7XjO8FQEm3hay0F5Hj+FFNrjdfcIO56EagPU5KBbYRnfvQo+J9AKcIgUBpVeR6E7dTNmRU0vJFF2gvxyCW/qre/p2G/YZpUflFB8OeFRmRS7f/dQ36TH7kdmPwvSLisMnpWs3ImFNEm09J+Wz07xw18gAZhKxlyDjqKK8mj1KAxcF0lT6vC63PwXHcSWS3RLb7FLn+k/ytRM9fZRv961pC5DuomaQnBQOKbXauroGWhiRwhQSZwnkyzfiWC9UvbOaDrhxMaWDdwaVKyeTNcbelI/jeEVzo55RqZwaqXyVjxFgUhJrNoi1kEfBf+/QI9kWtrAy5XSV8HH1tn8a7xpSws7jM3UF/8mzkRRx/W+QNMGcfG7T4JylwB9XSLf+Q1uaBBGW/2SUNMzYnPhBB2KG1v5hKGYOHW7/ViFchzcDEPmyY8kbQ49PXEeF/2G0evLlMGFyX6+HbgeART8ykfbc1+ckgoP0I+8SokcT+poM+cLimkjxO68peB2ejGvvotVxYkly3i8jMO51m12lguU0SDUZKovdJr4bknJyPEzRPSjh1f0trA4rvopY7Im8IM1MEfPo4qb6glNtfIYduf/lyRM5YLQpez7vLjBkZ3EgReP2njYgA/ZT4ucC5NYyfq4fxp0eD+2Cu/NflbEo26qoVIjtBcK4qEn8lK8F/v7M7DZ/sYCw7l/6GYUW+R4g1pggoPZMm97M0l76J9m5T/Ub4LtDAsQHkwDHiC94J9rrk/Lmhw9Fl+MuBBpeyenBtPAwpvBd/I+vGXHpx+WZjiGD8yomzY4EHVaycSr60eRoC9VpjUI7Ql+SfYQk5AhR31GLb+WRYfxfcDRUPDYjrbOSgCFM/mu4eTfDXD2kgXkQgVHxbt2lT/3VMkAua2Mvn1NP3P16PChDNfZM5ToIjzI2YmTOPqJ0vSZVjG5d6bLNky5dpwyYNZ/0roceJQ0oXUb742aA5n1Hd5oIs+STAmg5HubwU9/4mV3Yb3t0zhW2QuevGnBgN4gmIxJCTqrN3u173KsgMwc4AB4TkMQg8vsYI++uUjwrIZ2iiGr5LzTX00CCiIq8jg8vK4KKJYOA/22XSdV2prSQPGrk/8/XCbPhrqNUCkuTeneZS2DfDYkz9Xw9/FWiEmV/WCoLdf2NWmXUemmSMSls1hthafEc/v6iZWqyanMjoeLj3RFeBaI2qVs+vTsjjl9ZHUuIhCZYYSIKY2cxdx0nUfc5R/Xnv7sb9eNvyS7NqHXT/fD5b/qfAu57PsitJH1wINeyKN88Uoc8v9ZADG6ZvnuB4h5HuQO7tPERsGGdDfedn0eQW/tRWOPB31Q76QALIHn1UV0PVxzW4+y7odp/rvzI5hwd3jBhgAZJmrzlPNrMCzWwK6jGfhcqO9FI66Ulwebdno8lR24GHURfdONZUuE1gCP4buFlkRdLMsdz8qCtVvYVfkkaNDs1Z7PNv9omZufmklFHrxXAHbf7cBh/OG0Z5WMnR3JPphimM9nxzsoBiUzaBm3hcN2pTJ0zPifPN/ZmsPjiLR0tLNAHlYnl5anEulr88trO/wv8O/9jJgJ6MRD4k2rRgGUX63HBU+iKhEFdchL4y7Jk6NI9anAY+Ivp1o6N7rX9V6iTIPys6o/ILe6aRcTcqAuU4U6ViM/4pvVl962aHaHGMLHwcnHRESjAVBJ71ryqBAcBWTHUAcgvy4PQ0bKJVxv9KU/SJFphi+5ibz+oP706k1AAhYkvtheR5h7n+UsjM3VZ/Wdyd8y4tnmhGFSb29NmkHcCEjpLHFKOgzr9Sw113lLvp1zabKHxu/F1b5juxAmRBHdpGKmTPgoNah46kMKqq+75JVidtvvvftE1CxOPlfb/XoCnvcSlogA6hTgmnY0CO34chMD6OaFZFzr8ZY8wurDTmbSn3Pm/fp0ZfhXij2KeYaHNwNpj/faqF9MQzF3V58NA7aj+XfTq16aIdsgHfVFASXASE6M/b6vzBenA6QNBfP2adtQ54hg0qDb1k3g9jx5hR3IwTM6NbdMNLC8sc9TlPgYvhNiqOhxPGwG1LwU83QMRI6nVN358XDlvZoZfnXPmn6B1zEf2K3EmizjRpZm9pOCmkLPm1fcEfbGyaf4ghjV1rgv+UOrLt4MuisG6rGJr/aNm6bsX7DdhHPD3/HAvtCGoOXLzuHYNLuAp7jBp0u4Gd5wH9sWEul4fUbMSMev4myc/Ua1myJANYbsB7LyZzQBpb4+UO4KVYHW5a9qnQBP3NmW0Tzm6LH5SCd3+d0lWyoaXq50XvCiKuoOIiYqmKgebBDGerGSGecBdi5dLlRUnneSi90oqkDX8TGjDRys6/6QPKE8i0fm3aUpaCiRLrd2Tkj82u6/7B6Sr35rg/Imc3BpqdBRnNMBU87w8cc7FVsGb9KN0jkeZE0GUmKjhs+Lt9pziiMQqd0RiroTnxOT3zZXLWDA83Dd80p3M3TiwieQ6Ho80if9PZ0PxNDuVo/bCW38n8wF6OL+muGBIQC4h0nxERfUw160Elqj4CwkYjA1o0Lqd/eLb4/gJn3NrIVdCU6zbAEpFfy1Q9nfsBiZisIqcDxY/lfZeDzC5kJLVCN8yLfCke63jYce1GMt4G9nmcmf8ZaF7/NV4cA8avKDq3Gf47AiWOjS8eXQRZ57l3xx/aegNaUmAvGHZhp+UgcDxm+pPnmCMpqlHMzxmXf8WoOkjbEdDHMqf1i50WB5pC4fHDZwo/iG0iPDLtCDEHVvYihSykuWL36KgZBvDFrgRyT7sFG3TWiVlp9I328fG/FAum3ubyYn6FSZsgPHsfw1fG++tzlUtPLbnHgvz+evK6CvNY81/qMGAbNs0bzRaT2n7fDm3TcrgfEsjECBbLqfjXZcgXCtKEY5lwUDxKB4QABzkUTYLC7hIvOuBR8m+oppuDCNoq4YyJxR6ulGMpsPDaABgOInSlXdEQmz60binaBuxV3QWf+UAZyemrPZWssBeKHOatpx0c3NXr36hETrFFV79ykkv1/uUpVvbEOb0qdhnTN/+ey4xsPevdgOM1As5aXyRYVCJoZY7VH1pq4oJgrte02bCLdt28Ke2edhwsED1estPScuX1Ifs+pPntgjnPE+jl0Pxj6C10CT3ONeYR4Zk9bkLj9X8uij1EhXX3OvFQ6WYYsG/+7fn80qQe+ysUxrAXjoEmTwVeXI1ImLoCW0USL8qVm8ZjZu3Cs/NCQZyWd8m8bBu0p1h7aNKxiv3w5+EZg3WSr2LfI2vmj1SSP1OBu3Lp6fOg1daS7NH8DZUSrRqO0/bwtT5VzCWfrQFNblz59SP1uwS+f0jmXgHMQu+ndNjhhmUA4Zjm/WHravSi8L+oOlvXenh7KXDYXDwY80jJmRFrNXEvgSjjme/zzYJitaX5MupH1pgfImpnFViI6o5ZwDxhkW9UN35PvQkqMo9vv889eRKl9DGFy1qDb6YIL+UHOyit7zlVp458OWYGTmlLh1UyJTZCBuyVI6zHyJ29jEIVO3IUKHeP5XAGK658b/iLj0Y6bmIDpkOI5jUDGZ6VKsiNNAztl3H8P9ugZSqC7YKlC2qztfDZbsjTOPuX0mGxAWzRvxw1C3zYW5RSa1NmHwO6/BQ+tP4iWp2IlJudsL+j4R6+RC7+rn1TeO49oUqoGDi1fTMN5/DsR7O+JNQn4FbESYjXutqer/6N1DNfgNQ6fFJf9JftSZBfOQA2wNcASn/V3yzdHteWP4HGMdSY2JMeL42CgDSGxeTNWwuMPIHbIPkTCpTSDtRcR6iJWJ+EQAg0meZCu9cXqKwwhSO0fI6o3ItdYeF9e/Xb9/mkkMfRgklsMLT/8K2RsShEKPNDVJ1b0Sfd5rtTipxnYdMPH3XXNYS9HL+KkefohqdFOMl/1Y9Utp/LkZrD+HyVjKb78sU24dqH2Nw9wGdzUesz/eFEn4bzehpc7FhGFRW6v8bUYnlgHRQii9m2zhvR+WGvpiSYcNcSv/46Ng09EwJzfzRvXRkNexb2aPLzEUll75np6nC6MdJOLZFcN36nfAxaqyf5tyOf7TQLoQjdFyjqmzo82caHtcL8PDMZsJ98SIYrHiVfqTxHMw6tewBYqOIOr7Sq3n8Fdd7lG2pjz6aeGtAXXz0Ibw+eCkakwX7/3ffvo5cNKGzIRespV25Nv5zgydeL/X2FwK6gwe6UDo9/8d+7F1mFw01V07kkBH/f7Uf9uXPsSugMGQMuCkDbZeyw/XscIVXUIhWn1yVi9Is4SPhe46BBypeArm5k/UU384QWMrHe96Z+IbJUTOVyI5pXWLS/gzLgnt90eb4fBmjs5ISKOz894QaYcmu8tifDUtplyHidZeQoimHnr4xUKX3gfwS2qbo1JjyN1qafyj0qs1X2S7YoqCnAJZ+dxjOSwIJvF5YiTdRUWop4Jbk9EyuqGXYkf7WsCT9RkU35FD10QLbh0ME/kPUpfgv0MwAawpmWEIEYE+X5w/dR8pJqwFMnQ/DVXyq/bmPP+vsvnEON85dIJK4FbaXyLkZuGgufjyI+s7z1iDqaPv54219SLmhJDN9aaBoI5Y0x5s9CM08VQEHxM10q2MI73Lsub2Oiw2GOSzej+BspfoFDaTiNANC2rhBMUWtZ7po28kZ0i8Ncv9s1dZbM9AAtQoXYlV9idrw/p2vci300heAbc9R6Tv/Z1rKR7DciLKsiVVmlmg2Tf6jobaasU4ERmniy8AL789jvgf+whDphPN/ziLp1BSuT7+8BUo77K9BD0QNZd/+GEmGf5cn7QYNcVOnKH3VQI8JDm0lJxxdDp8japOMzCJNXl792vyND/WsZ/3Lgj6jFvzT0RgSDq1GUsRcTAcgsoVdd3DcDKq33RfmgdLAdE8YS+sbyb7xsgy9IS3JO9D2M+3vJXxE7bP3bmF/o5sSqZCPuhA6ATNqhiNCCpDODhTX5hTut/mPiVmx3oJmcolYg3Az2mOvx6eHOnNWP1eAA7kuAbeFbsI9C7Du0qsj4D9UQYYYLsO6DeyMqZDx73qdQqZcLFT3Nh+Idx8ogdNiWLyQm4V9OR0cjQVsN7AndIjuKah4tiTRcdfIG7NhHqPl8D4Hm0CDTeBRGsl2L93xT8tfsKhNASiTdJTbt7Mg80B4xjfQh/tp0ZS/UThGfIy3m5IvR/bu46NMELanygUtkhTBTONbPMpHP0D/mwgi106Pc0ZDOdYY4xbLfOyKnnfBudkbxlJZsQ/GG+YgicCSYM4jgRv4JmG4riJhmyMVyk/hX5EUA33B9i8R/Kk8O/H83r3wf0lzUm38ryMEyRkubYTn+uOYbA4AGmt8y/jT2mM+VYxSm5nd5+lFBH0LzV83oMQ6iPtTPg1ci1Lnc0hPng2YfOqSipc80REsbmRo7NgtThw3P62txpHhqAPvPzc9EDYwzGeAVLdvq0eoBKQPulgUEZM6bDFnAS23LyaqoWQ8OnBrtXFZid5T1k7k7hWogV8i14MrUpA9ytIKMFUQQexdsVOeWazg2c41SwGvFKb+mJ8e5QDqfBgF3L0cFM7jVd3cRQ5Qn2a3cXRWNMS6k5CUFXvdo89fbLk5UQOXdco5uIAazb6ZLfzoiOE5QMyCOcwEbRNh5Sa1STFeHREDKL8z6kBpzaoNvVgrYVwX9MWUJ1R1RNz0UMu5zsAaP8UpDwYg8yoLjyUiM5lY+ws1x/840I6tAUxoOIX4QSJ9f4faZdPV7f0uMnEyd/u5IXAEcZF0R3N6KRaDhil43Ip2X+bsK4M1m41DSiTSEfk2HZ4irzObfzSksI+lg7pAjbO5ZiH7NvRqIj5TmOw0U8BZ+7oVaYuLxNpH2Q3DQCxz92elrw9iLYzZXODniJ1/CkSiinK8iB2SzDURr/7k3MZjs6WX9+4+R3e3+rMxd/LpRG8j4yh7X1DBSRrEuHPvaWy9ZxbWUU0pzp4bujL/tBE8F7pvZJwzF3USsAT1fUEnvzNDbqhQb18HQ2TVSIopWyrtN5sBACghDwAt1qdxv54Es31qdHBSR19aXQA0thKrggI7fwUgqymb31xwM4JzCuvvlHDI2enAtX5pvzkdi61mzeg+Il7BWVYvs/o0JuyVx0woeZUcL/el/d23kmNSdQ8zPW/LTLvIPN0gIdp1CD9ThJEhhF/cOl7Qd0bQMg6KWJLvc58p9pKkWTUcbmcHDJNl/69fX+aiQ9BLKeNOQxE4XQS6ixrTJKMgIGeuL+nVdwj6j9/jjld//MAUafdjpemAxYnbUcXEg1k7iFFxroG+ODkudrp6FT05d0KseLcIiOBM5Ck4LsKAO3l1+h67LnJoTSEjgVEJmfPgyyED3uM68scKi/uanJ9iimeoa1mc+5QHXzPVJLdrgLlu9t9H/2ruu5laVLf1rdtXMw3GRwyNJCIECAhR4IwkQUURJv366kby3Fa7tc7a9a+6dUVllddOJtb4VgaYQRUM/i1yYavoKcylztOuB1w3PXlD26KrBcW94IeGSmnkEq5Zws+Z4r67KUwqNKLxGPMoZxlkRtD9yz/pSHSkciGyUzKkCmPFHaTxgqUjiSgHR9ZIfSYhI5xNRyDrJag/rvHG5kNIVXXe4THS0TdOQpUPCMJBSSXxhHTKHY3k9jpOZhJQgVInjIBDhQ6LxiBeAb36cHscc7cK4Ny7Gbut4DlMgxlIQVDVcZ+eWx8+qGVAbY8GeTawS/KUwVjheOC+7vPPjvlY3MPqOh41hW1et5+6alCTJsoGlO5Yao43nW4eFEa2439AdosaSDGIt9XR0Rt14pLtA7nl/Q22mo7k1liQYicpZLoYyLvE+iLZ7k0VJKeZh6JntLFeG9zyl2/GuiFIqcmdOuFBEQdBoZ1KayNQzmMMq6atjyUyOaEVOi6FnCiRowfZHGEGE2cE160QWTrzqzdmcVcqNp8G8AI+yuMOCEF5IJBlYT3rY4RdBya2NFVMH5pfrrawcmgysZyST1UKzF93ZaGIZuBmsNvhGzkQvOV7hDnPkgNHAppMsjCCd4fH9uc0m4UQvjOO0F3ZM4aTWORj1idwlwoUyippTzdqXURRGoWbneVBPrUbGdq/zPHyWdNS3I0oH4XLe66oJ/Qk0MSJ4Xw1v7KTdXtwnjQ+0vLYRPazXVK+S1wJwUODjYpx2KgJ63aswuFRAaDhGtFkF3J3h1Tm8h2zM2gXrXypKdJzzrItkvjP1xv1aMOG995p3oc0kopVea3K7ndqLSVtlXT43OxFLHC4NGYM5u3174g3yIBqYPF+OOjY3eEMQOe44bCc3grbRPafAePPI+bzx+z4bzYJM6bnpihE6EO9unY0QR/Vi6TuDg6de6Drf+iXMhtRO3DfwbdW8mI78qm1H2J4DdIrNTKmKeZllupXbquoyS8Ec/FQiFWA8Hi8MWT9W4wm5b9etbo1UdcUh5rKh9KkpBt4oXXFdfJxCU0AFTb5ZJCIHWCMImSJQmtt0Kx6mu6tNxlIkr+hhrkonQd0v9L47mkJS2sSpMJiWZht8TRhRDDnDm8X46AasqAF9M6WlQ0uRp4wnYgF67mgltzymM3a8JEwBxBBu1hLJVg9hsuTIYWWA41P47lz+sM8yuY54AwkKrqhmUM9vLd5JM97oMs4+pgGFnj1mySiDGt5GTgl3R+MZVAPwGDaSO51XPnuOZF0qTuDMWwi0lhK7tk0QJbCqCguZPRXtG0WGGJHGSTgrm+BMjKs9LZfrMmv3MsYtRzNpKdri4uCQ2XmsEvoqWsOLXMYkK+FNcd3CyangOGgMXhof9tu6WthlkOAON+z6t1k1vLfVxIlkK/I2osQGwlsreMdOytIdz5AI2h0qI6ug2EuKF3JAQ/D1eu/SRY0egM9zpGsEX1ljxfPXks3Zirp1WQ4Rkb2RWk0YZ+MDVVKbI2dy6wkhAS3PSVbAWzR7ZOfuIjkec+CDmv5Bm804BZlKk0zsQjujxq6+Qqp45jhHm26GzZ+bwl0iSThwZbksR2ZLjeOxzFZorcLrQsSR7pg9WEfKCcTuPKLb1VmsIi+ZZsAzmruRrS380HM7ZDzIuiArLVMOr+B0qkU7j7C9u9AyYTPfZhRu4StdsOQ9h0PRTOHzXTtjvjTtarLRN91yNVdLPBPD5UnXAauhZNgxHSAbioWKKqJlj6c3jbACgRY3C9eIfOI5a8eIedbNzHWtSMm5TSyY+M/Kxabop26WG7oxjMXF41MaYLMWIK/3CEYi5vLacDbJWi5mobDIfQi8mXrShE2qextbRSp1g6/xWYe4saVfsJdUFt2UdFpqdWedVKHnbEKpVtPScyXesRJFiMeKyvC7DoFmOqOYYUMT27C2WyKwklmbFjssxhio5PbiqkpifT2sTwlteIVSWMCnVEemgRQdKdsHiZwfMnPu9abpC4w7NqcjwoplO0zTNjH3HEKsiLniq5PDmAtIA7ooeuXAvVkBsxYivGdkvGCZrUJLShj2nL42dsM+r3qz2081jDZmC7SmRkWVHJJwPi8IYDHmlqZbLlkI2Z7aejW8Zrnj9zu3OFjZVpXGsuj5K/7Y2BZMz68Kwelm3HTGz8NIGvSKClwwMEe1P0sJvpntNRk9L+boWZjb1lmZeGTfeNoWVWTLmi/krUbN5FnI7/ZnvcQ7vAltIeaWORaNJxgnpecZ6kEx5BdqZ6v+fE6Z1aESBkmWLM1qsWa45m9NMaN2V9MZah9Ve1uWzarf43Ukn9goF7djP+Etgp1OiN6JEAUT8O0cz3p4PyJaGL2v8jjXkYTvYkvJ2stmKNayukRNRfEjbYmJh41EtgZZbXelFl/kbuoM1wnBqWLYbL12dgc5ZcwpSdCW5xzyuBpNJqgozNLzWFMzP8DJdjK8V4gZy7s+SoC3cLaGt6FW3aLPg8nZ4un5Uhxur9xNSUqKzrucxHBTifuqtxNntVTY+DDbrfWQr1Ofp905dPtHaEPhpLAii0HB8mPJWcm4X5Ojsk7g5uggNlImh3abSHVqTZVwox+ZcK2exkpJ97ZpkqSttrq0DUWdyARihZAn7lxLx1lUR7niB6pVl44p9RYur6ORks21gEgSpRhllmcZIUYrSyoKI7zdElU90W25RyeMiogy2c6RxooAt0L4jQVVSnpd0pecBL08IJeCwg36h5fAF9qza9vpoBrBF0peGA9fYHe4oS9E2dAXsuHaly+4S1/gB/JDX6CSjGtfiP3l0PdM0IzAceKP1z262CylrHTGHRQOfqR0ZCZGq2eC8AMHvAX8QkrgwObND3grBXwSHFZ1QdUExzdVuPQDF7KjHBRZ0MAXEiLXo3/hLPmCXLudrnUEgVwq+thvouswJPFCkpfqKIjD6HVGgr5UOvWlIvw5x7BbyjAzzE8ehSBNXxcy/MaQ2L/04UftaHfuytQgbBwdL62IP/xFXGfrnBQYzaHdpaJugLa8VAS5z1VV0YOSlzp1HXuALFGTgYlEFPysm6pIgvX1NLD3aFYXbeUF76wHvqgANmycKgya9xbOXhoGfhi8y4M35CWRR9K+1lVB6jRxF9ws9xm5rzMsihic2y8WExjygrO/PhRxy24SQW7HvJDiOswvLj6OjDPI7VA4fTfUhVgPQw2A+EmJf44Riv0YI1XR5n4AR0EA/4uqiYqwyJ1UK4ryCpN90DQnIz7DHk7bFP8YRB9jA32KjU/D7xFEX4COr4IDRiMvGH03GvVnEUEwn9AagPjGtZgXOfjHfwNIdkXeXJujBCgDrFSnDZzhhXwtbq8TDgXxeFM6XUv/XGO9KqIPUYnjn9RYvws24C68INgvVXQHFoz4w2B5VB/TFTCt94Cp+zhLnQEoNyx3qlcOY5BTUVHFZ8B157WFF8WprzmnooXUb6ogeC28aWuC6iurq6COz46bvpY92CDOg8o8lcF1hKdYu9QIRVpUw4px+NntIArjNH1TjyAe+ID6sHL8GCDqzbHd8Pkiv4JkAKfxO+vAPvErsCeWDyWRD6G1DLzGyUNAq1+TsreqDEWeTEg/mQ+7m85Jm6DKnSbgoVqovwN6r2f4np7qo7gJjNIZhLyvnPIWf3es9Z2A2XnPwEB5TOA+Z+z7YvFpdhPMC03e0P6JC4lhL08cyHvaf5n/SKJfTmEyYHziGYUZzMUp6jspfGelUfQJfT+B7K+jLvb37GyQukUv/arghwpwAJIg9qDKfEv5W3v8N6n6oWF8Fb4PDSP53F37DcP4e0TH/5MgzfxvgzTxfwLSX+7r/RbR6U9E+X/WOUcI7MY9fyFA6PW+iz6UFkEVA5oE1W/77fRno0nms5mG3/Xb0fs4n2Jux/hmR51+ZsupFDrSftyBnyH8uXR2cNFj01y8HgWzvWnwpM9/lVj53/+q9bs4/EDRfoy5r1DFf98xx+55SRD4g94l0Z/+3Kf98t9SA8wndO8XqAEQsD3mC4fqUZy+NnuTVLxmB0DN9TjyOW1yjJvNq24Avy96hLyWfikRWLjRIZufS/ilff6s7sHpz+oe7A/pHgIlX8i3OYPbQOMvFv2zSYPXE38PqiWcfFgOyYM/ZAiFhdd/P0jQRhjqyaF8WwezIo9t0aGSeN6WJsgnA6N3dddFPDS+HRn84fyrC6M5bpAuijpu4iIH5+MWTVNkt2L12pZL4xC2aaA48k5dgggdFHfxEQot71wPewCTEKj8QKSgkrrgQqtBRCNnyHRkxxBo1OjFi2uvQNmXvqgSIKeXVTwmNUiExh8051V43won/UVKFEXus6Y08qhF0WdZffTb/Ffm0ZUSUpjjeYAnOM3mlocPzLlnahb7/uDk3ueobqEOsQMVb30l+DfR/0EJkMzP5MIbBlBP6I9/G/mfZZ4f/A2p8XzIjKACp/tpLyVz4hz0SqGlux51q/v2H3gv96Lllln+Mox4g4SibdI4B0KU5xf5/ZzJ+wquEizQW3c5QwphX1j2UbQQ4olo3VuCr+Pts+tMD1xaBJClyJXFM7i70j/mFlhUXNbBG5XopUXrf4Xf+SVe5L0TST6Tv6fJVpTGvolL7Cdyqm8o5Tt1dOvN17dUvTMzGEOT0tNs+8/M+aPdRwZb+0LdG/6LOcfuzfnT2qdOAvsvBqaH3oPMfOgQYMxd20F/3yuKpHWDKgfeZf0Se4P9LStl+CEGEOrfgjAUuQ85ycf00DP7SjDfha5P5JP/H13/Hui6v2+BwL8NXPAGqqJo3sYg8LynwDrAFv8D
================================================
FILE: Documentation/etcd-internals/diagrams/write_workflow_follower.drawio
================================================
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
================================================
FILE: Documentation/etcd-internals/diagrams/write_workflow_leader.drawio
================================================
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
================================================
FILE: Documentation/postmortems/v3.5-data-inconsistency.md
================================================
# v3.5 data inconsistency postmortem
| | |
|---------|------------|
| Authors | serathius@ |
| Date | 2022-04-20 |
| Status | published |
## Summary
| | |
|---------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| Summary | Code refactor in v3.5.0 resulted in consistent index not being saved atomically. Independent crash could lead to committed transactions are not reflected on all the members. |
| Impact | No user reported problems in production as triggering the issue required frequent crashes, however issue was critical enough to motivate a public statement. Main impact comes from loosing user trust into etcd reliability. |
## Background
etcd v3 state is preserved on disk in two forms write ahead log (WAL) and database state (DB).
etcd v3.5 also still maintains v2 state, however it's deprecated and not relevant to the issue in this postmortem.
WAL stores history of changes for etcd state and database represents state at one point.
To know which point of history database is representing, it stores consistent index (CI).
It's a special metadata field that points to last entry in WAL that it has seen.
When etcd is updating database state, it replays entries from WAL and updates the consistent index to point to new entry.
This operation is required to be [atomic](https://en.wikipedia.org/wiki/Atomic_commit).
A partial fail would mean that database and WAL would no longer match, so some entries would be either skipped (if only CI is updated) or executed twice (if only changes are applied).
This is especially important for distributed system like etcd, where there are multiple cluster members, each applying the WAL entries to their database.
Correctness of the system depends on assumption that every member of the cluster, while replying WAL entries, will reach the same state.
## Root cause
To simplify managing consistency index, etcd has introduced backend hooks in https://github.com/etcd-io/etcd/pull/12855.
Goal was to ensure that consistency index is always updated, by automatically triggering update during commit.
Implementation was as follows, before applying the WAL entries, etcd updated in memory value of consistent index.
As part of transaction commit process, a database hook would read the value of consistent index and store it to database.
Problem is that in memory value of consistent index is shared, and there might be other in flight transactions apart from serial WAL apply flow.
So if we imagine scenario:
1. etcd server starts an apply workflow, and it just sets a new consistent index value.
2. The periodic commit is triggered, and it executes the backend hook and saves consistent index from apply workflow.
3. etcd server finished an apply workflow, saves new changes and saves same value of consistent index again.
Between second and third point there is a very small window where consistent index is increased without applying entry from WAL.
## Trigger
If etcd crashed after consistency index is saved, but before to apply workflow finished it would lead to data inconsistency.
When recovering the data etcd would skip executing changes from failed apply workflow, assuming they have been already executed.
This follows the issue reports and code used to reproduce the issue where trigger was etcd crashing under high request load.
Etcd v3.5.0 was released with bug (https://github.com/etcd-io/etcd/pull/13505) that could cause etcd to crash that was fixed in v3.5.1.
Apart from that all reports described etcd running under high memory pressure, causing it to go out of memory from time to time.
Reproduction run etcd under high stress and randomly killed one of the members using SIGKILL signal (not recoverable immediate process death).
## Detection
For single member cluster it is totally undetectable.
There is no mechanism or tool for verifying that state database matches WAL.
In cluster with multiple members it would mean that one of the members that crashed, will missing changes from failed apply workflow.
This means that it will have different state of database and will return different hash via `HashKV` grpc call.
There is an automatic mechanism to detect data inconsistency.
It can be executed during etcd start via `--experimental-initial-corrupt-check` and periodically via `--experimental-corrupt-check-time`.
Both checks however have a flaw, they depend on `HashKV` grpc method, which might fail causing the check to pass.
In multi member etcd cluster, each member can run with different performance and be at different stage of applying the WAL log.
Comparing database hashes between multiple etcd members requires all hashes to be calculated at the same change.
This is done by requesting hash for the same `revision` (version of key value store).
However, it will not work if the provided revision is not available on the members.
This can happen on very slow members, or in cases where corruption has lead revision numbers to diverge.
This means that for this issue, the corrupt check is only reliable during etcd start just after etcd crashes.
## Impact
We are not aware any cases of users reporting a data corruption in production environment.
However, issue was critical enough to motivate a public statement.
Main impact comes from loosing user trust into etcd reliability.
## Lessons learned
### What went well
* Multiple maintainers were able to work effectively on reproducing and fixing the issue. As they are in different timezones, there was always someone working on the issue.
* When fixing the main data inconsistency we have found multiple other edge cases that could lead to data corruption (https://github.com/etcd-io/etcd/issues/13514, https://github.com/etcd-io/etcd/issues/13922, https://github.com/etcd-io/etcd/issues/13937).
### What went wrong
* No users enable data corruption detection as it is still an experimental feature introduced in v3.3. All reported cases where detected manually making it almost impossible to reproduce.
* etcd has functional tests designed to detect such problems, however they are unmaintained, flaky and are missing crucial scenarios.
* etcd v3.5 release was not qualified as comprehensive as previous ones. Older maintainers run manual qualification process that is no longer known or executed.
* etcd apply code is so complicated that fixing the data inconsistency took almost 2 weeks and multiple tries. Fix needed to be so complicated that we needed to develop automatic validation for it (https://github.com/etcd-io/etcd/pull/13885).
* etcd v3.5 was recommended for production without enough insight on the production adoption. Production ready recommendations based on after some internal feedback... to get diverse usage, but the user's hold on till someone else will discover issues.
### Where we got lucky
* We reproduced the issue using etcd functional only because weird partition setup on workstation. Functional tests store etcd data under `/tmp` usually mounted to in memory filesystem. Problem was reproduced only because one of the maintainers has `/tmp` mounted to standard disk.
## Action items
Action items should directly address items listed in lessons learned.
We should double down on things that went well, fix things that went wrong, and stop depending on luck.
Action fall under three types, and we should have at least one item per type. Types:
* Prevent - Prevent similar issues from occurring. In this case, what testing we should introduce to find data inconsistency issues before release, preventing publishing broken release.
* Detect - Be more effective in detecting when similar issues occur. In this case, improve mechanism to detect data inconsistency issue so users will be automatically informed.
* Mitigate - Reduce time to recovery for users. In this case, how we ensure that users are able to quickly fix data inconsistency.
Actions should not be restricted to fixing the immediate issues and also propose long term strategic improvements.
To reflect this action items should have assigned priority:
* P0 - Critical for reliability of the v3.5 release. Should be prioritized this over all other work and backported to v3.5.
* P1 - Important for long term success of the project. Blocks v3.6 release.
* P2 - Stretch goals that would be nice to have for v3.6, however should not be blocking.
| Action Item | Type | Priority | Bug | Status |
|-------------------------------------------------------------------------------------|----------|----------|----------------------------------------------|--------|
| etcd testing can reproduce historical data inconsistency issues | Prevent | P0 | https://github.com/etcd-io/etcd/issues/14045 | DONE |
| etcd detects data corruption by default | Detect | P0 | https://github.com/etcd-io/etcd/issues/14039 | DONE |
| etcd testing is high quality, easy to maintain and expand | Prevent | P1 | https://github.com/etcd-io/etcd/issues/13637 | |
| etcd apply code should be easy to understand and validate correctness | Prevent | P1 | | |
| Critical etcd features are not abandoned when contributors move on | Prevent | P1 | https://github.com/etcd-io/etcd/issues/13775 | DONE |
| etcd is continuously qualified with failure injection | Prevent | P1 | https://github.com/etcd-io/etcd/pull/14911 | DONE |
| etcd can reliably detect data corruption (hash is linearizable) | Detect | P1 | | |
| etcd checks consistency of snapshots sent between leader and followers | Detect | P1 | https://github.com/etcd-io/etcd/issues/13973 | DONE |
| etcd recovery from data inconsistency procedures are documented and tested | Mitigate | P1 | | |
| etcd can imminently detect and recover from data corruption (implement Merkle root) | Mitigate | P2 | https://github.com/etcd-io/etcd/issues/13839 | |
## Timeline
| Date | Event |
|------------|-----------------------------------------------------------------------------------------------------------------------|
| 2021-05-08 | Pull request that caused data corruption was merged - https://github.com/etcd-io/etcd/pull/12855 |
| 2021-06-16 | Release v3.5.0 with data corruption was published - https://github.com/etcd-io/etcd/releases/tag/v3.5.0 |
| 2021-12-01 | Report of data corruption - https://github.com/etcd-io/etcd/issues/13514 |
| 2021-01-28 | Report of data corruption - https://github.com/etcd-io/etcd/issues/13654 |
| 2022-03-08 | Report of data corruption - https://github.com/etcd-io/etcd/issues/13766 |
| 2022-03-25 | Corruption confirmed by one of the maintainers - https://github.com/etcd-io/etcd/issues/13766#issuecomment-1078897588 |
| 2022-03-29 | Statement about the corruption was sent to etcd-dev@googlegroups.com and dev@kubernetes.io |
| 2022-04-24 | Release v3.5.3 with fix was published - https://github.com/etcd-io/etcd/releases/tag/v3.5.3 |
================================================
FILE: GOVERNANCE.md
================================================
# etcd Governance
## Principles
The etcd community adheres to the following principles:
- Open: etcd is open source.
- Welcoming and respectful: See [Code of Conduct].
- Transparent and accessible: Changes to the etcd code repository and CNCF related
activities (e.g. level, involvement, etc) are done in public.
- Merit: Ideas and contributions are accepted according to their technical merit for
the betterment of the project. For specific guidance on practical contribution steps
please see [contributor guide] guide.
## Roles and responsibilities
Etcd project roles along with their requirements and responsibilities are defined
in [community membership].
## Decision making process
Decisions are built on consensus between [maintainers] publicly. Proposals and ideas
can either be submitted for agreement via a GitHub issue or PR, or by sending an email
to `etcd-maintainers@googlegroups.com`.
## Conflict resolution
In general, we prefer that technical issues and maintainer membership are amicably
worked out between the persons involved. However, any technical dispute that has
reached an impasse with a subset of the community, any contributor may open a GitHub
issue or PR or send an email to `etcd-maintainers@googlegroups.com`. If the
maintainers themselves cannot decide an issue, the issue will be resolved by a
supermajority of the maintainers with a fallback on lazy consensus after three business
weeks inactive voting period and as long as two maintainers are on board.
## Changes in Governance
Changes in project governance could be initiated by opening a GitHub PR.
## SIG-etcd Governance
[SIG-etcd Governance] is documented in the Kubernetes/community repository.
[community membership]: /Documentation/contributor-guide/community-membership.md
[Code of Conduct]: /code-of-conduct.md
[contributor guide]: /CONTRIBUTING.md
[maintainers]: /OWNERS
[SIG-etcd Governance]: https://github.com/kubernetes/community/blob/master/sig-etcd/charter.md#deviations-from-sig-governance
================================================
FILE: LICENSE
================================================
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright 2013 The etcd Authors
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.
================================================
FILE: Makefile
================================================
REPOSITORY_ROOT := $(shell git rev-parse --show-toplevel)
.PHONY: all
all: build
include $(REPOSITORY_ROOT)/tests/robustness/Makefile
.PHONY: build
build:
GO_BUILD_FLAGS="${GO_BUILD_FLAGS} -v -mod=readonly" ./scripts/build.sh
.PHONY: install-benchmark
install-benchmark: build
ifeq (, $(shell command -v benchmark))
@echo "Installing etcd benchmark tool..."
go install -v ./tools/benchmark
else
@echo "benchmark tool already installed..."
endif
.PHONY: bench-put
bench-put: build install-benchmark
@echo "Running benchmark: put $(ARGS)"
./scripts/benchmark_test.sh put $(ARGS)
PLATFORMS=linux-amd64 linux-386 linux-arm linux-arm64 linux-ppc64le linux-s390x darwin-amd64 darwin-arm64 windows-amd64 windows-arm64
.PHONY: build-all
build-all:
@for platform in $(PLATFORMS); do \
$(MAKE) build-$${platform}; \
done
.PHONY: build-%
build-%:
GOOS=$$(echo $* | cut -d- -f 1) GOARCH=$$(echo $* | cut -d- -f 2) GO_BUILD_FLAGS="${GO_BUILD_FLAGS} -v -mod=readonly" ./scripts/build.sh
.PHONY: tools
tools:
GO_BUILD_FLAGS="${GO_BUILD_FLAGS} -v -mod=readonly" ./scripts/build_tools.sh
# Tests
GO_TEST_FLAGS?=
.PHONY: test
test:
PASSES="unit integration release e2e" ./scripts/test.sh $(GO_TEST_FLAGS)
.PHONY: test-unit
test-unit:
PASSES="unit" ./scripts/test.sh $(GO_TEST_FLAGS)
.PHONY: test-integration
test-integration:
PASSES="integration" ./scripts/test.sh $(GO_TEST_FLAGS)
.PHONY: test-e2e
test-e2e: build
PASSES="e2e" ./scripts/test.sh $(GO_TEST_FLAGS)
.PHONY: test-grpcproxy-integration
test-grpcproxy-integration:
PASSES="grpcproxy_integration" ./scripts/test.sh $(GO_TEST_FLAGS)
.PHONY: test-grpcproxy-e2e
test-grpcproxy-e2e: build
PASSES="grpcproxy_e2e" ./scripts/test.sh $(GO_TEST_FLAGS)
.PHONY: test-e2e-release
test-e2e-release: build
PASSES="release e2e" ./scripts/test.sh $(GO_TEST_FLAGS)
# When we release the first 3.7.0-alpha.0, we can remove `VERSION="3.7.99"` below.
.PHONY: test-release
test-release:
PASSES="release_tests" VERSION="3.7.99" ./scripts/test.sh $(GO_TEST_FLAGS)
.PHONY: test-robustness
test-robustness:
PASSES="robustness" ./scripts/test.sh $(GO_TEST_FLAGS)
.PHONY: test-coverage
test-coverage:
COVERDIR=covdir PASSES="build cov" ./scripts/test.sh $(GO_TEST_FLAGS)
.PHONY: upload-coverage-report
upload-coverage-report:
return_code=0; \
$(MAKE) test-coverage || return_code=$$?; \
COVERDIR=covdir ./scripts/codecov_upload.sh; \
exit $$return_code
.PHONY: fuzz
fuzz:
./scripts/fuzzing.sh
# Static analysis
.PHONY: verify
verify: verify-bom verify-lint verify-dep verify-shellcheck verify-mod-tidy \
verify-shellws verify-proto-annotations verify-genproto verify-yamllint \
verify-markdown-marker verify-go-versions verify-gomodguard \
verify-go-workspace verify-grpc-experimental
.PHONY: fix
fix: fix-mod-tidy fix-bom fix-lint fix-yamllint sync-toolchain-directive \
update-go-workspace fix-shell-ws
.PHONY: verify-bom
verify-bom:
PASSES="bom" ./scripts/test.sh
.PHONY: fix-bom
fix-bom:
./scripts/fix/bom.sh
.PHONY: verify-dep
verify-dep:
PASSES="dep" ./scripts/test.sh
.PHONY: verify-lint
verify-lint: install-golangci-lint
PASSES="lint" ./scripts/test.sh
.PHONY: fix-lint
fix-lint: install-golangci-lint
PASSES="lint_fix" ./scripts/test.sh
.PHONY: verify-shellcheck
verify-shellcheck:
PASSES="shellcheck" ./scripts/test.sh
.PHONY: verify-mod-tidy
verify-mod-tidy:
PASSES="mod_tidy" ./scripts/test.sh
.PHONY: fix-mod-tidy
fix-mod-tidy:
./scripts/fix/mod-tidy.sh
.PHONY: verify-shellws
verify-shellws:
PASSES="shellws" ./scripts/test.sh
.PHONY: fix-shell-ws
fix-shell-ws:
./scripts/fix/shell_ws.sh
.PHONY: verify-proto-annotations
verify-proto-annotations:
PASSES="proto_annotations" ./scripts/test.sh
.PHONY: verify-genproto
verify-genproto:
PASSES="genproto" ./scripts/test.sh
.PHONY: verify-yamllint
verify-yamllint:
ifeq (, $(shell command -v yamllint))
@echo "Installing yamllint..."
tmpdir=$$(mktemp -d); \
trap "rm -rf $$tmpdir" EXIT; \
python3 -m venv $$tmpdir; \
$$tmpdir/bin/python3 -m pip install yamllint; \
$$tmpdir/bin/yamllint --config-file tools/.yamllint .
else
@echo "yamllint already installed..."
yamllint --config-file tools/.yamllint .
endif
.PHONY: verify-markdown-marker
verify-markdown-marker:
PASSES="markdown_marker" ./scripts/test.sh
.PHONY: fix-yamllint
fix-yamllint:
./scripts/fix/yamllint.sh
.PHONY: run-govulncheck
run-govulncheck:
PASSES="govuln" ./scripts/test.sh
# Tools
.PHONY: install-golangci-lint
install-golangci-lint:
./scripts/verify_golangci-lint_version.sh
.PHONY: install-lazyfs
install-lazyfs: bin/lazyfs
bin/lazyfs:
rm /tmp/lazyfs -rf
git clone --depth 1 --branch 0.2.0 https://github.com/dsrhaslab/lazyfs /tmp/lazyfs
cd /tmp/lazyfs/libs/libpcache; ./build.sh
cd /tmp/lazyfs/lazyfs; ./build.sh
mkdir -p ./bin
cp /tmp/lazyfs/lazyfs/build/lazyfs ./bin/lazyfs
# Cleanup
.PHONY: clean
clean:
rm -f ./codecov
rm -rf ./covdir
rm -f ./bin/Dockerfile-release
rm -rf ./bin/etcd*
rm -rf ./bin/lazyfs
rm -rf ./bin/python
rm -rf ./default.etcd
rm -rf ./tests/e2e/default.etcd
rm -rf ./release
rm -rf ./coverage/*.err ./coverage/*.out
rm -rf ./tests/e2e/default.proxy
rm -rf ./bin/shellcheck*
find ./ -name "127.0.0.1:*" -o -name "localhost:*" -o -name "*.log" -o -name "agent-*" -o -name "*.coverprofile" -o -name "testname-proxy-*" -delete
.PHONY: verify-go-versions
verify-go-versions:
./scripts/verify_go_versions.sh
.PHONY: verify-gomodguard
verify-gomodguard:
PASSES="gomodguard" ./scripts/test.sh
.PHONY: verify-go-workspace
verify-go-workspace:
PASSES="go_workspace" ./scripts/test.sh
.PHONY: verify-grpc-experimental
verify-grpc-experimental:
./scripts/verify_grpc_experimental.sh
.PHONY: sync-toolchain-directive
sync-toolchain-directive:
./scripts/sync_go_toolchain_directive.sh
.PHONY: markdown-diff-lint
markdown-diff-lint:
./scripts/markdown_diff_lint.sh
.PHONY: update-go-workspace
update-go-workspace:
./scripts/update_go_workspace.sh
================================================
FILE: OWNERS
================================================
# See the OWNERS docs at https://go.k8s.io/owners
approvers:
- sig-etcd-chairs # Defined in OWNERS_ALIASES
- sig-etcd-tech-leads # Defined in OWNERS_ALIASES
- spzala # Sahdev Zala
emeritus_approvers:
- bdarnell # Ben Darnell
- fanminshi # Fanmin Shi
- gyuho # Gyuho Lee
- hexfusion # Sam Batschelet
- heyitsanthony # Anthony Romano
- jingyih # Jingyi Hu
- jpbetz # Joe Betz
- mitake # Hitoshi Mitake
- philips # Brandon Philips
- ptabor # Piotr Tabor
- wenjiaswe # Wenjia Zhang
- xiang90 # Xiang Li
================================================
FILE: OWNERS_ALIASES
================================================
aliases:
sig-etcd-chairs:
- ivanvc # Ivan Valdes
- jmhbnz # James Blair
- siyuanfoundation # Siyuan Zhang
sig-etcd-tech-leads:
- ahrtr # Benjamin Wang
- fuweid # Wei Fu
- serathius # Marek Siarkowicz
================================================
FILE: Procfile
================================================
# Use goreman to run `go install github.com/mattn/goreman@latest`
# Change the path of bin/etcd if etcd is located elsewhere
etcd1: bin/etcd --name infra1 --listen-client-urls http://127.0.0.1:2379 --advertise-client-urls http://127.0.0.1:2379 --listen-peer-urls http://127.0.0.1:12380 --initial-advertise-peer-urls http://127.0.0.1:12380 --initial-cluster-token etcd-cluster-1 --initial-cluster 'infra1=http://127.0.0.1:12380,infra2=http://127.0.0.1:22380,infra3=http://127.0.0.1:32380' --initial-cluster-state new --enable-pprof --logger=zap --log-outputs=stderr
etcd2: bin/etcd --name infra2 --listen-client-urls http://127.0.0.1:22379 --advertise-client-urls http://127.0.0.1:22379 --listen-peer-urls http://127.0.0.1:22380 --initial-advertise-peer-urls http://127.0.0.1:22380 --initial-cluster-token etcd-cluster-1 --initial-cluster 'infra1=http://127.0.0.1:12380,infra2=http://127.0.0.1:22380,infra3=http://127.0.0.1:32380' --initial-cluster-state new --enable-pprof --logger=zap --log-outputs=stderr
etcd3: bin/etcd --name infra3 --listen-client-urls http://127.0.0.1:32379 --advertise-client-urls http://127.0.0.1:32379 --listen-peer-urls http://127.0.0.1:32380 --initial-advertise-peer-urls http://127.0.0.1:32380 --initial-cluster-token etcd-cluster-1 --initial-cluster 'infra1=http://127.0.0.1:12380,infra2=http://127.0.0.1:22380,infra3=http://127.0.0.1:32380' --initial-cluster-state new --enable-pprof --logger=zap --log-outputs=stderr
#proxy: bin/etcd grpc-proxy start --endpoints=127.0.0.1:2379,127.0.0.1:22379,127.0.0.1:32379 --listen-addr=127.0.0.1:23790 --advertise-client-url=127.0.0.1:23790 --enable-pprof
# A learner node can be started using the below Procfile.learner (uncomment and run)
# Use goreman to run `go install github.com/mattn/goreman@latest`
# 1. Start the cluster using Procfile
# 2. Add learner node to the cluster
# % etcdctl member add infra4 --peer-urls="http://127.0.0.1:42380" --learner=true
# 3. Start learner node with goreman
# Change the path of bin/etcd if etcd is located elsewhere
# uncomment below to setup
# etcd4: bin/etcd --name infra4 --listen-client-urls http://127.0.0.1:42379 --advertise-client-urls http://127.0.0.1:42379 --listen-peer-urls http://127.0.0.1:42380 --initial-advertise-peer-urls http://127.0.0.1:42380 --initial-cluster-token etcd-cluster-1 --initial-cluster 'infra4=http://127.0.0.1:42380,infra1=http://127.0.0.1:12380,infra2=http://127.0.0.1:22380,infra3=http://127.0.0.1:32380' --initial-cluster-state existing --enable-pprof --logger=zap --log-outputs=stderr
# 4. The learner node can be promoted to voting member by the command
# % etcdctl member promote
================================================
FILE: README.md
================================================
# etcd
[](https://goreportcard.com/report/github.com/etcd-io/etcd)
[](https://app.codecov.io/gh/etcd-io/etcd/tree/main)
[](https://github.com/etcd-io/etcd/actions/workflows/tests.yaml)
[](https://github.com/etcd-io/etcd/actions/workflows/codeql-analysis.yml)
[](https://etcd.io/docs)
[](https://godocs.io/go.etcd.io/etcd/v3)
[](https://github.com/etcd-io/etcd/releases)
[](https://github.com/etcd-io/etcd/blob/main/LICENSE)
[](https://scorecard.dev/viewer/?uri=github.com/etcd-io/etcd)
**Note**: The `main` branch may be in an *unstable or even broken state* during development. For stable versions, see [releases][github-release].
etcd is a distributed reliable key-value store for the most critical data of a distributed system, with a focus on being:
* *Simple*: well-defined, user-facing API (gRPC)
* *Secure*: automatic TLS with optional client cert authentication
* *Fast*: benchmarked 10,000 writes/sec
* *Reliable*: properly distributed using Raft
etcd is written in Go and uses the [Raft][] consensus algorithm to manage a highly-available replicated log.
etcd is used [in production by many companies](./ADOPTERS.md), and the development team stands behind it in critical deployment scenarios, where etcd is frequently teamed with applications such as [Kubernetes][k8s], [locksmith][], [vulcand][], [Doorman][], and many others. Reliability is further ensured by rigorous [**robustness testing**](https://github.com/etcd-io/etcd/tree/main/tests/robustness).
See [etcdctl][etcdctl] for a simple command line client.

Original image credited to xkcd.com/2347, alterations by Josh Berkus.
[raft]: https://raft.github.io/
[k8s]: http://kubernetes.io/
[doorman]: https://github.com/youtube/doorman
[locksmith]: https://github.com/coreos/locksmith
[vulcand]: https://github.com/vulcand/vulcand
[etcdctl]: https://github.com/etcd-io/etcd/tree/main/etcdctl
## Documentation
The most common API documentation you'll need can be found here:
* [go.etcd.io/etcd/api/v3](https://godocs.io/go.etcd.io/etcd/api/v3)
* [go.etcd.io/etcd/client/pkg/v3](https://godocs.io/go.etcd.io/etcd/client/pkg/v3)
* [go.etcd.io/etcd/client/v3](https://godocs.io/go.etcd.io/etcd/client/v3)
* [go.etcd.io/etcd/etcdctl/v3](https://godocs.io/go.etcd.io/etcd/etcdctl/v3)
* [go.etcd.io/etcd/pkg/v3](https://godocs.io/go.etcd.io/etcd/pkg/v3)
* [go.etcd.io/etcd/raft/v3](https://godocs.io/go.etcd.io/etcd/raft/v3)
* [go.etcd.io/etcd/server/v3](https://godocs.io/go.etcd.io/etcd/server/v3)
## Maintainers
[Maintainers](OWNERS) strive to shape an inclusive open source project culture where users are heard and contributors feel respected and empowered. Maintainers aim to build productive relationships across different companies and disciplines. Read more about [Maintainers role and responsibilities](Documentation/contributor-guide/community-membership.md#maintainers).
## Getting started
### Getting etcd
The easiest way to get etcd is to use one of the pre-built release binaries which are available for OSX, Linux, Windows, and Docker on the [release page][github-release].
For more installation guides, please check out [play.etcd.io](http://play.etcd.io) and [operating etcd](https://etcd.io/docs/latest/op-guide).
[github-release]: https://github.com/etcd-io/etcd/releases
### Running etcd
First start a single-member cluster of etcd.
If etcd is installed using the [pre-built release binaries][github-release], run it from the installation location as below:
```bash
/tmp/etcd-download-test/etcd
```
The etcd command can be simply run as such if it is moved to the system path as below:
```bash
mv /tmp/etcd-download-test/etcd /usr/local/bin/
etcd
```
This will bring up etcd listening on port 2379 for client communication and on port 2380 for server-to-server communication.
Next, let's set a single key, and then retrieve it:
```bash
etcdctl put mykey "this is awesome"
etcdctl get mykey
```
etcd is now running and serving client requests. For more, please check out:
* [Interactive etcd playground](http://play.etcd.io)
* [Animated quick demo](https://etcd.io/docs/latest/demo)
### etcd TCP ports
The [official etcd ports][iana-ports] are 2379 for client requests, and 2380 for peer communication.
[iana-ports]: http://www.iana.org/assignments/service-names-port-numbers/service-names-port-numbers.txt
### Running a local etcd cluster
First install [goreman](https://github.com/mattn/goreman), which manages Procfile-based applications.
Our [Procfile script](./Procfile) will set up a local example cluster. Start it with:
```bash
goreman start
```
This will bring up 3 etcd members `infra1`, `infra2` and `infra3` and optionally etcd `grpc-proxy`, which runs locally and composes a cluster.
Every cluster member and proxy accepts key value reads and key value writes.
Follow the comments in [Procfile script](./Procfile) to add a learner node to the cluster.
### Install etcd client v3
```bash
go get go.etcd.io/etcd/client/v3
```
### Next steps
Now it's time to dig into the full etcd API and other guides.
* Read the full [documentation].
* Review etcd [frequently asked questions].
* Explore the full gRPC [API].
* Set up a [multi-machine cluster][clustering].
* Learn the [config format, env variables and flags][configuration].
* Find [language bindings and tools][integrations].
* Use TLS to [secure an etcd cluster][security].
* [Tune etcd][tuning].
[documentation]: https://etcd.io/docs/latest
[api]: https://etcd.io/docs/latest/learning/api
[clustering]: https://etcd.io/docs/latest/op-guide/clustering
[configuration]: https://etcd.io/docs/latest/op-guide/configuration
[integrations]: https://etcd.io/docs/latest/integrations
[security]: https://etcd.io/docs/latest/op-guide/security
[tuning]: https://etcd.io/docs/latest/tuning
## Contact
* Email: [etcd-dev](https://groups.google.com/g/etcd-dev)
* Slack: [#sig-etcd](https://kubernetes.slack.com/archives/C3HD8ARJ5) channel on Kubernetes ([get an invite](http://slack.kubernetes.io/))
* [Community meetings](#community-meetings)
### Community meetings
etcd contributors and maintainers meet every week at `11:00` AM (USA Pacific) on Thursday and meetings alternate between community meetings and issue triage meetings. Meeting agendas are recorded in a [shared Google doc][shared-meeting-notes] and everyone is welcome to suggest additional topics or other agendas.
Issue triage meetings are aimed at getting through our backlog of PRs and Issues. Triage meetings are open to any contributor; you don't have to be a reviewer or approver to help out! They can also be a good way to get started contributing.
The meeting lead role is rotated for each meeting between etcd maintainers or sig-etcd leads and is recorded in a [shared Google sheet][shared-rotation-sheet].
Meeting recordings are uploaded to the official etcd [YouTube channel].
Get calendar invitations by joining [etcd-dev](https://groups.google.com/g/etcd-dev) mailing group.
Join the CNCF-funded Zoom channel: [zoom.us/my/cncfetcdproject](https://zoom.us/my/cncfetcdproject)
[shared-meeting-notes]: https://docs.google.com/document/d/16XEGyPBisZvmmoIHSZzv__LoyOeluC5a4x353CX0SIM/edit
[shared-rotation-sheet]: https://docs.google.com/spreadsheets/d/1jodHIO7Dk2VWTs1IRnfMFaRktS9IH8XRyifOnPdSY8I/edit
[YouTube channel]: https://www.youtube.com/@etcdio
## Contributing
See [CONTRIBUTING](CONTRIBUTING.md) for details on setting up your development environment, submitting patches and the contribution workflow.
Please refer to [community-membership.md](Documentation/contributor-guide/community-membership.md#member) for information on becoming an etcd project member. We welcome and look forward to your contributions to the project!
Please also refer to [roadmap](Documentation/contributor-guide/roadmap.md) to get more details on the priorities for the next few major or minor releases.
## Reporting bugs
See [reporting bugs](https://github.com/etcd-io/etcd/blob/main/Documentation/contributor-guide/reporting_bugs.md) for details about reporting any issues. Before opening an issue please check it is not covered in our [frequently asked questions].
[frequently asked questions]: https://etcd.io/docs/latest/faq
## Reporting a security vulnerability
See [security disclosure and release process](security/README.md) for details on how to report a security vulnerability and how the etcd team manages it.
## Issue and PR management
See [issue triage guidelines](https://github.com/etcd-io/etcd/blob/main/Documentation/contributor-guide/triage_issues.md) for details on how issues are managed.
See [PR management](https://github.com/etcd-io/etcd/blob/main/Documentation/contributor-guide/triage_prs.md) for guidelines on how pull requests are managed.
## etcd Emeritus Maintainers
etcd [emeritus maintainers](OWNERS) dedicated a part of their career to etcd and reviewed code, triaged bugs and pushed the project forward over a substantial period of time. Their contribution is greatly appreciated.
### License
etcd is under the Apache 2.0 license. See the [LICENSE](LICENSE) file for details.
================================================
FILE: api/.gomodguard.yaml
================================================
---
blocked:
modules:
- go.etcd.io/etcd:
reason: "Forbidden dependency"
- go.etcd.io/etcd/api/v3:
reason: "Forbidden dependency"
- go.etcd.io/etcd/pkg/v3:
reason: "Forbidden dependency"
- go.etcd.io/etcd/server/v3:
reason: "Forbidden dependency"
- go.etcd.io/etcd/tests/v3:
reason: "Forbidden dependency"
- go.etcd.io/etcd/v3:
reason: "Forbidden dependency"
================================================
FILE: api/LICENSE
================================================
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright 2020 The etcd Authors
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.
================================================
FILE: api/authpb/auth.pb.go
================================================
// Code generated by protoc-gen-gogo. DO NOT EDIT.
// source: auth.proto
package authpb
import (
fmt "fmt"
io "io"
math "math"
math_bits "math/bits"
proto "github.com/golang/protobuf/proto"
)
// Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal
var _ = fmt.Errorf
var _ = math.Inf
// This is a compile-time assertion to ensure that this generated file
// is compatible with the proto package it is being compiled against.
// A compilation error at this line likely means your copy of the
// proto package needs to be updated.
const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
type Permission_Type int32
const (
Permission_READ Permission_Type = 0
Permission_WRITE Permission_Type = 1
Permission_READWRITE Permission_Type = 2
)
var Permission_Type_name = map[int32]string{
0: "READ",
1: "WRITE",
2: "READWRITE",
}
var Permission_Type_value = map[string]int32{
"READ": 0,
"WRITE": 1,
"READWRITE": 2,
}
func (x Permission_Type) String() string {
return proto.EnumName(Permission_Type_name, int32(x))
}
func (Permission_Type) EnumDescriptor() ([]byte, []int) {
return fileDescriptor_8bbd6f3875b0e874, []int{2, 0}
}
type UserAddOptions struct {
NoPassword bool `protobuf:"varint,1,opt,name=no_password,json=noPassword,proto3" json:"no_password,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *UserAddOptions) Reset() { *m = UserAddOptions{} }
func (m *UserAddOptions) String() string { return proto.CompactTextString(m) }
func (*UserAddOptions) ProtoMessage() {}
func (*UserAddOptions) Descriptor() ([]byte, []int) {
return fileDescriptor_8bbd6f3875b0e874, []int{0}
}
func (m *UserAddOptions) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
}
func (m *UserAddOptions) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
if deterministic {
return xxx_messageInfo_UserAddOptions.Marshal(b, m, deterministic)
} else {
b = b[:cap(b)]
n, err := m.MarshalToSizedBuffer(b)
if err != nil {
return nil, err
}
return b[:n], nil
}
}
func (m *UserAddOptions) XXX_Merge(src proto.Message) {
xxx_messageInfo_UserAddOptions.Merge(m, src)
}
func (m *UserAddOptions) XXX_Size() int {
return m.Size()
}
func (m *UserAddOptions) XXX_DiscardUnknown() {
xxx_messageInfo_UserAddOptions.DiscardUnknown(m)
}
var xxx_messageInfo_UserAddOptions proto.InternalMessageInfo
func (m *UserAddOptions) GetNoPassword() bool {
if m != nil {
return m.NoPassword
}
return false
}
// User is a single entry in the bucket authUsers
type User struct {
Name []byte `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
Password []byte `protobuf:"bytes,2,opt,name=password,proto3" json:"password,omitempty"`
Roles []string `protobuf:"bytes,3,rep,name=roles,proto3" json:"roles,omitempty"`
Options *UserAddOptions `protobuf:"bytes,4,opt,name=options,proto3" json:"options,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *User) Reset() { *m = User{} }
func (m *User) String() string { return proto.CompactTextString(m) }
func (*User) ProtoMessage() {}
func (*User) Descriptor() ([]byte, []int) {
return fileDescriptor_8bbd6f3875b0e874, []int{1}
}
func (m *User) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
}
func (m *User) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
if deterministic {
return xxx_messageInfo_User.Marshal(b, m, deterministic)
} else {
b = b[:cap(b)]
n, err := m.MarshalToSizedBuffer(b)
if err != nil {
return nil, err
}
return b[:n], nil
}
}
func (m *User) XXX_Merge(src proto.Message) {
xxx_messageInfo_User.Merge(m, src)
}
func (m *User) XXX_Size() int {
return m.Size()
}
func (m *User) XXX_DiscardUnknown() {
xxx_messageInfo_User.DiscardUnknown(m)
}
var xxx_messageInfo_User proto.InternalMessageInfo
func (m *User) GetName() []byte {
if m != nil {
return m.Name
}
return nil
}
func (m *User) GetPassword() []byte {
if m != nil {
return m.Password
}
return nil
}
func (m *User) GetRoles() []string {
if m != nil {
return m.Roles
}
return nil
}
func (m *User) GetOptions() *UserAddOptions {
if m != nil {
return m.Options
}
return nil
}
// Permission is a single entity
type Permission struct {
PermType Permission_Type `protobuf:"varint,1,opt,name=permType,proto3,enum=authpb.Permission_Type" json:"permType,omitempty"`
Key []byte `protobuf:"bytes,2,opt,name=key,proto3" json:"key,omitempty"`
RangeEnd []byte `protobuf:"bytes,3,opt,name=range_end,json=rangeEnd,proto3" json:"range_end,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *Permission) Reset() { *m = Permission{} }
func (m *Permission) String() string { return proto.CompactTextString(m) }
func (*Permission) ProtoMessage() {}
func (*Permission) Descriptor() ([]byte, []int) {
return fileDescriptor_8bbd6f3875b0e874, []int{2}
}
func (m *Permission) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
}
func (m *Permission) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
if deterministic {
return xxx_messageInfo_Permission.Marshal(b, m, deterministic)
} else {
b = b[:cap(b)]
n, err := m.MarshalToSizedBuffer(b)
if err != nil {
return nil, err
}
return b[:n], nil
}
}
func (m *Permission) XXX_Merge(src proto.Message) {
xxx_messageInfo_Permission.Merge(m, src)
}
func (m *Permission) XXX_Size() int {
return m.Size()
}
func (m *Permission) XXX_DiscardUnknown() {
xxx_messageInfo_Permission.DiscardUnknown(m)
}
var xxx_messageInfo_Permission proto.InternalMessageInfo
func (m *Permission) GetPermType() Permission_Type {
if m != nil {
return m.PermType
}
return Permission_READ
}
func (m *Permission) GetKey() []byte {
if m != nil {
return m.Key
}
return nil
}
func (m *Permission) GetRangeEnd() []byte {
if m != nil {
return m.RangeEnd
}
return nil
}
// Role is a single entry in the bucket authRoles
type Role struct {
Name []byte `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
KeyPermission []*Permission `protobuf:"bytes,2,rep,name=keyPermission,proto3" json:"keyPermission,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *Role) Reset() { *m = Role{} }
func (m *Role) String() string { return proto.CompactTextString(m) }
func (*Role) ProtoMessage() {}
func (*Role) Descriptor() ([]byte, []int) {
return fileDescriptor_8bbd6f3875b0e874, []int{3}
}
func (m *Role) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
}
func (m *Role) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
if deterministic {
return xxx_messageInfo_Role.Marshal(b, m, deterministic)
} else {
b = b[:cap(b)]
n, err := m.MarshalToSizedBuffer(b)
if err != nil {
return nil, err
}
return b[:n], nil
}
}
func (m *Role) XXX_Merge(src proto.Message) {
xxx_messageInfo_Role.Merge(m, src)
}
func (m *Role) XXX_Size() int {
return m.Size()
}
func (m *Role) XXX_DiscardUnknown() {
xxx_messageInfo_Role.DiscardUnknown(m)
}
var xxx_messageInfo_Role proto.InternalMessageInfo
func (m *Role) GetName() []byte {
if m != nil {
return m.Name
}
return nil
}
func (m *Role) GetKeyPermission() []*Permission {
if m != nil {
return m.KeyPermission
}
return nil
}
func init() {
proto.RegisterEnum("authpb.Permission_Type", Permission_Type_name, Permission_Type_value)
proto.RegisterType((*UserAddOptions)(nil), "authpb.UserAddOptions")
proto.RegisterType((*User)(nil), "authpb.User")
proto.RegisterType((*Permission)(nil), "authpb.Permission")
proto.RegisterType((*Role)(nil), "authpb.Role")
}
func init() { proto.RegisterFile("auth.proto", fileDescriptor_8bbd6f3875b0e874) }
var fileDescriptor_8bbd6f3875b0e874 = []byte{
// 342 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x6c, 0x91, 0xcf, 0x4e, 0xf2, 0x40,
0x14, 0xc5, 0x19, 0x5a, 0xf8, 0xda, 0xcb, 0x07, 0x21, 0x37, 0x46, 0x1b, 0x8d, 0xb5, 0xe9, 0xaa,
0x71, 0xd1, 0x2a, 0x6c, 0xdc, 0x62, 0x64, 0xe1, 0x4a, 0x32, 0xc1, 0x98, 0xb8, 0x21, 0xc5, 0x4e,
0xb0, 0x01, 0x66, 0x9a, 0x99, 0xaa, 0x61, 0xe3, 0x73, 0xb8, 0xf0, 0x81, 0x5c, 0xfa, 0x08, 0x06,
0x5f, 0xc4, 0xb4, 0xc3, 0x9f, 0x10, 0x5d, 0xf5, 0x9e, 0xd3, 0x73, 0xee, 0xfc, 0x32, 0x03, 0x10,
0x3f, 0xe5, 0x8f, 0x61, 0x26, 0x45, 0x2e, 0xb0, 0x5e, 0xcc, 0xd9, 0xd8, 0x3f, 0x87, 0xd6, 0xad,
0x62, 0xb2, 0x97, 0x24, 0x37, 0x59, 0x9e, 0x0a, 0xae, 0xf0, 0x04, 0x1a, 0x5c, 0x8c, 0xb2, 0x58,
0xa9, 0x17, 0x21, 0x13, 0x87, 0x78, 0x24, 0xb0, 0x28, 0x70, 0x31, 0x58, 0x39, 0xfe, 0x2b, 0x98,
0x45, 0x05, 0x11, 0x4c, 0x1e, 0xcf, 0x59, 0x99, 0xf8, 0x4f, 0xcb, 0x19, 0x0f, 0xc1, 0xda, 0x34,
0xab, 0xa5, 0xbf, 0xd1, 0xb8, 0x07, 0x35, 0x29, 0x66, 0x4c, 0x39, 0x86, 0x67, 0x04, 0x36, 0xd5,
0x02, 0xcf, 0xe0, 0x9f, 0xd0, 0x27, 0x3b, 0xa6, 0x47, 0x82, 0x46, 0x67, 0x3f, 0xd4, 0x68, 0xe1,
0x2e, 0x17, 0x5d, 0xc7, 0xfc, 0x77, 0x02, 0x30, 0x60, 0x72, 0x9e, 0x2a, 0x95, 0x0a, 0x8e, 0x5d,
0xb0, 0x32, 0x26, 0xe7, 0xc3, 0x45, 0xa6, 0x51, 0x5a, 0x9d, 0x83, 0xf5, 0x86, 0x6d, 0x2a, 0x2c,
0x7e, 0xd3, 0x4d, 0x10, 0xdb, 0x60, 0x4c, 0xd9, 0x62, 0x85, 0x58, 0x8c, 0x78, 0x04, 0xb6, 0x8c,
0xf9, 0x84, 0x8d, 0x18, 0x4f, 0x1c, 0x43, 0xa3, 0x97, 0x46, 0x9f, 0x27, 0xfe, 0x29, 0x98, 0x65,
0xcd, 0x02, 0x93, 0xf6, 0x7b, 0x57, 0xed, 0x0a, 0xda, 0x50, 0xbb, 0xa3, 0xd7, 0xc3, 0x7e, 0x9b,
0x60, 0x13, 0xec, 0xc2, 0xd4, 0xb2, 0xea, 0x0f, 0xc1, 0xa4, 0x62, 0xc6, 0xfe, 0xbc, 0x9e, 0x0b,
0x68, 0x4e, 0xd9, 0x62, 0x8b, 0xe5, 0x54, 0x3d, 0x23, 0x68, 0x74, 0xf0, 0x37, 0x30, 0xdd, 0x0d,
0x5e, 0x46, 0x1f, 0x4b, 0x97, 0x7c, 0x2e, 0x5d, 0xf2, 0xb5, 0x74, 0xc9, 0xdb, 0xb7, 0x5b, 0xb9,
0x3f, 0x9e, 0x88, 0x90, 0xe5, 0x0f, 0x49, 0x98, 0x8a, 0xa8, 0xf8, 0x46, 0x71, 0x96, 0x46, 0xcf,
0xdd, 0x48, 0xaf, 0x1a, 0xd7, 0xcb, 0x77, 0xee, 0xfe, 0x04, 0x00, 0x00, 0xff, 0xff, 0xc0, 0x1b,
0x2e, 0xdd, 0xf5, 0x01, 0x00, 0x00,
}
func (m *UserAddOptions) Marshal() (dAtA []byte, err error) {
size := m.Size()
dAtA = make([]byte, size)
n, err := m.MarshalToSizedBuffer(dAtA[:size])
if err != nil {
return nil, err
}
return dAtA[:n], nil
}
func (m *UserAddOptions) MarshalTo(dAtA []byte) (int, error) {
size := m.Size()
return m.MarshalToSizedBuffer(dAtA[:size])
}
func (m *UserAddOptions) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA)
_ = i
var l int
_ = l
if m.XXX_unrecognized != nil {
i -= len(m.XXX_unrecognized)
copy(dAtA[i:], m.XXX_unrecognized)
}
if m.NoPassword {
i--
if m.NoPassword {
dAtA[i] = 1
} else {
dAtA[i] = 0
}
i--
dAtA[i] = 0x8
}
return len(dAtA) - i, nil
}
func (m *User) Marshal() (dAtA []byte, err error) {
size := m.Size()
dAtA = make([]byte, size)
n, err := m.MarshalToSizedBuffer(dAtA[:size])
if err != nil {
return nil, err
}
return dAtA[:n], nil
}
func (m *User) MarshalTo(dAtA []byte) (int, error) {
size := m.Size()
return m.MarshalToSizedBuffer(dAtA[:size])
}
func (m *User) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA)
_ = i
var l int
_ = l
if m.XXX_unrecognized != nil {
i -= len(m.XXX_unrecognized)
copy(dAtA[i:], m.XXX_unrecognized)
}
if m.Options != nil {
{
size, err := m.Options.MarshalToSizedBuffer(dAtA[:i])
if err != nil {
return 0, err
}
i -= size
i = encodeVarintAuth(dAtA, i, uint64(size))
}
i--
dAtA[i] = 0x22
}
if len(m.Roles) > 0 {
for iNdEx := len(m.Roles) - 1; iNdEx >= 0; iNdEx-- {
i -= len(m.Roles[iNdEx])
copy(dAtA[i:], m.Roles[iNdEx])
i = encodeVarintAuth(dAtA, i, uint64(len(m.Roles[iNdEx])))
i--
dAtA[i] = 0x1a
}
}
if len(m.Password) > 0 {
i -= len(m.Password)
copy(dAtA[i:], m.Password)
i = encodeVarintAuth(dAtA, i, uint64(len(m.Password)))
i--
dAtA[i] = 0x12
}
if len(m.Name) > 0 {
i -= len(m.Name)
copy(dAtA[i:], m.Name)
i = encodeVarintAuth(dAtA, i, uint64(len(m.Name)))
i--
dAtA[i] = 0xa
}
return len(dAtA) - i, nil
}
func (m *Permission) Marshal() (dAtA []byte, err error) {
size := m.Size()
dAtA = make([]byte, size)
n, err := m.MarshalToSizedBuffer(dAtA[:size])
if err != nil {
return nil, err
}
return dAtA[:n], nil
}
func (m *Permission) MarshalTo(dAtA []byte) (int, error) {
size := m.Size()
return m.MarshalToSizedBuffer(dAtA[:size])
}
func (m *Permission) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA)
_ = i
var l int
_ = l
if m.XXX_unrecognized != nil {
i -= len(m.XXX_unrecognized)
copy(dAtA[i:], m.XXX_unrecognized)
}
if len(m.RangeEnd) > 0 {
i -= len(m.RangeEnd)
copy(dAtA[i:], m.RangeEnd)
i = encodeVarintAuth(dAtA, i, uint64(len(m.RangeEnd)))
i--
dAtA[i] = 0x1a
}
if len(m.Key) > 0 {
i -= len(m.Key)
copy(dAtA[i:], m.Key)
i = encodeVarintAuth(dAtA, i, uint64(len(m.Key)))
i--
dAtA[i] = 0x12
}
if m.PermType != 0 {
i = encodeVarintAuth(dAtA, i, uint64(m.PermType))
i--
dAtA[i] = 0x8
}
return len(dAtA) - i, nil
}
func (m *Role) Marshal() (dAtA []byte, err error) {
size := m.Size()
dAtA = make([]byte, size)
n, err := m.MarshalToSizedBuffer(dAtA[:size])
if err != nil {
return nil, err
}
return dAtA[:n], nil
}
func (m *Role) MarshalTo(dAtA []byte) (int, error) {
size := m.Size()
return m.MarshalToSizedBuffer(dAtA[:size])
}
func (m *Role) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA)
_ = i
var l int
_ = l
if m.XXX_unrecognized != nil {
i -= len(m.XXX_unrecognized)
copy(dAtA[i:], m.XXX_unrecognized)
}
if len(m.KeyPermission) > 0 {
for iNdEx := len(m.KeyPermission) - 1; iNdEx >= 0; iNdEx-- {
{
size, err := m.KeyPermission[iNdEx].MarshalToSizedBuffer(dAtA[:i])
if err != nil {
return 0, err
}
i -= size
i = encodeVarintAuth(dAtA, i, uint64(size))
}
i--
dAtA[i] = 0x12
}
}
if len(m.Name) > 0 {
i -= len(m.Name)
copy(dAtA[i:], m.Name)
i = encodeVarintAuth(dAtA, i, uint64(len(m.Name)))
i--
dAtA[i] = 0xa
}
return len(dAtA) - i, nil
}
func encodeVarintAuth(dAtA []byte, offset int, v uint64) int {
offset -= sovAuth(v)
base := offset
for v >= 1<<7 {
dAtA[offset] = uint8(v&0x7f | 0x80)
v >>= 7
offset++
}
dAtA[offset] = uint8(v)
return base
}
func (m *UserAddOptions) Size() (n int) {
if m == nil {
return 0
}
var l int
_ = l
if m.NoPassword {
n += 2
}
if m.XXX_unrecognized != nil {
n += len(m.XXX_unrecognized)
}
return n
}
func (m *User) Size() (n int) {
if m == nil {
return 0
}
var l int
_ = l
l = len(m.Name)
if l > 0 {
n += 1 + l + sovAuth(uint64(l))
}
l = len(m.Password)
if l > 0 {
n += 1 + l + sovAuth(uint64(l))
}
if len(m.Roles) > 0 {
for _, s := range m.Roles {
l = len(s)
n += 1 + l + sovAuth(uint64(l))
}
}
if m.Options != nil {
l = m.Options.Size()
n += 1 + l + sovAuth(uint64(l))
}
if m.XXX_unrecognized != nil {
n += len(m.XXX_unrecognized)
}
return n
}
func (m *Permission) Size() (n int) {
if m == nil {
return 0
}
var l int
_ = l
if m.PermType != 0 {
n += 1 + sovAuth(uint64(m.PermType))
}
l = len(m.Key)
if l > 0 {
n += 1 + l + sovAuth(uint64(l))
}
l = len(m.RangeEnd)
if l > 0 {
n += 1 + l + sovAuth(uint64(l))
}
if m.XXX_unrecognized != nil {
n += len(m.XXX_unrecognized)
}
return n
}
func (m *Role) Size() (n int) {
if m == nil {
return 0
}
var l int
_ = l
l = len(m.Name)
if l > 0 {
n += 1 + l + sovAuth(uint64(l))
}
if len(m.KeyPermission) > 0 {
for _, e := range m.KeyPermission {
l = e.Size()
n += 1 + l + sovAuth(uint64(l))
}
}
if m.XXX_unrecognized != nil {
n += len(m.XXX_unrecognized)
}
return n
}
func sovAuth(x uint64) (n int) {
return (math_bits.Len64(x|1) + 6) / 7
}
func sozAuth(x uint64) (n int) {
return sovAuth(uint64((x << 1) ^ uint64((int64(x) >> 63))))
}
func (m *UserAddOptions) Unmarshal(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0
for iNdEx < l {
preIndex := iNdEx
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowAuth
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
fieldNum := int32(wire >> 3)
wireType := int(wire & 0x7)
if wireType == 4 {
return fmt.Errorf("proto: UserAddOptions: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: UserAddOptions: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
case 1:
if wireType != 0 {
return fmt.Errorf("proto: wrong wireType = %d for field NoPassword", wireType)
}
var v int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowAuth
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
v |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
m.NoPassword = bool(v != 0)
default:
iNdEx = preIndex
skippy, err := skipAuth(dAtA[iNdEx:])
if err != nil {
return err
}
if (skippy < 0) || (iNdEx+skippy) < 0 {
return ErrInvalidLengthAuth
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
iNdEx += skippy
}
}
if iNdEx > l {
return io.ErrUnexpectedEOF
}
return nil
}
func (m *User) Unmarshal(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0
for iNdEx < l {
preIndex := iNdEx
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowAuth
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
fieldNum := int32(wire >> 3)
wireType := int(wire & 0x7)
if wireType == 4 {
return fmt.Errorf("proto: User: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: User: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
case 1:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType)
}
var byteLen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowAuth
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
byteLen |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
if byteLen < 0 {
return ErrInvalidLengthAuth
}
postIndex := iNdEx + byteLen
if postIndex < 0 {
return ErrInvalidLengthAuth
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.Name = append(m.Name[:0], dAtA[iNdEx:postIndex]...)
if m.Name == nil {
m.Name = []byte{}
}
iNdEx = postIndex
case 2:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Password", wireType)
}
var byteLen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowAuth
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
byteLen |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
if byteLen < 0 {
return ErrInvalidLengthAuth
}
postIndex := iNdEx + byteLen
if postIndex < 0 {
return ErrInvalidLengthAuth
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.Password = append(m.Password[:0], dAtA[iNdEx:postIndex]...)
if m.Password == nil {
m.Password = []byte{}
}
iNdEx = postIndex
case 3:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Roles", wireType)
}
var stringLen uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowAuth
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
stringLen |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
intStringLen := int(stringLen)
if intStringLen < 0 {
return ErrInvalidLengthAuth
}
postIndex := iNdEx + intStringLen
if postIndex < 0 {
return ErrInvalidLengthAuth
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.Roles = append(m.Roles, string(dAtA[iNdEx:postIndex]))
iNdEx = postIndex
case 4:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Options", wireType)
}
var msglen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowAuth
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
msglen |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
if msglen < 0 {
return ErrInvalidLengthAuth
}
postIndex := iNdEx + msglen
if postIndex < 0 {
return ErrInvalidLengthAuth
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
if m.Options == nil {
m.Options = &UserAddOptions{}
}
if err := m.Options.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
return err
}
iNdEx = postIndex
default:
iNdEx = preIndex
skippy, err := skipAuth(dAtA[iNdEx:])
if err != nil {
return err
}
if (skippy < 0) || (iNdEx+skippy) < 0 {
return ErrInvalidLengthAuth
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
iNdEx += skippy
}
}
if iNdEx > l {
return io.ErrUnexpectedEOF
}
return nil
}
func (m *Permission) Unmarshal(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0
for iNdEx < l {
preIndex := iNdEx
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowAuth
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
fieldNum := int32(wire >> 3)
wireType := int(wire & 0x7)
if wireType == 4 {
return fmt.Errorf("proto: Permission: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: Permission: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
case 1:
if wireType != 0 {
return fmt.Errorf("proto: wrong wireType = %d for field PermType", wireType)
}
m.PermType = 0
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowAuth
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
m.PermType |= Permission_Type(b&0x7F) << shift
if b < 0x80 {
break
}
}
case 2:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Key", wireType)
}
var byteLen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowAuth
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
byteLen |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
if byteLen < 0 {
return ErrInvalidLengthAuth
}
postIndex := iNdEx + byteLen
if postIndex < 0 {
return ErrInvalidLengthAuth
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.Key = append(m.Key[:0], dAtA[iNdEx:postIndex]...)
if m.Key == nil {
m.Key = []byte{}
}
iNdEx = postIndex
case 3:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field RangeEnd", wireType)
}
var byteLen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowAuth
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
byteLen |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
if byteLen < 0 {
return ErrInvalidLengthAuth
}
postIndex := iNdEx + byteLen
if postIndex < 0 {
return ErrInvalidLengthAuth
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.RangeEnd = append(m.RangeEnd[:0], dAtA[iNdEx:postIndex]...)
if m.RangeEnd == nil {
m.RangeEnd = []byte{}
}
iNdEx = postIndex
default:
iNdEx = preIndex
skippy, err := skipAuth(dAtA[iNdEx:])
if err != nil {
return err
}
if (skippy < 0) || (iNdEx+skippy) < 0 {
return ErrInvalidLengthAuth
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
iNdEx += skippy
}
}
if iNdEx > l {
return io.ErrUnexpectedEOF
}
return nil
}
func (m *Role) Unmarshal(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0
for iNdEx < l {
preIndex := iNdEx
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowAuth
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
fieldNum := int32(wire >> 3)
wireType := int(wire & 0x7)
if wireType == 4 {
return fmt.Errorf("proto: Role: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: Role: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
case 1:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType)
}
var byteLen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowAuth
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
byteLen |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
if byteLen < 0 {
return ErrInvalidLengthAuth
}
postIndex := iNdEx + byteLen
if postIndex < 0 {
return ErrInvalidLengthAuth
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.Name = append(m.Name[:0], dAtA[iNdEx:postIndex]...)
if m.Name == nil {
m.Name = []byte{}
}
iNdEx = postIndex
case 2:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field KeyPermission", wireType)
}
var msglen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowAuth
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
msglen |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
if msglen < 0 {
return ErrInvalidLengthAuth
}
postIndex := iNdEx + msglen
if postIndex < 0 {
return ErrInvalidLengthAuth
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.KeyPermission = append(m.KeyPermission, &Permission{})
if err := m.KeyPermission[len(m.KeyPermission)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
return err
}
iNdEx = postIndex
default:
iNdEx = preIndex
skippy, err := skipAuth(dAtA[iNdEx:])
if err != nil {
return err
}
if (skippy < 0) || (iNdEx+skippy) < 0 {
return ErrInvalidLengthAuth
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
iNdEx += skippy
}
}
if iNdEx > l {
return io.ErrUnexpectedEOF
}
return nil
}
func skipAuth(dAtA []byte) (n int, err error) {
l := len(dAtA)
iNdEx := 0
depth := 0
for iNdEx < l {
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return 0, ErrIntOverflowAuth
}
if iNdEx >= l {
return 0, io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= (uint64(b) & 0x7F) << shift
if b < 0x80 {
break
}
}
wireType := int(wire & 0x7)
switch wireType {
case 0:
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return 0, ErrIntOverflowAuth
}
if iNdEx >= l {
return 0, io.ErrUnexpectedEOF
}
iNdEx++
if dAtA[iNdEx-1] < 0x80 {
break
}
}
case 1:
iNdEx += 8
case 2:
var length int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return 0, ErrIntOverflowAuth
}
if iNdEx >= l {
return 0, io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
length |= (int(b) & 0x7F) << shift
if b < 0x80 {
break
}
}
if length < 0 {
return 0, ErrInvalidLengthAuth
}
iNdEx += length
case 3:
depth++
case 4:
if depth == 0 {
return 0, ErrUnexpectedEndOfGroupAuth
}
depth--
case 5:
iNdEx += 4
default:
return 0, fmt.Errorf("proto: illegal wireType %d", wireType)
}
if iNdEx < 0 {
return 0, ErrInvalidLengthAuth
}
if depth == 0 {
return iNdEx, nil
}
}
return 0, io.ErrUnexpectedEOF
}
var (
ErrInvalidLengthAuth = fmt.Errorf("proto: negative length found during unmarshaling")
ErrIntOverflowAuth = fmt.Errorf("proto: integer overflow")
ErrUnexpectedEndOfGroupAuth = fmt.Errorf("proto: unexpected end of group")
)
================================================
FILE: api/authpb/auth.proto
================================================
syntax = "proto3";
package authpb;
option go_package = "go.etcd.io/etcd/api/v3/authpb";
message UserAddOptions {
bool no_password = 1;
};
// User is a single entry in the bucket authUsers
message User {
bytes name = 1;
bytes password = 2;
repeated string roles = 3;
UserAddOptions options = 4;
}
// Permission is a single entity
message Permission {
enum Type {
READ = 0;
WRITE = 1;
READWRITE = 2;
}
Type permType = 1;
bytes key = 2;
bytes range_end = 3;
}
// Role is a single entry in the bucket authRoles
message Role {
bytes name = 1;
repeated Permission keyPermission = 2;
}
================================================
FILE: api/authpb/deprecated.go
================================================
// Copyright 2026 The etcd Authors
//
// 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 authpb
const (
// READ is an alias of Permission_READ
// Deprecated: use Permission_READ instead. Will be removed in v3.8.
READ = Permission_READ
// WRITE is an alias of Permission_WRITE
// Deprecated: use Permission_WRITE instead. Will be removed in v3.8.
WRITE = Permission_WRITE
// READWRITE is an alias of Permission_READWRITE
// Deprecated: use Permission_READWRITE instead. Will be removed in v3.8.
READWRITE = Permission_READWRITE
)
================================================
FILE: api/etcdserverpb/etcdserver.pb.go
================================================
// Code generated by protoc-gen-gogo. DO NOT EDIT.
// source: etcdserver.proto
package etcdserverpb
import (
fmt "fmt"
io "io"
math "math"
math_bits "math/bits"
proto "github.com/golang/protobuf/proto"
)
// Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal
var _ = fmt.Errorf
var _ = math.Inf
// This is a compile-time assertion to ensure that this generated file
// is compatible with the proto package it is being compiled against.
// A compilation error at this line likely means your copy of the
// proto package needs to be updated.
const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
type Metadata struct {
NodeID *uint64 `protobuf:"varint,1,opt,name=NodeID" json:"NodeID,omitempty"`
ClusterID *uint64 `protobuf:"varint,2,opt,name=ClusterID" json:"ClusterID,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *Metadata) Reset() { *m = Metadata{} }
func (m *Metadata) String() string { return proto.CompactTextString(m) }
func (*Metadata) ProtoMessage() {}
func (*Metadata) Descriptor() ([]byte, []int) {
return fileDescriptor_09ffbeb3bebbce7e, []int{0}
}
func (m *Metadata) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
}
func (m *Metadata) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
if deterministic {
return xxx_messageInfo_Metadata.Marshal(b, m, deterministic)
} else {
b = b[:cap(b)]
n, err := m.MarshalToSizedBuffer(b)
if err != nil {
return nil, err
}
return b[:n], nil
}
}
func (m *Metadata) XXX_Merge(src proto.Message) {
xxx_messageInfo_Metadata.Merge(m, src)
}
func (m *Metadata) XXX_Size() int {
return m.Size()
}
func (m *Metadata) XXX_DiscardUnknown() {
xxx_messageInfo_Metadata.DiscardUnknown(m)
}
var xxx_messageInfo_Metadata proto.InternalMessageInfo
func (m *Metadata) GetNodeID() uint64 {
if m != nil && m.NodeID != nil {
return *m.NodeID
}
return 0
}
func (m *Metadata) GetClusterID() uint64 {
if m != nil && m.ClusterID != nil {
return *m.ClusterID
}
return 0
}
func init() {
proto.RegisterType((*Metadata)(nil), "etcdserverpb.Metadata")
}
func init() { proto.RegisterFile("etcdserver.proto", fileDescriptor_09ffbeb3bebbce7e) }
var fileDescriptor_09ffbeb3bebbce7e = []byte{
// 139 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x12, 0x48, 0x2d, 0x49, 0x4e,
0x29, 0x4e, 0x2d, 0x2a, 0x4b, 0x2d, 0xd2, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0xe2, 0x41, 0x88,
0x14, 0x24, 0x29, 0x39, 0x70, 0x71, 0xf8, 0xa6, 0x96, 0x24, 0xa6, 0x24, 0x96, 0x24, 0x0a, 0x89,
0x71, 0xb1, 0xf9, 0xe5, 0xa7, 0xa4, 0x7a, 0xba, 0x48, 0x30, 0x2a, 0x30, 0x6a, 0xb0, 0x04, 0x41,
0x79, 0x42, 0x32, 0x5c, 0x9c, 0xce, 0x39, 0xa5, 0xc5, 0x25, 0xa9, 0x45, 0x9e, 0x2e, 0x12, 0x4c,
0x60, 0x29, 0x84, 0x80, 0x93, 0xe9, 0x89, 0x47, 0x72, 0x8c, 0x17, 0x1e, 0xc9, 0x31, 0x3e, 0x78,
0x24, 0xc7, 0x38, 0xe3, 0xb1, 0x1c, 0x43, 0x94, 0x72, 0x7a, 0xbe, 0x1e, 0xc8, 0x12, 0xbd, 0xcc,
0x7c, 0x7d, 0x10, 0xad, 0x9f, 0x58, 0x90, 0xa9, 0x5f, 0x66, 0xac, 0x8f, 0x6c, 0x31, 0x20, 0x00,
0x00, 0xff, 0xff, 0x5c, 0x60, 0x56, 0x96, 0x99, 0x00, 0x00, 0x00,
}
func (m *Metadata) Marshal() (dAtA []byte, err error) {
size := m.Size()
dAtA = make([]byte, size)
n, err := m.MarshalToSizedBuffer(dAtA[:size])
if err != nil {
return nil, err
}
return dAtA[:n], nil
}
func (m *Metadata) MarshalTo(dAtA []byte) (int, error) {
size := m.Size()
return m.MarshalToSizedBuffer(dAtA[:size])
}
func (m *Metadata) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA)
_ = i
var l int
_ = l
if m.XXX_unrecognized != nil {
i -= len(m.XXX_unrecognized)
copy(dAtA[i:], m.XXX_unrecognized)
}
if m.ClusterID != nil {
i = encodeVarintEtcdserver(dAtA, i, uint64(*m.ClusterID))
i--
dAtA[i] = 0x10
}
if m.NodeID != nil {
i = encodeVarintEtcdserver(dAtA, i, uint64(*m.NodeID))
i--
dAtA[i] = 0x8
}
return len(dAtA) - i, nil
}
func encodeVarintEtcdserver(dAtA []byte, offset int, v uint64) int {
offset -= sovEtcdserver(v)
base := offset
for v >= 1<<7 {
dAtA[offset] = uint8(v&0x7f | 0x80)
v >>= 7
offset++
}
dAtA[offset] = uint8(v)
return base
}
func (m *Metadata) Size() (n int) {
if m == nil {
return 0
}
var l int
_ = l
if m.NodeID != nil {
n += 1 + sovEtcdserver(uint64(*m.NodeID))
}
if m.ClusterID != nil {
n += 1 + sovEtcdserver(uint64(*m.ClusterID))
}
if m.XXX_unrecognized != nil {
n += len(m.XXX_unrecognized)
}
return n
}
func sovEtcdserver(x uint64) (n int) {
return (math_bits.Len64(x|1) + 6) / 7
}
func sozEtcdserver(x uint64) (n int) {
return sovEtcdserver(uint64((x << 1) ^ uint64((int64(x) >> 63))))
}
func (m *Metadata) Unmarshal(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0
for iNdEx < l {
preIndex := iNdEx
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowEtcdserver
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
fieldNum := int32(wire >> 3)
wireType := int(wire & 0x7)
if wireType == 4 {
return fmt.Errorf("proto: Metadata: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: Metadata: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
case 1:
if wireType != 0 {
return fmt.Errorf("proto: wrong wireType = %d for field NodeID", wireType)
}
var v uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowEtcdserver
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
v |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
m.NodeID = &v
case 2:
if wireType != 0 {
return fmt.Errorf("proto: wrong wireType = %d for field ClusterID", wireType)
}
var v uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowEtcdserver
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
v |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
m.ClusterID = &v
default:
iNdEx = preIndex
skippy, err := skipEtcdserver(dAtA[iNdEx:])
if err != nil {
return err
}
if (skippy < 0) || (iNdEx+skippy) < 0 {
return ErrInvalidLengthEtcdserver
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
iNdEx += skippy
}
}
if iNdEx > l {
return io.ErrUnexpectedEOF
}
return nil
}
func skipEtcdserver(dAtA []byte) (n int, err error) {
l := len(dAtA)
iNdEx := 0
depth := 0
for iNdEx < l {
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return 0, ErrIntOverflowEtcdserver
}
if iNdEx >= l {
return 0, io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= (uint64(b) & 0x7F) << shift
if b < 0x80 {
break
}
}
wireType := int(wire & 0x7)
switch wireType {
case 0:
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return 0, ErrIntOverflowEtcdserver
}
if iNdEx >= l {
return 0, io.ErrUnexpectedEOF
}
iNdEx++
if dAtA[iNdEx-1] < 0x80 {
break
}
}
case 1:
iNdEx += 8
case 2:
var length int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return 0, ErrIntOverflowEtcdserver
}
if iNdEx >= l {
return 0, io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
length |= (int(b) & 0x7F) << shift
if b < 0x80 {
break
}
}
if length < 0 {
return 0, ErrInvalidLengthEtcdserver
}
iNdEx += length
case 3:
depth++
case 4:
if depth == 0 {
return 0, ErrUnexpectedEndOfGroupEtcdserver
}
depth--
case 5:
iNdEx += 4
default:
return 0, fmt.Errorf("proto: illegal wireType %d", wireType)
}
if iNdEx < 0 {
return 0, ErrInvalidLengthEtcdserver
}
if depth == 0 {
return iNdEx, nil
}
}
return 0, io.ErrUnexpectedEOF
}
var (
ErrInvalidLengthEtcdserver = fmt.Errorf("proto: negative length found during unmarshaling")
ErrIntOverflowEtcdserver = fmt.Errorf("proto: integer overflow")
ErrUnexpectedEndOfGroupEtcdserver = fmt.Errorf("proto: unexpected end of group")
)
================================================
FILE: api/etcdserverpb/etcdserver.proto
================================================
syntax = "proto2";
package etcdserverpb;
option go_package = "go.etcd.io/etcd/api/v3/etcdserverpb";
message Metadata {
optional uint64 NodeID = 1;
optional uint64 ClusterID = 2;
}
================================================
FILE: api/etcdserverpb/gw/rpc.pb.gw.go
================================================
// Code generated by protoc-gen-grpc-gateway. DO NOT EDIT.
// source: api/etcdserverpb/rpc.proto
/*
Package etcdserverpb is a reverse proxy.
It translates gRPC into RESTful JSON APIs.
*/
package gw
import (
protov1 "github.com/golang/protobuf/proto"
"context"
"errors"
"go.etcd.io/etcd/api/v3/etcdserverpb"
"io"
"net/http"
"github.com/grpc-ecosystem/grpc-gateway/v2/runtime"
"github.com/grpc-ecosystem/grpc-gateway/v2/utilities"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/grpclog"
"google.golang.org/grpc/metadata"
"google.golang.org/grpc/status"
"google.golang.org/protobuf/proto"
)
// Suppress "imported and not used" errors
var (
_ codes.Code
_ io.Reader
_ status.Status
_ = errors.New
_ = runtime.String
_ = utilities.NewDoubleArray
_ = metadata.Join
)
func request_KV_Range_0(ctx context.Context, marshaler runtime.Marshaler, client etcdserverpb.KVClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var (
protoReq etcdserverpb.RangeRequest
metadata runtime.ServerMetadata
)
if err := marshaler.NewDecoder(req.Body).Decode(protov1.MessageV2(&protoReq)); err != nil && !errors.Is(err, io.EOF) {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
if req.Body != nil {
_, _ = io.Copy(io.Discard, req.Body)
}
msg, err := client.Range(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
return protov1.MessageV2(msg), metadata, err
}
func local_request_KV_Range_0(ctx context.Context, marshaler runtime.Marshaler, server etcdserverpb.KVServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var (
protoReq etcdserverpb.RangeRequest
metadata runtime.ServerMetadata
)
if err := marshaler.NewDecoder(req.Body).Decode(protov1.MessageV2(&protoReq)); err != nil && !errors.Is(err, io.EOF) {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := server.Range(ctx, &protoReq)
return protov1.MessageV2(msg), metadata, err
}
func request_KV_Put_0(ctx context.Context, marshaler runtime.Marshaler, client etcdserverpb.KVClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var (
protoReq etcdserverpb.PutRequest
metadata runtime.ServerMetadata
)
if err := marshaler.NewDecoder(req.Body).Decode(protov1.MessageV2(&protoReq)); err != nil && !errors.Is(err, io.EOF) {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
if req.Body != nil {
_, _ = io.Copy(io.Discard, req.Body)
}
msg, err := client.Put(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
return protov1.MessageV2(msg), metadata, err
}
func local_request_KV_Put_0(ctx context.Context, marshaler runtime.Marshaler, server etcdserverpb.KVServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var (
protoReq etcdserverpb.PutRequest
metadata runtime.ServerMetadata
)
if err := marshaler.NewDecoder(req.Body).Decode(protov1.MessageV2(&protoReq)); err != nil && !errors.Is(err, io.EOF) {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := server.Put(ctx, &protoReq)
return protov1.MessageV2(msg), metadata, err
}
func request_KV_DeleteRange_0(ctx context.Context, marshaler runtime.Marshaler, client etcdserverpb.KVClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var (
protoReq etcdserverpb.DeleteRangeRequest
metadata runtime.ServerMetadata
)
if err := marshaler.NewDecoder(req.Body).Decode(protov1.MessageV2(&protoReq)); err != nil && !errors.Is(err, io.EOF) {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
if req.Body != nil {
_, _ = io.Copy(io.Discard, req.Body)
}
msg, err := client.DeleteRange(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
return protov1.MessageV2(msg), metadata, err
}
func local_request_KV_DeleteRange_0(ctx context.Context, marshaler runtime.Marshaler, server etcdserverpb.KVServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var (
protoReq etcdserverpb.DeleteRangeRequest
metadata runtime.ServerMetadata
)
if err := marshaler.NewDecoder(req.Body).Decode(protov1.MessageV2(&protoReq)); err != nil && !errors.Is(err, io.EOF) {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := server.DeleteRange(ctx, &protoReq)
return protov1.MessageV2(msg), metadata, err
}
func request_KV_Txn_0(ctx context.Context, marshaler runtime.Marshaler, client etcdserverpb.KVClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var (
protoReq etcdserverpb.TxnRequest
metadata runtime.ServerMetadata
)
if err := marshaler.NewDecoder(req.Body).Decode(protov1.MessageV2(&protoReq)); err != nil && !errors.Is(err, io.EOF) {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
if req.Body != nil {
_, _ = io.Copy(io.Discard, req.Body)
}
msg, err := client.Txn(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
return protov1.MessageV2(msg), metadata, err
}
func local_request_KV_Txn_0(ctx context.Context, marshaler runtime.Marshaler, server etcdserverpb.KVServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var (
protoReq etcdserverpb.TxnRequest
metadata runtime.ServerMetadata
)
if err := marshaler.NewDecoder(req.Body).Decode(protov1.MessageV2(&protoReq)); err != nil && !errors.Is(err, io.EOF) {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := server.Txn(ctx, &protoReq)
return protov1.MessageV2(msg), metadata, err
}
func request_KV_Compact_0(ctx context.Context, marshaler runtime.Marshaler, client etcdserverpb.KVClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var (
protoReq etcdserverpb.CompactionRequest
metadata runtime.ServerMetadata
)
if err := marshaler.NewDecoder(req.Body).Decode(protov1.MessageV2(&protoReq)); err != nil && !errors.Is(err, io.EOF) {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
if req.Body != nil {
_, _ = io.Copy(io.Discard, req.Body)
}
msg, err := client.Compact(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
return protov1.MessageV2(msg), metadata, err
}
func local_request_KV_Compact_0(ctx context.Context, marshaler runtime.Marshaler, server etcdserverpb.KVServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var (
protoReq etcdserverpb.CompactionRequest
metadata runtime.ServerMetadata
)
if err := marshaler.NewDecoder(req.Body).Decode(protov1.MessageV2(&protoReq)); err != nil && !errors.Is(err, io.EOF) {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := server.Compact(ctx, &protoReq)
return protov1.MessageV2(msg), metadata, err
}
func request_Watch_Watch_0(ctx context.Context, marshaler runtime.Marshaler, client etcdserverpb.WatchClient, req *http.Request, pathParams map[string]string) (etcdserverpb.Watch_WatchClient, runtime.ServerMetadata, error) {
var metadata runtime.ServerMetadata
stream, err := client.Watch(ctx)
if err != nil {
grpclog.Errorf("Failed to start streaming: %v", err)
return nil, metadata, err
}
dec := marshaler.NewDecoder(req.Body)
handleSend := func() error {
var protoReq etcdserverpb.WatchRequest
err := dec.Decode(protov1.MessageV2(&protoReq))
if errors.Is(err, io.EOF) {
return err
}
if err != nil {
grpclog.Errorf("Failed to decode request: %v", err)
return status.Errorf(codes.InvalidArgument, "Failed to decode request: %v", err)
}
if err := stream.Send(&protoReq); err != nil {
grpclog.Errorf("Failed to send request: %v", err)
return err
}
return nil
}
go func() {
for {
if err := handleSend(); err != nil {
break
}
}
if err := stream.CloseSend(); err != nil {
grpclog.Errorf("Failed to terminate client stream: %v", err)
}
}()
header, err := stream.Header()
if err != nil {
grpclog.Errorf("Failed to get header from client: %v", err)
return nil, metadata, err
}
metadata.HeaderMD = header
return stream, metadata, nil
}
func request_Lease_LeaseGrant_0(ctx context.Context, marshaler runtime.Marshaler, client etcdserverpb.LeaseClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var (
protoReq etcdserverpb.LeaseGrantRequest
metadata runtime.ServerMetadata
)
if err := marshaler.NewDecoder(req.Body).Decode(protov1.MessageV2(&protoReq)); err != nil && !errors.Is(err, io.EOF) {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
if req.Body != nil {
_, _ = io.Copy(io.Discard, req.Body)
}
msg, err := client.LeaseGrant(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
return protov1.MessageV2(msg), metadata, err
}
func local_request_Lease_LeaseGrant_0(ctx context.Context, marshaler runtime.Marshaler, server etcdserverpb.LeaseServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var (
protoReq etcdserverpb.LeaseGrantRequest
metadata runtime.ServerMetadata
)
if err := marshaler.NewDecoder(req.Body).Decode(protov1.MessageV2(&protoReq)); err != nil && !errors.Is(err, io.EOF) {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := server.LeaseGrant(ctx, &protoReq)
return protov1.MessageV2(msg), metadata, err
}
func request_Lease_LeaseRevoke_0(ctx context.Context, marshaler runtime.Marshaler, client etcdserverpb.LeaseClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var (
protoReq etcdserverpb.LeaseRevokeRequest
metadata runtime.ServerMetadata
)
if err := marshaler.NewDecoder(req.Body).Decode(protov1.MessageV2(&protoReq)); err != nil && !errors.Is(err, io.EOF) {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
if req.Body != nil {
_, _ = io.Copy(io.Discard, req.Body)
}
msg, err := client.LeaseRevoke(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
return protov1.MessageV2(msg), metadata, err
}
func local_request_Lease_LeaseRevoke_0(ctx context.Context, marshaler runtime.Marshaler, server etcdserverpb.LeaseServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var (
protoReq etcdserverpb.LeaseRevokeRequest
metadata runtime.ServerMetadata
)
if err := marshaler.NewDecoder(req.Body).Decode(protov1.MessageV2(&protoReq)); err != nil && !errors.Is(err, io.EOF) {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := server.LeaseRevoke(ctx, &protoReq)
return protov1.MessageV2(msg), metadata, err
}
func request_Lease_LeaseRevoke_1(ctx context.Context, marshaler runtime.Marshaler, client etcdserverpb.LeaseClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var (
protoReq etcdserverpb.LeaseRevokeRequest
metadata runtime.ServerMetadata
)
if err := marshaler.NewDecoder(req.Body).Decode(protov1.MessageV2(&protoReq)); err != nil && !errors.Is(err, io.EOF) {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
if req.Body != nil {
_, _ = io.Copy(io.Discard, req.Body)
}
msg, err := client.LeaseRevoke(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
return protov1.MessageV2(msg), metadata, err
}
func local_request_Lease_LeaseRevoke_1(ctx context.Context, marshaler runtime.Marshaler, server etcdserverpb.LeaseServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var (
protoReq etcdserverpb.LeaseRevokeRequest
metadata runtime.ServerMetadata
)
if err := marshaler.NewDecoder(req.Body).Decode(protov1.MessageV2(&protoReq)); err != nil && !errors.Is(err, io.EOF) {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := server.LeaseRevoke(ctx, &protoReq)
return protov1.MessageV2(msg), metadata, err
}
func request_Lease_LeaseKeepAlive_0(ctx context.Context, marshaler runtime.Marshaler, client etcdserverpb.LeaseClient, req *http.Request, pathParams map[string]string) (etcdserverpb.Lease_LeaseKeepAliveClient, runtime.ServerMetadata, error) {
var metadata runtime.ServerMetadata
stream, err := client.LeaseKeepAlive(ctx)
if err != nil {
grpclog.Errorf("Failed to start streaming: %v", err)
return nil, metadata, err
}
dec := marshaler.NewDecoder(req.Body)
handleSend := func() error {
var protoReq etcdserverpb.LeaseKeepAliveRequest
err := dec.Decode(protov1.MessageV2(&protoReq))
if errors.Is(err, io.EOF) {
return err
}
if err != nil {
grpclog.Errorf("Failed to decode request: %v", err)
return status.Errorf(codes.InvalidArgument, "Failed to decode request: %v", err)
}
if err := stream.Send(&protoReq); err != nil {
grpclog.Errorf("Failed to send request: %v", err)
return err
}
return nil
}
go func() {
for {
if err := handleSend(); err != nil {
break
}
}
if err := stream.CloseSend(); err != nil {
grpclog.Errorf("Failed to terminate client stream: %v", err)
}
}()
header, err := stream.Header()
if err != nil {
grpclog.Errorf("Failed to get header from client: %v", err)
return nil, metadata, err
}
metadata.HeaderMD = header
return stream, metadata, nil
}
func request_Lease_LeaseTimeToLive_0(ctx context.Context, marshaler runtime.Marshaler, client etcdserverpb.LeaseClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var (
protoReq etcdserverpb.LeaseTimeToLiveRequest
metadata runtime.ServerMetadata
)
if err := marshaler.NewDecoder(req.Body).Decode(protov1.MessageV2(&protoReq)); err != nil && !errors.Is(err, io.EOF) {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
if req.Body != nil {
_, _ = io.Copy(io.Discard, req.Body)
}
msg, err := client.LeaseTimeToLive(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
return protov1.MessageV2(msg), metadata, err
}
func local_request_Lease_LeaseTimeToLive_0(ctx context.Context, marshaler runtime.Marshaler, server etcdserverpb.LeaseServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var (
protoReq etcdserverpb.LeaseTimeToLiveRequest
metadata runtime.ServerMetadata
)
if err := marshaler.NewDecoder(req.Body).Decode(protov1.MessageV2(&protoReq)); err != nil && !errors.Is(err, io.EOF) {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := server.LeaseTimeToLive(ctx, &protoReq)
return protov1.MessageV2(msg), metadata, err
}
func request_Lease_LeaseTimeToLive_1(ctx context.Context, marshaler runtime.Marshaler, client etcdserverpb.LeaseClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var (
protoReq etcdserverpb.LeaseTimeToLiveRequest
metadata runtime.ServerMetadata
)
if err := marshaler.NewDecoder(req.Body).Decode(protov1.MessageV2(&protoReq)); err != nil && !errors.Is(err, io.EOF) {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
if req.Body != nil {
_, _ = io.Copy(io.Discard, req.Body)
}
msg, err := client.LeaseTimeToLive(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
return protov1.MessageV2(msg), metadata, err
}
func local_request_Lease_LeaseTimeToLive_1(ctx context.Context, marshaler runtime.Marshaler, server etcdserverpb.LeaseServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var (
protoReq etcdserverpb.LeaseTimeToLiveRequest
metadata runtime.ServerMetadata
)
if err := marshaler.NewDecoder(req.Body).Decode(protov1.MessageV2(&protoReq)); err != nil && !errors.Is(err, io.EOF) {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := server.LeaseTimeToLive(ctx, &protoReq)
return protov1.MessageV2(msg), metadata, err
}
func request_Lease_LeaseLeases_0(ctx context.Context, marshaler runtime.Marshaler, client etcdserverpb.LeaseClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var (
protoReq etcdserverpb.LeaseLeasesRequest
metadata runtime.ServerMetadata
)
if err := marshaler.NewDecoder(req.Body).Decode(protov1.MessageV2(&protoReq)); err != nil && !errors.Is(err, io.EOF) {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
if req.Body != nil {
_, _ = io.Copy(io.Discard, req.Body)
}
msg, err := client.LeaseLeases(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
return protov1.MessageV2(msg), metadata, err
}
func local_request_Lease_LeaseLeases_0(ctx context.Context, marshaler runtime.Marshaler, server etcdserverpb.LeaseServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var (
protoReq etcdserverpb.LeaseLeasesRequest
metadata runtime.ServerMetadata
)
if err := marshaler.NewDecoder(req.Body).Decode(protov1.MessageV2(&protoReq)); err != nil && !errors.Is(err, io.EOF) {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := server.LeaseLeases(ctx, &protoReq)
return protov1.MessageV2(msg), metadata, err
}
func request_Lease_LeaseLeases_1(ctx context.Context, marshaler runtime.Marshaler, client etcdserverpb.LeaseClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var (
protoReq etcdserverpb.LeaseLeasesRequest
metadata runtime.ServerMetadata
)
if err := marshaler.NewDecoder(req.Body).Decode(protov1.MessageV2(&protoReq)); err != nil && !errors.Is(err, io.EOF) {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
if req.Body != nil {
_, _ = io.Copy(io.Discard, req.Body)
}
msg, err := client.LeaseLeases(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
return protov1.MessageV2(msg), metadata, err
}
func local_request_Lease_LeaseLeases_1(ctx context.Context, marshaler runtime.Marshaler, server etcdserverpb.LeaseServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var (
protoReq etcdserverpb.LeaseLeasesRequest
metadata runtime.ServerMetadata
)
if err := marshaler.NewDecoder(req.Body).Decode(protov1.MessageV2(&protoReq)); err != nil && !errors.Is(err, io.EOF) {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := server.LeaseLeases(ctx, &protoReq)
return protov1.MessageV2(msg), metadata, err
}
func request_Cluster_MemberAdd_0(ctx context.Context, marshaler runtime.Marshaler, client etcdserverpb.ClusterClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var (
protoReq etcdserverpb.MemberAddRequest
metadata runtime.ServerMetadata
)
if err := marshaler.NewDecoder(req.Body).Decode(protov1.MessageV2(&protoReq)); err != nil && !errors.Is(err, io.EOF) {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
if req.Body != nil {
_, _ = io.Copy(io.Discard, req.Body)
}
msg, err := client.MemberAdd(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
return protov1.MessageV2(msg), metadata, err
}
func local_request_Cluster_MemberAdd_0(ctx context.Context, marshaler runtime.Marshaler, server etcdserverpb.ClusterServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var (
protoReq etcdserverpb.MemberAddRequest
metadata runtime.ServerMetadata
)
if err := marshaler.NewDecoder(req.Body).Decode(protov1.MessageV2(&protoReq)); err != nil && !errors.Is(err, io.EOF) {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := server.MemberAdd(ctx, &protoReq)
return protov1.MessageV2(msg), metadata, err
}
func request_Cluster_MemberRemove_0(ctx context.Context, marshaler runtime.Marshaler, client etcdserverpb.ClusterClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var (
protoReq etcdserverpb.MemberRemoveRequest
metadata runtime.ServerMetadata
)
if err := marshaler.NewDecoder(req.Body).Decode(protov1.MessageV2(&protoReq)); err != nil && !errors.Is(err, io.EOF) {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
if req.Body != nil {
_, _ = io.Copy(io.Discard, req.Body)
}
msg, err := client.MemberRemove(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
return protov1.MessageV2(msg), metadata, err
}
func local_request_Cluster_MemberRemove_0(ctx context.Context, marshaler runtime.Marshaler, server etcdserverpb.ClusterServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var (
protoReq etcdserverpb.MemberRemoveRequest
metadata runtime.ServerMetadata
)
if err := marshaler.NewDecoder(req.Body).Decode(protov1.MessageV2(&protoReq)); err != nil && !errors.Is(err, io.EOF) {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := server.MemberRemove(ctx, &protoReq)
return protov1.MessageV2(msg), metadata, err
}
func request_Cluster_MemberUpdate_0(ctx context.Context, marshaler runtime.Marshaler, client etcdserverpb.ClusterClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var (
protoReq etcdserverpb.MemberUpdateRequest
metadata runtime.ServerMetadata
)
if err := marshaler.NewDecoder(req.Body).Decode(protov1.MessageV2(&protoReq)); err != nil && !errors.Is(err, io.EOF) {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
if req.Body != nil {
_, _ = io.Copy(io.Discard, req.Body)
}
msg, err := client.MemberUpdate(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
return protov1.MessageV2(msg), metadata, err
}
func local_request_Cluster_MemberUpdate_0(ctx context.Context, marshaler runtime.Marshaler, server etcdserverpb.ClusterServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var (
protoReq etcdserverpb.MemberUpdateRequest
metadata runtime.ServerMetadata
)
if err := marshaler.NewDecoder(req.Body).Decode(protov1.MessageV2(&protoReq)); err != nil && !errors.Is(err, io.EOF) {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := server.MemberUpdate(ctx, &protoReq)
return protov1.MessageV2(msg), metadata, err
}
func request_Cluster_MemberList_0(ctx context.Context, marshaler runtime.Marshaler, client etcdserverpb.ClusterClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var (
protoReq etcdserverpb.MemberListRequest
metadata runtime.ServerMetadata
)
if err := marshaler.NewDecoder(req.Body).Decode(protov1.MessageV2(&protoReq)); err != nil && !errors.Is(err, io.EOF) {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
if req.Body != nil {
_, _ = io.Copy(io.Discard, req.Body)
}
msg, err := client.MemberList(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
return protov1.MessageV2(msg), metadata, err
}
func local_request_Cluster_MemberList_0(ctx context.Context, marshaler runtime.Marshaler, server etcdserverpb.ClusterServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var (
protoReq etcdserverpb.MemberListRequest
metadata runtime.ServerMetadata
)
if err := marshaler.NewDecoder(req.Body).Decode(protov1.MessageV2(&protoReq)); err != nil && !errors.Is(err, io.EOF) {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := server.MemberList(ctx, &protoReq)
return protov1.MessageV2(msg), metadata, err
}
func request_Cluster_MemberPromote_0(ctx context.Context, marshaler runtime.Marshaler, client etcdserverpb.ClusterClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var (
protoReq etcdserverpb.MemberPromoteRequest
metadata runtime.ServerMetadata
)
if err := marshaler.NewDecoder(req.Body).Decode(protov1.MessageV2(&protoReq)); err != nil && !errors.Is(err, io.EOF) {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
if req.Body != nil {
_, _ = io.Copy(io.Discard, req.Body)
}
msg, err := client.MemberPromote(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
return protov1.MessageV2(msg), metadata, err
}
func local_request_Cluster_MemberPromote_0(ctx context.Context, marshaler runtime.Marshaler, server etcdserverpb.ClusterServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var (
protoReq etcdserverpb.MemberPromoteRequest
metadata runtime.ServerMetadata
)
if err := marshaler.NewDecoder(req.Body).Decode(protov1.MessageV2(&protoReq)); err != nil && !errors.Is(err, io.EOF) {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := server.MemberPromote(ctx, &protoReq)
return protov1.MessageV2(msg), metadata, err
}
func request_Maintenance_Alarm_0(ctx context.Context, marshaler runtime.Marshaler, client etcdserverpb.MaintenanceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var (
protoReq etcdserverpb.AlarmRequest
metadata runtime.ServerMetadata
)
if err := marshaler.NewDecoder(req.Body).Decode(protov1.MessageV2(&protoReq)); err != nil && !errors.Is(err, io.EOF) {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
if req.Body != nil {
_, _ = io.Copy(io.Discard, req.Body)
}
msg, err := client.Alarm(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
return protov1.MessageV2(msg), metadata, err
}
func local_request_Maintenance_Alarm_0(ctx context.Context, marshaler runtime.Marshaler, server etcdserverpb.MaintenanceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var (
protoReq etcdserverpb.AlarmRequest
metadata runtime.ServerMetadata
)
if err := marshaler.NewDecoder(req.Body).Decode(protov1.MessageV2(&protoReq)); err != nil && !errors.Is(err, io.EOF) {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := server.Alarm(ctx, &protoReq)
return protov1.MessageV2(msg), metadata, err
}
func request_Maintenance_Status_0(ctx context.Context, marshaler runtime.Marshaler, client etcdserverpb.MaintenanceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var (
protoReq etcdserverpb.StatusRequest
metadata runtime.ServerMetadata
)
if err := marshaler.NewDecoder(req.Body).Decode(protov1.MessageV2(&protoReq)); err != nil && !errors.Is(err, io.EOF) {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
if req.Body != nil {
_, _ = io.Copy(io.Discard, req.Body)
}
msg, err := client.Status(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
return protov1.MessageV2(msg), metadata, err
}
func local_request_Maintenance_Status_0(ctx context.Context, marshaler runtime.Marshaler, server etcdserverpb.MaintenanceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var (
protoReq etcdserverpb.StatusRequest
metadata runtime.ServerMetadata
)
if err := marshaler.NewDecoder(req.Body).Decode(protov1.MessageV2(&protoReq)); err != nil && !errors.Is(err, io.EOF) {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := server.Status(ctx, &protoReq)
return protov1.MessageV2(msg), metadata, err
}
func request_Maintenance_Defragment_0(ctx context.Context, marshaler runtime.Marshaler, client etcdserverpb.MaintenanceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var (
protoReq etcdserverpb.DefragmentRequest
metadata runtime.ServerMetadata
)
if err := marshaler.NewDecoder(req.Body).Decode(protov1.MessageV2(&protoReq)); err != nil && !errors.Is(err, io.EOF) {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
if req.Body != nil {
_, _ = io.Copy(io.Discard, req.Body)
}
msg, err := client.Defragment(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
return protov1.MessageV2(msg), metadata, err
}
func local_request_Maintenance_Defragment_0(ctx context.Context, marshaler runtime.Marshaler, server etcdserverpb.MaintenanceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var (
protoReq etcdserverpb.DefragmentRequest
metadata runtime.ServerMetadata
)
if err := marshaler.NewDecoder(req.Body).Decode(protov1.MessageV2(&protoReq)); err != nil && !errors.Is(err, io.EOF) {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := server.Defragment(ctx, &protoReq)
return protov1.MessageV2(msg), metadata, err
}
func request_Maintenance_Hash_0(ctx context.Context, marshaler runtime.Marshaler, client etcdserverpb.MaintenanceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var (
protoReq etcdserverpb.HashRequest
metadata runtime.ServerMetadata
)
if err := marshaler.NewDecoder(req.Body).Decode(protov1.MessageV2(&protoReq)); err != nil && !errors.Is(err, io.EOF) {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
if req.Body != nil {
_, _ = io.Copy(io.Discard, req.Body)
}
msg, err := client.Hash(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
return protov1.MessageV2(msg), metadata, err
}
func local_request_Maintenance_Hash_0(ctx context.Context, marshaler runtime.Marshaler, server etcdserverpb.MaintenanceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var (
protoReq etcdserverpb.HashRequest
metadata runtime.ServerMetadata
)
if err := marshaler.NewDecoder(req.Body).Decode(protov1.MessageV2(&protoReq)); err != nil && !errors.Is(err, io.EOF) {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := server.Hash(ctx, &protoReq)
return protov1.MessageV2(msg), metadata, err
}
func request_Maintenance_HashKV_0(ctx context.Context, marshaler runtime.Marshaler, client etcdserverpb.MaintenanceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var (
protoReq etcdserverpb.HashKVRequest
metadata runtime.ServerMetadata
)
if err := marshaler.NewDecoder(req.Body).Decode(protov1.MessageV2(&protoReq)); err != nil && !errors.Is(err, io.EOF) {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
if req.Body != nil {
_, _ = io.Copy(io.Discard, req.Body)
}
msg, err := client.HashKV(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
return protov1.MessageV2(msg), metadata, err
}
func local_request_Maintenance_HashKV_0(ctx context.Context, marshaler runtime.Marshaler, server etcdserverpb.MaintenanceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var (
protoReq etcdserverpb.HashKVRequest
metadata runtime.ServerMetadata
)
if err := marshaler.NewDecoder(req.Body).Decode(protov1.MessageV2(&protoReq)); err != nil && !errors.Is(err, io.EOF) {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := server.HashKV(ctx, &protoReq)
return protov1.MessageV2(msg), metadata, err
}
func request_Maintenance_Snapshot_0(ctx context.Context, marshaler runtime.Marshaler, client etcdserverpb.MaintenanceClient, req *http.Request, pathParams map[string]string) (etcdserverpb.Maintenance_SnapshotClient, runtime.ServerMetadata, error) {
var (
protoReq etcdserverpb.SnapshotRequest
metadata runtime.ServerMetadata
)
if err := marshaler.NewDecoder(req.Body).Decode(protov1.MessageV2(&protoReq)); err != nil && !errors.Is(err, io.EOF) {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
if req.Body != nil {
_, _ = io.Copy(io.Discard, req.Body)
}
stream, err := client.Snapshot(ctx, &protoReq)
if err != nil {
return nil, metadata, err
}
header, err := stream.Header()
if err != nil {
return nil, metadata, err
}
metadata.HeaderMD = header
return stream, metadata, nil
}
func request_Maintenance_MoveLeader_0(ctx context.Context, marshaler runtime.Marshaler, client etcdserverpb.MaintenanceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var (
protoReq etcdserverpb.MoveLeaderRequest
metadata runtime.ServerMetadata
)
if err := marshaler.NewDecoder(req.Body).Decode(protov1.MessageV2(&protoReq)); err != nil && !errors.Is(err, io.EOF) {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
if req.Body != nil {
_, _ = io.Copy(io.Discard, req.Body)
}
msg, err := client.MoveLeader(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
return protov1.MessageV2(msg), metadata, err
}
func local_request_Maintenance_MoveLeader_0(ctx context.Context, marshaler runtime.Marshaler, server etcdserverpb.MaintenanceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var (
protoReq etcdserverpb.MoveLeaderRequest
metadata runtime.ServerMetadata
)
if err := marshaler.NewDecoder(req.Body).Decode(protov1.MessageV2(&protoReq)); err != nil && !errors.Is(err, io.EOF) {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := server.MoveLeader(ctx, &protoReq)
return protov1.MessageV2(msg), metadata, err
}
func request_Maintenance_Downgrade_0(ctx context.Context, marshaler runtime.Marshaler, client etcdserverpb.MaintenanceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var (
protoReq etcdserverpb.DowngradeRequest
metadata runtime.ServerMetadata
)
if err := marshaler.NewDecoder(req.Body).Decode(protov1.MessageV2(&protoReq)); err != nil && !errors.Is(err, io.EOF) {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
if req.Body != nil {
_, _ = io.Copy(io.Discard, req.Body)
}
msg, err := client.Downgrade(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
return protov1.MessageV2(msg), metadata, err
}
func local_request_Maintenance_Downgrade_0(ctx context.Context, marshaler runtime.Marshaler, server etcdserverpb.MaintenanceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var (
protoReq etcdserverpb.DowngradeRequest
metadata runtime.ServerMetadata
)
if err := marshaler.NewDecoder(req.Body).Decode(protov1.MessageV2(&protoReq)); err != nil && !errors.Is(err, io.EOF) {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := server.Downgrade(ctx, &protoReq)
return protov1.MessageV2(msg), metadata, err
}
func request_Auth_AuthEnable_0(ctx context.Context, marshaler runtime.Marshaler, client etcdserverpb.AuthClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var (
protoReq etcdserverpb.AuthEnableRequest
metadata runtime.ServerMetadata
)
if err := marshaler.NewDecoder(req.Body).Decode(protov1.MessageV2(&protoReq)); err != nil && !errors.Is(err, io.EOF) {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
if req.Body != nil {
_, _ = io.Copy(io.Discard, req.Body)
}
msg, err := client.AuthEnable(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
return protov1.MessageV2(msg), metadata, err
}
func local_request_Auth_AuthEnable_0(ctx context.Context, marshaler runtime.Marshaler, server etcdserverpb.AuthServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var (
protoReq etcdserverpb.AuthEnableRequest
metadata runtime.ServerMetadata
)
if err := marshaler.NewDecoder(req.Body).Decode(protov1.MessageV2(&protoReq)); err != nil && !errors.Is(err, io.EOF) {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := server.AuthEnable(ctx, &protoReq)
return protov1.MessageV2(msg), metadata, err
}
func request_Auth_AuthDisable_0(ctx context.Context, marshaler runtime.Marshaler, client etcdserverpb.AuthClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var (
protoReq etcdserverpb.AuthDisableRequest
metadata runtime.ServerMetadata
)
if err := marshaler.NewDecoder(req.Body).Decode(protov1.MessageV2(&protoReq)); err != nil && !errors.Is(err, io.EOF) {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
if req.Body != nil {
_, _ = io.Copy(io.Discard, req.Body)
}
msg, err := client.AuthDisable(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
return protov1.MessageV2(msg), metadata, err
}
func local_request_Auth_AuthDisable_0(ctx context.Context, marshaler runtime.Marshaler, server etcdserverpb.AuthServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var (
protoReq etcdserverpb.AuthDisableRequest
metadata runtime.ServerMetadata
)
if err := marshaler.NewDecoder(req.Body).Decode(protov1.MessageV2(&protoReq)); err != nil && !errors.Is(err, io.EOF) {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := server.AuthDisable(ctx, &protoReq)
return protov1.MessageV2(msg), metadata, err
}
func request_Auth_AuthStatus_0(ctx context.Context, marshaler runtime.Marshaler, client etcdserverpb.AuthClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var (
protoReq etcdserverpb.AuthStatusRequest
metadata runtime.ServerMetadata
)
if err := marshaler.NewDecoder(req.Body).Decode(protov1.MessageV2(&protoReq)); err != nil && !errors.Is(err, io.EOF) {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
if req.Body != nil {
_, _ = io.Copy(io.Discard, req.Body)
}
msg, err := client.AuthStatus(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
return protov1.MessageV2(msg), metadata, err
}
func local_request_Auth_AuthStatus_0(ctx context.Context, marshaler runtime.Marshaler, server etcdserverpb.AuthServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var (
protoReq etcdserverpb.AuthStatusRequest
metadata runtime.ServerMetadata
)
if err := marshaler.NewDecoder(req.Body).Decode(protov1.MessageV2(&protoReq)); err != nil && !errors.Is(err, io.EOF) {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := server.AuthStatus(ctx, &protoReq)
return protov1.MessageV2(msg), metadata, err
}
func request_Auth_Authenticate_0(ctx context.Context, marshaler runtime.Marshaler, client etcdserverpb.AuthClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var (
protoReq etcdserverpb.AuthenticateRequest
metadata runtime.ServerMetadata
)
if err := marshaler.NewDecoder(req.Body).Decode(protov1.MessageV2(&protoReq)); err != nil && !errors.Is(err, io.EOF) {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
if req.Body != nil {
_, _ = io.Copy(io.Discard, req.Body)
}
msg, err := client.Authenticate(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
return protov1.MessageV2(msg), metadata, err
}
func local_request_Auth_Authenticate_0(ctx context.Context, marshaler runtime.Marshaler, server etcdserverpb.AuthServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var (
protoReq etcdserverpb.AuthenticateRequest
metadata runtime.ServerMetadata
)
if err := marshaler.NewDecoder(req.Body).Decode(protov1.MessageV2(&protoReq)); err != nil && !errors.Is(err, io.EOF) {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := server.Authenticate(ctx, &protoReq)
return protov1.MessageV2(msg), metadata, err
}
func request_Auth_UserAdd_0(ctx context.Context, marshaler runtime.Marshaler, client etcdserverpb.AuthClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var (
protoReq etcdserverpb.AuthUserAddRequest
metadata runtime.ServerMetadata
)
if err := marshaler.NewDecoder(req.Body).Decode(protov1.MessageV2(&protoReq)); err != nil && !errors.Is(err, io.EOF) {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
if req.Body != nil {
_, _ = io.Copy(io.Discard, req.Body)
}
msg, err := client.UserAdd(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
return protov1.MessageV2(msg), metadata, err
}
func local_request_Auth_UserAdd_0(ctx context.Context, marshaler runtime.Marshaler, server etcdserverpb.AuthServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var (
protoReq etcdserverpb.AuthUserAddRequest
metadata runtime.ServerMetadata
)
if err := marshaler.NewDecoder(req.Body).Decode(protov1.MessageV2(&protoReq)); err != nil && !errors.Is(err, io.EOF) {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := server.UserAdd(ctx, &protoReq)
return protov1.MessageV2(msg), metadata, err
}
func request_Auth_UserGet_0(ctx context.Context, marshaler runtime.Marshaler, client etcdserverpb.AuthClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var (
protoReq etcdserverpb.AuthUserGetRequest
metadata runtime.ServerMetadata
)
if err := marshaler.NewDecoder(req.Body).Decode(protov1.MessageV2(&protoReq)); err != nil && !errors.Is(err, io.EOF) {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
if req.Body != nil {
_, _ = io.Copy(io.Discard, req.Body)
}
msg, err := client.UserGet(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
return protov1.MessageV2(msg), metadata, err
}
func local_request_Auth_UserGet_0(ctx context.Context, marshaler runtime.Marshaler, server etcdserverpb.AuthServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var (
protoReq etcdserverpb.AuthUserGetRequest
metadata runtime.ServerMetadata
)
if err := marshaler.NewDecoder(req.Body).Decode(protov1.MessageV2(&protoReq)); err != nil && !errors.Is(err, io.EOF) {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := server.UserGet(ctx, &protoReq)
return protov1.MessageV2(msg), metadata, err
}
func request_Auth_UserList_0(ctx context.Context, marshaler runtime.Marshaler, client etcdserverpb.AuthClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var (
protoReq etcdserverpb.AuthUserListRequest
metadata runtime.ServerMetadata
)
if err := marshaler.NewDecoder(req.Body).Decode(protov1.MessageV2(&protoReq)); err != nil && !errors.Is(err, io.EOF) {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
if req.Body != nil {
_, _ = io.Copy(io.Discard, req.Body)
}
msg, err := client.UserList(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
return protov1.MessageV2(msg), metadata, err
}
func local_request_Auth_UserList_0(ctx context.Context, marshaler runtime.Marshaler, server etcdserverpb.AuthServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var (
protoReq etcdserverpb.AuthUserListRequest
metadata runtime.ServerMetadata
)
if err := marshaler.NewDecoder(req.Body).Decode(protov1.MessageV2(&protoReq)); err != nil && !errors.Is(err, io.EOF) {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := server.UserList(ctx, &protoReq)
return protov1.MessageV2(msg), metadata, err
}
func request_Auth_UserDelete_0(ctx context.Context, marshaler runtime.Marshaler, client etcdserverpb.AuthClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var (
protoReq etcdserverpb.AuthUserDeleteRequest
metadata runtime.ServerMetadata
)
if err := marshaler.NewDecoder(req.Body).Decode(protov1.MessageV2(&protoReq)); err != nil && !errors.Is(err, io.EOF) {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
if req.Body != nil {
_, _ = io.Copy(io.Discard, req.Body)
}
msg, err := client.UserDelete(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
return protov1.MessageV2(msg), metadata, err
}
func local_request_Auth_UserDelete_0(ctx context.Context, marshaler runtime.Marshaler, server etcdserverpb.AuthServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var (
protoReq etcdserverpb.AuthUserDeleteRequest
metadata runtime.ServerMetadata
)
if err := marshaler.NewDecoder(req.Body).Decode(protov1.MessageV2(&protoReq)); err != nil && !errors.Is(err, io.EOF) {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := server.UserDelete(ctx, &protoReq)
return protov1.MessageV2(msg), metadata, err
}
func request_Auth_UserChangePassword_0(ctx context.Context, marshaler runtime.Marshaler, client etcdserverpb.AuthClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var (
protoReq etcdserverpb.AuthUserChangePasswordRequest
metadata runtime.ServerMetadata
)
if err := marshaler.NewDecoder(req.Body).Decode(protov1.MessageV2(&protoReq)); err != nil && !errors.Is(err, io.EOF) {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
if req.Body != nil {
_, _ = io.Copy(io.Discard, req.Body)
}
msg, err := client.UserChangePassword(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
return protov1.MessageV2(msg), metadata, err
}
func local_request_Auth_UserChangePassword_0(ctx context.Context, marshaler runtime.Marshaler, server etcdserverpb.AuthServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var (
protoReq etcdserverpb.AuthUserChangePasswordRequest
metadata runtime.ServerMetadata
)
if err := marshaler.NewDecoder(req.Body).Decode(protov1.MessageV2(&protoReq)); err != nil && !errors.Is(err, io.EOF) {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := server.UserChangePassword(ctx, &protoReq)
return protov1.MessageV2(msg), metadata, err
}
func request_Auth_UserGrantRole_0(ctx context.Context, marshaler runtime.Marshaler, client etcdserverpb.AuthClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var (
protoReq etcdserverpb.AuthUserGrantRoleRequest
metadata runtime.ServerMetadata
)
if err := marshaler.NewDecoder(req.Body).Decode(protov1.MessageV2(&protoReq)); err != nil && !errors.Is(err, io.EOF) {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
if req.Body != nil {
_, _ = io.Copy(io.Discard, req.Body)
}
msg, err := client.UserGrantRole(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
return protov1.MessageV2(msg), metadata, err
}
func local_request_Auth_UserGrantRole_0(ctx context.Context, marshaler runtime.Marshaler, server etcdserverpb.AuthServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var (
protoReq etcdserverpb.AuthUserGrantRoleRequest
metadata runtime.ServerMetadata
)
if err := marshaler.NewDecoder(req.Body).Decode(protov1.MessageV2(&protoReq)); err != nil && !errors.Is(err, io.EOF) {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := server.UserGrantRole(ctx, &protoReq)
return protov1.MessageV2(msg), metadata, err
}
func request_Auth_UserRevokeRole_0(ctx context.Context, marshaler runtime.Marshaler, client etcdserverpb.AuthClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var (
protoReq etcdserverpb.AuthUserRevokeRoleRequest
metadata runtime.ServerMetadata
)
if err := marshaler.NewDecoder(req.Body).Decode(protov1.MessageV2(&protoReq)); err != nil && !errors.Is(err, io.EOF) {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
if req.Body != nil {
_, _ = io.Copy(io.Discard, req.Body)
}
msg, err := client.UserRevokeRole(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
return protov1.MessageV2(msg), metadata, err
}
func local_request_Auth_UserRevokeRole_0(ctx context.Context, marshaler runtime.Marshaler, server etcdserverpb.AuthServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var (
protoReq etcdserverpb.AuthUserRevokeRoleRequest
metadata runtime.ServerMetadata
)
if err := marshaler.NewDecoder(req.Body).Decode(protov1.MessageV2(&protoReq)); err != nil && !errors.Is(err, io.EOF) {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := server.UserRevokeRole(ctx, &protoReq)
return protov1.MessageV2(msg), metadata, err
}
func request_Auth_RoleAdd_0(ctx context.Context, marshaler runtime.Marshaler, client etcdserverpb.AuthClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var (
protoReq etcdserverpb.AuthRoleAddRequest
metadata runtime.ServerMetadata
)
if err := marshaler.NewDecoder(req.Body).Decode(protov1.MessageV2(&protoReq)); err != nil && !errors.Is(err, io.EOF) {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
if req.Body != nil {
_, _ = io.Copy(io.Discard, req.Body)
}
msg, err := client.RoleAdd(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
return protov1.MessageV2(msg), metadata, err
}
func local_request_Auth_RoleAdd_0(ctx context.Context, marshaler runtime.Marshaler, server etcdserverpb.AuthServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var (
protoReq etcdserverpb.AuthRoleAddRequest
metadata runtime.ServerMetadata
)
if err := marshaler.NewDecoder(req.Body).Decode(protov1.MessageV2(&protoReq)); err != nil && !errors.Is(err, io.EOF) {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := server.RoleAdd(ctx, &protoReq)
return protov1.MessageV2(msg), metadata, err
}
func request_Auth_RoleGet_0(ctx context.Context, marshaler runtime.Marshaler, client etcdserverpb.AuthClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var (
protoReq etcdserverpb.AuthRoleGetRequest
metadata runtime.ServerMetadata
)
if err := marshaler.NewDecoder(req.Body).Decode(protov1.MessageV2(&protoReq)); err != nil && !errors.Is(err, io.EOF) {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
if req.Body != nil {
_, _ = io.Copy(io.Discard, req.Body)
}
msg, err := client.RoleGet(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
return protov1.MessageV2(msg), metadata, err
}
func local_request_Auth_RoleGet_0(ctx context.Context, marshaler runtime.Marshaler, server etcdserverpb.AuthServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var (
protoReq etcdserverpb.AuthRoleGetRequest
metadata runtime.ServerMetadata
)
if err := marshaler.NewDecoder(req.Body).Decode(protov1.MessageV2(&protoReq)); err != nil && !errors.Is(err, io.EOF) {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := server.RoleGet(ctx, &protoReq)
return protov1.MessageV2(msg), metadata, err
}
func request_Auth_RoleList_0(ctx context.Context, marshaler runtime.Marshaler, client etcdserverpb.AuthClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var (
protoReq etcdserverpb.AuthRoleListRequest
metadata runtime.ServerMetadata
)
if err := marshaler.NewDecoder(req.Body).Decode(protov1.MessageV2(&protoReq)); err != nil && !errors.Is(err, io.EOF) {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
if req.Body != nil {
_, _ = io.Copy(io.Discard, req.Body)
}
msg, err := client.RoleList(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
return protov1.MessageV2(msg), metadata, err
}
func local_request_Auth_RoleList_0(ctx context.Context, marshaler runtime.Marshaler, server etcdserverpb.AuthServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var (
protoReq etcdserverpb.AuthRoleListRequest
metadata runtime.ServerMetadata
)
if err := marshaler.NewDecoder(req.Body).Decode(protov1.MessageV2(&protoReq)); err != nil && !errors.Is(err, io.EOF) {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := server.RoleList(ctx, &protoReq)
return protov1.MessageV2(msg), metadata, err
}
func request_Auth_RoleDelete_0(ctx context.Context, marshaler runtime.Marshaler, client etcdserverpb.AuthClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var (
protoReq etcdserverpb.AuthRoleDeleteRequest
metadata runtime.ServerMetadata
)
if err := marshaler.NewDecoder(req.Body).Decode(protov1.MessageV2(&protoReq)); err != nil && !errors.Is(err, io.EOF) {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
if req.Body != nil {
_, _ = io.Copy(io.Discard, req.Body)
}
msg, err := client.RoleDelete(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
return protov1.MessageV2(msg), metadata, err
}
func local_request_Auth_RoleDelete_0(ctx context.Context, marshaler runtime.Marshaler, server etcdserverpb.AuthServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var (
protoReq etcdserverpb.AuthRoleDeleteRequest
metadata runtime.ServerMetadata
)
if err := marshaler.NewDecoder(req.Body).Decode(protov1.MessageV2(&protoReq)); err != nil && !errors.Is(err, io.EOF) {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := server.RoleDelete(ctx, &protoReq)
return protov1.MessageV2(msg), metadata, err
}
func request_Auth_RoleGrantPermission_0(ctx context.Context, marshaler runtime.Marshaler, client etcdserverpb.AuthClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var (
protoReq etcdserverpb.AuthRoleGrantPermissionRequest
metadata runtime.ServerMetadata
)
if err := marshaler.NewDecoder(req.Body).Decode(protov1.MessageV2(&protoReq)); err != nil && !errors.Is(err, io.EOF) {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
if req.Body != nil {
_, _ = io.Copy(io.Discard, req.Body)
}
msg, err := client.RoleGrantPermission(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
return protov1.MessageV2(msg), metadata, err
}
func local_request_Auth_RoleGrantPermission_0(ctx context.Context, marshaler runtime.Marshaler, server etcdserverpb.AuthServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var (
protoReq etcdserverpb.AuthRoleGrantPermissionRequest
metadata runtime.ServerMetadata
)
if err := marshaler.NewDecoder(req.Body).Decode(protov1.MessageV2(&protoReq)); err != nil && !errors.Is(err, io.EOF) {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := server.RoleGrantPermission(ctx, &protoReq)
return protov1.MessageV2(msg), metadata, err
}
func request_Auth_RoleRevokePermission_0(ctx context.Context, marshaler runtime.Marshaler, client etcdserverpb.AuthClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var (
protoReq etcdserverpb.AuthRoleRevokePermissionRequest
metadata runtime.ServerMetadata
)
if err := marshaler.NewDecoder(req.Body).Decode(protov1.MessageV2(&protoReq)); err != nil && !errors.Is(err, io.EOF) {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
if req.Body != nil {
_, _ = io.Copy(io.Discard, req.Body)
}
msg, err := client.RoleRevokePermission(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
return protov1.MessageV2(msg), metadata, err
}
func local_request_Auth_RoleRevokePermission_0(ctx context.Context, marshaler runtime.Marshaler, server etcdserverpb.AuthServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var (
protoReq etcdserverpb.AuthRoleRevokePermissionRequest
metadata runtime.ServerMetadata
)
if err := marshaler.NewDecoder(req.Body).Decode(protov1.MessageV2(&protoReq)); err != nil && !errors.Is(err, io.EOF) {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := server.RoleRevokePermission(ctx, &protoReq)
return protov1.MessageV2(msg), metadata, err
}
// etcdserverpb.RegisterKVHandlerServer registers the http handlers for service KV to "mux".
// UnaryRPC :call etcdserverpb.KVServer directly.
// StreamingRPC :currently unsupported pending https://github.com/grpc/grpc-go/issues/906.
// Note that using this registration option will cause many gRPC library features to stop working. Consider using RegisterKVHandlerFromEndpoint instead.
// GRPC interceptors will not work for this type of registration. To use interceptors, you must use the "runtime.WithMiddlewares" option in the "runtime.NewServeMux" call.
func RegisterKVHandlerServer(ctx context.Context, mux *runtime.ServeMux, server etcdserverpb.KVServer) error {
mux.Handle(http.MethodPost, pattern_KV_Range_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
var stream runtime.ServerTransportStream
ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
annotatedContext, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/etcdserverpb.KV/Range", runtime.WithHTTPPathPattern("/v3/kv/range"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := local_request_KV_Range_0(annotatedContext, inboundMarshaler, server, req, pathParams)
md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
if err != nil {
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
return
}
forward_KV_Range_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle(http.MethodPost, pattern_KV_Put_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
var stream runtime.ServerTransportStream
ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
annotatedContext, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/etcdserverpb.KV/Put", runtime.WithHTTPPathPattern("/v3/kv/put"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := local_request_KV_Put_0(annotatedContext, inboundMarshaler, server, req, pathParams)
md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
if err != nil {
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
return
}
forward_KV_Put_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle(http.MethodPost, pattern_KV_DeleteRange_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
var stream runtime.ServerTransportStream
ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
annotatedContext, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/etcdserverpb.KV/DeleteRange", runtime.WithHTTPPathPattern("/v3/kv/deleterange"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := local_request_KV_DeleteRange_0(annotatedContext, inboundMarshaler, server, req, pathParams)
md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
if err != nil {
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
return
}
forward_KV_DeleteRange_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle(http.MethodPost, pattern_KV_Txn_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
var stream runtime.ServerTransportStream
ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
annotatedContext, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/etcdserverpb.KV/Txn", runtime.WithHTTPPathPattern("/v3/kv/txn"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := local_request_KV_Txn_0(annotatedContext, inboundMarshaler, server, req, pathParams)
md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
if err != nil {
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
return
}
forward_KV_Txn_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle(http.MethodPost, pattern_KV_Compact_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
var stream runtime.ServerTransportStream
ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
annotatedContext, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/etcdserverpb.KV/Compact", runtime.WithHTTPPathPattern("/v3/kv/compaction"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := local_request_KV_Compact_0(annotatedContext, inboundMarshaler, server, req, pathParams)
md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
if err != nil {
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
return
}
forward_KV_Compact_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
return nil
}
// etcdserverpb.RegisterWatchHandlerServer registers the http handlers for service Watch to "mux".
// UnaryRPC :call etcdserverpb.WatchServer directly.
// StreamingRPC :currently unsupported pending https://github.com/grpc/grpc-go/issues/906.
// Note that using this registration option will cause many gRPC library features to stop working. Consider using RegisterWatchHandlerFromEndpoint instead.
// GRPC interceptors will not work for this type of registration. To use interceptors, you must use the "runtime.WithMiddlewares" option in the "runtime.NewServeMux" call.
func RegisterWatchHandlerServer(ctx context.Context, mux *runtime.ServeMux, server etcdserverpb.WatchServer) error {
mux.Handle(http.MethodPost, pattern_Watch_Watch_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
err := status.Error(codes.Unimplemented, "streaming calls are not yet supported in the in-process transport")
_, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
})
return nil
}
// etcdserverpb.RegisterLeaseHandlerServer registers the http handlers for service Lease to "mux".
// UnaryRPC :call etcdserverpb.LeaseServer directly.
// StreamingRPC :currently unsupported pending https://github.com/grpc/grpc-go/issues/906.
// Note that using this registration option will cause many gRPC library features to stop working. Consider using RegisterLeaseHandlerFromEndpoint instead.
// GRPC interceptors will not work for this type of registration. To use interceptors, you must use the "runtime.WithMiddlewares" option in the "runtime.NewServeMux" call.
func RegisterLeaseHandlerServer(ctx context.Context, mux *runtime.ServeMux, server etcdserverpb.LeaseServer) error {
mux.Handle(http.MethodPost, pattern_Lease_LeaseGrant_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
var stream runtime.ServerTransportStream
ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
annotatedContext, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/etcdserverpb.Lease/LeaseGrant", runtime.WithHTTPPathPattern("/v3/lease/grant"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := local_request_Lease_LeaseGrant_0(annotatedContext, inboundMarshaler, server, req, pathParams)
md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
if err != nil {
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
return
}
forward_Lease_LeaseGrant_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle(http.MethodPost, pattern_Lease_LeaseRevoke_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
var stream runtime.ServerTransportStream
ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
annotatedContext, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/etcdserverpb.Lease/LeaseRevoke", runtime.WithHTTPPathPattern("/v3/lease/revoke"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := local_request_Lease_LeaseRevoke_0(annotatedContext, inboundMarshaler, server, req, pathParams)
md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
if err != nil {
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
return
}
forward_Lease_LeaseRevoke_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle(http.MethodPost, pattern_Lease_LeaseRevoke_1, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
var stream runtime.ServerTransportStream
ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
annotatedContext, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/etcdserverpb.Lease/LeaseRevoke", runtime.WithHTTPPathPattern("/v3/kv/lease/revoke"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := local_request_Lease_LeaseRevoke_1(annotatedContext, inboundMarshaler, server, req, pathParams)
md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
if err != nil {
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
return
}
forward_Lease_LeaseRevoke_1(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle(http.MethodPost, pattern_Lease_LeaseKeepAlive_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
err := status.Error(codes.Unimplemented, "streaming calls are not yet supported in the in-process transport")
_, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
})
mux.Handle(http.MethodPost, pattern_Lease_LeaseTimeToLive_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
var stream runtime.ServerTransportStream
ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
annotatedContext, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/etcdserverpb.Lease/LeaseTimeToLive", runtime.WithHTTPPathPattern("/v3/lease/timetolive"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := local_request_Lease_LeaseTimeToLive_0(annotatedContext, inboundMarshaler, server, req, pathParams)
md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
if err != nil {
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
return
}
forward_Lease_LeaseTimeToLive_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle(http.MethodPost, pattern_Lease_LeaseTimeToLive_1, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
var stream runtime.ServerTransportStream
ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
annotatedContext, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/etcdserverpb.Lease/LeaseTimeToLive", runtime.WithHTTPPathPattern("/v3/kv/lease/timetolive"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := local_request_Lease_LeaseTimeToLive_1(annotatedContext, inboundMarshaler, server, req, pathParams)
md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
if err != nil {
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
return
}
forward_Lease_LeaseTimeToLive_1(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle(http.MethodPost, pattern_Lease_LeaseLeases_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
var stream runtime.ServerTransportStream
ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
annotatedContext, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/etcdserverpb.Lease/LeaseLeases", runtime.WithHTTPPathPattern("/v3/lease/leases"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := local_request_Lease_LeaseLeases_0(annotatedContext, inboundMarshaler, server, req, pathParams)
md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
if err != nil {
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
return
}
forward_Lease_LeaseLeases_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle(http.MethodPost, pattern_Lease_LeaseLeases_1, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
var stream runtime.ServerTransportStream
ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
annotatedContext, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/etcdserverpb.Lease/LeaseLeases", runtime.WithHTTPPathPattern("/v3/kv/lease/leases"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := local_request_Lease_LeaseLeases_1(annotatedContext, inboundMarshaler, server, req, pathParams)
md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
if err != nil {
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
return
}
forward_Lease_LeaseLeases_1(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
return nil
}
// etcdserverpb.RegisterClusterHandlerServer registers the http handlers for service Cluster to "mux".
// UnaryRPC :call etcdserverpb.ClusterServer directly.
// StreamingRPC :currently unsupported pending https://github.com/grpc/grpc-go/issues/906.
// Note that using this registration option will cause many gRPC library features to stop working. Consider using RegisterClusterHandlerFromEndpoint instead.
// GRPC interceptors will not work for this type of registration. To use interceptors, you must use the "runtime.WithMiddlewares" option in the "runtime.NewServeMux" call.
func RegisterClusterHandlerServer(ctx context.Context, mux *runtime.ServeMux, server etcdserverpb.ClusterServer) error {
mux.Handle(http.MethodPost, pattern_Cluster_MemberAdd_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
var stream runtime.ServerTransportStream
ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
annotatedContext, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/etcdserverpb.Cluster/MemberAdd", runtime.WithHTTPPathPattern("/v3/cluster/member/add"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := local_request_Cluster_MemberAdd_0(annotatedContext, inboundMarshaler, server, req, pathParams)
md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
if err != nil {
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
return
}
forward_Cluster_MemberAdd_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle(http.MethodPost, pattern_Cluster_MemberRemove_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
var stream runtime.ServerTransportStream
ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
annotatedContext, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/etcdserverpb.Cluster/MemberRemove", runtime.WithHTTPPathPattern("/v3/cluster/member/remove"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := local_request_Cluster_MemberRemove_0(annotatedContext, inboundMarshaler, server, req, pathParams)
md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
if err != nil {
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
return
}
forward_Cluster_MemberRemove_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle(http.MethodPost, pattern_Cluster_MemberUpdate_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
var stream runtime.ServerTransportStream
ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
annotatedContext, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/etcdserverpb.Cluster/MemberUpdate", runtime.WithHTTPPathPattern("/v3/cluster/member/update"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := local_request_Cluster_MemberUpdate_0(annotatedContext, inboundMarshaler, server, req, pathParams)
md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
if err != nil {
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
return
}
forward_Cluster_MemberUpdate_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle(http.MethodPost, pattern_Cluster_MemberList_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
var stream runtime.ServerTransportStream
ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
annotatedContext, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/etcdserverpb.Cluster/MemberList", runtime.WithHTTPPathPattern("/v3/cluster/member/list"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := local_request_Cluster_MemberList_0(annotatedContext, inboundMarshaler, server, req, pathParams)
md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
if err != nil {
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
return
}
forward_Cluster_MemberList_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle(http.MethodPost, pattern_Cluster_MemberPromote_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
var stream runtime.ServerTransportStream
ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
annotatedContext, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/etcdserverpb.Cluster/MemberPromote", runtime.WithHTTPPathPattern("/v3/cluster/member/promote"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := local_request_Cluster_MemberPromote_0(annotatedContext, inboundMarshaler, server, req, pathParams)
md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
if err != nil {
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
return
}
forward_Cluster_MemberPromote_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
return nil
}
// etcdserverpb.RegisterMaintenanceHandlerServer registers the http handlers for service Maintenance to "mux".
// UnaryRPC :call etcdserverpb.MaintenanceServer directly.
// StreamingRPC :currently unsupported pending https://github.com/grpc/grpc-go/issues/906.
// Note that using this registration option will cause many gRPC library features to stop working. Consider using RegisterMaintenanceHandlerFromEndpoint instead.
// GRPC interceptors will not work for this type of registration. To use interceptors, you must use the "runtime.WithMiddlewares" option in the "runtime.NewServeMux" call.
func RegisterMaintenanceHandlerServer(ctx context.Context, mux *runtime.ServeMux, server etcdserverpb.MaintenanceServer) error {
mux.Handle(http.MethodPost, pattern_Maintenance_Alarm_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
var stream runtime.ServerTransportStream
ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
annotatedContext, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/etcdserverpb.Maintenance/Alarm", runtime.WithHTTPPathPattern("/v3/maintenance/alarm"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := local_request_Maintenance_Alarm_0(annotatedContext, inboundMarshaler, server, req, pathParams)
md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
if err != nil {
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
return
}
forward_Maintenance_Alarm_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle(http.MethodPost, pattern_Maintenance_Status_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
var stream runtime.ServerTransportStream
ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
annotatedContext, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/etcdserverpb.Maintenance/Status", runtime.WithHTTPPathPattern("/v3/maintenance/status"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := local_request_Maintenance_Status_0(annotatedContext, inboundMarshaler, server, req, pathParams)
md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
if err != nil {
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
return
}
forward_Maintenance_Status_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle(http.MethodPost, pattern_Maintenance_Defragment_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
var stream runtime.ServerTransportStream
ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
annotatedContext, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/etcdserverpb.Maintenance/Defragment", runtime.WithHTTPPathPattern("/v3/maintenance/defragment"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := local_request_Maintenance_Defragment_0(annotatedContext, inboundMarshaler, server, req, pathParams)
md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
if err != nil {
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
return
}
forward_Maintenance_Defragment_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle(http.MethodPost, pattern_Maintenance_Hash_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
var stream runtime.ServerTransportStream
ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
annotatedContext, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/etcdserverpb.Maintenance/Hash", runtime.WithHTTPPathPattern("/v3/maintenance/hash"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := local_request_Maintenance_Hash_0(annotatedContext, inboundMarshaler, server, req, pathParams)
md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
if err != nil {
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
return
}
forward_Maintenance_Hash_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle(http.MethodPost, pattern_Maintenance_HashKV_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
var stream runtime.ServerTransportStream
ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
annotatedContext, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/etcdserverpb.Maintenance/HashKV", runtime.WithHTTPPathPattern("/v3/maintenance/hashkv"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := local_request_Maintenance_HashKV_0(annotatedContext, inboundMarshaler, server, req, pathParams)
md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
if err != nil {
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
return
}
forward_Maintenance_HashKV_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle(http.MethodPost, pattern_Maintenance_Snapshot_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
err := status.Error(codes.Unimplemented, "streaming calls are not yet supported in the in-process transport")
_, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
})
mux.Handle(http.MethodPost, pattern_Maintenance_MoveLeader_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
var stream runtime.ServerTransportStream
ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
annotatedContext, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/etcdserverpb.Maintenance/MoveLeader", runtime.WithHTTPPathPattern("/v3/maintenance/transfer-leadership"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := local_request_Maintenance_MoveLeader_0(annotatedContext, inboundMarshaler, server, req, pathParams)
md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
if err != nil {
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
return
}
forward_Maintenance_MoveLeader_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle(http.MethodPost, pattern_Maintenance_Downgrade_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
var stream runtime.ServerTransportStream
ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
annotatedContext, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/etcdserverpb.Maintenance/Downgrade", runtime.WithHTTPPathPattern("/v3/maintenance/downgrade"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := local_request_Maintenance_Downgrade_0(annotatedContext, inboundMarshaler, server, req, pathParams)
md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
if err != nil {
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
return
}
forward_Maintenance_Downgrade_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
return nil
}
// etcdserverpb.RegisterAuthHandlerServer registers the http handlers for service Auth to "mux".
// UnaryRPC :call etcdserverpb.AuthServer directly.
// StreamingRPC :currently unsupported pending https://github.com/grpc/grpc-go/issues/906.
// Note that using this registration option will cause many gRPC library features to stop working. Consider using RegisterAuthHandlerFromEndpoint instead.
// GRPC interceptors will not work for this type of registration. To use interceptors, you must use the "runtime.WithMiddlewares" option in the "runtime.NewServeMux" call.
func RegisterAuthHandlerServer(ctx context.Context, mux *runtime.ServeMux, server etcdserverpb.AuthServer) error {
mux.Handle(http.MethodPost, pattern_Auth_AuthEnable_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
var stream runtime.ServerTransportStream
ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
annotatedContext, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/etcdserverpb.Auth/AuthEnable", runtime.WithHTTPPathPattern("/v3/auth/enable"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := local_request_Auth_AuthEnable_0(annotatedContext, inboundMarshaler, server, req, pathParams)
md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
if err != nil {
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
return
}
forward_Auth_AuthEnable_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle(http.MethodPost, pattern_Auth_AuthDisable_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
var stream runtime.ServerTransportStream
ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
annotatedContext, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/etcdserverpb.Auth/AuthDisable", runtime.WithHTTPPathPattern("/v3/auth/disable"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := local_request_Auth_AuthDisable_0(annotatedContext, inboundMarshaler, server, req, pathParams)
md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
if err != nil {
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
return
}
forward_Auth_AuthDisable_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle(http.MethodPost, pattern_Auth_AuthStatus_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
var stream runtime.ServerTransportStream
ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
annotatedContext, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/etcdserverpb.Auth/AuthStatus", runtime.WithHTTPPathPattern("/v3/auth/status"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := local_request_Auth_AuthStatus_0(annotatedContext, inboundMarshaler, server, req, pathParams)
md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
if err != nil {
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
return
}
forward_Auth_AuthStatus_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle(http.MethodPost, pattern_Auth_Authenticate_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
var stream runtime.ServerTransportStream
ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
annotatedContext, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/etcdserverpb.Auth/Authenticate", runtime.WithHTTPPathPattern("/v3/auth/authenticate"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := local_request_Auth_Authenticate_0(annotatedContext, inboundMarshaler, server, req, pathParams)
md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
if err != nil {
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
return
}
forward_Auth_Authenticate_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle(http.MethodPost, pattern_Auth_UserAdd_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
var stream runtime.ServerTransportStream
ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
annotatedContext, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/etcdserverpb.Auth/UserAdd", runtime.WithHTTPPathPattern("/v3/auth/user/add"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := local_request_Auth_UserAdd_0(annotatedContext, inboundMarshaler, server, req, pathParams)
md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
if err != nil {
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
return
}
forward_Auth_UserAdd_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle(http.MethodPost, pattern_Auth_UserGet_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
var stream runtime.ServerTransportStream
ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
annotatedContext, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/etcdserverpb.Auth/UserGet", runtime.WithHTTPPathPattern("/v3/auth/user/get"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := local_request_Auth_UserGet_0(annotatedContext, inboundMarshaler, server, req, pathParams)
md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
if err != nil {
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
return
}
forward_Auth_UserGet_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle(http.MethodPost, pattern_Auth_UserList_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
var stream runtime.ServerTransportStream
ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
annotatedContext, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/etcdserverpb.Auth/UserList", runtime.WithHTTPPathPattern("/v3/auth/user/list"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := local_request_Auth_UserList_0(annotatedContext, inboundMarshaler, server, req, pathParams)
md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
if err != nil {
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
return
}
forward_Auth_UserList_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle(http.MethodPost, pattern_Auth_UserDelete_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
var stream runtime.ServerTransportStream
ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
annotatedContext, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/etcdserverpb.Auth/UserDelete", runtime.WithHTTPPathPattern("/v3/auth/user/delete"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := local_request_Auth_UserDelete_0(annotatedContext, inboundMarshaler, server, req, pathParams)
md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
if err != nil {
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
return
}
forward_Auth_UserDelete_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle(http.MethodPost, pattern_Auth_UserChangePassword_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
var stream runtime.ServerTransportStream
ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
annotatedContext, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/etcdserverpb.Auth/UserChangePassword", runtime.WithHTTPPathPattern("/v3/auth/user/changepw"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := local_request_Auth_UserChangePassword_0(annotatedContext, inboundMarshaler, server, req, pathParams)
md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
if err != nil {
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
return
}
forward_Auth_UserChangePassword_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle(http.MethodPost, pattern_Auth_UserGrantRole_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
var stream runtime.ServerTransportStream
ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
annotatedContext, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/etcdserverpb.Auth/UserGrantRole", runtime.WithHTTPPathPattern("/v3/auth/user/grant"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := local_request_Auth_UserGrantRole_0(annotatedContext, inboundMarshaler, server, req, pathParams)
md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
if err != nil {
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
return
}
forward_Auth_UserGrantRole_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle(http.MethodPost, pattern_Auth_UserRevokeRole_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
var stream runtime.ServerTransportStream
ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
annotatedContext, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/etcdserverpb.Auth/UserRevokeRole", runtime.WithHTTPPathPattern("/v3/auth/user/revoke"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := local_request_Auth_UserRevokeRole_0(annotatedContext, inboundMarshaler, server, req, pathParams)
md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
if err != nil {
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
return
}
forward_Auth_UserRevokeRole_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle(http.MethodPost, pattern_Auth_RoleAdd_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
var stream runtime.ServerTransportStream
ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
annotatedContext, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/etcdserverpb.Auth/RoleAdd", runtime.WithHTTPPathPattern("/v3/auth/role/add"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := local_request_Auth_RoleAdd_0(annotatedContext, inboundMarshaler, server, req, pathParams)
md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
if err != nil {
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
return
}
forward_Auth_RoleAdd_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle(http.MethodPost, pattern_Auth_RoleGet_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
var stream runtime.ServerTransportStream
ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
annotatedContext, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/etcdserverpb.Auth/RoleGet", runtime.WithHTTPPathPattern("/v3/auth/role/get"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := local_request_Auth_RoleGet_0(annotatedContext, inboundMarshaler, server, req, pathParams)
md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
if err != nil {
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
return
}
forward_Auth_RoleGet_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle(http.MethodPost, pattern_Auth_RoleList_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
var stream runtime.ServerTransportStream
ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
annotatedContext, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/etcdserverpb.Auth/RoleList", runtime.WithHTTPPathPattern("/v3/auth/role/list"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := local_request_Auth_RoleList_0(annotatedContext, inboundMarshaler, server, req, pathParams)
md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
if err != nil {
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
return
}
forward_Auth_RoleList_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle(http.MethodPost, pattern_Auth_RoleDelete_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
var stream runtime.ServerTransportStream
ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
annotatedContext, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/etcdserverpb.Auth/RoleDelete", runtime.WithHTTPPathPattern("/v3/auth/role/delete"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := local_request_Auth_RoleDelete_0(annotatedContext, inboundMarshaler, server, req, pathParams)
md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
if err != nil {
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
return
}
forward_Auth_RoleDelete_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle(http.MethodPost, pattern_Auth_RoleGrantPermission_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
var stream runtime.ServerTransportStream
ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
annotatedContext, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/etcdserverpb.Auth/RoleGrantPermission", runtime.WithHTTPPathPattern("/v3/auth/role/grant"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := local_request_Auth_RoleGrantPermission_0(annotatedContext, inboundMarshaler, server, req, pathParams)
md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
if err != nil {
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
return
}
forward_Auth_RoleGrantPermission_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle(http.MethodPost, pattern_Auth_RoleRevokePermission_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
var stream runtime.ServerTransportStream
ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
annotatedContext, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/etcdserverpb.Auth/RoleRevokePermission", runtime.WithHTTPPathPattern("/v3/auth/role/revoke"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := local_request_Auth_RoleRevokePermission_0(annotatedContext, inboundMarshaler, server, req, pathParams)
md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
if err != nil {
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
return
}
forward_Auth_RoleRevokePermission_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
return nil
}
// RegisterKVHandlerFromEndpoint is same as RegisterKVHandler but
// automatically dials to "endpoint" and closes the connection when "ctx" gets done.
func RegisterKVHandlerFromEndpoint(ctx context.Context, mux *runtime.ServeMux, endpoint string, opts []grpc.DialOption) (err error) {
conn, err := grpc.NewClient(endpoint, opts...)
if err != nil {
return err
}
defer func() {
if err != nil {
if cerr := conn.Close(); cerr != nil {
grpclog.Errorf("Failed to close conn to %s: %v", endpoint, cerr)
}
return
}
go func() {
<-ctx.Done()
if cerr := conn.Close(); cerr != nil {
grpclog.Errorf("Failed to close conn to %s: %v", endpoint, cerr)
}
}()
}()
return RegisterKVHandler(ctx, mux, conn)
}
// RegisterKVHandler registers the http handlers for service KV to "mux".
// The handlers forward requests to the grpc endpoint over "conn".
func RegisterKVHandler(ctx context.Context, mux *runtime.ServeMux, conn *grpc.ClientConn) error {
return RegisterKVHandlerClient(ctx, mux, etcdserverpb.NewKVClient(conn))
}
// etcdserverpb.RegisterKVHandlerClient registers the http handlers for service KV
// to "mux". The handlers forward requests to the grpc endpoint over the given implementation of "KVClient".
// Note: the gRPC framework executes interceptors within the gRPC handler. If the passed in "KVClient"
// doesn't go through the normal gRPC flow (creating a gRPC client etc.) then it will be up to the passed in
// "KVClient" to call the correct interceptors. This client ignores the HTTP middlewares.
func RegisterKVHandlerClient(ctx context.Context, mux *runtime.ServeMux, client etcdserverpb.KVClient) error {
mux.Handle(http.MethodPost, pattern_KV_Range_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
annotatedContext, err := runtime.AnnotateContext(ctx, mux, req, "/etcdserverpb.KV/Range", runtime.WithHTTPPathPattern("/v3/kv/range"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := request_KV_Range_0(annotatedContext, inboundMarshaler, client, req, pathParams)
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
if err != nil {
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
return
}
forward_KV_Range_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle(http.MethodPost, pattern_KV_Put_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
annotatedContext, err := runtime.AnnotateContext(ctx, mux, req, "/etcdserverpb.KV/Put", runtime.WithHTTPPathPattern("/v3/kv/put"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := request_KV_Put_0(annotatedContext, inboundMarshaler, client, req, pathParams)
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
if err != nil {
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
return
}
forward_KV_Put_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle(http.MethodPost, pattern_KV_DeleteRange_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
annotatedContext, err := runtime.AnnotateContext(ctx, mux, req, "/etcdserverpb.KV/DeleteRange", runtime.WithHTTPPathPattern("/v3/kv/deleterange"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := request_KV_DeleteRange_0(annotatedContext, inboundMarshaler, client, req, pathParams)
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
if err != nil {
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
return
}
forward_KV_DeleteRange_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle(http.MethodPost, pattern_KV_Txn_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
annotatedContext, err := runtime.AnnotateContext(ctx, mux, req, "/etcdserverpb.KV/Txn", runtime.WithHTTPPathPattern("/v3/kv/txn"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := request_KV_Txn_0(annotatedContext, inboundMarshaler, client, req, pathParams)
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
if err != nil {
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
return
}
forward_KV_Txn_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle(http.MethodPost, pattern_KV_Compact_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
annotatedContext, err := runtime.AnnotateContext(ctx, mux, req, "/etcdserverpb.KV/Compact", runtime.WithHTTPPathPattern("/v3/kv/compaction"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := request_KV_Compact_0(annotatedContext, inboundMarshaler, client, req, pathParams)
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
if err != nil {
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
return
}
forward_KV_Compact_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
return nil
}
var (
pattern_KV_Range_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"v3", "kv", "range"}, ""))
pattern_KV_Put_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"v3", "kv", "put"}, ""))
pattern_KV_DeleteRange_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"v3", "kv", "deleterange"}, ""))
pattern_KV_Txn_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"v3", "kv", "txn"}, ""))
pattern_KV_Compact_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"v3", "kv", "compaction"}, ""))
)
var (
forward_KV_Range_0 = runtime.ForwardResponseMessage
forward_KV_Put_0 = runtime.ForwardResponseMessage
forward_KV_DeleteRange_0 = runtime.ForwardResponseMessage
forward_KV_Txn_0 = runtime.ForwardResponseMessage
forward_KV_Compact_0 = runtime.ForwardResponseMessage
)
// RegisterWatchHandlerFromEndpoint is same as RegisterWatchHandler but
// automatically dials to "endpoint" and closes the connection when "ctx" gets done.
func RegisterWatchHandlerFromEndpoint(ctx context.Context, mux *runtime.ServeMux, endpoint string, opts []grpc.DialOption) (err error) {
conn, err := grpc.NewClient(endpoint, opts...)
if err != nil {
return err
}
defer func() {
if err != nil {
if cerr := conn.Close(); cerr != nil {
grpclog.Errorf("Failed to close conn to %s: %v", endpoint, cerr)
}
return
}
go func() {
<-ctx.Done()
if cerr := conn.Close(); cerr != nil {
grpclog.Errorf("Failed to close conn to %s: %v", endpoint, cerr)
}
}()
}()
return RegisterWatchHandler(ctx, mux, conn)
}
// RegisterWatchHandler registers the http handlers for service Watch to "mux".
// The handlers forward requests to the grpc endpoint over "conn".
func RegisterWatchHandler(ctx context.Context, mux *runtime.ServeMux, conn *grpc.ClientConn) error {
return RegisterWatchHandlerClient(ctx, mux, etcdserverpb.NewWatchClient(conn))
}
// etcdserverpb.RegisterWatchHandlerClient registers the http handlers for service Watch
// to "mux". The handlers forward requests to the grpc endpoint over the given implementation of "WatchClient".
// Note: the gRPC framework executes interceptors within the gRPC handler. If the passed in "WatchClient"
// doesn't go through the normal gRPC flow (creating a gRPC client etc.) then it will be up to the passed in
// "WatchClient" to call the correct interceptors. This client ignores the HTTP middlewares.
func RegisterWatchHandlerClient(ctx context.Context, mux *runtime.ServeMux, client etcdserverpb.WatchClient) error {
mux.Handle(http.MethodPost, pattern_Watch_Watch_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
annotatedContext, err := runtime.AnnotateContext(ctx, mux, req, "/etcdserverpb.Watch/Watch", runtime.WithHTTPPathPattern("/v3/watch"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := request_Watch_Watch_0(annotatedContext, inboundMarshaler, client, req, pathParams)
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
if err != nil {
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
return
}
forward_Watch_Watch_0(annotatedContext, mux, outboundMarshaler, w, req, func() (proto.Message, error) {
m1, err := resp.Recv()
return protov1.MessageV2(m1), err
}, mux.GetForwardResponseOptions()...)
})
return nil
}
var (
pattern_Watch_Watch_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"v3", "watch"}, ""))
)
var (
forward_Watch_Watch_0 = runtime.ForwardResponseStream
)
// RegisterLeaseHandlerFromEndpoint is same as RegisterLeaseHandler but
// automatically dials to "endpoint" and closes the connection when "ctx" gets done.
func RegisterLeaseHandlerFromEndpoint(ctx context.Context, mux *runtime.ServeMux, endpoint string, opts []grpc.DialOption) (err error) {
conn, err := grpc.NewClient(endpoint, opts...)
if err != nil {
return err
}
defer func() {
if err != nil {
if cerr := conn.Close(); cerr != nil {
grpclog.Errorf("Failed to close conn to %s: %v", endpoint, cerr)
}
return
}
go func() {
<-ctx.Done()
if cerr := conn.Close(); cerr != nil {
grpclog.Errorf("Failed to close conn to %s: %v", endpoint, cerr)
}
}()
}()
return RegisterLeaseHandler(ctx, mux, conn)
}
// RegisterLeaseHandler registers the http handlers for service Lease to "mux".
// The handlers forward requests to the grpc endpoint over "conn".
func RegisterLeaseHandler(ctx context.Context, mux *runtime.ServeMux, conn *grpc.ClientConn) error {
return RegisterLeaseHandlerClient(ctx, mux, etcdserverpb.NewLeaseClient(conn))
}
// etcdserverpb.RegisterLeaseHandlerClient registers the http handlers for service Lease
// to "mux". The handlers forward requests to the grpc endpoint over the given implementation of "LeaseClient".
// Note: the gRPC framework executes interceptors within the gRPC handler. If the passed in "LeaseClient"
// doesn't go through the normal gRPC flow (creating a gRPC client etc.) then it will be up to the passed in
// "LeaseClient" to call the correct interceptors. This client ignores the HTTP middlewares.
func RegisterLeaseHandlerClient(ctx context.Context, mux *runtime.ServeMux, client etcdserverpb.LeaseClient) error {
mux.Handle(http.MethodPost, pattern_Lease_LeaseGrant_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
annotatedContext, err := runtime.AnnotateContext(ctx, mux, req, "/etcdserverpb.Lease/LeaseGrant", runtime.WithHTTPPathPattern("/v3/lease/grant"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := request_Lease_LeaseGrant_0(annotatedContext, inboundMarshaler, client, req, pathParams)
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
if err != nil {
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
return
}
forward_Lease_LeaseGrant_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle(http.MethodPost, pattern_Lease_LeaseRevoke_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
annotatedContext, err := runtime.AnnotateContext(ctx, mux, req, "/etcdserverpb.Lease/LeaseRevoke", runtime.WithHTTPPathPattern("/v3/lease/revoke"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := request_Lease_LeaseRevoke_0(annotatedContext, inboundMarshaler, client, req, pathParams)
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
if err != nil {
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
return
}
forward_Lease_LeaseRevoke_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle(http.MethodPost, pattern_Lease_LeaseRevoke_1, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
annotatedContext, err := runtime.AnnotateContext(ctx, mux, req, "/etcdserverpb.Lease/LeaseRevoke", runtime.WithHTTPPathPattern("/v3/kv/lease/revoke"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := request_Lease_LeaseRevoke_1(annotatedContext, inboundMarshaler, client, req, pathParams)
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
if err != nil {
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
return
}
forward_Lease_LeaseRevoke_1(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle(http.MethodPost, pattern_Lease_LeaseKeepAlive_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
annotatedContext, err := runtime.AnnotateContext(ctx, mux, req, "/etcdserverpb.Lease/LeaseKeepAlive", runtime.WithHTTPPathPattern("/v3/lease/keepalive"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := request_Lease_LeaseKeepAlive_0(annotatedContext, inboundMarshaler, client, req, pathParams)
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
if err != nil {
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
return
}
forward_Lease_LeaseKeepAlive_0(annotatedContext, mux, outboundMarshaler, w, req, func() (proto.Message, error) {
m1, err := resp.Recv()
return protov1.MessageV2(m1), err
}, mux.GetForwardResponseOptions()...)
})
mux.Handle(http.MethodPost, pattern_Lease_LeaseTimeToLive_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
annotatedContext, err := runtime.AnnotateContext(ctx, mux, req, "/etcdserverpb.Lease/LeaseTimeToLive", runtime.WithHTTPPathPattern("/v3/lease/timetolive"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := request_Lease_LeaseTimeToLive_0(annotatedContext, inboundMarshaler, client, req, pathParams)
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
if err != nil {
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
return
}
forward_Lease_LeaseTimeToLive_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle(http.MethodPost, pattern_Lease_LeaseTimeToLive_1, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
annotatedContext, err := runtime.AnnotateContext(ctx, mux, req, "/etcdserverpb.Lease/LeaseTimeToLive", runtime.WithHTTPPathPattern("/v3/kv/lease/timetolive"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := request_Lease_LeaseTimeToLive_1(annotatedContext, inboundMarshaler, client, req, pathParams)
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
if err != nil {
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
return
}
forward_Lease_LeaseTimeToLive_1(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle(http.MethodPost, pattern_Lease_LeaseLeases_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
annotatedContext, err := runtime.AnnotateContext(ctx, mux, req, "/etcdserverpb.Lease/LeaseLeases", runtime.WithHTTPPathPattern("/v3/lease/leases"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := request_Lease_LeaseLeases_0(annotatedContext, inboundMarshaler, client, req, pathParams)
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
if err != nil {
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
return
}
forward_Lease_LeaseLeases_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle(http.MethodPost, pattern_Lease_LeaseLeases_1, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
annotatedContext, err := runtime.AnnotateContext(ctx, mux, req, "/etcdserverpb.Lease/LeaseLeases", runtime.WithHTTPPathPattern("/v3/kv/lease/leases"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := request_Lease_LeaseLeases_1(annotatedContext, inboundMarshaler, client, req, pathParams)
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
if err != nil {
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
return
}
forward_Lease_LeaseLeases_1(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
return nil
}
var (
pattern_Lease_LeaseGrant_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"v3", "lease", "grant"}, ""))
pattern_Lease_LeaseRevoke_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"v3", "lease", "revoke"}, ""))
pattern_Lease_LeaseRevoke_1 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"v3", "kv", "lease", "revoke"}, ""))
pattern_Lease_LeaseKeepAlive_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"v3", "lease", "keepalive"}, ""))
pattern_Lease_LeaseTimeToLive_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"v3", "lease", "timetolive"}, ""))
pattern_Lease_LeaseTimeToLive_1 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"v3", "kv", "lease", "timetolive"}, ""))
pattern_Lease_LeaseLeases_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"v3", "lease", "leases"}, ""))
pattern_Lease_LeaseLeases_1 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"v3", "kv", "lease", "leases"}, ""))
)
var (
forward_Lease_LeaseGrant_0 = runtime.ForwardResponseMessage
forward_Lease_LeaseRevoke_0 = runtime.ForwardResponseMessage
forward_Lease_LeaseRevoke_1 = runtime.ForwardResponseMessage
forward_Lease_LeaseKeepAlive_0 = runtime.ForwardResponseStream
forward_Lease_LeaseTimeToLive_0 = runtime.ForwardResponseMessage
forward_Lease_LeaseTimeToLive_1 = runtime.ForwardResponseMessage
forward_Lease_LeaseLeases_0 = runtime.ForwardResponseMessage
forward_Lease_LeaseLeases_1 = runtime.ForwardResponseMessage
)
// RegisterClusterHandlerFromEndpoint is same as RegisterClusterHandler but
// automatically dials to "endpoint" and closes the connection when "ctx" gets done.
func RegisterClusterHandlerFromEndpoint(ctx context.Context, mux *runtime.ServeMux, endpoint string, opts []grpc.DialOption) (err error) {
conn, err := grpc.NewClient(endpoint, opts...)
if err != nil {
return err
}
defer func() {
if err != nil {
if cerr := conn.Close(); cerr != nil {
grpclog.Errorf("Failed to close conn to %s: %v", endpoint, cerr)
}
return
}
go func() {
<-ctx.Done()
if cerr := conn.Close(); cerr != nil {
grpclog.Errorf("Failed to close conn to %s: %v", endpoint, cerr)
}
}()
}()
return RegisterClusterHandler(ctx, mux, conn)
}
// RegisterClusterHandler registers the http handlers for service Cluster to "mux".
// The handlers forward requests to the grpc endpoint over "conn".
func RegisterClusterHandler(ctx context.Context, mux *runtime.ServeMux, conn *grpc.ClientConn) error {
return RegisterClusterHandlerClient(ctx, mux, etcdserverpb.NewClusterClient(conn))
}
// etcdserverpb.RegisterClusterHandlerClient registers the http handlers for service Cluster
// to "mux". The handlers forward requests to the grpc endpoint over the given implementation of "ClusterClient".
// Note: the gRPC framework executes interceptors within the gRPC handler. If the passed in "ClusterClient"
// doesn't go through the normal gRPC flow (creating a gRPC client etc.) then it will be up to the passed in
// "ClusterClient" to call the correct interceptors. This client ignores the HTTP middlewares.
func RegisterClusterHandlerClient(ctx context.Context, mux *runtime.ServeMux, client etcdserverpb.ClusterClient) error {
mux.Handle(http.MethodPost, pattern_Cluster_MemberAdd_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
annotatedContext, err := runtime.AnnotateContext(ctx, mux, req, "/etcdserverpb.Cluster/MemberAdd", runtime.WithHTTPPathPattern("/v3/cluster/member/add"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := request_Cluster_MemberAdd_0(annotatedContext, inboundMarshaler, client, req, pathParams)
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
if err != nil {
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
return
}
forward_Cluster_MemberAdd_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle(http.MethodPost, pattern_Cluster_MemberRemove_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
annotatedContext, err := runtime.AnnotateContext(ctx, mux, req, "/etcdserverpb.Cluster/MemberRemove", runtime.WithHTTPPathPattern("/v3/cluster/member/remove"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := request_Cluster_MemberRemove_0(annotatedContext, inboundMarshaler, client, req, pathParams)
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
if err != nil {
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
return
}
forward_Cluster_MemberRemove_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle(http.MethodPost, pattern_Cluster_MemberUpdate_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
annotatedContext, err := runtime.AnnotateContext(ctx, mux, req, "/etcdserverpb.Cluster/MemberUpdate", runtime.WithHTTPPathPattern("/v3/cluster/member/update"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := request_Cluster_MemberUpdate_0(annotatedContext, inboundMarshaler, client, req, pathParams)
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
if err != nil {
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
return
}
forward_Cluster_MemberUpdate_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle(http.MethodPost, pattern_Cluster_MemberList_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
annotatedContext, err := runtime.AnnotateContext(ctx, mux, req, "/etcdserverpb.Cluster/MemberList", runtime.WithHTTPPathPattern("/v3/cluster/member/list"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := request_Cluster_MemberList_0(annotatedContext, inboundMarshaler, client, req, pathParams)
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
if err != nil {
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
return
}
forward_Cluster_MemberList_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle(http.MethodPost, pattern_Cluster_MemberPromote_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
annotatedContext, err := runtime.AnnotateContext(ctx, mux, req, "/etcdserverpb.Cluster/MemberPromote", runtime.WithHTTPPathPattern("/v3/cluster/member/promote"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := request_Cluster_MemberPromote_0(annotatedContext, inboundMarshaler, client, req, pathParams)
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
if err != nil {
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
return
}
forward_Cluster_MemberPromote_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
return nil
}
var (
pattern_Cluster_MemberAdd_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"v3", "cluster", "member", "add"}, ""))
pattern_Cluster_MemberRemove_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"v3", "cluster", "member", "remove"}, ""))
pattern_Cluster_MemberUpdate_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"v3", "cluster", "member", "update"}, ""))
pattern_Cluster_MemberList_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"v3", "cluster", "member", "list"}, ""))
pattern_Cluster_MemberPromote_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"v3", "cluster", "member", "promote"}, ""))
)
var (
forward_Cluster_MemberAdd_0 = runtime.ForwardResponseMessage
forward_Cluster_MemberRemove_0 = runtime.ForwardResponseMessage
forward_Cluster_MemberUpdate_0 = runtime.ForwardResponseMessage
forward_Cluster_MemberList_0 = runtime.ForwardResponseMessage
forward_Cluster_MemberPromote_0 = runtime.ForwardResponseMessage
)
// RegisterMaintenanceHandlerFromEndpoint is same as RegisterMaintenanceHandler but
// automatically dials to "endpoint" and closes the connection when "ctx" gets done.
func RegisterMaintenanceHandlerFromEndpoint(ctx context.Context, mux *runtime.ServeMux, endpoint string, opts []grpc.DialOption) (err error) {
conn, err := grpc.NewClient(endpoint, opts...)
if err != nil {
return err
}
defer func() {
if err != nil {
if cerr := conn.Close(); cerr != nil {
grpclog.Errorf("Failed to close conn to %s: %v", endpoint, cerr)
}
return
}
go func() {
<-ctx.Done()
if cerr := conn.Close(); cerr != nil {
grpclog.Errorf("Failed to close conn to %s: %v", endpoint, cerr)
}
}()
}()
return RegisterMaintenanceHandler(ctx, mux, conn)
}
// RegisterMaintenanceHandler registers the http handlers for service Maintenance to "mux".
// The handlers forward requests to the grpc endpoint over "conn".
func RegisterMaintenanceHandler(ctx context.Context, mux *runtime.ServeMux, conn *grpc.ClientConn) error {
return RegisterMaintenanceHandlerClient(ctx, mux, etcdserverpb.NewMaintenanceClient(conn))
}
// etcdserverpb.RegisterMaintenanceHandlerClient registers the http handlers for service Maintenance
// to "mux". The handlers forward requests to the grpc endpoint over the given implementation of "MaintenanceClient".
// Note: the gRPC framework executes interceptors within the gRPC handler. If the passed in "MaintenanceClient"
// doesn't go through the normal gRPC flow (creating a gRPC client etc.) then it will be up to the passed in
// "MaintenanceClient" to call the correct interceptors. This client ignores the HTTP middlewares.
func RegisterMaintenanceHandlerClient(ctx context.Context, mux *runtime.ServeMux, client etcdserverpb.MaintenanceClient) error {
mux.Handle(http.MethodPost, pattern_Maintenance_Alarm_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
annotatedContext, err := runtime.AnnotateContext(ctx, mux, req, "/etcdserverpb.Maintenance/Alarm", runtime.WithHTTPPathPattern("/v3/maintenance/alarm"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := request_Maintenance_Alarm_0(annotatedContext, inboundMarshaler, client, req, pathParams)
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
if err != nil {
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
return
}
forward_Maintenance_Alarm_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle(http.MethodPost, pattern_Maintenance_Status_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
annotatedContext, err := runtime.AnnotateContext(ctx, mux, req, "/etcdserverpb.Maintenance/Status", runtime.WithHTTPPathPattern("/v3/maintenance/status"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := request_Maintenance_Status_0(annotatedContext, inboundMarshaler, client, req, pathParams)
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
if err != nil {
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
return
}
forward_Maintenance_Status_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle(http.MethodPost, pattern_Maintenance_Defragment_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
annotatedContext, err := runtime.AnnotateContext(ctx, mux, req, "/etcdserverpb.Maintenance/Defragment", runtime.WithHTTPPathPattern("/v3/maintenance/defragment"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := request_Maintenance_Defragment_0(annotatedContext, inboundMarshaler, client, req, pathParams)
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
if err != nil {
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
return
}
forward_Maintenance_Defragment_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle(http.MethodPost, pattern_Maintenance_Hash_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
annotatedContext, err := runtime.AnnotateContext(ctx, mux, req, "/etcdserverpb.Maintenance/Hash", runtime.WithHTTPPathPattern("/v3/maintenance/hash"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := request_Maintenance_Hash_0(annotatedContext, inboundMarshaler, client, req, pathParams)
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
if err != nil {
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
return
}
forward_Maintenance_Hash_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle(http.MethodPost, pattern_Maintenance_HashKV_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
annotatedContext, err := runtime.AnnotateContext(ctx, mux, req, "/etcdserverpb.Maintenance/HashKV", runtime.WithHTTPPathPattern("/v3/maintenance/hashkv"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := request_Maintenance_HashKV_0(annotatedContext, inboundMarshaler, client, req, pathParams)
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
if err != nil {
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
return
}
forward_Maintenance_HashKV_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle(http.MethodPost, pattern_Maintenance_Snapshot_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
annotatedContext, err := runtime.AnnotateContext(ctx, mux, req, "/etcdserverpb.Maintenance/Snapshot", runtime.WithHTTPPathPattern("/v3/maintenance/snapshot"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := request_Maintenance_Snapshot_0(annotatedContext, inboundMarshaler, client, req, pathParams)
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
if err != nil {
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
return
}
forward_Maintenance_Snapshot_0(annotatedContext, mux, outboundMarshaler, w, req, func() (proto.Message, error) {
m1, err := resp.Recv()
return protov1.MessageV2(m1), err
}, mux.GetForwardResponseOptions()...)
})
mux.Handle(http.MethodPost, pattern_Maintenance_MoveLeader_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
annotatedContext, err := runtime.AnnotateContext(ctx, mux, req, "/etcdserverpb.Maintenance/MoveLeader", runtime.WithHTTPPathPattern("/v3/maintenance/transfer-leadership"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := request_Maintenance_MoveLeader_0(annotatedContext, inboundMarshaler, client, req, pathParams)
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
if err != nil {
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
return
}
forward_Maintenance_MoveLeader_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle(http.MethodPost, pattern_Maintenance_Downgrade_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
annotatedContext, err := runtime.AnnotateContext(ctx, mux, req, "/etcdserverpb.Maintenance/Downgrade", runtime.WithHTTPPathPattern("/v3/maintenance/downgrade"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := request_Maintenance_Downgrade_0(annotatedContext, inboundMarshaler, client, req, pathParams)
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
if err != nil {
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
return
}
forward_Maintenance_Downgrade_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
return nil
}
var (
pattern_Maintenance_Alarm_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"v3", "maintenance", "alarm"}, ""))
pattern_Maintenance_Status_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"v3", "maintenance", "status"}, ""))
pattern_Maintenance_Defragment_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"v3", "maintenance", "defragment"}, ""))
pattern_Maintenance_Hash_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"v3", "maintenance", "hash"}, ""))
pattern_Maintenance_HashKV_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"v3", "maintenance", "hashkv"}, ""))
pattern_Maintenance_Snapshot_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"v3", "maintenance", "snapshot"}, ""))
pattern_Maintenance_MoveLeader_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"v3", "maintenance", "transfer-leadership"}, ""))
pattern_Maintenance_Downgrade_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"v3", "maintenance", "downgrade"}, ""))
)
var (
forward_Maintenance_Alarm_0 = runtime.ForwardResponseMessage
forward_Maintenance_Status_0 = runtime.ForwardResponseMessage
forward_Maintenance_Defragment_0 = runtime.ForwardResponseMessage
forward_Maintenance_Hash_0 = runtime.ForwardResponseMessage
forward_Maintenance_HashKV_0 = runtime.ForwardResponseMessage
forward_Maintenance_Snapshot_0 = runtime.ForwardResponseStream
forward_Maintenance_MoveLeader_0 = runtime.ForwardResponseMessage
forward_Maintenance_Downgrade_0 = runtime.ForwardResponseMessage
)
// RegisterAuthHandlerFromEndpoint is same as RegisterAuthHandler but
// automatically dials to "endpoint" and closes the connection when "ctx" gets done.
func RegisterAuthHandlerFromEndpoint(ctx context.Context, mux *runtime.ServeMux, endpoint string, opts []grpc.DialOption) (err error) {
conn, err := grpc.NewClient(endpoint, opts...)
if err != nil {
return err
}
defer func() {
if err != nil {
if cerr := conn.Close(); cerr != nil {
grpclog.Errorf("Failed to close conn to %s: %v", endpoint, cerr)
}
return
}
go func() {
<-ctx.Done()
if cerr := conn.Close(); cerr != nil {
grpclog.Errorf("Failed to close conn to %s: %v", endpoint, cerr)
}
}()
}()
return RegisterAuthHandler(ctx, mux, conn)
}
// RegisterAuthHandler registers the http handlers for service Auth to "mux".
// The handlers forward requests to the grpc endpoint over "conn".
func RegisterAuthHandler(ctx context.Context, mux *runtime.ServeMux, conn *grpc.ClientConn) error {
return RegisterAuthHandlerClient(ctx, mux, etcdserverpb.NewAuthClient(conn))
}
// etcdserverpb.RegisterAuthHandlerClient registers the http handlers for service Auth
// to "mux". The handlers forward requests to the grpc endpoint over the given implementation of "AuthClient".
// Note: the gRPC framework executes interceptors within the gRPC handler. If the passed in "AuthClient"
// doesn't go through the normal gRPC flow (creating a gRPC client etc.) then it will be up to the passed in
// "AuthClient" to call the correct interceptors. This client ignores the HTTP middlewares.
func RegisterAuthHandlerClient(ctx context.Context, mux *runtime.ServeMux, client etcdserverpb.AuthClient) error {
mux.Handle(http.MethodPost, pattern_Auth_AuthEnable_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
annotatedContext, err := runtime.AnnotateContext(ctx, mux, req, "/etcdserverpb.Auth/AuthEnable", runtime.WithHTTPPathPattern("/v3/auth/enable"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := request_Auth_AuthEnable_0(annotatedContext, inboundMarshaler, client, req, pathParams)
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
if err != nil {
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
return
}
forward_Auth_AuthEnable_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle(http.MethodPost, pattern_Auth_AuthDisable_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
annotatedContext, err := runtime.AnnotateContext(ctx, mux, req, "/etcdserverpb.Auth/AuthDisable", runtime.WithHTTPPathPattern("/v3/auth/disable"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := request_Auth_AuthDisable_0(annotatedContext, inboundMarshaler, client, req, pathParams)
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
if err != nil {
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
return
}
forward_Auth_AuthDisable_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle(http.MethodPost, pattern_Auth_AuthStatus_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
annotatedContext, err := runtime.AnnotateContext(ctx, mux, req, "/etcdserverpb.Auth/AuthStatus", runtime.WithHTTPPathPattern("/v3/auth/status"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := request_Auth_AuthStatus_0(annotatedContext, inboundMarshaler, client, req, pathParams)
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
if err != nil {
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
return
}
forward_Auth_AuthStatus_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle(http.MethodPost, pattern_Auth_Authenticate_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
annotatedContext, err := runtime.AnnotateContext(ctx, mux, req, "/etcdserverpb.Auth/Authenticate", runtime.WithHTTPPathPattern("/v3/auth/authenticate"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := request_Auth_Authenticate_0(annotatedContext, inboundMarshaler, client, req, pathParams)
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
if err != nil {
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
return
}
forward_Auth_Authenticate_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle(http.MethodPost, pattern_Auth_UserAdd_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
annotatedContext, err := runtime.AnnotateContext(ctx, mux, req, "/etcdserverpb.Auth/UserAdd", runtime.WithHTTPPathPattern("/v3/auth/user/add"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := request_Auth_UserAdd_0(annotatedContext, inboundMarshaler, client, req, pathParams)
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
if err != nil {
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
return
}
forward_Auth_UserAdd_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle(http.MethodPost, pattern_Auth_UserGet_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
annotatedContext, err := runtime.AnnotateContext(ctx, mux, req, "/etcdserverpb.Auth/UserGet", runtime.WithHTTPPathPattern("/v3/auth/user/get"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := request_Auth_UserGet_0(annotatedContext, inboundMarshaler, client, req, pathParams)
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
if err != nil {
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
return
}
forward_Auth_UserGet_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle(http.MethodPost, pattern_Auth_UserList_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
annotatedContext, err := runtime.AnnotateContext(ctx, mux, req, "/etcdserverpb.Auth/UserList", runtime.WithHTTPPathPattern("/v3/auth/user/list"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := request_Auth_UserList_0(annotatedContext, inboundMarshaler, client, req, pathParams)
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
if err != nil {
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
return
}
forward_Auth_UserList_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle(http.MethodPost, pattern_Auth_UserDelete_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
annotatedContext, err := runtime.AnnotateContext(ctx, mux, req, "/etcdserverpb.Auth/UserDelete", runtime.WithHTTPPathPattern("/v3/auth/user/delete"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := request_Auth_UserDelete_0(annotatedContext, inboundMarshaler, client, req, pathParams)
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
if err != nil {
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
return
}
forward_Auth_UserDelete_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle(http.MethodPost, pattern_Auth_UserChangePassword_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
annotatedContext, err := runtime.AnnotateContext(ctx, mux, req, "/etcdserverpb.Auth/UserChangePassword", runtime.WithHTTPPathPattern("/v3/auth/user/changepw"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := request_Auth_UserChangePassword_0(annotatedContext, inboundMarshaler, client, req, pathParams)
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
if err != nil {
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
return
}
forward_Auth_UserChangePassword_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle(http.MethodPost, pattern_Auth_UserGrantRole_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
annotatedContext, err := runtime.AnnotateContext(ctx, mux, req, "/etcdserverpb.Auth/UserGrantRole", runtime.WithHTTPPathPattern("/v3/auth/user/grant"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := request_Auth_UserGrantRole_0(annotatedContext, inboundMarshaler, client, req, pathParams)
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
if err != nil {
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
return
}
forward_Auth_UserGrantRole_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle(http.MethodPost, pattern_Auth_UserRevokeRole_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
annotatedContext, err := runtime.AnnotateContext(ctx, mux, req, "/etcdserverpb.Auth/UserRevokeRole", runtime.WithHTTPPathPattern("/v3/auth/user/revoke"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := request_Auth_UserRevokeRole_0(annotatedContext, inboundMarshaler, client, req, pathParams)
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
if err != nil {
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
return
}
forward_Auth_UserRevokeRole_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle(http.MethodPost, pattern_Auth_RoleAdd_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
annotatedContext, err := runtime.AnnotateContext(ctx, mux, req, "/etcdserverpb.Auth/RoleAdd", runtime.WithHTTPPathPattern("/v3/auth/role/add"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := request_Auth_RoleAdd_0(annotatedContext, inboundMarshaler, client, req, pathParams)
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
if err != nil {
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
return
}
forward_Auth_RoleAdd_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle(http.MethodPost, pattern_Auth_RoleGet_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
annotatedContext, err := runtime.AnnotateContext(ctx, mux, req, "/etcdserverpb.Auth/RoleGet", runtime.WithHTTPPathPattern("/v3/auth/role/get"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := request_Auth_RoleGet_0(annotatedContext, inboundMarshaler, client, req, pathParams)
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
if err != nil {
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
return
}
forward_Auth_RoleGet_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle(http.MethodPost, pattern_Auth_RoleList_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
annotatedContext, err := runtime.AnnotateContext(ctx, mux, req, "/etcdserverpb.Auth/RoleList", runtime.WithHTTPPathPattern("/v3/auth/role/list"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := request_Auth_RoleList_0(annotatedContext, inboundMarshaler, client, req, pathParams)
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
if err != nil {
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
return
}
forward_Auth_RoleList_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle(http.MethodPost, pattern_Auth_RoleDelete_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
annotatedContext, err := runtime.AnnotateContext(ctx, mux, req, "/etcdserverpb.Auth/RoleDelete", runtime.WithHTTPPathPattern("/v3/auth/role/delete"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := request_Auth_RoleDelete_0(annotatedContext, inboundMarshaler, client, req, pathParams)
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
if err != nil {
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
return
}
forward_Auth_RoleDelete_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle(http.MethodPost, pattern_Auth_RoleGrantPermission_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
annotatedContext, err := runtime.AnnotateContext(ctx, mux, req, "/etcdserverpb.Auth/RoleGrantPermission", runtime.WithHTTPPathPattern("/v3/auth/role/grant"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := request_Auth_RoleGrantPermission_0(annotatedContext, inboundMarshaler, client, req, pathParams)
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
if err != nil {
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
return
}
forward_Auth_RoleGrantPermission_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle(http.MethodPost, pattern_Auth_RoleRevokePermission_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
annotatedContext, err := runtime.AnnotateContext(ctx, mux, req, "/etcdserverpb.Auth/RoleRevokePermission", runtime.WithHTTPPathPattern("/v3/auth/role/revoke"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := request_Auth_RoleRevokePermission_0(annotatedContext, inboundMarshaler, client, req, pathParams)
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
if err != nil {
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
return
}
forward_Auth_RoleRevokePermission_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
return nil
}
var (
pattern_Auth_AuthEnable_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"v3", "auth", "enable"}, ""))
pattern_Auth_AuthDisable_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"v3", "auth", "disable"}, ""))
pattern_Auth_AuthStatus_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"v3", "auth", "status"}, ""))
pattern_Auth_Authenticate_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"v3", "auth", "authenticate"}, ""))
pattern_Auth_UserAdd_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"v3", "auth", "user", "add"}, ""))
pattern_Auth_UserGet_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"v3", "auth", "user", "get"}, ""))
pattern_Auth_UserList_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"v3", "auth", "user", "list"}, ""))
pattern_Auth_UserDelete_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"v3", "auth", "user", "delete"}, ""))
pattern_Auth_UserChangePassword_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"v3", "auth", "user", "changepw"}, ""))
pattern_Auth_UserGrantRole_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"v3", "auth", "user", "grant"}, ""))
pattern_Auth_UserRevokeRole_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"v3", "auth", "user", "revoke"}, ""))
pattern_Auth_RoleAdd_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"v3", "auth", "role", "add"}, ""))
pattern_Auth_RoleGet_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"v3", "auth", "role", "get"}, ""))
pattern_Auth_RoleList_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"v3", "auth", "role", "list"}, ""))
pattern_Auth_RoleDelete_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"v3", "auth", "role", "delete"}, ""))
pattern_Auth_RoleGrantPermission_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"v3", "auth", "role", "grant"}, ""))
pattern_Auth_RoleRevokePermission_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"v3", "auth", "role", "revoke"}, ""))
)
var (
forward_Auth_AuthEnable_0 = runtime.ForwardResponseMessage
forward_Auth_AuthDisable_0 = runtime.ForwardResponseMessage
forward_Auth_AuthStatus_0 = runtime.ForwardResponseMessage
forward_Auth_Authenticate_0 = runtime.ForwardResponseMessage
forward_Auth_UserAdd_0 = runtime.ForwardResponseMessage
forward_Auth_UserGet_0 = runtime.ForwardResponseMessage
forward_Auth_UserList_0 = runtime.ForwardResponseMessage
forward_Auth_UserDelete_0 = runtime.ForwardResponseMessage
forward_Auth_UserChangePassword_0 = runtime.ForwardResponseMessage
forward_Auth_UserGrantRole_0 = runtime.ForwardResponseMessage
forward_Auth_UserRevokeRole_0 = runtime.ForwardResponseMessage
forward_Auth_RoleAdd_0 = runtime.ForwardResponseMessage
forward_Auth_RoleGet_0 = runtime.ForwardResponseMessage
forward_Auth_RoleList_0 = runtime.ForwardResponseMessage
forward_Auth_RoleDelete_0 = runtime.ForwardResponseMessage
forward_Auth_RoleGrantPermission_0 = runtime.ForwardResponseMessage
forward_Auth_RoleRevokePermission_0 = runtime.ForwardResponseMessage
)
================================================
FILE: api/etcdserverpb/raft_internal.pb.go
================================================
// Code generated by protoc-gen-gogo. DO NOT EDIT.
// source: raft_internal.proto
package etcdserverpb
import (
fmt "fmt"
io "io"
math "math"
math_bits "math/bits"
proto "github.com/golang/protobuf/proto"
membershippb "go.etcd.io/etcd/api/v3/membershippb"
_ "go.etcd.io/etcd/api/v3/versionpb"
)
// Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal
var _ = fmt.Errorf
var _ = math.Inf
// This is a compile-time assertion to ensure that this generated file
// is compatible with the proto package it is being compiled against.
// A compilation error at this line likely means your copy of the
// proto package needs to be updated.
const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
type RequestHeader struct {
ID uint64 `protobuf:"varint,1,opt,name=ID,proto3" json:"ID,omitempty"`
// username is a username that is associated with an auth token of gRPC connection
Username string `protobuf:"bytes,2,opt,name=username,proto3" json:"username,omitempty"`
// auth_revision is a revision number of auth.authStore. It is not related to mvcc
AuthRevision uint64 `protobuf:"varint,3,opt,name=auth_revision,json=authRevision,proto3" json:"auth_revision,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *RequestHeader) Reset() { *m = RequestHeader{} }
func (m *RequestHeader) String() string { return proto.CompactTextString(m) }
func (*RequestHeader) ProtoMessage() {}
func (*RequestHeader) Descriptor() ([]byte, []int) {
return fileDescriptor_b4c9a9be0cfca103, []int{0}
}
func (m *RequestHeader) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
}
func (m *RequestHeader) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
if deterministic {
return xxx_messageInfo_RequestHeader.Marshal(b, m, deterministic)
} else {
b = b[:cap(b)]
n, err := m.MarshalToSizedBuffer(b)
if err != nil {
return nil, err
}
return b[:n], nil
}
}
func (m *RequestHeader) XXX_Merge(src proto.Message) {
xxx_messageInfo_RequestHeader.Merge(m, src)
}
func (m *RequestHeader) XXX_Size() int {
return m.Size()
}
func (m *RequestHeader) XXX_DiscardUnknown() {
xxx_messageInfo_RequestHeader.DiscardUnknown(m)
}
var xxx_messageInfo_RequestHeader proto.InternalMessageInfo
func (m *RequestHeader) GetID() uint64 {
if m != nil {
return m.ID
}
return 0
}
func (m *RequestHeader) GetUsername() string {
if m != nil {
return m.Username
}
return ""
}
func (m *RequestHeader) GetAuthRevision() uint64 {
if m != nil {
return m.AuthRevision
}
return 0
}
// An InternalRaftRequest is the union of all requests which can be
// sent via raft.
type InternalRaftRequest struct {
Header *RequestHeader `protobuf:"bytes,100,opt,name=header,proto3" json:"header,omitempty"`
ID uint64 `protobuf:"varint,1,opt,name=ID,proto3" json:"ID,omitempty"`
Range *RangeRequest `protobuf:"bytes,3,opt,name=range,proto3" json:"range,omitempty"`
Put *PutRequest `protobuf:"bytes,4,opt,name=put,proto3" json:"put,omitempty"`
DeleteRange *DeleteRangeRequest `protobuf:"bytes,5,opt,name=delete_range,json=deleteRange,proto3" json:"delete_range,omitempty"`
Txn *TxnRequest `protobuf:"bytes,6,opt,name=txn,proto3" json:"txn,omitempty"`
Compaction *CompactionRequest `protobuf:"bytes,7,opt,name=compaction,proto3" json:"compaction,omitempty"`
LeaseGrant *LeaseGrantRequest `protobuf:"bytes,8,opt,name=lease_grant,json=leaseGrant,proto3" json:"lease_grant,omitempty"`
LeaseRevoke *LeaseRevokeRequest `protobuf:"bytes,9,opt,name=lease_revoke,json=leaseRevoke,proto3" json:"lease_revoke,omitempty"`
Alarm *AlarmRequest `protobuf:"bytes,10,opt,name=alarm,proto3" json:"alarm,omitempty"`
LeaseCheckpoint *LeaseCheckpointRequest `protobuf:"bytes,11,opt,name=lease_checkpoint,json=leaseCheckpoint,proto3" json:"lease_checkpoint,omitempty"`
AuthEnable *AuthEnableRequest `protobuf:"bytes,1000,opt,name=auth_enable,json=authEnable,proto3" json:"auth_enable,omitempty"`
AuthDisable *AuthDisableRequest `protobuf:"bytes,1011,opt,name=auth_disable,json=authDisable,proto3" json:"auth_disable,omitempty"`
AuthStatus *AuthStatusRequest `protobuf:"bytes,1013,opt,name=auth_status,json=authStatus,proto3" json:"auth_status,omitempty"`
Authenticate *InternalAuthenticateRequest `protobuf:"bytes,1012,opt,name=authenticate,proto3" json:"authenticate,omitempty"`
AuthUserAdd *AuthUserAddRequest `protobuf:"bytes,1100,opt,name=auth_user_add,json=authUserAdd,proto3" json:"auth_user_add,omitempty"`
AuthUserDelete *AuthUserDeleteRequest `protobuf:"bytes,1101,opt,name=auth_user_delete,json=authUserDelete,proto3" json:"auth_user_delete,omitempty"`
AuthUserGet *AuthUserGetRequest `protobuf:"bytes,1102,opt,name=auth_user_get,json=authUserGet,proto3" json:"auth_user_get,omitempty"`
AuthUserChangePassword *AuthUserChangePasswordRequest `protobuf:"bytes,1103,opt,name=auth_user_change_password,json=authUserChangePassword,proto3" json:"auth_user_change_password,omitempty"`
AuthUserGrantRole *AuthUserGrantRoleRequest `protobuf:"bytes,1104,opt,name=auth_user_grant_role,json=authUserGrantRole,proto3" json:"auth_user_grant_role,omitempty"`
AuthUserRevokeRole *AuthUserRevokeRoleRequest `protobuf:"bytes,1105,opt,name=auth_user_revoke_role,json=authUserRevokeRole,proto3" json:"auth_user_revoke_role,omitempty"`
AuthUserList *AuthUserListRequest `protobuf:"bytes,1106,opt,name=auth_user_list,json=authUserList,proto3" json:"auth_user_list,omitempty"`
AuthRoleList *AuthRoleListRequest `protobuf:"bytes,1107,opt,name=auth_role_list,json=authRoleList,proto3" json:"auth_role_list,omitempty"`
AuthRoleAdd *AuthRoleAddRequest `protobuf:"bytes,1200,opt,name=auth_role_add,json=authRoleAdd,proto3" json:"auth_role_add,omitempty"`
AuthRoleDelete *AuthRoleDeleteRequest `protobuf:"bytes,1201,opt,name=auth_role_delete,json=authRoleDelete,proto3" json:"auth_role_delete,omitempty"`
AuthRoleGet *AuthRoleGetRequest `protobuf:"bytes,1202,opt,name=auth_role_get,json=authRoleGet,proto3" json:"auth_role_get,omitempty"`
AuthRoleGrantPermission *AuthRoleGrantPermissionRequest `protobuf:"bytes,1203,opt,name=auth_role_grant_permission,json=authRoleGrantPermission,proto3" json:"auth_role_grant_permission,omitempty"`
AuthRoleRevokePermission *AuthRoleRevokePermissionRequest `protobuf:"bytes,1204,opt,name=auth_role_revoke_permission,json=authRoleRevokePermission,proto3" json:"auth_role_revoke_permission,omitempty"`
ClusterVersionSet *membershippb.ClusterVersionSetRequest `protobuf:"bytes,1300,opt,name=cluster_version_set,json=clusterVersionSet,proto3" json:"cluster_version_set,omitempty"`
ClusterMemberAttrSet *membershippb.ClusterMemberAttrSetRequest `protobuf:"bytes,1301,opt,name=cluster_member_attr_set,json=clusterMemberAttrSet,proto3" json:"cluster_member_attr_set,omitempty"`
DowngradeInfoSet *membershippb.DowngradeInfoSetRequest `protobuf:"bytes,1302,opt,name=downgrade_info_set,json=downgradeInfoSet,proto3" json:"downgrade_info_set,omitempty"`
DowngradeVersionTest *DowngradeVersionTestRequest `protobuf:"bytes,9900,opt,name=downgrade_version_test,json=downgradeVersionTest,proto3" json:"downgrade_version_test,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *InternalRaftRequest) Reset() { *m = InternalRaftRequest{} }
func (m *InternalRaftRequest) String() string { return proto.CompactTextString(m) }
func (*InternalRaftRequest) ProtoMessage() {}
func (*InternalRaftRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_b4c9a9be0cfca103, []int{1}
}
func (m *InternalRaftRequest) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
}
func (m *InternalRaftRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
if deterministic {
return xxx_messageInfo_InternalRaftRequest.Marshal(b, m, deterministic)
} else {
b = b[:cap(b)]
n, err := m.MarshalToSizedBuffer(b)
if err != nil {
return nil, err
}
return b[:n], nil
}
}
func (m *InternalRaftRequest) XXX_Merge(src proto.Message) {
xxx_messageInfo_InternalRaftRequest.Merge(m, src)
}
func (m *InternalRaftRequest) XXX_Size() int {
return m.Size()
}
func (m *InternalRaftRequest) XXX_DiscardUnknown() {
xxx_messageInfo_InternalRaftRequest.DiscardUnknown(m)
}
var xxx_messageInfo_InternalRaftRequest proto.InternalMessageInfo
func (m *InternalRaftRequest) GetHeader() *RequestHeader {
if m != nil {
return m.Header
}
return nil
}
func (m *InternalRaftRequest) GetID() uint64 {
if m != nil {
return m.ID
}
return 0
}
func (m *InternalRaftRequest) GetRange() *RangeRequest {
if m != nil {
return m.Range
}
return nil
}
func (m *InternalRaftRequest) GetPut() *PutRequest {
if m != nil {
return m.Put
}
return nil
}
func (m *InternalRaftRequest) GetDeleteRange() *DeleteRangeRequest {
if m != nil {
return m.DeleteRange
}
return nil
}
func (m *InternalRaftRequest) GetTxn() *TxnRequest {
if m != nil {
return m.Txn
}
return nil
}
func (m *InternalRaftRequest) GetCompaction() *CompactionRequest {
if m != nil {
return m.Compaction
}
return nil
}
func (m *InternalRaftRequest) GetLeaseGrant() *LeaseGrantRequest {
if m != nil {
return m.LeaseGrant
}
return nil
}
func (m *InternalRaftRequest) GetLeaseRevoke() *LeaseRevokeRequest {
if m != nil {
return m.LeaseRevoke
}
return nil
}
func (m *InternalRaftRequest) GetAlarm() *AlarmRequest {
if m != nil {
return m.Alarm
}
return nil
}
func (m *InternalRaftRequest) GetLeaseCheckpoint() *LeaseCheckpointRequest {
if m != nil {
return m.LeaseCheckpoint
}
return nil
}
func (m *InternalRaftRequest) GetAuthEnable() *AuthEnableRequest {
if m != nil {
return m.AuthEnable
}
return nil
}
func (m *InternalRaftRequest) GetAuthDisable() *AuthDisableRequest {
if m != nil {
return m.AuthDisable
}
return nil
}
func (m *InternalRaftRequest) GetAuthStatus() *AuthStatusRequest {
if m != nil {
return m.AuthStatus
}
return nil
}
func (m *InternalRaftRequest) GetAuthenticate() *InternalAuthenticateRequest {
if m != nil {
return m.Authenticate
}
return nil
}
func (m *InternalRaftRequest) GetAuthUserAdd() *AuthUserAddRequest {
if m != nil {
return m.AuthUserAdd
}
return nil
}
func (m *InternalRaftRequest) GetAuthUserDelete() *AuthUserDeleteRequest {
if m != nil {
return m.AuthUserDelete
}
return nil
}
func (m *InternalRaftRequest) GetAuthUserGet() *AuthUserGetRequest {
if m != nil {
return m.AuthUserGet
}
return nil
}
func (m *InternalRaftRequest) GetAuthUserChangePassword() *AuthUserChangePasswordRequest {
if m != nil {
return m.AuthUserChangePassword
}
return nil
}
func (m *InternalRaftRequest) GetAuthUserGrantRole() *AuthUserGrantRoleRequest {
if m != nil {
return m.AuthUserGrantRole
}
return nil
}
func (m *InternalRaftRequest) GetAuthUserRevokeRole() *AuthUserRevokeRoleRequest {
if m != nil {
return m.AuthUserRevokeRole
}
return nil
}
func (m *InternalRaftRequest) GetAuthUserList() *AuthUserListRequest {
if m != nil {
return m.AuthUserList
}
return nil
}
func (m *InternalRaftRequest) GetAuthRoleList() *AuthRoleListRequest {
if m != nil {
return m.AuthRoleList
}
return nil
}
func (m *InternalRaftRequest) GetAuthRoleAdd() *AuthRoleAddRequest {
if m != nil {
return m.AuthRoleAdd
}
return nil
}
func (m *InternalRaftRequest) GetAuthRoleDelete() *AuthRoleDeleteRequest {
if m != nil {
return m.AuthRoleDelete
}
return nil
}
func (m *InternalRaftRequest) GetAuthRoleGet() *AuthRoleGetRequest {
if m != nil {
return m.AuthRoleGet
}
return nil
}
func (m *InternalRaftRequest) GetAuthRoleGrantPermission() *AuthRoleGrantPermissionRequest {
if m != nil {
return m.AuthRoleGrantPermission
}
return nil
}
func (m *InternalRaftRequest) GetAuthRoleRevokePermission() *AuthRoleRevokePermissionRequest {
if m != nil {
return m.AuthRoleRevokePermission
}
return nil
}
func (m *InternalRaftRequest) GetClusterVersionSet() *membershippb.ClusterVersionSetRequest {
if m != nil {
return m.ClusterVersionSet
}
return nil
}
func (m *InternalRaftRequest) GetClusterMemberAttrSet() *membershippb.ClusterMemberAttrSetRequest {
if m != nil {
return m.ClusterMemberAttrSet
}
return nil
}
func (m *InternalRaftRequest) GetDowngradeInfoSet() *membershippb.DowngradeInfoSetRequest {
if m != nil {
return m.DowngradeInfoSet
}
return nil
}
func (m *InternalRaftRequest) GetDowngradeVersionTest() *DowngradeVersionTestRequest {
if m != nil {
return m.DowngradeVersionTest
}
return nil
}
type EmptyResponse struct {
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *EmptyResponse) Reset() { *m = EmptyResponse{} }
func (m *EmptyResponse) String() string { return proto.CompactTextString(m) }
func (*EmptyResponse) ProtoMessage() {}
func (*EmptyResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_b4c9a9be0cfca103, []int{2}
}
func (m *EmptyResponse) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
}
func (m *EmptyResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
if deterministic {
return xxx_messageInfo_EmptyResponse.Marshal(b, m, deterministic)
} else {
b = b[:cap(b)]
n, err := m.MarshalToSizedBuffer(b)
if err != nil {
return nil, err
}
return b[:n], nil
}
}
func (m *EmptyResponse) XXX_Merge(src proto.Message) {
xxx_messageInfo_EmptyResponse.Merge(m, src)
}
func (m *EmptyResponse) XXX_Size() int {
return m.Size()
}
func (m *EmptyResponse) XXX_DiscardUnknown() {
xxx_messageInfo_EmptyResponse.DiscardUnknown(m)
}
var xxx_messageInfo_EmptyResponse proto.InternalMessageInfo
// What is the difference between AuthenticateRequest (defined in rpc.proto) and InternalAuthenticateRequest?
// InternalAuthenticateRequest has a member that is filled by etcdserver and shouldn't be user-facing.
// For avoiding misusage the field, we have an internal version of AuthenticateRequest.
type InternalAuthenticateRequest struct {
Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
Password string `protobuf:"bytes,2,opt,name=password,proto3" json:"password,omitempty"`
// simple_token is generated in API layer (etcdserver/v3_server.go)
SimpleToken string `protobuf:"bytes,3,opt,name=simple_token,json=simpleToken,proto3" json:"simple_token,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *InternalAuthenticateRequest) Reset() { *m = InternalAuthenticateRequest{} }
func (m *InternalAuthenticateRequest) String() string { return proto.CompactTextString(m) }
func (*InternalAuthenticateRequest) ProtoMessage() {}
func (*InternalAuthenticateRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_b4c9a9be0cfca103, []int{3}
}
func (m *InternalAuthenticateRequest) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
}
func (m *InternalAuthenticateRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
if deterministic {
return xxx_messageInfo_InternalAuthenticateRequest.Marshal(b, m, deterministic)
} else {
b = b[:cap(b)]
n, err := m.MarshalToSizedBuffer(b)
if err != nil {
return nil, err
}
return b[:n], nil
}
}
func (m *InternalAuthenticateRequest) XXX_Merge(src proto.Message) {
xxx_messageInfo_InternalAuthenticateRequest.Merge(m, src)
}
func (m *InternalAuthenticateRequest) XXX_Size() int {
return m.Size()
}
func (m *InternalAuthenticateRequest) XXX_DiscardUnknown() {
xxx_messageInfo_InternalAuthenticateRequest.DiscardUnknown(m)
}
var xxx_messageInfo_InternalAuthenticateRequest proto.InternalMessageInfo
func (m *InternalAuthenticateRequest) GetName() string {
if m != nil {
return m.Name
}
return ""
}
func (m *InternalAuthenticateRequest) GetPassword() string {
if m != nil {
return m.Password
}
return ""
}
func (m *InternalAuthenticateRequest) GetSimpleToken() string {
if m != nil {
return m.SimpleToken
}
return ""
}
func init() {
proto.RegisterType((*RequestHeader)(nil), "etcdserverpb.RequestHeader")
proto.RegisterType((*InternalRaftRequest)(nil), "etcdserverpb.InternalRaftRequest")
proto.RegisterType((*EmptyResponse)(nil), "etcdserverpb.EmptyResponse")
proto.RegisterType((*InternalAuthenticateRequest)(nil), "etcdserverpb.InternalAuthenticateRequest")
}
func init() { proto.RegisterFile("raft_internal.proto", fileDescriptor_b4c9a9be0cfca103) }
var fileDescriptor_b4c9a9be0cfca103 = []byte{
// 1077 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x7c, 0x56, 0x4d, 0x73, 0x1b, 0x45,
0x10, 0x45, 0xb6, 0x63, 0x5b, 0x23, 0xdb, 0x71, 0xc6, 0x26, 0x19, 0xec, 0x2a, 0xe3, 0x38, 0x10,
0x0c, 0x04, 0x39, 0xd8, 0x84, 0x03, 0x17, 0x50, 0x2c, 0x97, 0xe3, 0x54, 0x92, 0x72, 0x6d, 0x0c,
0x95, 0x82, 0xa2, 0x96, 0xd1, 0x6e, 0x5b, 0xda, 0x78, 0xb5, 0xbb, 0xcc, 0x8c, 0x1c, 0xe7, 0xca,
0x91, 0x33, 0x50, 0xf9, 0x11, 0x1c, 0xf8, 0xfc, 0x0f, 0x1c, 0xf8, 0x08, 0xf0, 0x07, 0x28, 0x73,
0xe1, 0x0e, 0xdc, 0x53, 0xf3, 0xb1, 0xb3, 0x5a, 0x69, 0xe4, 0x9b, 0xb6, 0xfb, 0xf5, 0x7b, 0x6f,
0x66, 0xbb, 0x57, 0x8d, 0x16, 0x18, 0x3d, 0x14, 0x7e, 0x94, 0x08, 0x60, 0x09, 0x8d, 0xeb, 0x19,
0x4b, 0x45, 0x8a, 0x67, 0x40, 0x04, 0x21, 0x07, 0x76, 0x0c, 0x2c, 0x6b, 0x2d, 0x55, 0x59, 0x16,
0xe8, 0xc4, 0xd2, 0xaa, 0x4c, 0x6c, 0xd0, 0x2c, 0xda, 0x38, 0x06, 0xc6, 0xa3, 0x34, 0xc9, 0x5a,
0xf9, 0x2f, 0x83, 0xb8, 0x6a, 0x11, 0x5d, 0xe8, 0xb6, 0x80, 0xf1, 0x4e, 0x94, 0x65, 0xad, 0xbe,
0x07, 0x8d, 0x5b, 0x63, 0x68, 0xd6, 0x83, 0x4f, 0x7b, 0xc0, 0xc5, 0x2d, 0xa0, 0x21, 0x30, 0x3c,
0x87, 0xc6, 0xf6, 0x9a, 0xa4, 0xb2, 0x5a, 0x59, 0x9f, 0xf0, 0xc6, 0xf6, 0x9a, 0x78, 0x09, 0x4d,
0xf7, 0xb8, 0x34, 0xd5, 0x05, 0x32, 0xb6, 0x5a, 0x59, 0xaf, 0x7a, 0xf6, 0x19, 0x5f, 0x43, 0xb3,
0xb4, 0x27, 0x3a, 0x3e, 0x83, 0xe3, 0x48, 0x6a, 0x93, 0x71, 0x59, 0x76, 0x73, 0xea, 0xf3, 0x1f,
0xc9, 0xf8, 0x56, 0xfd, 0x4d, 0x6f, 0x46, 0x66, 0x3d, 0x93, 0x7c, 0x67, 0xea, 0x33, 0x15, 0xbe,
0xbe, 0xf6, 0x64, 0x01, 0x2d, 0xec, 0x99, 0x93, 0x7a, 0xf4, 0x50, 0x18, 0x03, 0x78, 0x0b, 0x4d,
0x76, 0x94, 0x09, 0x12, 0xae, 0x56, 0xd6, 0x6b, 0x9b, 0xcb, 0xf5, 0xfe, 0xf3, 0xd7, 0x4b, 0x3e,
0x3d, 0x03, 0x1d, 0xf2, 0x7b, 0x1d, 0x9d, 0x63, 0x34, 0x69, 0x83, 0xf2, 0x52, 0xdb, 0x5c, 0x1a,
0xe0, 0x90, 0x29, 0x43, 0xe4, 0x69, 0x20, 0x7e, 0x0d, 0x8d, 0x67, 0x3d, 0x41, 0x26, 0x14, 0x9e,
0x94, 0xf1, 0xfb, 0xbd, 0xdc, 0x9d, 0x27, 0x41, 0x78, 0x1b, 0xcd, 0x84, 0x10, 0x83, 0x00, 0x5f,
0x8b, 0x9c, 0x53, 0x45, 0xab, 0xe5, 0xa2, 0xa6, 0x42, 0x94, 0xa4, 0x6a, 0x61, 0x11, 0x93, 0x82,
0xe2, 0x24, 0x21, 0x93, 0x2e, 0xc1, 0x83, 0x93, 0xc4, 0x0a, 0x8a, 0x93, 0x04, 0xbf, 0x8b, 0x50,
0x90, 0x76, 0x33, 0x1a, 0x08, 0x79, 0xbf, 0x53, 0xaa, 0xe4, 0xc5, 0x72, 0xc9, 0xb6, 0xcd, 0xe7,
0x95, 0x7d, 0x25, 0xf8, 0x3d, 0x54, 0x8b, 0x81, 0x72, 0xf0, 0xdb, 0x8c, 0x26, 0x82, 0x4c, 0xbb,
0x18, 0xee, 0x48, 0xc0, 0xae, 0xcc, 0x5b, 0x86, 0xd8, 0x86, 0xe4, 0x99, 0x35, 0x03, 0x83, 0xe3,
0xf4, 0x08, 0x48, 0xd5, 0x75, 0x66, 0x45, 0xe1, 0x29, 0x80, 0x3d, 0x73, 0x5c, 0xc4, 0xe4, 0x6b,
0xa1, 0x31, 0x65, 0x5d, 0x82, 0x5c, 0xaf, 0xa5, 0x21, 0x53, 0xf6, 0xb5, 0x28, 0x20, 0x7e, 0x80,
0xe6, 0xb5, 0x6c, 0xd0, 0x81, 0xe0, 0x28, 0x4b, 0xa3, 0x44, 0x90, 0x9a, 0x2a, 0x7e, 0xc9, 0x21,
0xbd, 0x6d, 0x41, 0x86, 0x26, 0xef, 0xc2, 0xb7, 0xbc, 0xf3, 0x71, 0x19, 0x80, 0x1b, 0xa8, 0xa6,
0xda, 0x16, 0x12, 0xda, 0x8a, 0x81, 0xfc, 0xe3, 0xbc, 0xd5, 0x46, 0x4f, 0x74, 0x76, 0x14, 0xc0,
0xde, 0x09, 0xb5, 0x21, 0xdc, 0x44, 0xaa, 0xb7, 0xfd, 0x30, 0xe2, 0x8a, 0xe3, 0xdf, 0x29, 0xd7,
0xa5, 0x48, 0x8e, 0xa6, 0x46, 0xd8, 0x4b, 0xa1, 0x45, 0x0c, 0xdf, 0x36, 0x46, 0xb8, 0xa0, 0xa2,
0xc7, 0xc9, 0xff, 0x23, 0x8d, 0xdc, 0x57, 0x80, 0x81, 0x93, 0xdd, 0xd0, 0x8e, 0x74, 0x0e, 0xdf,
0xd3, 0x8e, 0x20, 0x11, 0x51, 0x40, 0x05, 0x90, 0xff, 0x34, 0xd9, 0xab, 0x65, 0xb2, 0x7c, 0xec,
0x1a, 0x7d, 0xd0, 0xdc, 0x5a, 0xa9, 0x1e, 0xef, 0x98, 0xd9, 0x96, 0xc3, 0xee, 0xd3, 0x30, 0x24,
0x3f, 0x4f, 0x8f, 0x3a, 0xe2, 0xfb, 0x1c, 0x58, 0x23, 0x0c, 0x4b, 0x47, 0x34, 0x31, 0x7c, 0x0f,
0xcd, 0x17, 0x34, 0x7a, 0x08, 0xc8, 0x2f, 0x9a, 0xe9, 0x8a, 0x9b, 0xc9, 0x4c, 0x8f, 0x21, 0x9b,
0xa3, 0xa5, 0x70, 0xd9, 0x56, 0x1b, 0x04, 0xf9, 0xf5, 0x4c, 0x5b, 0xbb, 0x20, 0x86, 0x6c, 0xed,
0x82, 0xc0, 0x6d, 0xf4, 0x42, 0x41, 0x13, 0x74, 0xe4, 0x58, 0xfa, 0x19, 0xe5, 0xfc, 0x51, 0xca,
0x42, 0xf2, 0x9b, 0xa6, 0x7c, 0xdd, 0x4d, 0xb9, 0xad, 0xd0, 0xfb, 0x06, 0x9c, 0xb3, 0x5f, 0xa4,
0xce, 0x34, 0x7e, 0x80, 0x16, 0xfb, 0xfc, 0xca, 0x79, 0xf2, 0x59, 0x1a, 0x03, 0x79, 0xaa, 0x35,
0xae, 0x8e, 0xb0, 0xad, 0x66, 0x31, 0x2d, 0xda, 0xe6, 0x02, 0x1d, 0xcc, 0xe0, 0x8f, 0xd0, 0xf3,
0x05, 0xb3, 0x1e, 0x4d, 0x4d, 0xfd, 0xbb, 0xa6, 0x7e, 0xc5, 0x4d, 0x6d, 0x66, 0xb4, 0x8f, 0x1b,
0xd3, 0xa1, 0x14, 0xbe, 0x85, 0xe6, 0x0a, 0xf2, 0x38, 0xe2, 0x82, 0xfc, 0xa1, 0x59, 0x2f, 0xbb,
0x59, 0xef, 0x44, 0x5c, 0x94, 0xfa, 0x28, 0x0f, 0x5a, 0x26, 0x69, 0x4d, 0x33, 0xfd, 0x39, 0x92,
0x49, 0x4a, 0x0f, 0x31, 0xe5, 0x41, 0xfb, 0xea, 0x15, 0x93, 0xec, 0xc8, 0x6f, 0xaa, 0xa3, 0x5e,
0xbd, 0xac, 0x19, 0xec, 0x48, 0x13, 0xb3, 0x1d, 0xa9, 0x68, 0x4c, 0x47, 0x7e, 0x5b, 0x1d, 0xd5,
0x91, 0xb2, 0xca, 0xd1, 0x91, 0x45, 0xb8, 0x6c, 0x4b, 0x76, 0xe4, 0x77, 0x67, 0xda, 0x1a, 0xec,
0x48, 0x13, 0xc3, 0x0f, 0xd1, 0x52, 0x1f, 0x8d, 0x6a, 0x94, 0x0c, 0x58, 0x37, 0xe2, 0xea, 0x8f,
0xf5, 0x7b, 0xcd, 0x79, 0x6d, 0x04, 0xa7, 0x84, 0xef, 0x5b, 0x74, 0xce, 0x7f, 0x89, 0xba, 0xf3,
0xb8, 0x8b, 0x96, 0x0b, 0x2d, 0xd3, 0x3a, 0x7d, 0x62, 0x3f, 0x68, 0xb1, 0x37, 0xdc, 0x62, 0xba,
0x4b, 0x86, 0xd5, 0x08, 0x1d, 0x01, 0xc0, 0x9f, 0xa0, 0x85, 0x20, 0xee, 0x71, 0x01, 0xcc, 0x37,
0x4b, 0x8a, 0xcf, 0x41, 0x90, 0x2f, 0x90, 0x19, 0x81, 0xfe, 0x0d, 0xa5, 0xbe, 0xad, 0x91, 0x1f,
0x68, 0xe0, 0x7d, 0x10, 0x43, 0x5f, 0xbd, 0x0b, 0xc1, 0x20, 0x04, 0x3f, 0x44, 0x97, 0x72, 0x05,
0x4d, 0xe6, 0x53, 0x21, 0x98, 0x52, 0xf9, 0x12, 0x99, 0xef, 0xa0, 0x4b, 0xe5, 0xae, 0x8a, 0x35,
0x84, 0x60, 0x2e, 0xa1, 0xc5, 0xc0, 0x81, 0xc2, 0x1f, 0x23, 0x1c, 0xa6, 0x8f, 0x92, 0x36, 0xa3,
0x21, 0xf8, 0x51, 0x72, 0x98, 0x2a, 0x99, 0xaf, 0xb4, 0xcc, 0xcb, 0x65, 0x99, 0x66, 0x0e, 0xdc,
0x4b, 0x0e, 0x53, 0x97, 0xc4, 0x7c, 0x38, 0x80, 0xc0, 0x11, 0xba, 0x58, 0xd0, 0xe7, 0xd7, 0x25,
0x80, 0x0b, 0xf2, 0xf5, 0x5d, 0xd7, 0x17, 0xdd, 0x4a, 0x98, 0xeb, 0x38, 0x00, 0x3e, 0x28, 0xf3,
0xb6, 0xb7, 0x18, 0x3a, 0x50, 0x76, 0x21, 0xbb, 0x3d, 0x31, 0x3d, 0x36, 0x3f, 0xee, 0x8d, 0x1d,
0x6f, 0xae, 0x9d, 0x47, 0xb3, 0x3b, 0xdd, 0x4c, 0x3c, 0xf6, 0x80, 0x67, 0x69, 0xc2, 0x61, 0xed,
0x31, 0x5a, 0x3e, 0xe3, 0x3f, 0x03, 0x63, 0x34, 0xa1, 0x36, 0xc3, 0x8a, 0xda, 0x0c, 0xd5, 0x6f,
0xb9, 0x31, 0xda, 0x4f, 0xa9, 0xd9, 0x18, 0xf3, 0x67, 0x7c, 0x19, 0xcd, 0xf0, 0xa8, 0x9b, 0xc5,
0xe0, 0x8b, 0xf4, 0x08, 0xf4, 0xc2, 0x58, 0xf5, 0x6a, 0x3a, 0x76, 0x20, 0x43, 0xd6, 0xd5, 0xcd,
0x1b, 0x3f, 0x9d, 0xae, 0x54, 0x9e, 0x9e, 0xae, 0x54, 0xfe, 0x3a, 0x5d, 0xa9, 0x3c, 0xf9, 0x7b,
0xe5, 0xb9, 0x0f, 0xaf, 0xb4, 0x53, 0x75, 0xf8, 0x7a, 0x94, 0x6e, 0x14, 0x1b, 0xf0, 0xd6, 0x46,
0xff, 0x85, 0xb4, 0x26, 0xd5, 0x62, 0xbb, 0xf5, 0x2c, 0x00, 0x00, 0xff, 0xff, 0x7f, 0x47, 0x30,
0xbe, 0x52, 0x0b, 0x00, 0x00,
}
func (m *RequestHeader) Marshal() (dAtA []byte, err error) {
size := m.Size()
dAtA = make([]byte, size)
n, err := m.MarshalToSizedBuffer(dAtA[:size])
if err != nil {
return nil, err
}
return dAtA[:n], nil
}
func (m *RequestHeader) MarshalTo(dAtA []byte) (int, error) {
size := m.Size()
return m.MarshalToSizedBuffer(dAtA[:size])
}
func (m *RequestHeader) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA)
_ = i
var l int
_ = l
if m.XXX_unrecognized != nil {
i -= len(m.XXX_unrecognized)
copy(dAtA[i:], m.XXX_unrecognized)
}
if m.AuthRevision != 0 {
i = encodeVarintRaftInternal(dAtA, i, uint64(m.AuthRevision))
i--
dAtA[i] = 0x18
}
if len(m.Username) > 0 {
i -= len(m.Username)
copy(dAtA[i:], m.Username)
i = encodeVarintRaftInternal(dAtA, i, uint64(len(m.Username)))
i--
dAtA[i] = 0x12
}
if m.ID != 0 {
i = encodeVarintRaftInternal(dAtA, i, uint64(m.ID))
i--
dAtA[i] = 0x8
}
return len(dAtA) - i, nil
}
func (m *InternalRaftRequest) Marshal() (dAtA []byte, err error) {
size := m.Size()
dAtA = make([]byte, size)
n, err := m.MarshalToSizedBuffer(dAtA[:size])
if err != nil {
return nil, err
}
return dAtA[:n], nil
}
func (m *InternalRaftRequest) MarshalTo(dAtA []byte) (int, error) {
size := m.Size()
return m.MarshalToSizedBuffer(dAtA[:size])
}
func (m *InternalRaftRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA)
_ = i
var l int
_ = l
if m.XXX_unrecognized != nil {
i -= len(m.XXX_unrecognized)
copy(dAtA[i:], m.XXX_unrecognized)
}
if m.DowngradeVersionTest != nil {
{
size, err := m.DowngradeVersionTest.MarshalToSizedBuffer(dAtA[:i])
if err != nil {
return 0, err
}
i -= size
i = encodeVarintRaftInternal(dAtA, i, uint64(size))
}
i--
dAtA[i] = 0x4
i--
dAtA[i] = 0xea
i--
dAtA[i] = 0xe2
}
if m.DowngradeInfoSet != nil {
{
size, err := m.DowngradeInfoSet.MarshalToSizedBuffer(dAtA[:i])
if err != nil {
return 0, err
}
i -= size
i = encodeVarintRaftInternal(dAtA, i, uint64(size))
}
i--
dAtA[i] = 0x51
i--
dAtA[i] = 0xb2
}
if m.ClusterMemberAttrSet != nil {
{
size, err := m.ClusterMemberAttrSet.MarshalToSizedBuffer(dAtA[:i])
if err != nil {
return 0, err
}
i -= size
i = encodeVarintRaftInternal(dAtA, i, uint64(size))
}
i--
dAtA[i] = 0x51
i--
dAtA[i] = 0xaa
}
if m.ClusterVersionSet != nil {
{
size, err := m.ClusterVersionSet.MarshalToSizedBuffer(dAtA[:i])
if err != nil {
return 0, err
}
i -= size
i = encodeVarintRaftInternal(dAtA, i, uint64(size))
}
i--
dAtA[i] = 0x51
i--
dAtA[i] = 0xa2
}
if m.AuthRoleRevokePermission != nil {
{
size, err := m.AuthRoleRevokePermission.MarshalToSizedBuffer(dAtA[:i])
if err != nil {
return 0, err
}
i -= size
i = encodeVarintRaftInternal(dAtA, i, uint64(size))
}
i--
dAtA[i] = 0x4b
i--
dAtA[i] = 0xa2
}
if m.AuthRoleGrantPermission != nil {
{
size, err := m.AuthRoleGrantPermission.MarshalToSizedBuffer(dAtA[:i])
if err != nil {
return 0, err
}
i -= size
i = encodeVarintRaftInternal(dAtA, i, uint64(size))
}
i--
dAtA[i] = 0x4b
i--
dAtA[i] = 0x9a
}
if m.AuthRoleGet != nil {
{
size, err := m.AuthRoleGet.MarshalToSizedBuffer(dAtA[:i])
if err != nil {
return 0, err
}
i -= size
i = encodeVarintRaftInternal(dAtA, i, uint64(size))
}
i--
dAtA[i] = 0x4b
i--
dAtA[i] = 0x92
}
if m.AuthRoleDelete != nil {
{
size, err := m.AuthRoleDelete.MarshalToSizedBuffer(dAtA[:i])
if err != nil {
return 0, err
}
i -= size
i = encodeVarintRaftInternal(dAtA, i, uint64(size))
}
i--
dAtA[i] = 0x4b
i--
dAtA[i] = 0x8a
}
if m.AuthRoleAdd != nil {
{
size, err := m.AuthRoleAdd.MarshalToSizedBuffer(dAtA[:i])
if err != nil {
return 0, err
}
i -= size
i = encodeVarintRaftInternal(dAtA, i, uint64(size))
}
i--
dAtA[i] = 0x4b
i--
dAtA[i] = 0x82
}
if m.AuthRoleList != nil {
{
size, err := m.AuthRoleList.MarshalToSizedBuffer(dAtA[:i])
if err != nil {
return 0, err
}
i -= size
i = encodeVarintRaftInternal(dAtA, i, uint64(size))
}
i--
dAtA[i] = 0x45
i--
dAtA[i] = 0x9a
}
if m.AuthUserList != nil {
{
size, err := m.AuthUserList.MarshalToSizedBuffer(dAtA[:i])
if err != nil {
return 0, err
}
i -= size
i = encodeVarintRaftInternal(dAtA, i, uint64(size))
}
i--
dAtA[i] = 0x45
i--
dAtA[i] = 0x92
}
if m.AuthUserRevokeRole != nil {
{
size, err := m.AuthUserRevokeRole.MarshalToSizedBuffer(dAtA[:i])
if err != nil {
return 0, err
}
i -= size
i = encodeVarintRaftInternal(dAtA, i, uint64(size))
}
i--
dAtA[i] = 0x45
i--
dAtA[i] = 0x8a
}
if m.AuthUserGrantRole != nil {
{
size, err := m.AuthUserGrantRole.MarshalToSizedBuffer(dAtA[:i])
if err != nil {
return 0, err
}
i -= size
i = encodeVarintRaftInternal(dAtA, i, uint64(size))
}
i--
dAtA[i] = 0x45
i--
dAtA[i] = 0x82
}
if m.AuthUserChangePassword != nil {
{
size, err := m.AuthUserChangePassword.MarshalToSizedBuffer(dAtA[:i])
if err != nil {
return 0, err
}
i -= size
i = encodeVarintRaftInternal(dAtA, i, uint64(size))
}
i--
dAtA[i] = 0x44
i--
dAtA[i] = 0xfa
}
if m.AuthUserGet != nil {
{
size, err := m.AuthUserGet.MarshalToSizedBuffer(dAtA[:i])
if err != nil {
return 0, err
}
i -= size
i = encodeVarintRaftInternal(dAtA, i, uint64(size))
}
i--
dAtA[i] = 0x44
i--
dAtA[i] = 0xf2
}
if m.AuthUserDelete != nil {
{
size, err := m.AuthUserDelete.MarshalToSizedBuffer(dAtA[:i])
if err != nil {
return 0, err
}
i -= size
i = encodeVarintRaftInternal(dAtA, i, uint64(size))
}
i--
dAtA[i] = 0x44
i--
dAtA[i] = 0xea
}
if m.AuthUserAdd != nil {
{
size, err := m.AuthUserAdd.MarshalToSizedBuffer(dAtA[:i])
if err != nil {
return 0, err
}
i -= size
i = encodeVarintRaftInternal(dAtA, i, uint64(size))
}
i--
dAtA[i] = 0x44
i--
dAtA[i] = 0xe2
}
if m.AuthStatus != nil {
{
size, err := m.AuthStatus.MarshalToSizedBuffer(dAtA[:i])
if err != nil {
return 0, err
}
i -= size
i = encodeVarintRaftInternal(dAtA, i, uint64(size))
}
i--
dAtA[i] = 0x3f
i--
dAtA[i] = 0xaa
}
if m.Authenticate != nil {
{
size, err := m.Authenticate.MarshalToSizedBuffer(dAtA[:i])
if err != nil {
return 0, err
}
i -= size
i = encodeVarintRaftInternal(dAtA, i, uint64(size))
}
i--
dAtA[i] = 0x3f
i--
dAtA[i] = 0xa2
}
if m.AuthDisable != nil {
{
size, err := m.AuthDisable.MarshalToSizedBuffer(dAtA[:i])
if err != nil {
return 0, err
}
i -= size
i = encodeVarintRaftInternal(dAtA, i, uint64(size))
}
i--
dAtA[i] = 0x3f
i--
dAtA[i] = 0x9a
}
if m.AuthEnable != nil {
{
size, err := m.AuthEnable.MarshalToSizedBuffer(dAtA[:i])
if err != nil {
return 0, err
}
i -= size
i = encodeVarintRaftInternal(dAtA, i, uint64(size))
}
i--
dAtA[i] = 0x3e
i--
dAtA[i] = 0xc2
}
if m.Header != nil {
{
size, err := m.Header.MarshalToSizedBuffer(dAtA[:i])
if err != nil {
return 0, err
}
i -= size
i = encodeVarintRaftInternal(dAtA, i, uint64(size))
}
i--
dAtA[i] = 0x6
i--
dAtA[i] = 0xa2
}
if m.LeaseCheckpoint != nil {
{
size, err := m.LeaseCheckpoint.MarshalToSizedBuffer(dAtA[:i])
if err != nil {
return 0, err
}
i -= size
i = encodeVarintRaftInternal(dAtA, i, uint64(size))
}
i--
dAtA[i] = 0x5a
}
if m.Alarm != nil {
{
size, err := m.Alarm.MarshalToSizedBuffer(dAtA[:i])
if err != nil {
return 0, err
}
i -= size
i = encodeVarintRaftInternal(dAtA, i, uint64(size))
}
i--
dAtA[i] = 0x52
}
if m.LeaseRevoke != nil {
{
size, err := m.LeaseRevoke.MarshalToSizedBuffer(dAtA[:i])
if err != nil {
return 0, err
}
i -= size
i = encodeVarintRaftInternal(dAtA, i, uint64(size))
}
i--
dAtA[i] = 0x4a
}
if m.LeaseGrant != nil {
{
size, err := m.LeaseGrant.MarshalToSizedBuffer(dAtA[:i])
if err != nil {
return 0, err
}
i -= size
i = encodeVarintRaftInternal(dAtA, i, uint64(size))
}
i--
dAtA[i] = 0x42
}
if m.Compaction != nil {
{
size, err := m.Compaction.MarshalToSizedBuffer(dAtA[:i])
if err != nil {
return 0, err
}
i -= size
i = encodeVarintRaftInternal(dAtA, i, uint64(size))
}
i--
dAtA[i] = 0x3a
}
if m.Txn != nil {
{
size, err := m.Txn.MarshalToSizedBuffer(dAtA[:i])
if err != nil {
return 0, err
}
i -= size
i = encodeVarintRaftInternal(dAtA, i, uint64(size))
}
i--
dAtA[i] = 0x32
}
if m.DeleteRange != nil {
{
size, err := m.DeleteRange.MarshalToSizedBuffer(dAtA[:i])
if err != nil {
return 0, err
}
i -= size
i = encodeVarintRaftInternal(dAtA, i, uint64(size))
}
i--
dAtA[i] = 0x2a
}
if m.Put != nil {
{
size, err := m.Put.MarshalToSizedBuffer(dAtA[:i])
if err != nil {
return 0, err
}
i -= size
i = encodeVarintRaftInternal(dAtA, i, uint64(size))
}
i--
dAtA[i] = 0x22
}
if m.Range != nil {
{
size, err := m.Range.MarshalToSizedBuffer(dAtA[:i])
if err != nil {
return 0, err
}
i -= size
i = encodeVarintRaftInternal(dAtA, i, uint64(size))
}
i--
dAtA[i] = 0x1a
}
if m.ID != 0 {
i = encodeVarintRaftInternal(dAtA, i, uint64(m.ID))
i--
dAtA[i] = 0x8
}
return len(dAtA) - i, nil
}
func (m *EmptyResponse) Marshal() (dAtA []byte, err error) {
size := m.Size()
dAtA = make([]byte, size)
n, err := m.MarshalToSizedBuffer(dAtA[:size])
if err != nil {
return nil, err
}
return dAtA[:n], nil
}
func (m *EmptyResponse) MarshalTo(dAtA []byte) (int, error) {
size := m.Size()
return m.MarshalToSizedBuffer(dAtA[:size])
}
func (m *EmptyResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA)
_ = i
var l int
_ = l
if m.XXX_unrecognized != nil {
i -= len(m.XXX_unrecognized)
copy(dAtA[i:], m.XXX_unrecognized)
}
return len(dAtA) - i, nil
}
func (m *InternalAuthenticateRequest) Marshal() (dAtA []byte, err error) {
size := m.Size()
dAtA = make([]byte, size)
n, err := m.MarshalToSizedBuffer(dAtA[:size])
if err != nil {
return nil, err
}
return dAtA[:n], nil
}
func (m *InternalAuthenticateRequest) MarshalTo(dAtA []byte) (int, error) {
size := m.Size()
return m.MarshalToSizedBuffer(dAtA[:size])
}
func (m *InternalAuthenticateRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA)
_ = i
var l int
_ = l
if m.XXX_unrecognized != nil {
i -= len(m.XXX_unrecognized)
copy(dAtA[i:], m.XXX_unrecognized)
}
if len(m.SimpleToken) > 0 {
i -= len(m.SimpleToken)
copy(dAtA[i:], m.SimpleToken)
i = encodeVarintRaftInternal(dAtA, i, uint64(len(m.SimpleToken)))
i--
dAtA[i] = 0x1a
}
if len(m.Password) > 0 {
i -= len(m.Password)
copy(dAtA[i:], m.Password)
i = encodeVarintRaftInternal(dAtA, i, uint64(len(m.Password)))
i--
dAtA[i] = 0x12
}
if len(m.Name) > 0 {
i -= len(m.Name)
copy(dAtA[i:], m.Name)
i = encodeVarintRaftInternal(dAtA, i, uint64(len(m.Name)))
i--
dAtA[i] = 0xa
}
return len(dAtA) - i, nil
}
func encodeVarintRaftInternal(dAtA []byte, offset int, v uint64) int {
offset -= sovRaftInternal(v)
base := offset
for v >= 1<<7 {
dAtA[offset] = uint8(v&0x7f | 0x80)
v >>= 7
offset++
}
dAtA[offset] = uint8(v)
return base
}
func (m *RequestHeader) Size() (n int) {
if m == nil {
return 0
}
var l int
_ = l
if m.ID != 0 {
n += 1 + sovRaftInternal(uint64(m.ID))
}
l = len(m.Username)
if l > 0 {
n += 1 + l + sovRaftInternal(uint64(l))
}
if m.AuthRevision != 0 {
n += 1 + sovRaftInternal(uint64(m.AuthRevision))
}
if m.XXX_unrecognized != nil {
n += len(m.XXX_unrecognized)
}
return n
}
func (m *InternalRaftRequest) Size() (n int) {
if m == nil {
return 0
}
var l int
_ = l
if m.ID != 0 {
n += 1 + sovRaftInternal(uint64(m.ID))
}
if m.Range != nil {
l = m.Range.Size()
n += 1 + l + sovRaftInternal(uint64(l))
}
if m.Put != nil {
l = m.Put.Size()
n += 1 + l + sovRaftInternal(uint64(l))
}
if m.DeleteRange != nil {
l = m.DeleteRange.Size()
n += 1 + l + sovRaftInternal(uint64(l))
}
if m.Txn != nil {
l = m.Txn.Size()
n += 1 + l + sovRaftInternal(uint64(l))
}
if m.Compaction != nil {
l = m.Compaction.Size()
n += 1 + l + sovRaftInternal(uint64(l))
}
if m.LeaseGrant != nil {
l = m.LeaseGrant.Size()
n += 1 + l + sovRaftInternal(uint64(l))
}
if m.LeaseRevoke != nil {
l = m.LeaseRevoke.Size()
n += 1 + l + sovRaftInternal(uint64(l))
}
if m.Alarm != nil {
l = m.Alarm.Size()
n += 1 + l + sovRaftInternal(uint64(l))
}
if m.LeaseCheckpoint != nil {
l = m.LeaseCheckpoint.Size()
n += 1 + l + sovRaftInternal(uint64(l))
}
if m.Header != nil {
l = m.Header.Size()
n += 2 + l + sovRaftInternal(uint64(l))
}
if m.AuthEnable != nil {
l = m.AuthEnable.Size()
n += 2 + l + sovRaftInternal(uint64(l))
}
if m.AuthDisable != nil {
l = m.AuthDisable.Size()
n += 2 + l + sovRaftInternal(uint64(l))
}
if m.Authenticate != nil {
l = m.Authenticate.Size()
n += 2 + l + sovRaftInternal(uint64(l))
}
if m.AuthStatus != nil {
l = m.AuthStatus.Size()
n += 2 + l + sovRaftInternal(uint64(l))
}
if m.AuthUserAdd != nil {
l = m.AuthUserAdd.Size()
n += 2 + l + sovRaftInternal(uint64(l))
}
if m.AuthUserDelete != nil {
l = m.AuthUserDelete.Size()
n += 2 + l + sovRaftInternal(uint64(l))
}
if m.AuthUserGet != nil {
l = m.AuthUserGet.Size()
n += 2 + l + sovRaftInternal(uint64(l))
}
if m.AuthUserChangePassword != nil {
l = m.AuthUserChangePassword.Size()
n += 2 + l + sovRaftInternal(uint64(l))
}
if m.AuthUserGrantRole != nil {
l = m.AuthUserGrantRole.Size()
n += 2 + l + sovRaftInternal(uint64(l))
}
if m.AuthUserRevokeRole != nil {
l = m.AuthUserRevokeRole.Size()
n += 2 + l + sovRaftInternal(uint64(l))
}
if m.AuthUserList != nil {
l = m.AuthUserList.Size()
n += 2 + l + sovRaftInternal(uint64(l))
}
if m.AuthRoleList != nil {
l = m.AuthRoleList.Size()
n += 2 + l + sovRaftInternal(uint64(l))
}
if m.AuthRoleAdd != nil {
l = m.AuthRoleAdd.Size()
n += 2 + l + sovRaftInternal(uint64(l))
}
if m.AuthRoleDelete != nil {
l = m.AuthRoleDelete.Size()
n += 2 + l + sovRaftInternal(uint64(l))
}
if m.AuthRoleGet != nil {
l = m.AuthRoleGet.Size()
n += 2 + l + sovRaftInternal(uint64(l))
}
if m.AuthRoleGrantPermission != nil {
l = m.AuthRoleGrantPermission.Size()
n += 2 + l + sovRaftInternal(uint64(l))
}
if m.AuthRoleRevokePermission != nil {
l = m.AuthRoleRevokePermission.Size()
n += 2 + l + sovRaftInternal(uint64(l))
}
if m.ClusterVersionSet != nil {
l = m.ClusterVersionSet.Size()
n += 2 + l + sovRaftInternal(uint64(l))
}
if m.ClusterMemberAttrSet != nil {
l = m.ClusterMemberAttrSet.Size()
n += 2 + l + sovRaftInternal(uint64(l))
}
if m.DowngradeInfoSet != nil {
l = m.DowngradeInfoSet.Size()
n += 2 + l + sovRaftInternal(uint64(l))
}
if m.DowngradeVersionTest != nil {
l = m.DowngradeVersionTest.Size()
n += 3 + l + sovRaftInternal(uint64(l))
}
if m.XXX_unrecognized != nil {
n += len(m.XXX_unrecognized)
}
return n
}
func (m *EmptyResponse) Size() (n int) {
if m == nil {
return 0
}
var l int
_ = l
if m.XXX_unrecognized != nil {
n += len(m.XXX_unrecognized)
}
return n
}
func (m *InternalAuthenticateRequest) Size() (n int) {
if m == nil {
return 0
}
var l int
_ = l
l = len(m.Name)
if l > 0 {
n += 1 + l + sovRaftInternal(uint64(l))
}
l = len(m.Password)
if l > 0 {
n += 1 + l + sovRaftInternal(uint64(l))
}
l = len(m.SimpleToken)
if l > 0 {
n += 1 + l + sovRaftInternal(uint64(l))
}
if m.XXX_unrecognized != nil {
n += len(m.XXX_unrecognized)
}
return n
}
func sovRaftInternal(x uint64) (n int) {
return (math_bits.Len64(x|1) + 6) / 7
}
func sozRaftInternal(x uint64) (n int) {
return sovRaftInternal(uint64((x << 1) ^ uint64((int64(x) >> 63))))
}
func (m *RequestHeader) Unmarshal(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0
for iNdEx < l {
preIndex := iNdEx
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRaftInternal
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
fieldNum := int32(wire >> 3)
wireType := int(wire & 0x7)
if wireType == 4 {
return fmt.Errorf("proto: RequestHeader: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: RequestHeader: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
case 1:
if wireType != 0 {
return fmt.Errorf("proto: wrong wireType = %d for field ID", wireType)
}
m.ID = 0
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRaftInternal
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
m.ID |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
case 2:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Username", wireType)
}
var stringLen uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRaftInternal
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
stringLen |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
intStringLen := int(stringLen)
if intStringLen < 0 {
return ErrInvalidLengthRaftInternal
}
postIndex := iNdEx + intStringLen
if postIndex < 0 {
return ErrInvalidLengthRaftInternal
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.Username = string(dAtA[iNdEx:postIndex])
iNdEx = postIndex
case 3:
if wireType != 0 {
return fmt.Errorf("proto: wrong wireType = %d for field AuthRevision", wireType)
}
m.AuthRevision = 0
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRaftInternal
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
m.AuthRevision |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
default:
iNdEx = preIndex
skippy, err := skipRaftInternal(dAtA[iNdEx:])
if err != nil {
return err
}
if (skippy < 0) || (iNdEx+skippy) < 0 {
return ErrInvalidLengthRaftInternal
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
iNdEx += skippy
}
}
if iNdEx > l {
return io.ErrUnexpectedEOF
}
return nil
}
func (m *InternalRaftRequest) Unmarshal(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0
for iNdEx < l {
preIndex := iNdEx
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRaftInternal
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
fieldNum := int32(wire >> 3)
wireType := int(wire & 0x7)
if wireType == 4 {
return fmt.Errorf("proto: InternalRaftRequest: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: InternalRaftRequest: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
case 1:
if wireType != 0 {
return fmt.Errorf("proto: wrong wireType = %d for field ID", wireType)
}
m.ID = 0
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRaftInternal
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
m.ID |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
case 3:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Range", wireType)
}
var msglen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRaftInternal
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
msglen |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
if msglen < 0 {
return ErrInvalidLengthRaftInternal
}
postIndex := iNdEx + msglen
if postIndex < 0 {
return ErrInvalidLengthRaftInternal
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
if m.Range == nil {
m.Range = &RangeRequest{}
}
if err := m.Range.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
return err
}
iNdEx = postIndex
case 4:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Put", wireType)
}
var msglen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRaftInternal
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
msglen |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
if msglen < 0 {
return ErrInvalidLengthRaftInternal
}
postIndex := iNdEx + msglen
if postIndex < 0 {
return ErrInvalidLengthRaftInternal
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
if m.Put == nil {
m.Put = &PutRequest{}
}
if err := m.Put.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
return err
}
iNdEx = postIndex
case 5:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field DeleteRange", wireType)
}
var msglen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRaftInternal
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
msglen |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
if msglen < 0 {
return ErrInvalidLengthRaftInternal
}
postIndex := iNdEx + msglen
if postIndex < 0 {
return ErrInvalidLengthRaftInternal
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
if m.DeleteRange == nil {
m.DeleteRange = &DeleteRangeRequest{}
}
if err := m.DeleteRange.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
return err
}
iNdEx = postIndex
case 6:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Txn", wireType)
}
var msglen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRaftInternal
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
msglen |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
if msglen < 0 {
return ErrInvalidLengthRaftInternal
}
postIndex := iNdEx + msglen
if postIndex < 0 {
return ErrInvalidLengthRaftInternal
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
if m.Txn == nil {
m.Txn = &TxnRequest{}
}
if err := m.Txn.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
return err
}
iNdEx = postIndex
case 7:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Compaction", wireType)
}
var msglen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRaftInternal
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
msglen |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
if msglen < 0 {
return ErrInvalidLengthRaftInternal
}
postIndex := iNdEx + msglen
if postIndex < 0 {
return ErrInvalidLengthRaftInternal
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
if m.Compaction == nil {
m.Compaction = &CompactionRequest{}
}
if err := m.Compaction.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
return err
}
iNdEx = postIndex
case 8:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field LeaseGrant", wireType)
}
var msglen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRaftInternal
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
msglen |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
if msglen < 0 {
return ErrInvalidLengthRaftInternal
}
postIndex := iNdEx + msglen
if postIndex < 0 {
return ErrInvalidLengthRaftInternal
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
if m.LeaseGrant == nil {
m.LeaseGrant = &LeaseGrantRequest{}
}
if err := m.LeaseGrant.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
return err
}
iNdEx = postIndex
case 9:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field LeaseRevoke", wireType)
}
var msglen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRaftInternal
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
msglen |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
if msglen < 0 {
return ErrInvalidLengthRaftInternal
}
postIndex := iNdEx + msglen
if postIndex < 0 {
return ErrInvalidLengthRaftInternal
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
if m.LeaseRevoke == nil {
m.LeaseRevoke = &LeaseRevokeRequest{}
}
if err := m.LeaseRevoke.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
return err
}
iNdEx = postIndex
case 10:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Alarm", wireType)
}
var msglen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRaftInternal
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
msglen |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
if msglen < 0 {
return ErrInvalidLengthRaftInternal
}
postIndex := iNdEx + msglen
if postIndex < 0 {
return ErrInvalidLengthRaftInternal
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
if m.Alarm == nil {
m.Alarm = &AlarmRequest{}
}
if err := m.Alarm.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
return err
}
iNdEx = postIndex
case 11:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field LeaseCheckpoint", wireType)
}
var msglen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRaftInternal
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
msglen |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
if msglen < 0 {
return ErrInvalidLengthRaftInternal
}
postIndex := iNdEx + msglen
if postIndex < 0 {
return ErrInvalidLengthRaftInternal
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
if m.LeaseCheckpoint == nil {
m.LeaseCheckpoint = &LeaseCheckpointRequest{}
}
if err := m.LeaseCheckpoint.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
return err
}
iNdEx = postIndex
case 100:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Header", wireType)
}
var msglen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRaftInternal
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
msglen |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
if msglen < 0 {
return ErrInvalidLengthRaftInternal
}
postIndex := iNdEx + msglen
if postIndex < 0 {
return ErrInvalidLengthRaftInternal
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
if m.Header == nil {
m.Header = &RequestHeader{}
}
if err := m.Header.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
return err
}
iNdEx = postIndex
case 1000:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field AuthEnable", wireType)
}
var msglen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRaftInternal
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
msglen |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
if msglen < 0 {
return ErrInvalidLengthRaftInternal
}
postIndex := iNdEx + msglen
if postIndex < 0 {
return ErrInvalidLengthRaftInternal
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
if m.AuthEnable == nil {
m.AuthEnable = &AuthEnableRequest{}
}
if err := m.AuthEnable.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
return err
}
iNdEx = postIndex
case 1011:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field AuthDisable", wireType)
}
var msglen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRaftInternal
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
msglen |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
if msglen < 0 {
return ErrInvalidLengthRaftInternal
}
postIndex := iNdEx + msglen
if postIndex < 0 {
return ErrInvalidLengthRaftInternal
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
if m.AuthDisable == nil {
m.AuthDisable = &AuthDisableRequest{}
}
if err := m.AuthDisable.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
return err
}
iNdEx = postIndex
case 1012:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Authenticate", wireType)
}
var msglen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRaftInternal
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
msglen |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
if msglen < 0 {
return ErrInvalidLengthRaftInternal
}
postIndex := iNdEx + msglen
if postIndex < 0 {
return ErrInvalidLengthRaftInternal
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
if m.Authenticate == nil {
m.Authenticate = &InternalAuthenticateRequest{}
}
if err := m.Authenticate.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
return err
}
iNdEx = postIndex
case 1013:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field AuthStatus", wireType)
}
var msglen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRaftInternal
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
msglen |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
if msglen < 0 {
return ErrInvalidLengthRaftInternal
}
postIndex := iNdEx + msglen
if postIndex < 0 {
return ErrInvalidLengthRaftInternal
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
if m.AuthStatus == nil {
m.AuthStatus = &AuthStatusRequest{}
}
if err := m.AuthStatus.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
return err
}
iNdEx = postIndex
case 1100:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field AuthUserAdd", wireType)
}
var msglen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRaftInternal
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
msglen |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
if msglen < 0 {
return ErrInvalidLengthRaftInternal
}
postIndex := iNdEx + msglen
if postIndex < 0 {
return ErrInvalidLengthRaftInternal
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
if m.AuthUserAdd == nil {
m.AuthUserAdd = &AuthUserAddRequest{}
}
if err := m.AuthUserAdd.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
return err
}
iNdEx = postIndex
case 1101:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field AuthUserDelete", wireType)
}
var msglen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRaftInternal
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
msglen |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
if msglen < 0 {
return ErrInvalidLengthRaftInternal
}
postIndex := iNdEx + msglen
if postIndex < 0 {
return ErrInvalidLengthRaftInternal
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
if m.AuthUserDelete == nil {
m.AuthUserDelete = &AuthUserDeleteRequest{}
}
if err := m.AuthUserDelete.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
return err
}
iNdEx = postIndex
case 1102:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field AuthUserGet", wireType)
}
var msglen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRaftInternal
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
msglen |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
if msglen < 0 {
return ErrInvalidLengthRaftInternal
}
postIndex := iNdEx + msglen
if postIndex < 0 {
return ErrInvalidLengthRaftInternal
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
if m.AuthUserGet == nil {
m.AuthUserGet = &AuthUserGetRequest{}
}
if err := m.AuthUserGet.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
return err
}
iNdEx = postIndex
case 1103:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field AuthUserChangePassword", wireType)
}
var msglen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRaftInternal
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
msglen |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
if msglen < 0 {
return ErrInvalidLengthRaftInternal
}
postIndex := iNdEx + msglen
if postIndex < 0 {
return ErrInvalidLengthRaftInternal
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
if m.AuthUserChangePassword == nil {
m.AuthUserChangePassword = &AuthUserChangePasswordRequest{}
}
if err := m.AuthUserChangePassword.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
return err
}
iNdEx = postIndex
case 1104:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field AuthUserGrantRole", wireType)
}
var msglen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRaftInternal
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
msglen |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
if msglen < 0 {
return ErrInvalidLengthRaftInternal
}
postIndex := iNdEx + msglen
if postIndex < 0 {
return ErrInvalidLengthRaftInternal
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
if m.AuthUserGrantRole == nil {
m.AuthUserGrantRole = &AuthUserGrantRoleRequest{}
}
if err := m.AuthUserGrantRole.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
return err
}
iNdEx = postIndex
case 1105:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field AuthUserRevokeRole", wireType)
}
var msglen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRaftInternal
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
msglen |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
if msglen < 0 {
return ErrInvalidLengthRaftInternal
}
postIndex := iNdEx + msglen
if postIndex < 0 {
return ErrInvalidLengthRaftInternal
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
if m.AuthUserRevokeRole == nil {
m.AuthUserRevokeRole = &AuthUserRevokeRoleRequest{}
}
if err := m.AuthUserRevokeRole.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
return err
}
iNdEx = postIndex
case 1106:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field AuthUserList", wireType)
}
var msglen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRaftInternal
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
msglen |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
if msglen < 0 {
return ErrInvalidLengthRaftInternal
}
postIndex := iNdEx + msglen
if postIndex < 0 {
return ErrInvalidLengthRaftInternal
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
if m.AuthUserList == nil {
m.AuthUserList = &AuthUserListRequest{}
}
if err := m.AuthUserList.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
return err
}
iNdEx = postIndex
case 1107:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field AuthRoleList", wireType)
}
var msglen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRaftInternal
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
msglen |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
if msglen < 0 {
return ErrInvalidLengthRaftInternal
}
postIndex := iNdEx + msglen
if postIndex < 0 {
return ErrInvalidLengthRaftInternal
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
if m.AuthRoleList == nil {
m.AuthRoleList = &AuthRoleListRequest{}
}
if err := m.AuthRoleList.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
return err
}
iNdEx = postIndex
case 1200:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field AuthRoleAdd", wireType)
}
var msglen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRaftInternal
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
msglen |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
if msglen < 0 {
return ErrInvalidLengthRaftInternal
}
postIndex := iNdEx + msglen
if postIndex < 0 {
return ErrInvalidLengthRaftInternal
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
if m.AuthRoleAdd == nil {
m.AuthRoleAdd = &AuthRoleAddRequest{}
}
if err := m.AuthRoleAdd.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
return err
}
iNdEx = postIndex
case 1201:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field AuthRoleDelete", wireType)
}
var msglen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRaftInternal
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
msglen |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
if msglen < 0 {
return ErrInvalidLengthRaftInternal
}
postIndex := iNdEx + msglen
if postIndex < 0 {
return ErrInvalidLengthRaftInternal
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
if m.AuthRoleDelete == nil {
m.AuthRoleDelete = &AuthRoleDeleteRequest{}
}
if err := m.AuthRoleDelete.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
return err
}
iNdEx = postIndex
case 1202:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field AuthRoleGet", wireType)
}
var msglen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRaftInternal
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
msglen |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
if msglen < 0 {
return ErrInvalidLengthRaftInternal
}
postIndex := iNdEx + msglen
if postIndex < 0 {
return ErrInvalidLengthRaftInternal
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
if m.AuthRoleGet == nil {
m.AuthRoleGet = &AuthRoleGetRequest{}
}
if err := m.AuthRoleGet.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
return err
}
iNdEx = postIndex
case 1203:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field AuthRoleGrantPermission", wireType)
}
var msglen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRaftInternal
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
msglen |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
if msglen < 0 {
return ErrInvalidLengthRaftInternal
}
postIndex := iNdEx + msglen
if postIndex < 0 {
return ErrInvalidLengthRaftInternal
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
if m.AuthRoleGrantPermission == nil {
m.AuthRoleGrantPermission = &AuthRoleGrantPermissionRequest{}
}
if err := m.AuthRoleGrantPermission.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
return err
}
iNdEx = postIndex
case 1204:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field AuthRoleRevokePermission", wireType)
}
var msglen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRaftInternal
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
msglen |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
if msglen < 0 {
return ErrInvalidLengthRaftInternal
}
postIndex := iNdEx + msglen
if postIndex < 0 {
return ErrInvalidLengthRaftInternal
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
if m.AuthRoleRevokePermission == nil {
m.AuthRoleRevokePermission = &AuthRoleRevokePermissionRequest{}
}
if err := m.AuthRoleRevokePermission.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
return err
}
iNdEx = postIndex
case 1300:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field ClusterVersionSet", wireType)
}
var msglen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRaftInternal
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
msglen |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
if msglen < 0 {
return ErrInvalidLengthRaftInternal
}
postIndex := iNdEx + msglen
if postIndex < 0 {
return ErrInvalidLengthRaftInternal
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
if m.ClusterVersionSet == nil {
m.ClusterVersionSet = &membershippb.ClusterVersionSetRequest{}
}
if err := m.ClusterVersionSet.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
return err
}
iNdEx = postIndex
case 1301:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field ClusterMemberAttrSet", wireType)
}
var msglen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRaftInternal
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
msglen |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
if msglen < 0 {
return ErrInvalidLengthRaftInternal
}
postIndex := iNdEx + msglen
if postIndex < 0 {
return ErrInvalidLengthRaftInternal
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
if m.ClusterMemberAttrSet == nil {
m.ClusterMemberAttrSet = &membershippb.ClusterMemberAttrSetRequest{}
}
if err := m.ClusterMemberAttrSet.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
return err
}
iNdEx = postIndex
case 1302:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field DowngradeInfoSet", wireType)
}
var msglen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRaftInternal
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
msglen |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
if msglen < 0 {
return ErrInvalidLengthRaftInternal
}
postIndex := iNdEx + msglen
if postIndex < 0 {
return ErrInvalidLengthRaftInternal
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
if m.DowngradeInfoSet == nil {
m.DowngradeInfoSet = &membershippb.DowngradeInfoSetRequest{}
}
if err := m.DowngradeInfoSet.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
return err
}
iNdEx = postIndex
case 9900:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field DowngradeVersionTest", wireType)
}
var msglen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRaftInternal
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
msglen |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
if msglen < 0 {
return ErrInvalidLengthRaftInternal
}
postIndex := iNdEx + msglen
if postIndex < 0 {
return ErrInvalidLengthRaftInternal
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
if m.DowngradeVersionTest == nil {
m.DowngradeVersionTest = &DowngradeVersionTestRequest{}
}
if err := m.DowngradeVersionTest.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
return err
}
iNdEx = postIndex
default:
iNdEx = preIndex
skippy, err := skipRaftInternal(dAtA[iNdEx:])
if err != nil {
return err
}
if (skippy < 0) || (iNdEx+skippy) < 0 {
return ErrInvalidLengthRaftInternal
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
iNdEx += skippy
}
}
if iNdEx > l {
return io.ErrUnexpectedEOF
}
return nil
}
func (m *EmptyResponse) Unmarshal(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0
for iNdEx < l {
preIndex := iNdEx
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRaftInternal
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
fieldNum := int32(wire >> 3)
wireType := int(wire & 0x7)
if wireType == 4 {
return fmt.Errorf("proto: EmptyResponse: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: EmptyResponse: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
default:
iNdEx = preIndex
skippy, err := skipRaftInternal(dAtA[iNdEx:])
if err != nil {
return err
}
if (skippy < 0) || (iNdEx+skippy) < 0 {
return ErrInvalidLengthRaftInternal
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
iNdEx += skippy
}
}
if iNdEx > l {
return io.ErrUnexpectedEOF
}
return nil
}
func (m *InternalAuthenticateRequest) Unmarshal(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0
for iNdEx < l {
preIndex := iNdEx
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRaftInternal
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
fieldNum := int32(wire >> 3)
wireType := int(wire & 0x7)
if wireType == 4 {
return fmt.Errorf("proto: InternalAuthenticateRequest: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: InternalAuthenticateRequest: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
case 1:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType)
}
var stringLen uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRaftInternal
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
stringLen |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
intStringLen := int(stringLen)
if intStringLen < 0 {
return ErrInvalidLengthRaftInternal
}
postIndex := iNdEx + intStringLen
if postIndex < 0 {
return ErrInvalidLengthRaftInternal
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.Name = string(dAtA[iNdEx:postIndex])
iNdEx = postIndex
case 2:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Password", wireType)
}
var stringLen uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRaftInternal
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
stringLen |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
intStringLen := int(stringLen)
if intStringLen < 0 {
return ErrInvalidLengthRaftInternal
}
postIndex := iNdEx + intStringLen
if postIndex < 0 {
return ErrInvalidLengthRaftInternal
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.Password = string(dAtA[iNdEx:postIndex])
iNdEx = postIndex
case 3:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field SimpleToken", wireType)
}
var stringLen uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRaftInternal
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
stringLen |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
intStringLen := int(stringLen)
if intStringLen < 0 {
return ErrInvalidLengthRaftInternal
}
postIndex := iNdEx + intStringLen
if postIndex < 0 {
return ErrInvalidLengthRaftInternal
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.SimpleToken = string(dAtA[iNdEx:postIndex])
iNdEx = postIndex
default:
iNdEx = preIndex
skippy, err := skipRaftInternal(dAtA[iNdEx:])
if err != nil {
return err
}
if (skippy < 0) || (iNdEx+skippy) < 0 {
return ErrInvalidLengthRaftInternal
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
iNdEx += skippy
}
}
if iNdEx > l {
return io.ErrUnexpectedEOF
}
return nil
}
func skipRaftInternal(dAtA []byte) (n int, err error) {
l := len(dAtA)
iNdEx := 0
depth := 0
for iNdEx < l {
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return 0, ErrIntOverflowRaftInternal
}
if iNdEx >= l {
return 0, io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= (uint64(b) & 0x7F) << shift
if b < 0x80 {
break
}
}
wireType := int(wire & 0x7)
switch wireType {
case 0:
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return 0, ErrIntOverflowRaftInternal
}
if iNdEx >= l {
return 0, io.ErrUnexpectedEOF
}
iNdEx++
if dAtA[iNdEx-1] < 0x80 {
break
}
}
case 1:
iNdEx += 8
case 2:
var length int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return 0, ErrIntOverflowRaftInternal
}
if iNdEx >= l {
return 0, io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
length |= (int(b) & 0x7F) << shift
if b < 0x80 {
break
}
}
if length < 0 {
return 0, ErrInvalidLengthRaftInternal
}
iNdEx += length
case 3:
depth++
case 4:
if depth == 0 {
return 0, ErrUnexpectedEndOfGroupRaftInternal
}
depth--
case 5:
iNdEx += 4
default:
return 0, fmt.Errorf("proto: illegal wireType %d", wireType)
}
if iNdEx < 0 {
return 0, ErrInvalidLengthRaftInternal
}
if depth == 0 {
return iNdEx, nil
}
}
return 0, io.ErrUnexpectedEOF
}
var (
ErrInvalidLengthRaftInternal = fmt.Errorf("proto: negative length found during unmarshaling")
ErrIntOverflowRaftInternal = fmt.Errorf("proto: integer overflow")
ErrUnexpectedEndOfGroupRaftInternal = fmt.Errorf("proto: unexpected end of group")
)
================================================
FILE: api/etcdserverpb/raft_internal.proto
================================================
syntax = "proto3";
package etcdserverpb;
import "rpc.proto";
import "etcd/api/versionpb/version.proto";
import "etcd/api/membershippb/membership.proto";
option go_package = "go.etcd.io/etcd/api/v3/etcdserverpb";
message RequestHeader {
option (versionpb.etcd_version_msg) = "3.0";
uint64 ID = 1;
// username is a username that is associated with an auth token of gRPC connection
string username = 2;
// auth_revision is a revision number of auth.authStore. It is not related to mvcc
uint64 auth_revision = 3 [(versionpb.etcd_version_field) = "3.1"];
}
// An InternalRaftRequest is the union of all requests which can be
// sent via raft.
message InternalRaftRequest {
option (versionpb.etcd_version_msg) = "3.0";
RequestHeader header = 100;
uint64 ID = 1;
reserved 2;
reserved "v2";
RangeRequest range = 3;
PutRequest put = 4;
DeleteRangeRequest delete_range = 5;
TxnRequest txn = 6;
CompactionRequest compaction = 7;
LeaseGrantRequest lease_grant = 8;
LeaseRevokeRequest lease_revoke = 9;
AlarmRequest alarm = 10;
LeaseCheckpointRequest lease_checkpoint = 11 [(versionpb.etcd_version_field) = "3.4"];
AuthEnableRequest auth_enable = 1000;
AuthDisableRequest auth_disable = 1011;
AuthStatusRequest auth_status = 1013 [(versionpb.etcd_version_field) = "3.5"];
InternalAuthenticateRequest authenticate = 1012;
AuthUserAddRequest auth_user_add = 1100;
AuthUserDeleteRequest auth_user_delete = 1101;
AuthUserGetRequest auth_user_get = 1102;
AuthUserChangePasswordRequest auth_user_change_password = 1103;
AuthUserGrantRoleRequest auth_user_grant_role = 1104;
AuthUserRevokeRoleRequest auth_user_revoke_role = 1105;
AuthUserListRequest auth_user_list = 1106;
AuthRoleListRequest auth_role_list = 1107;
AuthRoleAddRequest auth_role_add = 1200;
AuthRoleDeleteRequest auth_role_delete = 1201;
AuthRoleGetRequest auth_role_get = 1202;
AuthRoleGrantPermissionRequest auth_role_grant_permission = 1203;
AuthRoleRevokePermissionRequest auth_role_revoke_permission = 1204;
membershippb.ClusterVersionSetRequest cluster_version_set = 1300 [(versionpb.etcd_version_field) = "3.5"];
membershippb.ClusterMemberAttrSetRequest cluster_member_attr_set = 1301 [(versionpb.etcd_version_field) = "3.5"];
membershippb.DowngradeInfoSetRequest downgrade_info_set = 1302 [(versionpb.etcd_version_field) = "3.5"];
DowngradeVersionTestRequest downgrade_version_test = 9900 [(versionpb.etcd_version_field) = "3.6"];
}
message EmptyResponse {
}
// What is the difference between AuthenticateRequest (defined in rpc.proto) and InternalAuthenticateRequest?
// InternalAuthenticateRequest has a member that is filled by etcdserver and shouldn't be user-facing.
// For avoiding misusage the field, we have an internal version of AuthenticateRequest.
message InternalAuthenticateRequest {
option (versionpb.etcd_version_msg) = "3.0";
string name = 1;
string password = 2;
// simple_token is generated in API layer (etcdserver/v3_server.go)
string simple_token = 3;
}
================================================
FILE: api/etcdserverpb/raft_internal_stringer.go
================================================
// Copyright 2018 The etcd Authors
//
// 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 etcdserverpb
import (
"fmt"
"strings"
proto "github.com/golang/protobuf/proto" //nolint:staticcheck // TODO: remove for a supported version
)
// InternalRaftStringer implements custom proto Stringer:
// redact password, replace value fields with value_size fields.
type InternalRaftStringer struct {
Request *InternalRaftRequest
}
func (as *InternalRaftStringer) String() string {
switch {
case as.Request.LeaseGrant != nil:
return fmt.Sprintf("header:<%s> lease_grant:",
as.Request.Header.String(),
as.Request.LeaseGrant.TTL,
as.Request.LeaseGrant.ID,
)
case as.Request.LeaseRevoke != nil:
return fmt.Sprintf("header:<%s> lease_revoke:",
as.Request.Header.String(),
as.Request.LeaseRevoke.ID,
)
case as.Request.Authenticate != nil:
return fmt.Sprintf("header:<%s> authenticate:",
as.Request.Header.String(),
as.Request.Authenticate.Name,
as.Request.Authenticate.SimpleToken,
)
case as.Request.AuthUserAdd != nil:
return fmt.Sprintf("header:<%s> auth_user_add:",
as.Request.Header.String(),
as.Request.AuthUserAdd.Name,
)
case as.Request.AuthUserChangePassword != nil:
return fmt.Sprintf("header:<%s> auth_user_change_password:",
as.Request.Header.String(),
as.Request.AuthUserChangePassword.Name,
)
case as.Request.Put != nil:
return fmt.Sprintf("header:<%s> put:<%s>",
as.Request.Header.String(),
NewLoggablePutRequest(as.Request.Put).String(),
)
case as.Request.Txn != nil:
return fmt.Sprintf("header:<%s> txn:<%s>",
as.Request.Header.String(),
NewLoggableTxnRequest(as.Request.Txn).String(),
)
default:
// nothing to redact
}
return as.Request.String()
}
// txnRequestStringer implements fmt.Stringer, a custom proto String to replace value bytes
// fields with value size fields in any nested txn and put operations.
type txnRequestStringer struct {
Request *TxnRequest
}
func NewLoggableTxnRequest(request *TxnRequest) fmt.Stringer {
return &txnRequestStringer{request}
}
func (as *txnRequestStringer) String() string {
var compare []string
for _, c := range as.Request.Compare {
switch cv := c.TargetUnion.(type) {
case *Compare_Value:
compare = append(compare, newLoggableValueCompare(c, cv).String())
default:
// nothing to redact
compare = append(compare, c.String())
}
}
var success []string
for _, s := range as.Request.Success {
success = append(success, newLoggableRequestOp(s).String())
}
var failure []string
for _, f := range as.Request.Failure {
failure = append(failure, newLoggableRequestOp(f).String())
}
return fmt.Sprintf("compare:<%s> success:<%s> failure:<%s>",
strings.Join(compare, " "),
strings.Join(success, " "),
strings.Join(failure, " "),
)
}
// requestOpStringer implements a custom proto String to replace value bytes fields with value
// size fields in any nested txn and put operations.
type requestOpStringer struct {
Op *RequestOp
}
func newLoggableRequestOp(op *RequestOp) *requestOpStringer {
return &requestOpStringer{op}
}
func (as *requestOpStringer) String() string {
switch op := as.Op.Request.(type) {
case *RequestOp_RequestPut:
return fmt.Sprintf("request_put:<%s>", NewLoggablePutRequest(op.RequestPut).String())
case *RequestOp_RequestTxn:
return fmt.Sprintf("request_txn:<%s>", NewLoggableTxnRequest(op.RequestTxn).String())
default:
// nothing to redact
}
return as.Op.String()
}
// loggableValueCompare implements a custom proto String for Compare.Value union member types to
// replace the value bytes field with a value size field.
// To preserve proto encoding of the key and range_end bytes, a faked out proto type is used here.
type loggableValueCompare struct {
Result Compare_CompareResult `protobuf:"varint,1,opt,name=result,proto3,enum=etcdserverpb.Compare_CompareResult"`
Target Compare_CompareTarget `protobuf:"varint,2,opt,name=target,proto3,enum=etcdserverpb.Compare_CompareTarget"`
Key []byte `protobuf:"bytes,3,opt,name=key,proto3"`
ValueSize int64 `protobuf:"varint,7,opt,name=value_size,proto3"`
RangeEnd []byte `protobuf:"bytes,64,opt,name=range_end,proto3"`
}
func newLoggableValueCompare(c *Compare, cv *Compare_Value) *loggableValueCompare {
return &loggableValueCompare{
c.Result,
c.Target,
c.Key,
int64(len(cv.Value)),
c.RangeEnd,
}
}
func (m *loggableValueCompare) Reset() { *m = loggableValueCompare{} }
func (m *loggableValueCompare) String() string { return proto.CompactTextString(m) }
func (*loggableValueCompare) ProtoMessage() {}
// loggablePutRequest implements proto.Message, a custom proto String to replace value bytes
// field with a value size field.
// To preserve proto encoding of the key bytes, a faked out proto type is used here.
type loggablePutRequest struct {
Key []byte `protobuf:"bytes,1,opt,name=key,proto3"`
ValueSize int64 `protobuf:"varint,2,opt,name=value_size,proto3"`
Lease int64 `protobuf:"varint,3,opt,name=lease,proto3"`
PrevKv bool `protobuf:"varint,4,opt,name=prev_kv,proto3"`
IgnoreValue bool `protobuf:"varint,5,opt,name=ignore_value,proto3"`
IgnoreLease bool `protobuf:"varint,6,opt,name=ignore_lease,proto3"`
}
func NewLoggablePutRequest(request *PutRequest) proto.Message {
return &loggablePutRequest{
request.Key,
int64(len(request.Value)),
request.Lease,
request.PrevKv,
request.IgnoreValue,
request.IgnoreLease,
}
}
func (m *loggablePutRequest) Reset() { *m = loggablePutRequest{} }
func (m *loggablePutRequest) String() string { return proto.CompactTextString(m) }
func (*loggablePutRequest) ProtoMessage() {}
================================================
FILE: api/etcdserverpb/raft_internal_stringer_test.go
================================================
// Copyright 2020 The etcd Authors
//
// 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 etcdserverpb_test
import (
"testing"
"github.com/stretchr/testify/assert"
pb "go.etcd.io/etcd/api/v3/etcdserverpb"
)
// TestInvalidGoTypeIntPanic tests conditions that caused
// panic: invalid Go type int for field k8s_io.kubernetes.vendor.go_etcd_io.etcd.etcdserver.etcdserverpb.loggablePutRequest.value_size
// See https://github.com/kubernetes/kubernetes/issues/91937 for more details
func TestInvalidGoTypeIntPanic(t *testing.T) {
assert.Empty(t, pb.NewLoggablePutRequest(&pb.PutRequest{}).String())
}
================================================
FILE: api/etcdserverpb/rpc.pb.go
================================================
// Code generated by protoc-gen-gogo. DO NOT EDIT.
// source: rpc.proto
package etcdserverpb
import (
fmt "fmt"
io "io"
math "math"
math_bits "math/bits"
proto "github.com/golang/protobuf/proto"
_ "github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-openapiv2/options"
authpb "go.etcd.io/etcd/api/v3/authpb"
mvccpb "go.etcd.io/etcd/api/v3/mvccpb"
_ "go.etcd.io/etcd/api/v3/versionpb"
_ "google.golang.org/genproto/googleapis/api/annotations"
)
// Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal
var _ = fmt.Errorf
var _ = math.Inf
// This is a compile-time assertion to ensure that this generated file
// is compatible with the proto package it is being compiled against.
// A compilation error at this line likely means your copy of the
// proto package needs to be updated.
const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
type AlarmType int32
const (
AlarmType_NONE AlarmType = 0
AlarmType_NOSPACE AlarmType = 1
AlarmType_CORRUPT AlarmType = 2
)
var AlarmType_name = map[int32]string{
0: "NONE",
1: "NOSPACE",
2: "CORRUPT",
}
var AlarmType_value = map[string]int32{
"NONE": 0,
"NOSPACE": 1,
"CORRUPT": 2,
}
func (x AlarmType) String() string {
return proto.EnumName(AlarmType_name, int32(x))
}
func (AlarmType) EnumDescriptor() ([]byte, []int) {
return fileDescriptor_77a6da22d6a3feb1, []int{0}
}
type RangeRequest_SortOrder int32
const (
RangeRequest_NONE RangeRequest_SortOrder = 0
RangeRequest_ASCEND RangeRequest_SortOrder = 1
RangeRequest_DESCEND RangeRequest_SortOrder = 2
)
var RangeRequest_SortOrder_name = map[int32]string{
0: "NONE",
1: "ASCEND",
2: "DESCEND",
}
var RangeRequest_SortOrder_value = map[string]int32{
"NONE": 0,
"ASCEND": 1,
"DESCEND": 2,
}
func (x RangeRequest_SortOrder) String() string {
return proto.EnumName(RangeRequest_SortOrder_name, int32(x))
}
func (RangeRequest_SortOrder) EnumDescriptor() ([]byte, []int) {
return fileDescriptor_77a6da22d6a3feb1, []int{1, 0}
}
type RangeRequest_SortTarget int32
const (
RangeRequest_KEY RangeRequest_SortTarget = 0
RangeRequest_VERSION RangeRequest_SortTarget = 1
RangeRequest_CREATE RangeRequest_SortTarget = 2
RangeRequest_MOD RangeRequest_SortTarget = 3
RangeRequest_VALUE RangeRequest_SortTarget = 4
)
var RangeRequest_SortTarget_name = map[int32]string{
0: "KEY",
1: "VERSION",
2: "CREATE",
3: "MOD",
4: "VALUE",
}
var RangeRequest_SortTarget_value = map[string]int32{
"KEY": 0,
"VERSION": 1,
"CREATE": 2,
"MOD": 3,
"VALUE": 4,
}
func (x RangeRequest_SortTarget) String() string {
return proto.EnumName(RangeRequest_SortTarget_name, int32(x))
}
func (RangeRequest_SortTarget) EnumDescriptor() ([]byte, []int) {
return fileDescriptor_77a6da22d6a3feb1, []int{1, 1}
}
type Compare_CompareResult int32
const (
Compare_EQUAL Compare_CompareResult = 0
Compare_GREATER Compare_CompareResult = 1
Compare_LESS Compare_CompareResult = 2
Compare_NOT_EQUAL Compare_CompareResult = 3
)
var Compare_CompareResult_name = map[int32]string{
0: "EQUAL",
1: "GREATER",
2: "LESS",
3: "NOT_EQUAL",
}
var Compare_CompareResult_value = map[string]int32{
"EQUAL": 0,
"GREATER": 1,
"LESS": 2,
"NOT_EQUAL": 3,
}
func (x Compare_CompareResult) String() string {
return proto.EnumName(Compare_CompareResult_name, int32(x))
}
func (Compare_CompareResult) EnumDescriptor() ([]byte, []int) {
return fileDescriptor_77a6da22d6a3feb1, []int{9, 0}
}
type Compare_CompareTarget int32
const (
Compare_VERSION Compare_CompareTarget = 0
Compare_CREATE Compare_CompareTarget = 1
Compare_MOD Compare_CompareTarget = 2
Compare_VALUE Compare_CompareTarget = 3
Compare_LEASE Compare_CompareTarget = 4
)
var Compare_CompareTarget_name = map[int32]string{
0: "VERSION",
1: "CREATE",
2: "MOD",
3: "VALUE",
4: "LEASE",
}
var Compare_CompareTarget_value = map[string]int32{
"VERSION": 0,
"CREATE": 1,
"MOD": 2,
"VALUE": 3,
"LEASE": 4,
}
func (x Compare_CompareTarget) String() string {
return proto.EnumName(Compare_CompareTarget_name, int32(x))
}
func (Compare_CompareTarget) EnumDescriptor() ([]byte, []int) {
return fileDescriptor_77a6da22d6a3feb1, []int{9, 1}
}
type WatchCreateRequest_FilterType int32
const (
// filter out put event.
WatchCreateRequest_NOPUT WatchCreateRequest_FilterType = 0
// filter out delete event.
WatchCreateRequest_NODELETE WatchCreateRequest_FilterType = 1
)
var WatchCreateRequest_FilterType_name = map[int32]string{
0: "NOPUT",
1: "NODELETE",
}
var WatchCreateRequest_FilterType_value = map[string]int32{
"NOPUT": 0,
"NODELETE": 1,
}
func (x WatchCreateRequest_FilterType) String() string {
return proto.EnumName(WatchCreateRequest_FilterType_name, int32(x))
}
func (WatchCreateRequest_FilterType) EnumDescriptor() ([]byte, []int) {
return fileDescriptor_77a6da22d6a3feb1, []int{21, 0}
}
type AlarmRequest_AlarmAction int32
const (
AlarmRequest_GET AlarmRequest_AlarmAction = 0
AlarmRequest_ACTIVATE AlarmRequest_AlarmAction = 1
AlarmRequest_DEACTIVATE AlarmRequest_AlarmAction = 2
)
var AlarmRequest_AlarmAction_name = map[int32]string{
0: "GET",
1: "ACTIVATE",
2: "DEACTIVATE",
}
var AlarmRequest_AlarmAction_value = map[string]int32{
"GET": 0,
"ACTIVATE": 1,
"DEACTIVATE": 2,
}
func (x AlarmRequest_AlarmAction) String() string {
return proto.EnumName(AlarmRequest_AlarmAction_name, int32(x))
}
func (AlarmRequest_AlarmAction) EnumDescriptor() ([]byte, []int) {
return fileDescriptor_77a6da22d6a3feb1, []int{54, 0}
}
type DowngradeRequest_DowngradeAction int32
const (
DowngradeRequest_VALIDATE DowngradeRequest_DowngradeAction = 0
DowngradeRequest_ENABLE DowngradeRequest_DowngradeAction = 1
DowngradeRequest_CANCEL DowngradeRequest_DowngradeAction = 2
)
var DowngradeRequest_DowngradeAction_name = map[int32]string{
0: "VALIDATE",
1: "ENABLE",
2: "CANCEL",
}
var DowngradeRequest_DowngradeAction_value = map[string]int32{
"VALIDATE": 0,
"ENABLE": 1,
"CANCEL": 2,
}
func (x DowngradeRequest_DowngradeAction) String() string {
return proto.EnumName(DowngradeRequest_DowngradeAction_name, int32(x))
}
func (DowngradeRequest_DowngradeAction) EnumDescriptor() ([]byte, []int) {
return fileDescriptor_77a6da22d6a3feb1, []int{57, 0}
}
type ResponseHeader struct {
// cluster_id is the ID of the cluster which sent the response.
ClusterId uint64 `protobuf:"varint,1,opt,name=cluster_id,json=clusterId,proto3" json:"cluster_id,omitempty"`
// member_id is the ID of the member which sent the response.
MemberId uint64 `protobuf:"varint,2,opt,name=member_id,json=memberId,proto3" json:"member_id,omitempty"`
// revision is the key-value store revision when the request was applied, and it's
// unset (so 0) in case of calls not interacting with key-value store.
// For watch progress responses, the header.revision indicates progress. All future events
// received in this stream are guaranteed to have a higher revision number than the
// header.revision number.
Revision int64 `protobuf:"varint,3,opt,name=revision,proto3" json:"revision,omitempty"`
// raft_term is the raft term when the request was applied.
RaftTerm uint64 `protobuf:"varint,4,opt,name=raft_term,json=raftTerm,proto3" json:"raft_term,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *ResponseHeader) Reset() { *m = ResponseHeader{} }
func (m *ResponseHeader) String() string { return proto.CompactTextString(m) }
func (*ResponseHeader) ProtoMessage() {}
func (*ResponseHeader) Descriptor() ([]byte, []int) {
return fileDescriptor_77a6da22d6a3feb1, []int{0}
}
func (m *ResponseHeader) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
}
func (m *ResponseHeader) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
if deterministic {
return xxx_messageInfo_ResponseHeader.Marshal(b, m, deterministic)
} else {
b = b[:cap(b)]
n, err := m.MarshalToSizedBuffer(b)
if err != nil {
return nil, err
}
return b[:n], nil
}
}
func (m *ResponseHeader) XXX_Merge(src proto.Message) {
xxx_messageInfo_ResponseHeader.Merge(m, src)
}
func (m *ResponseHeader) XXX_Size() int {
return m.Size()
}
func (m *ResponseHeader) XXX_DiscardUnknown() {
xxx_messageInfo_ResponseHeader.DiscardUnknown(m)
}
var xxx_messageInfo_ResponseHeader proto.InternalMessageInfo
func (m *ResponseHeader) GetClusterId() uint64 {
if m != nil {
return m.ClusterId
}
return 0
}
func (m *ResponseHeader) GetMemberId() uint64 {
if m != nil {
return m.MemberId
}
return 0
}
func (m *ResponseHeader) GetRevision() int64 {
if m != nil {
return m.Revision
}
return 0
}
func (m *ResponseHeader) GetRaftTerm() uint64 {
if m != nil {
return m.RaftTerm
}
return 0
}
type RangeRequest struct {
// key is the first key for the range. If range_end is not given, the request only looks up key.
Key []byte `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"`
// range_end is the upper bound on the requested range [key, range_end).
// If range_end is '\0', the range is all keys >= key.
// If range_end is key plus one (e.g., "aa"+1 == "ab", "a\xff"+1 == "b"),
// then the range request gets all keys prefixed with key.
// If both key and range_end are '\0', then the range request returns all keys.
RangeEnd []byte `protobuf:"bytes,2,opt,name=range_end,json=rangeEnd,proto3" json:"range_end,omitempty"`
// limit is a limit on the number of keys returned for the request. When limit is set to 0,
// it is treated as no limit.
Limit int64 `protobuf:"varint,3,opt,name=limit,proto3" json:"limit,omitempty"`
// revision is the point-in-time of the key-value store to use for the range.
// If revision is less or equal to zero, the range is over the newest key-value store.
// If the revision has been compacted, ErrCompacted is returned as a response.
Revision int64 `protobuf:"varint,4,opt,name=revision,proto3" json:"revision,omitempty"`
// sort_order is the order for returned sorted results.
SortOrder RangeRequest_SortOrder `protobuf:"varint,5,opt,name=sort_order,json=sortOrder,proto3,enum=etcdserverpb.RangeRequest_SortOrder" json:"sort_order,omitempty"`
// sort_target is the key-value field to use for sorting.
SortTarget RangeRequest_SortTarget `protobuf:"varint,6,opt,name=sort_target,json=sortTarget,proto3,enum=etcdserverpb.RangeRequest_SortTarget" json:"sort_target,omitempty"`
// serializable sets the range request to use serializable member-local reads.
// Range requests are linearizable by default; linearizable requests have higher
// latency and lower throughput than serializable requests but reflect the current
// consensus of the cluster. For better performance, in exchange for possible stale reads,
// a serializable range request is served locally without needing to reach consensus
// with other nodes in the cluster.
Serializable bool `protobuf:"varint,7,opt,name=serializable,proto3" json:"serializable,omitempty"`
// keys_only when set returns only the keys and not the values.
KeysOnly bool `protobuf:"varint,8,opt,name=keys_only,json=keysOnly,proto3" json:"keys_only,omitempty"`
// count_only when set returns only the count of the keys in the range.
CountOnly bool `protobuf:"varint,9,opt,name=count_only,json=countOnly,proto3" json:"count_only,omitempty"`
// min_mod_revision is the lower bound for returned key mod revisions; all keys with
// lesser mod revisions will be filtered away.
MinModRevision int64 `protobuf:"varint,10,opt,name=min_mod_revision,json=minModRevision,proto3" json:"min_mod_revision,omitempty"`
// max_mod_revision is the upper bound for returned key mod revisions; all keys with
// greater mod revisions will be filtered away.
MaxModRevision int64 `protobuf:"varint,11,opt,name=max_mod_revision,json=maxModRevision,proto3" json:"max_mod_revision,omitempty"`
// min_create_revision is the lower bound for returned key create revisions; all keys with
// lesser create revisions will be filtered away.
MinCreateRevision int64 `protobuf:"varint,12,opt,name=min_create_revision,json=minCreateRevision,proto3" json:"min_create_revision,omitempty"`
// max_create_revision is the upper bound for returned key create revisions; all keys with
// greater create revisions will be filtered away.
MaxCreateRevision int64 `protobuf:"varint,13,opt,name=max_create_revision,json=maxCreateRevision,proto3" json:"max_create_revision,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *RangeRequest) Reset() { *m = RangeRequest{} }
func (m *RangeRequest) String() string { return proto.CompactTextString(m) }
func (*RangeRequest) ProtoMessage() {}
func (*RangeRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_77a6da22d6a3feb1, []int{1}
}
func (m *RangeRequest) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
}
func (m *RangeRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
if deterministic {
return xxx_messageInfo_RangeRequest.Marshal(b, m, deterministic)
} else {
b = b[:cap(b)]
n, err := m.MarshalToSizedBuffer(b)
if err != nil {
return nil, err
}
return b[:n], nil
}
}
func (m *RangeRequest) XXX_Merge(src proto.Message) {
xxx_messageInfo_RangeRequest.Merge(m, src)
}
func (m *RangeRequest) XXX_Size() int {
return m.Size()
}
func (m *RangeRequest) XXX_DiscardUnknown() {
xxx_messageInfo_RangeRequest.DiscardUnknown(m)
}
var xxx_messageInfo_RangeRequest proto.InternalMessageInfo
func (m *RangeRequest) GetKey() []byte {
if m != nil {
return m.Key
}
return nil
}
func (m *RangeRequest) GetRangeEnd() []byte {
if m != nil {
return m.RangeEnd
}
return nil
}
func (m *RangeRequest) GetLimit() int64 {
if m != nil {
return m.Limit
}
return 0
}
func (m *RangeRequest) GetRevision() int64 {
if m != nil {
return m.Revision
}
return 0
}
func (m *RangeRequest) GetSortOrder() RangeRequest_SortOrder {
if m != nil {
return m.SortOrder
}
return RangeRequest_NONE
}
func (m *RangeRequest) GetSortTarget() RangeRequest_SortTarget {
if m != nil {
return m.SortTarget
}
return RangeRequest_KEY
}
func (m *RangeRequest) GetSerializable() bool {
if m != nil {
return m.Serializable
}
return false
}
func (m *RangeRequest) GetKeysOnly() bool {
if m != nil {
return m.KeysOnly
}
return false
}
func (m *RangeRequest) GetCountOnly() bool {
if m != nil {
return m.CountOnly
}
return false
}
func (m *RangeRequest) GetMinModRevision() int64 {
if m != nil {
return m.MinModRevision
}
return 0
}
func (m *RangeRequest) GetMaxModRevision() int64 {
if m != nil {
return m.MaxModRevision
}
return 0
}
func (m *RangeRequest) GetMinCreateRevision() int64 {
if m != nil {
return m.MinCreateRevision
}
return 0
}
func (m *RangeRequest) GetMaxCreateRevision() int64 {
if m != nil {
return m.MaxCreateRevision
}
return 0
}
type RangeResponse struct {
Header *ResponseHeader `protobuf:"bytes,1,opt,name=header,proto3" json:"header,omitempty"`
// kvs is the list of key-value pairs matched by the range request.
// kvs is empty when count is requested.
Kvs []*mvccpb.KeyValue `protobuf:"bytes,2,rep,name=kvs,proto3" json:"kvs,omitempty"`
// more indicates if there are more keys to return in the requested range.
More bool `protobuf:"varint,3,opt,name=more,proto3" json:"more,omitempty"`
// count is set to the actual number of keys within the range when requested.
// Unlike Kvs, it is unaffected by limits and filters (e.g., Min/Max, Create/Modify, Revisions)
// and reflects the full count within the specified range.
Count int64 `protobuf:"varint,4,opt,name=count,proto3" json:"count,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *RangeResponse) Reset() { *m = RangeResponse{} }
func (m *RangeResponse) String() string { return proto.CompactTextString(m) }
func (*RangeResponse) ProtoMessage() {}
func (*RangeResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_77a6da22d6a3feb1, []int{2}
}
func (m *RangeResponse) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
}
func (m *RangeResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
if deterministic {
return xxx_messageInfo_RangeResponse.Marshal(b, m, deterministic)
} else {
b = b[:cap(b)]
n, err := m.MarshalToSizedBuffer(b)
if err != nil {
return nil, err
}
return b[:n], nil
}
}
func (m *RangeResponse) XXX_Merge(src proto.Message) {
xxx_messageInfo_RangeResponse.Merge(m, src)
}
func (m *RangeResponse) XXX_Size() int {
return m.Size()
}
func (m *RangeResponse) XXX_DiscardUnknown() {
xxx_messageInfo_RangeResponse.DiscardUnknown(m)
}
var xxx_messageInfo_RangeResponse proto.InternalMessageInfo
func (m *RangeResponse) GetHeader() *ResponseHeader {
if m != nil {
return m.Header
}
return nil
}
func (m *RangeResponse) GetKvs() []*mvccpb.KeyValue {
if m != nil {
return m.Kvs
}
return nil
}
func (m *RangeResponse) GetMore() bool {
if m != nil {
return m.More
}
return false
}
func (m *RangeResponse) GetCount() int64 {
if m != nil {
return m.Count
}
return 0
}
type PutRequest struct {
// key is the key, in bytes, to put into the key-value store.
Key []byte `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"`
// value is the value, in bytes, to associate with the key in the key-value store.
Value []byte `protobuf:"bytes,2,opt,name=value,proto3" json:"value,omitempty"`
// lease is the lease ID to associate with the key in the key-value store. A lease
// value of 0 indicates no lease.
Lease int64 `protobuf:"varint,3,opt,name=lease,proto3" json:"lease,omitempty"`
// If prev_kv is set, etcd gets the previous key-value pair before changing it.
// The previous key-value pair will be returned in the put response.
PrevKv bool `protobuf:"varint,4,opt,name=prev_kv,json=prevKv,proto3" json:"prev_kv,omitempty"`
// If ignore_value is set, etcd updates the key using its current value.
// Returns an error if the key does not exist.
IgnoreValue bool `protobuf:"varint,5,opt,name=ignore_value,json=ignoreValue,proto3" json:"ignore_value,omitempty"`
// If ignore_lease is set, etcd updates the key using its current lease.
// Returns an error if the key does not exist.
IgnoreLease bool `protobuf:"varint,6,opt,name=ignore_lease,json=ignoreLease,proto3" json:"ignore_lease,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *PutRequest) Reset() { *m = PutRequest{} }
func (m *PutRequest) String() string { return proto.CompactTextString(m) }
func (*PutRequest) ProtoMessage() {}
func (*PutRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_77a6da22d6a3feb1, []int{3}
}
func (m *PutRequest) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
}
func (m *PutRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
if deterministic {
return xxx_messageInfo_PutRequest.Marshal(b, m, deterministic)
} else {
b = b[:cap(b)]
n, err := m.MarshalToSizedBuffer(b)
if err != nil {
return nil, err
}
return b[:n], nil
}
}
func (m *PutRequest) XXX_Merge(src proto.Message) {
xxx_messageInfo_PutRequest.Merge(m, src)
}
func (m *PutRequest) XXX_Size() int {
return m.Size()
}
func (m *PutRequest) XXX_DiscardUnknown() {
xxx_messageInfo_PutRequest.DiscardUnknown(m)
}
var xxx_messageInfo_PutRequest proto.InternalMessageInfo
func (m *PutRequest) GetKey() []byte {
if m != nil {
return m.Key
}
return nil
}
func (m *PutRequest) GetValue() []byte {
if m != nil {
return m.Value
}
return nil
}
func (m *PutRequest) GetLease() int64 {
if m != nil {
return m.Lease
}
return 0
}
func (m *PutRequest) GetPrevKv() bool {
if m != nil {
return m.PrevKv
}
return false
}
func (m *PutRequest) GetIgnoreValue() bool {
if m != nil {
return m.IgnoreValue
}
return false
}
func (m *PutRequest) GetIgnoreLease() bool {
if m != nil {
return m.IgnoreLease
}
return false
}
type PutResponse struct {
Header *ResponseHeader `protobuf:"bytes,1,opt,name=header,proto3" json:"header,omitempty"`
// if prev_kv is set in the request, the previous key-value pair will be returned.
PrevKv *mvccpb.KeyValue `protobuf:"bytes,2,opt,name=prev_kv,json=prevKv,proto3" json:"prev_kv,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *PutResponse) Reset() { *m = PutResponse{} }
func (m *PutResponse) String() string { return proto.CompactTextString(m) }
func (*PutResponse) ProtoMessage() {}
func (*PutResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_77a6da22d6a3feb1, []int{4}
}
func (m *PutResponse) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
}
func (m *PutResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
if deterministic {
return xxx_messageInfo_PutResponse.Marshal(b, m, deterministic)
} else {
b = b[:cap(b)]
n, err := m.MarshalToSizedBuffer(b)
if err != nil {
return nil, err
}
return b[:n], nil
}
}
func (m *PutResponse) XXX_Merge(src proto.Message) {
xxx_messageInfo_PutResponse.Merge(m, src)
}
func (m *PutResponse) XXX_Size() int {
return m.Size()
}
func (m *PutResponse) XXX_DiscardUnknown() {
xxx_messageInfo_PutResponse.DiscardUnknown(m)
}
var xxx_messageInfo_PutResponse proto.InternalMessageInfo
func (m *PutResponse) GetHeader() *ResponseHeader {
if m != nil {
return m.Header
}
return nil
}
func (m *PutResponse) GetPrevKv() *mvccpb.KeyValue {
if m != nil {
return m.PrevKv
}
return nil
}
type DeleteRangeRequest struct {
// key is the first key to delete in the range.
Key []byte `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"`
// range_end is the key following the last key to delete for the range [key, range_end).
// If range_end is not given, the range is defined to contain only the key argument.
// If range_end is one bit larger than the given key, then the range is all the keys
// with the prefix (the given key).
// If range_end is '\0', the range is all keys greater than or equal to the key argument.
RangeEnd []byte `protobuf:"bytes,2,opt,name=range_end,json=rangeEnd,proto3" json:"range_end,omitempty"`
// If prev_kv is set, etcd gets the previous key-value pairs before deleting it.
// The previous key-value pairs will be returned in the delete response.
PrevKv bool `protobuf:"varint,3,opt,name=prev_kv,json=prevKv,proto3" json:"prev_kv,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *DeleteRangeRequest) Reset() { *m = DeleteRangeRequest{} }
func (m *DeleteRangeRequest) String() string { return proto.CompactTextString(m) }
func (*DeleteRangeRequest) ProtoMessage() {}
func (*DeleteRangeRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_77a6da22d6a3feb1, []int{5}
}
func (m *DeleteRangeRequest) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
}
func (m *DeleteRangeRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
if deterministic {
return xxx_messageInfo_DeleteRangeRequest.Marshal(b, m, deterministic)
} else {
b = b[:cap(b)]
n, err := m.MarshalToSizedBuffer(b)
if err != nil {
return nil, err
}
return b[:n], nil
}
}
func (m *DeleteRangeRequest) XXX_Merge(src proto.Message) {
xxx_messageInfo_DeleteRangeRequest.Merge(m, src)
}
func (m *DeleteRangeRequest) XXX_Size() int {
return m.Size()
}
func (m *DeleteRangeRequest) XXX_DiscardUnknown() {
xxx_messageInfo_DeleteRangeRequest.DiscardUnknown(m)
}
var xxx_messageInfo_DeleteRangeRequest proto.InternalMessageInfo
func (m *DeleteRangeRequest) GetKey() []byte {
if m != nil {
return m.Key
}
return nil
}
func (m *DeleteRangeRequest) GetRangeEnd() []byte {
if m != nil {
return m.RangeEnd
}
return nil
}
func (m *DeleteRangeRequest) GetPrevKv() bool {
if m != nil {
return m.PrevKv
}
return false
}
type DeleteRangeResponse struct {
Header *ResponseHeader `protobuf:"bytes,1,opt,name=header,proto3" json:"header,omitempty"`
// deleted is the number of keys deleted by the delete range request.
Deleted int64 `protobuf:"varint,2,opt,name=deleted,proto3" json:"deleted,omitempty"`
// if prev_kv is set in the request, the previous key-value pairs will be returned.
PrevKvs []*mvccpb.KeyValue `protobuf:"bytes,3,rep,name=prev_kvs,json=prevKvs,proto3" json:"prev_kvs,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *DeleteRangeResponse) Reset() { *m = DeleteRangeResponse{} }
func (m *DeleteRangeResponse) String() string { return proto.CompactTextString(m) }
func (*DeleteRangeResponse) ProtoMessage() {}
func (*DeleteRangeResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_77a6da22d6a3feb1, []int{6}
}
func (m *DeleteRangeResponse) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
}
func (m *DeleteRangeResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
if deterministic {
return xxx_messageInfo_DeleteRangeResponse.Marshal(b, m, deterministic)
} else {
b = b[:cap(b)]
n, err := m.MarshalToSizedBuffer(b)
if err != nil {
return nil, err
}
return b[:n], nil
}
}
func (m *DeleteRangeResponse) XXX_Merge(src proto.Message) {
xxx_messageInfo_DeleteRangeResponse.Merge(m, src)
}
func (m *DeleteRangeResponse) XXX_Size() int {
return m.Size()
}
func (m *DeleteRangeResponse) XXX_DiscardUnknown() {
xxx_messageInfo_DeleteRangeResponse.DiscardUnknown(m)
}
var xxx_messageInfo_DeleteRangeResponse proto.InternalMessageInfo
func (m *DeleteRangeResponse) GetHeader() *ResponseHeader {
if m != nil {
return m.Header
}
return nil
}
func (m *DeleteRangeResponse) GetDeleted() int64 {
if m != nil {
return m.Deleted
}
return 0
}
func (m *DeleteRangeResponse) GetPrevKvs() []*mvccpb.KeyValue {
if m != nil {
return m.PrevKvs
}
return nil
}
type RequestOp struct {
// request is a union of request types accepted by a transaction.
//
// Types that are valid to be assigned to Request:
// *RequestOp_RequestRange
// *RequestOp_RequestPut
// *RequestOp_RequestDeleteRange
// *RequestOp_RequestTxn
Request isRequestOp_Request `protobuf_oneof:"request"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *RequestOp) Reset() { *m = RequestOp{} }
func (m *RequestOp) String() string { return proto.CompactTextString(m) }
func (*RequestOp) ProtoMessage() {}
func (*RequestOp) Descriptor() ([]byte, []int) {
return fileDescriptor_77a6da22d6a3feb1, []int{7}
}
func (m *RequestOp) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
}
func (m *RequestOp) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
if deterministic {
return xxx_messageInfo_RequestOp.Marshal(b, m, deterministic)
} else {
b = b[:cap(b)]
n, err := m.MarshalToSizedBuffer(b)
if err != nil {
return nil, err
}
return b[:n], nil
}
}
func (m *RequestOp) XXX_Merge(src proto.Message) {
xxx_messageInfo_RequestOp.Merge(m, src)
}
func (m *RequestOp) XXX_Size() int {
return m.Size()
}
func (m *RequestOp) XXX_DiscardUnknown() {
xxx_messageInfo_RequestOp.DiscardUnknown(m)
}
var xxx_messageInfo_RequestOp proto.InternalMessageInfo
type isRequestOp_Request interface {
isRequestOp_Request()
MarshalTo([]byte) (int, error)
Size() int
}
type RequestOp_RequestRange struct {
RequestRange *RangeRequest `protobuf:"bytes,1,opt,name=request_range,json=requestRange,proto3,oneof" json:"request_range,omitempty"`
}
type RequestOp_RequestPut struct {
RequestPut *PutRequest `protobuf:"bytes,2,opt,name=request_put,json=requestPut,proto3,oneof" json:"request_put,omitempty"`
}
type RequestOp_RequestDeleteRange struct {
RequestDeleteRange *DeleteRangeRequest `protobuf:"bytes,3,opt,name=request_delete_range,json=requestDeleteRange,proto3,oneof" json:"request_delete_range,omitempty"`
}
type RequestOp_RequestTxn struct {
RequestTxn *TxnRequest `protobuf:"bytes,4,opt,name=request_txn,json=requestTxn,proto3,oneof" json:"request_txn,omitempty"`
}
func (*RequestOp_RequestRange) isRequestOp_Request() {}
func (*RequestOp_RequestPut) isRequestOp_Request() {}
func (*RequestOp_RequestDeleteRange) isRequestOp_Request() {}
func (*RequestOp_RequestTxn) isRequestOp_Request() {}
func (m *RequestOp) GetRequest() isRequestOp_Request {
if m != nil {
return m.Request
}
return nil
}
func (m *RequestOp) GetRequestRange() *RangeRequest {
if x, ok := m.GetRequest().(*RequestOp_RequestRange); ok {
return x.RequestRange
}
return nil
}
func (m *RequestOp) GetRequestPut() *PutRequest {
if x, ok := m.GetRequest().(*RequestOp_RequestPut); ok {
return x.RequestPut
}
return nil
}
func (m *RequestOp) GetRequestDeleteRange() *DeleteRangeRequest {
if x, ok := m.GetRequest().(*RequestOp_RequestDeleteRange); ok {
return x.RequestDeleteRange
}
return nil
}
func (m *RequestOp) GetRequestTxn() *TxnRequest {
if x, ok := m.GetRequest().(*RequestOp_RequestTxn); ok {
return x.RequestTxn
}
return nil
}
// XXX_OneofWrappers is for the internal use of the proto package.
func (*RequestOp) XXX_OneofWrappers() []interface{} {
return []interface{}{
(*RequestOp_RequestRange)(nil),
(*RequestOp_RequestPut)(nil),
(*RequestOp_RequestDeleteRange)(nil),
(*RequestOp_RequestTxn)(nil),
}
}
type ResponseOp struct {
// response is a union of response types returned by a transaction.
//
// Types that are valid to be assigned to Response:
// *ResponseOp_ResponseRange
// *ResponseOp_ResponsePut
// *ResponseOp_ResponseDeleteRange
// *ResponseOp_ResponseTxn
Response isResponseOp_Response `protobuf_oneof:"response"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *ResponseOp) Reset() { *m = ResponseOp{} }
func (m *ResponseOp) String() string { return proto.CompactTextString(m) }
func (*ResponseOp) ProtoMessage() {}
func (*ResponseOp) Descriptor() ([]byte, []int) {
return fileDescriptor_77a6da22d6a3feb1, []int{8}
}
func (m *ResponseOp) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
}
func (m *ResponseOp) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
if deterministic {
return xxx_messageInfo_ResponseOp.Marshal(b, m, deterministic)
} else {
b = b[:cap(b)]
n, err := m.MarshalToSizedBuffer(b)
if err != nil {
return nil, err
}
return b[:n], nil
}
}
func (m *ResponseOp) XXX_Merge(src proto.Message) {
xxx_messageInfo_ResponseOp.Merge(m, src)
}
func (m *ResponseOp) XXX_Size() int {
return m.Size()
}
func (m *ResponseOp) XXX_DiscardUnknown() {
xxx_messageInfo_ResponseOp.DiscardUnknown(m)
}
var xxx_messageInfo_ResponseOp proto.InternalMessageInfo
type isResponseOp_Response interface {
isResponseOp_Response()
MarshalTo([]byte) (int, error)
Size() int
}
type ResponseOp_ResponseRange struct {
ResponseRange *RangeResponse `protobuf:"bytes,1,opt,name=response_range,json=responseRange,proto3,oneof" json:"response_range,omitempty"`
}
type ResponseOp_ResponsePut struct {
ResponsePut *PutResponse `protobuf:"bytes,2,opt,name=response_put,json=responsePut,proto3,oneof" json:"response_put,omitempty"`
}
type ResponseOp_ResponseDeleteRange struct {
ResponseDeleteRange *DeleteRangeResponse `protobuf:"bytes,3,opt,name=response_delete_range,json=responseDeleteRange,proto3,oneof" json:"response_delete_range,omitempty"`
}
type ResponseOp_ResponseTxn struct {
ResponseTxn *TxnResponse `protobuf:"bytes,4,opt,name=response_txn,json=responseTxn,proto3,oneof" json:"response_txn,omitempty"`
}
func (*ResponseOp_ResponseRange) isResponseOp_Response() {}
func (*ResponseOp_ResponsePut) isResponseOp_Response() {}
func (*ResponseOp_ResponseDeleteRange) isResponseOp_Response() {}
func (*ResponseOp_ResponseTxn) isResponseOp_Response() {}
func (m *ResponseOp) GetResponse() isResponseOp_Response {
if m != nil {
return m.Response
}
return nil
}
func (m *ResponseOp) GetResponseRange() *RangeResponse {
if x, ok := m.GetResponse().(*ResponseOp_ResponseRange); ok {
return x.ResponseRange
}
return nil
}
func (m *ResponseOp) GetResponsePut() *PutResponse {
if x, ok := m.GetResponse().(*ResponseOp_ResponsePut); ok {
return x.ResponsePut
}
return nil
}
func (m *ResponseOp) GetResponseDeleteRange() *DeleteRangeResponse {
if x, ok := m.GetResponse().(*ResponseOp_ResponseDeleteRange); ok {
return x.ResponseDeleteRange
}
return nil
}
func (m *ResponseOp) GetResponseTxn() *TxnResponse {
if x, ok := m.GetResponse().(*ResponseOp_ResponseTxn); ok {
return x.ResponseTxn
}
return nil
}
// XXX_OneofWrappers is for the internal use of the proto package.
func (*ResponseOp) XXX_OneofWrappers() []interface{} {
return []interface{}{
(*ResponseOp_ResponseRange)(nil),
(*ResponseOp_ResponsePut)(nil),
(*ResponseOp_ResponseDeleteRange)(nil),
(*ResponseOp_ResponseTxn)(nil),
}
}
type Compare struct {
// result is logical comparison operation for this comparison.
Result Compare_CompareResult `protobuf:"varint,1,opt,name=result,proto3,enum=etcdserverpb.Compare_CompareResult" json:"result,omitempty"`
// target is the key-value field to inspect for the comparison.
Target Compare_CompareTarget `protobuf:"varint,2,opt,name=target,proto3,enum=etcdserverpb.Compare_CompareTarget" json:"target,omitempty"`
// key is the subject key for the comparison operation.
Key []byte `protobuf:"bytes,3,opt,name=key,proto3" json:"key,omitempty"`
// Types that are valid to be assigned to TargetUnion:
// *Compare_Version
// *Compare_CreateRevision
// *Compare_ModRevision
// *Compare_Value
// *Compare_Lease
TargetUnion isCompare_TargetUnion `protobuf_oneof:"target_union"`
// range_end compares the given target to all keys in the range [key, range_end).
// See RangeRequest for more details on key ranges.
RangeEnd []byte `protobuf:"bytes,64,opt,name=range_end,json=rangeEnd,proto3" json:"range_end,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *Compare) Reset() { *m = Compare{} }
func (m *Compare) String() string { return proto.CompactTextString(m) }
func (*Compare) ProtoMessage() {}
func (*Compare) Descriptor() ([]byte, []int) {
return fileDescriptor_77a6da22d6a3feb1, []int{9}
}
func (m *Compare) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
}
func (m *Compare) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
if deterministic {
return xxx_messageInfo_Compare.Marshal(b, m, deterministic)
} else {
b = b[:cap(b)]
n, err := m.MarshalToSizedBuffer(b)
if err != nil {
return nil, err
}
return b[:n], nil
}
}
func (m *Compare) XXX_Merge(src proto.Message) {
xxx_messageInfo_Compare.Merge(m, src)
}
func (m *Compare) XXX_Size() int {
return m.Size()
}
func (m *Compare) XXX_DiscardUnknown() {
xxx_messageInfo_Compare.DiscardUnknown(m)
}
var xxx_messageInfo_Compare proto.InternalMessageInfo
type isCompare_TargetUnion interface {
isCompare_TargetUnion()
MarshalTo([]byte) (int, error)
Size() int
}
type Compare_Version struct {
Version int64 `protobuf:"varint,4,opt,name=version,proto3,oneof" json:"version,omitempty"`
}
type Compare_CreateRevision struct {
CreateRevision int64 `protobuf:"varint,5,opt,name=create_revision,json=createRevision,proto3,oneof" json:"create_revision,omitempty"`
}
type Compare_ModRevision struct {
ModRevision int64 `protobuf:"varint,6,opt,name=mod_revision,json=modRevision,proto3,oneof" json:"mod_revision,omitempty"`
}
type Compare_Value struct {
Value []byte `protobuf:"bytes,7,opt,name=value,proto3,oneof" json:"value,omitempty"`
}
type Compare_Lease struct {
Lease int64 `protobuf:"varint,8,opt,name=lease,proto3,oneof" json:"lease,omitempty"`
}
func (*Compare_Version) isCompare_TargetUnion() {}
func (*Compare_CreateRevision) isCompare_TargetUnion() {}
func (*Compare_ModRevision) isCompare_TargetUnion() {}
func (*Compare_Value) isCompare_TargetUnion() {}
func (*Compare_Lease) isCompare_TargetUnion() {}
func (m *Compare) GetTargetUnion() isCompare_TargetUnion {
if m != nil {
return m.TargetUnion
}
return nil
}
func (m *Compare) GetResult() Compare_CompareResult {
if m != nil {
return m.Result
}
return Compare_EQUAL
}
func (m *Compare) GetTarget() Compare_CompareTarget {
if m != nil {
return m.Target
}
return Compare_VERSION
}
func (m *Compare) GetKey() []byte {
if m != nil {
return m.Key
}
return nil
}
func (m *Compare) GetVersion() int64 {
if x, ok := m.GetTargetUnion().(*Compare_Version); ok {
return x.Version
}
return 0
}
func (m *Compare) GetCreateRevision() int64 {
if x, ok := m.GetTargetUnion().(*Compare_CreateRevision); ok {
return x.CreateRevision
}
return 0
}
func (m *Compare) GetModRevision() int64 {
if x, ok := m.GetTargetUnion().(*Compare_ModRevision); ok {
return x.ModRevision
}
return 0
}
func (m *Compare) GetValue() []byte {
if x, ok := m.GetTargetUnion().(*Compare_Value); ok {
return x.Value
}
return nil
}
func (m *Compare) GetLease() int64 {
if x, ok := m.GetTargetUnion().(*Compare_Lease); ok {
return x.Lease
}
return 0
}
func (m *Compare) GetRangeEnd() []byte {
if m != nil {
return m.RangeEnd
}
return nil
}
// XXX_OneofWrappers is for the internal use of the proto package.
func (*Compare) XXX_OneofWrappers() []interface{} {
return []interface{}{
(*Compare_Version)(nil),
(*Compare_CreateRevision)(nil),
(*Compare_ModRevision)(nil),
(*Compare_Value)(nil),
(*Compare_Lease)(nil),
}
}
// From google paxosdb paper:
// Our implementation hinges around a powerful primitive which we call MultiOp. All other database
// operations except for iteration are implemented as a single call to MultiOp. A MultiOp is applied atomically
// and consists of three components:
// 1. A list of tests called guard. Each test in guard checks a single entry in the database. It may check
// for the absence or presence of a value, or compare with a given value. Two different tests in the guard
// may apply to the same or different entries in the database. All tests in the guard are applied and
// MultiOp returns the results. If all tests are true, MultiOp executes t op (see item 2 below), otherwise
// it executes f op (see item 3 below).
// 2. A list of database operations called t op. Each operation in the list is either an insert, delete, or
// lookup operation, and applies to a single database entry. Two different operations in the list may apply
// to the same or different entries in the database. These operations are executed
// if guard evaluates to
// true.
// 3. A list of database operations called f op. Like t op, but executed if guard evaluates to false.
type TxnRequest struct {
// compare is a list of predicates representing a conjunction of terms.
// If the comparisons succeed, then the success requests will be processed in order,
// and the response will contain their respective responses in order.
// If the comparisons fail, then the failure requests will be processed in order,
// and the response will contain their respective responses in order.
Compare []*Compare `protobuf:"bytes,1,rep,name=compare,proto3" json:"compare,omitempty"`
// success is a list of requests which will be applied when compare evaluates to true.
Success []*RequestOp `protobuf:"bytes,2,rep,name=success,proto3" json:"success,omitempty"`
// failure is a list of requests which will be applied when compare evaluates to false.
Failure []*RequestOp `protobuf:"bytes,3,rep,name=failure,proto3" json:"failure,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *TxnRequest) Reset() { *m = TxnRequest{} }
func (m *TxnRequest) String() string { return proto.CompactTextString(m) }
func (*TxnRequest) ProtoMessage() {}
func (*TxnRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_77a6da22d6a3feb1, []int{10}
}
func (m *TxnRequest) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
}
func (m *TxnRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
if deterministic {
return xxx_messageInfo_TxnRequest.Marshal(b, m, deterministic)
} else {
b = b[:cap(b)]
n, err := m.MarshalToSizedBuffer(b)
if err != nil {
return nil, err
}
return b[:n], nil
}
}
func (m *TxnRequest) XXX_Merge(src proto.Message) {
xxx_messageInfo_TxnRequest.Merge(m, src)
}
func (m *TxnRequest) XXX_Size() int {
return m.Size()
}
func (m *TxnRequest) XXX_DiscardUnknown() {
xxx_messageInfo_TxnRequest.DiscardUnknown(m)
}
var xxx_messageInfo_TxnRequest proto.InternalMessageInfo
func (m *TxnRequest) GetCompare() []*Compare {
if m != nil {
return m.Compare
}
return nil
}
func (m *TxnRequest) GetSuccess() []*RequestOp {
if m != nil {
return m.Success
}
return nil
}
func (m *TxnRequest) GetFailure() []*RequestOp {
if m != nil {
return m.Failure
}
return nil
}
type TxnResponse struct {
Header *ResponseHeader `protobuf:"bytes,1,opt,name=header,proto3" json:"header,omitempty"`
// succeeded is set to true if the compare evaluated to true or false otherwise.
Succeeded bool `protobuf:"varint,2,opt,name=succeeded,proto3" json:"succeeded,omitempty"`
// responses is a list of responses corresponding to the results from applying
// success if succeeded is true or failure if succeeded is false.
Responses []*ResponseOp `protobuf:"bytes,3,rep,name=responses,proto3" json:"responses,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *TxnResponse) Reset() { *m = TxnResponse{} }
func (m *TxnResponse) String() string { return proto.CompactTextString(m) }
func (*TxnResponse) ProtoMessage() {}
func (*TxnResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_77a6da22d6a3feb1, []int{11}
}
func (m *TxnResponse) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
}
func (m *TxnResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
if deterministic {
return xxx_messageInfo_TxnResponse.Marshal(b, m, deterministic)
} else {
b = b[:cap(b)]
n, err := m.MarshalToSizedBuffer(b)
if err != nil {
return nil, err
}
return b[:n], nil
}
}
func (m *TxnResponse) XXX_Merge(src proto.Message) {
xxx_messageInfo_TxnResponse.Merge(m, src)
}
func (m *TxnResponse) XXX_Size() int {
return m.Size()
}
func (m *TxnResponse) XXX_DiscardUnknown() {
xxx_messageInfo_TxnResponse.DiscardUnknown(m)
}
var xxx_messageInfo_TxnResponse proto.InternalMessageInfo
func (m *TxnResponse) GetHeader() *ResponseHeader {
if m != nil {
return m.Header
}
return nil
}
func (m *TxnResponse) GetSucceeded() bool {
if m != nil {
return m.Succeeded
}
return false
}
func (m *TxnResponse) GetResponses() []*ResponseOp {
if m != nil {
return m.Responses
}
return nil
}
// CompactionRequest compacts the key-value store up to a given revision. All superseded keys
// with a revision less than the compaction revision will be removed.
type CompactionRequest struct {
// revision is the key-value store revision for the compaction operation.
Revision int64 `protobuf:"varint,1,opt,name=revision,proto3" json:"revision,omitempty"`
// physical is set so the RPC will wait until the compaction is physically
// applied to the local database such that compacted entries are totally
// removed from the backend database.
Physical bool `protobuf:"varint,2,opt,name=physical,proto3" json:"physical,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *CompactionRequest) Reset() { *m = CompactionRequest{} }
func (m *CompactionRequest) String() string { return proto.CompactTextString(m) }
func (*CompactionRequest) ProtoMessage() {}
func (*CompactionRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_77a6da22d6a3feb1, []int{12}
}
func (m *CompactionRequest) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
}
func (m *CompactionRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
if deterministic {
return xxx_messageInfo_CompactionRequest.Marshal(b, m, deterministic)
} else {
b = b[:cap(b)]
n, err := m.MarshalToSizedBuffer(b)
if err != nil {
return nil, err
}
return b[:n], nil
}
}
func (m *CompactionRequest) XXX_Merge(src proto.Message) {
xxx_messageInfo_CompactionRequest.Merge(m, src)
}
func (m *CompactionRequest) XXX_Size() int {
return m.Size()
}
func (m *CompactionRequest) XXX_DiscardUnknown() {
xxx_messageInfo_CompactionRequest.DiscardUnknown(m)
}
var xxx_messageInfo_CompactionRequest proto.InternalMessageInfo
func (m *CompactionRequest) GetRevision() int64 {
if m != nil {
return m.Revision
}
return 0
}
func (m *CompactionRequest) GetPhysical() bool {
if m != nil {
return m.Physical
}
return false
}
type CompactionResponse struct {
Header *ResponseHeader `protobuf:"bytes,1,opt,name=header,proto3" json:"header,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *CompactionResponse) Reset() { *m = CompactionResponse{} }
func (m *CompactionResponse) String() string { return proto.CompactTextString(m) }
func (*CompactionResponse) ProtoMessage() {}
func (*CompactionResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_77a6da22d6a3feb1, []int{13}
}
func (m *CompactionResponse) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
}
func (m *CompactionResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
if deterministic {
return xxx_messageInfo_CompactionResponse.Marshal(b, m, deterministic)
} else {
b = b[:cap(b)]
n, err := m.MarshalToSizedBuffer(b)
if err != nil {
return nil, err
}
return b[:n], nil
}
}
func (m *CompactionResponse) XXX_Merge(src proto.Message) {
xxx_messageInfo_CompactionResponse.Merge(m, src)
}
func (m *CompactionResponse) XXX_Size() int {
return m.Size()
}
func (m *CompactionResponse) XXX_DiscardUnknown() {
xxx_messageInfo_CompactionResponse.DiscardUnknown(m)
}
var xxx_messageInfo_CompactionResponse proto.InternalMessageInfo
func (m *CompactionResponse) GetHeader() *ResponseHeader {
if m != nil {
return m.Header
}
return nil
}
type HashRequest struct {
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *HashRequest) Reset() { *m = HashRequest{} }
func (m *HashRequest) String() string { return proto.CompactTextString(m) }
func (*HashRequest) ProtoMessage() {}
func (*HashRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_77a6da22d6a3feb1, []int{14}
}
func (m *HashRequest) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
}
func (m *HashRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
if deterministic {
return xxx_messageInfo_HashRequest.Marshal(b, m, deterministic)
} else {
b = b[:cap(b)]
n, err := m.MarshalToSizedBuffer(b)
if err != nil {
return nil, err
}
return b[:n], nil
}
}
func (m *HashRequest) XXX_Merge(src proto.Message) {
xxx_messageInfo_HashRequest.Merge(m, src)
}
func (m *HashRequest) XXX_Size() int {
return m.Size()
}
func (m *HashRequest) XXX_DiscardUnknown() {
xxx_messageInfo_HashRequest.DiscardUnknown(m)
}
var xxx_messageInfo_HashRequest proto.InternalMessageInfo
type HashKVRequest struct {
// revision is the key-value store revision for the hash operation.
Revision int64 `protobuf:"varint,1,opt,name=revision,proto3" json:"revision,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *HashKVRequest) Reset() { *m = HashKVRequest{} }
func (m *HashKVRequest) String() string { return proto.CompactTextString(m) }
func (*HashKVRequest) ProtoMessage() {}
func (*HashKVRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_77a6da22d6a3feb1, []int{15}
}
func (m *HashKVRequest) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
}
func (m *HashKVRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
if deterministic {
return xxx_messageInfo_HashKVRequest.Marshal(b, m, deterministic)
} else {
b = b[:cap(b)]
n, err := m.MarshalToSizedBuffer(b)
if err != nil {
return nil, err
}
return b[:n], nil
}
}
func (m *HashKVRequest) XXX_Merge(src proto.Message) {
xxx_messageInfo_HashKVRequest.Merge(m, src)
}
func (m *HashKVRequest) XXX_Size() int {
return m.Size()
}
func (m *HashKVRequest) XXX_DiscardUnknown() {
xxx_messageInfo_HashKVRequest.DiscardUnknown(m)
}
var xxx_messageInfo_HashKVRequest proto.InternalMessageInfo
func (m *HashKVRequest) GetRevision() int64 {
if m != nil {
return m.Revision
}
return 0
}
type HashKVResponse struct {
Header *ResponseHeader `protobuf:"bytes,1,opt,name=header,proto3" json:"header,omitempty"`
// hash is the hash value computed from the responding member's MVCC keys up to a given revision.
Hash uint32 `protobuf:"varint,2,opt,name=hash,proto3" json:"hash,omitempty"`
// compact_revision is the compacted revision of key-value store when hash begins.
CompactRevision int64 `protobuf:"varint,3,opt,name=compact_revision,json=compactRevision,proto3" json:"compact_revision,omitempty"`
// hash_revision is the revision up to which the hash is calculated.
HashRevision int64 `protobuf:"varint,4,opt,name=hash_revision,json=hashRevision,proto3" json:"hash_revision,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *HashKVResponse) Reset() { *m = HashKVResponse{} }
func (m *HashKVResponse) String() string { return proto.CompactTextString(m) }
func (*HashKVResponse) ProtoMessage() {}
func (*HashKVResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_77a6da22d6a3feb1, []int{16}
}
func (m *HashKVResponse) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
}
func (m *HashKVResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
if deterministic {
return xxx_messageInfo_HashKVResponse.Marshal(b, m, deterministic)
} else {
b = b[:cap(b)]
n, err := m.MarshalToSizedBuffer(b)
if err != nil {
return nil, err
}
return b[:n], nil
}
}
func (m *HashKVResponse) XXX_Merge(src proto.Message) {
xxx_messageInfo_HashKVResponse.Merge(m, src)
}
func (m *HashKVResponse) XXX_Size() int {
return m.Size()
}
func (m *HashKVResponse) XXX_DiscardUnknown() {
xxx_messageInfo_HashKVResponse.DiscardUnknown(m)
}
var xxx_messageInfo_HashKVResponse proto.InternalMessageInfo
func (m *HashKVResponse) GetHeader() *ResponseHeader {
if m != nil {
return m.Header
}
return nil
}
func (m *HashKVResponse) GetHash() uint32 {
if m != nil {
return m.Hash
}
return 0
}
func (m *HashKVResponse) GetCompactRevision() int64 {
if m != nil {
return m.CompactRevision
}
return 0
}
func (m *HashKVResponse) GetHashRevision() int64 {
if m != nil {
return m.HashRevision
}
return 0
}
type HashResponse struct {
Header *ResponseHeader `protobuf:"bytes,1,opt,name=header,proto3" json:"header,omitempty"`
// hash is the hash value computed from the responding member's KV's backend.
Hash uint32 `protobuf:"varint,2,opt,name=hash,proto3" json:"hash,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *HashResponse) Reset() { *m = HashResponse{} }
func (m *HashResponse) String() string { return proto.CompactTextString(m) }
func (*HashResponse) ProtoMessage() {}
func (*HashResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_77a6da22d6a3feb1, []int{17}
}
func (m *HashResponse) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
}
func (m *HashResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
if deterministic {
return xxx_messageInfo_HashResponse.Marshal(b, m, deterministic)
} else {
b = b[:cap(b)]
n, err := m.MarshalToSizedBuffer(b)
if err != nil {
return nil, err
}
return b[:n], nil
}
}
func (m *HashResponse) XXX_Merge(src proto.Message) {
xxx_messageInfo_HashResponse.Merge(m, src)
}
func (m *HashResponse) XXX_Size() int {
return m.Size()
}
func (m *HashResponse) XXX_DiscardUnknown() {
xxx_messageInfo_HashResponse.DiscardUnknown(m)
}
var xxx_messageInfo_HashResponse proto.InternalMessageInfo
func (m *HashResponse) GetHeader() *ResponseHeader {
if m != nil {
return m.Header
}
return nil
}
func (m *HashResponse) GetHash() uint32 {
if m != nil {
return m.Hash
}
return 0
}
type SnapshotRequest struct {
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *SnapshotRequest) Reset() { *m = SnapshotRequest{} }
func (m *SnapshotRequest) String() string { return proto.CompactTextString(m) }
func (*SnapshotRequest) ProtoMessage() {}
func (*SnapshotRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_77a6da22d6a3feb1, []int{18}
}
func (m *SnapshotRequest) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
}
func (m *SnapshotRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
if deterministic {
return xxx_messageInfo_SnapshotRequest.Marshal(b, m, deterministic)
} else {
b = b[:cap(b)]
n, err := m.MarshalToSizedBuffer(b)
if err != nil {
return nil, err
}
return b[:n], nil
}
}
func (m *SnapshotRequest) XXX_Merge(src proto.Message) {
xxx_messageInfo_SnapshotRequest.Merge(m, src)
}
func (m *SnapshotRequest) XXX_Size() int {
return m.Size()
}
func (m *SnapshotRequest) XXX_DiscardUnknown() {
xxx_messageInfo_SnapshotRequest.DiscardUnknown(m)
}
var xxx_messageInfo_SnapshotRequest proto.InternalMessageInfo
type SnapshotResponse struct {
// header has the current key-value store information. The first header in the snapshot
// stream indicates the point in time of the snapshot.
Header *ResponseHeader `protobuf:"bytes,1,opt,name=header,proto3" json:"header,omitempty"`
// remaining_bytes is the number of blob bytes to be sent after this message
RemainingBytes uint64 `protobuf:"varint,2,opt,name=remaining_bytes,json=remainingBytes,proto3" json:"remaining_bytes,omitempty"`
// blob contains the next chunk of the snapshot in the snapshot stream.
Blob []byte `protobuf:"bytes,3,opt,name=blob,proto3" json:"blob,omitempty"`
// local version of server that created the snapshot.
// In cluster with binaries with different version, each cluster can return different result.
// Informs which etcd server version should be used when restoring the snapshot.
Version string `protobuf:"bytes,4,opt,name=version,proto3" json:"version,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *SnapshotResponse) Reset() { *m = SnapshotResponse{} }
func (m *SnapshotResponse) String() string { return proto.CompactTextString(m) }
func (*SnapshotResponse) ProtoMessage() {}
func (*SnapshotResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_77a6da22d6a3feb1, []int{19}
}
func (m *SnapshotResponse) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
}
func (m *SnapshotResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
if deterministic {
return xxx_messageInfo_SnapshotResponse.Marshal(b, m, deterministic)
} else {
b = b[:cap(b)]
n, err := m.MarshalToSizedBuffer(b)
if err != nil {
return nil, err
}
return b[:n], nil
}
}
func (m *SnapshotResponse) XXX_Merge(src proto.Message) {
xxx_messageInfo_SnapshotResponse.Merge(m, src)
}
func (m *SnapshotResponse) XXX_Size() int {
return m.Size()
}
func (m *SnapshotResponse) XXX_DiscardUnknown() {
xxx_messageInfo_SnapshotResponse.DiscardUnknown(m)
}
var xxx_messageInfo_SnapshotResponse proto.InternalMessageInfo
func (m *SnapshotResponse) GetHeader() *ResponseHeader {
if m != nil {
return m.Header
}
return nil
}
func (m *SnapshotResponse) GetRemainingBytes() uint64 {
if m != nil {
return m.RemainingBytes
}
return 0
}
func (m *SnapshotResponse) GetBlob() []byte {
if m != nil {
return m.Blob
}
return nil
}
func (m *SnapshotResponse) GetVersion() string {
if m != nil {
return m.Version
}
return ""
}
type WatchRequest struct {
// request_union is a request to either create a new watcher or cancel an existing watcher.
//
// Types that are valid to be assigned to RequestUnion:
// *WatchRequest_CreateRequest
// *WatchRequest_CancelRequest
// *WatchRequest_ProgressRequest
RequestUnion isWatchRequest_RequestUnion `protobuf_oneof:"request_union"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *WatchRequest) Reset() { *m = WatchRequest{} }
func (m *WatchRequest) String() string { return proto.CompactTextString(m) }
func (*WatchRequest) ProtoMessage() {}
func (*WatchRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_77a6da22d6a3feb1, []int{20}
}
func (m *WatchRequest) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
}
func (m *WatchRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
if deterministic {
return xxx_messageInfo_WatchRequest.Marshal(b, m, deterministic)
} else {
b = b[:cap(b)]
n, err := m.MarshalToSizedBuffer(b)
if err != nil {
return nil, err
}
return b[:n], nil
}
}
func (m *WatchRequest) XXX_Merge(src proto.Message) {
xxx_messageInfo_WatchRequest.Merge(m, src)
}
func (m *WatchRequest) XXX_Size() int {
return m.Size()
}
func (m *WatchRequest) XXX_DiscardUnknown() {
xxx_messageInfo_WatchRequest.DiscardUnknown(m)
}
var xxx_messageInfo_WatchRequest proto.InternalMessageInfo
type isWatchRequest_RequestUnion interface {
isWatchRequest_RequestUnion()
MarshalTo([]byte) (int, error)
Size() int
}
type WatchRequest_CreateRequest struct {
CreateRequest *WatchCreateRequest `protobuf:"bytes,1,opt,name=create_request,json=createRequest,proto3,oneof" json:"create_request,omitempty"`
}
type WatchRequest_CancelRequest struct {
CancelRequest *WatchCancelRequest `protobuf:"bytes,2,opt,name=cancel_request,json=cancelRequest,proto3,oneof" json:"cancel_request,omitempty"`
}
type WatchRequest_ProgressRequest struct {
ProgressRequest *WatchProgressRequest `protobuf:"bytes,3,opt,name=progress_request,json=progressRequest,proto3,oneof" json:"progress_request,omitempty"`
}
func (*WatchRequest_CreateRequest) isWatchRequest_RequestUnion() {}
func (*WatchRequest_CancelRequest) isWatchRequest_RequestUnion() {}
func (*WatchRequest_ProgressRequest) isWatchRequest_RequestUnion() {}
func (m *WatchRequest) GetRequestUnion() isWatchRequest_RequestUnion {
if m != nil {
return m.RequestUnion
}
return nil
}
func (m *WatchRequest) GetCreateRequest() *WatchCreateRequest {
if x, ok := m.GetRequestUnion().(*WatchRequest_CreateRequest); ok {
return x.CreateRequest
}
return nil
}
func (m *WatchRequest) GetCancelRequest() *WatchCancelRequest {
if x, ok := m.GetRequestUnion().(*WatchRequest_CancelRequest); ok {
return x.CancelRequest
}
return nil
}
func (m *WatchRequest) GetProgressRequest() *WatchProgressRequest {
if x, ok := m.GetRequestUnion().(*WatchRequest_ProgressRequest); ok {
return x.ProgressRequest
}
return nil
}
// XXX_OneofWrappers is for the internal use of the proto package.
func (*WatchRequest) XXX_OneofWrappers() []interface{} {
return []interface{}{
(*WatchRequest_CreateRequest)(nil),
(*WatchRequest_CancelRequest)(nil),
(*WatchRequest_ProgressRequest)(nil),
}
}
type WatchCreateRequest struct {
// key is the key to register for watching.
Key []byte `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"`
// range_end is the end of the range [key, range_end) to watch. If range_end is not given,
// only the key argument is watched. If range_end is equal to '\0', all keys greater than
// or equal to the key argument are watched.
// If the range_end is one bit larger than the given key,
// then all keys with the prefix (the given key) will be watched.
RangeEnd []byte `protobuf:"bytes,2,opt,name=range_end,json=rangeEnd,proto3" json:"range_end,omitempty"`
// start_revision is an optional revision to watch from (inclusive). No start_revision is "now".
StartRevision int64 `protobuf:"varint,3,opt,name=start_revision,json=startRevision,proto3" json:"start_revision,omitempty"`
// progress_notify is set so that the etcd server will periodically send a WatchResponse with
// no events to the new watcher if there are no recent events. It is useful when clients
// wish to recover a disconnected watcher starting from a recent known revision.
// The etcd server may decide how often it will send notifications based on current load.
ProgressNotify bool `protobuf:"varint,4,opt,name=progress_notify,json=progressNotify,proto3" json:"progress_notify,omitempty"`
// filters filter the events at server side before it sends back to the watcher.
Filters []WatchCreateRequest_FilterType `protobuf:"varint,5,rep,packed,name=filters,proto3,enum=etcdserverpb.WatchCreateRequest_FilterType" json:"filters,omitempty"`
// If prev_kv is set, created watcher gets the previous KV before the event happens.
// If the previous KV is already compacted, nothing will be returned.
PrevKv bool `protobuf:"varint,6,opt,name=prev_kv,json=prevKv,proto3" json:"prev_kv,omitempty"`
// If watch_id is provided and non-zero, it will be assigned to this watcher.
// Since creating a watcher in etcd is not a synchronous operation,
// this can be used ensure that ordering is correct when creating multiple
// watchers on the same stream. Creating a watcher with an ID already in
// use on the stream will cause an error to be returned.
WatchId int64 `protobuf:"varint,7,opt,name=watch_id,json=watchId,proto3" json:"watch_id,omitempty"`
// fragment enables splitting large revisions into multiple watch responses.
Fragment bool `protobuf:"varint,8,opt,name=fragment,proto3" json:"fragment,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *WatchCreateRequest) Reset() { *m = WatchCreateRequest{} }
func (m *WatchCreateRequest) String() string { return proto.CompactTextString(m) }
func (*WatchCreateRequest) ProtoMessage() {}
func (*WatchCreateRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_77a6da22d6a3feb1, []int{21}
}
func (m *WatchCreateRequest) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
}
func (m *WatchCreateRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
if deterministic {
return xxx_messageInfo_WatchCreateRequest.Marshal(b, m, deterministic)
} else {
b = b[:cap(b)]
n, err := m.MarshalToSizedBuffer(b)
if err != nil {
return nil, err
}
return b[:n], nil
}
}
func (m *WatchCreateRequest) XXX_Merge(src proto.Message) {
xxx_messageInfo_WatchCreateRequest.Merge(m, src)
}
func (m *WatchCreateRequest) XXX_Size() int {
return m.Size()
}
func (m *WatchCreateRequest) XXX_DiscardUnknown() {
xxx_messageInfo_WatchCreateRequest.DiscardUnknown(m)
}
var xxx_messageInfo_WatchCreateRequest proto.InternalMessageInfo
func (m *WatchCreateRequest) GetKey() []byte {
if m != nil {
return m.Key
}
return nil
}
func (m *WatchCreateRequest) GetRangeEnd() []byte {
if m != nil {
return m.RangeEnd
}
return nil
}
func (m *WatchCreateRequest) GetStartRevision() int64 {
if m != nil {
return m.StartRevision
}
return 0
}
func (m *WatchCreateRequest) GetProgressNotify() bool {
if m != nil {
return m.ProgressNotify
}
return false
}
func (m *WatchCreateRequest) GetFilters() []WatchCreateRequest_FilterType {
if m != nil {
return m.Filters
}
return nil
}
func (m *WatchCreateRequest) GetPrevKv() bool {
if m != nil {
return m.PrevKv
}
return false
}
func (m *WatchCreateRequest) GetWatchId() int64 {
if m != nil {
return m.WatchId
}
return 0
}
func (m *WatchCreateRequest) GetFragment() bool {
if m != nil {
return m.Fragment
}
return false
}
type WatchCancelRequest struct {
// watch_id is the watcher id to cancel so that no more events are transmitted.
WatchId int64 `protobuf:"varint,1,opt,name=watch_id,json=watchId,proto3" json:"watch_id,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *WatchCancelRequest) Reset() { *m = WatchCancelRequest{} }
func (m *WatchCancelRequest) String() string { return proto.CompactTextString(m) }
func (*WatchCancelRequest) ProtoMessage() {}
func (*WatchCancelRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_77a6da22d6a3feb1, []int{22}
}
func (m *WatchCancelRequest) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
}
func (m *WatchCancelRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
if deterministic {
return xxx_messageInfo_WatchCancelRequest.Marshal(b, m, deterministic)
} else {
b = b[:cap(b)]
n, err := m.MarshalToSizedBuffer(b)
if err != nil {
return nil, err
}
return b[:n], nil
}
}
func (m *WatchCancelRequest) XXX_Merge(src proto.Message) {
xxx_messageInfo_WatchCancelRequest.Merge(m, src)
}
func (m *WatchCancelRequest) XXX_Size() int {
return m.Size()
}
func (m *WatchCancelRequest) XXX_DiscardUnknown() {
xxx_messageInfo_WatchCancelRequest.DiscardUnknown(m)
}
var xxx_messageInfo_WatchCancelRequest proto.InternalMessageInfo
func (m *WatchCancelRequest) GetWatchId() int64 {
if m != nil {
return m.WatchId
}
return 0
}
// Requests the a watch stream progress status be sent in the watch response stream as soon as
// possible.
type WatchProgressRequest struct {
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *WatchProgressRequest) Reset() { *m = WatchProgressRequest{} }
func (m *WatchProgressRequest) String() string { return proto.CompactTextString(m) }
func (*WatchProgressRequest) ProtoMessage() {}
func (*WatchProgressRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_77a6da22d6a3feb1, []int{23}
}
func (m *WatchProgressRequest) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
}
func (m *WatchProgressRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
if deterministic {
return xxx_messageInfo_WatchProgressRequest.Marshal(b, m, deterministic)
} else {
b = b[:cap(b)]
n, err := m.MarshalToSizedBuffer(b)
if err != nil {
return nil, err
}
return b[:n], nil
}
}
func (m *WatchProgressRequest) XXX_Merge(src proto.Message) {
xxx_messageInfo_WatchProgressRequest.Merge(m, src)
}
func (m *WatchProgressRequest) XXX_Size() int {
return m.Size()
}
func (m *WatchProgressRequest) XXX_DiscardUnknown() {
xxx_messageInfo_WatchProgressRequest.DiscardUnknown(m)
}
var xxx_messageInfo_WatchProgressRequest proto.InternalMessageInfo
type WatchResponse struct {
Header *ResponseHeader `protobuf:"bytes,1,opt,name=header,proto3" json:"header,omitempty"`
// watch_id is the ID of the watcher that corresponds to the response.
WatchId int64 `protobuf:"varint,2,opt,name=watch_id,json=watchId,proto3" json:"watch_id,omitempty"`
// created is set to true if the response is for a create watch request.
// The client should record the watch_id and expect to receive events for
// the created watcher from the same stream.
// All events sent to the created watcher will attach with the same watch_id.
Created bool `protobuf:"varint,3,opt,name=created,proto3" json:"created,omitempty"`
// canceled is set to true if the response is for a cancel watch request
// or if the start_revision has already been compacted.
// No further events will be sent to the canceled watcher.
Canceled bool `protobuf:"varint,4,opt,name=canceled,proto3" json:"canceled,omitempty"`
// compact_revision is set to the minimum index if a watcher tries to watch
// at a compacted index.
//
// This happens when creating a watcher at a compacted revision or the watcher cannot
// catch up with the progress of the key-value store.
//
// The client should treat the watcher as canceled and should not try to create any
// watcher with the same start_revision again.
CompactRevision int64 `protobuf:"varint,5,opt,name=compact_revision,json=compactRevision,proto3" json:"compact_revision,omitempty"`
// cancel_reason indicates the reason for canceling the watcher.
CancelReason string `protobuf:"bytes,6,opt,name=cancel_reason,json=cancelReason,proto3" json:"cancel_reason,omitempty"`
// framgment is true if large watch response was split over multiple responses.
Fragment bool `protobuf:"varint,7,opt,name=fragment,proto3" json:"fragment,omitempty"`
Events []*mvccpb.Event `protobuf:"bytes,11,rep,name=events,proto3" json:"events,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *WatchResponse) Reset() { *m = WatchResponse{} }
func (m *WatchResponse) String() string { return proto.CompactTextString(m) }
func (*WatchResponse) ProtoMessage() {}
func (*WatchResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_77a6da22d6a3feb1, []int{24}
}
func (m *WatchResponse) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
}
func (m *WatchResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
if deterministic {
return xxx_messageInfo_WatchResponse.Marshal(b, m, deterministic)
} else {
b = b[:cap(b)]
n, err := m.MarshalToSizedBuffer(b)
if err != nil {
return nil, err
}
return b[:n], nil
}
}
func (m *WatchResponse) XXX_Merge(src proto.Message) {
xxx_messageInfo_WatchResponse.Merge(m, src)
}
func (m *WatchResponse) XXX_Size() int {
return m.Size()
}
func (m *WatchResponse) XXX_DiscardUnknown() {
xxx_messageInfo_WatchResponse.DiscardUnknown(m)
}
var xxx_messageInfo_WatchResponse proto.InternalMessageInfo
func (m *WatchResponse) GetHeader() *ResponseHeader {
if m != nil {
return m.Header
}
return nil
}
func (m *WatchResponse) GetWatchId() int64 {
if m != nil {
return m.WatchId
}
return 0
}
func (m *WatchResponse) GetCreated() bool {
if m != nil {
return m.Created
}
return false
}
func (m *WatchResponse) GetCanceled() bool {
if m != nil {
return m.Canceled
}
return false
}
func (m *WatchResponse) GetCompactRevision() int64 {
if m != nil {
return m.CompactRevision
}
return 0
}
func (m *WatchResponse) GetCancelReason() string {
if m != nil {
return m.CancelReason
}
return ""
}
func (m *WatchResponse) GetFragment() bool {
if m != nil {
return m.Fragment
}
return false
}
func (m *WatchResponse) GetEvents() []*mvccpb.Event {
if m != nil {
return m.Events
}
return nil
}
type LeaseGrantRequest struct {
// TTL is the advisory time-to-live in seconds. Expired lease will return -1.
TTL int64 `protobuf:"varint,1,opt,name=TTL,proto3" json:"TTL,omitempty"`
// ID is the requested ID for the lease. If ID is set to 0, the lessor chooses an ID.
ID int64 `protobuf:"varint,2,opt,name=ID,proto3" json:"ID,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *LeaseGrantRequest) Reset() { *m = LeaseGrantRequest{} }
func (m *LeaseGrantRequest) String() string { return proto.CompactTextString(m) }
func (*LeaseGrantRequest) ProtoMessage() {}
func (*LeaseGrantRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_77a6da22d6a3feb1, []int{25}
}
func (m *LeaseGrantRequest) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
}
func (m *LeaseGrantRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
if deterministic {
return xxx_messageInfo_LeaseGrantRequest.Marshal(b, m, deterministic)
} else {
b = b[:cap(b)]
n, err := m.MarshalToSizedBuffer(b)
if err != nil {
return nil, err
}
return b[:n], nil
}
}
func (m *LeaseGrantRequest) XXX_Merge(src proto.Message) {
xxx_messageInfo_LeaseGrantRequest.Merge(m, src)
}
func (m *LeaseGrantRequest) XXX_Size() int {
return m.Size()
}
func (m *LeaseGrantRequest) XXX_DiscardUnknown() {
xxx_messageInfo_LeaseGrantRequest.DiscardUnknown(m)
}
var xxx_messageInfo_LeaseGrantRequest proto.InternalMessageInfo
func (m *LeaseGrantRequest) GetTTL() int64 {
if m != nil {
return m.TTL
}
return 0
}
func (m *LeaseGrantRequest) GetID() int64 {
if m != nil {
return m.ID
}
return 0
}
type LeaseGrantResponse struct {
Header *ResponseHeader `protobuf:"bytes,1,opt,name=header,proto3" json:"header,omitempty"`
// ID is the lease ID for the granted lease.
ID int64 `protobuf:"varint,2,opt,name=ID,proto3" json:"ID,omitempty"`
// TTL is the server chosen lease time-to-live in seconds.
TTL int64 `protobuf:"varint,3,opt,name=TTL,proto3" json:"TTL,omitempty"`
Error string `protobuf:"bytes,4,opt,name=error,proto3" json:"error,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *LeaseGrantResponse) Reset() { *m = LeaseGrantResponse{} }
func (m *LeaseGrantResponse) String() string { return proto.CompactTextString(m) }
func (*LeaseGrantResponse) ProtoMessage() {}
func (*LeaseGrantResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_77a6da22d6a3feb1, []int{26}
}
func (m *LeaseGrantResponse) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
}
func (m *LeaseGrantResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
if deterministic {
return xxx_messageInfo_LeaseGrantResponse.Marshal(b, m, deterministic)
} else {
b = b[:cap(b)]
n, err := m.MarshalToSizedBuffer(b)
if err != nil {
return nil, err
}
return b[:n], nil
}
}
func (m *LeaseGrantResponse) XXX_Merge(src proto.Message) {
xxx_messageInfo_LeaseGrantResponse.Merge(m, src)
}
func (m *LeaseGrantResponse) XXX_Size() int {
return m.Size()
}
func (m *LeaseGrantResponse) XXX_DiscardUnknown() {
xxx_messageInfo_LeaseGrantResponse.DiscardUnknown(m)
}
var xxx_messageInfo_LeaseGrantResponse proto.InternalMessageInfo
func (m *LeaseGrantResponse) GetHeader() *ResponseHeader {
if m != nil {
return m.Header
}
return nil
}
func (m *LeaseGrantResponse) GetID() int64 {
if m != nil {
return m.ID
}
return 0
}
func (m *LeaseGrantResponse) GetTTL() int64 {
if m != nil {
return m.TTL
}
return 0
}
func (m *LeaseGrantResponse) GetError() string {
if m != nil {
return m.Error
}
return ""
}
type LeaseRevokeRequest struct {
// ID is the lease ID to revoke. When the ID is revoked, all associated keys will be deleted.
ID int64 `protobuf:"varint,1,opt,name=ID,proto3" json:"ID,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *LeaseRevokeRequest) Reset() { *m = LeaseRevokeRequest{} }
func (m *LeaseRevokeRequest) String() string { return proto.CompactTextString(m) }
func (*LeaseRevokeRequest) ProtoMessage() {}
func (*LeaseRevokeRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_77a6da22d6a3feb1, []int{27}
}
func (m *LeaseRevokeRequest) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
}
func (m *LeaseRevokeRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
if deterministic {
return xxx_messageInfo_LeaseRevokeRequest.Marshal(b, m, deterministic)
} else {
b = b[:cap(b)]
n, err := m.MarshalToSizedBuffer(b)
if err != nil {
return nil, err
}
return b[:n], nil
}
}
func (m *LeaseRevokeRequest) XXX_Merge(src proto.Message) {
xxx_messageInfo_LeaseRevokeRequest.Merge(m, src)
}
func (m *LeaseRevokeRequest) XXX_Size() int {
return m.Size()
}
func (m *LeaseRevokeRequest) XXX_DiscardUnknown() {
xxx_messageInfo_LeaseRevokeRequest.DiscardUnknown(m)
}
var xxx_messageInfo_LeaseRevokeRequest proto.InternalMessageInfo
func (m *LeaseRevokeRequest) GetID() int64 {
if m != nil {
return m.ID
}
return 0
}
type LeaseRevokeResponse struct {
Header *ResponseHeader `protobuf:"bytes,1,opt,name=header,proto3" json:"header,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *LeaseRevokeResponse) Reset() { *m = LeaseRevokeResponse{} }
func (m *LeaseRevokeResponse) String() string { return proto.CompactTextString(m) }
func (*LeaseRevokeResponse) ProtoMessage() {}
func (*LeaseRevokeResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_77a6da22d6a3feb1, []int{28}
}
func (m *LeaseRevokeResponse) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
}
func (m *LeaseRevokeResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
if deterministic {
return xxx_messageInfo_LeaseRevokeResponse.Marshal(b, m, deterministic)
} else {
b = b[:cap(b)]
n, err := m.MarshalToSizedBuffer(b)
if err != nil {
return nil, err
}
return b[:n], nil
}
}
func (m *LeaseRevokeResponse) XXX_Merge(src proto.Message) {
xxx_messageInfo_LeaseRevokeResponse.Merge(m, src)
}
func (m *LeaseRevokeResponse) XXX_Size() int {
return m.Size()
}
func (m *LeaseRevokeResponse) XXX_DiscardUnknown() {
xxx_messageInfo_LeaseRevokeResponse.DiscardUnknown(m)
}
var xxx_messageInfo_LeaseRevokeResponse proto.InternalMessageInfo
func (m *LeaseRevokeResponse) GetHeader() *ResponseHeader {
if m != nil {
return m.Header
}
return nil
}
type LeaseCheckpoint struct {
// ID is the lease ID to checkpoint.
ID int64 `protobuf:"varint,1,opt,name=ID,proto3" json:"ID,omitempty"`
// Remaining_TTL is the remaining time until expiry of the lease.
Remaining_TTL int64 `protobuf:"varint,2,opt,name=remaining_TTL,json=remainingTTL,proto3" json:"remaining_TTL,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *LeaseCheckpoint) Reset() { *m = LeaseCheckpoint{} }
func (m *LeaseCheckpoint) String() string { return proto.CompactTextString(m) }
func (*LeaseCheckpoint) ProtoMessage() {}
func (*LeaseCheckpoint) Descriptor() ([]byte, []int) {
return fileDescriptor_77a6da22d6a3feb1, []int{29}
}
func (m *LeaseCheckpoint) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
}
func (m *LeaseCheckpoint) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
if deterministic {
return xxx_messageInfo_LeaseCheckpoint.Marshal(b, m, deterministic)
} else {
b = b[:cap(b)]
n, err := m.MarshalToSizedBuffer(b)
if err != nil {
return nil, err
}
return b[:n], nil
}
}
func (m *LeaseCheckpoint) XXX_Merge(src proto.Message) {
xxx_messageInfo_LeaseCheckpoint.Merge(m, src)
}
func (m *LeaseCheckpoint) XXX_Size() int {
return m.Size()
}
func (m *LeaseCheckpoint) XXX_DiscardUnknown() {
xxx_messageInfo_LeaseCheckpoint.DiscardUnknown(m)
}
var xxx_messageInfo_LeaseCheckpoint proto.InternalMessageInfo
func (m *LeaseCheckpoint) GetID() int64 {
if m != nil {
return m.ID
}
return 0
}
func (m *LeaseCheckpoint) GetRemaining_TTL() int64 {
if m != nil {
return m.Remaining_TTL
}
return 0
}
type LeaseCheckpointRequest struct {
Checkpoints []*LeaseCheckpoint `protobuf:"bytes,1,rep,name=checkpoints,proto3" json:"checkpoints,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *LeaseCheckpointRequest) Reset() { *m = LeaseCheckpointRequest{} }
func (m *LeaseCheckpointRequest) String() string { return proto.CompactTextString(m) }
func (*LeaseCheckpointRequest) ProtoMessage() {}
func (*LeaseCheckpointRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_77a6da22d6a3feb1, []int{30}
}
func (m *LeaseCheckpointRequest) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
}
func (m *LeaseCheckpointRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
if deterministic {
return xxx_messageInfo_LeaseCheckpointRequest.Marshal(b, m, deterministic)
} else {
b = b[:cap(b)]
n, err := m.MarshalToSizedBuffer(b)
if err != nil {
return nil, err
}
return b[:n], nil
}
}
func (m *LeaseCheckpointRequest) XXX_Merge(src proto.Message) {
xxx_messageInfo_LeaseCheckpointRequest.Merge(m, src)
}
func (m *LeaseCheckpointRequest) XXX_Size() int {
return m.Size()
}
func (m *LeaseCheckpointRequest) XXX_DiscardUnknown() {
xxx_messageInfo_LeaseCheckpointRequest.DiscardUnknown(m)
}
var xxx_messageInfo_LeaseCheckpointRequest proto.InternalMessageInfo
func (m *LeaseCheckpointRequest) GetCheckpoints() []*LeaseCheckpoint {
if m != nil {
return m.Checkpoints
}
return nil
}
type LeaseCheckpointResponse struct {
Header *ResponseHeader `protobuf:"bytes,1,opt,name=header,proto3" json:"header,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *LeaseCheckpointResponse) Reset() { *m = LeaseCheckpointResponse{} }
func (m *LeaseCheckpointResponse) String() string { return proto.CompactTextString(m) }
func (*LeaseCheckpointResponse) ProtoMessage() {}
func (*LeaseCheckpointResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_77a6da22d6a3feb1, []int{31}
}
func (m *LeaseCheckpointResponse) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
}
func (m *LeaseCheckpointResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
if deterministic {
return xxx_messageInfo_LeaseCheckpointResponse.Marshal(b, m, deterministic)
} else {
b = b[:cap(b)]
n, err := m.MarshalToSizedBuffer(b)
if err != nil {
return nil, err
}
return b[:n], nil
}
}
func (m *LeaseCheckpointResponse) XXX_Merge(src proto.Message) {
xxx_messageInfo_LeaseCheckpointResponse.Merge(m, src)
}
func (m *LeaseCheckpointResponse) XXX_Size() int {
return m.Size()
}
func (m *LeaseCheckpointResponse) XXX_DiscardUnknown() {
xxx_messageInfo_LeaseCheckpointResponse.DiscardUnknown(m)
}
var xxx_messageInfo_LeaseCheckpointResponse proto.InternalMessageInfo
func (m *LeaseCheckpointResponse) GetHeader() *ResponseHeader {
if m != nil {
return m.Header
}
return nil
}
type LeaseKeepAliveRequest struct {
// ID is the lease ID for the lease to keep alive.
ID int64 `protobuf:"varint,1,opt,name=ID,proto3" json:"ID,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *LeaseKeepAliveRequest) Reset() { *m = LeaseKeepAliveRequest{} }
func (m *LeaseKeepAliveRequest) String() string { return proto.CompactTextString(m) }
func (*LeaseKeepAliveRequest) ProtoMessage() {}
func (*LeaseKeepAliveRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_77a6da22d6a3feb1, []int{32}
}
func (m *LeaseKeepAliveRequest) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
}
func (m *LeaseKeepAliveRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
if deterministic {
return xxx_messageInfo_LeaseKeepAliveRequest.Marshal(b, m, deterministic)
} else {
b = b[:cap(b)]
n, err := m.MarshalToSizedBuffer(b)
if err != nil {
return nil, err
}
return b[:n], nil
}
}
func (m *LeaseKeepAliveRequest) XXX_Merge(src proto.Message) {
xxx_messageInfo_LeaseKeepAliveRequest.Merge(m, src)
}
func (m *LeaseKeepAliveRequest) XXX_Size() int {
return m.Size()
}
func (m *LeaseKeepAliveRequest) XXX_DiscardUnknown() {
xxx_messageInfo_LeaseKeepAliveRequest.DiscardUnknown(m)
}
var xxx_messageInfo_LeaseKeepAliveRequest proto.InternalMessageInfo
func (m *LeaseKeepAliveRequest) GetID() int64 {
if m != nil {
return m.ID
}
return 0
}
type LeaseKeepAliveResponse struct {
Header *ResponseHeader `protobuf:"bytes,1,opt,name=header,proto3" json:"header,omitempty"`
// ID is the lease ID from the keep alive request.
ID int64 `protobuf:"varint,2,opt,name=ID,proto3" json:"ID,omitempty"`
// TTL is the new time-to-live for the lease.
TTL int64 `protobuf:"varint,3,opt,name=TTL,proto3" json:"TTL,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *LeaseKeepAliveResponse) Reset() { *m = LeaseKeepAliveResponse{} }
func (m *LeaseKeepAliveResponse) String() string { return proto.CompactTextString(m) }
func (*LeaseKeepAliveResponse) ProtoMessage() {}
func (*LeaseKeepAliveResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_77a6da22d6a3feb1, []int{33}
}
func (m *LeaseKeepAliveResponse) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
}
func (m *LeaseKeepAliveResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
if deterministic {
return xxx_messageInfo_LeaseKeepAliveResponse.Marshal(b, m, deterministic)
} else {
b = b[:cap(b)]
n, err := m.MarshalToSizedBuffer(b)
if err != nil {
return nil, err
}
return b[:n], nil
}
}
func (m *LeaseKeepAliveResponse) XXX_Merge(src proto.Message) {
xxx_messageInfo_LeaseKeepAliveResponse.Merge(m, src)
}
func (m *LeaseKeepAliveResponse) XXX_Size() int {
return m.Size()
}
func (m *LeaseKeepAliveResponse) XXX_DiscardUnknown() {
xxx_messageInfo_LeaseKeepAliveResponse.DiscardUnknown(m)
}
var xxx_messageInfo_LeaseKeepAliveResponse proto.InternalMessageInfo
func (m *LeaseKeepAliveResponse) GetHeader() *ResponseHeader {
if m != nil {
return m.Header
}
return nil
}
func (m *LeaseKeepAliveResponse) GetID() int64 {
if m != nil {
return m.ID
}
return 0
}
func (m *LeaseKeepAliveResponse) GetTTL() int64 {
if m != nil {
return m.TTL
}
return 0
}
type LeaseTimeToLiveRequest struct {
// ID is the lease ID for the lease.
ID int64 `protobuf:"varint,1,opt,name=ID,proto3" json:"ID,omitempty"`
// keys is true to query all the keys attached to this lease.
Keys bool `protobuf:"varint,2,opt,name=keys,proto3" json:"keys,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *LeaseTimeToLiveRequest) Reset() { *m = LeaseTimeToLiveRequest{} }
func (m *LeaseTimeToLiveRequest) String() string { return proto.CompactTextString(m) }
func (*LeaseTimeToLiveRequest) ProtoMessage() {}
func (*LeaseTimeToLiveRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_77a6da22d6a3feb1, []int{34}
}
func (m *LeaseTimeToLiveRequest) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
}
func (m *LeaseTimeToLiveRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
if deterministic {
return xxx_messageInfo_LeaseTimeToLiveRequest.Marshal(b, m, deterministic)
} else {
b = b[:cap(b)]
n, err := m.MarshalToSizedBuffer(b)
if err != nil {
return nil, err
}
return b[:n], nil
}
}
func (m *LeaseTimeToLiveRequest) XXX_Merge(src proto.Message) {
xxx_messageInfo_LeaseTimeToLiveRequest.Merge(m, src)
}
func (m *LeaseTimeToLiveRequest) XXX_Size() int {
return m.Size()
}
func (m *LeaseTimeToLiveRequest) XXX_DiscardUnknown() {
xxx_messageInfo_LeaseTimeToLiveRequest.DiscardUnknown(m)
}
var xxx_messageInfo_LeaseTimeToLiveRequest proto.InternalMessageInfo
func (m *LeaseTimeToLiveRequest) GetID() int64 {
if m != nil {
return m.ID
}
return 0
}
func (m *LeaseTimeToLiveRequest) GetKeys() bool {
if m != nil {
return m.Keys
}
return false
}
type LeaseTimeToLiveResponse struct {
Header *ResponseHeader `protobuf:"bytes,1,opt,name=header,proto3" json:"header,omitempty"`
// ID is the lease ID from the keep alive request.
ID int64 `protobuf:"varint,2,opt,name=ID,proto3" json:"ID,omitempty"`
// TTL is the remaining TTL in seconds for the lease; the lease will expire in under TTL+1 seconds.
TTL int64 `protobuf:"varint,3,opt,name=TTL,proto3" json:"TTL,omitempty"`
// GrantedTTL is the initial granted time in seconds upon lease creation/renewal.
GrantedTTL int64 `protobuf:"varint,4,opt,name=grantedTTL,proto3" json:"grantedTTL,omitempty"`
// Keys is the list of keys attached to this lease.
Keys [][]byte `protobuf:"bytes,5,rep,name=keys,proto3" json:"keys,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *LeaseTimeToLiveResponse) Reset() { *m = LeaseTimeToLiveResponse{} }
func (m *LeaseTimeToLiveResponse) String() string { return proto.CompactTextString(m) }
func (*LeaseTimeToLiveResponse) ProtoMessage() {}
func (*LeaseTimeToLiveResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_77a6da22d6a3feb1, []int{35}
}
func (m *LeaseTimeToLiveResponse) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
}
func (m *LeaseTimeToLiveResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
if deterministic {
return xxx_messageInfo_LeaseTimeToLiveResponse.Marshal(b, m, deterministic)
} else {
b = b[:cap(b)]
n, err := m.MarshalToSizedBuffer(b)
if err != nil {
return nil, err
}
return b[:n], nil
}
}
func (m *LeaseTimeToLiveResponse) XXX_Merge(src proto.Message) {
xxx_messageInfo_LeaseTimeToLiveResponse.Merge(m, src)
}
func (m *LeaseTimeToLiveResponse) XXX_Size() int {
return m.Size()
}
func (m *LeaseTimeToLiveResponse) XXX_DiscardUnknown() {
xxx_messageInfo_LeaseTimeToLiveResponse.DiscardUnknown(m)
}
var xxx_messageInfo_LeaseTimeToLiveResponse proto.InternalMessageInfo
func (m *LeaseTimeToLiveResponse) GetHeader() *ResponseHeader {
if m != nil {
return m.Header
}
return nil
}
func (m *LeaseTimeToLiveResponse) GetID() int64 {
if m != nil {
return m.ID
}
return 0
}
func (m *LeaseTimeToLiveResponse) GetTTL() int64 {
if m != nil {
return m.TTL
}
return 0
}
func (m *LeaseTimeToLiveResponse) GetGrantedTTL() int64 {
if m != nil {
return m.GrantedTTL
}
return 0
}
func (m *LeaseTimeToLiveResponse) GetKeys() [][]byte {
if m != nil {
return m.Keys
}
return nil
}
type LeaseLeasesRequest struct {
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *LeaseLeasesRequest) Reset() { *m = LeaseLeasesRequest{} }
func (m *LeaseLeasesRequest) String() string { return proto.CompactTextString(m) }
func (*LeaseLeasesRequest) ProtoMessage() {}
func (*LeaseLeasesRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_77a6da22d6a3feb1, []int{36}
}
func (m *LeaseLeasesRequest) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
}
func (m *LeaseLeasesRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
if deterministic {
return xxx_messageInfo_LeaseLeasesRequest.Marshal(b, m, deterministic)
} else {
b = b[:cap(b)]
n, err := m.MarshalToSizedBuffer(b)
if err != nil {
return nil, err
}
return b[:n], nil
}
}
func (m *LeaseLeasesRequest) XXX_Merge(src proto.Message) {
xxx_messageInfo_LeaseLeasesRequest.Merge(m, src)
}
func (m *LeaseLeasesRequest) XXX_Size() int {
return m.Size()
}
func (m *LeaseLeasesRequest) XXX_DiscardUnknown() {
xxx_messageInfo_LeaseLeasesRequest.DiscardUnknown(m)
}
var xxx_messageInfo_LeaseLeasesRequest proto.InternalMessageInfo
type LeaseStatus struct {
ID int64 `protobuf:"varint,1,opt,name=ID,proto3" json:"ID,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *LeaseStatus) Reset() { *m = LeaseStatus{} }
func (m *LeaseStatus) String() string { return proto.CompactTextString(m) }
func (*LeaseStatus) ProtoMessage() {}
func (*LeaseStatus) Descriptor() ([]byte, []int) {
return fileDescriptor_77a6da22d6a3feb1, []int{37}
}
func (m *LeaseStatus) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
}
func (m *LeaseStatus) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
if deterministic {
return xxx_messageInfo_LeaseStatus.Marshal(b, m, deterministic)
} else {
b = b[:cap(b)]
n, err := m.MarshalToSizedBuffer(b)
if err != nil {
return nil, err
}
return b[:n], nil
}
}
func (m *LeaseStatus) XXX_Merge(src proto.Message) {
xxx_messageInfo_LeaseStatus.Merge(m, src)
}
func (m *LeaseStatus) XXX_Size() int {
return m.Size()
}
func (m *LeaseStatus) XXX_DiscardUnknown() {
xxx_messageInfo_LeaseStatus.DiscardUnknown(m)
}
var xxx_messageInfo_LeaseStatus proto.InternalMessageInfo
func (m *LeaseStatus) GetID() int64 {
if m != nil {
return m.ID
}
return 0
}
type LeaseLeasesResponse struct {
Header *ResponseHeader `protobuf:"bytes,1,opt,name=header,proto3" json:"header,omitempty"`
Leases []*LeaseStatus `protobuf:"bytes,2,rep,name=leases,proto3" json:"leases,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *LeaseLeasesResponse) Reset() { *m = LeaseLeasesResponse{} }
func (m *LeaseLeasesResponse) String() string { return proto.CompactTextString(m) }
func (*LeaseLeasesResponse) ProtoMessage() {}
func (*LeaseLeasesResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_77a6da22d6a3feb1, []int{38}
}
func (m *LeaseLeasesResponse) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
}
func (m *LeaseLeasesResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
if deterministic {
return xxx_messageInfo_LeaseLeasesResponse.Marshal(b, m, deterministic)
} else {
b = b[:cap(b)]
n, err := m.MarshalToSizedBuffer(b)
if err != nil {
return nil, err
}
return b[:n], nil
}
}
func (m *LeaseLeasesResponse) XXX_Merge(src proto.Message) {
xxx_messageInfo_LeaseLeasesResponse.Merge(m, src)
}
func (m *LeaseLeasesResponse) XXX_Size() int {
return m.Size()
}
func (m *LeaseLeasesResponse) XXX_DiscardUnknown() {
xxx_messageInfo_LeaseLeasesResponse.DiscardUnknown(m)
}
var xxx_messageInfo_LeaseLeasesResponse proto.InternalMessageInfo
func (m *LeaseLeasesResponse) GetHeader() *ResponseHeader {
if m != nil {
return m.Header
}
return nil
}
func (m *LeaseLeasesResponse) GetLeases() []*LeaseStatus {
if m != nil {
return m.Leases
}
return nil
}
type Member struct {
// ID is the member ID for this member.
ID uint64 `protobuf:"varint,1,opt,name=ID,proto3" json:"ID,omitempty"`
// name is the human-readable name of the member. If the member is not started, the name will be an empty string.
Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"`
// peerURLs is the list of URLs the member exposes to the cluster for communication.
PeerURLs []string `protobuf:"bytes,3,rep,name=peerURLs,proto3" json:"peerURLs,omitempty"`
// clientURLs is the list of URLs the member exposes to clients for communication. If the member is not started, clientURLs will be empty.
ClientURLs []string `protobuf:"bytes,4,rep,name=clientURLs,proto3" json:"clientURLs,omitempty"`
// isLearner indicates if the member is raft learner.
IsLearner bool `protobuf:"varint,5,opt,name=isLearner,proto3" json:"isLearner,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *Member) Reset() { *m = Member{} }
func (m *Member) String() string { return proto.CompactTextString(m) }
func (*Member) ProtoMessage() {}
func (*Member) Descriptor() ([]byte, []int) {
return fileDescriptor_77a6da22d6a3feb1, []int{39}
}
func (m *Member) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
}
func (m *Member) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
if deterministic {
return xxx_messageInfo_Member.Marshal(b, m, deterministic)
} else {
b = b[:cap(b)]
n, err := m.MarshalToSizedBuffer(b)
if err != nil {
return nil, err
}
return b[:n], nil
}
}
func (m *Member) XXX_Merge(src proto.Message) {
xxx_messageInfo_Member.Merge(m, src)
}
func (m *Member) XXX_Size() int {
return m.Size()
}
func (m *Member) XXX_DiscardUnknown() {
xxx_messageInfo_Member.DiscardUnknown(m)
}
var xxx_messageInfo_Member proto.InternalMessageInfo
func (m *Member) GetID() uint64 {
if m != nil {
return m.ID
}
return 0
}
func (m *Member) GetName() string {
if m != nil {
return m.Name
}
return ""
}
func (m *Member) GetPeerURLs() []string {
if m != nil {
return m.PeerURLs
}
return nil
}
func (m *Member) GetClientURLs() []string {
if m != nil {
return m.ClientURLs
}
return nil
}
func (m *Member) GetIsLearner() bool {
if m != nil {
return m.IsLearner
}
return false
}
type MemberAddRequest struct {
// peerURLs is the list of URLs the added member will use to communicate with the cluster.
PeerURLs []string `protobuf:"bytes,1,rep,name=peerURLs,proto3" json:"peerURLs,omitempty"`
// isLearner indicates if the added member is raft learner.
IsLearner bool `protobuf:"varint,2,opt,name=isLearner,proto3" json:"isLearner,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *MemberAddRequest) Reset() { *m = MemberAddRequest{} }
func (m *MemberAddRequest) String() string { return proto.CompactTextString(m) }
func (*MemberAddRequest) ProtoMessage() {}
func (*MemberAddRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_77a6da22d6a3feb1, []int{40}
}
func (m *MemberAddRequest) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
}
func (m *MemberAddRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
if deterministic {
return xxx_messageInfo_MemberAddRequest.Marshal(b, m, deterministic)
} else {
b = b[:cap(b)]
n, err := m.MarshalToSizedBuffer(b)
if err != nil {
return nil, err
}
return b[:n], nil
}
}
func (m *MemberAddRequest) XXX_Merge(src proto.Message) {
xxx_messageInfo_MemberAddRequest.Merge(m, src)
}
func (m *MemberAddRequest) XXX_Size() int {
return m.Size()
}
func (m *MemberAddRequest) XXX_DiscardUnknown() {
xxx_messageInfo_MemberAddRequest.DiscardUnknown(m)
}
var xxx_messageInfo_MemberAddRequest proto.InternalMessageInfo
func (m *MemberAddRequest) GetPeerURLs() []string {
if m != nil {
return m.PeerURLs
}
return nil
}
func (m *MemberAddRequest) GetIsLearner() bool {
if m != nil {
return m.IsLearner
}
return false
}
type MemberAddResponse struct {
Header *ResponseHeader `protobuf:"bytes,1,opt,name=header,proto3" json:"header,omitempty"`
// member is the member information for the added member.
Member *Member `protobuf:"bytes,2,opt,name=member,proto3" json:"member,omitempty"`
// members is a list of all members after adding the new member.
Members []*Member `protobuf:"bytes,3,rep,name=members,proto3" json:"members,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *MemberAddResponse) Reset() { *m = MemberAddResponse{} }
func (m *MemberAddResponse) String() string { return proto.CompactTextString(m) }
func (*MemberAddResponse) ProtoMessage() {}
func (*MemberAddResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_77a6da22d6a3feb1, []int{41}
}
func (m *MemberAddResponse) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
}
func (m *MemberAddResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
if deterministic {
return xxx_messageInfo_MemberAddResponse.Marshal(b, m, deterministic)
} else {
b = b[:cap(b)]
n, err := m.MarshalToSizedBuffer(b)
if err != nil {
return nil, err
}
return b[:n], nil
}
}
func (m *MemberAddResponse) XXX_Merge(src proto.Message) {
xxx_messageInfo_MemberAddResponse.Merge(m, src)
}
func (m *MemberAddResponse) XXX_Size() int {
return m.Size()
}
func (m *MemberAddResponse) XXX_DiscardUnknown() {
xxx_messageInfo_MemberAddResponse.DiscardUnknown(m)
}
var xxx_messageInfo_MemberAddResponse proto.InternalMessageInfo
func (m *MemberAddResponse) GetHeader() *ResponseHeader {
if m != nil {
return m.Header
}
return nil
}
func (m *MemberAddResponse) GetMember() *Member {
if m != nil {
return m.Member
}
return nil
}
func (m *MemberAddResponse) GetMembers() []*Member {
if m != nil {
return m.Members
}
return nil
}
type MemberRemoveRequest struct {
// ID is the member ID of the member to remove.
ID uint64 `protobuf:"varint,1,opt,name=ID,proto3" json:"ID,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *MemberRemoveRequest) Reset() { *m = MemberRemoveRequest{} }
func (m *MemberRemoveRequest) String() string { return proto.CompactTextString(m) }
func (*MemberRemoveRequest) ProtoMessage() {}
func (*MemberRemoveRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_77a6da22d6a3feb1, []int{42}
}
func (m *MemberRemoveRequest) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
}
func (m *MemberRemoveRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
if deterministic {
return xxx_messageInfo_MemberRemoveRequest.Marshal(b, m, deterministic)
} else {
b = b[:cap(b)]
n, err := m.MarshalToSizedBuffer(b)
if err != nil {
return nil, err
}
return b[:n], nil
}
}
func (m *MemberRemoveRequest) XXX_Merge(src proto.Message) {
xxx_messageInfo_MemberRemoveRequest.Merge(m, src)
}
func (m *MemberRemoveRequest) XXX_Size() int {
return m.Size()
}
func (m *MemberRemoveRequest) XXX_DiscardUnknown() {
xxx_messageInfo_MemberRemoveRequest.DiscardUnknown(m)
}
var xxx_messageInfo_MemberRemoveRequest proto.InternalMessageInfo
func (m *MemberRemoveRequest) GetID() uint64 {
if m != nil {
return m.ID
}
return 0
}
type MemberRemoveResponse struct {
Header *ResponseHeader `protobuf:"bytes,1,opt,name=header,proto3" json:"header,omitempty"`
// members is a list of all members after removing the member.
Members []*Member `protobuf:"bytes,2,rep,name=members,proto3" json:"members,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *MemberRemoveResponse) Reset() { *m = MemberRemoveResponse{} }
func (m *MemberRemoveResponse) String() string { return proto.CompactTextString(m) }
func (*MemberRemoveResponse) ProtoMessage() {}
func (*MemberRemoveResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_77a6da22d6a3feb1, []int{43}
}
func (m *MemberRemoveResponse) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
}
func (m *MemberRemoveResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
if deterministic {
return xxx_messageInfo_MemberRemoveResponse.Marshal(b, m, deterministic)
} else {
b = b[:cap(b)]
n, err := m.MarshalToSizedBuffer(b)
if err != nil {
return nil, err
}
return b[:n], nil
}
}
func (m *MemberRemoveResponse) XXX_Merge(src proto.Message) {
xxx_messageInfo_MemberRemoveResponse.Merge(m, src)
}
func (m *MemberRemoveResponse) XXX_Size() int {
return m.Size()
}
func (m *MemberRemoveResponse) XXX_DiscardUnknown() {
xxx_messageInfo_MemberRemoveResponse.DiscardUnknown(m)
}
var xxx_messageInfo_MemberRemoveResponse proto.InternalMessageInfo
func (m *MemberRemoveResponse) GetHeader() *ResponseHeader {
if m != nil {
return m.Header
}
return nil
}
func (m *MemberRemoveResponse) GetMembers() []*Member {
if m != nil {
return m.Members
}
return nil
}
type MemberUpdateRequest struct {
// ID is the member ID of the member to update.
ID uint64 `protobuf:"varint,1,opt,name=ID,proto3" json:"ID,omitempty"`
// peerURLs is the new list of URLs the member will use to communicate with the cluster.
PeerURLs []string `protobuf:"bytes,2,rep,name=peerURLs,proto3" json:"peerURLs,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *MemberUpdateRequest) Reset() { *m = MemberUpdateRequest{} }
func (m *MemberUpdateRequest) String() string { return proto.CompactTextString(m) }
func (*MemberUpdateRequest) ProtoMessage() {}
func (*MemberUpdateRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_77a6da22d6a3feb1, []int{44}
}
func (m *MemberUpdateRequest) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
}
func (m *MemberUpdateRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
if deterministic {
return xxx_messageInfo_MemberUpdateRequest.Marshal(b, m, deterministic)
} else {
b = b[:cap(b)]
n, err := m.MarshalToSizedBuffer(b)
if err != nil {
return nil, err
}
return b[:n], nil
}
}
func (m *MemberUpdateRequest) XXX_Merge(src proto.Message) {
xxx_messageInfo_MemberUpdateRequest.Merge(m, src)
}
func (m *MemberUpdateRequest) XXX_Size() int {
return m.Size()
}
func (m *MemberUpdateRequest) XXX_DiscardUnknown() {
xxx_messageInfo_MemberUpdateRequest.DiscardUnknown(m)
}
var xxx_messageInfo_MemberUpdateRequest proto.InternalMessageInfo
func (m *MemberUpdateRequest) GetID() uint64 {
if m != nil {
return m.ID
}
return 0
}
func (m *MemberUpdateRequest) GetPeerURLs() []string {
if m != nil {
return m.PeerURLs
}
return nil
}
type MemberUpdateResponse struct {
Header *ResponseHeader `protobuf:"bytes,1,opt,name=header,proto3" json:"header,omitempty"`
// members is a list of all members after updating the member.
Members []*Member `protobuf:"bytes,2,rep,name=members,proto3" json:"members,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *MemberUpdateResponse) Reset() { *m = MemberUpdateResponse{} }
func (m *MemberUpdateResponse) String() string { return proto.CompactTextString(m) }
func (*MemberUpdateResponse) ProtoMessage() {}
func (*MemberUpdateResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_77a6da22d6a3feb1, []int{45}
}
func (m *MemberUpdateResponse) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
}
func (m *MemberUpdateResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
if deterministic {
return xxx_messageInfo_MemberUpdateResponse.Marshal(b, m, deterministic)
} else {
b = b[:cap(b)]
n, err := m.MarshalToSizedBuffer(b)
if err != nil {
return nil, err
}
return b[:n], nil
}
}
func (m *MemberUpdateResponse) XXX_Merge(src proto.Message) {
xxx_messageInfo_MemberUpdateResponse.Merge(m, src)
}
func (m *MemberUpdateResponse) XXX_Size() int {
return m.Size()
}
func (m *MemberUpdateResponse) XXX_DiscardUnknown() {
xxx_messageInfo_MemberUpdateResponse.DiscardUnknown(m)
}
var xxx_messageInfo_MemberUpdateResponse proto.InternalMessageInfo
func (m *MemberUpdateResponse) GetHeader() *ResponseHeader {
if m != nil {
return m.Header
}
return nil
}
func (m *MemberUpdateResponse) GetMembers() []*Member {
if m != nil {
return m.Members
}
return nil
}
type MemberListRequest struct {
Linearizable bool `protobuf:"varint,1,opt,name=linearizable,proto3" json:"linearizable,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *MemberListRequest) Reset() { *m = MemberListRequest{} }
func (m *MemberListRequest) String() string { return proto.CompactTextString(m) }
func (*MemberListRequest) ProtoMessage() {}
func (*MemberListRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_77a6da22d6a3feb1, []int{46}
}
func (m *MemberListRequest) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
}
func (m *MemberListRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
if deterministic {
return xxx_messageInfo_MemberListRequest.Marshal(b, m, deterministic)
} else {
b = b[:cap(b)]
n, err := m.MarshalToSizedBuffer(b)
if err != nil {
return nil, err
}
return b[:n], nil
}
}
func (m *MemberListRequest) XXX_Merge(src proto.Message) {
xxx_messageInfo_MemberListRequest.Merge(m, src)
}
func (m *MemberListRequest) XXX_Size() int {
return m.Size()
}
func (m *MemberListRequest) XXX_DiscardUnknown() {
xxx_messageInfo_MemberListRequest.DiscardUnknown(m)
}
var xxx_messageInfo_MemberListRequest proto.InternalMessageInfo
func (m *MemberListRequest) GetLinearizable() bool {
if m != nil {
return m.Linearizable
}
return false
}
type MemberListResponse struct {
Header *ResponseHeader `protobuf:"bytes,1,opt,name=header,proto3" json:"header,omitempty"`
// members is a list of all members associated with the cluster.
Members []*Member `protobuf:"bytes,2,rep,name=members,proto3" json:"members,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *MemberListResponse) Reset() { *m = MemberListResponse{} }
func (m *MemberListResponse) String() string { return proto.CompactTextString(m) }
func (*MemberListResponse) ProtoMessage() {}
func (*MemberListResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_77a6da22d6a3feb1, []int{47}
}
func (m *MemberListResponse) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
}
func (m *MemberListResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
if deterministic {
return xxx_messageInfo_MemberListResponse.Marshal(b, m, deterministic)
} else {
b = b[:cap(b)]
n, err := m.MarshalToSizedBuffer(b)
if err != nil {
return nil, err
}
return b[:n], nil
}
}
func (m *MemberListResponse) XXX_Merge(src proto.Message) {
xxx_messageInfo_MemberListResponse.Merge(m, src)
}
func (m *MemberListResponse) XXX_Size() int {
return m.Size()
}
func (m *MemberListResponse) XXX_DiscardUnknown() {
xxx_messageInfo_MemberListResponse.DiscardUnknown(m)
}
var xxx_messageInfo_MemberListResponse proto.InternalMessageInfo
func (m *MemberListResponse) GetHeader() *ResponseHeader {
if m != nil {
return m.Header
}
return nil
}
func (m *MemberListResponse) GetMembers() []*Member {
if m != nil {
return m.Members
}
return nil
}
type MemberPromoteRequest struct {
// ID is the member ID of the member to promote.
ID uint64 `protobuf:"varint,1,opt,name=ID,proto3" json:"ID,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *MemberPromoteRequest) Reset() { *m = MemberPromoteRequest{} }
func (m *MemberPromoteRequest) String() string { return proto.CompactTextString(m) }
func (*MemberPromoteRequest) ProtoMessage() {}
func (*MemberPromoteRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_77a6da22d6a3feb1, []int{48}
}
func (m *MemberPromoteRequest) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
}
func (m *MemberPromoteRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
if deterministic {
return xxx_messageInfo_MemberPromoteRequest.Marshal(b, m, deterministic)
} else {
b = b[:cap(b)]
n, err := m.MarshalToSizedBuffer(b)
if err != nil {
return nil, err
}
return b[:n], nil
}
}
func (m *MemberPromoteRequest) XXX_Merge(src proto.Message) {
xxx_messageInfo_MemberPromoteRequest.Merge(m, src)
}
func (m *MemberPromoteRequest) XXX_Size() int {
return m.Size()
}
func (m *MemberPromoteRequest) XXX_DiscardUnknown() {
xxx_messageInfo_MemberPromoteRequest.DiscardUnknown(m)
}
var xxx_messageInfo_MemberPromoteRequest proto.InternalMessageInfo
func (m *MemberPromoteRequest) GetID() uint64 {
if m != nil {
return m.ID
}
return 0
}
type MemberPromoteResponse struct {
Header *ResponseHeader `protobuf:"bytes,1,opt,name=header,proto3" json:"header,omitempty"`
// members is a list of all members after promoting the member.
Members []*Member `protobuf:"bytes,2,rep,name=members,proto3" json:"members,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *MemberPromoteResponse) Reset() { *m = MemberPromoteResponse{} }
func (m *MemberPromoteResponse) String() string { return proto.CompactTextString(m) }
func (*MemberPromoteResponse) ProtoMessage() {}
func (*MemberPromoteResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_77a6da22d6a3feb1, []int{49}
}
func (m *MemberPromoteResponse) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
}
func (m *MemberPromoteResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
if deterministic {
return xxx_messageInfo_MemberPromoteResponse.Marshal(b, m, deterministic)
} else {
b = b[:cap(b)]
n, err := m.MarshalToSizedBuffer(b)
if err != nil {
return nil, err
}
return b[:n], nil
}
}
func (m *MemberPromoteResponse) XXX_Merge(src proto.Message) {
xxx_messageInfo_MemberPromoteResponse.Merge(m, src)
}
func (m *MemberPromoteResponse) XXX_Size() int {
return m.Size()
}
func (m *MemberPromoteResponse) XXX_DiscardUnknown() {
xxx_messageInfo_MemberPromoteResponse.DiscardUnknown(m)
}
var xxx_messageInfo_MemberPromoteResponse proto.InternalMessageInfo
func (m *MemberPromoteResponse) GetHeader() *ResponseHeader {
if m != nil {
return m.Header
}
return nil
}
func (m *MemberPromoteResponse) GetMembers() []*Member {
if m != nil {
return m.Members
}
return nil
}
type DefragmentRequest struct {
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *DefragmentRequest) Reset() { *m = DefragmentRequest{} }
func (m *DefragmentRequest) String() string { return proto.CompactTextString(m) }
func (*DefragmentRequest) ProtoMessage() {}
func (*DefragmentRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_77a6da22d6a3feb1, []int{50}
}
func (m *DefragmentRequest) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
}
func (m *DefragmentRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
if deterministic {
return xxx_messageInfo_DefragmentRequest.Marshal(b, m, deterministic)
} else {
b = b[:cap(b)]
n, err := m.MarshalToSizedBuffer(b)
if err != nil {
return nil, err
}
return b[:n], nil
}
}
func (m *DefragmentRequest) XXX_Merge(src proto.Message) {
xxx_messageInfo_DefragmentRequest.Merge(m, src)
}
func (m *DefragmentRequest) XXX_Size() int {
return m.Size()
}
func (m *DefragmentRequest) XXX_DiscardUnknown() {
xxx_messageInfo_DefragmentRequest.DiscardUnknown(m)
}
var xxx_messageInfo_DefragmentRequest proto.InternalMessageInfo
type DefragmentResponse struct {
Header *ResponseHeader `protobuf:"bytes,1,opt,name=header,proto3" json:"header,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *DefragmentResponse) Reset() { *m = DefragmentResponse{} }
func (m *DefragmentResponse) String() string { return proto.CompactTextString(m) }
func (*DefragmentResponse) ProtoMessage() {}
func (*DefragmentResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_77a6da22d6a3feb1, []int{51}
}
func (m *DefragmentResponse) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
}
func (m *DefragmentResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
if deterministic {
return xxx_messageInfo_DefragmentResponse.Marshal(b, m, deterministic)
} else {
b = b[:cap(b)]
n, err := m.MarshalToSizedBuffer(b)
if err != nil {
return nil, err
}
return b[:n], nil
}
}
func (m *DefragmentResponse) XXX_Merge(src proto.Message) {
xxx_messageInfo_DefragmentResponse.Merge(m, src)
}
func (m *DefragmentResponse) XXX_Size() int {
return m.Size()
}
func (m *DefragmentResponse) XXX_DiscardUnknown() {
xxx_messageInfo_DefragmentResponse.DiscardUnknown(m)
}
var xxx_messageInfo_DefragmentResponse proto.InternalMessageInfo
func (m *DefragmentResponse) GetHeader() *ResponseHeader {
if m != nil {
return m.Header
}
return nil
}
type MoveLeaderRequest struct {
// targetID is the node ID for the new leader.
TargetID uint64 `protobuf:"varint,1,opt,name=targetID,proto3" json:"targetID,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *MoveLeaderRequest) Reset() { *m = MoveLeaderRequest{} }
func (m *MoveLeaderRequest) String() string { return proto.CompactTextString(m) }
func (*MoveLeaderRequest) ProtoMessage() {}
func (*MoveLeaderRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_77a6da22d6a3feb1, []int{52}
}
func (m *MoveLeaderRequest) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
}
func (m *MoveLeaderRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
if deterministic {
return xxx_messageInfo_MoveLeaderRequest.Marshal(b, m, deterministic)
} else {
b = b[:cap(b)]
n, err := m.MarshalToSizedBuffer(b)
if err != nil {
return nil, err
}
return b[:n], nil
}
}
func (m *MoveLeaderRequest) XXX_Merge(src proto.Message) {
xxx_messageInfo_MoveLeaderRequest.Merge(m, src)
}
func (m *MoveLeaderRequest) XXX_Size() int {
return m.Size()
}
func (m *MoveLeaderRequest) XXX_DiscardUnknown() {
xxx_messageInfo_MoveLeaderRequest.DiscardUnknown(m)
}
var xxx_messageInfo_MoveLeaderRequest proto.InternalMessageInfo
func (m *MoveLeaderRequest) GetTargetID() uint64 {
if m != nil {
return m.TargetID
}
return 0
}
type MoveLeaderResponse struct {
Header *ResponseHeader `protobuf:"bytes,1,opt,name=header,proto3" json:"header,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *MoveLeaderResponse) Reset() { *m = MoveLeaderResponse{} }
func (m *MoveLeaderResponse) String() string { return proto.CompactTextString(m) }
func (*MoveLeaderResponse) ProtoMessage() {}
func (*MoveLeaderResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_77a6da22d6a3feb1, []int{53}
}
func (m *MoveLeaderResponse) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
}
func (m *MoveLeaderResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
if deterministic {
return xxx_messageInfo_MoveLeaderResponse.Marshal(b, m, deterministic)
} else {
b = b[:cap(b)]
n, err := m.MarshalToSizedBuffer(b)
if err != nil {
return nil, err
}
return b[:n], nil
}
}
func (m *MoveLeaderResponse) XXX_Merge(src proto.Message) {
xxx_messageInfo_MoveLeaderResponse.Merge(m, src)
}
func (m *MoveLeaderResponse) XXX_Size() int {
return m.Size()
}
func (m *MoveLeaderResponse) XXX_DiscardUnknown() {
xxx_messageInfo_MoveLeaderResponse.DiscardUnknown(m)
}
var xxx_messageInfo_MoveLeaderResponse proto.InternalMessageInfo
func (m *MoveLeaderResponse) GetHeader() *ResponseHeader {
if m != nil {
return m.Header
}
return nil
}
type AlarmRequest struct {
// action is the kind of alarm request to issue. The action
// may GET alarm statuses, ACTIVATE an alarm, or DEACTIVATE a
// raised alarm.
Action AlarmRequest_AlarmAction `protobuf:"varint,1,opt,name=action,proto3,enum=etcdserverpb.AlarmRequest_AlarmAction" json:"action,omitempty"`
// memberID is the ID of the member associated with the alarm. If memberID is 0, the
// alarm request covers all members.
MemberID uint64 `protobuf:"varint,2,opt,name=memberID,proto3" json:"memberID,omitempty"`
// alarm is the type of alarm to consider for this request.
Alarm AlarmType `protobuf:"varint,3,opt,name=alarm,proto3,enum=etcdserverpb.AlarmType" json:"alarm,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *AlarmRequest) Reset() { *m = AlarmRequest{} }
func (m *AlarmRequest) String() string { return proto.CompactTextString(m) }
func (*AlarmRequest) ProtoMessage() {}
func (*AlarmRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_77a6da22d6a3feb1, []int{54}
}
func (m *AlarmRequest) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
}
func (m *AlarmRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
if deterministic {
return xxx_messageInfo_AlarmRequest.Marshal(b, m, deterministic)
} else {
b = b[:cap(b)]
n, err := m.MarshalToSizedBuffer(b)
if err != nil {
return nil, err
}
return b[:n], nil
}
}
func (m *AlarmRequest) XXX_Merge(src proto.Message) {
xxx_messageInfo_AlarmRequest.Merge(m, src)
}
func (m *AlarmRequest) XXX_Size() int {
return m.Size()
}
func (m *AlarmRequest) XXX_DiscardUnknown() {
xxx_messageInfo_AlarmRequest.DiscardUnknown(m)
}
var xxx_messageInfo_AlarmRequest proto.InternalMessageInfo
func (m *AlarmRequest) GetAction() AlarmRequest_AlarmAction {
if m != nil {
return m.Action
}
return AlarmRequest_GET
}
func (m *AlarmRequest) GetMemberID() uint64 {
if m != nil {
return m.MemberID
}
return 0
}
func (m *AlarmRequest) GetAlarm() AlarmType {
if m != nil {
return m.Alarm
}
return AlarmType_NONE
}
type AlarmMember struct {
// memberID is the ID of the member associated with the raised alarm.
MemberID uint64 `protobuf:"varint,1,opt,name=memberID,proto3" json:"memberID,omitempty"`
// alarm is the type of alarm which has been raised.
Alarm AlarmType `protobuf:"varint,2,opt,name=alarm,proto3,enum=etcdserverpb.AlarmType" json:"alarm,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *AlarmMember) Reset() { *m = AlarmMember{} }
func (m *AlarmMember) String() string { return proto.CompactTextString(m) }
func (*AlarmMember) ProtoMessage() {}
func (*AlarmMember) Descriptor() ([]byte, []int) {
return fileDescriptor_77a6da22d6a3feb1, []int{55}
}
func (m *AlarmMember) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
}
func (m *AlarmMember) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
if deterministic {
return xxx_messageInfo_AlarmMember.Marshal(b, m, deterministic)
} else {
b = b[:cap(b)]
n, err := m.MarshalToSizedBuffer(b)
if err != nil {
return nil, err
}
return b[:n], nil
}
}
func (m *AlarmMember) XXX_Merge(src proto.Message) {
xxx_messageInfo_AlarmMember.Merge(m, src)
}
func (m *AlarmMember) XXX_Size() int {
return m.Size()
}
func (m *AlarmMember) XXX_DiscardUnknown() {
xxx_messageInfo_AlarmMember.DiscardUnknown(m)
}
var xxx_messageInfo_AlarmMember proto.InternalMessageInfo
func (m *AlarmMember) GetMemberID() uint64 {
if m != nil {
return m.MemberID
}
return 0
}
func (m *AlarmMember) GetAlarm() AlarmType {
if m != nil {
return m.Alarm
}
return AlarmType_NONE
}
type AlarmResponse struct {
Header *ResponseHeader `protobuf:"bytes,1,opt,name=header,proto3" json:"header,omitempty"`
// alarms is a list of alarms associated with the alarm request.
Alarms []*AlarmMember `protobuf:"bytes,2,rep,name=alarms,proto3" json:"alarms,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *AlarmResponse) Reset() { *m = AlarmResponse{} }
func (m *AlarmResponse) String() string { return proto.CompactTextString(m) }
func (*AlarmResponse) ProtoMessage() {}
func (*AlarmResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_77a6da22d6a3feb1, []int{56}
}
func (m *AlarmResponse) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
}
func (m *AlarmResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
if deterministic {
return xxx_messageInfo_AlarmResponse.Marshal(b, m, deterministic)
} else {
b = b[:cap(b)]
n, err := m.MarshalToSizedBuffer(b)
if err != nil {
return nil, err
}
return b[:n], nil
}
}
func (m *AlarmResponse) XXX_Merge(src proto.Message) {
xxx_messageInfo_AlarmResponse.Merge(m, src)
}
func (m *AlarmResponse) XXX_Size() int {
return m.Size()
}
func (m *AlarmResponse) XXX_DiscardUnknown() {
xxx_messageInfo_AlarmResponse.DiscardUnknown(m)
}
var xxx_messageInfo_AlarmResponse proto.InternalMessageInfo
func (m *AlarmResponse) GetHeader() *ResponseHeader {
if m != nil {
return m.Header
}
return nil
}
func (m *AlarmResponse) GetAlarms() []*AlarmMember {
if m != nil {
return m.Alarms
}
return nil
}
type DowngradeRequest struct {
// action is the kind of downgrade request to issue. The action may
// VALIDATE the target version, DOWNGRADE the cluster version,
// or CANCEL the current downgrading job.
Action DowngradeRequest_DowngradeAction `protobuf:"varint,1,opt,name=action,proto3,enum=etcdserverpb.DowngradeRequest_DowngradeAction" json:"action,omitempty"`
// version is the target version to downgrade.
Version string `protobuf:"bytes,2,opt,name=version,proto3" json:"version,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *DowngradeRequest) Reset() { *m = DowngradeRequest{} }
func (m *DowngradeRequest) String() string { return proto.CompactTextString(m) }
func (*DowngradeRequest) ProtoMessage() {}
func (*DowngradeRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_77a6da22d6a3feb1, []int{57}
}
func (m *DowngradeRequest) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
}
func (m *DowngradeRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
if deterministic {
return xxx_messageInfo_DowngradeRequest.Marshal(b, m, deterministic)
} else {
b = b[:cap(b)]
n, err := m.MarshalToSizedBuffer(b)
if err != nil {
return nil, err
}
return b[:n], nil
}
}
func (m *DowngradeRequest) XXX_Merge(src proto.Message) {
xxx_messageInfo_DowngradeRequest.Merge(m, src)
}
func (m *DowngradeRequest) XXX_Size() int {
return m.Size()
}
func (m *DowngradeRequest) XXX_DiscardUnknown() {
xxx_messageInfo_DowngradeRequest.DiscardUnknown(m)
}
var xxx_messageInfo_DowngradeRequest proto.InternalMessageInfo
func (m *DowngradeRequest) GetAction() DowngradeRequest_DowngradeAction {
if m != nil {
return m.Action
}
return DowngradeRequest_VALIDATE
}
func (m *DowngradeRequest) GetVersion() string {
if m != nil {
return m.Version
}
return ""
}
type DowngradeResponse struct {
Header *ResponseHeader `protobuf:"bytes,1,opt,name=header,proto3" json:"header,omitempty"`
// version is the current cluster version.
Version string `protobuf:"bytes,2,opt,name=version,proto3" json:"version,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *DowngradeResponse) Reset() { *m = DowngradeResponse{} }
func (m *DowngradeResponse) String() string { return proto.CompactTextString(m) }
func (*DowngradeResponse) ProtoMessage() {}
func (*DowngradeResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_77a6da22d6a3feb1, []int{58}
}
func (m *DowngradeResponse) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
}
func (m *DowngradeResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
if deterministic {
return xxx_messageInfo_DowngradeResponse.Marshal(b, m, deterministic)
} else {
b = b[:cap(b)]
n, err := m.MarshalToSizedBuffer(b)
if err != nil {
return nil, err
}
return b[:n], nil
}
}
func (m *DowngradeResponse) XXX_Merge(src proto.Message) {
xxx_messageInfo_DowngradeResponse.Merge(m, src)
}
func (m *DowngradeResponse) XXX_Size() int {
return m.Size()
}
func (m *DowngradeResponse) XXX_DiscardUnknown() {
xxx_messageInfo_DowngradeResponse.DiscardUnknown(m)
}
var xxx_messageInfo_DowngradeResponse proto.InternalMessageInfo
func (m *DowngradeResponse) GetHeader() *ResponseHeader {
if m != nil {
return m.Header
}
return nil
}
func (m *DowngradeResponse) GetVersion() string {
if m != nil {
return m.Version
}
return ""
}
// DowngradeVersionTestRequest is used for test only. The version in
// this request will be read as the WAL record version.If the downgrade
// target version is less than this version, then the downgrade(online)
// or migration(offline) isn't safe, so shouldn't be allowed.
type DowngradeVersionTestRequest struct {
Ver string `protobuf:"bytes,1,opt,name=ver,proto3" json:"ver,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *DowngradeVersionTestRequest) Reset() { *m = DowngradeVersionTestRequest{} }
func (m *DowngradeVersionTestRequest) String() string { return proto.CompactTextString(m) }
func (*DowngradeVersionTestRequest) ProtoMessage() {}
func (*DowngradeVersionTestRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_77a6da22d6a3feb1, []int{59}
}
func (m *DowngradeVersionTestRequest) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
}
func (m *DowngradeVersionTestRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
if deterministic {
return xxx_messageInfo_DowngradeVersionTestRequest.Marshal(b, m, deterministic)
} else {
b = b[:cap(b)]
n, err := m.MarshalToSizedBuffer(b)
if err != nil {
return nil, err
}
return b[:n], nil
}
}
func (m *DowngradeVersionTestRequest) XXX_Merge(src proto.Message) {
xxx_messageInfo_DowngradeVersionTestRequest.Merge(m, src)
}
func (m *DowngradeVersionTestRequest) XXX_Size() int {
return m.Size()
}
func (m *DowngradeVersionTestRequest) XXX_DiscardUnknown() {
xxx_messageInfo_DowngradeVersionTestRequest.DiscardUnknown(m)
}
var xxx_messageInfo_DowngradeVersionTestRequest proto.InternalMessageInfo
func (m *DowngradeVersionTestRequest) GetVer() string {
if m != nil {
return m.Ver
}
return ""
}
type StatusRequest struct {
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *StatusRequest) Reset() { *m = StatusRequest{} }
func (m *StatusRequest) String() string { return proto.CompactTextString(m) }
func (*StatusRequest) ProtoMessage() {}
func (*StatusRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_77a6da22d6a3feb1, []int{60}
}
func (m *StatusRequest) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
}
func (m *StatusRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
if deterministic {
return xxx_messageInfo_StatusRequest.Marshal(b, m, deterministic)
} else {
b = b[:cap(b)]
n, err := m.MarshalToSizedBuffer(b)
if err != nil {
return nil, err
}
return b[:n], nil
}
}
func (m *StatusRequest) XXX_Merge(src proto.Message) {
xxx_messageInfo_StatusRequest.Merge(m, src)
}
func (m *StatusRequest) XXX_Size() int {
return m.Size()
}
func (m *StatusRequest) XXX_DiscardUnknown() {
xxx_messageInfo_StatusRequest.DiscardUnknown(m)
}
var xxx_messageInfo_StatusRequest proto.InternalMessageInfo
type StatusResponse struct {
Header *ResponseHeader `protobuf:"bytes,1,opt,name=header,proto3" json:"header,omitempty"`
// version is the cluster protocol version used by the responding member.
Version string `protobuf:"bytes,2,opt,name=version,proto3" json:"version,omitempty"`
// dbSize is the size of the backend database physically allocated, in bytes, of the responding member.
DbSize int64 `protobuf:"varint,3,opt,name=dbSize,proto3" json:"dbSize,omitempty"`
// leader is the member ID which the responding member believes is the current leader.
Leader uint64 `protobuf:"varint,4,opt,name=leader,proto3" json:"leader,omitempty"`
// raftIndex is the current raft committed index of the responding member.
RaftIndex uint64 `protobuf:"varint,5,opt,name=raftIndex,proto3" json:"raftIndex,omitempty"`
// raftTerm is the current raft term of the responding member.
RaftTerm uint64 `protobuf:"varint,6,opt,name=raftTerm,proto3" json:"raftTerm,omitempty"`
// raftAppliedIndex is the current raft applied index of the responding member.
RaftAppliedIndex uint64 `protobuf:"varint,7,opt,name=raftAppliedIndex,proto3" json:"raftAppliedIndex,omitempty"`
// errors contains alarm/health information and status.
Errors []string `protobuf:"bytes,8,rep,name=errors,proto3" json:"errors,omitempty"`
// dbSizeInUse is the size of the backend database logically in use, in bytes, of the responding member.
DbSizeInUse int64 `protobuf:"varint,9,opt,name=dbSizeInUse,proto3" json:"dbSizeInUse,omitempty"`
// isLearner indicates if the member is raft learner.
IsLearner bool `protobuf:"varint,10,opt,name=isLearner,proto3" json:"isLearner,omitempty"`
// storageVersion is the version of the db file. It might be updated with delay in relationship to the target cluster version.
StorageVersion string `protobuf:"bytes,11,opt,name=storageVersion,proto3" json:"storageVersion,omitempty"`
// dbSizeQuota is the configured etcd storage quota in bytes (the value passed to etcd instance by flag --quota-backend-bytes)
DbSizeQuota int64 `protobuf:"varint,12,opt,name=dbSizeQuota,proto3" json:"dbSizeQuota,omitempty"`
// downgradeInfo indicates if there is downgrade process.
DowngradeInfo *DowngradeInfo `protobuf:"bytes,13,opt,name=downgradeInfo,proto3" json:"downgradeInfo,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *StatusResponse) Reset() { *m = StatusResponse{} }
func (m *StatusResponse) String() string { return proto.CompactTextString(m) }
func (*StatusResponse) ProtoMessage() {}
func (*StatusResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_77a6da22d6a3feb1, []int{61}
}
func (m *StatusResponse) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
}
func (m *StatusResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
if deterministic {
return xxx_messageInfo_StatusResponse.Marshal(b, m, deterministic)
} else {
b = b[:cap(b)]
n, err := m.MarshalToSizedBuffer(b)
if err != nil {
return nil, err
}
return b[:n], nil
}
}
func (m *StatusResponse) XXX_Merge(src proto.Message) {
xxx_messageInfo_StatusResponse.Merge(m, src)
}
func (m *StatusResponse) XXX_Size() int {
return m.Size()
}
func (m *StatusResponse) XXX_DiscardUnknown() {
xxx_messageInfo_StatusResponse.DiscardUnknown(m)
}
var xxx_messageInfo_StatusResponse proto.InternalMessageInfo
func (m *StatusResponse) GetHeader() *ResponseHeader {
if m != nil {
return m.Header
}
return nil
}
func (m *StatusResponse) GetVersion() string {
if m != nil {
return m.Version
}
return ""
}
func (m *StatusResponse) GetDbSize() int64 {
if m != nil {
return m.DbSize
}
return 0
}
func (m *StatusResponse) GetLeader() uint64 {
if m != nil {
return m.Leader
}
return 0
}
func (m *StatusResponse) GetRaftIndex() uint64 {
if m != nil {
return m.RaftIndex
}
return 0
}
func (m *StatusResponse) GetRaftTerm() uint64 {
if m != nil {
return m.RaftTerm
}
return 0
}
func (m *StatusResponse) GetRaftAppliedIndex() uint64 {
if m != nil {
return m.RaftAppliedIndex
}
return 0
}
func (m *StatusResponse) GetErrors() []string {
if m != nil {
return m.Errors
}
return nil
}
func (m *StatusResponse) GetDbSizeInUse() int64 {
if m != nil {
return m.DbSizeInUse
}
return 0
}
func (m *StatusResponse) GetIsLearner() bool {
if m != nil {
return m.IsLearner
}
return false
}
func (m *StatusResponse) GetStorageVersion() string {
if m != nil {
return m.StorageVersion
}
return ""
}
func (m *StatusResponse) GetDbSizeQuota() int64 {
if m != nil {
return m.DbSizeQuota
}
return 0
}
func (m *StatusResponse) GetDowngradeInfo() *DowngradeInfo {
if m != nil {
return m.DowngradeInfo
}
return nil
}
type DowngradeInfo struct {
// enabled indicates whether the cluster is enabled to downgrade.
Enabled bool `protobuf:"varint,1,opt,name=enabled,proto3" json:"enabled,omitempty"`
// targetVersion is the target downgrade version.
TargetVersion string `protobuf:"bytes,2,opt,name=targetVersion,proto3" json:"targetVersion,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *DowngradeInfo) Reset() { *m = DowngradeInfo{} }
func (m *DowngradeInfo) String() string { return proto.CompactTextString(m) }
func (*DowngradeInfo) ProtoMessage() {}
func (*DowngradeInfo) Descriptor() ([]byte, []int) {
return fileDescriptor_77a6da22d6a3feb1, []int{62}
}
func (m *DowngradeInfo) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
}
func (m *DowngradeInfo) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
if deterministic {
return xxx_messageInfo_DowngradeInfo.Marshal(b, m, deterministic)
} else {
b = b[:cap(b)]
n, err := m.MarshalToSizedBuffer(b)
if err != nil {
return nil, err
}
return b[:n], nil
}
}
func (m *DowngradeInfo) XXX_Merge(src proto.Message) {
xxx_messageInfo_DowngradeInfo.Merge(m, src)
}
func (m *DowngradeInfo) XXX_Size() int {
return m.Size()
}
func (m *DowngradeInfo) XXX_DiscardUnknown() {
xxx_messageInfo_DowngradeInfo.DiscardUnknown(m)
}
var xxx_messageInfo_DowngradeInfo proto.InternalMessageInfo
func (m *DowngradeInfo) GetEnabled() bool {
if m != nil {
return m.Enabled
}
return false
}
func (m *DowngradeInfo) GetTargetVersion() string {
if m != nil {
return m.TargetVersion
}
return ""
}
type AuthEnableRequest struct {
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *AuthEnableRequest) Reset() { *m = AuthEnableRequest{} }
func (m *AuthEnableRequest) String() string { return proto.CompactTextString(m) }
func (*AuthEnableRequest) ProtoMessage() {}
func (*AuthEnableRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_77a6da22d6a3feb1, []int{63}
}
func (m *AuthEnableRequest) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
}
func (m *AuthEnableRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
if deterministic {
return xxx_messageInfo_AuthEnableRequest.Marshal(b, m, deterministic)
} else {
b = b[:cap(b)]
n, err := m.MarshalToSizedBuffer(b)
if err != nil {
return nil, err
}
return b[:n], nil
}
}
func (m *AuthEnableRequest) XXX_Merge(src proto.Message) {
xxx_messageInfo_AuthEnableRequest.Merge(m, src)
}
func (m *AuthEnableRequest) XXX_Size() int {
return m.Size()
}
func (m *AuthEnableRequest) XXX_DiscardUnknown() {
xxx_messageInfo_AuthEnableRequest.DiscardUnknown(m)
}
var xxx_messageInfo_AuthEnableRequest proto.InternalMessageInfo
type AuthDisableRequest struct {
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *AuthDisableRequest) Reset() { *m = AuthDisableRequest{} }
func (m *AuthDisableRequest) String() string { return proto.CompactTextString(m) }
func (*AuthDisableRequest) ProtoMessage() {}
func (*AuthDisableRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_77a6da22d6a3feb1, []int{64}
}
func (m *AuthDisableRequest) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
}
func (m *AuthDisableRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
if deterministic {
return xxx_messageInfo_AuthDisableRequest.Marshal(b, m, deterministic)
} else {
b = b[:cap(b)]
n, err := m.MarshalToSizedBuffer(b)
if err != nil {
return nil, err
}
return b[:n], nil
}
}
func (m *AuthDisableRequest) XXX_Merge(src proto.Message) {
xxx_messageInfo_AuthDisableRequest.Merge(m, src)
}
func (m *AuthDisableRequest) XXX_Size() int {
return m.Size()
}
func (m *AuthDisableRequest) XXX_DiscardUnknown() {
xxx_messageInfo_AuthDisableRequest.DiscardUnknown(m)
}
var xxx_messageInfo_AuthDisableRequest proto.InternalMessageInfo
type AuthStatusRequest struct {
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *AuthStatusRequest) Reset() { *m = AuthStatusRequest{} }
func (m *AuthStatusRequest) String() string { return proto.CompactTextString(m) }
func (*AuthStatusRequest) ProtoMessage() {}
func (*AuthStatusRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_77a6da22d6a3feb1, []int{65}
}
func (m *AuthStatusRequest) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
}
func (m *AuthStatusRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
if deterministic {
return xxx_messageInfo_AuthStatusRequest.Marshal(b, m, deterministic)
} else {
b = b[:cap(b)]
n, err := m.MarshalToSizedBuffer(b)
if err != nil {
return nil, err
}
return b[:n], nil
}
}
func (m *AuthStatusRequest) XXX_Merge(src proto.Message) {
xxx_messageInfo_AuthStatusRequest.Merge(m, src)
}
func (m *AuthStatusRequest) XXX_Size() int {
return m.Size()
}
func (m *AuthStatusRequest) XXX_DiscardUnknown() {
xxx_messageInfo_AuthStatusRequest.DiscardUnknown(m)
}
var xxx_messageInfo_AuthStatusRequest proto.InternalMessageInfo
type AuthenticateRequest struct {
Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
Password string `protobuf:"bytes,2,opt,name=password,proto3" json:"password,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *AuthenticateRequest) Reset() { *m = AuthenticateRequest{} }
func (m *AuthenticateRequest) String() string { return proto.CompactTextString(m) }
func (*AuthenticateRequest) ProtoMessage() {}
func (*AuthenticateRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_77a6da22d6a3feb1, []int{66}
}
func (m *AuthenticateRequest) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
}
func (m *AuthenticateRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
if deterministic {
return xxx_messageInfo_AuthenticateRequest.Marshal(b, m, deterministic)
} else {
b = b[:cap(b)]
n, err := m.MarshalToSizedBuffer(b)
if err != nil {
return nil, err
}
return b[:n], nil
}
}
func (m *AuthenticateRequest) XXX_Merge(src proto.Message) {
xxx_messageInfo_AuthenticateRequest.Merge(m, src)
}
func (m *AuthenticateRequest) XXX_Size() int {
return m.Size()
}
func (m *AuthenticateRequest) XXX_DiscardUnknown() {
xxx_messageInfo_AuthenticateRequest.DiscardUnknown(m)
}
var xxx_messageInfo_AuthenticateRequest proto.InternalMessageInfo
func (m *AuthenticateRequest) GetName() string {
if m != nil {
return m.Name
}
return ""
}
func (m *AuthenticateRequest) GetPassword() string {
if m != nil {
return m.Password
}
return ""
}
type AuthUserAddRequest struct {
Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
Password string `protobuf:"bytes,2,opt,name=password,proto3" json:"password,omitempty"`
Options *authpb.UserAddOptions `protobuf:"bytes,3,opt,name=options,proto3" json:"options,omitempty"`
HashedPassword string `protobuf:"bytes,4,opt,name=hashedPassword,proto3" json:"hashedPassword,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *AuthUserAddRequest) Reset() { *m = AuthUserAddRequest{} }
func (m *AuthUserAddRequest) String() string { return proto.CompactTextString(m) }
func (*AuthUserAddRequest) ProtoMessage() {}
func (*AuthUserAddRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_77a6da22d6a3feb1, []int{67}
}
func (m *AuthUserAddRequest) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
}
func (m *AuthUserAddRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
if deterministic {
return xxx_messageInfo_AuthUserAddRequest.Marshal(b, m, deterministic)
} else {
b = b[:cap(b)]
n, err := m.MarshalToSizedBuffer(b)
if err != nil {
return nil, err
}
return b[:n], nil
}
}
func (m *AuthUserAddRequest) XXX_Merge(src proto.Message) {
xxx_messageInfo_AuthUserAddRequest.Merge(m, src)
}
func (m *AuthUserAddRequest) XXX_Size() int {
return m.Size()
}
func (m *AuthUserAddRequest) XXX_DiscardUnknown() {
xxx_messageInfo_AuthUserAddRequest.DiscardUnknown(m)
}
var xxx_messageInfo_AuthUserAddRequest proto.InternalMessageInfo
func (m *AuthUserAddRequest) GetName() string {
if m != nil {
return m.Name
}
return ""
}
func (m *AuthUserAddRequest) GetPassword() string {
if m != nil {
return m.Password
}
return ""
}
func (m *AuthUserAddRequest) GetOptions() *authpb.UserAddOptions {
if m != nil {
return m.Options
}
return nil
}
func (m *AuthUserAddRequest) GetHashedPassword() string {
if m != nil {
return m.HashedPassword
}
return ""
}
type AuthUserGetRequest struct {
Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *AuthUserGetRequest) Reset() { *m = AuthUserGetRequest{} }
func (m *AuthUserGetRequest) String() string { return proto.CompactTextString(m) }
func (*AuthUserGetRequest) ProtoMessage() {}
func (*AuthUserGetRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_77a6da22d6a3feb1, []int{68}
}
func (m *AuthUserGetRequest) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
}
func (m *AuthUserGetRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
if deterministic {
return xxx_messageInfo_AuthUserGetRequest.Marshal(b, m, deterministic)
} else {
b = b[:cap(b)]
n, err := m.MarshalToSizedBuffer(b)
if err != nil {
return nil, err
}
return b[:n], nil
}
}
func (m *AuthUserGetRequest) XXX_Merge(src proto.Message) {
xxx_messageInfo_AuthUserGetRequest.Merge(m, src)
}
func (m *AuthUserGetRequest) XXX_Size() int {
return m.Size()
}
func (m *AuthUserGetRequest) XXX_DiscardUnknown() {
xxx_messageInfo_AuthUserGetRequest.DiscardUnknown(m)
}
var xxx_messageInfo_AuthUserGetRequest proto.InternalMessageInfo
func (m *AuthUserGetRequest) GetName() string {
if m != nil {
return m.Name
}
return ""
}
type AuthUserDeleteRequest struct {
// name is the name of the user to delete.
Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *AuthUserDeleteRequest) Reset() { *m = AuthUserDeleteRequest{} }
func (m *AuthUserDeleteRequest) String() string { return proto.CompactTextString(m) }
func (*AuthUserDeleteRequest) ProtoMessage() {}
func (*AuthUserDeleteRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_77a6da22d6a3feb1, []int{69}
}
func (m *AuthUserDeleteRequest) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
}
func (m *AuthUserDeleteRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
if deterministic {
return xxx_messageInfo_AuthUserDeleteRequest.Marshal(b, m, deterministic)
} else {
b = b[:cap(b)]
n, err := m.MarshalToSizedBuffer(b)
if err != nil {
return nil, err
}
return b[:n], nil
}
}
func (m *AuthUserDeleteRequest) XXX_Merge(src proto.Message) {
xxx_messageInfo_AuthUserDeleteRequest.Merge(m, src)
}
func (m *AuthUserDeleteRequest) XXX_Size() int {
return m.Size()
}
func (m *AuthUserDeleteRequest) XXX_DiscardUnknown() {
xxx_messageInfo_AuthUserDeleteRequest.DiscardUnknown(m)
}
var xxx_messageInfo_AuthUserDeleteRequest proto.InternalMessageInfo
func (m *AuthUserDeleteRequest) GetName() string {
if m != nil {
return m.Name
}
return ""
}
type AuthUserChangePasswordRequest struct {
// name is the name of the user whose password is being changed.
Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
// password is the new password for the user. Note that this field will be removed in the API layer.
Password string `protobuf:"bytes,2,opt,name=password,proto3" json:"password,omitempty"`
// hashedPassword is the new password for the user. Note that this field will be initialized in the API layer.
HashedPassword string `protobuf:"bytes,3,opt,name=hashedPassword,proto3" json:"hashedPassword,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *AuthUserChangePasswordRequest) Reset() { *m = AuthUserChangePasswordRequest{} }
func (m *AuthUserChangePasswordRequest) String() string { return proto.CompactTextString(m) }
func (*AuthUserChangePasswordRequest) ProtoMessage() {}
func (*AuthUserChangePasswordRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_77a6da22d6a3feb1, []int{70}
}
func (m *AuthUserChangePasswordRequest) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
}
func (m *AuthUserChangePasswordRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
if deterministic {
return xxx_messageInfo_AuthUserChangePasswordRequest.Marshal(b, m, deterministic)
} else {
b = b[:cap(b)]
n, err := m.MarshalToSizedBuffer(b)
if err != nil {
return nil, err
}
return b[:n], nil
}
}
func (m *AuthUserChangePasswordRequest) XXX_Merge(src proto.Message) {
xxx_messageInfo_AuthUserChangePasswordRequest.Merge(m, src)
}
func (m *AuthUserChangePasswordRequest) XXX_Size() int {
return m.Size()
}
func (m *AuthUserChangePasswordRequest) XXX_DiscardUnknown() {
xxx_messageInfo_AuthUserChangePasswordRequest.DiscardUnknown(m)
}
var xxx_messageInfo_AuthUserChangePasswordRequest proto.InternalMessageInfo
func (m *AuthUserChangePasswordRequest) GetName() string {
if m != nil {
return m.Name
}
return ""
}
func (m *AuthUserChangePasswordRequest) GetPassword() string {
if m != nil {
return m.Password
}
return ""
}
func (m *AuthUserChangePasswordRequest) GetHashedPassword() string {
if m != nil {
return m.HashedPassword
}
return ""
}
type AuthUserGrantRoleRequest struct {
// user is the name of the user which should be granted a given role.
User string `protobuf:"bytes,1,opt,name=user,proto3" json:"user,omitempty"`
// role is the name of the role to grant to the user.
Role string `protobuf:"bytes,2,opt,name=role,proto3" json:"role,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *AuthUserGrantRoleRequest) Reset() { *m = AuthUserGrantRoleRequest{} }
func (m *AuthUserGrantRoleRequest) String() string { return proto.CompactTextString(m) }
func (*AuthUserGrantRoleRequest) ProtoMessage() {}
func (*AuthUserGrantRoleRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_77a6da22d6a3feb1, []int{71}
}
func (m *AuthUserGrantRoleRequest) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
}
func (m *AuthUserGrantRoleRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
if deterministic {
return xxx_messageInfo_AuthUserGrantRoleRequest.Marshal(b, m, deterministic)
} else {
b = b[:cap(b)]
n, err := m.MarshalToSizedBuffer(b)
if err != nil {
return nil, err
}
return b[:n], nil
}
}
func (m *AuthUserGrantRoleRequest) XXX_Merge(src proto.Message) {
xxx_messageInfo_AuthUserGrantRoleRequest.Merge(m, src)
}
func (m *AuthUserGrantRoleRequest) XXX_Size() int {
return m.Size()
}
func (m *AuthUserGrantRoleRequest) XXX_DiscardUnknown() {
xxx_messageInfo_AuthUserGrantRoleRequest.DiscardUnknown(m)
}
var xxx_messageInfo_AuthUserGrantRoleRequest proto.InternalMessageInfo
func (m *AuthUserGrantRoleRequest) GetUser() string {
if m != nil {
return m.User
}
return ""
}
func (m *AuthUserGrantRoleRequest) GetRole() string {
if m != nil {
return m.Role
}
return ""
}
type AuthUserRevokeRoleRequest struct {
Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
Role string `protobuf:"bytes,2,opt,name=role,proto3" json:"role,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *AuthUserRevokeRoleRequest) Reset() { *m = AuthUserRevokeRoleRequest{} }
func (m *AuthUserRevokeRoleRequest) String() string { return proto.CompactTextString(m) }
func (*AuthUserRevokeRoleRequest) ProtoMessage() {}
func (*AuthUserRevokeRoleRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_77a6da22d6a3feb1, []int{72}
}
func (m *AuthUserRevokeRoleRequest) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
}
func (m *AuthUserRevokeRoleRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
if deterministic {
return xxx_messageInfo_AuthUserRevokeRoleRequest.Marshal(b, m, deterministic)
} else {
b = b[:cap(b)]
n, err := m.MarshalToSizedBuffer(b)
if err != nil {
return nil, err
}
return b[:n], nil
}
}
func (m *AuthUserRevokeRoleRequest) XXX_Merge(src proto.Message) {
xxx_messageInfo_AuthUserRevokeRoleRequest.Merge(m, src)
}
func (m *AuthUserRevokeRoleRequest) XXX_Size() int {
return m.Size()
}
func (m *AuthUserRevokeRoleRequest) XXX_DiscardUnknown() {
xxx_messageInfo_AuthUserRevokeRoleRequest.DiscardUnknown(m)
}
var xxx_messageInfo_AuthUserRevokeRoleRequest proto.InternalMessageInfo
func (m *AuthUserRevokeRoleRequest) GetName() string {
if m != nil {
return m.Name
}
return ""
}
func (m *AuthUserRevokeRoleRequest) GetRole() string {
if m != nil {
return m.Role
}
return ""
}
type AuthRoleAddRequest struct {
// name is the name of the role to add to the authentication system.
Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *AuthRoleAddRequest) Reset() { *m = AuthRoleAddRequest{} }
func (m *AuthRoleAddRequest) String() string { return proto.CompactTextString(m) }
func (*AuthRoleAddRequest) ProtoMessage() {}
func (*AuthRoleAddRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_77a6da22d6a3feb1, []int{73}
}
func (m *AuthRoleAddRequest) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
}
func (m *AuthRoleAddRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
if deterministic {
return xxx_messageInfo_AuthRoleAddRequest.Marshal(b, m, deterministic)
} else {
b = b[:cap(b)]
n, err := m.MarshalToSizedBuffer(b)
if err != nil {
return nil, err
}
return b[:n], nil
}
}
func (m *AuthRoleAddRequest) XXX_Merge(src proto.Message) {
xxx_messageInfo_AuthRoleAddRequest.Merge(m, src)
}
func (m *AuthRoleAddRequest) XXX_Size() int {
return m.Size()
}
func (m *AuthRoleAddRequest) XXX_DiscardUnknown() {
xxx_messageInfo_AuthRoleAddRequest.DiscardUnknown(m)
}
var xxx_messageInfo_AuthRoleAddRequest proto.InternalMessageInfo
func (m *AuthRoleAddRequest) GetName() string {
if m != nil {
return m.Name
}
return ""
}
type AuthRoleGetRequest struct {
Role string `protobuf:"bytes,1,opt,name=role,proto3" json:"role,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *AuthRoleGetRequest) Reset() { *m = AuthRoleGetRequest{} }
func (m *AuthRoleGetRequest) String() string { return proto.CompactTextString(m) }
func (*AuthRoleGetRequest) ProtoMessage() {}
func (*AuthRoleGetRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_77a6da22d6a3feb1, []int{74}
}
func (m *AuthRoleGetRequest) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
}
func (m *AuthRoleGetRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
if deterministic {
return xxx_messageInfo_AuthRoleGetRequest.Marshal(b, m, deterministic)
} else {
b = b[:cap(b)]
n, err := m.MarshalToSizedBuffer(b)
if err != nil {
return nil, err
}
return b[:n], nil
}
}
func (m *AuthRoleGetRequest) XXX_Merge(src proto.Message) {
xxx_messageInfo_AuthRoleGetRequest.Merge(m, src)
}
func (m *AuthRoleGetRequest) XXX_Size() int {
return m.Size()
}
func (m *AuthRoleGetRequest) XXX_DiscardUnknown() {
xxx_messageInfo_AuthRoleGetRequest.DiscardUnknown(m)
}
var xxx_messageInfo_AuthRoleGetRequest proto.InternalMessageInfo
func (m *AuthRoleGetRequest) GetRole() string {
if m != nil {
return m.Role
}
return ""
}
type AuthUserListRequest struct {
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *AuthUserListRequest) Reset() { *m = AuthUserListRequest{} }
func (m *AuthUserListRequest) String() string { return proto.CompactTextString(m) }
func (*AuthUserListRequest) ProtoMessage() {}
func (*AuthUserListRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_77a6da22d6a3feb1, []int{75}
}
func (m *AuthUserListRequest) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
}
func (m *AuthUserListRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
if deterministic {
return xxx_messageInfo_AuthUserListRequest.Marshal(b, m, deterministic)
} else {
b = b[:cap(b)]
n, err := m.MarshalToSizedBuffer(b)
if err != nil {
return nil, err
}
return b[:n], nil
}
}
func (m *AuthUserListRequest) XXX_Merge(src proto.Message) {
xxx_messageInfo_AuthUserListRequest.Merge(m, src)
}
func (m *AuthUserListRequest) XXX_Size() int {
return m.Size()
}
func (m *AuthUserListRequest) XXX_DiscardUnknown() {
xxx_messageInfo_AuthUserListRequest.DiscardUnknown(m)
}
var xxx_messageInfo_AuthUserListRequest proto.InternalMessageInfo
type AuthRoleListRequest struct {
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *AuthRoleListRequest) Reset() { *m = AuthRoleListRequest{} }
func (m *AuthRoleListRequest) String() string { return proto.CompactTextString(m) }
func (*AuthRoleListRequest) ProtoMessage() {}
func (*AuthRoleListRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_77a6da22d6a3feb1, []int{76}
}
func (m *AuthRoleListRequest) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
}
func (m *AuthRoleListRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
if deterministic {
return xxx_messageInfo_AuthRoleListRequest.Marshal(b, m, deterministic)
} else {
b = b[:cap(b)]
n, err := m.MarshalToSizedBuffer(b)
if err != nil {
return nil, err
}
return b[:n], nil
}
}
func (m *AuthRoleListRequest) XXX_Merge(src proto.Message) {
xxx_messageInfo_AuthRoleListRequest.Merge(m, src)
}
func (m *AuthRoleListRequest) XXX_Size() int {
return m.Size()
}
func (m *AuthRoleListRequest) XXX_DiscardUnknown() {
xxx_messageInfo_AuthRoleListRequest.DiscardUnknown(m)
}
var xxx_messageInfo_AuthRoleListRequest proto.InternalMessageInfo
type AuthRoleDeleteRequest struct {
Role string `protobuf:"bytes,1,opt,name=role,proto3" json:"role,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *AuthRoleDeleteRequest) Reset() { *m = AuthRoleDeleteRequest{} }
func (m *AuthRoleDeleteRequest) String() string { return proto.CompactTextString(m) }
func (*AuthRoleDeleteRequest) ProtoMessage() {}
func (*AuthRoleDeleteRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_77a6da22d6a3feb1, []int{77}
}
func (m *AuthRoleDeleteRequest) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
}
func (m *AuthRoleDeleteRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
if deterministic {
return xxx_messageInfo_AuthRoleDeleteRequest.Marshal(b, m, deterministic)
} else {
b = b[:cap(b)]
n, err := m.MarshalToSizedBuffer(b)
if err != nil {
return nil, err
}
return b[:n], nil
}
}
func (m *AuthRoleDeleteRequest) XXX_Merge(src proto.Message) {
xxx_messageInfo_AuthRoleDeleteRequest.Merge(m, src)
}
func (m *AuthRoleDeleteRequest) XXX_Size() int {
return m.Size()
}
func (m *AuthRoleDeleteRequest) XXX_DiscardUnknown() {
xxx_messageInfo_AuthRoleDeleteRequest.DiscardUnknown(m)
}
var xxx_messageInfo_AuthRoleDeleteRequest proto.InternalMessageInfo
func (m *AuthRoleDeleteRequest) GetRole() string {
if m != nil {
return m.Role
}
return ""
}
type AuthRoleGrantPermissionRequest struct {
// name is the name of the role which will be granted the permission.
Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
// perm is the permission to grant to the role.
Perm *authpb.Permission `protobuf:"bytes,2,opt,name=perm,proto3" json:"perm,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *AuthRoleGrantPermissionRequest) Reset() { *m = AuthRoleGrantPermissionRequest{} }
func (m *AuthRoleGrantPermissionRequest) String() string { return proto.CompactTextString(m) }
func (*AuthRoleGrantPermissionRequest) ProtoMessage() {}
func (*AuthRoleGrantPermissionRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_77a6da22d6a3feb1, []int{78}
}
func (m *AuthRoleGrantPermissionRequest) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
}
func (m *AuthRoleGrantPermissionRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
if deterministic {
return xxx_messageInfo_AuthRoleGrantPermissionRequest.Marshal(b, m, deterministic)
} else {
b = b[:cap(b)]
n, err := m.MarshalToSizedBuffer(b)
if err != nil {
return nil, err
}
return b[:n], nil
}
}
func (m *AuthRoleGrantPermissionRequest) XXX_Merge(src proto.Message) {
xxx_messageInfo_AuthRoleGrantPermissionRequest.Merge(m, src)
}
func (m *AuthRoleGrantPermissionRequest) XXX_Size() int {
return m.Size()
}
func (m *AuthRoleGrantPermissionRequest) XXX_DiscardUnknown() {
xxx_messageInfo_AuthRoleGrantPermissionRequest.DiscardUnknown(m)
}
var xxx_messageInfo_AuthRoleGrantPermissionRequest proto.InternalMessageInfo
func (m *AuthRoleGrantPermissionRequest) GetName() string {
if m != nil {
return m.Name
}
return ""
}
func (m *AuthRoleGrantPermissionRequest) GetPerm() *authpb.Permission {
if m != nil {
return m.Perm
}
return nil
}
type AuthRoleRevokePermissionRequest struct {
Role string `protobuf:"bytes,1,opt,name=role,proto3" json:"role,omitempty"`
Key []byte `protobuf:"bytes,2,opt,name=key,proto3" json:"key,omitempty"`
RangeEnd []byte `protobuf:"bytes,3,opt,name=range_end,json=rangeEnd,proto3" json:"range_end,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *AuthRoleRevokePermissionRequest) Reset() { *m = AuthRoleRevokePermissionRequest{} }
func (m *AuthRoleRevokePermissionRequest) String() string { return proto.CompactTextString(m) }
func (*AuthRoleRevokePermissionRequest) ProtoMessage() {}
func (*AuthRoleRevokePermissionRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_77a6da22d6a3feb1, []int{79}
}
func (m *AuthRoleRevokePermissionRequest) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
}
func (m *AuthRoleRevokePermissionRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
if deterministic {
return xxx_messageInfo_AuthRoleRevokePermissionRequest.Marshal(b, m, deterministic)
} else {
b = b[:cap(b)]
n, err := m.MarshalToSizedBuffer(b)
if err != nil {
return nil, err
}
return b[:n], nil
}
}
func (m *AuthRoleRevokePermissionRequest) XXX_Merge(src proto.Message) {
xxx_messageInfo_AuthRoleRevokePermissionRequest.Merge(m, src)
}
func (m *AuthRoleRevokePermissionRequest) XXX_Size() int {
return m.Size()
}
func (m *AuthRoleRevokePermissionRequest) XXX_DiscardUnknown() {
xxx_messageInfo_AuthRoleRevokePermissionRequest.DiscardUnknown(m)
}
var xxx_messageInfo_AuthRoleRevokePermissionRequest proto.InternalMessageInfo
func (m *AuthRoleRevokePermissionRequest) GetRole() string {
if m != nil {
return m.Role
}
return ""
}
func (m *AuthRoleRevokePermissionRequest) GetKey() []byte {
if m != nil {
return m.Key
}
return nil
}
func (m *AuthRoleRevokePermissionRequest) GetRangeEnd() []byte {
if m != nil {
return m.RangeEnd
}
return nil
}
type AuthEnableResponse struct {
Header *ResponseHeader `protobuf:"bytes,1,opt,name=header,proto3" json:"header,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *AuthEnableResponse) Reset() { *m = AuthEnableResponse{} }
func (m *AuthEnableResponse) String() string { return proto.CompactTextString(m) }
func (*AuthEnableResponse) ProtoMessage() {}
func (*AuthEnableResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_77a6da22d6a3feb1, []int{80}
}
func (m *AuthEnableResponse) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
}
func (m *AuthEnableResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
if deterministic {
return xxx_messageInfo_AuthEnableResponse.Marshal(b, m, deterministic)
} else {
b = b[:cap(b)]
n, err := m.MarshalToSizedBuffer(b)
if err != nil {
return nil, err
}
return b[:n], nil
}
}
func (m *AuthEnableResponse) XXX_Merge(src proto.Message) {
xxx_messageInfo_AuthEnableResponse.Merge(m, src)
}
func (m *AuthEnableResponse) XXX_Size() int {
return m.Size()
}
func (m *AuthEnableResponse) XXX_DiscardUnknown() {
xxx_messageInfo_AuthEnableResponse.DiscardUnknown(m)
}
var xxx_messageInfo_AuthEnableResponse proto.InternalMessageInfo
func (m *AuthEnableResponse) GetHeader() *ResponseHeader {
if m != nil {
return m.Header
}
return nil
}
type AuthDisableResponse struct {
Header *ResponseHeader `protobuf:"bytes,1,opt,name=header,proto3" json:"header,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *AuthDisableResponse) Reset() { *m = AuthDisableResponse{} }
func (m *AuthDisableResponse) String() string { return proto.CompactTextString(m) }
func (*AuthDisableResponse) ProtoMessage() {}
func (*AuthDisableResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_77a6da22d6a3feb1, []int{81}
}
func (m *AuthDisableResponse) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
}
func (m *AuthDisableResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
if deterministic {
return xxx_messageInfo_AuthDisableResponse.Marshal(b, m, deterministic)
} else {
b = b[:cap(b)]
n, err := m.MarshalToSizedBuffer(b)
if err != nil {
return nil, err
}
return b[:n], nil
}
}
func (m *AuthDisableResponse) XXX_Merge(src proto.Message) {
xxx_messageInfo_AuthDisableResponse.Merge(m, src)
}
func (m *AuthDisableResponse) XXX_Size() int {
return m.Size()
}
func (m *AuthDisableResponse) XXX_DiscardUnknown() {
xxx_messageInfo_AuthDisableResponse.DiscardUnknown(m)
}
var xxx_messageInfo_AuthDisableResponse proto.InternalMessageInfo
func (m *AuthDisableResponse) GetHeader() *ResponseHeader {
if m != nil {
return m.Header
}
return nil
}
type AuthStatusResponse struct {
Header *ResponseHeader `protobuf:"bytes,1,opt,name=header,proto3" json:"header,omitempty"`
Enabled bool `protobuf:"varint,2,opt,name=enabled,proto3" json:"enabled,omitempty"`
// authRevision is the current revision of auth store
AuthRevision uint64 `protobuf:"varint,3,opt,name=authRevision,proto3" json:"authRevision,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *AuthStatusResponse) Reset() { *m = AuthStatusResponse{} }
func (m *AuthStatusResponse) String() string { return proto.CompactTextString(m) }
func (*AuthStatusResponse) ProtoMessage() {}
func (*AuthStatusResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_77a6da22d6a3feb1, []int{82}
}
func (m *AuthStatusResponse) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
}
func (m *AuthStatusResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
if deterministic {
return xxx_messageInfo_AuthStatusResponse.Marshal(b, m, deterministic)
} else {
b = b[:cap(b)]
n, err := m.MarshalToSizedBuffer(b)
if err != nil {
return nil, err
}
return b[:n], nil
}
}
func (m *AuthStatusResponse) XXX_Merge(src proto.Message) {
xxx_messageInfo_AuthStatusResponse.Merge(m, src)
}
func (m *AuthStatusResponse) XXX_Size() int {
return m.Size()
}
func (m *AuthStatusResponse) XXX_DiscardUnknown() {
xxx_messageInfo_AuthStatusResponse.DiscardUnknown(m)
}
var xxx_messageInfo_AuthStatusResponse proto.InternalMessageInfo
func (m *AuthStatusResponse) GetHeader() *ResponseHeader {
if m != nil {
return m.Header
}
return nil
}
func (m *AuthStatusResponse) GetEnabled() bool {
if m != nil {
return m.Enabled
}
return false
}
func (m *AuthStatusResponse) GetAuthRevision() uint64 {
if m != nil {
return m.AuthRevision
}
return 0
}
type AuthenticateResponse struct {
Header *ResponseHeader `protobuf:"bytes,1,opt,name=header,proto3" json:"header,omitempty"`
// token is an authorized token that can be used in succeeding RPCs
Token string `protobuf:"bytes,2,opt,name=token,proto3" json:"token,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *AuthenticateResponse) Reset() { *m = AuthenticateResponse{} }
func (m *AuthenticateResponse) String() string { return proto.CompactTextString(m) }
func (*AuthenticateResponse) ProtoMessage() {}
func (*AuthenticateResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_77a6da22d6a3feb1, []int{83}
}
func (m *AuthenticateResponse) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
}
func (m *AuthenticateResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
if deterministic {
return xxx_messageInfo_AuthenticateResponse.Marshal(b, m, deterministic)
} else {
b = b[:cap(b)]
n, err := m.MarshalToSizedBuffer(b)
if err != nil {
return nil, err
}
return b[:n], nil
}
}
func (m *AuthenticateResponse) XXX_Merge(src proto.Message) {
xxx_messageInfo_AuthenticateResponse.Merge(m, src)
}
func (m *AuthenticateResponse) XXX_Size() int {
return m.Size()
}
func (m *AuthenticateResponse) XXX_DiscardUnknown() {
xxx_messageInfo_AuthenticateResponse.DiscardUnknown(m)
}
var xxx_messageInfo_AuthenticateResponse proto.InternalMessageInfo
func (m *AuthenticateResponse) GetHeader() *ResponseHeader {
if m != nil {
return m.Header
}
return nil
}
func (m *AuthenticateResponse) GetToken() string {
if m != nil {
return m.Token
}
return ""
}
type AuthUserAddResponse struct {
Header *ResponseHeader `protobuf:"bytes,1,opt,name=header,proto3" json:"header,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *AuthUserAddResponse) Reset() { *m = AuthUserAddResponse{} }
func (m *AuthUserAddResponse) String() string { return proto.CompactTextString(m) }
func (*AuthUserAddResponse) ProtoMessage() {}
func (*AuthUserAddResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_77a6da22d6a3feb1, []int{84}
}
func (m *AuthUserAddResponse) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
}
func (m *AuthUserAddResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
if deterministic {
return xxx_messageInfo_AuthUserAddResponse.Marshal(b, m, deterministic)
} else {
b = b[:cap(b)]
n, err := m.MarshalToSizedBuffer(b)
if err != nil {
return nil, err
}
return b[:n], nil
}
}
func (m *AuthUserAddResponse) XXX_Merge(src proto.Message) {
xxx_messageInfo_AuthUserAddResponse.Merge(m, src)
}
func (m *AuthUserAddResponse) XXX_Size() int {
return m.Size()
}
func (m *AuthUserAddResponse) XXX_DiscardUnknown() {
xxx_messageInfo_AuthUserAddResponse.DiscardUnknown(m)
}
var xxx_messageInfo_AuthUserAddResponse proto.InternalMessageInfo
func (m *AuthUserAddResponse) GetHeader() *ResponseHeader {
if m != nil {
return m.Header
}
return nil
}
type AuthUserGetResponse struct {
Header *ResponseHeader `protobuf:"bytes,1,opt,name=header,proto3" json:"header,omitempty"`
Roles []string `protobuf:"bytes,2,rep,name=roles,proto3" json:"roles,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *AuthUserGetResponse) Reset() { *m = AuthUserGetResponse{} }
func (m *AuthUserGetResponse) String() string { return proto.CompactTextString(m) }
func (*AuthUserGetResponse) ProtoMessage() {}
func (*AuthUserGetResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_77a6da22d6a3feb1, []int{85}
}
func (m *AuthUserGetResponse) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
}
func (m *AuthUserGetResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
if deterministic {
return xxx_messageInfo_AuthUserGetResponse.Marshal(b, m, deterministic)
} else {
b = b[:cap(b)]
n, err := m.MarshalToSizedBuffer(b)
if err != nil {
return nil, err
}
return b[:n], nil
}
}
func (m *AuthUserGetResponse) XXX_Merge(src proto.Message) {
xxx_messageInfo_AuthUserGetResponse.Merge(m, src)
}
func (m *AuthUserGetResponse) XXX_Size() int {
return m.Size()
}
func (m *AuthUserGetResponse) XXX_DiscardUnknown() {
xxx_messageInfo_AuthUserGetResponse.DiscardUnknown(m)
}
var xxx_messageInfo_AuthUserGetResponse proto.InternalMessageInfo
func (m *AuthUserGetResponse) GetHeader() *ResponseHeader {
if m != nil {
return m.Header
}
return nil
}
func (m *AuthUserGetResponse) GetRoles() []string {
if m != nil {
return m.Roles
}
return nil
}
type AuthUserDeleteResponse struct {
Header *ResponseHeader `protobuf:"bytes,1,opt,name=header,proto3" json:"header,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *AuthUserDeleteResponse) Reset() { *m = AuthUserDeleteResponse{} }
func (m *AuthUserDeleteResponse) String() string { return proto.CompactTextString(m) }
func (*AuthUserDeleteResponse) ProtoMessage() {}
func (*AuthUserDeleteResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_77a6da22d6a3feb1, []int{86}
}
func (m *AuthUserDeleteResponse) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
}
func (m *AuthUserDeleteResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
if deterministic {
return xxx_messageInfo_AuthUserDeleteResponse.Marshal(b, m, deterministic)
} else {
b = b[:cap(b)]
n, err := m.MarshalToSizedBuffer(b)
if err != nil {
return nil, err
}
return b[:n], nil
}
}
func (m *AuthUserDeleteResponse) XXX_Merge(src proto.Message) {
xxx_messageInfo_AuthUserDeleteResponse.Merge(m, src)
}
func (m *AuthUserDeleteResponse) XXX_Size() int {
return m.Size()
}
func (m *AuthUserDeleteResponse) XXX_DiscardUnknown() {
xxx_messageInfo_AuthUserDeleteResponse.DiscardUnknown(m)
}
var xxx_messageInfo_AuthUserDeleteResponse proto.InternalMessageInfo
func (m *AuthUserDeleteResponse) GetHeader() *ResponseHeader {
if m != nil {
return m.Header
}
return nil
}
type AuthUserChangePasswordResponse struct {
Header *ResponseHeader `protobuf:"bytes,1,opt,name=header,proto3" json:"header,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *AuthUserChangePasswordResponse) Reset() { *m = AuthUserChangePasswordResponse{} }
func (m *AuthUserChangePasswordResponse) String() string { return proto.CompactTextString(m) }
func (*AuthUserChangePasswordResponse) ProtoMessage() {}
func (*AuthUserChangePasswordResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_77a6da22d6a3feb1, []int{87}
}
func (m *AuthUserChangePasswordResponse) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
}
func (m *AuthUserChangePasswordResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
if deterministic {
return xxx_messageInfo_AuthUserChangePasswordResponse.Marshal(b, m, deterministic)
} else {
b = b[:cap(b)]
n, err := m.MarshalToSizedBuffer(b)
if err != nil {
return nil, err
}
return b[:n], nil
}
}
func (m *AuthUserChangePasswordResponse) XXX_Merge(src proto.Message) {
xxx_messageInfo_AuthUserChangePasswordResponse.Merge(m, src)
}
func (m *AuthUserChangePasswordResponse) XXX_Size() int {
return m.Size()
}
func (m *AuthUserChangePasswordResponse) XXX_DiscardUnknown() {
xxx_messageInfo_AuthUserChangePasswordResponse.DiscardUnknown(m)
}
var xxx_messageInfo_AuthUserChangePasswordResponse proto.InternalMessageInfo
func (m *AuthUserChangePasswordResponse) GetHeader() *ResponseHeader {
if m != nil {
return m.Header
}
return nil
}
type AuthUserGrantRoleResponse struct {
Header *ResponseHeader `protobuf:"bytes,1,opt,name=header,proto3" json:"header,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *AuthUserGrantRoleResponse) Reset() { *m = AuthUserGrantRoleResponse{} }
func (m *AuthUserGrantRoleResponse) String() string { return proto.CompactTextString(m) }
func (*AuthUserGrantRoleResponse) ProtoMessage() {}
func (*AuthUserGrantRoleResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_77a6da22d6a3feb1, []int{88}
}
func (m *AuthUserGrantRoleResponse) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
}
func (m *AuthUserGrantRoleResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
if deterministic {
return xxx_messageInfo_AuthUserGrantRoleResponse.Marshal(b, m, deterministic)
} else {
b = b[:cap(b)]
n, err := m.MarshalToSizedBuffer(b)
if err != nil {
return nil, err
}
return b[:n], nil
}
}
func (m *AuthUserGrantRoleResponse) XXX_Merge(src proto.Message) {
xxx_messageInfo_AuthUserGrantRoleResponse.Merge(m, src)
}
func (m *AuthUserGrantRoleResponse) XXX_Size() int {
return m.Size()
}
func (m *AuthUserGrantRoleResponse) XXX_DiscardUnknown() {
xxx_messageInfo_AuthUserGrantRoleResponse.DiscardUnknown(m)
}
var xxx_messageInfo_AuthUserGrantRoleResponse proto.InternalMessageInfo
func (m *AuthUserGrantRoleResponse) GetHeader() *ResponseHeader {
if m != nil {
return m.Header
}
return nil
}
type AuthUserRevokeRoleResponse struct {
Header *ResponseHeader `protobuf:"bytes,1,opt,name=header,proto3" json:"header,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *AuthUserRevokeRoleResponse) Reset() { *m = AuthUserRevokeRoleResponse{} }
func (m *AuthUserRevokeRoleResponse) String() string { return proto.CompactTextString(m) }
func (*AuthUserRevokeRoleResponse) ProtoMessage() {}
func (*AuthUserRevokeRoleResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_77a6da22d6a3feb1, []int{89}
}
func (m *AuthUserRevokeRoleResponse) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
}
func (m *AuthUserRevokeRoleResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
if deterministic {
return xxx_messageInfo_AuthUserRevokeRoleResponse.Marshal(b, m, deterministic)
} else {
b = b[:cap(b)]
n, err := m.MarshalToSizedBuffer(b)
if err != nil {
return nil, err
}
return b[:n], nil
}
}
func (m *AuthUserRevokeRoleResponse) XXX_Merge(src proto.Message) {
xxx_messageInfo_AuthUserRevokeRoleResponse.Merge(m, src)
}
func (m *AuthUserRevokeRoleResponse) XXX_Size() int {
return m.Size()
}
func (m *AuthUserRevokeRoleResponse) XXX_DiscardUnknown() {
xxx_messageInfo_AuthUserRevokeRoleResponse.DiscardUnknown(m)
}
var xxx_messageInfo_AuthUserRevokeRoleResponse proto.InternalMessageInfo
func (m *AuthUserRevokeRoleResponse) GetHeader() *ResponseHeader {
if m != nil {
return m.Header
}
return nil
}
type AuthRoleAddResponse struct {
Header *ResponseHeader `protobuf:"bytes,1,opt,name=header,proto3" json:"header,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *AuthRoleAddResponse) Reset() { *m = AuthRoleAddResponse{} }
func (m *AuthRoleAddResponse) String() string { return proto.CompactTextString(m) }
func (*AuthRoleAddResponse) ProtoMessage() {}
func (*AuthRoleAddResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_77a6da22d6a3feb1, []int{90}
}
func (m *AuthRoleAddResponse) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
}
func (m *AuthRoleAddResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
if deterministic {
return xxx_messageInfo_AuthRoleAddResponse.Marshal(b, m, deterministic)
} else {
b = b[:cap(b)]
n, err := m.MarshalToSizedBuffer(b)
if err != nil {
return nil, err
}
return b[:n], nil
}
}
func (m *AuthRoleAddResponse) XXX_Merge(src proto.Message) {
xxx_messageInfo_AuthRoleAddResponse.Merge(m, src)
}
func (m *AuthRoleAddResponse) XXX_Size() int {
return m.Size()
}
func (m *AuthRoleAddResponse) XXX_DiscardUnknown() {
xxx_messageInfo_AuthRoleAddResponse.DiscardUnknown(m)
}
var xxx_messageInfo_AuthRoleAddResponse proto.InternalMessageInfo
func (m *AuthRoleAddResponse) GetHeader() *ResponseHeader {
if m != nil {
return m.Header
}
return nil
}
type AuthRoleGetResponse struct {
Header *ResponseHeader `protobuf:"bytes,1,opt,name=header,proto3" json:"header,omitempty"`
Perm []*authpb.Permission `protobuf:"bytes,2,rep,name=perm,proto3" json:"perm,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *AuthRoleGetResponse) Reset() { *m = AuthRoleGetResponse{} }
func (m *AuthRoleGetResponse) String() string { return proto.CompactTextString(m) }
func (*AuthRoleGetResponse) ProtoMessage() {}
func (*AuthRoleGetResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_77a6da22d6a3feb1, []int{91}
}
func (m *AuthRoleGetResponse) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
}
func (m *AuthRoleGetResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
if deterministic {
return xxx_messageInfo_AuthRoleGetResponse.Marshal(b, m, deterministic)
} else {
b = b[:cap(b)]
n, err := m.MarshalToSizedBuffer(b)
if err != nil {
return nil, err
}
return b[:n], nil
}
}
func (m *AuthRoleGetResponse) XXX_Merge(src proto.Message) {
xxx_messageInfo_AuthRoleGetResponse.Merge(m, src)
}
func (m *AuthRoleGetResponse) XXX_Size() int {
return m.Size()
}
func (m *AuthRoleGetResponse) XXX_DiscardUnknown() {
xxx_messageInfo_AuthRoleGetResponse.DiscardUnknown(m)
}
var xxx_messageInfo_AuthRoleGetResponse proto.InternalMessageInfo
func (m *AuthRoleGetResponse) GetHeader() *ResponseHeader {
if m != nil {
return m.Header
}
return nil
}
func (m *AuthRoleGetResponse) GetPerm() []*authpb.Permission {
if m != nil {
return m.Perm
}
return nil
}
type AuthRoleListResponse struct {
Header *ResponseHeader `protobuf:"bytes,1,opt,name=header,proto3" json:"header,omitempty"`
Roles []string `protobuf:"bytes,2,rep,name=roles,proto3" json:"roles,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *AuthRoleListResponse) Reset() { *m = AuthRoleListResponse{} }
func (m *AuthRoleListResponse) String() string { return proto.CompactTextString(m) }
func (*AuthRoleListResponse) ProtoMessage() {}
func (*AuthRoleListResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_77a6da22d6a3feb1, []int{92}
}
func (m *AuthRoleListResponse) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
}
func (m *AuthRoleListResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
if deterministic {
return xxx_messageInfo_AuthRoleListResponse.Marshal(b, m, deterministic)
} else {
b = b[:cap(b)]
n, err := m.MarshalToSizedBuffer(b)
if err != nil {
return nil, err
}
return b[:n], nil
}
}
func (m *AuthRoleListResponse) XXX_Merge(src proto.Message) {
xxx_messageInfo_AuthRoleListResponse.Merge(m, src)
}
func (m *AuthRoleListResponse) XXX_Size() int {
return m.Size()
}
func (m *AuthRoleListResponse) XXX_DiscardUnknown() {
xxx_messageInfo_AuthRoleListResponse.DiscardUnknown(m)
}
var xxx_messageInfo_AuthRoleListResponse proto.InternalMessageInfo
func (m *AuthRoleListResponse) GetHeader() *ResponseHeader {
if m != nil {
return m.Header
}
return nil
}
func (m *AuthRoleListResponse) GetRoles() []string {
if m != nil {
return m.Roles
}
return nil
}
type AuthUserListResponse struct {
Header *ResponseHeader `protobuf:"bytes,1,opt,name=header,proto3" json:"header,omitempty"`
Users []string `protobuf:"bytes,2,rep,name=users,proto3" json:"users,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *AuthUserListResponse) Reset() { *m = AuthUserListResponse{} }
func (m *AuthUserListResponse) String() string { return proto.CompactTextString(m) }
func (*AuthUserListResponse) ProtoMessage() {}
func (*AuthUserListResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_77a6da22d6a3feb1, []int{93}
}
func (m *AuthUserListResponse) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
}
func (m *AuthUserListResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
if deterministic {
return xxx_messageInfo_AuthUserListResponse.Marshal(b, m, deterministic)
} else {
b = b[:cap(b)]
n, err := m.MarshalToSizedBuffer(b)
if err != nil {
return nil, err
}
return b[:n], nil
}
}
func (m *AuthUserListResponse) XXX_Merge(src proto.Message) {
xxx_messageInfo_AuthUserListResponse.Merge(m, src)
}
func (m *AuthUserListResponse) XXX_Size() int {
return m.Size()
}
func (m *AuthUserListResponse) XXX_DiscardUnknown() {
xxx_messageInfo_AuthUserListResponse.DiscardUnknown(m)
}
var xxx_messageInfo_AuthUserListResponse proto.InternalMessageInfo
func (m *AuthUserListResponse) GetHeader() *ResponseHeader {
if m != nil {
return m.Header
}
return nil
}
func (m *AuthUserListResponse) GetUsers() []string {
if m != nil {
return m.Users
}
return nil
}
type AuthRoleDeleteResponse struct {
Header *ResponseHeader `protobuf:"bytes,1,opt,name=header,proto3" json:"header,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *AuthRoleDeleteResponse) Reset() { *m = AuthRoleDeleteResponse{} }
func (m *AuthRoleDeleteResponse) String() string { return proto.CompactTextString(m) }
func (*AuthRoleDeleteResponse) ProtoMessage() {}
func (*AuthRoleDeleteResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_77a6da22d6a3feb1, []int{94}
}
func (m *AuthRoleDeleteResponse) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
}
func (m *AuthRoleDeleteResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
if deterministic {
return xxx_messageInfo_AuthRoleDeleteResponse.Marshal(b, m, deterministic)
} else {
b = b[:cap(b)]
n, err := m.MarshalToSizedBuffer(b)
if err != nil {
return nil, err
}
return b[:n], nil
}
}
func (m *AuthRoleDeleteResponse) XXX_Merge(src proto.Message) {
xxx_messageInfo_AuthRoleDeleteResponse.Merge(m, src)
}
func (m *AuthRoleDeleteResponse) XXX_Size() int {
return m.Size()
}
func (m *AuthRoleDeleteResponse) XXX_DiscardUnknown() {
xxx_messageInfo_AuthRoleDeleteResponse.DiscardUnknown(m)
}
var xxx_messageInfo_AuthRoleDeleteResponse proto.InternalMessageInfo
func (m *AuthRoleDeleteResponse) GetHeader() *ResponseHeader {
if m != nil {
return m.Header
}
return nil
}
type AuthRoleGrantPermissionResponse struct {
Header *ResponseHeader `protobuf:"bytes,1,opt,name=header,proto3" json:"header,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *AuthRoleGrantPermissionResponse) Reset() { *m = AuthRoleGrantPermissionResponse{} }
func (m *AuthRoleGrantPermissionResponse) String() string { return proto.CompactTextString(m) }
func (*AuthRoleGrantPermissionResponse) ProtoMessage() {}
func (*AuthRoleGrantPermissionResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_77a6da22d6a3feb1, []int{95}
}
func (m *AuthRoleGrantPermissionResponse) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
}
func (m *AuthRoleGrantPermissionResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
if deterministic {
return xxx_messageInfo_AuthRoleGrantPermissionResponse.Marshal(b, m, deterministic)
} else {
b = b[:cap(b)]
n, err := m.MarshalToSizedBuffer(b)
if err != nil {
return nil, err
}
return b[:n], nil
}
}
func (m *AuthRoleGrantPermissionResponse) XXX_Merge(src proto.Message) {
xxx_messageInfo_AuthRoleGrantPermissionResponse.Merge(m, src)
}
func (m *AuthRoleGrantPermissionResponse) XXX_Size() int {
return m.Size()
}
func (m *AuthRoleGrantPermissionResponse) XXX_DiscardUnknown() {
xxx_messageInfo_AuthRoleGrantPermissionResponse.DiscardUnknown(m)
}
var xxx_messageInfo_AuthRoleGrantPermissionResponse proto.InternalMessageInfo
func (m *AuthRoleGrantPermissionResponse) GetHeader() *ResponseHeader {
if m != nil {
return m.Header
}
return nil
}
type AuthRoleRevokePermissionResponse struct {
Header *ResponseHeader `protobuf:"bytes,1,opt,name=header,proto3" json:"header,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *AuthRoleRevokePermissionResponse) Reset() { *m = AuthRoleRevokePermissionResponse{} }
func (m *AuthRoleRevokePermissionResponse) String() string { return proto.CompactTextString(m) }
func (*AuthRoleRevokePermissionResponse) ProtoMessage() {}
func (*AuthRoleRevokePermissionResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_77a6da22d6a3feb1, []int{96}
}
func (m *AuthRoleRevokePermissionResponse) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
}
func (m *AuthRoleRevokePermissionResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
if deterministic {
return xxx_messageInfo_AuthRoleRevokePermissionResponse.Marshal(b, m, deterministic)
} else {
b = b[:cap(b)]
n, err := m.MarshalToSizedBuffer(b)
if err != nil {
return nil, err
}
return b[:n], nil
}
}
func (m *AuthRoleRevokePermissionResponse) XXX_Merge(src proto.Message) {
xxx_messageInfo_AuthRoleRevokePermissionResponse.Merge(m, src)
}
func (m *AuthRoleRevokePermissionResponse) XXX_Size() int {
return m.Size()
}
func (m *AuthRoleRevokePermissionResponse) XXX_DiscardUnknown() {
xxx_messageInfo_AuthRoleRevokePermissionResponse.DiscardUnknown(m)
}
var xxx_messageInfo_AuthRoleRevokePermissionResponse proto.InternalMessageInfo
func (m *AuthRoleRevokePermissionResponse) GetHeader() *ResponseHeader {
if m != nil {
return m.Header
}
return nil
}
func init() {
proto.RegisterEnum("etcdserverpb.AlarmType", AlarmType_name, AlarmType_value)
proto.RegisterEnum("etcdserverpb.RangeRequest_SortOrder", RangeRequest_SortOrder_name, RangeRequest_SortOrder_value)
proto.RegisterEnum("etcdserverpb.RangeRequest_SortTarget", RangeRequest_SortTarget_name, RangeRequest_SortTarget_value)
proto.RegisterEnum("etcdserverpb.Compare_CompareResult", Compare_CompareResult_name, Compare_CompareResult_value)
proto.RegisterEnum("etcdserverpb.Compare_CompareTarget", Compare_CompareTarget_name, Compare_CompareTarget_value)
proto.RegisterEnum("etcdserverpb.WatchCreateRequest_FilterType", WatchCreateRequest_FilterType_name, WatchCreateRequest_FilterType_value)
proto.RegisterEnum("etcdserverpb.AlarmRequest_AlarmAction", AlarmRequest_AlarmAction_name, AlarmRequest_AlarmAction_value)
proto.RegisterEnum("etcdserverpb.DowngradeRequest_DowngradeAction", DowngradeRequest_DowngradeAction_name, DowngradeRequest_DowngradeAction_value)
proto.RegisterType((*ResponseHeader)(nil), "etcdserverpb.ResponseHeader")
proto.RegisterType((*RangeRequest)(nil), "etcdserverpb.RangeRequest")
proto.RegisterType((*RangeResponse)(nil), "etcdserverpb.RangeResponse")
proto.RegisterType((*PutRequest)(nil), "etcdserverpb.PutRequest")
proto.RegisterType((*PutResponse)(nil), "etcdserverpb.PutResponse")
proto.RegisterType((*DeleteRangeRequest)(nil), "etcdserverpb.DeleteRangeRequest")
proto.RegisterType((*DeleteRangeResponse)(nil), "etcdserverpb.DeleteRangeResponse")
proto.RegisterType((*RequestOp)(nil), "etcdserverpb.RequestOp")
proto.RegisterType((*ResponseOp)(nil), "etcdserverpb.ResponseOp")
proto.RegisterType((*Compare)(nil), "etcdserverpb.Compare")
proto.RegisterType((*TxnRequest)(nil), "etcdserverpb.TxnRequest")
proto.RegisterType((*TxnResponse)(nil), "etcdserverpb.TxnResponse")
proto.RegisterType((*CompactionRequest)(nil), "etcdserverpb.CompactionRequest")
proto.RegisterType((*CompactionResponse)(nil), "etcdserverpb.CompactionResponse")
proto.RegisterType((*HashRequest)(nil), "etcdserverpb.HashRequest")
proto.RegisterType((*HashKVRequest)(nil), "etcdserverpb.HashKVRequest")
proto.RegisterType((*HashKVResponse)(nil), "etcdserverpb.HashKVResponse")
proto.RegisterType((*HashResponse)(nil), "etcdserverpb.HashResponse")
proto.RegisterType((*SnapshotRequest)(nil), "etcdserverpb.SnapshotRequest")
proto.RegisterType((*SnapshotResponse)(nil), "etcdserverpb.SnapshotResponse")
proto.RegisterType((*WatchRequest)(nil), "etcdserverpb.WatchRequest")
proto.RegisterType((*WatchCreateRequest)(nil), "etcdserverpb.WatchCreateRequest")
proto.RegisterType((*WatchCancelRequest)(nil), "etcdserverpb.WatchCancelRequest")
proto.RegisterType((*WatchProgressRequest)(nil), "etcdserverpb.WatchProgressRequest")
proto.RegisterType((*WatchResponse)(nil), "etcdserverpb.WatchResponse")
proto.RegisterType((*LeaseGrantRequest)(nil), "etcdserverpb.LeaseGrantRequest")
proto.RegisterType((*LeaseGrantResponse)(nil), "etcdserverpb.LeaseGrantResponse")
proto.RegisterType((*LeaseRevokeRequest)(nil), "etcdserverpb.LeaseRevokeRequest")
proto.RegisterType((*LeaseRevokeResponse)(nil), "etcdserverpb.LeaseRevokeResponse")
proto.RegisterType((*LeaseCheckpoint)(nil), "etcdserverpb.LeaseCheckpoint")
proto.RegisterType((*LeaseCheckpointRequest)(nil), "etcdserverpb.LeaseCheckpointRequest")
proto.RegisterType((*LeaseCheckpointResponse)(nil), "etcdserverpb.LeaseCheckpointResponse")
proto.RegisterType((*LeaseKeepAliveRequest)(nil), "etcdserverpb.LeaseKeepAliveRequest")
proto.RegisterType((*LeaseKeepAliveResponse)(nil), "etcdserverpb.LeaseKeepAliveResponse")
proto.RegisterType((*LeaseTimeToLiveRequest)(nil), "etcdserverpb.LeaseTimeToLiveRequest")
proto.RegisterType((*LeaseTimeToLiveResponse)(nil), "etcdserverpb.LeaseTimeToLiveResponse")
proto.RegisterType((*LeaseLeasesRequest)(nil), "etcdserverpb.LeaseLeasesRequest")
proto.RegisterType((*LeaseStatus)(nil), "etcdserverpb.LeaseStatus")
proto.RegisterType((*LeaseLeasesResponse)(nil), "etcdserverpb.LeaseLeasesResponse")
proto.RegisterType((*Member)(nil), "etcdserverpb.Member")
proto.RegisterType((*MemberAddRequest)(nil), "etcdserverpb.MemberAddRequest")
proto.RegisterType((*MemberAddResponse)(nil), "etcdserverpb.MemberAddResponse")
proto.RegisterType((*MemberRemoveRequest)(nil), "etcdserverpb.MemberRemoveRequest")
proto.RegisterType((*MemberRemoveResponse)(nil), "etcdserverpb.MemberRemoveResponse")
proto.RegisterType((*MemberUpdateRequest)(nil), "etcdserverpb.MemberUpdateRequest")
proto.RegisterType((*MemberUpdateResponse)(nil), "etcdserverpb.MemberUpdateResponse")
proto.RegisterType((*MemberListRequest)(nil), "etcdserverpb.MemberListRequest")
proto.RegisterType((*MemberListResponse)(nil), "etcdserverpb.MemberListResponse")
proto.RegisterType((*MemberPromoteRequest)(nil), "etcdserverpb.MemberPromoteRequest")
proto.RegisterType((*MemberPromoteResponse)(nil), "etcdserverpb.MemberPromoteResponse")
proto.RegisterType((*DefragmentRequest)(nil), "etcdserverpb.DefragmentRequest")
proto.RegisterType((*DefragmentResponse)(nil), "etcdserverpb.DefragmentResponse")
proto.RegisterType((*MoveLeaderRequest)(nil), "etcdserverpb.MoveLeaderRequest")
proto.RegisterType((*MoveLeaderResponse)(nil), "etcdserverpb.MoveLeaderResponse")
proto.RegisterType((*AlarmRequest)(nil), "etcdserverpb.AlarmRequest")
proto.RegisterType((*AlarmMember)(nil), "etcdserverpb.AlarmMember")
proto.RegisterType((*AlarmResponse)(nil), "etcdserverpb.AlarmResponse")
proto.RegisterType((*DowngradeRequest)(nil), "etcdserverpb.DowngradeRequest")
proto.RegisterType((*DowngradeResponse)(nil), "etcdserverpb.DowngradeResponse")
proto.RegisterType((*DowngradeVersionTestRequest)(nil), "etcdserverpb.DowngradeVersionTestRequest")
proto.RegisterType((*StatusRequest)(nil), "etcdserverpb.StatusRequest")
proto.RegisterType((*StatusResponse)(nil), "etcdserverpb.StatusResponse")
proto.RegisterType((*DowngradeInfo)(nil), "etcdserverpb.DowngradeInfo")
proto.RegisterType((*AuthEnableRequest)(nil), "etcdserverpb.AuthEnableRequest")
proto.RegisterType((*AuthDisableRequest)(nil), "etcdserverpb.AuthDisableRequest")
proto.RegisterType((*AuthStatusRequest)(nil), "etcdserverpb.AuthStatusRequest")
proto.RegisterType((*AuthenticateRequest)(nil), "etcdserverpb.AuthenticateRequest")
proto.RegisterType((*AuthUserAddRequest)(nil), "etcdserverpb.AuthUserAddRequest")
proto.RegisterType((*AuthUserGetRequest)(nil), "etcdserverpb.AuthUserGetRequest")
proto.RegisterType((*AuthUserDeleteRequest)(nil), "etcdserverpb.AuthUserDeleteRequest")
proto.RegisterType((*AuthUserChangePasswordRequest)(nil), "etcdserverpb.AuthUserChangePasswordRequest")
proto.RegisterType((*AuthUserGrantRoleRequest)(nil), "etcdserverpb.AuthUserGrantRoleRequest")
proto.RegisterType((*AuthUserRevokeRoleRequest)(nil), "etcdserverpb.AuthUserRevokeRoleRequest")
proto.RegisterType((*AuthRoleAddRequest)(nil), "etcdserverpb.AuthRoleAddRequest")
proto.RegisterType((*AuthRoleGetRequest)(nil), "etcdserverpb.AuthRoleGetRequest")
proto.RegisterType((*AuthUserListRequest)(nil), "etcdserverpb.AuthUserListRequest")
proto.RegisterType((*AuthRoleListRequest)(nil), "etcdserverpb.AuthRoleListRequest")
proto.RegisterType((*AuthRoleDeleteRequest)(nil), "etcdserverpb.AuthRoleDeleteRequest")
proto.RegisterType((*AuthRoleGrantPermissionRequest)(nil), "etcdserverpb.AuthRoleGrantPermissionRequest")
proto.RegisterType((*AuthRoleRevokePermissionRequest)(nil), "etcdserverpb.AuthRoleRevokePermissionRequest")
proto.RegisterType((*AuthEnableResponse)(nil), "etcdserverpb.AuthEnableResponse")
proto.RegisterType((*AuthDisableResponse)(nil), "etcdserverpb.AuthDisableResponse")
proto.RegisterType((*AuthStatusResponse)(nil), "etcdserverpb.AuthStatusResponse")
proto.RegisterType((*AuthenticateResponse)(nil), "etcdserverpb.AuthenticateResponse")
proto.RegisterType((*AuthUserAddResponse)(nil), "etcdserverpb.AuthUserAddResponse")
proto.RegisterType((*AuthUserGetResponse)(nil), "etcdserverpb.AuthUserGetResponse")
proto.RegisterType((*AuthUserDeleteResponse)(nil), "etcdserverpb.AuthUserDeleteResponse")
proto.RegisterType((*AuthUserChangePasswordResponse)(nil), "etcdserverpb.AuthUserChangePasswordResponse")
proto.RegisterType((*AuthUserGrantRoleResponse)(nil), "etcdserverpb.AuthUserGrantRoleResponse")
proto.RegisterType((*AuthUserRevokeRoleResponse)(nil), "etcdserverpb.AuthUserRevokeRoleResponse")
proto.RegisterType((*AuthRoleAddResponse)(nil), "etcdserverpb.AuthRoleAddResponse")
proto.RegisterType((*AuthRoleGetResponse)(nil), "etcdserverpb.AuthRoleGetResponse")
proto.RegisterType((*AuthRoleListResponse)(nil), "etcdserverpb.AuthRoleListResponse")
proto.RegisterType((*AuthUserListResponse)(nil), "etcdserverpb.AuthUserListResponse")
proto.RegisterType((*AuthRoleDeleteResponse)(nil), "etcdserverpb.AuthRoleDeleteResponse")
proto.RegisterType((*AuthRoleGrantPermissionResponse)(nil), "etcdserverpb.AuthRoleGrantPermissionResponse")
proto.RegisterType((*AuthRoleRevokePermissionResponse)(nil), "etcdserverpb.AuthRoleRevokePermissionResponse")
}
func init() { proto.RegisterFile("rpc.proto", fileDescriptor_77a6da22d6a3feb1) }
var fileDescriptor_77a6da22d6a3feb1 = []byte{
// 4564 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xc4, 0x3c, 0x5d, 0x6f, 0x5c, 0x49,
0x56, 0xbe, 0xdd, 0xb6, 0xdb, 0x7d, 0xfa, 0xc3, 0x9d, 0x8a, 0x93, 0x74, 0x3a, 0x89, 0xe3, 0xb9,
0x49, 0x66, 0x32, 0x99, 0x89, 0x3b, 0xb1, 0x93, 0x99, 0x25, 0x68, 0x86, 0xed, 0xd8, 0x3d, 0x89,
0x37, 0x8e, 0xed, 0xb9, 0xee, 0x64, 0x76, 0x82, 0xb4, 0xe6, 0xba, 0xbb, 0x62, 0xdf, 0x75, 0xf7,
0xbd, 0xbd, 0xf7, 0x5e, 0x77, 0xec, 0xe1, 0x61, 0x87, 0x85, 0x65, 0xb5, 0x20, 0xad, 0xc4, 0x20,
0xa1, 0x15, 0x82, 0x17, 0x40, 0x82, 0x07, 0x40, 0xf0, 0xc0, 0x03, 0x02, 0x89, 0x07, 0x78, 0x80,
0x07, 0x24, 0x24, 0xfe, 0x00, 0x0c, 0xfb, 0xc4, 0xaf, 0x58, 0xd5, 0xd7, 0xad, 0xaa, 0xfb, 0x61,
0x67, 0xd6, 0x1e, 0xed, 0x4b, 0x7c, 0xab, 0xea, 0x7c, 0xd5, 0x39, 0x55, 0xe7, 0x54, 0x9d, 0x53,
0x69, 0x28, 0xfa, 0xc3, 0xee, 0xfc, 0xd0, 0xf7, 0x42, 0x0f, 0x95, 0x71, 0xd8, 0xed, 0x05, 0xd8,
0x1f, 0x61, 0x7f, 0xb8, 0xdd, 0xa8, 0x93, 0x56, 0xd3, 0x1e, 0x3a, 0xcd, 0xc1, 0xa8, 0xdb, 0x1d,
0x6e, 0x37, 0xf7, 0x46, 0x0c, 0xae, 0xd1, 0x88, 0x46, 0xec, 0xfd, 0x70, 0x77, 0xb8, 0x4d, 0xff,
0xf0, 0xb1, 0xb9, 0x68, 0x6c, 0x84, 0xfd, 0xc0, 0xf1, 0xdc, 0xe1, 0xb6, 0xf8, 0xe2, 0x10, 0x97,
0x77, 0x3c, 0x6f, 0xa7, 0x8f, 0x19, 0xbe, 0xeb, 0x7a, 0xa1, 0x1d, 0x3a, 0x9e, 0x1b, 0xf0, 0x51,
0xf6, 0xa7, 0x7b, 0x7b, 0x07, 0xbb, 0xb7, 0xbd, 0x21, 0x76, 0xed, 0xa1, 0x33, 0x5a, 0x68, 0x7a,
0x43, 0x0a, 0x93, 0x84, 0x37, 0x7f, 0x62, 0x40, 0xd5, 0xc2, 0xc1, 0xd0, 0x73, 0x03, 0xfc, 0x18,
0xdb, 0x3d, 0xec, 0xa3, 0x2b, 0x00, 0xdd, 0xfe, 0x7e, 0x10, 0x62, 0x7f, 0xcb, 0xe9, 0xd5, 0x8d,
0x39, 0xe3, 0xe6, 0xb8, 0x55, 0xe4, 0x3d, 0x2b, 0x3d, 0x74, 0x09, 0x8a, 0x03, 0x3c, 0xd8, 0x66,
0xa3, 0x39, 0x3a, 0x3a, 0xc5, 0x3a, 0x56, 0x7a, 0xa8, 0x01, 0x53, 0x3e, 0x1e, 0x39, 0x44, 0xdc,
0x7a, 0x7e, 0xce, 0xb8, 0x99, 0xb7, 0xa2, 0x36, 0x41, 0xf4, 0xed, 0x97, 0xe1, 0x56, 0x88, 0xfd,
0x41, 0x7d, 0x9c, 0x21, 0x92, 0x8e, 0x0e, 0xf6, 0x07, 0x0f, 0x0a, 0x3f, 0xf8, 0x87, 0x7a, 0x7e,
0x71, 0xfe, 0x8e, 0xf9, 0xaf, 0x13, 0x50, 0xb6, 0x6c, 0x77, 0x07, 0x5b, 0xf8, 0x7b, 0xfb, 0x38,
0x08, 0x51, 0x0d, 0xf2, 0x7b, 0xf8, 0x90, 0xca, 0x51, 0xb6, 0xc8, 0x27, 0x23, 0xe4, 0xee, 0xe0,
0x2d, 0xec, 0x32, 0x09, 0xca, 0x84, 0x90, 0xbb, 0x83, 0xdb, 0x6e, 0x0f, 0xcd, 0xc0, 0x44, 0xdf,
0x19, 0x38, 0x21, 0x67, 0xcf, 0x1a, 0x9a, 0x5c, 0xe3, 0x31, 0xb9, 0x96, 0x00, 0x02, 0xcf, 0x0f,
0xb7, 0x3c, 0xbf, 0x87, 0xfd, 0xfa, 0xc4, 0x9c, 0x71, 0xb3, 0xba, 0x70, 0x7d, 0x5e, 0xb5, 0xe5,
0xbc, 0x2a, 0xd0, 0xfc, 0xa6, 0xe7, 0x87, 0xeb, 0x04, 0xd6, 0x2a, 0x06, 0xe2, 0x13, 0x7d, 0x04,
0x25, 0x4a, 0x24, 0xb4, 0xfd, 0x1d, 0x1c, 0xd6, 0x27, 0x29, 0x95, 0x1b, 0xc7, 0x50, 0xe9, 0x50,
0x60, 0x8b, 0xb2, 0x67, 0xdf, 0xc8, 0x84, 0x72, 0x80, 0x7d, 0xc7, 0xee, 0x3b, 0x9f, 0xd9, 0xdb,
0x7d, 0x5c, 0x2f, 0xcc, 0x19, 0x37, 0xa7, 0x2c, 0xad, 0x8f, 0xcc, 0x7f, 0x0f, 0x1f, 0x06, 0x5b,
0x9e, 0xdb, 0x3f, 0xac, 0x4f, 0x51, 0x80, 0x29, 0xd2, 0xb1, 0xee, 0xf6, 0x0f, 0xa9, 0xf5, 0xbc,
0x7d, 0x37, 0x64, 0xa3, 0x45, 0x3a, 0x5a, 0xa4, 0x3d, 0x74, 0xf8, 0x2e, 0xd4, 0x06, 0x8e, 0xbb,
0x35, 0xf0, 0x7a, 0x5b, 0x91, 0x42, 0x80, 0x28, 0xe4, 0x61, 0xe1, 0xf7, 0xa8, 0x05, 0xee, 0x5a,
0xd5, 0x81, 0xe3, 0x3e, 0xf5, 0x7a, 0x96, 0xd0, 0x0f, 0x41, 0xb1, 0x0f, 0x74, 0x94, 0x52, 0x1c,
0xc5, 0x3e, 0x50, 0x51, 0xde, 0x87, 0xb3, 0x84, 0x4b, 0xd7, 0xc7, 0x76, 0x88, 0x25, 0x56, 0x59,
0xc7, 0x3a, 0x33, 0x70, 0xdc, 0x25, 0x0a, 0xa2, 0x21, 0xda, 0x07, 0x09, 0xc4, 0x4a, 0x1c, 0xd1,
0x3e, 0xd0, 0x11, 0xcd, 0xf7, 0xa1, 0x18, 0xd9, 0x05, 0x4d, 0xc1, 0xf8, 0xda, 0xfa, 0x5a, 0xbb,
0x36, 0x86, 0x00, 0x26, 0x5b, 0x9b, 0x4b, 0xed, 0xb5, 0xe5, 0x9a, 0x81, 0x4a, 0x50, 0x58, 0x6e,
0xb3, 0x46, 0xae, 0x51, 0xf8, 0x82, 0xaf, 0xb7, 0x27, 0x00, 0xd2, 0x14, 0xa8, 0x00, 0xf9, 0x27,
0xed, 0x4f, 0x6b, 0x63, 0x04, 0xf8, 0x79, 0xdb, 0xda, 0x5c, 0x59, 0x5f, 0xab, 0x19, 0x84, 0xca,
0x92, 0xd5, 0x6e, 0x75, 0xda, 0xb5, 0x1c, 0x81, 0x78, 0xba, 0xbe, 0x5c, 0xcb, 0xa3, 0x22, 0x4c,
0x3c, 0x6f, 0xad, 0x3e, 0x6b, 0xd7, 0xc6, 0x23, 0x62, 0x72, 0x15, 0xff, 0x89, 0x01, 0x15, 0x6e,
0x6e, 0xb6, 0xb7, 0xd0, 0x3d, 0x98, 0xdc, 0xa5, 0xfb, 0x8b, 0xae, 0xe4, 0xd2, 0xc2, 0xe5, 0xd8,
0xda, 0xd0, 0xf6, 0xa0, 0xc5, 0x61, 0x91, 0x09, 0xf9, 0xbd, 0x51, 0x50, 0xcf, 0xcd, 0xe5, 0x6f,
0x96, 0x16, 0x6a, 0xf3, 0xcc, 0x93, 0xcc, 0x3f, 0xc1, 0x87, 0xcf, 0xed, 0xfe, 0x3e, 0xb6, 0xc8,
0x20, 0x42, 0x30, 0x3e, 0xf0, 0x7c, 0x4c, 0x17, 0xfc, 0x94, 0x45, 0xbf, 0xc9, 0x2e, 0xa0, 0x36,
0xe7, 0x8b, 0x9d, 0x35, 0xa4, 0x78, 0xff, 0x69, 0x00, 0x6c, 0xec, 0x87, 0xd9, 0x5b, 0x6c, 0x06,
0x26, 0x46, 0x84, 0x03, 0xdf, 0x5e, 0xac, 0x41, 0xf7, 0x16, 0xb6, 0x03, 0x1c, 0xed, 0x2d, 0xd2,
0x40, 0x73, 0x50, 0x18, 0xfa, 0x78, 0xb4, 0xb5, 0x37, 0xa2, 0xdc, 0xa6, 0xa4, 0x9d, 0x26, 0x49,
0xff, 0x93, 0x11, 0xba, 0x05, 0x65, 0x67, 0xc7, 0xf5, 0x7c, 0xbc, 0xc5, 0x88, 0x4e, 0xa8, 0x60,
0x0b, 0x56, 0x89, 0x0d, 0xd2, 0x29, 0x29, 0xb0, 0x8c, 0xd5, 0x64, 0x2a, 0xec, 0x2a, 0x19, 0x93,
0xf3, 0xf9, 0xdc, 0x80, 0x12, 0x9d, 0xcf, 0x89, 0x94, 0xbd, 0x20, 0x27, 0x92, 0xa3, 0x68, 0x09,
0x85, 0x27, 0xa6, 0x26, 0x45, 0x70, 0x01, 0x2d, 0xe3, 0x3e, 0x0e, 0xf1, 0x49, 0x9c, 0x97, 0xa2,
0xca, 0x7c, 0xaa, 0x2a, 0x25, 0xbf, 0xbf, 0x30, 0xe0, 0xac, 0xc6, 0xf0, 0x44, 0x53, 0xaf, 0x43,
0xa1, 0x47, 0x89, 0x31, 0x99, 0xf2, 0x96, 0x68, 0xa2, 0x7b, 0x30, 0xc5, 0x45, 0x0a, 0xea, 0xf9,
0xf4, 0x65, 0x28, 0xa5, 0x2c, 0x30, 0x29, 0x03, 0x29, 0xe6, 0x3f, 0xe5, 0xa0, 0xc8, 0x95, 0xb1,
0x3e, 0x44, 0x2d, 0xa8, 0xf8, 0xac, 0xb1, 0x45, 0xe7, 0xcc, 0x65, 0x6c, 0x64, 0xfb, 0xc9, 0xc7,
0x63, 0x56, 0x99, 0xa3, 0xd0, 0x6e, 0xf4, 0xab, 0x50, 0x12, 0x24, 0x86, 0xfb, 0x21, 0x37, 0x54,
0x5d, 0x27, 0x20, 0x97, 0xf6, 0xe3, 0x31, 0x0b, 0x38, 0xf8, 0xc6, 0x7e, 0x88, 0x3a, 0x30, 0x23,
0x90, 0xd9, 0xfc, 0xb8, 0x18, 0x79, 0x4a, 0x65, 0x4e, 0xa7, 0x92, 0x34, 0xe7, 0xe3, 0x31, 0x0b,
0x71, 0x7c, 0x65, 0x10, 0x2d, 0x4b, 0x91, 0xc2, 0x03, 0x16, 0x5f, 0x12, 0x22, 0x75, 0x0e, 0x5c,
0x4e, 0x44, 0x68, 0x6b, 0x51, 0x91, 0xad, 0x73, 0xe0, 0x46, 0x2a, 0x7b, 0x58, 0x84, 0x02, 0xef,
0x36, 0xff, 0x23, 0x07, 0x20, 0x2c, 0xb6, 0x3e, 0x44, 0xcb, 0x50, 0xf5, 0x79, 0x4b, 0xd3, 0xdf,
0xa5, 0x54, 0xfd, 0x71, 0x43, 0x8f, 0x59, 0x15, 0x81, 0xc4, 0xc4, 0xfd, 0x10, 0xca, 0x11, 0x15,
0xa9, 0xc2, 0x8b, 0x29, 0x2a, 0x8c, 0x28, 0x94, 0x04, 0x02, 0x51, 0xe2, 0x27, 0x70, 0x2e, 0xc2,
0x4f, 0xd1, 0xe2, 0x1b, 0x47, 0x68, 0x31, 0x22, 0x78, 0x56, 0x50, 0x50, 0xf5, 0xf8, 0x48, 0x11,
0x4c, 0x2a, 0xf2, 0x62, 0x8a, 0x22, 0x19, 0x90, 0xaa, 0xc9, 0x48, 0x42, 0x4d, 0x95, 0x40, 0xc2,
0x3e, 0xeb, 0x37, 0xff, 0x6a, 0x1c, 0x0a, 0x4b, 0xde, 0x60, 0x68, 0xfb, 0x64, 0x11, 0x4d, 0xfa,
0x38, 0xd8, 0xef, 0x87, 0x54, 0x81, 0xd5, 0x85, 0x6b, 0x3a, 0x0f, 0x0e, 0x26, 0xfe, 0x5a, 0x14,
0xd4, 0xe2, 0x28, 0x04, 0x99, 0x47, 0xf9, 0xdc, 0x6b, 0x20, 0xf3, 0x18, 0xcf, 0x51, 0x84, 0x43,
0xc8, 0x4b, 0x87, 0xd0, 0x80, 0x02, 0x3f, 0xe0, 0x31, 0x67, 0xfd, 0x78, 0xcc, 0x12, 0x1d, 0xe8,
0x6d, 0x98, 0x8e, 0x87, 0xc2, 0x09, 0x0e, 0x53, 0xed, 0xea, 0x91, 0xf3, 0x1a, 0x94, 0xb5, 0x08,
0x3d, 0xc9, 0xe1, 0x4a, 0x03, 0x25, 0x2e, 0x9f, 0x17, 0x6e, 0x9d, 0x1c, 0x2b, 0xca, 0x8f, 0xc7,
0x84, 0x63, 0xbf, 0x2a, 0x1c, 0xfb, 0x94, 0x1a, 0x68, 0x89, 0x5e, 0xb9, 0x8f, 0xbf, 0xae, 0x7a,
0xad, 0x6f, 0x12, 0xe4, 0x08, 0x48, 0xba, 0x2f, 0xd3, 0x82, 0x8a, 0xa6, 0x32, 0x12, 0x23, 0xdb,
0x1f, 0x3f, 0x6b, 0xad, 0xb2, 0x80, 0xfa, 0x88, 0xc6, 0x50, 0xab, 0x66, 0x90, 0x00, 0xbd, 0xda,
0xde, 0xdc, 0xac, 0xe5, 0xd0, 0x79, 0x28, 0xae, 0xad, 0x77, 0xb6, 0x18, 0x54, 0xbe, 0x51, 0xf8,
0x63, 0xe6, 0x49, 0x64, 0x7c, 0xfe, 0x34, 0xa2, 0xc9, 0x43, 0xb4, 0x12, 0x99, 0xc7, 0x94, 0xc8,
0x6c, 0x88, 0xc8, 0x9c, 0x93, 0x91, 0x39, 0x8f, 0x10, 0x4c, 0xac, 0xb6, 0x5b, 0x9b, 0x34, 0x48,
0x33, 0xd2, 0x8b, 0xc9, 0x68, 0xfd, 0xb0, 0x0a, 0x65, 0x66, 0x9e, 0xad, 0x7d, 0x97, 0x1c, 0x26,
0xfe, 0xda, 0x00, 0x90, 0x1b, 0x16, 0x35, 0xa1, 0xd0, 0x65, 0x22, 0xd4, 0x0d, 0xea, 0x01, 0xcf,
0xa5, 0x5a, 0xdc, 0x12, 0x50, 0xe8, 0x2e, 0x14, 0x82, 0xfd, 0x6e, 0x17, 0x07, 0x22, 0x72, 0x5f,
0x88, 0x3b, 0x61, 0xee, 0x10, 0x2d, 0x01, 0x47, 0x50, 0x5e, 0xda, 0x4e, 0x7f, 0x9f, 0xc6, 0xf1,
0xa3, 0x51, 0x38, 0x9c, 0xf4, 0xb1, 0x7f, 0x66, 0x40, 0x49, 0xd9, 0x16, 0xbf, 0x60, 0x08, 0xb8,
0x0c, 0x45, 0x2a, 0x0c, 0xee, 0xf1, 0x20, 0x30, 0x65, 0xc9, 0x0e, 0xf4, 0x1e, 0x14, 0xc5, 0x4e,
0x12, 0x71, 0xa0, 0x9e, 0x4e, 0x76, 0x7d, 0x68, 0x49, 0x50, 0x29, 0x64, 0x07, 0xce, 0x50, 0x3d,
0x75, 0xc9, 0xed, 0x43, 0x68, 0x56, 0x3d, 0x96, 0x1b, 0xb1, 0x63, 0x79, 0x03, 0xa6, 0x86, 0xbb,
0x87, 0x81, 0xd3, 0xb5, 0xfb, 0x5c, 0x9c, 0xa8, 0x2d, 0xa9, 0x6e, 0x02, 0x52, 0xa9, 0x9e, 0x44,
0x01, 0x92, 0xe8, 0x79, 0x28, 0x3d, 0xb6, 0x83, 0x5d, 0x2e, 0xa4, 0xec, 0xbf, 0x07, 0x15, 0xd2,
0xff, 0xe4, 0xf9, 0x6b, 0x88, 0x2f, 0xb0, 0x16, 0xcd, 0x7f, 0x36, 0xa0, 0x2a, 0xd0, 0x4e, 0x64,
0x20, 0x04, 0xe3, 0xbb, 0x76, 0xb0, 0x4b, 0x95, 0x51, 0xb1, 0xe8, 0x37, 0x7a, 0x1b, 0x6a, 0x5d,
0x36, 0xff, 0xad, 0xd8, 0xbd, 0x6b, 0x9a, 0xf7, 0x47, 0x7b, 0xff, 0x5d, 0xa8, 0x10, 0x94, 0x2d,
0xfd, 0x1e, 0x24, 0xb6, 0xf1, 0x7b, 0x56, 0x79, 0x97, 0xce, 0x39, 0x2e, 0xbe, 0x0d, 0x65, 0xa6,
0x8c, 0xd3, 0x96, 0x5d, 0xea, 0xb5, 0x01, 0xd3, 0x9b, 0xae, 0x3d, 0x0c, 0x76, 0xbd, 0x30, 0xa6,
0xf3, 0x45, 0xf3, 0xef, 0x0d, 0xa8, 0xc9, 0xc1, 0x13, 0xc9, 0xf0, 0x16, 0x4c, 0xfb, 0x78, 0x60,
0x3b, 0xae, 0xe3, 0xee, 0x6c, 0x6d, 0x1f, 0x86, 0x38, 0xe0, 0xd7, 0xd7, 0x6a, 0xd4, 0xfd, 0x90,
0xf4, 0x12, 0x61, 0xb7, 0xfb, 0xde, 0x36, 0x77, 0xd2, 0xf4, 0x1b, 0xbd, 0xa1, 0x7b, 0xe9, 0xa2,
0xd4, 0x9b, 0xe8, 0x97, 0x32, 0xff, 0x34, 0x07, 0xe5, 0x4f, 0xec, 0xb0, 0x2b, 0x56, 0x10, 0x5a,
0x81, 0x6a, 0xe4, 0xc6, 0x69, 0x0f, 0x97, 0x3b, 0x76, 0xe0, 0xa0, 0x38, 0xe2, 0x5e, 0x23, 0x0e,
0x1c, 0x95, 0xae, 0xda, 0x41, 0x49, 0xd9, 0x6e, 0x17, 0xf7, 0x23, 0x52, 0xb9, 0x6c, 0x52, 0x14,
0x50, 0x25, 0xa5, 0x76, 0xa0, 0x6f, 0x43, 0x6d, 0xe8, 0x7b, 0x3b, 0x3e, 0x0e, 0x82, 0x88, 0x18,
0x0b, 0xe1, 0x66, 0x0a, 0xb1, 0x0d, 0x0e, 0x1a, 0x3b, 0xc5, 0xdc, 0x7b, 0x3c, 0x66, 0x4d, 0x0f,
0xf5, 0x31, 0xe9, 0x58, 0xa7, 0xe5, 0x79, 0x8f, 0x79, 0xd6, 0x1f, 0xe5, 0x01, 0x25, 0xa7, 0xf9,
0x55, 0x8f, 0xc9, 0x37, 0xa0, 0x1a, 0x84, 0xb6, 0x9f, 0x58, 0xf3, 0x15, 0xda, 0x1b, 0xad, 0xf8,
0xb7, 0x20, 0x92, 0x6c, 0xcb, 0xf5, 0x42, 0xe7, 0xe5, 0x21, 0xbb, 0xa0, 0x58, 0x55, 0xd1, 0xbd,
0x46, 0x7b, 0xd1, 0x1a, 0x14, 0x5e, 0x3a, 0xfd, 0x10, 0xfb, 0x41, 0x7d, 0x62, 0x2e, 0x7f, 0xb3,
0xba, 0xf0, 0xce, 0x71, 0x86, 0x99, 0xff, 0x88, 0xc2, 0x77, 0x0e, 0x87, 0xea, 0xe9, 0x97, 0x13,
0x51, 0x8f, 0xf1, 0x93, 0xe9, 0x37, 0x22, 0x13, 0xa6, 0x5e, 0x11, 0xa2, 0x5b, 0x4e, 0x8f, 0xc6,
0xe2, 0x68, 0x1f, 0xde, 0xb3, 0x0a, 0x74, 0x60, 0xa5, 0x87, 0xae, 0xc1, 0xd4, 0x4b, 0xdf, 0xde,
0x19, 0x60, 0x37, 0x64, 0xb7, 0x7c, 0x09, 0x13, 0x0d, 0x98, 0xf3, 0x00, 0x52, 0x14, 0x12, 0xf9,
0xd6, 0xd6, 0x37, 0x9e, 0x75, 0x6a, 0x63, 0xa8, 0x0c, 0x53, 0x6b, 0xeb, 0xcb, 0xed, 0xd5, 0x36,
0x89, 0x8d, 0x22, 0xe6, 0xdd, 0x95, 0x9b, 0xae, 0x25, 0x0c, 0xa1, 0xad, 0x09, 0x55, 0x2e, 0x43,
0xbf, 0x74, 0x0b, 0xb9, 0x04, 0x89, 0xbb, 0xe6, 0x55, 0x98, 0x49, 0x5b, 0x1a, 0x02, 0xe0, 0x9e,
0xf9, 0x6f, 0x39, 0xa8, 0xf0, 0x8d, 0x70, 0xa2, 0x9d, 0x7b, 0x51, 0x91, 0x8a, 0x5f, 0x4f, 0x84,
0x92, 0xea, 0x50, 0x60, 0x1b, 0xa4, 0xc7, 0xef, 0xbf, 0xa2, 0x49, 0x9c, 0x33, 0x5b, 0xef, 0xb8,
0xc7, 0xcd, 0x1e, 0xb5, 0x53, 0xdd, 0xe6, 0x44, 0xa6, 0xdb, 0x8c, 0x36, 0x9c, 0x1d, 0xf0, 0x83,
0x55, 0x51, 0x9a, 0xa2, 0x2c, 0x36, 0x15, 0x19, 0xd4, 0x6c, 0x56, 0xc8, 0xb0, 0x19, 0xba, 0x01,
0x93, 0x78, 0x84, 0xdd, 0x30, 0xa8, 0x97, 0x68, 0x20, 0xad, 0x88, 0x0b, 0x55, 0x9b, 0xf4, 0x5a,
0x7c, 0x50, 0x9a, 0xea, 0x43, 0x38, 0x43, 0xef, 0xbb, 0x8f, 0x7c, 0xdb, 0x55, 0xef, 0xec, 0x9d,
0xce, 0x2a, 0x0f, 0x3b, 0xe4, 0x13, 0x55, 0x21, 0xb7, 0xb2, 0xcc, 0xf5, 0x93, 0x5b, 0x59, 0x96,
0xf8, 0xbf, 0x6f, 0x00, 0x52, 0x09, 0x9c, 0xc8, 0x16, 0x31, 0x2e, 0x42, 0x8e, 0xbc, 0x94, 0x63,
0x06, 0x26, 0xb0, 0xef, 0x7b, 0x3e, 0x73, 0x94, 0x16, 0x6b, 0x48, 0x69, 0x6e, 0x73, 0x61, 0x2c,
0x3c, 0xf2, 0xf6, 0x22, 0x0f, 0xc0, 0xc8, 0x1a, 0x49, 0xe1, 0x3b, 0x70, 0x56, 0x03, 0x3f, 0x9d,
0x10, 0xbf, 0x0e, 0xd3, 0x94, 0xea, 0xd2, 0x2e, 0xee, 0xee, 0x0d, 0x3d, 0xc7, 0x4d, 0x48, 0x80,
0xae, 0x11, 0xdf, 0x25, 0xc2, 0x05, 0x99, 0x22, 0x9b, 0x73, 0x39, 0xea, 0xec, 0x74, 0x56, 0xe5,
0x52, 0xdf, 0x86, 0xf3, 0x31, 0x82, 0x62, 0x66, 0xbf, 0x06, 0xa5, 0x6e, 0xd4, 0x19, 0xf0, 0x13,
0xe4, 0x15, 0x5d, 0xdc, 0x38, 0xaa, 0x8a, 0x21, 0x79, 0x7c, 0x1b, 0x2e, 0x24, 0x78, 0x9c, 0x86,
0x3a, 0xee, 0x99, 0x77, 0xe0, 0x1c, 0xa5, 0xfc, 0x04, 0xe3, 0x61, 0xab, 0xef, 0x8c, 0x8e, 0x37,
0xcb, 0x21, 0x9f, 0xaf, 0x82, 0xf1, 0xf5, 0x2e, 0x2b, 0xc9, 0xba, 0xcd, 0x59, 0x77, 0x9c, 0x01,
0xee, 0x78, 0xab, 0xd9, 0xd2, 0x92, 0x40, 0xbe, 0x87, 0x0f, 0x03, 0x7e, 0x7c, 0xa4, 0xdf, 0xd2,
0x7b, 0xfd, 0xad, 0xc1, 0xd5, 0xa9, 0xd2, 0xf9, 0x9a, 0xb7, 0xc6, 0x2c, 0xc0, 0x0e, 0xd9, 0x83,
0xb8, 0x47, 0x06, 0x58, 0x6e, 0x4e, 0xe9, 0x89, 0x04, 0x26, 0x51, 0xa8, 0x1c, 0x17, 0xf8, 0x0a,
0xdf, 0x38, 0xf4, 0x9f, 0x20, 0x71, 0x52, 0x7a, 0x13, 0x4a, 0x74, 0x64, 0x33, 0xb4, 0xc3, 0xfd,
0x20, 0xcb, 0x72, 0x8b, 0xe6, 0x8f, 0x0c, 0xbe, 0xa3, 0x04, 0x9d, 0x13, 0xcd, 0xf9, 0x2e, 0x4c,
0xd2, 0x1b, 0xa2, 0xb8, 0xe9, 0x5c, 0x4c, 0x59, 0xd8, 0x4c, 0x22, 0x8b, 0x03, 0x2a, 0xe7, 0x24,
0x03, 0x26, 0x9f, 0xd2, 0xca, 0x81, 0x22, 0xed, 0xb8, 0xb0, 0x9c, 0x6b, 0x0f, 0x58, 0xfa, 0xb1,
0x68, 0xd1, 0x6f, 0x7a, 0x21, 0xc0, 0xd8, 0x7f, 0x66, 0xad, 0xb2, 0x1b, 0x48, 0xd1, 0x8a, 0xda,
0x44, 0xb1, 0xdd, 0xbe, 0x83, 0xdd, 0x90, 0x8e, 0x8e, 0xd3, 0x51, 0xa5, 0x07, 0xdd, 0x80, 0xa2,
0x13, 0xac, 0x62, 0xdb, 0x77, 0x79, 0x8a, 0x5f, 0x71, 0xcc, 0x72, 0x44, 0xae, 0xb1, 0xef, 0x40,
0x8d, 0x49, 0xd6, 0xea, 0xf5, 0x94, 0xd3, 0x7e, 0xc4, 0xdf, 0x88, 0xf1, 0xd7, 0xe8, 0xe7, 0x8e,
0xa7, 0xff, 0x77, 0x06, 0x9c, 0x51, 0x18, 0x9c, 0xc8, 0x04, 0xef, 0xc2, 0x24, 0xab, 0xbf, 0xf0,
0xa3, 0xe0, 0x8c, 0x8e, 0xc5, 0xd8, 0x58, 0x1c, 0x06, 0xcd, 0x43, 0x81, 0x7d, 0x89, 0x6b, 0x5c,
0x3a, 0xb8, 0x00, 0x92, 0x22, 0xcf, 0xc3, 0x59, 0x3e, 0x86, 0x07, 0x5e, 0xda, 0x9e, 0x1b, 0xd7,
0x3d, 0xc4, 0x0f, 0x0d, 0x98, 0xd1, 0x11, 0x4e, 0x34, 0x4b, 0x45, 0xee, 0xdc, 0x57, 0x92, 0xfb,
0x5b, 0x42, 0xee, 0x67, 0xc3, 0x9e, 0x72, 0xe4, 0x8c, 0xaf, 0x38, 0xd5, 0xba, 0x39, 0xdd, 0xba,
0x92, 0xd6, 0x4f, 0xa2, 0x39, 0x09, 0x62, 0x27, 0x9a, 0xd3, 0xfb, 0xaf, 0x35, 0x27, 0xe5, 0x08,
0x96, 0x98, 0xdc, 0x8a, 0x58, 0x46, 0xab, 0x4e, 0x10, 0x45, 0x9c, 0x77, 0xa0, 0xdc, 0x77, 0x5c,
0x6c, 0xfb, 0xbc, 0x86, 0x64, 0xa8, 0xeb, 0xf1, 0xbe, 0xa5, 0x0d, 0x4a, 0x52, 0xbf, 0x6d, 0x00,
0x52, 0x69, 0xfd, 0x72, 0xac, 0xd5, 0x14, 0x0a, 0xde, 0xf0, 0xbd, 0x81, 0x17, 0x1e, 0xb7, 0xcc,
0xee, 0x99, 0xbf, 0x6b, 0xc0, 0xb9, 0x18, 0xc6, 0x2f, 0x43, 0xf2, 0x7b, 0xe6, 0x65, 0x38, 0xb3,
0x8c, 0xc5, 0x19, 0x2f, 0x91, 0x3b, 0xd8, 0x04, 0xa4, 0x8e, 0x9e, 0xce, 0x29, 0xe6, 0x1b, 0x70,
0xe6, 0xa9, 0x37, 0x22, 0x8e, 0x9c, 0x0c, 0x4b, 0x37, 0xc5, 0x92, 0x59, 0x91, 0xbe, 0xa2, 0xb6,
0x74, 0xbd, 0x9b, 0x80, 0x54, 0xcc, 0xd3, 0x10, 0x67, 0xd1, 0xfc, 0x5f, 0x03, 0xca, 0xad, 0xbe,
0xed, 0x0f, 0x84, 0x28, 0x1f, 0xc2, 0x24, 0xcb, 0xcc, 0xf0, 0x34, 0xeb, 0x9b, 0x3a, 0x3d, 0x15,
0x96, 0x35, 0x5a, 0x2c, 0x8f, 0xc3, 0xb1, 0xc8, 0x54, 0x78, 0x65, 0x79, 0x39, 0x56, 0x69, 0x5e,
0x46, 0xb7, 0x61, 0xc2, 0x26, 0x28, 0x34, 0xbc, 0x56, 0xe3, 0xe9, 0x32, 0x4a, 0x8d, 0x5c, 0x89,
0x2c, 0x06, 0x65, 0x7e, 0x00, 0x25, 0x85, 0x03, 0x2a, 0x40, 0xfe, 0x51, 0x9b, 0x5f, 0x93, 0x5a,
0x4b, 0x9d, 0x95, 0xe7, 0x2c, 0x85, 0x58, 0x05, 0x58, 0x6e, 0x47, 0xed, 0x5c, 0x4a, 0x61, 0xcf,
0xe6, 0x74, 0x78, 0xdc, 0x52, 0x25, 0x34, 0xb2, 0x24, 0xcc, 0xbd, 0x8e, 0x84, 0x92, 0xc5, 0x6f,
0x19, 0x50, 0xe1, 0xaa, 0x39, 0x69, 0x68, 0xa6, 0x94, 0x33, 0x42, 0xb3, 0x32, 0x0d, 0x8b, 0x03,
0x4a, 0x19, 0xfe, 0xc5, 0x80, 0xda, 0xb2, 0xf7, 0xca, 0xdd, 0xf1, 0xed, 0x5e, 0xb4, 0x07, 0x3f,
0x8a, 0x99, 0x73, 0x3e, 0x96, 0xe9, 0x8f, 0xc1, 0xcb, 0x8e, 0x98, 0x59, 0xeb, 0x32, 0x97, 0xc2,
0xe2, 0xbb, 0x68, 0x9a, 0xdf, 0x84, 0xe9, 0x18, 0x12, 0x31, 0xd0, 0xf3, 0xd6, 0xea, 0xca, 0x32,
0x31, 0x08, 0xcd, 0xf7, 0xb6, 0xd7, 0x5a, 0x0f, 0x57, 0xdb, 0xbc, 0x2a, 0xdb, 0x5a, 0x5b, 0x6a,
0xaf, 0x4a, 0x43, 0xdd, 0x17, 0x33, 0xb8, 0x6f, 0xf6, 0xe1, 0x8c, 0x22, 0xd0, 0x49, 0x8b, 0x63,
0xe9, 0xf2, 0x4a, 0x6e, 0xdf, 0x80, 0x4b, 0x11, 0xb7, 0xe7, 0x6c, 0xb0, 0x83, 0x03, 0xf5, 0xb2,
0x36, 0xe2, 0x4c, 0x8b, 0x16, 0xf9, 0x14, 0x98, 0xef, 0x99, 0x75, 0xa8, 0xf0, 0xf3, 0x51, 0xdc,
0x65, 0xfc, 0xf9, 0x38, 0x54, 0xc5, 0xd0, 0xd7, 0x23, 0x3f, 0x3a, 0x0f, 0x93, 0xbd, 0xed, 0x4d,
0xe7, 0x33, 0x51, 0xd1, 0xe5, 0x2d, 0xd2, 0xdf, 0x67, 0x7c, 0xd8, 0x3b, 0x0d, 0xde, 0x42, 0x97,
0xd9, 0x13, 0x8e, 0x15, 0xb7, 0x87, 0x0f, 0xe8, 0x31, 0x6a, 0xdc, 0x92, 0x1d, 0x34, 0x1d, 0xca,
0xdf, 0x73, 0xd0, 0x5b, 0xb2, 0xf2, 0xbe, 0x03, 0x2d, 0x42, 0x8d, 0x7c, 0xb7, 0x86, 0xc3, 0xbe,
0x83, 0x7b, 0x8c, 0x00, 0xb9, 0x20, 0x8f, 0xcb, 0x73, 0x52, 0x02, 0x00, 0x5d, 0x85, 0x49, 0x7a,
0x79, 0x0c, 0xea, 0x53, 0x24, 0x22, 0x4b, 0x50, 0xde, 0x8d, 0xde, 0x86, 0x12, 0x93, 0x78, 0xc5,
0x7d, 0x16, 0x60, 0xfa, 0xda, 0x41, 0xc9, 0xa4, 0xa8, 0x63, 0xfa, 0x09, 0x0d, 0xb2, 0x4e, 0x68,
0xa8, 0x09, 0xd5, 0x20, 0xf4, 0x7c, 0x7b, 0x47, 0x98, 0x91, 0x3e, 0x75, 0x50, 0xd2, 0x7d, 0xb1,
0x61, 0x29, 0xc2, 0xc7, 0xfb, 0x5e, 0x68, 0xeb, 0x4f, 0x1c, 0xde, 0xb3, 0xd4, 0x31, 0xf4, 0x2d,
0xa8, 0xf4, 0xc4, 0x22, 0x59, 0x71, 0x5f, 0x7a, 0xf4, 0x59, 0x43, 0xa2, 0x7a, 0xb7, 0xac, 0x82,
0x48, 0x4a, 0x3a, 0xaa, 0x7a, 0x93, 0xad, 0x68, 0x18, 0xc4, 0xda, 0xd8, 0x25, 0xa1, 0x9d, 0x65,
0x70, 0xa6, 0x2c, 0xd1, 0x44, 0xd7, 0xa1, 0xc2, 0x22, 0xc1, 0x73, 0x6d, 0x35, 0xe8, 0x9d, 0x24,
0x8e, 0xb5, 0xf6, 0xc3, 0xdd, 0x36, 0x45, 0x4a, 0x2c, 0xca, 0x2b, 0x80, 0xc8, 0xe8, 0xb2, 0x13,
0xa4, 0x0e, 0x73, 0xe4, 0xd4, 0x15, 0x7d, 0xdf, 0x5c, 0x83, 0xb3, 0x64, 0x14, 0xbb, 0xa1, 0xd3,
0x55, 0x8e, 0x62, 0xe2, 0xb0, 0x6f, 0xc4, 0x0e, 0xfb, 0x76, 0x10, 0xbc, 0xf2, 0xfc, 0x1e, 0x17,
0x33, 0x6a, 0x4b, 0x6e, 0xff, 0x68, 0x30, 0x69, 0x9e, 0x05, 0xda, 0x41, 0xfd, 0x2b, 0xd2, 0x43,
0xbf, 0x02, 0x05, 0xfe, 0x40, 0x8a, 0xe7, 0x3f, 0xcf, 0xcf, 0xb3, 0x87, 0x59, 0xf3, 0x9c, 0xf0,
0x3a, 0x1b, 0x55, 0x72, 0x74, 0x1c, 0x9e, 0x2c, 0x97, 0x5d, 0x3b, 0xd8, 0xc5, 0xbd, 0x0d, 0x41,
0x5c, 0xcb, 0x0e, 0xdf, 0xb7, 0x62, 0xc3, 0x52, 0xf6, 0xbb, 0x52, 0xf4, 0x47, 0x38, 0x3c, 0x42,
0x74, 0xb5, 0xfe, 0x70, 0x4e, 0xa0, 0xf0, 0xb2, 0xe9, 0xeb, 0x60, 0xfd, 0xd8, 0x80, 0x2b, 0x02,
0x6d, 0x69, 0xd7, 0x76, 0x77, 0xb0, 0x10, 0xe6, 0x17, 0xd5, 0x57, 0x72, 0xd2, 0xf9, 0xd7, 0x9c,
0xf4, 0x13, 0xa8, 0x47, 0x93, 0xa6, 0xb9, 0x28, 0xaf, 0xaf, 0x4e, 0x62, 0x3f, 0x88, 0x9c, 0x24,
0xfd, 0x26, 0x7d, 0xbe, 0xd7, 0x8f, 0xae, 0x81, 0xe4, 0x5b, 0x12, 0x5b, 0x85, 0x8b, 0x82, 0x18,
0x4f, 0x0e, 0xe9, 0xd4, 0x12, 0x73, 0x3a, 0x92, 0x1a, 0xb7, 0x07, 0xa1, 0x71, 0xf4, 0x52, 0x4a,
0x45, 0xd1, 0x4d, 0x48, 0xb9, 0x18, 0x69, 0x5c, 0x66, 0xd9, 0x0e, 0x20, 0x32, 0x2b, 0x27, 0xf6,
0xc4, 0x38, 0x21, 0x99, 0x3a, 0xce, 0x97, 0x00, 0x19, 0x4f, 0x2c, 0x81, 0x6c, 0xae, 0x18, 0x66,
0x23, 0x41, 0x89, 0xda, 0x37, 0xb0, 0x3f, 0x70, 0x82, 0x40, 0x29, 0xc4, 0xa5, 0xa9, 0xeb, 0x4d,
0x18, 0x1f, 0x62, 0x7e, 0x7c, 0x29, 0x2d, 0x20, 0xb1, 0x27, 0x14, 0x64, 0x3a, 0x2e, 0xd9, 0x0c,
0xe0, 0xaa, 0x60, 0xc3, 0x0c, 0x92, 0xca, 0x27, 0x2e, 0xa6, 0x48, 0xfe, 0xe7, 0x32, 0x92, 0xff,
0x79, 0x3d, 0xf9, 0xaf, 0x1d, 0xa9, 0x55, 0x47, 0x75, 0x3a, 0x47, 0xea, 0x0e, 0x33, 0x40, 0xe4,
0xdf, 0x4e, 0x87, 0xea, 0x1f, 0x70, 0x47, 0x75, 0x5a, 0xe1, 0x5c, 0x38, 0xf8, 0x9c, 0xee, 0xe0,
0x4d, 0x28, 0x13, 0x23, 0x59, 0x6a, 0x55, 0x64, 0xdc, 0xd2, 0xfa, 0xa4, 0x33, 0xde, 0x83, 0x19,
0xdd, 0x19, 0x9f, 0x48, 0xa8, 0x19, 0x98, 0x08, 0xbd, 0x3d, 0x2c, 0x62, 0x0a, 0x6b, 0x24, 0xd4,
0x1a, 0x39, 0xea, 0xd3, 0x51, 0xeb, 0x77, 0x25, 0x55, 0xba, 0x01, 0x4f, 0x3a, 0x03, 0xb2, 0x1c,
0xc5, 0xed, 0x9f, 0x35, 0x24, 0xaf, 0x4f, 0xe0, 0x7c, 0xdc, 0xf9, 0x9e, 0xce, 0x24, 0xb6, 0xd8,
0xe6, 0x4c, 0x73, 0xcf, 0xa7, 0xc3, 0xe0, 0x85, 0xf4, 0x93, 0x8a, 0xd3, 0x3d, 0x1d, 0xda, 0xbf,
0x0e, 0x8d, 0x34, 0x1f, 0x7c, 0xaa, 0x7b, 0x31, 0x72, 0xc9, 0xa7, 0x43, 0xf5, 0x87, 0x86, 0x24,
0xab, 0xae, 0x9a, 0x0f, 0xbe, 0x0a, 0x59, 0x11, 0xeb, 0xee, 0x44, 0xcb, 0xa7, 0x19, 0x79, 0xcb,
0x7c, 0xba, 0xb7, 0x94, 0x28, 0x14, 0x50, 0xec, 0x3f, 0xe9, 0xea, 0xbf, 0xce, 0xd5, 0xcb, 0x99,
0xc9, 0xb8, 0x73, 0x52, 0x66, 0x24, 0x3c, 0x47, 0xcc, 0x68, 0x23, 0xb1, 0x55, 0xd4, 0x20, 0x75,
0x3a, 0xa6, 0xfb, 0x0d, 0x19, 0x60, 0x12, 0x71, 0xec, 0x74, 0x38, 0xd8, 0x30, 0x97, 0x1d, 0xc2,
0x4e, 0x85, 0xc5, 0xad, 0x16, 0x14, 0xa3, 0xbb, 0xbf, 0xf2, 0x52, 0xb9, 0x04, 0x85, 0xb5, 0xf5,
0xcd, 0x8d, 0xd6, 0x12, 0xb9, 0xda, 0xce, 0x40, 0x61, 0x69, 0xdd, 0xb2, 0x9e, 0x6d, 0x74, 0xc8,
0xdd, 0x36, 0xfe, 0x70, 0x69, 0xe1, 0x67, 0x79, 0xc8, 0x3d, 0x79, 0x8e, 0x3e, 0x85, 0x09, 0xf6,
0x70, 0xee, 0x88, 0xf7, 0x93, 0x8d, 0xa3, 0xde, 0x06, 0x9a, 0x17, 0x7e, 0xf0, 0xdf, 0x3f, 0xfb,
0xc3, 0xdc, 0x19, 0xb3, 0xdc, 0x1c, 0x2d, 0x36, 0xf7, 0x46, 0x4d, 0x1a, 0x64, 0x1f, 0x18, 0xb7,
0xd0, 0xc7, 0x90, 0xdf, 0xd8, 0x0f, 0x51, 0xe6, 0xbb, 0xca, 0x46, 0xf6, 0x73, 0x41, 0xf3, 0x1c,
0x25, 0x3a, 0x6d, 0x02, 0x27, 0x3a, 0xdc, 0x0f, 0x09, 0xc9, 0xef, 0x41, 0x49, 0x7d, 0xec, 0x77,
0xec, 0x63, 0xcb, 0xc6, 0xf1, 0x0f, 0x09, 0xcd, 0x2b, 0x94, 0xd5, 0x05, 0x13, 0x71, 0x56, 0xec,
0x39, 0xa2, 0x3a, 0x8b, 0xce, 0x81, 0x8b, 0x32, 0x9f, 0x62, 0x36, 0xb2, 0xdf, 0x16, 0x26, 0x66,
0x11, 0x1e, 0xb8, 0x84, 0xe4, 0x77, 0xf9, 0x23, 0xc2, 0x6e, 0x88, 0xae, 0xa6, 0xbc, 0x02, 0x53,
0x5f, 0x37, 0x35, 0xe6, 0xb2, 0x01, 0x38, 0x93, 0xcb, 0x94, 0xc9, 0x79, 0xf3, 0x0c, 0x67, 0xd2,
0x8d, 0x40, 0x1e, 0x18, 0xb7, 0x16, 0xba, 0x30, 0x41, 0xab, 0xe7, 0xe8, 0x85, 0xf8, 0x68, 0xa4,
0xbc, 0x4b, 0xc8, 0x30, 0xb4, 0x56, 0x77, 0x37, 0x67, 0x28, 0xa3, 0xaa, 0x59, 0x24, 0x8c, 0x68,
0xed, 0xfc, 0x81, 0x71, 0xeb, 0xa6, 0x71, 0xc7, 0x58, 0xf8, 0x9b, 0x09, 0x98, 0xa0, 0x55, 0x1a,
0xb4, 0x07, 0x20, 0xab, 0xc4, 0xf1, 0xd9, 0x25, 0x0a, 0xd0, 0xf1, 0xd9, 0x25, 0x0b, 0xcc, 0x66,
0x83, 0x32, 0x9d, 0x31, 0xa7, 0x09, 0x53, 0x5a, 0xfc, 0x69, 0xd2, 0x5a, 0x17, 0xd1, 0xe3, 0x8f,
0x0d, 0x5e, 0xae, 0x62, 0xdb, 0x0c, 0xa5, 0x51, 0xd3, 0x2a, 0xc4, 0xf1, 0xe5, 0x90, 0x52, 0x14,
0x36, 0xef, 0x53, 0x86, 0x4d, 0xb3, 0x26, 0x19, 0xfa, 0x14, 0xe2, 0x81, 0x71, 0xeb, 0x45, 0xdd,
0x3c, 0xcb, 0xb5, 0x1c, 0x1b, 0x41, 0xdf, 0x87, 0xaa, 0x5e, 0xcb, 0x44, 0xd7, 0x52, 0x78, 0xc5,
0x6b, 0xa3, 0x8d, 0xeb, 0x47, 0x03, 0x71, 0x99, 0x66, 0xa9, 0x4c, 0x9c, 0x39, 0xe3, 0xbc, 0x87,
0xf1, 0xd0, 0x26, 0x40, 0xdc, 0x06, 0xe8, 0x4f, 0x0d, 0x5e, 0x8e, 0x96, 0xa5, 0x48, 0x94, 0x46,
0x3d, 0x51, 0xf1, 0x6c, 0xdc, 0x38, 0x06, 0x8a, 0x0b, 0xf1, 0x01, 0x15, 0xe2, 0x7d, 0x73, 0x46,
0x0a, 0x11, 0x3a, 0x03, 0x1c, 0x7a, 0x5c, 0x8a, 0x17, 0x97, 0xcd, 0x0b, 0x9a, 0x72, 0xb4, 0x51,
0x69, 0x2c, 0x56, 0x32, 0x4c, 0x35, 0x96, 0x56, 0x95, 0x4c, 0x35, 0x96, 0x5e, 0x6f, 0x4c, 0x33,
0x16, 0x2f, 0x10, 0xa6, 0x18, 0x2b, 0x1a, 0x59, 0xf8, 0xff, 0x71, 0x28, 0x2c, 0xb1, 0xff, 0x8c,
0x84, 0x3c, 0x28, 0x46, 0x45, 0x34, 0x34, 0x9b, 0x96, 0xa7, 0x97, 0x57, 0xb9, 0xc6, 0xd5, 0xcc,
0x71, 0x2e, 0xd0, 0x1b, 0x54, 0xa0, 0x4b, 0xe6, 0x79, 0xc2, 0x99, 0xff, 0x7f, 0xa7, 0x26, 0xcb,
0xe6, 0x36, 0xed, 0x5e, 0x8f, 0x28, 0xe2, 0x37, 0xa1, 0xac, 0x96, 0xb4, 0xd0, 0x1b, 0xa9, 0xb5,
0x01, 0xb5, 0x3e, 0xd6, 0x30, 0x8f, 0x02, 0xe1, 0x9c, 0xaf, 0x53, 0xce, 0xb3, 0xe6, 0xc5, 0x14,
0xce, 0x3e, 0x05, 0xd5, 0x98, 0xb3, 0xda, 0x53, 0x3a, 0x73, 0xad, 0xc8, 0x95, 0xce, 0x5c, 0x2f,
0x5d, 0x1d, 0xc9, 0x7c, 0x9f, 0x82, 0x12, 0xe6, 0x01, 0x80, 0x2c, 0x0e, 0xa1, 0x54, 0x5d, 0x2a,
0x17, 0xd6, 0xb8, 0x73, 0x48, 0xd6, 0x95, 0x4c, 0x93, 0xb2, 0xe5, 0xeb, 0x2e, 0xc6, 0xb6, 0xef,
0x04, 0x21, 0xdb, 0x98, 0x15, 0xad, 0xb4, 0x83, 0x52, 0xe7, 0xa3, 0x57, 0x8a, 0x1a, 0xd7, 0x8e,
0x84, 0xe1, 0xdc, 0x6f, 0x50, 0xee, 0x57, 0xcd, 0x46, 0x0a, 0xf7, 0x21, 0x83, 0x25, 0x8b, 0xed,
0xf3, 0x02, 0x94, 0x9e, 0xda, 0x8e, 0x1b, 0x62, 0xd7, 0x76, 0xbb, 0x18, 0x6d, 0xc3, 0x04, 0x8d,
0xdd, 0x71, 0x47, 0xac, 0x56, 0x32, 0xe2, 0x8e, 0x58, 0x4b, 0xe5, 0x9b, 0x73, 0x94, 0x71, 0xc3,
0x3c, 0x47, 0x18, 0x0f, 0x24, 0xe9, 0x26, 0x2b, 0x02, 0x18, 0xb7, 0xd0, 0x4b, 0x98, 0xe4, 0x25,
0xfc, 0x18, 0x21, 0x2d, 0xa9, 0xd6, 0xb8, 0x9c, 0x3e, 0x98, 0xb6, 0x96, 0x55, 0x36, 0x01, 0x85,
0x23, 0x7c, 0x46, 0x00, 0xb2, 0x22, 0x15, 0xb7, 0x68, 0xa2, 0x92, 0xd5, 0x98, 0xcb, 0x06, 0x48,
0xd3, 0xa9, 0xca, 0xb3, 0x17, 0xc1, 0x12, 0xbe, 0xdf, 0x81, 0xf1, 0xc7, 0x76, 0xb0, 0x8b, 0x62,
0xb1, 0x57, 0x79, 0x71, 0xdb, 0x68, 0xa4, 0x0d, 0x71, 0x2e, 0x57, 0x29, 0x97, 0x8b, 0xcc, 0x95,
0xa9, 0x5c, 0xe8, 0x9b, 0x52, 0xa6, 0x3f, 0xf6, 0xdc, 0x36, 0xae, 0x3f, 0xed, 0xed, 0x6e, 0x5c,
0x7f, 0xfa, 0x0b, 0xdd, 0x6c, 0xfd, 0x11, 0x2e, 0x7b, 0x23, 0xc2, 0x67, 0x08, 0x53, 0xe2, 0x61,
0x2a, 0x8a, 0x3d, 0xe7, 0x89, 0xbd, 0x66, 0x6d, 0xcc, 0x66, 0x0d, 0x73, 0x6e, 0xd7, 0x28, 0xb7,
0x2b, 0x66, 0x3d, 0x61, 0x2d, 0x0e, 0xf9, 0xc0, 0xb8, 0x75, 0xc7, 0x40, 0xdf, 0x07, 0x90, 0x45,
0xbb, 0xc4, 0x1e, 0x8c, 0x17, 0x02, 0x13, 0x7b, 0x30, 0x51, 0xef, 0x33, 0xe7, 0x29, 0xdf, 0x9b,
0xe6, 0xb5, 0x38, 0xdf, 0xd0, 0xb7, 0xdd, 0xe0, 0x25, 0xf6, 0x6f, 0xb3, 0xbc, 0x7f, 0xb0, 0xeb,
0x0c, 0xc9, 0x94, 0x7d, 0x28, 0x46, 0xb9, 0xe6, 0xb8, 0xbf, 0x8d, 0x57, 0x7f, 0xe2, 0xfe, 0x36,
0x51, 0x8c, 0xd1, 0x1d, 0x8f, 0xb6, 0x5e, 0x04, 0x28, 0xd9, 0x82, 0x7f, 0x59, 0x83, 0x71, 0x72,
0x24, 0x27, 0xc7, 0x13, 0x99, 0xee, 0x89, 0xcf, 0x3e, 0x91, 0xb1, 0x8e, 0xcf, 0x3e, 0x99, 0x29,
0xd2, 0x8f, 0x27, 0xe4, 0xba, 0xd6, 0x64, 0x79, 0x14, 0x32, 0x53, 0x0f, 0x4a, 0x4a, 0x1a, 0x08,
0xa5, 0x10, 0xd3, 0x33, 0xe0, 0xf1, 0x80, 0x97, 0x92, 0x43, 0x32, 0x2f, 0x51, 0x7e, 0xe7, 0x58,
0xc0, 0xa3, 0xfc, 0x7a, 0x0c, 0x82, 0x30, 0xe4, 0xb3, 0xe3, 0x3b, 0x3f, 0x65, 0x76, 0xfa, 0xee,
0x9f, 0xcb, 0x06, 0xc8, 0x9c, 0x9d, 0xdc, 0xfa, 0xaf, 0xa0, 0xac, 0xa6, 0x7e, 0x50, 0x8a, 0xf0,
0xb1, 0x1c, 0x7d, 0x3c, 0x92, 0xa4, 0x65, 0x8e, 0x74, 0xdf, 0x46, 0x59, 0xda, 0x0a, 0x18, 0x61,
0xdc, 0x87, 0x02, 0x4f, 0x01, 0xa5, 0xa9, 0x54, 0x4f, 0xe3, 0xa7, 0xa9, 0x34, 0x96, 0x3f, 0xd2,
0xcf, 0xcf, 0x94, 0x23, 0xb9, 0x8a, 0x8a, 0x68, 0xcd, 0xb9, 0x3d, 0xc2, 0x61, 0x16, 0x37, 0x99,
0xb6, 0xcd, 0xe2, 0xa6, 0x64, 0x08, 0xb2, 0xb8, 0xed, 0xe0, 0x90, 0xfb, 0x03, 0x71, 0xbd, 0x46,
0x19, 0xc4, 0xd4, 0x08, 0x69, 0x1e, 0x05, 0x92, 0x76, 0xbd, 0x91, 0x0c, 0x45, 0x78, 0x3c, 0x00,
0x90, 0xe9, 0xa8, 0xf8, 0x99, 0x35, 0xb5, 0x52, 0x10, 0x3f, 0xb3, 0xa6, 0x67, 0xb4, 0x74, 0x1f,
0x2b, 0xf9, 0xb2, 0xdb, 0x15, 0xe1, 0xfc, 0x85, 0x01, 0x28, 0x99, 0xb0, 0x42, 0xef, 0xa4, 0x53,
0x4f, 0xad, 0x3a, 0x34, 0xde, 0x7d, 0x3d, 0xe0, 0x34, 0x87, 0x2c, 0x45, 0xea, 0x52, 0xe8, 0xe1,
0x2b, 0x22, 0xd4, 0xe7, 0x06, 0x54, 0xb4, 0x24, 0x17, 0x7a, 0x33, 0xc3, 0xa6, 0xb1, 0xd2, 0x43,
0xe3, 0xad, 0x63, 0xe1, 0xd2, 0x0e, 0xf3, 0xca, 0x0a, 0x10, 0xb7, 0x9a, 0xdf, 0x31, 0xa0, 0xaa,
0xe7, 0xc2, 0x50, 0x06, 0xed, 0x44, 0xc5, 0xa2, 0x71, 0xf3, 0x78, 0xc0, 0xa3, 0xcd, 0x23, 0x2f,
0x34, 0x7d, 0x28, 0xf0, 0xa4, 0x59, 0xda, 0xc2, 0xd7, 0x4b, 0x1c, 0x69, 0x0b, 0x3f, 0x96, 0x71,
0x4b, 0x59, 0xf8, 0xbe, 0xd7, 0xc7, 0xca, 0x36, 0xe3, 0xb9, 0xb4, 0x2c, 0x6e, 0x47, 0x6f, 0xb3,
0x58, 0x22, 0x2e, 0x8b, 0x9b, 0xdc, 0x66, 0x22, 0x65, 0x86, 0x32, 0x88, 0x1d, 0xb3, 0xcd, 0xe2,
0x19, 0xb7, 0x94, 0x6d, 0x46, 0x19, 0x2a, 0xdb, 0x4c, 0xa6, 0xb2, 0xd2, 0xb6, 0x59, 0xa2, 0x1a,
0x93, 0xb6, 0xcd, 0x92, 0xd9, 0xb0, 0x14, 0x3b, 0x52, 0xbe, 0xda, 0x36, 0x3b, 0x9b, 0x92, 0xec,
0x42, 0xef, 0x66, 0x28, 0x31, 0xb5, 0xb6, 0xd3, 0xb8, 0xfd, 0x9a, 0xd0, 0x99, 0x6b, 0x9c, 0xa9,
0x5f, 0xac, 0xf1, 0x3f, 0x32, 0x60, 0x26, 0x2d, 0x3f, 0x86, 0x32, 0xf8, 0x64, 0x94, 0x82, 0x1a,
0xf3, 0xaf, 0x0b, 0x7e, 0xb4, 0xb6, 0xa2, 0x55, 0xff, 0x70, 0xe7, 0x8b, 0x56, 0xf3, 0xc5, 0x55,
0xb8, 0x02, 0x93, 0xad, 0xa1, 0xf3, 0x04, 0x1f, 0xa2, 0xb3, 0x53, 0xb9, 0x46, 0x85, 0xd0, 0xf5,
0x7c, 0xe7, 0x33, 0xfa, 0xab, 0x17, 0x73, 0xb9, 0xed, 0x32, 0x40, 0x04, 0x30, 0xf6, 0xef, 0x5f,
0xce, 0x1a, 0xff, 0xf5, 0xe5, 0xac, 0xf1, 0x3f, 0x5f, 0xce, 0x1a, 0x3f, 0xfd, 0xbf, 0xd9, 0xb1,
0x17, 0xd7, 0x76, 0x3c, 0x2a, 0xd6, 0xbc, 0xe3, 0x35, 0xe5, 0x2f, 0x71, 0x2c, 0x36, 0x55, 0x51,
0xb7, 0x27, 0xe9, 0x4f, 0x67, 0x2c, 0xfe, 0x3c, 0x00, 0x00, 0xff, 0xff, 0x08, 0x5e, 0xc8, 0xca,
0xfb, 0x43, 0x00, 0x00,
}
func (m *ResponseHeader) Marshal() (dAtA []byte, err error) {
size := m.Size()
dAtA = make([]byte, size)
n, err := m.MarshalToSizedBuffer(dAtA[:size])
if err != nil {
return nil, err
}
return dAtA[:n], nil
}
func (m *ResponseHeader) MarshalTo(dAtA []byte) (int, error) {
size := m.Size()
return m.MarshalToSizedBuffer(dAtA[:size])
}
func (m *ResponseHeader) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA)
_ = i
var l int
_ = l
if m.XXX_unrecognized != nil {
i -= len(m.XXX_unrecognized)
copy(dAtA[i:], m.XXX_unrecognized)
}
if m.RaftTerm != 0 {
i = encodeVarintRpc(dAtA, i, uint64(m.RaftTerm))
i--
dAtA[i] = 0x20
}
if m.Revision != 0 {
i = encodeVarintRpc(dAtA, i, uint64(m.Revision))
i--
dAtA[i] = 0x18
}
if m.MemberId != 0 {
i = encodeVarintRpc(dAtA, i, uint64(m.MemberId))
i--
dAtA[i] = 0x10
}
if m.ClusterId != 0 {
i = encodeVarintRpc(dAtA, i, uint64(m.ClusterId))
i--
dAtA[i] = 0x8
}
return len(dAtA) - i, nil
}
func (m *RangeRequest) Marshal() (dAtA []byte, err error) {
size := m.Size()
dAtA = make([]byte, size)
n, err := m.MarshalToSizedBuffer(dAtA[:size])
if err != nil {
return nil, err
}
return dAtA[:n], nil
}
func (m *RangeRequest) MarshalTo(dAtA []byte) (int, error) {
size := m.Size()
return m.MarshalToSizedBuffer(dAtA[:size])
}
func (m *RangeRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA)
_ = i
var l int
_ = l
if m.XXX_unrecognized != nil {
i -= len(m.XXX_unrecognized)
copy(dAtA[i:], m.XXX_unrecognized)
}
if m.MaxCreateRevision != 0 {
i = encodeVarintRpc(dAtA, i, uint64(m.MaxCreateRevision))
i--
dAtA[i] = 0x68
}
if m.MinCreateRevision != 0 {
i = encodeVarintRpc(dAtA, i, uint64(m.MinCreateRevision))
i--
dAtA[i] = 0x60
}
if m.MaxModRevision != 0 {
i = encodeVarintRpc(dAtA, i, uint64(m.MaxModRevision))
i--
dAtA[i] = 0x58
}
if m.MinModRevision != 0 {
i = encodeVarintRpc(dAtA, i, uint64(m.MinModRevision))
i--
dAtA[i] = 0x50
}
if m.CountOnly {
i--
if m.CountOnly {
dAtA[i] = 1
} else {
dAtA[i] = 0
}
i--
dAtA[i] = 0x48
}
if m.KeysOnly {
i--
if m.KeysOnly {
dAtA[i] = 1
} else {
dAtA[i] = 0
}
i--
dAtA[i] = 0x40
}
if m.Serializable {
i--
if m.Serializable {
dAtA[i] = 1
} else {
dAtA[i] = 0
}
i--
dAtA[i] = 0x38
}
if m.SortTarget != 0 {
i = encodeVarintRpc(dAtA, i, uint64(m.SortTarget))
i--
dAtA[i] = 0x30
}
if m.SortOrder != 0 {
i = encodeVarintRpc(dAtA, i, uint64(m.SortOrder))
i--
dAtA[i] = 0x28
}
if m.Revision != 0 {
i = encodeVarintRpc(dAtA, i, uint64(m.Revision))
i--
dAtA[i] = 0x20
}
if m.Limit != 0 {
i = encodeVarintRpc(dAtA, i, uint64(m.Limit))
i--
dAtA[i] = 0x18
}
if len(m.RangeEnd) > 0 {
i -= len(m.RangeEnd)
copy(dAtA[i:], m.RangeEnd)
i = encodeVarintRpc(dAtA, i, uint64(len(m.RangeEnd)))
i--
dAtA[i] = 0x12
}
if len(m.Key) > 0 {
i -= len(m.Key)
copy(dAtA[i:], m.Key)
i = encodeVarintRpc(dAtA, i, uint64(len(m.Key)))
i--
dAtA[i] = 0xa
}
return len(dAtA) - i, nil
}
func (m *RangeResponse) Marshal() (dAtA []byte, err error) {
size := m.Size()
dAtA = make([]byte, size)
n, err := m.MarshalToSizedBuffer(dAtA[:size])
if err != nil {
return nil, err
}
return dAtA[:n], nil
}
func (m *RangeResponse) MarshalTo(dAtA []byte) (int, error) {
size := m.Size()
return m.MarshalToSizedBuffer(dAtA[:size])
}
func (m *RangeResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA)
_ = i
var l int
_ = l
if m.XXX_unrecognized != nil {
i -= len(m.XXX_unrecognized)
copy(dAtA[i:], m.XXX_unrecognized)
}
if m.Count != 0 {
i = encodeVarintRpc(dAtA, i, uint64(m.Count))
i--
dAtA[i] = 0x20
}
if m.More {
i--
if m.More {
dAtA[i] = 1
} else {
dAtA[i] = 0
}
i--
dAtA[i] = 0x18
}
if len(m.Kvs) > 0 {
for iNdEx := len(m.Kvs) - 1; iNdEx >= 0; iNdEx-- {
{
size, err := m.Kvs[iNdEx].MarshalToSizedBuffer(dAtA[:i])
if err != nil {
return 0, err
}
i -= size
i = encodeVarintRpc(dAtA, i, uint64(size))
}
i--
dAtA[i] = 0x12
}
}
if m.Header != nil {
{
size, err := m.Header.MarshalToSizedBuffer(dAtA[:i])
if err != nil {
return 0, err
}
i -= size
i = encodeVarintRpc(dAtA, i, uint64(size))
}
i--
dAtA[i] = 0xa
}
return len(dAtA) - i, nil
}
func (m *PutRequest) Marshal() (dAtA []byte, err error) {
size := m.Size()
dAtA = make([]byte, size)
n, err := m.MarshalToSizedBuffer(dAtA[:size])
if err != nil {
return nil, err
}
return dAtA[:n], nil
}
func (m *PutRequest) MarshalTo(dAtA []byte) (int, error) {
size := m.Size()
return m.MarshalToSizedBuffer(dAtA[:size])
}
func (m *PutRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA)
_ = i
var l int
_ = l
if m.XXX_unrecognized != nil {
i -= len(m.XXX_unrecognized)
copy(dAtA[i:], m.XXX_unrecognized)
}
if m.IgnoreLease {
i--
if m.IgnoreLease {
dAtA[i] = 1
} else {
dAtA[i] = 0
}
i--
dAtA[i] = 0x30
}
if m.IgnoreValue {
i--
if m.IgnoreValue {
dAtA[i] = 1
} else {
dAtA[i] = 0
}
i--
dAtA[i] = 0x28
}
if m.PrevKv {
i--
if m.PrevKv {
dAtA[i] = 1
} else {
dAtA[i] = 0
}
i--
dAtA[i] = 0x20
}
if m.Lease != 0 {
i = encodeVarintRpc(dAtA, i, uint64(m.Lease))
i--
dAtA[i] = 0x18
}
if len(m.Value) > 0 {
i -= len(m.Value)
copy(dAtA[i:], m.Value)
i = encodeVarintRpc(dAtA, i, uint64(len(m.Value)))
i--
dAtA[i] = 0x12
}
if len(m.Key) > 0 {
i -= len(m.Key)
copy(dAtA[i:], m.Key)
i = encodeVarintRpc(dAtA, i, uint64(len(m.Key)))
i--
dAtA[i] = 0xa
}
return len(dAtA) - i, nil
}
func (m *PutResponse) Marshal() (dAtA []byte, err error) {
size := m.Size()
dAtA = make([]byte, size)
n, err := m.MarshalToSizedBuffer(dAtA[:size])
if err != nil {
return nil, err
}
return dAtA[:n], nil
}
func (m *PutResponse) MarshalTo(dAtA []byte) (int, error) {
size := m.Size()
return m.MarshalToSizedBuffer(dAtA[:size])
}
func (m *PutResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA)
_ = i
var l int
_ = l
if m.XXX_unrecognized != nil {
i -= len(m.XXX_unrecognized)
copy(dAtA[i:], m.XXX_unrecognized)
}
if m.PrevKv != nil {
{
size, err := m.PrevKv.MarshalToSizedBuffer(dAtA[:i])
if err != nil {
return 0, err
}
i -= size
i = encodeVarintRpc(dAtA, i, uint64(size))
}
i--
dAtA[i] = 0x12
}
if m.Header != nil {
{
size, err := m.Header.MarshalToSizedBuffer(dAtA[:i])
if err != nil {
return 0, err
}
i -= size
i = encodeVarintRpc(dAtA, i, uint64(size))
}
i--
dAtA[i] = 0xa
}
return len(dAtA) - i, nil
}
func (m *DeleteRangeRequest) Marshal() (dAtA []byte, err error) {
size := m.Size()
dAtA = make([]byte, size)
n, err := m.MarshalToSizedBuffer(dAtA[:size])
if err != nil {
return nil, err
}
return dAtA[:n], nil
}
func (m *DeleteRangeRequest) MarshalTo(dAtA []byte) (int, error) {
size := m.Size()
return m.MarshalToSizedBuffer(dAtA[:size])
}
func (m *DeleteRangeRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA)
_ = i
var l int
_ = l
if m.XXX_unrecognized != nil {
i -= len(m.XXX_unrecognized)
copy(dAtA[i:], m.XXX_unrecognized)
}
if m.PrevKv {
i--
if m.PrevKv {
dAtA[i] = 1
} else {
dAtA[i] = 0
}
i--
dAtA[i] = 0x18
}
if len(m.RangeEnd) > 0 {
i -= len(m.RangeEnd)
copy(dAtA[i:], m.RangeEnd)
i = encodeVarintRpc(dAtA, i, uint64(len(m.RangeEnd)))
i--
dAtA[i] = 0x12
}
if len(m.Key) > 0 {
i -= len(m.Key)
copy(dAtA[i:], m.Key)
i = encodeVarintRpc(dAtA, i, uint64(len(m.Key)))
i--
dAtA[i] = 0xa
}
return len(dAtA) - i, nil
}
func (m *DeleteRangeResponse) Marshal() (dAtA []byte, err error) {
size := m.Size()
dAtA = make([]byte, size)
n, err := m.MarshalToSizedBuffer(dAtA[:size])
if err != nil {
return nil, err
}
return dAtA[:n], nil
}
func (m *DeleteRangeResponse) MarshalTo(dAtA []byte) (int, error) {
size := m.Size()
return m.MarshalToSizedBuffer(dAtA[:size])
}
func (m *DeleteRangeResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA)
_ = i
var l int
_ = l
if m.XXX_unrecognized != nil {
i -= len(m.XXX_unrecognized)
copy(dAtA[i:], m.XXX_unrecognized)
}
if len(m.PrevKvs) > 0 {
for iNdEx := len(m.PrevKvs) - 1; iNdEx >= 0; iNdEx-- {
{
size, err := m.PrevKvs[iNdEx].MarshalToSizedBuffer(dAtA[:i])
if err != nil {
return 0, err
}
i -= size
i = encodeVarintRpc(dAtA, i, uint64(size))
}
i--
dAtA[i] = 0x1a
}
}
if m.Deleted != 0 {
i = encodeVarintRpc(dAtA, i, uint64(m.Deleted))
i--
dAtA[i] = 0x10
}
if m.Header != nil {
{
size, err := m.Header.MarshalToSizedBuffer(dAtA[:i])
if err != nil {
return 0, err
}
i -= size
i = encodeVarintRpc(dAtA, i, uint64(size))
}
i--
dAtA[i] = 0xa
}
return len(dAtA) - i, nil
}
func (m *RequestOp) Marshal() (dAtA []byte, err error) {
size := m.Size()
dAtA = make([]byte, size)
n, err := m.MarshalToSizedBuffer(dAtA[:size])
if err != nil {
return nil, err
}
return dAtA[:n], nil
}
func (m *RequestOp) MarshalTo(dAtA []byte) (int, error) {
size := m.Size()
return m.MarshalToSizedBuffer(dAtA[:size])
}
func (m *RequestOp) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA)
_ = i
var l int
_ = l
if m.XXX_unrecognized != nil {
i -= len(m.XXX_unrecognized)
copy(dAtA[i:], m.XXX_unrecognized)
}
if m.Request != nil {
{
size := m.Request.Size()
i -= size
if _, err := m.Request.MarshalTo(dAtA[i:]); err != nil {
return 0, err
}
}
}
return len(dAtA) - i, nil
}
func (m *RequestOp_RequestRange) MarshalTo(dAtA []byte) (int, error) {
size := m.Size()
return m.MarshalToSizedBuffer(dAtA[:size])
}
func (m *RequestOp_RequestRange) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA)
if m.RequestRange != nil {
{
size, err := m.RequestRange.MarshalToSizedBuffer(dAtA[:i])
if err != nil {
return 0, err
}
i -= size
i = encodeVarintRpc(dAtA, i, uint64(size))
}
i--
dAtA[i] = 0xa
}
return len(dAtA) - i, nil
}
func (m *RequestOp_RequestPut) MarshalTo(dAtA []byte) (int, error) {
size := m.Size()
return m.MarshalToSizedBuffer(dAtA[:size])
}
func (m *RequestOp_RequestPut) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA)
if m.RequestPut != nil {
{
size, err := m.RequestPut.MarshalToSizedBuffer(dAtA[:i])
if err != nil {
return 0, err
}
i -= size
i = encodeVarintRpc(dAtA, i, uint64(size))
}
i--
dAtA[i] = 0x12
}
return len(dAtA) - i, nil
}
func (m *RequestOp_RequestDeleteRange) MarshalTo(dAtA []byte) (int, error) {
size := m.Size()
return m.MarshalToSizedBuffer(dAtA[:size])
}
func (m *RequestOp_RequestDeleteRange) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA)
if m.RequestDeleteRange != nil {
{
size, err := m.RequestDeleteRange.MarshalToSizedBuffer(dAtA[:i])
if err != nil {
return 0, err
}
i -= size
i = encodeVarintRpc(dAtA, i, uint64(size))
}
i--
dAtA[i] = 0x1a
}
return len(dAtA) - i, nil
}
func (m *RequestOp_RequestTxn) MarshalTo(dAtA []byte) (int, error) {
size := m.Size()
return m.MarshalToSizedBuffer(dAtA[:size])
}
func (m *RequestOp_RequestTxn) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA)
if m.RequestTxn != nil {
{
size, err := m.RequestTxn.MarshalToSizedBuffer(dAtA[:i])
if err != nil {
return 0, err
}
i -= size
i = encodeVarintRpc(dAtA, i, uint64(size))
}
i--
dAtA[i] = 0x22
}
return len(dAtA) - i, nil
}
func (m *ResponseOp) Marshal() (dAtA []byte, err error) {
size := m.Size()
dAtA = make([]byte, size)
n, err := m.MarshalToSizedBuffer(dAtA[:size])
if err != nil {
return nil, err
}
return dAtA[:n], nil
}
func (m *ResponseOp) MarshalTo(dAtA []byte) (int, error) {
size := m.Size()
return m.MarshalToSizedBuffer(dAtA[:size])
}
func (m *ResponseOp) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA)
_ = i
var l int
_ = l
if m.XXX_unrecognized != nil {
i -= len(m.XXX_unrecognized)
copy(dAtA[i:], m.XXX_unrecognized)
}
if m.Response != nil {
{
size := m.Response.Size()
i -= size
if _, err := m.Response.MarshalTo(dAtA[i:]); err != nil {
return 0, err
}
}
}
return len(dAtA) - i, nil
}
func (m *ResponseOp_ResponseRange) MarshalTo(dAtA []byte) (int, error) {
size := m.Size()
return m.MarshalToSizedBuffer(dAtA[:size])
}
func (m *ResponseOp_ResponseRange) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA)
if m.ResponseRange != nil {
{
size, err := m.ResponseRange.MarshalToSizedBuffer(dAtA[:i])
if err != nil {
return 0, err
}
i -= size
i = encodeVarintRpc(dAtA, i, uint64(size))
}
i--
dAtA[i] = 0xa
}
return len(dAtA) - i, nil
}
func (m *ResponseOp_ResponsePut) MarshalTo(dAtA []byte) (int, error) {
size := m.Size()
return m.MarshalToSizedBuffer(dAtA[:size])
}
func (m *ResponseOp_ResponsePut) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA)
if m.ResponsePut != nil {
{
size, err := m.ResponsePut.MarshalToSizedBuffer(dAtA[:i])
if err != nil {
return 0, err
}
i -= size
i = encodeVarintRpc(dAtA, i, uint64(size))
}
i--
dAtA[i] = 0x12
}
return len(dAtA) - i, nil
}
func (m *ResponseOp_ResponseDeleteRange) MarshalTo(dAtA []byte) (int, error) {
size := m.Size()
return m.MarshalToSizedBuffer(dAtA[:size])
}
func (m *ResponseOp_ResponseDeleteRange) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA)
if m.ResponseDeleteRange != nil {
{
size, err := m.ResponseDeleteRange.MarshalToSizedBuffer(dAtA[:i])
if err != nil {
return 0, err
}
i -= size
i = encodeVarintRpc(dAtA, i, uint64(size))
}
i--
dAtA[i] = 0x1a
}
return len(dAtA) - i, nil
}
func (m *ResponseOp_ResponseTxn) MarshalTo(dAtA []byte) (int, error) {
size := m.Size()
return m.MarshalToSizedBuffer(dAtA[:size])
}
func (m *ResponseOp_ResponseTxn) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA)
if m.ResponseTxn != nil {
{
size, err := m.ResponseTxn.MarshalToSizedBuffer(dAtA[:i])
if err != nil {
return 0, err
}
i -= size
i = encodeVarintRpc(dAtA, i, uint64(size))
}
i--
dAtA[i] = 0x22
}
return len(dAtA) - i, nil
}
func (m *Compare) Marshal() (dAtA []byte, err error) {
size := m.Size()
dAtA = make([]byte, size)
n, err := m.MarshalToSizedBuffer(dAtA[:size])
if err != nil {
return nil, err
}
return dAtA[:n], nil
}
func (m *Compare) MarshalTo(dAtA []byte) (int, error) {
size := m.Size()
return m.MarshalToSizedBuffer(dAtA[:size])
}
func (m *Compare) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA)
_ = i
var l int
_ = l
if m.XXX_unrecognized != nil {
i -= len(m.XXX_unrecognized)
copy(dAtA[i:], m.XXX_unrecognized)
}
if len(m.RangeEnd) > 0 {
i -= len(m.RangeEnd)
copy(dAtA[i:], m.RangeEnd)
i = encodeVarintRpc(dAtA, i, uint64(len(m.RangeEnd)))
i--
dAtA[i] = 0x4
i--
dAtA[i] = 0x82
}
if m.TargetUnion != nil {
{
size := m.TargetUnion.Size()
i -= size
if _, err := m.TargetUnion.MarshalTo(dAtA[i:]); err != nil {
return 0, err
}
}
}
if len(m.Key) > 0 {
i -= len(m.Key)
copy(dAtA[i:], m.Key)
i = encodeVarintRpc(dAtA, i, uint64(len(m.Key)))
i--
dAtA[i] = 0x1a
}
if m.Target != 0 {
i = encodeVarintRpc(dAtA, i, uint64(m.Target))
i--
dAtA[i] = 0x10
}
if m.Result != 0 {
i = encodeVarintRpc(dAtA, i, uint64(m.Result))
i--
dAtA[i] = 0x8
}
return len(dAtA) - i, nil
}
func (m *Compare_Version) MarshalTo(dAtA []byte) (int, error) {
size := m.Size()
return m.MarshalToSizedBuffer(dAtA[:size])
}
func (m *Compare_Version) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA)
i = encodeVarintRpc(dAtA, i, uint64(m.Version))
i--
dAtA[i] = 0x20
return len(dAtA) - i, nil
}
func (m *Compare_CreateRevision) MarshalTo(dAtA []byte) (int, error) {
size := m.Size()
return m.MarshalToSizedBuffer(dAtA[:size])
}
func (m *Compare_CreateRevision) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA)
i = encodeVarintRpc(dAtA, i, uint64(m.CreateRevision))
i--
dAtA[i] = 0x28
return len(dAtA) - i, nil
}
func (m *Compare_ModRevision) MarshalTo(dAtA []byte) (int, error) {
size := m.Size()
return m.MarshalToSizedBuffer(dAtA[:size])
}
func (m *Compare_ModRevision) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA)
i = encodeVarintRpc(dAtA, i, uint64(m.ModRevision))
i--
dAtA[i] = 0x30
return len(dAtA) - i, nil
}
func (m *Compare_Value) MarshalTo(dAtA []byte) (int, error) {
size := m.Size()
return m.MarshalToSizedBuffer(dAtA[:size])
}
func (m *Compare_Value) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA)
if m.Value != nil {
i -= len(m.Value)
copy(dAtA[i:], m.Value)
i = encodeVarintRpc(dAtA, i, uint64(len(m.Value)))
i--
dAtA[i] = 0x3a
}
return len(dAtA) - i, nil
}
func (m *Compare_Lease) MarshalTo(dAtA []byte) (int, error) {
size := m.Size()
return m.MarshalToSizedBuffer(dAtA[:size])
}
func (m *Compare_Lease) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA)
i = encodeVarintRpc(dAtA, i, uint64(m.Lease))
i--
dAtA[i] = 0x40
return len(dAtA) - i, nil
}
func (m *TxnRequest) Marshal() (dAtA []byte, err error) {
size := m.Size()
dAtA = make([]byte, size)
n, err := m.MarshalToSizedBuffer(dAtA[:size])
if err != nil {
return nil, err
}
return dAtA[:n], nil
}
func (m *TxnRequest) MarshalTo(dAtA []byte) (int, error) {
size := m.Size()
return m.MarshalToSizedBuffer(dAtA[:size])
}
func (m *TxnRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA)
_ = i
var l int
_ = l
if m.XXX_unrecognized != nil {
i -= len(m.XXX_unrecognized)
copy(dAtA[i:], m.XXX_unrecognized)
}
if len(m.Failure) > 0 {
for iNdEx := len(m.Failure) - 1; iNdEx >= 0; iNdEx-- {
{
size, err := m.Failure[iNdEx].MarshalToSizedBuffer(dAtA[:i])
if err != nil {
return 0, err
}
i -= size
i = encodeVarintRpc(dAtA, i, uint64(size))
}
i--
dAtA[i] = 0x1a
}
}
if len(m.Success) > 0 {
for iNdEx := len(m.Success) - 1; iNdEx >= 0; iNdEx-- {
{
size, err := m.Success[iNdEx].MarshalToSizedBuffer(dAtA[:i])
if err != nil {
return 0, err
}
i -= size
i = encodeVarintRpc(dAtA, i, uint64(size))
}
i--
dAtA[i] = 0x12
}
}
if len(m.Compare) > 0 {
for iNdEx := len(m.Compare) - 1; iNdEx >= 0; iNdEx-- {
{
size, err := m.Compare[iNdEx].MarshalToSizedBuffer(dAtA[:i])
if err != nil {
return 0, err
}
i -= size
i = encodeVarintRpc(dAtA, i, uint64(size))
}
i--
dAtA[i] = 0xa
}
}
return len(dAtA) - i, nil
}
func (m *TxnResponse) Marshal() (dAtA []byte, err error) {
size := m.Size()
dAtA = make([]byte, size)
n, err := m.MarshalToSizedBuffer(dAtA[:size])
if err != nil {
return nil, err
}
return dAtA[:n], nil
}
func (m *TxnResponse) MarshalTo(dAtA []byte) (int, error) {
size := m.Size()
return m.MarshalToSizedBuffer(dAtA[:size])
}
func (m *TxnResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA)
_ = i
var l int
_ = l
if m.XXX_unrecognized != nil {
i -= len(m.XXX_unrecognized)
copy(dAtA[i:], m.XXX_unrecognized)
}
if len(m.Responses) > 0 {
for iNdEx := len(m.Responses) - 1; iNdEx >= 0; iNdEx-- {
{
size, err := m.Responses[iNdEx].MarshalToSizedBuffer(dAtA[:i])
if err != nil {
return 0, err
}
i -= size
i = encodeVarintRpc(dAtA, i, uint64(size))
}
i--
dAtA[i] = 0x1a
}
}
if m.Succeeded {
i--
if m.Succeeded {
dAtA[i] = 1
} else {
dAtA[i] = 0
}
i--
dAtA[i] = 0x10
}
if m.Header != nil {
{
size, err := m.Header.MarshalToSizedBuffer(dAtA[:i])
if err != nil {
return 0, err
}
i -= size
i = encodeVarintRpc(dAtA, i, uint64(size))
}
i--
dAtA[i] = 0xa
}
return len(dAtA) - i, nil
}
func (m *CompactionRequest) Marshal() (dAtA []byte, err error) {
size := m.Size()
dAtA = make([]byte, size)
n, err := m.MarshalToSizedBuffer(dAtA[:size])
if err != nil {
return nil, err
}
return dAtA[:n], nil
}
func (m *CompactionRequest) MarshalTo(dAtA []byte) (int, error) {
size := m.Size()
return m.MarshalToSizedBuffer(dAtA[:size])
}
func (m *CompactionRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA)
_ = i
var l int
_ = l
if m.XXX_unrecognized != nil {
i -= len(m.XXX_unrecognized)
copy(dAtA[i:], m.XXX_unrecognized)
}
if m.Physical {
i--
if m.Physical {
dAtA[i] = 1
} else {
dAtA[i] = 0
}
i--
dAtA[i] = 0x10
}
if m.Revision != 0 {
i = encodeVarintRpc(dAtA, i, uint64(m.Revision))
i--
dAtA[i] = 0x8
}
return len(dAtA) - i, nil
}
func (m *CompactionResponse) Marshal() (dAtA []byte, err error) {
size := m.Size()
dAtA = make([]byte, size)
n, err := m.MarshalToSizedBuffer(dAtA[:size])
if err != nil {
return nil, err
}
return dAtA[:n], nil
}
func (m *CompactionResponse) MarshalTo(dAtA []byte) (int, error) {
size := m.Size()
return m.MarshalToSizedBuffer(dAtA[:size])
}
func (m *CompactionResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA)
_ = i
var l int
_ = l
if m.XXX_unrecognized != nil {
i -= len(m.XXX_unrecognized)
copy(dAtA[i:], m.XXX_unrecognized)
}
if m.Header != nil {
{
size, err := m.Header.MarshalToSizedBuffer(dAtA[:i])
if err != nil {
return 0, err
}
i -= size
i = encodeVarintRpc(dAtA, i, uint64(size))
}
i--
dAtA[i] = 0xa
}
return len(dAtA) - i, nil
}
func (m *HashRequest) Marshal() (dAtA []byte, err error) {
size := m.Size()
dAtA = make([]byte, size)
n, err := m.MarshalToSizedBuffer(dAtA[:size])
if err != nil {
return nil, err
}
return dAtA[:n], nil
}
func (m *HashRequest) MarshalTo(dAtA []byte) (int, error) {
size := m.Size()
return m.MarshalToSizedBuffer(dAtA[:size])
}
func (m *HashRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA)
_ = i
var l int
_ = l
if m.XXX_unrecognized != nil {
i -= len(m.XXX_unrecognized)
copy(dAtA[i:], m.XXX_unrecognized)
}
return len(dAtA) - i, nil
}
func (m *HashKVRequest) Marshal() (dAtA []byte, err error) {
size := m.Size()
dAtA = make([]byte, size)
n, err := m.MarshalToSizedBuffer(dAtA[:size])
if err != nil {
return nil, err
}
return dAtA[:n], nil
}
func (m *HashKVRequest) MarshalTo(dAtA []byte) (int, error) {
size := m.Size()
return m.MarshalToSizedBuffer(dAtA[:size])
}
func (m *HashKVRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA)
_ = i
var l int
_ = l
if m.XXX_unrecognized != nil {
i -= len(m.XXX_unrecognized)
copy(dAtA[i:], m.XXX_unrecognized)
}
if m.Revision != 0 {
i = encodeVarintRpc(dAtA, i, uint64(m.Revision))
i--
dAtA[i] = 0x8
}
return len(dAtA) - i, nil
}
func (m *HashKVResponse) Marshal() (dAtA []byte, err error) {
size := m.Size()
dAtA = make([]byte, size)
n, err := m.MarshalToSizedBuffer(dAtA[:size])
if err != nil {
return nil, err
}
return dAtA[:n], nil
}
func (m *HashKVResponse) MarshalTo(dAtA []byte) (int, error) {
size := m.Size()
return m.MarshalToSizedBuffer(dAtA[:size])
}
func (m *HashKVResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA)
_ = i
var l int
_ = l
if m.XXX_unrecognized != nil {
i -= len(m.XXX_unrecognized)
copy(dAtA[i:], m.XXX_unrecognized)
}
if m.HashRevision != 0 {
i = encodeVarintRpc(dAtA, i, uint64(m.HashRevision))
i--
dAtA[i] = 0x20
}
if m.CompactRevision != 0 {
i = encodeVarintRpc(dAtA, i, uint64(m.CompactRevision))
i--
dAtA[i] = 0x18
}
if m.Hash != 0 {
i = encodeVarintRpc(dAtA, i, uint64(m.Hash))
i--
dAtA[i] = 0x10
}
if m.Header != nil {
{
size, err := m.Header.MarshalToSizedBuffer(dAtA[:i])
if err != nil {
return 0, err
}
i -= size
i = encodeVarintRpc(dAtA, i, uint64(size))
}
i--
dAtA[i] = 0xa
}
return len(dAtA) - i, nil
}
func (m *HashResponse) Marshal() (dAtA []byte, err error) {
size := m.Size()
dAtA = make([]byte, size)
n, err := m.MarshalToSizedBuffer(dAtA[:size])
if err != nil {
return nil, err
}
return dAtA[:n], nil
}
func (m *HashResponse) MarshalTo(dAtA []byte) (int, error) {
size := m.Size()
return m.MarshalToSizedBuffer(dAtA[:size])
}
func (m *HashResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA)
_ = i
var l int
_ = l
if m.XXX_unrecognized != nil {
i -= len(m.XXX_unrecognized)
copy(dAtA[i:], m.XXX_unrecognized)
}
if m.Hash != 0 {
i = encodeVarintRpc(dAtA, i, uint64(m.Hash))
i--
dAtA[i] = 0x10
}
if m.Header != nil {
{
size, err := m.Header.MarshalToSizedBuffer(dAtA[:i])
if err != nil {
return 0, err
}
i -= size
i = encodeVarintRpc(dAtA, i, uint64(size))
}
i--
dAtA[i] = 0xa
}
return len(dAtA) - i, nil
}
func (m *SnapshotRequest) Marshal() (dAtA []byte, err error) {
size := m.Size()
dAtA = make([]byte, size)
n, err := m.MarshalToSizedBuffer(dAtA[:size])
if err != nil {
return nil, err
}
return dAtA[:n], nil
}
func (m *SnapshotRequest) MarshalTo(dAtA []byte) (int, error) {
size := m.Size()
return m.MarshalToSizedBuffer(dAtA[:size])
}
func (m *SnapshotRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA)
_ = i
var l int
_ = l
if m.XXX_unrecognized != nil {
i -= len(m.XXX_unrecognized)
copy(dAtA[i:], m.XXX_unrecognized)
}
return len(dAtA) - i, nil
}
func (m *SnapshotResponse) Marshal() (dAtA []byte, err error) {
size := m.Size()
dAtA = make([]byte, size)
n, err := m.MarshalToSizedBuffer(dAtA[:size])
if err != nil {
return nil, err
}
return dAtA[:n], nil
}
func (m *SnapshotResponse) MarshalTo(dAtA []byte) (int, error) {
size := m.Size()
return m.MarshalToSizedBuffer(dAtA[:size])
}
func (m *SnapshotResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA)
_ = i
var l int
_ = l
if m.XXX_unrecognized != nil {
i -= len(m.XXX_unrecognized)
copy(dAtA[i:], m.XXX_unrecognized)
}
if len(m.Version) > 0 {
i -= len(m.Version)
copy(dAtA[i:], m.Version)
i = encodeVarintRpc(dAtA, i, uint64(len(m.Version)))
i--
dAtA[i] = 0x22
}
if len(m.Blob) > 0 {
i -= len(m.Blob)
copy(dAtA[i:], m.Blob)
i = encodeVarintRpc(dAtA, i, uint64(len(m.Blob)))
i--
dAtA[i] = 0x1a
}
if m.RemainingBytes != 0 {
i = encodeVarintRpc(dAtA, i, uint64(m.RemainingBytes))
i--
dAtA[i] = 0x10
}
if m.Header != nil {
{
size, err := m.Header.MarshalToSizedBuffer(dAtA[:i])
if err != nil {
return 0, err
}
i -= size
i = encodeVarintRpc(dAtA, i, uint64(size))
}
i--
dAtA[i] = 0xa
}
return len(dAtA) - i, nil
}
func (m *WatchRequest) Marshal() (dAtA []byte, err error) {
size := m.Size()
dAtA = make([]byte, size)
n, err := m.MarshalToSizedBuffer(dAtA[:size])
if err != nil {
return nil, err
}
return dAtA[:n], nil
}
func (m *WatchRequest) MarshalTo(dAtA []byte) (int, error) {
size := m.Size()
return m.MarshalToSizedBuffer(dAtA[:size])
}
func (m *WatchRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA)
_ = i
var l int
_ = l
if m.XXX_unrecognized != nil {
i -= len(m.XXX_unrecognized)
copy(dAtA[i:], m.XXX_unrecognized)
}
if m.RequestUnion != nil {
{
size := m.RequestUnion.Size()
i -= size
if _, err := m.RequestUnion.MarshalTo(dAtA[i:]); err != nil {
return 0, err
}
}
}
return len(dAtA) - i, nil
}
func (m *WatchRequest_CreateRequest) MarshalTo(dAtA []byte) (int, error) {
size := m.Size()
return m.MarshalToSizedBuffer(dAtA[:size])
}
func (m *WatchRequest_CreateRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA)
if m.CreateRequest != nil {
{
size, err := m.CreateRequest.MarshalToSizedBuffer(dAtA[:i])
if err != nil {
return 0, err
}
i -= size
i = encodeVarintRpc(dAtA, i, uint64(size))
}
i--
dAtA[i] = 0xa
}
return len(dAtA) - i, nil
}
func (m *WatchRequest_CancelRequest) MarshalTo(dAtA []byte) (int, error) {
size := m.Size()
return m.MarshalToSizedBuffer(dAtA[:size])
}
func (m *WatchRequest_CancelRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA)
if m.CancelRequest != nil {
{
size, err := m.CancelRequest.MarshalToSizedBuffer(dAtA[:i])
if err != nil {
return 0, err
}
i -= size
i = encodeVarintRpc(dAtA, i, uint64(size))
}
i--
dAtA[i] = 0x12
}
return len(dAtA) - i, nil
}
func (m *WatchRequest_ProgressRequest) MarshalTo(dAtA []byte) (int, error) {
size := m.Size()
return m.MarshalToSizedBuffer(dAtA[:size])
}
func (m *WatchRequest_ProgressRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA)
if m.ProgressRequest != nil {
{
size, err := m.ProgressRequest.MarshalToSizedBuffer(dAtA[:i])
if err != nil {
return 0, err
}
i -= size
i = encodeVarintRpc(dAtA, i, uint64(size))
}
i--
dAtA[i] = 0x1a
}
return len(dAtA) - i, nil
}
func (m *WatchCreateRequest) Marshal() (dAtA []byte, err error) {
size := m.Size()
dAtA = make([]byte, size)
n, err := m.MarshalToSizedBuffer(dAtA[:size])
if err != nil {
return nil, err
}
return dAtA[:n], nil
}
func (m *WatchCreateRequest) MarshalTo(dAtA []byte) (int, error) {
size := m.Size()
return m.MarshalToSizedBuffer(dAtA[:size])
}
func (m *WatchCreateRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA)
_ = i
var l int
_ = l
if m.XXX_unrecognized != nil {
i -= len(m.XXX_unrecognized)
copy(dAtA[i:], m.XXX_unrecognized)
}
if m.Fragment {
i--
if m.Fragment {
dAtA[i] = 1
} else {
dAtA[i] = 0
}
i--
dAtA[i] = 0x40
}
if m.WatchId != 0 {
i = encodeVarintRpc(dAtA, i, uint64(m.WatchId))
i--
dAtA[i] = 0x38
}
if m.PrevKv {
i--
if m.PrevKv {
dAtA[i] = 1
} else {
dAtA[i] = 0
}
i--
dAtA[i] = 0x30
}
if len(m.Filters) > 0 {
dAtA22 := make([]byte, len(m.Filters)*10)
var j21 int
for _, num := range m.Filters {
for num >= 1<<7 {
dAtA22[j21] = uint8(uint64(num)&0x7f | 0x80)
num >>= 7
j21++
}
dAtA22[j21] = uint8(num)
j21++
}
i -= j21
copy(dAtA[i:], dAtA22[:j21])
i = encodeVarintRpc(dAtA, i, uint64(j21))
i--
dAtA[i] = 0x2a
}
if m.ProgressNotify {
i--
if m.ProgressNotify {
dAtA[i] = 1
} else {
dAtA[i] = 0
}
i--
dAtA[i] = 0x20
}
if m.StartRevision != 0 {
i = encodeVarintRpc(dAtA, i, uint64(m.StartRevision))
i--
dAtA[i] = 0x18
}
if len(m.RangeEnd) > 0 {
i -= len(m.RangeEnd)
copy(dAtA[i:], m.RangeEnd)
i = encodeVarintRpc(dAtA, i, uint64(len(m.RangeEnd)))
i--
dAtA[i] = 0x12
}
if len(m.Key) > 0 {
i -= len(m.Key)
copy(dAtA[i:], m.Key)
i = encodeVarintRpc(dAtA, i, uint64(len(m.Key)))
i--
dAtA[i] = 0xa
}
return len(dAtA) - i, nil
}
func (m *WatchCancelRequest) Marshal() (dAtA []byte, err error) {
size := m.Size()
dAtA = make([]byte, size)
n, err := m.MarshalToSizedBuffer(dAtA[:size])
if err != nil {
return nil, err
}
return dAtA[:n], nil
}
func (m *WatchCancelRequest) MarshalTo(dAtA []byte) (int, error) {
size := m.Size()
return m.MarshalToSizedBuffer(dAtA[:size])
}
func (m *WatchCancelRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA)
_ = i
var l int
_ = l
if m.XXX_unrecognized != nil {
i -= len(m.XXX_unrecognized)
copy(dAtA[i:], m.XXX_unrecognized)
}
if m.WatchId != 0 {
i = encodeVarintRpc(dAtA, i, uint64(m.WatchId))
i--
dAtA[i] = 0x8
}
return len(dAtA) - i, nil
}
func (m *WatchProgressRequest) Marshal() (dAtA []byte, err error) {
size := m.Size()
dAtA = make([]byte, size)
n, err := m.MarshalToSizedBuffer(dAtA[:size])
if err != nil {
return nil, err
}
return dAtA[:n], nil
}
func (m *WatchProgressRequest) MarshalTo(dAtA []byte) (int, error) {
size := m.Size()
return m.MarshalToSizedBuffer(dAtA[:size])
}
func (m *WatchProgressRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA)
_ = i
var l int
_ = l
if m.XXX_unrecognized != nil {
i -= len(m.XXX_unrecognized)
copy(dAtA[i:], m.XXX_unrecognized)
}
return len(dAtA) - i, nil
}
func (m *WatchResponse) Marshal() (dAtA []byte, err error) {
size := m.Size()
dAtA = make([]byte, size)
n, err := m.MarshalToSizedBuffer(dAtA[:size])
if err != nil {
return nil, err
}
return dAtA[:n], nil
}
func (m *WatchResponse) MarshalTo(dAtA []byte) (int, error) {
size := m.Size()
return m.MarshalToSizedBuffer(dAtA[:size])
}
func (m *WatchResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA)
_ = i
var l int
_ = l
if m.XXX_unrecognized != nil {
i -= len(m.XXX_unrecognized)
copy(dAtA[i:], m.XXX_unrecognized)
}
if len(m.Events) > 0 {
for iNdEx := len(m.Events) - 1; iNdEx >= 0; iNdEx-- {
{
size, err := m.Events[iNdEx].MarshalToSizedBuffer(dAtA[:i])
if err != nil {
return 0, err
}
i -= size
i = encodeVarintRpc(dAtA, i, uint64(size))
}
i--
dAtA[i] = 0x5a
}
}
if m.Fragment {
i--
if m.Fragment {
dAtA[i] = 1
} else {
dAtA[i] = 0
}
i--
dAtA[i] = 0x38
}
if len(m.CancelReason) > 0 {
i -= len(m.CancelReason)
copy(dAtA[i:], m.CancelReason)
i = encodeVarintRpc(dAtA, i, uint64(len(m.CancelReason)))
i--
dAtA[i] = 0x32
}
if m.CompactRevision != 0 {
i = encodeVarintRpc(dAtA, i, uint64(m.CompactRevision))
i--
dAtA[i] = 0x28
}
if m.Canceled {
i--
if m.Canceled {
dAtA[i] = 1
} else {
dAtA[i] = 0
}
i--
dAtA[i] = 0x20
}
if m.Created {
i--
if m.Created {
dAtA[i] = 1
} else {
dAtA[i] = 0
}
i--
dAtA[i] = 0x18
}
if m.WatchId != 0 {
i = encodeVarintRpc(dAtA, i, uint64(m.WatchId))
i--
dAtA[i] = 0x10
}
if m.Header != nil {
{
size, err := m.Header.MarshalToSizedBuffer(dAtA[:i])
if err != nil {
return 0, err
}
i -= size
i = encodeVarintRpc(dAtA, i, uint64(size))
}
i--
dAtA[i] = 0xa
}
return len(dAtA) - i, nil
}
func (m *LeaseGrantRequest) Marshal() (dAtA []byte, err error) {
size := m.Size()
dAtA = make([]byte, size)
n, err := m.MarshalToSizedBuffer(dAtA[:size])
if err != nil {
return nil, err
}
return dAtA[:n], nil
}
func (m *LeaseGrantRequest) MarshalTo(dAtA []byte) (int, error) {
size := m.Size()
return m.MarshalToSizedBuffer(dAtA[:size])
}
func (m *LeaseGrantRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA)
_ = i
var l int
_ = l
if m.XXX_unrecognized != nil {
i -= len(m.XXX_unrecognized)
copy(dAtA[i:], m.XXX_unrecognized)
}
if m.ID != 0 {
i = encodeVarintRpc(dAtA, i, uint64(m.ID))
i--
dAtA[i] = 0x10
}
if m.TTL != 0 {
i = encodeVarintRpc(dAtA, i, uint64(m.TTL))
i--
dAtA[i] = 0x8
}
return len(dAtA) - i, nil
}
func (m *LeaseGrantResponse) Marshal() (dAtA []byte, err error) {
size := m.Size()
dAtA = make([]byte, size)
n, err := m.MarshalToSizedBuffer(dAtA[:size])
if err != nil {
return nil, err
}
return dAtA[:n], nil
}
func (m *LeaseGrantResponse) MarshalTo(dAtA []byte) (int, error) {
size := m.Size()
return m.MarshalToSizedBuffer(dAtA[:size])
}
func (m *LeaseGrantResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA)
_ = i
var l int
_ = l
if m.XXX_unrecognized != nil {
i -= len(m.XXX_unrecognized)
copy(dAtA[i:], m.XXX_unrecognized)
}
if len(m.Error) > 0 {
i -= len(m.Error)
copy(dAtA[i:], m.Error)
i = encodeVarintRpc(dAtA, i, uint64(len(m.Error)))
i--
dAtA[i] = 0x22
}
if m.TTL != 0 {
i = encodeVarintRpc(dAtA, i, uint64(m.TTL))
i--
dAtA[i] = 0x18
}
if m.ID != 0 {
i = encodeVarintRpc(dAtA, i, uint64(m.ID))
i--
dAtA[i] = 0x10
}
if m.Header != nil {
{
size, err := m.Header.MarshalToSizedBuffer(dAtA[:i])
if err != nil {
return 0, err
}
i -= size
i = encodeVarintRpc(dAtA, i, uint64(size))
}
i--
dAtA[i] = 0xa
}
return len(dAtA) - i, nil
}
func (m *LeaseRevokeRequest) Marshal() (dAtA []byte, err error) {
size := m.Size()
dAtA = make([]byte, size)
n, err := m.MarshalToSizedBuffer(dAtA[:size])
if err != nil {
return nil, err
}
return dAtA[:n], nil
}
func (m *LeaseRevokeRequest) MarshalTo(dAtA []byte) (int, error) {
size := m.Size()
return m.MarshalToSizedBuffer(dAtA[:size])
}
func (m *LeaseRevokeRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA)
_ = i
var l int
_ = l
if m.XXX_unrecognized != nil {
i -= len(m.XXX_unrecognized)
copy(dAtA[i:], m.XXX_unrecognized)
}
if m.ID != 0 {
i = encodeVarintRpc(dAtA, i, uint64(m.ID))
i--
dAtA[i] = 0x8
}
return len(dAtA) - i, nil
}
func (m *LeaseRevokeResponse) Marshal() (dAtA []byte, err error) {
size := m.Size()
dAtA = make([]byte, size)
n, err := m.MarshalToSizedBuffer(dAtA[:size])
if err != nil {
return nil, err
}
return dAtA[:n], nil
}
func (m *LeaseRevokeResponse) MarshalTo(dAtA []byte) (int, error) {
size := m.Size()
return m.MarshalToSizedBuffer(dAtA[:size])
}
func (m *LeaseRevokeResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA)
_ = i
var l int
_ = l
if m.XXX_unrecognized != nil {
i -= len(m.XXX_unrecognized)
copy(dAtA[i:], m.XXX_unrecognized)
}
if m.Header != nil {
{
size, err := m.Header.MarshalToSizedBuffer(dAtA[:i])
if err != nil {
return 0, err
}
i -= size
i = encodeVarintRpc(dAtA, i, uint64(size))
}
i--
dAtA[i] = 0xa
}
return len(dAtA) - i, nil
}
func (m *LeaseCheckpoint) Marshal() (dAtA []byte, err error) {
size := m.Size()
dAtA = make([]byte, size)
n, err := m.MarshalToSizedBuffer(dAtA[:size])
if err != nil {
return nil, err
}
return dAtA[:n], nil
}
func (m *LeaseCheckpoint) MarshalTo(dAtA []byte) (int, error) {
size := m.Size()
return m.MarshalToSizedBuffer(dAtA[:size])
}
func (m *LeaseCheckpoint) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA)
_ = i
var l int
_ = l
if m.XXX_unrecognized != nil {
i -= len(m.XXX_unrecognized)
copy(dAtA[i:], m.XXX_unrecognized)
}
if m.Remaining_TTL != 0 {
i = encodeVarintRpc(dAtA, i, uint64(m.Remaining_TTL))
i--
dAtA[i] = 0x10
}
if m.ID != 0 {
i = encodeVarintRpc(dAtA, i, uint64(m.ID))
i--
dAtA[i] = 0x8
}
return len(dAtA) - i, nil
}
func (m *LeaseCheckpointRequest) Marshal() (dAtA []byte, err error) {
size := m.Size()
dAtA = make([]byte, size)
n, err := m.MarshalToSizedBuffer(dAtA[:size])
if err != nil {
return nil, err
}
return dAtA[:n], nil
}
func (m *LeaseCheckpointRequest) MarshalTo(dAtA []byte) (int, error) {
size := m.Size()
return m.MarshalToSizedBuffer(dAtA[:size])
}
func (m *LeaseCheckpointRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA)
_ = i
var l int
_ = l
if m.XXX_unrecognized != nil {
i -= len(m.XXX_unrecognized)
copy(dAtA[i:], m.XXX_unrecognized)
}
if len(m.Checkpoints) > 0 {
for iNdEx := len(m.Checkpoints) - 1; iNdEx >= 0; iNdEx-- {
{
size, err := m.Checkpoints[iNdEx].MarshalToSizedBuffer(dAtA[:i])
if err != nil {
return 0, err
}
i -= size
i = encodeVarintRpc(dAtA, i, uint64(size))
}
i--
dAtA[i] = 0xa
}
}
return len(dAtA) - i, nil
}
func (m *LeaseCheckpointResponse) Marshal() (dAtA []byte, err error) {
size := m.Size()
dAtA = make([]byte, size)
n, err := m.MarshalToSizedBuffer(dAtA[:size])
if err != nil {
return nil, err
}
return dAtA[:n], nil
}
func (m *LeaseCheckpointResponse) MarshalTo(dAtA []byte) (int, error) {
size := m.Size()
return m.MarshalToSizedBuffer(dAtA[:size])
}
func (m *LeaseCheckpointResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA)
_ = i
var l int
_ = l
if m.XXX_unrecognized != nil {
i -= len(m.XXX_unrecognized)
copy(dAtA[i:], m.XXX_unrecognized)
}
if m.Header != nil {
{
size, err := m.Header.MarshalToSizedBuffer(dAtA[:i])
if err != nil {
return 0, err
}
i -= size
i = encodeVarintRpc(dAtA, i, uint64(size))
}
i--
dAtA[i] = 0xa
}
return len(dAtA) - i, nil
}
func (m *LeaseKeepAliveRequest) Marshal() (dAtA []byte, err error) {
size := m.Size()
dAtA = make([]byte, size)
n, err := m.MarshalToSizedBuffer(dAtA[:size])
if err != nil {
return nil, err
}
return dAtA[:n], nil
}
func (m *LeaseKeepAliveRequest) MarshalTo(dAtA []byte) (int, error) {
size := m.Size()
return m.MarshalToSizedBuffer(dAtA[:size])
}
func (m *LeaseKeepAliveRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA)
_ = i
var l int
_ = l
if m.XXX_unrecognized != nil {
i -= len(m.XXX_unrecognized)
copy(dAtA[i:], m.XXX_unrecognized)
}
if m.ID != 0 {
i = encodeVarintRpc(dAtA, i, uint64(m.ID))
i--
dAtA[i] = 0x8
}
return len(dAtA) - i, nil
}
func (m *LeaseKeepAliveResponse) Marshal() (dAtA []byte, err error) {
size := m.Size()
dAtA = make([]byte, size)
n, err := m.MarshalToSizedBuffer(dAtA[:size])
if err != nil {
return nil, err
}
return dAtA[:n], nil
}
func (m *LeaseKeepAliveResponse) MarshalTo(dAtA []byte) (int, error) {
size := m.Size()
return m.MarshalToSizedBuffer(dAtA[:size])
}
func (m *LeaseKeepAliveResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA)
_ = i
var l int
_ = l
if m.XXX_unrecognized != nil {
i -= len(m.XXX_unrecognized)
copy(dAtA[i:], m.XXX_unrecognized)
}
if m.TTL != 0 {
i = encodeVarintRpc(dAtA, i, uint64(m.TTL))
i--
dAtA[i] = 0x18
}
if m.ID != 0 {
i = encodeVarintRpc(dAtA, i, uint64(m.ID))
i--
dAtA[i] = 0x10
}
if m.Header != nil {
{
size, err := m.Header.MarshalToSizedBuffer(dAtA[:i])
if err != nil {
return 0, err
}
i -= size
i = encodeVarintRpc(dAtA, i, uint64(size))
}
i--
dAtA[i] = 0xa
}
return len(dAtA) - i, nil
}
func (m *LeaseTimeToLiveRequest) Marshal() (dAtA []byte, err error) {
size := m.Size()
dAtA = make([]byte, size)
n, err := m.MarshalToSizedBuffer(dAtA[:size])
if err != nil {
return nil, err
}
return dAtA[:n], nil
}
func (m *LeaseTimeToLiveRequest) MarshalTo(dAtA []byte) (int, error) {
size := m.Size()
return m.MarshalToSizedBuffer(dAtA[:size])
}
func (m *LeaseTimeToLiveRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA)
_ = i
var l int
_ = l
if m.XXX_unrecognized != nil {
i -= len(m.XXX_unrecognized)
copy(dAtA[i:], m.XXX_unrecognized)
}
if m.Keys {
i--
if m.Keys {
dAtA[i] = 1
} else {
dAtA[i] = 0
}
i--
dAtA[i] = 0x10
}
if m.ID != 0 {
i = encodeVarintRpc(dAtA, i, uint64(m.ID))
i--
dAtA[i] = 0x8
}
return len(dAtA) - i, nil
}
func (m *LeaseTimeToLiveResponse) Marshal() (dAtA []byte, err error) {
size := m.Size()
dAtA = make([]byte, size)
n, err := m.MarshalToSizedBuffer(dAtA[:size])
if err != nil {
return nil, err
}
return dAtA[:n], nil
}
func (m *LeaseTimeToLiveResponse) MarshalTo(dAtA []byte) (int, error) {
size := m.Size()
return m.MarshalToSizedBuffer(dAtA[:size])
}
func (m *LeaseTimeToLiveResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA)
_ = i
var l int
_ = l
if m.XXX_unrecognized != nil {
i -= len(m.XXX_unrecognized)
copy(dAtA[i:], m.XXX_unrecognized)
}
if len(m.Keys) > 0 {
for iNdEx := len(m.Keys) - 1; iNdEx >= 0; iNdEx-- {
i -= len(m.Keys[iNdEx])
copy(dAtA[i:], m.Keys[iNdEx])
i = encodeVarintRpc(dAtA, i, uint64(len(m.Keys[iNdEx])))
i--
dAtA[i] = 0x2a
}
}
if m.GrantedTTL != 0 {
i = encodeVarintRpc(dAtA, i, uint64(m.GrantedTTL))
i--
dAtA[i] = 0x20
}
if m.TTL != 0 {
i = encodeVarintRpc(dAtA, i, uint64(m.TTL))
i--
dAtA[i] = 0x18
}
if m.ID != 0 {
i = encodeVarintRpc(dAtA, i, uint64(m.ID))
i--
dAtA[i] = 0x10
}
if m.Header != nil {
{
size, err := m.Header.MarshalToSizedBuffer(dAtA[:i])
if err != nil {
return 0, err
}
i -= size
i = encodeVarintRpc(dAtA, i, uint64(size))
}
i--
dAtA[i] = 0xa
}
return len(dAtA) - i, nil
}
func (m *LeaseLeasesRequest) Marshal() (dAtA []byte, err error) {
size := m.Size()
dAtA = make([]byte, size)
n, err := m.MarshalToSizedBuffer(dAtA[:size])
if err != nil {
return nil, err
}
return dAtA[:n], nil
}
func (m *LeaseLeasesRequest) MarshalTo(dAtA []byte) (int, error) {
size := m.Size()
return m.MarshalToSizedBuffer(dAtA[:size])
}
func (m *LeaseLeasesRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA)
_ = i
var l int
_ = l
if m.XXX_unrecognized != nil {
i -= len(m.XXX_unrecognized)
copy(dAtA[i:], m.XXX_unrecognized)
}
return len(dAtA) - i, nil
}
func (m *LeaseStatus) Marshal() (dAtA []byte, err error) {
size := m.Size()
dAtA = make([]byte, size)
n, err := m.MarshalToSizedBuffer(dAtA[:size])
if err != nil {
return nil, err
}
return dAtA[:n], nil
}
func (m *LeaseStatus) MarshalTo(dAtA []byte) (int, error) {
size := m.Size()
return m.MarshalToSizedBuffer(dAtA[:size])
}
func (m *LeaseStatus) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA)
_ = i
var l int
_ = l
if m.XXX_unrecognized != nil {
i -= len(m.XXX_unrecognized)
copy(dAtA[i:], m.XXX_unrecognized)
}
if m.ID != 0 {
i = encodeVarintRpc(dAtA, i, uint64(m.ID))
i--
dAtA[i] = 0x8
}
return len(dAtA) - i, nil
}
func (m *LeaseLeasesResponse) Marshal() (dAtA []byte, err error) {
size := m.Size()
dAtA = make([]byte, size)
n, err := m.MarshalToSizedBuffer(dAtA[:size])
if err != nil {
return nil, err
}
return dAtA[:n], nil
}
func (m *LeaseLeasesResponse) MarshalTo(dAtA []byte) (int, error) {
size := m.Size()
return m.MarshalToSizedBuffer(dAtA[:size])
}
func (m *LeaseLeasesResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA)
_ = i
var l int
_ = l
if m.XXX_unrecognized != nil {
i -= len(m.XXX_unrecognized)
copy(dAtA[i:], m.XXX_unrecognized)
}
if len(m.Leases) > 0 {
for iNdEx := len(m.Leases) - 1; iNdEx >= 0; iNdEx-- {
{
size, err := m.Leases[iNdEx].MarshalToSizedBuffer(dAtA[:i])
if err != nil {
return 0, err
}
i -= size
i = encodeVarintRpc(dAtA, i, uint64(size))
}
i--
dAtA[i] = 0x12
}
}
if m.Header != nil {
{
size, err := m.Header.MarshalToSizedBuffer(dAtA[:i])
if err != nil {
return 0, err
}
i -= size
i = encodeVarintRpc(dAtA, i, uint64(size))
}
i--
dAtA[i] = 0xa
}
return len(dAtA) - i, nil
}
func (m *Member) Marshal() (dAtA []byte, err error) {
size := m.Size()
dAtA = make([]byte, size)
n, err := m.MarshalToSizedBuffer(dAtA[:size])
if err != nil {
return nil, err
}
return dAtA[:n], nil
}
func (m *Member) MarshalTo(dAtA []byte) (int, error) {
size := m.Size()
return m.MarshalToSizedBuffer(dAtA[:size])
}
func (m *Member) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA)
_ = i
var l int
_ = l
if m.XXX_unrecognized != nil {
i -= len(m.XXX_unrecognized)
copy(dAtA[i:], m.XXX_unrecognized)
}
if m.IsLearner {
i--
if m.IsLearner {
dAtA[i] = 1
} else {
dAtA[i] = 0
}
i--
dAtA[i] = 0x28
}
if len(m.ClientURLs) > 0 {
for iNdEx := len(m.ClientURLs) - 1; iNdEx >= 0; iNdEx-- {
i -= len(m.ClientURLs[iNdEx])
copy(dAtA[i:], m.ClientURLs[iNdEx])
i = encodeVarintRpc(dAtA, i, uint64(len(m.ClientURLs[iNdEx])))
i--
dAtA[i] = 0x22
}
}
if len(m.PeerURLs) > 0 {
for iNdEx := len(m.PeerURLs) - 1; iNdEx >= 0; iNdEx-- {
i -= len(m.PeerURLs[iNdEx])
copy(dAtA[i:], m.PeerURLs[iNdEx])
i = encodeVarintRpc(dAtA, i, uint64(len(m.PeerURLs[iNdEx])))
i--
dAtA[i] = 0x1a
}
}
if len(m.Name) > 0 {
i -= len(m.Name)
copy(dAtA[i:], m.Name)
i = encodeVarintRpc(dAtA, i, uint64(len(m.Name)))
i--
dAtA[i] = 0x12
}
if m.ID != 0 {
i = encodeVarintRpc(dAtA, i, uint64(m.ID))
i--
dAtA[i] = 0x8
}
return len(dAtA) - i, nil
}
func (m *MemberAddRequest) Marshal() (dAtA []byte, err error) {
size := m.Size()
dAtA = make([]byte, size)
n, err := m.MarshalToSizedBuffer(dAtA[:size])
if err != nil {
return nil, err
}
return dAtA[:n], nil
}
func (m *MemberAddRequest) MarshalTo(dAtA []byte) (int, error) {
size := m.Size()
return m.MarshalToSizedBuffer(dAtA[:size])
}
func (m *MemberAddRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA)
_ = i
var l int
_ = l
if m.XXX_unrecognized != nil {
i -= len(m.XXX_unrecognized)
copy(dAtA[i:], m.XXX_unrecognized)
}
if m.IsLearner {
i--
if m.IsLearner {
dAtA[i] = 1
} else {
dAtA[i] = 0
}
i--
dAtA[i] = 0x10
}
if len(m.PeerURLs) > 0 {
for iNdEx := len(m.PeerURLs) - 1; iNdEx >= 0; iNdEx-- {
i -= len(m.PeerURLs[iNdEx])
copy(dAtA[i:], m.PeerURLs[iNdEx])
i = encodeVarintRpc(dAtA, i, uint64(len(m.PeerURLs[iNdEx])))
i--
dAtA[i] = 0xa
}
}
return len(dAtA) - i, nil
}
func (m *MemberAddResponse) Marshal() (dAtA []byte, err error) {
size := m.Size()
dAtA = make([]byte, size)
n, err := m.MarshalToSizedBuffer(dAtA[:size])
if err != nil {
return nil, err
}
return dAtA[:n], nil
}
func (m *MemberAddResponse) MarshalTo(dAtA []byte) (int, error) {
size := m.Size()
return m.MarshalToSizedBuffer(dAtA[:size])
}
func (m *MemberAddResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA)
_ = i
var l int
_ = l
if m.XXX_unrecognized != nil {
i -= len(m.XXX_unrecognized)
copy(dAtA[i:], m.XXX_unrecognized)
}
if len(m.Members) > 0 {
for iNdEx := len(m.Members) - 1; iNdEx >= 0; iNdEx-- {
{
size, err := m.Members[iNdEx].MarshalToSizedBuffer(dAtA[:i])
if err != nil {
return 0, err
}
i -= size
i = encodeVarintRpc(dAtA, i, uint64(size))
}
i--
dAtA[i] = 0x1a
}
}
if m.Member != nil {
{
size, err := m.Member.MarshalToSizedBuffer(dAtA[:i])
if err != nil {
return 0, err
}
i -= size
i = encodeVarintRpc(dAtA, i, uint64(size))
}
i--
dAtA[i] = 0x12
}
if m.Header != nil {
{
size, err := m.Header.MarshalToSizedBuffer(dAtA[:i])
if err != nil {
return 0, err
}
i -= size
i = encodeVarintRpc(dAtA, i, uint64(size))
}
i--
dAtA[i] = 0xa
}
return len(dAtA) - i, nil
}
func (m *MemberRemoveRequest) Marshal() (dAtA []byte, err error) {
size := m.Size()
dAtA = make([]byte, size)
n, err := m.MarshalToSizedBuffer(dAtA[:size])
if err != nil {
return nil, err
}
return dAtA[:n], nil
}
func (m *MemberRemoveRequest) MarshalTo(dAtA []byte) (int, error) {
size := m.Size()
return m.MarshalToSizedBuffer(dAtA[:size])
}
func (m *MemberRemoveRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA)
_ = i
var l int
_ = l
if m.XXX_unrecognized != nil {
i -= len(m.XXX_unrecognized)
copy(dAtA[i:], m.XXX_unrecognized)
}
if m.ID != 0 {
i = encodeVarintRpc(dAtA, i, uint64(m.ID))
i--
dAtA[i] = 0x8
}
return len(dAtA) - i, nil
}
func (m *MemberRemoveResponse) Marshal() (dAtA []byte, err error) {
size := m.Size()
dAtA = make([]byte, size)
n, err := m.MarshalToSizedBuffer(dAtA[:size])
if err != nil {
return nil, err
}
return dAtA[:n], nil
}
func (m *MemberRemoveResponse) MarshalTo(dAtA []byte) (int, error) {
size := m.Size()
return m.MarshalToSizedBuffer(dAtA[:size])
}
func (m *MemberRemoveResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA)
_ = i
var l int
_ = l
if m.XXX_unrecognized != nil {
i -= len(m.XXX_unrecognized)
copy(dAtA[i:], m.XXX_unrecognized)
}
if len(m.Members) > 0 {
for iNdEx := len(m.Members) - 1; iNdEx >= 0; iNdEx-- {
{
size, err := m.Members[iNdEx].MarshalToSizedBuffer(dAtA[:i])
if err != nil {
return 0, err
}
i -= size
i = encodeVarintRpc(dAtA, i, uint64(size))
}
i--
dAtA[i] = 0x12
}
}
if m.Header != nil {
{
size, err := m.Header.MarshalToSizedBuffer(dAtA[:i])
if err != nil {
return 0, err
}
i -= size
i = encodeVarintRpc(dAtA, i, uint64(size))
}
i--
dAtA[i] = 0xa
}
return len(dAtA) - i, nil
}
func (m *MemberUpdateRequest) Marshal() (dAtA []byte, err error) {
size := m.Size()
dAtA = make([]byte, size)
n, err := m.MarshalToSizedBuffer(dAtA[:size])
if err != nil {
return nil, err
}
return dAtA[:n], nil
}
func (m *MemberUpdateRequest) MarshalTo(dAtA []byte) (int, error) {
size := m.Size()
return m.MarshalToSizedBuffer(dAtA[:size])
}
func (m *MemberUpdateRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA)
_ = i
var l int
_ = l
if m.XXX_unrecognized != nil {
i -= len(m.XXX_unrecognized)
copy(dAtA[i:], m.XXX_unrecognized)
}
if len(m.PeerURLs) > 0 {
for iNdEx := len(m.PeerURLs) - 1; iNdEx >= 0; iNdEx-- {
i -= len(m.PeerURLs[iNdEx])
copy(dAtA[i:], m.PeerURLs[iNdEx])
i = encodeVarintRpc(dAtA, i, uint64(len(m.PeerURLs[iNdEx])))
i--
dAtA[i] = 0x12
}
}
if m.ID != 0 {
i = encodeVarintRpc(dAtA, i, uint64(m.ID))
i--
dAtA[i] = 0x8
}
return len(dAtA) - i, nil
}
func (m *MemberUpdateResponse) Marshal() (dAtA []byte, err error) {
size := m.Size()
dAtA = make([]byte, size)
n, err := m.MarshalToSizedBuffer(dAtA[:size])
if err != nil {
return nil, err
}
return dAtA[:n], nil
}
func (m *MemberUpdateResponse) MarshalTo(dAtA []byte) (int, error) {
size := m.Size()
return m.MarshalToSizedBuffer(dAtA[:size])
}
func (m *MemberUpdateResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA)
_ = i
var l int
_ = l
if m.XXX_unrecognized != nil {
i -= len(m.XXX_unrecognized)
copy(dAtA[i:], m.XXX_unrecognized)
}
if len(m.Members) > 0 {
for iNdEx := len(m.Members) - 1; iNdEx >= 0; iNdEx-- {
{
size, err := m.Members[iNdEx].MarshalToSizedBuffer(dAtA[:i])
if err != nil {
return 0, err
}
i -= size
i = encodeVarintRpc(dAtA, i, uint64(size))
}
i--
dAtA[i] = 0x12
}
}
if m.Header != nil {
{
size, err := m.Header.MarshalToSizedBuffer(dAtA[:i])
if err != nil {
return 0, err
}
i -= size
i = encodeVarintRpc(dAtA, i, uint64(size))
}
i--
dAtA[i] = 0xa
}
return len(dAtA) - i, nil
}
func (m *MemberListRequest) Marshal() (dAtA []byte, err error) {
size := m.Size()
dAtA = make([]byte, size)
n, err := m.MarshalToSizedBuffer(dAtA[:size])
if err != nil {
return nil, err
}
return dAtA[:n], nil
}
func (m *MemberListRequest) MarshalTo(dAtA []byte) (int, error) {
size := m.Size()
return m.MarshalToSizedBuffer(dAtA[:size])
}
func (m *MemberListRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA)
_ = i
var l int
_ = l
if m.XXX_unrecognized != nil {
i -= len(m.XXX_unrecognized)
copy(dAtA[i:], m.XXX_unrecognized)
}
if m.Linearizable {
i--
if m.Linearizable {
dAtA[i] = 1
} else {
dAtA[i] = 0
}
i--
dAtA[i] = 0x8
}
return len(dAtA) - i, nil
}
func (m *MemberListResponse) Marshal() (dAtA []byte, err error) {
size := m.Size()
dAtA = make([]byte, size)
n, err := m.MarshalToSizedBuffer(dAtA[:size])
if err != nil {
return nil, err
}
return dAtA[:n], nil
}
func (m *MemberListResponse) MarshalTo(dAtA []byte) (int, error) {
size := m.Size()
return m.MarshalToSizedBuffer(dAtA[:size])
}
func (m *MemberListResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA)
_ = i
var l int
_ = l
if m.XXX_unrecognized != nil {
i -= len(m.XXX_unrecognized)
copy(dAtA[i:], m.XXX_unrecognized)
}
if len(m.Members) > 0 {
for iNdEx := len(m.Members) - 1; iNdEx >= 0; iNdEx-- {
{
size, err := m.Members[iNdEx].MarshalToSizedBuffer(dAtA[:i])
if err != nil {
return 0, err
}
i -= size
i = encodeVarintRpc(dAtA, i, uint64(size))
}
i--
dAtA[i] = 0x12
}
}
if m.Header != nil {
{
size, err := m.Header.MarshalToSizedBuffer(dAtA[:i])
if err != nil {
return 0, err
}
i -= size
i = encodeVarintRpc(dAtA, i, uint64(size))
}
i--
dAtA[i] = 0xa
}
return len(dAtA) - i, nil
}
func (m *MemberPromoteRequest) Marshal() (dAtA []byte, err error) {
size := m.Size()
dAtA = make([]byte, size)
n, err := m.MarshalToSizedBuffer(dAtA[:size])
if err != nil {
return nil, err
}
return dAtA[:n], nil
}
func (m *MemberPromoteRequest) MarshalTo(dAtA []byte) (int, error) {
size := m.Size()
return m.MarshalToSizedBuffer(dAtA[:size])
}
func (m *MemberPromoteRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA)
_ = i
var l int
_ = l
if m.XXX_unrecognized != nil {
i -= len(m.XXX_unrecognized)
copy(dAtA[i:], m.XXX_unrecognized)
}
if m.ID != 0 {
i = encodeVarintRpc(dAtA, i, uint64(m.ID))
i--
dAtA[i] = 0x8
}
return len(dAtA) - i, nil
}
func (m *MemberPromoteResponse) Marshal() (dAtA []byte, err error) {
size := m.Size()
dAtA = make([]byte, size)
n, err := m.MarshalToSizedBuffer(dAtA[:size])
if err != nil {
return nil, err
}
return dAtA[:n], nil
}
func (m *MemberPromoteResponse) MarshalTo(dAtA []byte) (int, error) {
size := m.Size()
return m.MarshalToSizedBuffer(dAtA[:size])
}
func (m *MemberPromoteResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA)
_ = i
var l int
_ = l
if m.XXX_unrecognized != nil {
i -= len(m.XXX_unrecognized)
copy(dAtA[i:], m.XXX_unrecognized)
}
if len(m.Members) > 0 {
for iNdEx := len(m.Members) - 1; iNdEx >= 0; iNdEx-- {
{
size, err := m.Members[iNdEx].MarshalToSizedBuffer(dAtA[:i])
if err != nil {
return 0, err
}
i -= size
i = encodeVarintRpc(dAtA, i, uint64(size))
}
i--
dAtA[i] = 0x12
}
}
if m.Header != nil {
{
size, err := m.Header.MarshalToSizedBuffer(dAtA[:i])
if err != nil {
return 0, err
}
i -= size
i = encodeVarintRpc(dAtA, i, uint64(size))
}
i--
dAtA[i] = 0xa
}
return len(dAtA) - i, nil
}
func (m *DefragmentRequest) Marshal() (dAtA []byte, err error) {
size := m.Size()
dAtA = make([]byte, size)
n, err := m.MarshalToSizedBuffer(dAtA[:size])
if err != nil {
return nil, err
}
return dAtA[:n], nil
}
func (m *DefragmentRequest) MarshalTo(dAtA []byte) (int, error) {
size := m.Size()
return m.MarshalToSizedBuffer(dAtA[:size])
}
func (m *DefragmentRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA)
_ = i
var l int
_ = l
if m.XXX_unrecognized != nil {
i -= len(m.XXX_unrecognized)
copy(dAtA[i:], m.XXX_unrecognized)
}
return len(dAtA) - i, nil
}
func (m *DefragmentResponse) Marshal() (dAtA []byte, err error) {
size := m.Size()
dAtA = make([]byte, size)
n, err := m.MarshalToSizedBuffer(dAtA[:size])
if err != nil {
return nil, err
}
return dAtA[:n], nil
}
func (m *DefragmentResponse) MarshalTo(dAtA []byte) (int, error) {
size := m.Size()
return m.MarshalToSizedBuffer(dAtA[:size])
}
func (m *DefragmentResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA)
_ = i
var l int
_ = l
if m.XXX_unrecognized != nil {
i -= len(m.XXX_unrecognized)
copy(dAtA[i:], m.XXX_unrecognized)
}
if m.Header != nil {
{
size, err := m.Header.MarshalToSizedBuffer(dAtA[:i])
if err != nil {
return 0, err
}
i -= size
i = encodeVarintRpc(dAtA, i, uint64(size))
}
i--
dAtA[i] = 0xa
}
return len(dAtA) - i, nil
}
func (m *MoveLeaderRequest) Marshal() (dAtA []byte, err error) {
size := m.Size()
dAtA = make([]byte, size)
n, err := m.MarshalToSizedBuffer(dAtA[:size])
if err != nil {
return nil, err
}
return dAtA[:n], nil
}
func (m *MoveLeaderRequest) MarshalTo(dAtA []byte) (int, error) {
size := m.Size()
return m.MarshalToSizedBuffer(dAtA[:size])
}
func (m *MoveLeaderRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA)
_ = i
var l int
_ = l
if m.XXX_unrecognized != nil {
i -= len(m.XXX_unrecognized)
copy(dAtA[i:], m.XXX_unrecognized)
}
if m.TargetID != 0 {
i = encodeVarintRpc(dAtA, i, uint64(m.TargetID))
i--
dAtA[i] = 0x8
}
return len(dAtA) - i, nil
}
func (m *MoveLeaderResponse) Marshal() (dAtA []byte, err error) {
size := m.Size()
dAtA = make([]byte, size)
n, err := m.MarshalToSizedBuffer(dAtA[:size])
if err != nil {
return nil, err
}
return dAtA[:n], nil
}
func (m *MoveLeaderResponse) MarshalTo(dAtA []byte) (int, error) {
size := m.Size()
return m.MarshalToSizedBuffer(dAtA[:size])
}
func (m *MoveLeaderResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA)
_ = i
var l int
_ = l
if m.XXX_unrecognized != nil {
i -= len(m.XXX_unrecognized)
copy(dAtA[i:], m.XXX_unrecognized)
}
if m.Header != nil {
{
size, err := m.Header.MarshalToSizedBuffer(dAtA[:i])
if err != nil {
return 0, err
}
i -= size
i = encodeVarintRpc(dAtA, i, uint64(size))
}
i--
dAtA[i] = 0xa
}
return len(dAtA) - i, nil
}
func (m *AlarmRequest) Marshal() (dAtA []byte, err error) {
size := m.Size()
dAtA = make([]byte, size)
n, err := m.MarshalToSizedBuffer(dAtA[:size])
if err != nil {
return nil, err
}
return dAtA[:n], nil
}
func (m *AlarmRequest) MarshalTo(dAtA []byte) (int, error) {
size := m.Size()
return m.MarshalToSizedBuffer(dAtA[:size])
}
func (m *AlarmRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA)
_ = i
var l int
_ = l
if m.XXX_unrecognized != nil {
i -= len(m.XXX_unrecognized)
copy(dAtA[i:], m.XXX_unrecognized)
}
if m.Alarm != 0 {
i = encodeVarintRpc(dAtA, i, uint64(m.Alarm))
i--
dAtA[i] = 0x18
}
if m.MemberID != 0 {
i = encodeVarintRpc(dAtA, i, uint64(m.MemberID))
i--
dAtA[i] = 0x10
}
if m.Action != 0 {
i = encodeVarintRpc(dAtA, i, uint64(m.Action))
i--
dAtA[i] = 0x8
}
return len(dAtA) - i, nil
}
func (m *AlarmMember) Marshal() (dAtA []byte, err error) {
size := m.Size()
dAtA = make([]byte, size)
n, err := m.MarshalToSizedBuffer(dAtA[:size])
if err != nil {
return nil, err
}
return dAtA[:n], nil
}
func (m *AlarmMember) MarshalTo(dAtA []byte) (int, error) {
size := m.Size()
return m.MarshalToSizedBuffer(dAtA[:size])
}
func (m *AlarmMember) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA)
_ = i
var l int
_ = l
if m.XXX_unrecognized != nil {
i -= len(m.XXX_unrecognized)
copy(dAtA[i:], m.XXX_unrecognized)
}
if m.Alarm != 0 {
i = encodeVarintRpc(dAtA, i, uint64(m.Alarm))
i--
dAtA[i] = 0x10
}
if m.MemberID != 0 {
i = encodeVarintRpc(dAtA, i, uint64(m.MemberID))
i--
dAtA[i] = 0x8
}
return len(dAtA) - i, nil
}
func (m *AlarmResponse) Marshal() (dAtA []byte, err error) {
size := m.Size()
dAtA = make([]byte, size)
n, err := m.MarshalToSizedBuffer(dAtA[:size])
if err != nil {
return nil, err
}
return dAtA[:n], nil
}
func (m *AlarmResponse) MarshalTo(dAtA []byte) (int, error) {
size := m.Size()
return m.MarshalToSizedBuffer(dAtA[:size])
}
func (m *AlarmResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA)
_ = i
var l int
_ = l
if m.XXX_unrecognized != nil {
i -= len(m.XXX_unrecognized)
copy(dAtA[i:], m.XXX_unrecognized)
}
if len(m.Alarms) > 0 {
for iNdEx := len(m.Alarms) - 1; iNdEx >= 0; iNdEx-- {
{
size, err := m.Alarms[iNdEx].MarshalToSizedBuffer(dAtA[:i])
if err != nil {
return 0, err
}
i -= size
i = encodeVarintRpc(dAtA, i, uint64(size))
}
i--
dAtA[i] = 0x12
}
}
if m.Header != nil {
{
size, err := m.Header.MarshalToSizedBuffer(dAtA[:i])
if err != nil {
return 0, err
}
i -= size
i = encodeVarintRpc(dAtA, i, uint64(size))
}
i--
dAtA[i] = 0xa
}
return len(dAtA) - i, nil
}
func (m *DowngradeRequest) Marshal() (dAtA []byte, err error) {
size := m.Size()
dAtA = make([]byte, size)
n, err := m.MarshalToSizedBuffer(dAtA[:size])
if err != nil {
return nil, err
}
return dAtA[:n], nil
}
func (m *DowngradeRequest) MarshalTo(dAtA []byte) (int, error) {
size := m.Size()
return m.MarshalToSizedBuffer(dAtA[:size])
}
func (m *DowngradeRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA)
_ = i
var l int
_ = l
if m.XXX_unrecognized != nil {
i -= len(m.XXX_unrecognized)
copy(dAtA[i:], m.XXX_unrecognized)
}
if len(m.Version) > 0 {
i -= len(m.Version)
copy(dAtA[i:], m.Version)
i = encodeVarintRpc(dAtA, i, uint64(len(m.Version)))
i--
dAtA[i] = 0x12
}
if m.Action != 0 {
i = encodeVarintRpc(dAtA, i, uint64(m.Action))
i--
dAtA[i] = 0x8
}
return len(dAtA) - i, nil
}
func (m *DowngradeResponse) Marshal() (dAtA []byte, err error) {
size := m.Size()
dAtA = make([]byte, size)
n, err := m.MarshalToSizedBuffer(dAtA[:size])
if err != nil {
return nil, err
}
return dAtA[:n], nil
}
func (m *DowngradeResponse) MarshalTo(dAtA []byte) (int, error) {
size := m.Size()
return m.MarshalToSizedBuffer(dAtA[:size])
}
func (m *DowngradeResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA)
_ = i
var l int
_ = l
if m.XXX_unrecognized != nil {
i -= len(m.XXX_unrecognized)
copy(dAtA[i:], m.XXX_unrecognized)
}
if len(m.Version) > 0 {
i -= len(m.Version)
copy(dAtA[i:], m.Version)
i = encodeVarintRpc(dAtA, i, uint64(len(m.Version)))
i--
dAtA[i] = 0x12
}
if m.Header != nil {
{
size, err := m.Header.MarshalToSizedBuffer(dAtA[:i])
if err != nil {
return 0, err
}
i -= size
i = encodeVarintRpc(dAtA, i, uint64(size))
}
i--
dAtA[i] = 0xa
}
return len(dAtA) - i, nil
}
func (m *DowngradeVersionTestRequest) Marshal() (dAtA []byte, err error) {
size := m.Size()
dAtA = make([]byte, size)
n, err := m.MarshalToSizedBuffer(dAtA[:size])
if err != nil {
return nil, err
}
return dAtA[:n], nil
}
func (m *DowngradeVersionTestRequest) MarshalTo(dAtA []byte) (int, error) {
size := m.Size()
return m.MarshalToSizedBuffer(dAtA[:size])
}
func (m *DowngradeVersionTestRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA)
_ = i
var l int
_ = l
if m.XXX_unrecognized != nil {
i -= len(m.XXX_unrecognized)
copy(dAtA[i:], m.XXX_unrecognized)
}
if len(m.Ver) > 0 {
i -= len(m.Ver)
copy(dAtA[i:], m.Ver)
i = encodeVarintRpc(dAtA, i, uint64(len(m.Ver)))
i--
dAtA[i] = 0xa
}
return len(dAtA) - i, nil
}
func (m *StatusRequest) Marshal() (dAtA []byte, err error) {
size := m.Size()
dAtA = make([]byte, size)
n, err := m.MarshalToSizedBuffer(dAtA[:size])
if err != nil {
return nil, err
}
return dAtA[:n], nil
}
func (m *StatusRequest) MarshalTo(dAtA []byte) (int, error) {
size := m.Size()
return m.MarshalToSizedBuffer(dAtA[:size])
}
func (m *StatusRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA)
_ = i
var l int
_ = l
if m.XXX_unrecognized != nil {
i -= len(m.XXX_unrecognized)
copy(dAtA[i:], m.XXX_unrecognized)
}
return len(dAtA) - i, nil
}
func (m *StatusResponse) Marshal() (dAtA []byte, err error) {
size := m.Size()
dAtA = make([]byte, size)
n, err := m.MarshalToSizedBuffer(dAtA[:size])
if err != nil {
return nil, err
}
return dAtA[:n], nil
}
func (m *StatusResponse) MarshalTo(dAtA []byte) (int, error) {
size := m.Size()
return m.MarshalToSizedBuffer(dAtA[:size])
}
func (m *StatusResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA)
_ = i
var l int
_ = l
if m.XXX_unrecognized != nil {
i -= len(m.XXX_unrecognized)
copy(dAtA[i:], m.XXX_unrecognized)
}
if m.DowngradeInfo != nil {
{
size, err := m.DowngradeInfo.MarshalToSizedBuffer(dAtA[:i])
if err != nil {
return 0, err
}
i -= size
i = encodeVarintRpc(dAtA, i, uint64(size))
}
i--
dAtA[i] = 0x6a
}
if m.DbSizeQuota != 0 {
i = encodeVarintRpc(dAtA, i, uint64(m.DbSizeQuota))
i--
dAtA[i] = 0x60
}
if len(m.StorageVersion) > 0 {
i -= len(m.StorageVersion)
copy(dAtA[i:], m.StorageVersion)
i = encodeVarintRpc(dAtA, i, uint64(len(m.StorageVersion)))
i--
dAtA[i] = 0x5a
}
if m.IsLearner {
i--
if m.IsLearner {
dAtA[i] = 1
} else {
dAtA[i] = 0
}
i--
dAtA[i] = 0x50
}
if m.DbSizeInUse != 0 {
i = encodeVarintRpc(dAtA, i, uint64(m.DbSizeInUse))
i--
dAtA[i] = 0x48
}
if len(m.Errors) > 0 {
for iNdEx := len(m.Errors) - 1; iNdEx >= 0; iNdEx-- {
i -= len(m.Errors[iNdEx])
copy(dAtA[i:], m.Errors[iNdEx])
i = encodeVarintRpc(dAtA, i, uint64(len(m.Errors[iNdEx])))
i--
dAtA[i] = 0x42
}
}
if m.RaftAppliedIndex != 0 {
i = encodeVarintRpc(dAtA, i, uint64(m.RaftAppliedIndex))
i--
dAtA[i] = 0x38
}
if m.RaftTerm != 0 {
i = encodeVarintRpc(dAtA, i, uint64(m.RaftTerm))
i--
dAtA[i] = 0x30
}
if m.RaftIndex != 0 {
i = encodeVarintRpc(dAtA, i, uint64(m.RaftIndex))
i--
dAtA[i] = 0x28
}
if m.Leader != 0 {
i = encodeVarintRpc(dAtA, i, uint64(m.Leader))
i--
dAtA[i] = 0x20
}
if m.DbSize != 0 {
i = encodeVarintRpc(dAtA, i, uint64(m.DbSize))
i--
dAtA[i] = 0x18
}
if len(m.Version) > 0 {
i -= len(m.Version)
copy(dAtA[i:], m.Version)
i = encodeVarintRpc(dAtA, i, uint64(len(m.Version)))
i--
dAtA[i] = 0x12
}
if m.Header != nil {
{
size, err := m.Header.MarshalToSizedBuffer(dAtA[:i])
if err != nil {
return 0, err
}
i -= size
i = encodeVarintRpc(dAtA, i, uint64(size))
}
i--
dAtA[i] = 0xa
}
return len(dAtA) - i, nil
}
func (m *DowngradeInfo) Marshal() (dAtA []byte, err error) {
size := m.Size()
dAtA = make([]byte, size)
n, err := m.MarshalToSizedBuffer(dAtA[:size])
if err != nil {
return nil, err
}
return dAtA[:n], nil
}
func (m *DowngradeInfo) MarshalTo(dAtA []byte) (int, error) {
size := m.Size()
return m.MarshalToSizedBuffer(dAtA[:size])
}
func (m *DowngradeInfo) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA)
_ = i
var l int
_ = l
if m.XXX_unrecognized != nil {
i -= len(m.XXX_unrecognized)
copy(dAtA[i:], m.XXX_unrecognized)
}
if len(m.TargetVersion) > 0 {
i -= len(m.TargetVersion)
copy(dAtA[i:], m.TargetVersion)
i = encodeVarintRpc(dAtA, i, uint64(len(m.TargetVersion)))
i--
dAtA[i] = 0x12
}
if m.Enabled {
i--
if m.Enabled {
dAtA[i] = 1
} else {
dAtA[i] = 0
}
i--
dAtA[i] = 0x8
}
return len(dAtA) - i, nil
}
func (m *AuthEnableRequest) Marshal() (dAtA []byte, err error) {
size := m.Size()
dAtA = make([]byte, size)
n, err := m.MarshalToSizedBuffer(dAtA[:size])
if err != nil {
return nil, err
}
return dAtA[:n], nil
}
func (m *AuthEnableRequest) MarshalTo(dAtA []byte) (int, error) {
size := m.Size()
return m.MarshalToSizedBuffer(dAtA[:size])
}
func (m *AuthEnableRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA)
_ = i
var l int
_ = l
if m.XXX_unrecognized != nil {
i -= len(m.XXX_unrecognized)
copy(dAtA[i:], m.XXX_unrecognized)
}
return len(dAtA) - i, nil
}
func (m *AuthDisableRequest) Marshal() (dAtA []byte, err error) {
size := m.Size()
dAtA = make([]byte, size)
n, err := m.MarshalToSizedBuffer(dAtA[:size])
if err != nil {
return nil, err
}
return dAtA[:n], nil
}
func (m *AuthDisableRequest) MarshalTo(dAtA []byte) (int, error) {
size := m.Size()
return m.MarshalToSizedBuffer(dAtA[:size])
}
func (m *AuthDisableRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA)
_ = i
var l int
_ = l
if m.XXX_unrecognized != nil {
i -= len(m.XXX_unrecognized)
copy(dAtA[i:], m.XXX_unrecognized)
}
return len(dAtA) - i, nil
}
func (m *AuthStatusRequest) Marshal() (dAtA []byte, err error) {
size := m.Size()
dAtA = make([]byte, size)
n, err := m.MarshalToSizedBuffer(dAtA[:size])
if err != nil {
return nil, err
}
return dAtA[:n], nil
}
func (m *AuthStatusRequest) MarshalTo(dAtA []byte) (int, error) {
size := m.Size()
return m.MarshalToSizedBuffer(dAtA[:size])
}
func (m *AuthStatusRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA)
_ = i
var l int
_ = l
if m.XXX_unrecognized != nil {
i -= len(m.XXX_unrecognized)
copy(dAtA[i:], m.XXX_unrecognized)
}
return len(dAtA) - i, nil
}
func (m *AuthenticateRequest) Marshal() (dAtA []byte, err error) {
size := m.Size()
dAtA = make([]byte, size)
n, err := m.MarshalToSizedBuffer(dAtA[:size])
if err != nil {
return nil, err
}
return dAtA[:n], nil
}
func (m *AuthenticateRequest) MarshalTo(dAtA []byte) (int, error) {
size := m.Size()
return m.MarshalToSizedBuffer(dAtA[:size])
}
func (m *AuthenticateRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA)
_ = i
var l int
_ = l
if m.XXX_unrecognized != nil {
i -= len(m.XXX_unrecognized)
copy(dAtA[i:], m.XXX_unrecognized)
}
if len(m.Password) > 0 {
i -= len(m.Password)
copy(dAtA[i:], m.Password)
i = encodeVarintRpc(dAtA, i, uint64(len(m.Password)))
i--
dAtA[i] = 0x12
}
if len(m.Name) > 0 {
i -= len(m.Name)
copy(dAtA[i:], m.Name)
i = encodeVarintRpc(dAtA, i, uint64(len(m.Name)))
i--
dAtA[i] = 0xa
}
return len(dAtA) - i, nil
}
func (m *AuthUserAddRequest) Marshal() (dAtA []byte, err error) {
size := m.Size()
dAtA = make([]byte, size)
n, err := m.MarshalToSizedBuffer(dAtA[:size])
if err != nil {
return nil, err
}
return dAtA[:n], nil
}
func (m *AuthUserAddRequest) MarshalTo(dAtA []byte) (int, error) {
size := m.Size()
return m.MarshalToSizedBuffer(dAtA[:size])
}
func (m *AuthUserAddRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA)
_ = i
var l int
_ = l
if m.XXX_unrecognized != nil {
i -= len(m.XXX_unrecognized)
copy(dAtA[i:], m.XXX_unrecognized)
}
if len(m.HashedPassword) > 0 {
i -= len(m.HashedPassword)
copy(dAtA[i:], m.HashedPassword)
i = encodeVarintRpc(dAtA, i, uint64(len(m.HashedPassword)))
i--
dAtA[i] = 0x22
}
if m.Options != nil {
{
size, err := m.Options.MarshalToSizedBuffer(dAtA[:i])
if err != nil {
return 0, err
}
i -= size
i = encodeVarintRpc(dAtA, i, uint64(size))
}
i--
dAtA[i] = 0x1a
}
if len(m.Password) > 0 {
i -= len(m.Password)
copy(dAtA[i:], m.Password)
i = encodeVarintRpc(dAtA, i, uint64(len(m.Password)))
i--
dAtA[i] = 0x12
}
if len(m.Name) > 0 {
i -= len(m.Name)
copy(dAtA[i:], m.Name)
i = encodeVarintRpc(dAtA, i, uint64(len(m.Name)))
i--
dAtA[i] = 0xa
}
return len(dAtA) - i, nil
}
func (m *AuthUserGetRequest) Marshal() (dAtA []byte, err error) {
size := m.Size()
dAtA = make([]byte, size)
n, err := m.MarshalToSizedBuffer(dAtA[:size])
if err != nil {
return nil, err
}
return dAtA[:n], nil
}
func (m *AuthUserGetRequest) MarshalTo(dAtA []byte) (int, error) {
size := m.Size()
return m.MarshalToSizedBuffer(dAtA[:size])
}
func (m *AuthUserGetRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA)
_ = i
var l int
_ = l
if m.XXX_unrecognized != nil {
i -= len(m.XXX_unrecognized)
copy(dAtA[i:], m.XXX_unrecognized)
}
if len(m.Name) > 0 {
i -= len(m.Name)
copy(dAtA[i:], m.Name)
i = encodeVarintRpc(dAtA, i, uint64(len(m.Name)))
i--
dAtA[i] = 0xa
}
return len(dAtA) - i, nil
}
func (m *AuthUserDeleteRequest) Marshal() (dAtA []byte, err error) {
size := m.Size()
dAtA = make([]byte, size)
n, err := m.MarshalToSizedBuffer(dAtA[:size])
if err != nil {
return nil, err
}
return dAtA[:n], nil
}
func (m *AuthUserDeleteRequest) MarshalTo(dAtA []byte) (int, error) {
size := m.Size()
return m.MarshalToSizedBuffer(dAtA[:size])
}
func (m *AuthUserDeleteRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA)
_ = i
var l int
_ = l
if m.XXX_unrecognized != nil {
i -= len(m.XXX_unrecognized)
copy(dAtA[i:], m.XXX_unrecognized)
}
if len(m.Name) > 0 {
i -= len(m.Name)
copy(dAtA[i:], m.Name)
i = encodeVarintRpc(dAtA, i, uint64(len(m.Name)))
i--
dAtA[i] = 0xa
}
return len(dAtA) - i, nil
}
func (m *AuthUserChangePasswordRequest) Marshal() (dAtA []byte, err error) {
size := m.Size()
dAtA = make([]byte, size)
n, err := m.MarshalToSizedBuffer(dAtA[:size])
if err != nil {
return nil, err
}
return dAtA[:n], nil
}
func (m *AuthUserChangePasswordRequest) MarshalTo(dAtA []byte) (int, error) {
size := m.Size()
return m.MarshalToSizedBuffer(dAtA[:size])
}
func (m *AuthUserChangePasswordRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA)
_ = i
var l int
_ = l
if m.XXX_unrecognized != nil {
i -= len(m.XXX_unrecognized)
copy(dAtA[i:], m.XXX_unrecognized)
}
if len(m.HashedPassword) > 0 {
i -= len(m.HashedPassword)
copy(dAtA[i:], m.HashedPassword)
i = encodeVarintRpc(dAtA, i, uint64(len(m.HashedPassword)))
i--
dAtA[i] = 0x1a
}
if len(m.Password) > 0 {
i -= len(m.Password)
copy(dAtA[i:], m.Password)
i = encodeVarintRpc(dAtA, i, uint64(len(m.Password)))
i--
dAtA[i] = 0x12
}
if len(m.Name) > 0 {
i -= len(m.Name)
copy(dAtA[i:], m.Name)
i = encodeVarintRpc(dAtA, i, uint64(len(m.Name)))
i--
dAtA[i] = 0xa
}
return len(dAtA) - i, nil
}
func (m *AuthUserGrantRoleRequest) Marshal() (dAtA []byte, err error) {
size := m.Size()
dAtA = make([]byte, size)
n, err := m.MarshalToSizedBuffer(dAtA[:size])
if err != nil {
return nil, err
}
return dAtA[:n], nil
}
func (m *AuthUserGrantRoleRequest) MarshalTo(dAtA []byte) (int, error) {
size := m.Size()
return m.MarshalToSizedBuffer(dAtA[:size])
}
func (m *AuthUserGrantRoleRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA)
_ = i
var l int
_ = l
if m.XXX_unrecognized != nil {
i -= len(m.XXX_unrecognized)
copy(dAtA[i:], m.XXX_unrecognized)
}
if len(m.Role) > 0 {
i -= len(m.Role)
copy(dAtA[i:], m.Role)
i = encodeVarintRpc(dAtA, i, uint64(len(m.Role)))
i--
dAtA[i] = 0x12
}
if len(m.User) > 0 {
i -= len(m.User)
copy(dAtA[i:], m.User)
i = encodeVarintRpc(dAtA, i, uint64(len(m.User)))
i--
dAtA[i] = 0xa
}
return len(dAtA) - i, nil
}
func (m *AuthUserRevokeRoleRequest) Marshal() (dAtA []byte, err error) {
size := m.Size()
dAtA = make([]byte, size)
n, err := m.MarshalToSizedBuffer(dAtA[:size])
if err != nil {
return nil, err
}
return dAtA[:n], nil
}
func (m *AuthUserRevokeRoleRequest) MarshalTo(dAtA []byte) (int, error) {
size := m.Size()
return m.MarshalToSizedBuffer(dAtA[:size])
}
func (m *AuthUserRevokeRoleRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA)
_ = i
var l int
_ = l
if m.XXX_unrecognized != nil {
i -= len(m.XXX_unrecognized)
copy(dAtA[i:], m.XXX_unrecognized)
}
if len(m.Role) > 0 {
i -= len(m.Role)
copy(dAtA[i:], m.Role)
i = encodeVarintRpc(dAtA, i, uint64(len(m.Role)))
i--
dAtA[i] = 0x12
}
if len(m.Name) > 0 {
i -= len(m.Name)
copy(dAtA[i:], m.Name)
i = encodeVarintRpc(dAtA, i, uint64(len(m.Name)))
i--
dAtA[i] = 0xa
}
return len(dAtA) - i, nil
}
func (m *AuthRoleAddRequest) Marshal() (dAtA []byte, err error) {
size := m.Size()
dAtA = make([]byte, size)
n, err := m.MarshalToSizedBuffer(dAtA[:size])
if err != nil {
return nil, err
}
return dAtA[:n], nil
}
func (m *AuthRoleAddRequest) MarshalTo(dAtA []byte) (int, error) {
size := m.Size()
return m.MarshalToSizedBuffer(dAtA[:size])
}
func (m *AuthRoleAddRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA)
_ = i
var l int
_ = l
if m.XXX_unrecognized != nil {
i -= len(m.XXX_unrecognized)
copy(dAtA[i:], m.XXX_unrecognized)
}
if len(m.Name) > 0 {
i -= len(m.Name)
copy(dAtA[i:], m.Name)
i = encodeVarintRpc(dAtA, i, uint64(len(m.Name)))
i--
dAtA[i] = 0xa
}
return len(dAtA) - i, nil
}
func (m *AuthRoleGetRequest) Marshal() (dAtA []byte, err error) {
size := m.Size()
dAtA = make([]byte, size)
n, err := m.MarshalToSizedBuffer(dAtA[:size])
if err != nil {
return nil, err
}
return dAtA[:n], nil
}
func (m *AuthRoleGetRequest) MarshalTo(dAtA []byte) (int, error) {
size := m.Size()
return m.MarshalToSizedBuffer(dAtA[:size])
}
func (m *AuthRoleGetRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA)
_ = i
var l int
_ = l
if m.XXX_unrecognized != nil {
i -= len(m.XXX_unrecognized)
copy(dAtA[i:], m.XXX_unrecognized)
}
if len(m.Role) > 0 {
i -= len(m.Role)
copy(dAtA[i:], m.Role)
i = encodeVarintRpc(dAtA, i, uint64(len(m.Role)))
i--
dAtA[i] = 0xa
}
return len(dAtA) - i, nil
}
func (m *AuthUserListRequest) Marshal() (dAtA []byte, err error) {
size := m.Size()
dAtA = make([]byte, size)
n, err := m.MarshalToSizedBuffer(dAtA[:size])
if err != nil {
return nil, err
}
return dAtA[:n], nil
}
func (m *AuthUserListRequest) MarshalTo(dAtA []byte) (int, error) {
size := m.Size()
return m.MarshalToSizedBuffer(dAtA[:size])
}
func (m *AuthUserListRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA)
_ = i
var l int
_ = l
if m.XXX_unrecognized != nil {
i -= len(m.XXX_unrecognized)
copy(dAtA[i:], m.XXX_unrecognized)
}
return len(dAtA) - i, nil
}
func (m *AuthRoleListRequest) Marshal() (dAtA []byte, err error) {
size := m.Size()
dAtA = make([]byte, size)
n, err := m.MarshalToSizedBuffer(dAtA[:size])
if err != nil {
return nil, err
}
return dAtA[:n], nil
}
func (m *AuthRoleListRequest) MarshalTo(dAtA []byte) (int, error) {
size := m.Size()
return m.MarshalToSizedBuffer(dAtA[:size])
}
func (m *AuthRoleListRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA)
_ = i
var l int
_ = l
if m.XXX_unrecognized != nil {
i -= len(m.XXX_unrecognized)
copy(dAtA[i:], m.XXX_unrecognized)
}
return len(dAtA) - i, nil
}
func (m *AuthRoleDeleteRequest) Marshal() (dAtA []byte, err error) {
size := m.Size()
dAtA = make([]byte, size)
n, err := m.MarshalToSizedBuffer(dAtA[:size])
if err != nil {
return nil, err
}
return dAtA[:n], nil
}
func (m *AuthRoleDeleteRequest) MarshalTo(dAtA []byte) (int, error) {
size := m.Size()
return m.MarshalToSizedBuffer(dAtA[:size])
}
func (m *AuthRoleDeleteRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA)
_ = i
var l int
_ = l
if m.XXX_unrecognized != nil {
i -= len(m.XXX_unrecognized)
copy(dAtA[i:], m.XXX_unrecognized)
}
if len(m.Role) > 0 {
i -= len(m.Role)
copy(dAtA[i:], m.Role)
i = encodeVarintRpc(dAtA, i, uint64(len(m.Role)))
i--
dAtA[i] = 0xa
}
return len(dAtA) - i, nil
}
func (m *AuthRoleGrantPermissionRequest) Marshal() (dAtA []byte, err error) {
size := m.Size()
dAtA = make([]byte, size)
n, err := m.MarshalToSizedBuffer(dAtA[:size])
if err != nil {
return nil, err
}
return dAtA[:n], nil
}
func (m *AuthRoleGrantPermissionRequest) MarshalTo(dAtA []byte) (int, error) {
size := m.Size()
return m.MarshalToSizedBuffer(dAtA[:size])
}
func (m *AuthRoleGrantPermissionRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA)
_ = i
var l int
_ = l
if m.XXX_unrecognized != nil {
i -= len(m.XXX_unrecognized)
copy(dAtA[i:], m.XXX_unrecognized)
}
if m.Perm != nil {
{
size, err := m.Perm.MarshalToSizedBuffer(dAtA[:i])
if err != nil {
return 0, err
}
i -= size
i = encodeVarintRpc(dAtA, i, uint64(size))
}
i--
dAtA[i] = 0x12
}
if len(m.Name) > 0 {
i -= len(m.Name)
copy(dAtA[i:], m.Name)
i = encodeVarintRpc(dAtA, i, uint64(len(m.Name)))
i--
dAtA[i] = 0xa
}
return len(dAtA) - i, nil
}
func (m *AuthRoleRevokePermissionRequest) Marshal() (dAtA []byte, err error) {
size := m.Size()
dAtA = make([]byte, size)
n, err := m.MarshalToSizedBuffer(dAtA[:size])
if err != nil {
return nil, err
}
return dAtA[:n], nil
}
func (m *AuthRoleRevokePermissionRequest) MarshalTo(dAtA []byte) (int, error) {
size := m.Size()
return m.MarshalToSizedBuffer(dAtA[:size])
}
func (m *AuthRoleRevokePermissionRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA)
_ = i
var l int
_ = l
if m.XXX_unrecognized != nil {
i -= len(m.XXX_unrecognized)
copy(dAtA[i:], m.XXX_unrecognized)
}
if len(m.RangeEnd) > 0 {
i -= len(m.RangeEnd)
copy(dAtA[i:], m.RangeEnd)
i = encodeVarintRpc(dAtA, i, uint64(len(m.RangeEnd)))
i--
dAtA[i] = 0x1a
}
if len(m.Key) > 0 {
i -= len(m.Key)
copy(dAtA[i:], m.Key)
i = encodeVarintRpc(dAtA, i, uint64(len(m.Key)))
i--
dAtA[i] = 0x12
}
if len(m.Role) > 0 {
i -= len(m.Role)
copy(dAtA[i:], m.Role)
i = encodeVarintRpc(dAtA, i, uint64(len(m.Role)))
i--
dAtA[i] = 0xa
}
return len(dAtA) - i, nil
}
func (m *AuthEnableResponse) Marshal() (dAtA []byte, err error) {
size := m.Size()
dAtA = make([]byte, size)
n, err := m.MarshalToSizedBuffer(dAtA[:size])
if err != nil {
return nil, err
}
return dAtA[:n], nil
}
func (m *AuthEnableResponse) MarshalTo(dAtA []byte) (int, error) {
size := m.Size()
return m.MarshalToSizedBuffer(dAtA[:size])
}
func (m *AuthEnableResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA)
_ = i
var l int
_ = l
if m.XXX_unrecognized != nil {
i -= len(m.XXX_unrecognized)
copy(dAtA[i:], m.XXX_unrecognized)
}
if m.Header != nil {
{
size, err := m.Header.MarshalToSizedBuffer(dAtA[:i])
if err != nil {
return 0, err
}
i -= size
i = encodeVarintRpc(dAtA, i, uint64(size))
}
i--
dAtA[i] = 0xa
}
return len(dAtA) - i, nil
}
func (m *AuthDisableResponse) Marshal() (dAtA []byte, err error) {
size := m.Size()
dAtA = make([]byte, size)
n, err := m.MarshalToSizedBuffer(dAtA[:size])
if err != nil {
return nil, err
}
return dAtA[:n], nil
}
func (m *AuthDisableResponse) MarshalTo(dAtA []byte) (int, error) {
size := m.Size()
return m.MarshalToSizedBuffer(dAtA[:size])
}
func (m *AuthDisableResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA)
_ = i
var l int
_ = l
if m.XXX_unrecognized != nil {
i -= len(m.XXX_unrecognized)
copy(dAtA[i:], m.XXX_unrecognized)
}
if m.Header != nil {
{
size, err := m.Header.MarshalToSizedBuffer(dAtA[:i])
if err != nil {
return 0, err
}
i -= size
i = encodeVarintRpc(dAtA, i, uint64(size))
}
i--
dAtA[i] = 0xa
}
return len(dAtA) - i, nil
}
func (m *AuthStatusResponse) Marshal() (dAtA []byte, err error) {
size := m.Size()
dAtA = make([]byte, size)
n, err := m.MarshalToSizedBuffer(dAtA[:size])
if err != nil {
return nil, err
}
return dAtA[:n], nil
}
func (m *AuthStatusResponse) MarshalTo(dAtA []byte) (int, error) {
size := m.Size()
return m.MarshalToSizedBuffer(dAtA[:size])
}
func (m *AuthStatusResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA)
_ = i
var l int
_ = l
if m.XXX_unrecognized != nil {
i -= len(m.XXX_unrecognized)
copy(dAtA[i:], m.XXX_unrecognized)
}
if m.AuthRevision != 0 {
i = encodeVarintRpc(dAtA, i, uint64(m.AuthRevision))
i--
dAtA[i] = 0x18
}
if m.Enabled {
i--
if m.Enabled {
dAtA[i] = 1
} else {
dAtA[i] = 0
}
i--
dAtA[i] = 0x10
}
if m.Header != nil {
{
size, err := m.Header.MarshalToSizedBuffer(dAtA[:i])
if err != nil {
return 0, err
}
i -= size
i = encodeVarintRpc(dAtA, i, uint64(size))
}
i--
dAtA[i] = 0xa
}
return len(dAtA) - i, nil
}
func (m *AuthenticateResponse) Marshal() (dAtA []byte, err error) {
size := m.Size()
dAtA = make([]byte, size)
n, err := m.MarshalToSizedBuffer(dAtA[:size])
if err != nil {
return nil, err
}
return dAtA[:n], nil
}
func (m *AuthenticateResponse) MarshalTo(dAtA []byte) (int, error) {
size := m.Size()
return m.MarshalToSizedBuffer(dAtA[:size])
}
func (m *AuthenticateResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA)
_ = i
var l int
_ = l
if m.XXX_unrecognized != nil {
i -= len(m.XXX_unrecognized)
copy(dAtA[i:], m.XXX_unrecognized)
}
if len(m.Token) > 0 {
i -= len(m.Token)
copy(dAtA[i:], m.Token)
i = encodeVarintRpc(dAtA, i, uint64(len(m.Token)))
i--
dAtA[i] = 0x12
}
if m.Header != nil {
{
size, err := m.Header.MarshalToSizedBuffer(dAtA[:i])
if err != nil {
return 0, err
}
i -= size
i = encodeVarintRpc(dAtA, i, uint64(size))
}
i--
dAtA[i] = 0xa
}
return len(dAtA) - i, nil
}
func (m *AuthUserAddResponse) Marshal() (dAtA []byte, err error) {
size := m.Size()
dAtA = make([]byte, size)
n, err := m.MarshalToSizedBuffer(dAtA[:size])
if err != nil {
return nil, err
}
return dAtA[:n], nil
}
func (m *AuthUserAddResponse) MarshalTo(dAtA []byte) (int, error) {
size := m.Size()
return m.MarshalToSizedBuffer(dAtA[:size])
}
func (m *AuthUserAddResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA)
_ = i
var l int
_ = l
if m.XXX_unrecognized != nil {
i -= len(m.XXX_unrecognized)
copy(dAtA[i:], m.XXX_unrecognized)
}
if m.Header != nil {
{
size, err := m.Header.MarshalToSizedBuffer(dAtA[:i])
if err != nil {
return 0, err
}
i -= size
i = encodeVarintRpc(dAtA, i, uint64(size))
}
i--
dAtA[i] = 0xa
}
return len(dAtA) - i, nil
}
func (m *AuthUserGetResponse) Marshal() (dAtA []byte, err error) {
size := m.Size()
dAtA = make([]byte, size)
n, err := m.MarshalToSizedBuffer(dAtA[:size])
if err != nil {
return nil, err
}
return dAtA[:n], nil
}
func (m *AuthUserGetResponse) MarshalTo(dAtA []byte) (int, error) {
size := m.Size()
return m.MarshalToSizedBuffer(dAtA[:size])
}
func (m *AuthUserGetResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA)
_ = i
var l int
_ = l
if m.XXX_unrecognized != nil {
i -= len(m.XXX_unrecognized)
copy(dAtA[i:], m.XXX_unrecognized)
}
if len(m.Roles) > 0 {
for iNdEx := len(m.Roles) - 1; iNdEx >= 0; iNdEx-- {
i -= len(m.Roles[iNdEx])
copy(dAtA[i:], m.Roles[iNdEx])
i = encodeVarintRpc(dAtA, i, uint64(len(m.Roles[iNdEx])))
i--
dAtA[i] = 0x12
}
}
if m.Header != nil {
{
size, err := m.Header.MarshalToSizedBuffer(dAtA[:i])
if err != nil {
return 0, err
}
i -= size
i = encodeVarintRpc(dAtA, i, uint64(size))
}
i--
dAtA[i] = 0xa
}
return len(dAtA) - i, nil
}
func (m *AuthUserDeleteResponse) Marshal() (dAtA []byte, err error) {
size := m.Size()
dAtA = make([]byte, size)
n, err := m.MarshalToSizedBuffer(dAtA[:size])
if err != nil {
return nil, err
}
return dAtA[:n], nil
}
func (m *AuthUserDeleteResponse) MarshalTo(dAtA []byte) (int, error) {
size := m.Size()
return m.MarshalToSizedBuffer(dAtA[:size])
}
func (m *AuthUserDeleteResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA)
_ = i
var l int
_ = l
if m.XXX_unrecognized != nil {
i -= len(m.XXX_unrecognized)
copy(dAtA[i:], m.XXX_unrecognized)
}
if m.Header != nil {
{
size, err := m.Header.MarshalToSizedBuffer(dAtA[:i])
if err != nil {
return 0, err
}
i -= size
i = encodeVarintRpc(dAtA, i, uint64(size))
}
i--
dAtA[i] = 0xa
}
return len(dAtA) - i, nil
}
func (m *AuthUserChangePasswordResponse) Marshal() (dAtA []byte, err error) {
size := m.Size()
dAtA = make([]byte, size)
n, err := m.MarshalToSizedBuffer(dAtA[:size])
if err != nil {
return nil, err
}
return dAtA[:n], nil
}
func (m *AuthUserChangePasswordResponse) MarshalTo(dAtA []byte) (int, error) {
size := m.Size()
return m.MarshalToSizedBuffer(dAtA[:size])
}
func (m *AuthUserChangePasswordResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA)
_ = i
var l int
_ = l
if m.XXX_unrecognized != nil {
i -= len(m.XXX_unrecognized)
copy(dAtA[i:], m.XXX_unrecognized)
}
if m.Header != nil {
{
size, err := m.Header.MarshalToSizedBuffer(dAtA[:i])
if err != nil {
return 0, err
}
i -= size
i = encodeVarintRpc(dAtA, i, uint64(size))
}
i--
dAtA[i] = 0xa
}
return len(dAtA) - i, nil
}
func (m *AuthUserGrantRoleResponse) Marshal() (dAtA []byte, err error) {
size := m.Size()
dAtA = make([]byte, size)
n, err := m.MarshalToSizedBuffer(dAtA[:size])
if err != nil {
return nil, err
}
return dAtA[:n], nil
}
func (m *AuthUserGrantRoleResponse) MarshalTo(dAtA []byte) (int, error) {
size := m.Size()
return m.MarshalToSizedBuffer(dAtA[:size])
}
func (m *AuthUserGrantRoleResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA)
_ = i
var l int
_ = l
if m.XXX_unrecognized != nil {
i -= len(m.XXX_unrecognized)
copy(dAtA[i:], m.XXX_unrecognized)
}
if m.Header != nil {
{
size, err := m.Header.MarshalToSizedBuffer(dAtA[:i])
if err != nil {
return 0, err
}
i -= size
i = encodeVarintRpc(dAtA, i, uint64(size))
}
i--
dAtA[i] = 0xa
}
return len(dAtA) - i, nil
}
func (m *AuthUserRevokeRoleResponse) Marshal() (dAtA []byte, err error) {
size := m.Size()
dAtA = make([]byte, size)
n, err := m.MarshalToSizedBuffer(dAtA[:size])
if err != nil {
return nil, err
}
return dAtA[:n], nil
}
func (m *AuthUserRevokeRoleResponse) MarshalTo(dAtA []byte) (int, error) {
size := m.Size()
return m.MarshalToSizedBuffer(dAtA[:size])
}
func (m *AuthUserRevokeRoleResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA)
_ = i
var l int
_ = l
if m.XXX_unrecognized != nil {
i -= len(m.XXX_unrecognized)
copy(dAtA[i:], m.XXX_unrecognized)
}
if m.Header != nil {
{
size, err := m.Header.MarshalToSizedBuffer(dAtA[:i])
if err != nil {
return 0, err
}
i -= size
i = encodeVarintRpc(dAtA, i, uint64(size))
}
i--
dAtA[i] = 0xa
}
return len(dAtA) - i, nil
}
func (m *AuthRoleAddResponse) Marshal() (dAtA []byte, err error) {
size := m.Size()
dAtA = make([]byte, size)
n, err := m.MarshalToSizedBuffer(dAtA[:size])
if err != nil {
return nil, err
}
return dAtA[:n], nil
}
func (m *AuthRoleAddResponse) MarshalTo(dAtA []byte) (int, error) {
size := m.Size()
return m.MarshalToSizedBuffer(dAtA[:size])
}
func (m *AuthRoleAddResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA)
_ = i
var l int
_ = l
if m.XXX_unrecognized != nil {
i -= len(m.XXX_unrecognized)
copy(dAtA[i:], m.XXX_unrecognized)
}
if m.Header != nil {
{
size, err := m.Header.MarshalToSizedBuffer(dAtA[:i])
if err != nil {
return 0, err
}
i -= size
i = encodeVarintRpc(dAtA, i, uint64(size))
}
i--
dAtA[i] = 0xa
}
return len(dAtA) - i, nil
}
func (m *AuthRoleGetResponse) Marshal() (dAtA []byte, err error) {
size := m.Size()
dAtA = make([]byte, size)
n, err := m.MarshalToSizedBuffer(dAtA[:size])
if err != nil {
return nil, err
}
return dAtA[:n], nil
}
func (m *AuthRoleGetResponse) MarshalTo(dAtA []byte) (int, error) {
size := m.Size()
return m.MarshalToSizedBuffer(dAtA[:size])
}
func (m *AuthRoleGetResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA)
_ = i
var l int
_ = l
if m.XXX_unrecognized != nil {
i -= len(m.XXX_unrecognized)
copy(dAtA[i:], m.XXX_unrecognized)
}
if len(m.Perm) > 0 {
for iNdEx := len(m.Perm) - 1; iNdEx >= 0; iNdEx-- {
{
size, err := m.Perm[iNdEx].MarshalToSizedBuffer(dAtA[:i])
if err != nil {
return 0, err
}
i -= size
i = encodeVarintRpc(dAtA, i, uint64(size))
}
i--
dAtA[i] = 0x12
}
}
if m.Header != nil {
{
size, err := m.Header.MarshalToSizedBuffer(dAtA[:i])
if err != nil {
return 0, err
}
i -= size
i = encodeVarintRpc(dAtA, i, uint64(size))
}
i--
dAtA[i] = 0xa
}
return len(dAtA) - i, nil
}
func (m *AuthRoleListResponse) Marshal() (dAtA []byte, err error) {
size := m.Size()
dAtA = make([]byte, size)
n, err := m.MarshalToSizedBuffer(dAtA[:size])
if err != nil {
return nil, err
}
return dAtA[:n], nil
}
func (m *AuthRoleListResponse) MarshalTo(dAtA []byte) (int, error) {
size := m.Size()
return m.MarshalToSizedBuffer(dAtA[:size])
}
func (m *AuthRoleListResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA)
_ = i
var l int
_ = l
if m.XXX_unrecognized != nil {
i -= len(m.XXX_unrecognized)
copy(dAtA[i:], m.XXX_unrecognized)
}
if len(m.Roles) > 0 {
for iNdEx := len(m.Roles) - 1; iNdEx >= 0; iNdEx-- {
i -= len(m.Roles[iNdEx])
copy(dAtA[i:], m.Roles[iNdEx])
i = encodeVarintRpc(dAtA, i, uint64(len(m.Roles[iNdEx])))
i--
dAtA[i] = 0x12
}
}
if m.Header != nil {
{
size, err := m.Header.MarshalToSizedBuffer(dAtA[:i])
if err != nil {
return 0, err
}
i -= size
i = encodeVarintRpc(dAtA, i, uint64(size))
}
i--
dAtA[i] = 0xa
}
return len(dAtA) - i, nil
}
func (m *AuthUserListResponse) Marshal() (dAtA []byte, err error) {
size := m.Size()
dAtA = make([]byte, size)
n, err := m.MarshalToSizedBuffer(dAtA[:size])
if err != nil {
return nil, err
}
return dAtA[:n], nil
}
func (m *AuthUserListResponse) MarshalTo(dAtA []byte) (int, error) {
size := m.Size()
return m.MarshalToSizedBuffer(dAtA[:size])
}
func (m *AuthUserListResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA)
_ = i
var l int
_ = l
if m.XXX_unrecognized != nil {
i -= len(m.XXX_unrecognized)
copy(dAtA[i:], m.XXX_unrecognized)
}
if len(m.Users) > 0 {
for iNdEx := len(m.Users) - 1; iNdEx >= 0; iNdEx-- {
i -= len(m.Users[iNdEx])
copy(dAtA[i:], m.Users[iNdEx])
i = encodeVarintRpc(dAtA, i, uint64(len(m.Users[iNdEx])))
i--
dAtA[i] = 0x12
}
}
if m.Header != nil {
{
size, err := m.Header.MarshalToSizedBuffer(dAtA[:i])
if err != nil {
return 0, err
}
i -= size
i = encodeVarintRpc(dAtA, i, uint64(size))
}
i--
dAtA[i] = 0xa
}
return len(dAtA) - i, nil
}
func (m *AuthRoleDeleteResponse) Marshal() (dAtA []byte, err error) {
size := m.Size()
dAtA = make([]byte, size)
n, err := m.MarshalToSizedBuffer(dAtA[:size])
if err != nil {
return nil, err
}
return dAtA[:n], nil
}
func (m *AuthRoleDeleteResponse) MarshalTo(dAtA []byte) (int, error) {
size := m.Size()
return m.MarshalToSizedBuffer(dAtA[:size])
}
func (m *AuthRoleDeleteResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA)
_ = i
var l int
_ = l
if m.XXX_unrecognized != nil {
i -= len(m.XXX_unrecognized)
copy(dAtA[i:], m.XXX_unrecognized)
}
if m.Header != nil {
{
size, err := m.Header.MarshalToSizedBuffer(dAtA[:i])
if err != nil {
return 0, err
}
i -= size
i = encodeVarintRpc(dAtA, i, uint64(size))
}
i--
dAtA[i] = 0xa
}
return len(dAtA) - i, nil
}
func (m *AuthRoleGrantPermissionResponse) Marshal() (dAtA []byte, err error) {
size := m.Size()
dAtA = make([]byte, size)
n, err := m.MarshalToSizedBuffer(dAtA[:size])
if err != nil {
return nil, err
}
return dAtA[:n], nil
}
func (m *AuthRoleGrantPermissionResponse) MarshalTo(dAtA []byte) (int, error) {
size := m.Size()
return m.MarshalToSizedBuffer(dAtA[:size])
}
func (m *AuthRoleGrantPermissionResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA)
_ = i
var l int
_ = l
if m.XXX_unrecognized != nil {
i -= len(m.XXX_unrecognized)
copy(dAtA[i:], m.XXX_unrecognized)
}
if m.Header != nil {
{
size, err := m.Header.MarshalToSizedBuffer(dAtA[:i])
if err != nil {
return 0, err
}
i -= size
i = encodeVarintRpc(dAtA, i, uint64(size))
}
i--
dAtA[i] = 0xa
}
return len(dAtA) - i, nil
}
func (m *AuthRoleRevokePermissionResponse) Marshal() (dAtA []byte, err error) {
size := m.Size()
dAtA = make([]byte, size)
n, err := m.MarshalToSizedBuffer(dAtA[:size])
if err != nil {
return nil, err
}
return dAtA[:n], nil
}
func (m *AuthRoleRevokePermissionResponse) MarshalTo(dAtA []byte) (int, error) {
size := m.Size()
return m.MarshalToSizedBuffer(dAtA[:size])
}
func (m *AuthRoleRevokePermissionResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA)
_ = i
var l int
_ = l
if m.XXX_unrecognized != nil {
i -= len(m.XXX_unrecognized)
copy(dAtA[i:], m.XXX_unrecognized)
}
if m.Header != nil {
{
size, err := m.Header.MarshalToSizedBuffer(dAtA[:i])
if err != nil {
return 0, err
}
i -= size
i = encodeVarintRpc(dAtA, i, uint64(size))
}
i--
dAtA[i] = 0xa
}
return len(dAtA) - i, nil
}
func encodeVarintRpc(dAtA []byte, offset int, v uint64) int {
offset -= sovRpc(v)
base := offset
for v >= 1<<7 {
dAtA[offset] = uint8(v&0x7f | 0x80)
v >>= 7
offset++
}
dAtA[offset] = uint8(v)
return base
}
func (m *ResponseHeader) Size() (n int) {
if m == nil {
return 0
}
var l int
_ = l
if m.ClusterId != 0 {
n += 1 + sovRpc(uint64(m.ClusterId))
}
if m.MemberId != 0 {
n += 1 + sovRpc(uint64(m.MemberId))
}
if m.Revision != 0 {
n += 1 + sovRpc(uint64(m.Revision))
}
if m.RaftTerm != 0 {
n += 1 + sovRpc(uint64(m.RaftTerm))
}
if m.XXX_unrecognized != nil {
n += len(m.XXX_unrecognized)
}
return n
}
func (m *RangeRequest) Size() (n int) {
if m == nil {
return 0
}
var l int
_ = l
l = len(m.Key)
if l > 0 {
n += 1 + l + sovRpc(uint64(l))
}
l = len(m.RangeEnd)
if l > 0 {
n += 1 + l + sovRpc(uint64(l))
}
if m.Limit != 0 {
n += 1 + sovRpc(uint64(m.Limit))
}
if m.Revision != 0 {
n += 1 + sovRpc(uint64(m.Revision))
}
if m.SortOrder != 0 {
n += 1 + sovRpc(uint64(m.SortOrder))
}
if m.SortTarget != 0 {
n += 1 + sovRpc(uint64(m.SortTarget))
}
if m.Serializable {
n += 2
}
if m.KeysOnly {
n += 2
}
if m.CountOnly {
n += 2
}
if m.MinModRevision != 0 {
n += 1 + sovRpc(uint64(m.MinModRevision))
}
if m.MaxModRevision != 0 {
n += 1 + sovRpc(uint64(m.MaxModRevision))
}
if m.MinCreateRevision != 0 {
n += 1 + sovRpc(uint64(m.MinCreateRevision))
}
if m.MaxCreateRevision != 0 {
n += 1 + sovRpc(uint64(m.MaxCreateRevision))
}
if m.XXX_unrecognized != nil {
n += len(m.XXX_unrecognized)
}
return n
}
func (m *RangeResponse) Size() (n int) {
if m == nil {
return 0
}
var l int
_ = l
if m.Header != nil {
l = m.Header.Size()
n += 1 + l + sovRpc(uint64(l))
}
if len(m.Kvs) > 0 {
for _, e := range m.Kvs {
l = e.Size()
n += 1 + l + sovRpc(uint64(l))
}
}
if m.More {
n += 2
}
if m.Count != 0 {
n += 1 + sovRpc(uint64(m.Count))
}
if m.XXX_unrecognized != nil {
n += len(m.XXX_unrecognized)
}
return n
}
func (m *PutRequest) Size() (n int) {
if m == nil {
return 0
}
var l int
_ = l
l = len(m.Key)
if l > 0 {
n += 1 + l + sovRpc(uint64(l))
}
l = len(m.Value)
if l > 0 {
n += 1 + l + sovRpc(uint64(l))
}
if m.Lease != 0 {
n += 1 + sovRpc(uint64(m.Lease))
}
if m.PrevKv {
n += 2
}
if m.IgnoreValue {
n += 2
}
if m.IgnoreLease {
n += 2
}
if m.XXX_unrecognized != nil {
n += len(m.XXX_unrecognized)
}
return n
}
func (m *PutResponse) Size() (n int) {
if m == nil {
return 0
}
var l int
_ = l
if m.Header != nil {
l = m.Header.Size()
n += 1 + l + sovRpc(uint64(l))
}
if m.PrevKv != nil {
l = m.PrevKv.Size()
n += 1 + l + sovRpc(uint64(l))
}
if m.XXX_unrecognized != nil {
n += len(m.XXX_unrecognized)
}
return n
}
func (m *DeleteRangeRequest) Size() (n int) {
if m == nil {
return 0
}
var l int
_ = l
l = len(m.Key)
if l > 0 {
n += 1 + l + sovRpc(uint64(l))
}
l = len(m.RangeEnd)
if l > 0 {
n += 1 + l + sovRpc(uint64(l))
}
if m.PrevKv {
n += 2
}
if m.XXX_unrecognized != nil {
n += len(m.XXX_unrecognized)
}
return n
}
func (m *DeleteRangeResponse) Size() (n int) {
if m == nil {
return 0
}
var l int
_ = l
if m.Header != nil {
l = m.Header.Size()
n += 1 + l + sovRpc(uint64(l))
}
if m.Deleted != 0 {
n += 1 + sovRpc(uint64(m.Deleted))
}
if len(m.PrevKvs) > 0 {
for _, e := range m.PrevKvs {
l = e.Size()
n += 1 + l + sovRpc(uint64(l))
}
}
if m.XXX_unrecognized != nil {
n += len(m.XXX_unrecognized)
}
return n
}
func (m *RequestOp) Size() (n int) {
if m == nil {
return 0
}
var l int
_ = l
if m.Request != nil {
n += m.Request.Size()
}
if m.XXX_unrecognized != nil {
n += len(m.XXX_unrecognized)
}
return n
}
func (m *RequestOp_RequestRange) Size() (n int) {
if m == nil {
return 0
}
var l int
_ = l
if m.RequestRange != nil {
l = m.RequestRange.Size()
n += 1 + l + sovRpc(uint64(l))
}
return n
}
func (m *RequestOp_RequestPut) Size() (n int) {
if m == nil {
return 0
}
var l int
_ = l
if m.RequestPut != nil {
l = m.RequestPut.Size()
n += 1 + l + sovRpc(uint64(l))
}
return n
}
func (m *RequestOp_RequestDeleteRange) Size() (n int) {
if m == nil {
return 0
}
var l int
_ = l
if m.RequestDeleteRange != nil {
l = m.RequestDeleteRange.Size()
n += 1 + l + sovRpc(uint64(l))
}
return n
}
func (m *RequestOp_RequestTxn) Size() (n int) {
if m == nil {
return 0
}
var l int
_ = l
if m.RequestTxn != nil {
l = m.RequestTxn.Size()
n += 1 + l + sovRpc(uint64(l))
}
return n
}
func (m *ResponseOp) Size() (n int) {
if m == nil {
return 0
}
var l int
_ = l
if m.Response != nil {
n += m.Response.Size()
}
if m.XXX_unrecognized != nil {
n += len(m.XXX_unrecognized)
}
return n
}
func (m *ResponseOp_ResponseRange) Size() (n int) {
if m == nil {
return 0
}
var l int
_ = l
if m.ResponseRange != nil {
l = m.ResponseRange.Size()
n += 1 + l + sovRpc(uint64(l))
}
return n
}
func (m *ResponseOp_ResponsePut) Size() (n int) {
if m == nil {
return 0
}
var l int
_ = l
if m.ResponsePut != nil {
l = m.ResponsePut.Size()
n += 1 + l + sovRpc(uint64(l))
}
return n
}
func (m *ResponseOp_ResponseDeleteRange) Size() (n int) {
if m == nil {
return 0
}
var l int
_ = l
if m.ResponseDeleteRange != nil {
l = m.ResponseDeleteRange.Size()
n += 1 + l + sovRpc(uint64(l))
}
return n
}
func (m *ResponseOp_ResponseTxn) Size() (n int) {
if m == nil {
return 0
}
var l int
_ = l
if m.ResponseTxn != nil {
l = m.ResponseTxn.Size()
n += 1 + l + sovRpc(uint64(l))
}
return n
}
func (m *Compare) Size() (n int) {
if m == nil {
return 0
}
var l int
_ = l
if m.Result != 0 {
n += 1 + sovRpc(uint64(m.Result))
}
if m.Target != 0 {
n += 1 + sovRpc(uint64(m.Target))
}
l = len(m.Key)
if l > 0 {
n += 1 + l + sovRpc(uint64(l))
}
if m.TargetUnion != nil {
n += m.TargetUnion.Size()
}
l = len(m.RangeEnd)
if l > 0 {
n += 2 + l + sovRpc(uint64(l))
}
if m.XXX_unrecognized != nil {
n += len(m.XXX_unrecognized)
}
return n
}
func (m *Compare_Version) Size() (n int) {
if m == nil {
return 0
}
var l int
_ = l
n += 1 + sovRpc(uint64(m.Version))
return n
}
func (m *Compare_CreateRevision) Size() (n int) {
if m == nil {
return 0
}
var l int
_ = l
n += 1 + sovRpc(uint64(m.CreateRevision))
return n
}
func (m *Compare_ModRevision) Size() (n int) {
if m == nil {
return 0
}
var l int
_ = l
n += 1 + sovRpc(uint64(m.ModRevision))
return n
}
func (m *Compare_Value) Size() (n int) {
if m == nil {
return 0
}
var l int
_ = l
if m.Value != nil {
l = len(m.Value)
n += 1 + l + sovRpc(uint64(l))
}
return n
}
func (m *Compare_Lease) Size() (n int) {
if m == nil {
return 0
}
var l int
_ = l
n += 1 + sovRpc(uint64(m.Lease))
return n
}
func (m *TxnRequest) Size() (n int) {
if m == nil {
return 0
}
var l int
_ = l
if len(m.Compare) > 0 {
for _, e := range m.Compare {
l = e.Size()
n += 1 + l + sovRpc(uint64(l))
}
}
if len(m.Success) > 0 {
for _, e := range m.Success {
l = e.Size()
n += 1 + l + sovRpc(uint64(l))
}
}
if len(m.Failure) > 0 {
for _, e := range m.Failure {
l = e.Size()
n += 1 + l + sovRpc(uint64(l))
}
}
if m.XXX_unrecognized != nil {
n += len(m.XXX_unrecognized)
}
return n
}
func (m *TxnResponse) Size() (n int) {
if m == nil {
return 0
}
var l int
_ = l
if m.Header != nil {
l = m.Header.Size()
n += 1 + l + sovRpc(uint64(l))
}
if m.Succeeded {
n += 2
}
if len(m.Responses) > 0 {
for _, e := range m.Responses {
l = e.Size()
n += 1 + l + sovRpc(uint64(l))
}
}
if m.XXX_unrecognized != nil {
n += len(m.XXX_unrecognized)
}
return n
}
func (m *CompactionRequest) Size() (n int) {
if m == nil {
return 0
}
var l int
_ = l
if m.Revision != 0 {
n += 1 + sovRpc(uint64(m.Revision))
}
if m.Physical {
n += 2
}
if m.XXX_unrecognized != nil {
n += len(m.XXX_unrecognized)
}
return n
}
func (m *CompactionResponse) Size() (n int) {
if m == nil {
return 0
}
var l int
_ = l
if m.Header != nil {
l = m.Header.Size()
n += 1 + l + sovRpc(uint64(l))
}
if m.XXX_unrecognized != nil {
n += len(m.XXX_unrecognized)
}
return n
}
func (m *HashRequest) Size() (n int) {
if m == nil {
return 0
}
var l int
_ = l
if m.XXX_unrecognized != nil {
n += len(m.XXX_unrecognized)
}
return n
}
func (m *HashKVRequest) Size() (n int) {
if m == nil {
return 0
}
var l int
_ = l
if m.Revision != 0 {
n += 1 + sovRpc(uint64(m.Revision))
}
if m.XXX_unrecognized != nil {
n += len(m.XXX_unrecognized)
}
return n
}
func (m *HashKVResponse) Size() (n int) {
if m == nil {
return 0
}
var l int
_ = l
if m.Header != nil {
l = m.Header.Size()
n += 1 + l + sovRpc(uint64(l))
}
if m.Hash != 0 {
n += 1 + sovRpc(uint64(m.Hash))
}
if m.CompactRevision != 0 {
n += 1 + sovRpc(uint64(m.CompactRevision))
}
if m.HashRevision != 0 {
n += 1 + sovRpc(uint64(m.HashRevision))
}
if m.XXX_unrecognized != nil {
n += len(m.XXX_unrecognized)
}
return n
}
func (m *HashResponse) Size() (n int) {
if m == nil {
return 0
}
var l int
_ = l
if m.Header != nil {
l = m.Header.Size()
n += 1 + l + sovRpc(uint64(l))
}
if m.Hash != 0 {
n += 1 + sovRpc(uint64(m.Hash))
}
if m.XXX_unrecognized != nil {
n += len(m.XXX_unrecognized)
}
return n
}
func (m *SnapshotRequest) Size() (n int) {
if m == nil {
return 0
}
var l int
_ = l
if m.XXX_unrecognized != nil {
n += len(m.XXX_unrecognized)
}
return n
}
func (m *SnapshotResponse) Size() (n int) {
if m == nil {
return 0
}
var l int
_ = l
if m.Header != nil {
l = m.Header.Size()
n += 1 + l + sovRpc(uint64(l))
}
if m.RemainingBytes != 0 {
n += 1 + sovRpc(uint64(m.RemainingBytes))
}
l = len(m.Blob)
if l > 0 {
n += 1 + l + sovRpc(uint64(l))
}
l = len(m.Version)
if l > 0 {
n += 1 + l + sovRpc(uint64(l))
}
if m.XXX_unrecognized != nil {
n += len(m.XXX_unrecognized)
}
return n
}
func (m *WatchRequest) Size() (n int) {
if m == nil {
return 0
}
var l int
_ = l
if m.RequestUnion != nil {
n += m.RequestUnion.Size()
}
if m.XXX_unrecognized != nil {
n += len(m.XXX_unrecognized)
}
return n
}
func (m *WatchRequest_CreateRequest) Size() (n int) {
if m == nil {
return 0
}
var l int
_ = l
if m.CreateRequest != nil {
l = m.CreateRequest.Size()
n += 1 + l + sovRpc(uint64(l))
}
return n
}
func (m *WatchRequest_CancelRequest) Size() (n int) {
if m == nil {
return 0
}
var l int
_ = l
if m.CancelRequest != nil {
l = m.CancelRequest.Size()
n += 1 + l + sovRpc(uint64(l))
}
return n
}
func (m *WatchRequest_ProgressRequest) Size() (n int) {
if m == nil {
return 0
}
var l int
_ = l
if m.ProgressRequest != nil {
l = m.ProgressRequest.Size()
n += 1 + l + sovRpc(uint64(l))
}
return n
}
func (m *WatchCreateRequest) Size() (n int) {
if m == nil {
return 0
}
var l int
_ = l
l = len(m.Key)
if l > 0 {
n += 1 + l + sovRpc(uint64(l))
}
l = len(m.RangeEnd)
if l > 0 {
n += 1 + l + sovRpc(uint64(l))
}
if m.StartRevision != 0 {
n += 1 + sovRpc(uint64(m.StartRevision))
}
if m.ProgressNotify {
n += 2
}
if len(m.Filters) > 0 {
l = 0
for _, e := range m.Filters {
l += sovRpc(uint64(e))
}
n += 1 + sovRpc(uint64(l)) + l
}
if m.PrevKv {
n += 2
}
if m.WatchId != 0 {
n += 1 + sovRpc(uint64(m.WatchId))
}
if m.Fragment {
n += 2
}
if m.XXX_unrecognized != nil {
n += len(m.XXX_unrecognized)
}
return n
}
func (m *WatchCancelRequest) Size() (n int) {
if m == nil {
return 0
}
var l int
_ = l
if m.WatchId != 0 {
n += 1 + sovRpc(uint64(m.WatchId))
}
if m.XXX_unrecognized != nil {
n += len(m.XXX_unrecognized)
}
return n
}
func (m *WatchProgressRequest) Size() (n int) {
if m == nil {
return 0
}
var l int
_ = l
if m.XXX_unrecognized != nil {
n += len(m.XXX_unrecognized)
}
return n
}
func (m *WatchResponse) Size() (n int) {
if m == nil {
return 0
}
var l int
_ = l
if m.Header != nil {
l = m.Header.Size()
n += 1 + l + sovRpc(uint64(l))
}
if m.WatchId != 0 {
n += 1 + sovRpc(uint64(m.WatchId))
}
if m.Created {
n += 2
}
if m.Canceled {
n += 2
}
if m.CompactRevision != 0 {
n += 1 + sovRpc(uint64(m.CompactRevision))
}
l = len(m.CancelReason)
if l > 0 {
n += 1 + l + sovRpc(uint64(l))
}
if m.Fragment {
n += 2
}
if len(m.Events) > 0 {
for _, e := range m.Events {
l = e.Size()
n += 1 + l + sovRpc(uint64(l))
}
}
if m.XXX_unrecognized != nil {
n += len(m.XXX_unrecognized)
}
return n
}
func (m *LeaseGrantRequest) Size() (n int) {
if m == nil {
return 0
}
var l int
_ = l
if m.TTL != 0 {
n += 1 + sovRpc(uint64(m.TTL))
}
if m.ID != 0 {
n += 1 + sovRpc(uint64(m.ID))
}
if m.XXX_unrecognized != nil {
n += len(m.XXX_unrecognized)
}
return n
}
func (m *LeaseGrantResponse) Size() (n int) {
if m == nil {
return 0
}
var l int
_ = l
if m.Header != nil {
l = m.Header.Size()
n += 1 + l + sovRpc(uint64(l))
}
if m.ID != 0 {
n += 1 + sovRpc(uint64(m.ID))
}
if m.TTL != 0 {
n += 1 + sovRpc(uint64(m.TTL))
}
l = len(m.Error)
if l > 0 {
n += 1 + l + sovRpc(uint64(l))
}
if m.XXX_unrecognized != nil {
n += len(m.XXX_unrecognized)
}
return n
}
func (m *LeaseRevokeRequest) Size() (n int) {
if m == nil {
return 0
}
var l int
_ = l
if m.ID != 0 {
n += 1 + sovRpc(uint64(m.ID))
}
if m.XXX_unrecognized != nil {
n += len(m.XXX_unrecognized)
}
return n
}
func (m *LeaseRevokeResponse) Size() (n int) {
if m == nil {
return 0
}
var l int
_ = l
if m.Header != nil {
l = m.Header.Size()
n += 1 + l + sovRpc(uint64(l))
}
if m.XXX_unrecognized != nil {
n += len(m.XXX_unrecognized)
}
return n
}
func (m *LeaseCheckpoint) Size() (n int) {
if m == nil {
return 0
}
var l int
_ = l
if m.ID != 0 {
n += 1 + sovRpc(uint64(m.ID))
}
if m.Remaining_TTL != 0 {
n += 1 + sovRpc(uint64(m.Remaining_TTL))
}
if m.XXX_unrecognized != nil {
n += len(m.XXX_unrecognized)
}
return n
}
func (m *LeaseCheckpointRequest) Size() (n int) {
if m == nil {
return 0
}
var l int
_ = l
if len(m.Checkpoints) > 0 {
for _, e := range m.Checkpoints {
l = e.Size()
n += 1 + l + sovRpc(uint64(l))
}
}
if m.XXX_unrecognized != nil {
n += len(m.XXX_unrecognized)
}
return n
}
func (m *LeaseCheckpointResponse) Size() (n int) {
if m == nil {
return 0
}
var l int
_ = l
if m.Header != nil {
l = m.Header.Size()
n += 1 + l + sovRpc(uint64(l))
}
if m.XXX_unrecognized != nil {
n += len(m.XXX_unrecognized)
}
return n
}
func (m *LeaseKeepAliveRequest) Size() (n int) {
if m == nil {
return 0
}
var l int
_ = l
if m.ID != 0 {
n += 1 + sovRpc(uint64(m.ID))
}
if m.XXX_unrecognized != nil {
n += len(m.XXX_unrecognized)
}
return n
}
func (m *LeaseKeepAliveResponse) Size() (n int) {
if m == nil {
return 0
}
var l int
_ = l
if m.Header != nil {
l = m.Header.Size()
n += 1 + l + sovRpc(uint64(l))
}
if m.ID != 0 {
n += 1 + sovRpc(uint64(m.ID))
}
if m.TTL != 0 {
n += 1 + sovRpc(uint64(m.TTL))
}
if m.XXX_unrecognized != nil {
n += len(m.XXX_unrecognized)
}
return n
}
func (m *LeaseTimeToLiveRequest) Size() (n int) {
if m == nil {
return 0
}
var l int
_ = l
if m.ID != 0 {
n += 1 + sovRpc(uint64(m.ID))
}
if m.Keys {
n += 2
}
if m.XXX_unrecognized != nil {
n += len(m.XXX_unrecognized)
}
return n
}
func (m *LeaseTimeToLiveResponse) Size() (n int) {
if m == nil {
return 0
}
var l int
_ = l
if m.Header != nil {
l = m.Header.Size()
n += 1 + l + sovRpc(uint64(l))
}
if m.ID != 0 {
n += 1 + sovRpc(uint64(m.ID))
}
if m.TTL != 0 {
n += 1 + sovRpc(uint64(m.TTL))
}
if m.GrantedTTL != 0 {
n += 1 + sovRpc(uint64(m.GrantedTTL))
}
if len(m.Keys) > 0 {
for _, b := range m.Keys {
l = len(b)
n += 1 + l + sovRpc(uint64(l))
}
}
if m.XXX_unrecognized != nil {
n += len(m.XXX_unrecognized)
}
return n
}
func (m *LeaseLeasesRequest) Size() (n int) {
if m == nil {
return 0
}
var l int
_ = l
if m.XXX_unrecognized != nil {
n += len(m.XXX_unrecognized)
}
return n
}
func (m *LeaseStatus) Size() (n int) {
if m == nil {
return 0
}
var l int
_ = l
if m.ID != 0 {
n += 1 + sovRpc(uint64(m.ID))
}
if m.XXX_unrecognized != nil {
n += len(m.XXX_unrecognized)
}
return n
}
func (m *LeaseLeasesResponse) Size() (n int) {
if m == nil {
return 0
}
var l int
_ = l
if m.Header != nil {
l = m.Header.Size()
n += 1 + l + sovRpc(uint64(l))
}
if len(m.Leases) > 0 {
for _, e := range m.Leases {
l = e.Size()
n += 1 + l + sovRpc(uint64(l))
}
}
if m.XXX_unrecognized != nil {
n += len(m.XXX_unrecognized)
}
return n
}
func (m *Member) Size() (n int) {
if m == nil {
return 0
}
var l int
_ = l
if m.ID != 0 {
n += 1 + sovRpc(uint64(m.ID))
}
l = len(m.Name)
if l > 0 {
n += 1 + l + sovRpc(uint64(l))
}
if len(m.PeerURLs) > 0 {
for _, s := range m.PeerURLs {
l = len(s)
n += 1 + l + sovRpc(uint64(l))
}
}
if len(m.ClientURLs) > 0 {
for _, s := range m.ClientURLs {
l = len(s)
n += 1 + l + sovRpc(uint64(l))
}
}
if m.IsLearner {
n += 2
}
if m.XXX_unrecognized != nil {
n += len(m.XXX_unrecognized)
}
return n
}
func (m *MemberAddRequest) Size() (n int) {
if m == nil {
return 0
}
var l int
_ = l
if len(m.PeerURLs) > 0 {
for _, s := range m.PeerURLs {
l = len(s)
n += 1 + l + sovRpc(uint64(l))
}
}
if m.IsLearner {
n += 2
}
if m.XXX_unrecognized != nil {
n += len(m.XXX_unrecognized)
}
return n
}
func (m *MemberAddResponse) Size() (n int) {
if m == nil {
return 0
}
var l int
_ = l
if m.Header != nil {
l = m.Header.Size()
n += 1 + l + sovRpc(uint64(l))
}
if m.Member != nil {
l = m.Member.Size()
n += 1 + l + sovRpc(uint64(l))
}
if len(m.Members) > 0 {
for _, e := range m.Members {
l = e.Size()
n += 1 + l + sovRpc(uint64(l))
}
}
if m.XXX_unrecognized != nil {
n += len(m.XXX_unrecognized)
}
return n
}
func (m *MemberRemoveRequest) Size() (n int) {
if m == nil {
return 0
}
var l int
_ = l
if m.ID != 0 {
n += 1 + sovRpc(uint64(m.ID))
}
if m.XXX_unrecognized != nil {
n += len(m.XXX_unrecognized)
}
return n
}
func (m *MemberRemoveResponse) Size() (n int) {
if m == nil {
return 0
}
var l int
_ = l
if m.Header != nil {
l = m.Header.Size()
n += 1 + l + sovRpc(uint64(l))
}
if len(m.Members) > 0 {
for _, e := range m.Members {
l = e.Size()
n += 1 + l + sovRpc(uint64(l))
}
}
if m.XXX_unrecognized != nil {
n += len(m.XXX_unrecognized)
}
return n
}
func (m *MemberUpdateRequest) Size() (n int) {
if m == nil {
return 0
}
var l int
_ = l
if m.ID != 0 {
n += 1 + sovRpc(uint64(m.ID))
}
if len(m.PeerURLs) > 0 {
for _, s := range m.PeerURLs {
l = len(s)
n += 1 + l + sovRpc(uint64(l))
}
}
if m.XXX_unrecognized != nil {
n += len(m.XXX_unrecognized)
}
return n
}
func (m *MemberUpdateResponse) Size() (n int) {
if m == nil {
return 0
}
var l int
_ = l
if m.Header != nil {
l = m.Header.Size()
n += 1 + l + sovRpc(uint64(l))
}
if len(m.Members) > 0 {
for _, e := range m.Members {
l = e.Size()
n += 1 + l + sovRpc(uint64(l))
}
}
if m.XXX_unrecognized != nil {
n += len(m.XXX_unrecognized)
}
return n
}
func (m *MemberListRequest) Size() (n int) {
if m == nil {
return 0
}
var l int
_ = l
if m.Linearizable {
n += 2
}
if m.XXX_unrecognized != nil {
n += len(m.XXX_unrecognized)
}
return n
}
func (m *MemberListResponse) Size() (n int) {
if m == nil {
return 0
}
var l int
_ = l
if m.Header != nil {
l = m.Header.Size()
n += 1 + l + sovRpc(uint64(l))
}
if len(m.Members) > 0 {
for _, e := range m.Members {
l = e.Size()
n += 1 + l + sovRpc(uint64(l))
}
}
if m.XXX_unrecognized != nil {
n += len(m.XXX_unrecognized)
}
return n
}
func (m *MemberPromoteRequest) Size() (n int) {
if m == nil {
return 0
}
var l int
_ = l
if m.ID != 0 {
n += 1 + sovRpc(uint64(m.ID))
}
if m.XXX_unrecognized != nil {
n += len(m.XXX_unrecognized)
}
return n
}
func (m *MemberPromoteResponse) Size() (n int) {
if m == nil {
return 0
}
var l int
_ = l
if m.Header != nil {
l = m.Header.Size()
n += 1 + l + sovRpc(uint64(l))
}
if len(m.Members) > 0 {
for _, e := range m.Members {
l = e.Size()
n += 1 + l + sovRpc(uint64(l))
}
}
if m.XXX_unrecognized != nil {
n += len(m.XXX_unrecognized)
}
return n
}
func (m *DefragmentRequest) Size() (n int) {
if m == nil {
return 0
}
var l int
_ = l
if m.XXX_unrecognized != nil {
n += len(m.XXX_unrecognized)
}
return n
}
func (m *DefragmentResponse) Size() (n int) {
if m == nil {
return 0
}
var l int
_ = l
if m.Header != nil {
l = m.Header.Size()
n += 1 + l + sovRpc(uint64(l))
}
if m.XXX_unrecognized != nil {
n += len(m.XXX_unrecognized)
}
return n
}
func (m *MoveLeaderRequest) Size() (n int) {
if m == nil {
return 0
}
var l int
_ = l
if m.TargetID != 0 {
n += 1 + sovRpc(uint64(m.TargetID))
}
if m.XXX_unrecognized != nil {
n += len(m.XXX_unrecognized)
}
return n
}
func (m *MoveLeaderResponse) Size() (n int) {
if m == nil {
return 0
}
var l int
_ = l
if m.Header != nil {
l = m.Header.Size()
n += 1 + l + sovRpc(uint64(l))
}
if m.XXX_unrecognized != nil {
n += len(m.XXX_unrecognized)
}
return n
}
func (m *AlarmRequest) Size() (n int) {
if m == nil {
return 0
}
var l int
_ = l
if m.Action != 0 {
n += 1 + sovRpc(uint64(m.Action))
}
if m.MemberID != 0 {
n += 1 + sovRpc(uint64(m.MemberID))
}
if m.Alarm != 0 {
n += 1 + sovRpc(uint64(m.Alarm))
}
if m.XXX_unrecognized != nil {
n += len(m.XXX_unrecognized)
}
return n
}
func (m *AlarmMember) Size() (n int) {
if m == nil {
return 0
}
var l int
_ = l
if m.MemberID != 0 {
n += 1 + sovRpc(uint64(m.MemberID))
}
if m.Alarm != 0 {
n += 1 + sovRpc(uint64(m.Alarm))
}
if m.XXX_unrecognized != nil {
n += len(m.XXX_unrecognized)
}
return n
}
func (m *AlarmResponse) Size() (n int) {
if m == nil {
return 0
}
var l int
_ = l
if m.Header != nil {
l = m.Header.Size()
n += 1 + l + sovRpc(uint64(l))
}
if len(m.Alarms) > 0 {
for _, e := range m.Alarms {
l = e.Size()
n += 1 + l + sovRpc(uint64(l))
}
}
if m.XXX_unrecognized != nil {
n += len(m.XXX_unrecognized)
}
return n
}
func (m *DowngradeRequest) Size() (n int) {
if m == nil {
return 0
}
var l int
_ = l
if m.Action != 0 {
n += 1 + sovRpc(uint64(m.Action))
}
l = len(m.Version)
if l > 0 {
n += 1 + l + sovRpc(uint64(l))
}
if m.XXX_unrecognized != nil {
n += len(m.XXX_unrecognized)
}
return n
}
func (m *DowngradeResponse) Size() (n int) {
if m == nil {
return 0
}
var l int
_ = l
if m.Header != nil {
l = m.Header.Size()
n += 1 + l + sovRpc(uint64(l))
}
l = len(m.Version)
if l > 0 {
n += 1 + l + sovRpc(uint64(l))
}
if m.XXX_unrecognized != nil {
n += len(m.XXX_unrecognized)
}
return n
}
func (m *DowngradeVersionTestRequest) Size() (n int) {
if m == nil {
return 0
}
var l int
_ = l
l = len(m.Ver)
if l > 0 {
n += 1 + l + sovRpc(uint64(l))
}
if m.XXX_unrecognized != nil {
n += len(m.XXX_unrecognized)
}
return n
}
func (m *StatusRequest) Size() (n int) {
if m == nil {
return 0
}
var l int
_ = l
if m.XXX_unrecognized != nil {
n += len(m.XXX_unrecognized)
}
return n
}
func (m *StatusResponse) Size() (n int) {
if m == nil {
return 0
}
var l int
_ = l
if m.Header != nil {
l = m.Header.Size()
n += 1 + l + sovRpc(uint64(l))
}
l = len(m.Version)
if l > 0 {
n += 1 + l + sovRpc(uint64(l))
}
if m.DbSize != 0 {
n += 1 + sovRpc(uint64(m.DbSize))
}
if m.Leader != 0 {
n += 1 + sovRpc(uint64(m.Leader))
}
if m.RaftIndex != 0 {
n += 1 + sovRpc(uint64(m.RaftIndex))
}
if m.RaftTerm != 0 {
n += 1 + sovRpc(uint64(m.RaftTerm))
}
if m.RaftAppliedIndex != 0 {
n += 1 + sovRpc(uint64(m.RaftAppliedIndex))
}
if len(m.Errors) > 0 {
for _, s := range m.Errors {
l = len(s)
n += 1 + l + sovRpc(uint64(l))
}
}
if m.DbSizeInUse != 0 {
n += 1 + sovRpc(uint64(m.DbSizeInUse))
}
if m.IsLearner {
n += 2
}
l = len(m.StorageVersion)
if l > 0 {
n += 1 + l + sovRpc(uint64(l))
}
if m.DbSizeQuota != 0 {
n += 1 + sovRpc(uint64(m.DbSizeQuota))
}
if m.DowngradeInfo != nil {
l = m.DowngradeInfo.Size()
n += 1 + l + sovRpc(uint64(l))
}
if m.XXX_unrecognized != nil {
n += len(m.XXX_unrecognized)
}
return n
}
func (m *DowngradeInfo) Size() (n int) {
if m == nil {
return 0
}
var l int
_ = l
if m.Enabled {
n += 2
}
l = len(m.TargetVersion)
if l > 0 {
n += 1 + l + sovRpc(uint64(l))
}
if m.XXX_unrecognized != nil {
n += len(m.XXX_unrecognized)
}
return n
}
func (m *AuthEnableRequest) Size() (n int) {
if m == nil {
return 0
}
var l int
_ = l
if m.XXX_unrecognized != nil {
n += len(m.XXX_unrecognized)
}
return n
}
func (m *AuthDisableRequest) Size() (n int) {
if m == nil {
return 0
}
var l int
_ = l
if m.XXX_unrecognized != nil {
n += len(m.XXX_unrecognized)
}
return n
}
func (m *AuthStatusRequest) Size() (n int) {
if m == nil {
return 0
}
var l int
_ = l
if m.XXX_unrecognized != nil {
n += len(m.XXX_unrecognized)
}
return n
}
func (m *AuthenticateRequest) Size() (n int) {
if m == nil {
return 0
}
var l int
_ = l
l = len(m.Name)
if l > 0 {
n += 1 + l + sovRpc(uint64(l))
}
l = len(m.Password)
if l > 0 {
n += 1 + l + sovRpc(uint64(l))
}
if m.XXX_unrecognized != nil {
n += len(m.XXX_unrecognized)
}
return n
}
func (m *AuthUserAddRequest) Size() (n int) {
if m == nil {
return 0
}
var l int
_ = l
l = len(m.Name)
if l > 0 {
n += 1 + l + sovRpc(uint64(l))
}
l = len(m.Password)
if l > 0 {
n += 1 + l + sovRpc(uint64(l))
}
if m.Options != nil {
l = m.Options.Size()
n += 1 + l + sovRpc(uint64(l))
}
l = len(m.HashedPassword)
if l > 0 {
n += 1 + l + sovRpc(uint64(l))
}
if m.XXX_unrecognized != nil {
n += len(m.XXX_unrecognized)
}
return n
}
func (m *AuthUserGetRequest) Size() (n int) {
if m == nil {
return 0
}
var l int
_ = l
l = len(m.Name)
if l > 0 {
n += 1 + l + sovRpc(uint64(l))
}
if m.XXX_unrecognized != nil {
n += len(m.XXX_unrecognized)
}
return n
}
func (m *AuthUserDeleteRequest) Size() (n int) {
if m == nil {
return 0
}
var l int
_ = l
l = len(m.Name)
if l > 0 {
n += 1 + l + sovRpc(uint64(l))
}
if m.XXX_unrecognized != nil {
n += len(m.XXX_unrecognized)
}
return n
}
func (m *AuthUserChangePasswordRequest) Size() (n int) {
if m == nil {
return 0
}
var l int
_ = l
l = len(m.Name)
if l > 0 {
n += 1 + l + sovRpc(uint64(l))
}
l = len(m.Password)
if l > 0 {
n += 1 + l + sovRpc(uint64(l))
}
l = len(m.HashedPassword)
if l > 0 {
n += 1 + l + sovRpc(uint64(l))
}
if m.XXX_unrecognized != nil {
n += len(m.XXX_unrecognized)
}
return n
}
func (m *AuthUserGrantRoleRequest) Size() (n int) {
if m == nil {
return 0
}
var l int
_ = l
l = len(m.User)
if l > 0 {
n += 1 + l + sovRpc(uint64(l))
}
l = len(m.Role)
if l > 0 {
n += 1 + l + sovRpc(uint64(l))
}
if m.XXX_unrecognized != nil {
n += len(m.XXX_unrecognized)
}
return n
}
func (m *AuthUserRevokeRoleRequest) Size() (n int) {
if m == nil {
return 0
}
var l int
_ = l
l = len(m.Name)
if l > 0 {
n += 1 + l + sovRpc(uint64(l))
}
l = len(m.Role)
if l > 0 {
n += 1 + l + sovRpc(uint64(l))
}
if m.XXX_unrecognized != nil {
n += len(m.XXX_unrecognized)
}
return n
}
func (m *AuthRoleAddRequest) Size() (n int) {
if m == nil {
return 0
}
var l int
_ = l
l = len(m.Name)
if l > 0 {
n += 1 + l + sovRpc(uint64(l))
}
if m.XXX_unrecognized != nil {
n += len(m.XXX_unrecognized)
}
return n
}
func (m *AuthRoleGetRequest) Size() (n int) {
if m == nil {
return 0
}
var l int
_ = l
l = len(m.Role)
if l > 0 {
n += 1 + l + sovRpc(uint64(l))
}
if m.XXX_unrecognized != nil {
n += len(m.XXX_unrecognized)
}
return n
}
func (m *AuthUserListRequest) Size() (n int) {
if m == nil {
return 0
}
var l int
_ = l
if m.XXX_unrecognized != nil {
n += len(m.XXX_unrecognized)
}
return n
}
func (m *AuthRoleListRequest) Size() (n int) {
if m == nil {
return 0
}
var l int
_ = l
if m.XXX_unrecognized != nil {
n += len(m.XXX_unrecognized)
}
return n
}
func (m *AuthRoleDeleteRequest) Size() (n int) {
if m == nil {
return 0
}
var l int
_ = l
l = len(m.Role)
if l > 0 {
n += 1 + l + sovRpc(uint64(l))
}
if m.XXX_unrecognized != nil {
n += len(m.XXX_unrecognized)
}
return n
}
func (m *AuthRoleGrantPermissionRequest) Size() (n int) {
if m == nil {
return 0
}
var l int
_ = l
l = len(m.Name)
if l > 0 {
n += 1 + l + sovRpc(uint64(l))
}
if m.Perm != nil {
l = m.Perm.Size()
n += 1 + l + sovRpc(uint64(l))
}
if m.XXX_unrecognized != nil {
n += len(m.XXX_unrecognized)
}
return n
}
func (m *AuthRoleRevokePermissionRequest) Size() (n int) {
if m == nil {
return 0
}
var l int
_ = l
l = len(m.Role)
if l > 0 {
n += 1 + l + sovRpc(uint64(l))
}
l = len(m.Key)
if l > 0 {
n += 1 + l + sovRpc(uint64(l))
}
l = len(m.RangeEnd)
if l > 0 {
n += 1 + l + sovRpc(uint64(l))
}
if m.XXX_unrecognized != nil {
n += len(m.XXX_unrecognized)
}
return n
}
func (m *AuthEnableResponse) Size() (n int) {
if m == nil {
return 0
}
var l int
_ = l
if m.Header != nil {
l = m.Header.Size()
n += 1 + l + sovRpc(uint64(l))
}
if m.XXX_unrecognized != nil {
n += len(m.XXX_unrecognized)
}
return n
}
func (m *AuthDisableResponse) Size() (n int) {
if m == nil {
return 0
}
var l int
_ = l
if m.Header != nil {
l = m.Header.Size()
n += 1 + l + sovRpc(uint64(l))
}
if m.XXX_unrecognized != nil {
n += len(m.XXX_unrecognized)
}
return n
}
func (m *AuthStatusResponse) Size() (n int) {
if m == nil {
return 0
}
var l int
_ = l
if m.Header != nil {
l = m.Header.Size()
n += 1 + l + sovRpc(uint64(l))
}
if m.Enabled {
n += 2
}
if m.AuthRevision != 0 {
n += 1 + sovRpc(uint64(m.AuthRevision))
}
if m.XXX_unrecognized != nil {
n += len(m.XXX_unrecognized)
}
return n
}
func (m *AuthenticateResponse) Size() (n int) {
if m == nil {
return 0
}
var l int
_ = l
if m.Header != nil {
l = m.Header.Size()
n += 1 + l + sovRpc(uint64(l))
}
l = len(m.Token)
if l > 0 {
n += 1 + l + sovRpc(uint64(l))
}
if m.XXX_unrecognized != nil {
n += len(m.XXX_unrecognized)
}
return n
}
func (m *AuthUserAddResponse) Size() (n int) {
if m == nil {
return 0
}
var l int
_ = l
if m.Header != nil {
l = m.Header.Size()
n += 1 + l + sovRpc(uint64(l))
}
if m.XXX_unrecognized != nil {
n += len(m.XXX_unrecognized)
}
return n
}
func (m *AuthUserGetResponse) Size() (n int) {
if m == nil {
return 0
}
var l int
_ = l
if m.Header != nil {
l = m.Header.Size()
n += 1 + l + sovRpc(uint64(l))
}
if len(m.Roles) > 0 {
for _, s := range m.Roles {
l = len(s)
n += 1 + l + sovRpc(uint64(l))
}
}
if m.XXX_unrecognized != nil {
n += len(m.XXX_unrecognized)
}
return n
}
func (m *AuthUserDeleteResponse) Size() (n int) {
if m == nil {
return 0
}
var l int
_ = l
if m.Header != nil {
l = m.Header.Size()
n += 1 + l + sovRpc(uint64(l))
}
if m.XXX_unrecognized != nil {
n += len(m.XXX_unrecognized)
}
return n
}
func (m *AuthUserChangePasswordResponse) Size() (n int) {
if m == nil {
return 0
}
var l int
_ = l
if m.Header != nil {
l = m.Header.Size()
n += 1 + l + sovRpc(uint64(l))
}
if m.XXX_unrecognized != nil {
n += len(m.XXX_unrecognized)
}
return n
}
func (m *AuthUserGrantRoleResponse) Size() (n int) {
if m == nil {
return 0
}
var l int
_ = l
if m.Header != nil {
l = m.Header.Size()
n += 1 + l + sovRpc(uint64(l))
}
if m.XXX_unrecognized != nil {
n += len(m.XXX_unrecognized)
}
return n
}
func (m *AuthUserRevokeRoleResponse) Size() (n int) {
if m == nil {
return 0
}
var l int
_ = l
if m.Header != nil {
l = m.Header.Size()
n += 1 + l + sovRpc(uint64(l))
}
if m.XXX_unrecognized != nil {
n += len(m.XXX_unrecognized)
}
return n
}
func (m *AuthRoleAddResponse) Size() (n int) {
if m == nil {
return 0
}
var l int
_ = l
if m.Header != nil {
l = m.Header.Size()
n += 1 + l + sovRpc(uint64(l))
}
if m.XXX_unrecognized != nil {
n += len(m.XXX_unrecognized)
}
return n
}
func (m *AuthRoleGetResponse) Size() (n int) {
if m == nil {
return 0
}
var l int
_ = l
if m.Header != nil {
l = m.Header.Size()
n += 1 + l + sovRpc(uint64(l))
}
if len(m.Perm) > 0 {
for _, e := range m.Perm {
l = e.Size()
n += 1 + l + sovRpc(uint64(l))
}
}
if m.XXX_unrecognized != nil {
n += len(m.XXX_unrecognized)
}
return n
}
func (m *AuthRoleListResponse) Size() (n int) {
if m == nil {
return 0
}
var l int
_ = l
if m.Header != nil {
l = m.Header.Size()
n += 1 + l + sovRpc(uint64(l))
}
if len(m.Roles) > 0 {
for _, s := range m.Roles {
l = len(s)
n += 1 + l + sovRpc(uint64(l))
}
}
if m.XXX_unrecognized != nil {
n += len(m.XXX_unrecognized)
}
return n
}
func (m *AuthUserListResponse) Size() (n int) {
if m == nil {
return 0
}
var l int
_ = l
if m.Header != nil {
l = m.Header.Size()
n += 1 + l + sovRpc(uint64(l))
}
if len(m.Users) > 0 {
for _, s := range m.Users {
l = len(s)
n += 1 + l + sovRpc(uint64(l))
}
}
if m.XXX_unrecognized != nil {
n += len(m.XXX_unrecognized)
}
return n
}
func (m *AuthRoleDeleteResponse) Size() (n int) {
if m == nil {
return 0
}
var l int
_ = l
if m.Header != nil {
l = m.Header.Size()
n += 1 + l + sovRpc(uint64(l))
}
if m.XXX_unrecognized != nil {
n += len(m.XXX_unrecognized)
}
return n
}
func (m *AuthRoleGrantPermissionResponse) Size() (n int) {
if m == nil {
return 0
}
var l int
_ = l
if m.Header != nil {
l = m.Header.Size()
n += 1 + l + sovRpc(uint64(l))
}
if m.XXX_unrecognized != nil {
n += len(m.XXX_unrecognized)
}
return n
}
func (m *AuthRoleRevokePermissionResponse) Size() (n int) {
if m == nil {
return 0
}
var l int
_ = l
if m.Header != nil {
l = m.Header.Size()
n += 1 + l + sovRpc(uint64(l))
}
if m.XXX_unrecognized != nil {
n += len(m.XXX_unrecognized)
}
return n
}
func sovRpc(x uint64) (n int) {
return (math_bits.Len64(x|1) + 6) / 7
}
func sozRpc(x uint64) (n int) {
return sovRpc(uint64((x << 1) ^ uint64((int64(x) >> 63))))
}
func (m *ResponseHeader) Unmarshal(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0
for iNdEx < l {
preIndex := iNdEx
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
fieldNum := int32(wire >> 3)
wireType := int(wire & 0x7)
if wireType == 4 {
return fmt.Errorf("proto: ResponseHeader: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: ResponseHeader: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
case 1:
if wireType != 0 {
return fmt.Errorf("proto: wrong wireType = %d for field ClusterId", wireType)
}
m.ClusterId = 0
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
m.ClusterId |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
case 2:
if wireType != 0 {
return fmt.Errorf("proto: wrong wireType = %d for field MemberId", wireType)
}
m.MemberId = 0
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
m.MemberId |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
case 3:
if wireType != 0 {
return fmt.Errorf("proto: wrong wireType = %d for field Revision", wireType)
}
m.Revision = 0
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
m.Revision |= int64(b&0x7F) << shift
if b < 0x80 {
break
}
}
case 4:
if wireType != 0 {
return fmt.Errorf("proto: wrong wireType = %d for field RaftTerm", wireType)
}
m.RaftTerm = 0
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
m.RaftTerm |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
default:
iNdEx = preIndex
skippy, err := skipRpc(dAtA[iNdEx:])
if err != nil {
return err
}
if (skippy < 0) || (iNdEx+skippy) < 0 {
return ErrInvalidLengthRpc
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
iNdEx += skippy
}
}
if iNdEx > l {
return io.ErrUnexpectedEOF
}
return nil
}
func (m *RangeRequest) Unmarshal(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0
for iNdEx < l {
preIndex := iNdEx
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
fieldNum := int32(wire >> 3)
wireType := int(wire & 0x7)
if wireType == 4 {
return fmt.Errorf("proto: RangeRequest: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: RangeRequest: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
case 1:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Key", wireType)
}
var byteLen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
byteLen |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
if byteLen < 0 {
return ErrInvalidLengthRpc
}
postIndex := iNdEx + byteLen
if postIndex < 0 {
return ErrInvalidLengthRpc
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.Key = append(m.Key[:0], dAtA[iNdEx:postIndex]...)
if m.Key == nil {
m.Key = []byte{}
}
iNdEx = postIndex
case 2:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field RangeEnd", wireType)
}
var byteLen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
byteLen |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
if byteLen < 0 {
return ErrInvalidLengthRpc
}
postIndex := iNdEx + byteLen
if postIndex < 0 {
return ErrInvalidLengthRpc
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.RangeEnd = append(m.RangeEnd[:0], dAtA[iNdEx:postIndex]...)
if m.RangeEnd == nil {
m.RangeEnd = []byte{}
}
iNdEx = postIndex
case 3:
if wireType != 0 {
return fmt.Errorf("proto: wrong wireType = %d for field Limit", wireType)
}
m.Limit = 0
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
m.Limit |= int64(b&0x7F) << shift
if b < 0x80 {
break
}
}
case 4:
if wireType != 0 {
return fmt.Errorf("proto: wrong wireType = %d for field Revision", wireType)
}
m.Revision = 0
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
m.Revision |= int64(b&0x7F) << shift
if b < 0x80 {
break
}
}
case 5:
if wireType != 0 {
return fmt.Errorf("proto: wrong wireType = %d for field SortOrder", wireType)
}
m.SortOrder = 0
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
m.SortOrder |= RangeRequest_SortOrder(b&0x7F) << shift
if b < 0x80 {
break
}
}
case 6:
if wireType != 0 {
return fmt.Errorf("proto: wrong wireType = %d for field SortTarget", wireType)
}
m.SortTarget = 0
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
m.SortTarget |= RangeRequest_SortTarget(b&0x7F) << shift
if b < 0x80 {
break
}
}
case 7:
if wireType != 0 {
return fmt.Errorf("proto: wrong wireType = %d for field Serializable", wireType)
}
var v int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
v |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
m.Serializable = bool(v != 0)
case 8:
if wireType != 0 {
return fmt.Errorf("proto: wrong wireType = %d for field KeysOnly", wireType)
}
var v int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
v |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
m.KeysOnly = bool(v != 0)
case 9:
if wireType != 0 {
return fmt.Errorf("proto: wrong wireType = %d for field CountOnly", wireType)
}
var v int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
v |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
m.CountOnly = bool(v != 0)
case 10:
if wireType != 0 {
return fmt.Errorf("proto: wrong wireType = %d for field MinModRevision", wireType)
}
m.MinModRevision = 0
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
m.MinModRevision |= int64(b&0x7F) << shift
if b < 0x80 {
break
}
}
case 11:
if wireType != 0 {
return fmt.Errorf("proto: wrong wireType = %d for field MaxModRevision", wireType)
}
m.MaxModRevision = 0
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
m.MaxModRevision |= int64(b&0x7F) << shift
if b < 0x80 {
break
}
}
case 12:
if wireType != 0 {
return fmt.Errorf("proto: wrong wireType = %d for field MinCreateRevision", wireType)
}
m.MinCreateRevision = 0
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
m.MinCreateRevision |= int64(b&0x7F) << shift
if b < 0x80 {
break
}
}
case 13:
if wireType != 0 {
return fmt.Errorf("proto: wrong wireType = %d for field MaxCreateRevision", wireType)
}
m.MaxCreateRevision = 0
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
m.MaxCreateRevision |= int64(b&0x7F) << shift
if b < 0x80 {
break
}
}
default:
iNdEx = preIndex
skippy, err := skipRpc(dAtA[iNdEx:])
if err != nil {
return err
}
if (skippy < 0) || (iNdEx+skippy) < 0 {
return ErrInvalidLengthRpc
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
iNdEx += skippy
}
}
if iNdEx > l {
return io.ErrUnexpectedEOF
}
return nil
}
func (m *RangeResponse) Unmarshal(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0
for iNdEx < l {
preIndex := iNdEx
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
fieldNum := int32(wire >> 3)
wireType := int(wire & 0x7)
if wireType == 4 {
return fmt.Errorf("proto: RangeResponse: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: RangeResponse: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
case 1:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Header", wireType)
}
var msglen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
msglen |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
if msglen < 0 {
return ErrInvalidLengthRpc
}
postIndex := iNdEx + msglen
if postIndex < 0 {
return ErrInvalidLengthRpc
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
if m.Header == nil {
m.Header = &ResponseHeader{}
}
if err := m.Header.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
return err
}
iNdEx = postIndex
case 2:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Kvs", wireType)
}
var msglen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
msglen |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
if msglen < 0 {
return ErrInvalidLengthRpc
}
postIndex := iNdEx + msglen
if postIndex < 0 {
return ErrInvalidLengthRpc
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.Kvs = append(m.Kvs, &mvccpb.KeyValue{})
if err := m.Kvs[len(m.Kvs)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
return err
}
iNdEx = postIndex
case 3:
if wireType != 0 {
return fmt.Errorf("proto: wrong wireType = %d for field More", wireType)
}
var v int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
v |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
m.More = bool(v != 0)
case 4:
if wireType != 0 {
return fmt.Errorf("proto: wrong wireType = %d for field Count", wireType)
}
m.Count = 0
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
m.Count |= int64(b&0x7F) << shift
if b < 0x80 {
break
}
}
default:
iNdEx = preIndex
skippy, err := skipRpc(dAtA[iNdEx:])
if err != nil {
return err
}
if (skippy < 0) || (iNdEx+skippy) < 0 {
return ErrInvalidLengthRpc
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
iNdEx += skippy
}
}
if iNdEx > l {
return io.ErrUnexpectedEOF
}
return nil
}
func (m *PutRequest) Unmarshal(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0
for iNdEx < l {
preIndex := iNdEx
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
fieldNum := int32(wire >> 3)
wireType := int(wire & 0x7)
if wireType == 4 {
return fmt.Errorf("proto: PutRequest: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: PutRequest: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
case 1:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Key", wireType)
}
var byteLen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
byteLen |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
if byteLen < 0 {
return ErrInvalidLengthRpc
}
postIndex := iNdEx + byteLen
if postIndex < 0 {
return ErrInvalidLengthRpc
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.Key = append(m.Key[:0], dAtA[iNdEx:postIndex]...)
if m.Key == nil {
m.Key = []byte{}
}
iNdEx = postIndex
case 2:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Value", wireType)
}
var byteLen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
byteLen |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
if byteLen < 0 {
return ErrInvalidLengthRpc
}
postIndex := iNdEx + byteLen
if postIndex < 0 {
return ErrInvalidLengthRpc
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.Value = append(m.Value[:0], dAtA[iNdEx:postIndex]...)
if m.Value == nil {
m.Value = []byte{}
}
iNdEx = postIndex
case 3:
if wireType != 0 {
return fmt.Errorf("proto: wrong wireType = %d for field Lease", wireType)
}
m.Lease = 0
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
m.Lease |= int64(b&0x7F) << shift
if b < 0x80 {
break
}
}
case 4:
if wireType != 0 {
return fmt.Errorf("proto: wrong wireType = %d for field PrevKv", wireType)
}
var v int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
v |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
m.PrevKv = bool(v != 0)
case 5:
if wireType != 0 {
return fmt.Errorf("proto: wrong wireType = %d for field IgnoreValue", wireType)
}
var v int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
v |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
m.IgnoreValue = bool(v != 0)
case 6:
if wireType != 0 {
return fmt.Errorf("proto: wrong wireType = %d for field IgnoreLease", wireType)
}
var v int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
v |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
m.IgnoreLease = bool(v != 0)
default:
iNdEx = preIndex
skippy, err := skipRpc(dAtA[iNdEx:])
if err != nil {
return err
}
if (skippy < 0) || (iNdEx+skippy) < 0 {
return ErrInvalidLengthRpc
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
iNdEx += skippy
}
}
if iNdEx > l {
return io.ErrUnexpectedEOF
}
return nil
}
func (m *PutResponse) Unmarshal(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0
for iNdEx < l {
preIndex := iNdEx
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
fieldNum := int32(wire >> 3)
wireType := int(wire & 0x7)
if wireType == 4 {
return fmt.Errorf("proto: PutResponse: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: PutResponse: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
case 1:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Header", wireType)
}
var msglen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
msglen |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
if msglen < 0 {
return ErrInvalidLengthRpc
}
postIndex := iNdEx + msglen
if postIndex < 0 {
return ErrInvalidLengthRpc
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
if m.Header == nil {
m.Header = &ResponseHeader{}
}
if err := m.Header.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
return err
}
iNdEx = postIndex
case 2:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field PrevKv", wireType)
}
var msglen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
msglen |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
if msglen < 0 {
return ErrInvalidLengthRpc
}
postIndex := iNdEx + msglen
if postIndex < 0 {
return ErrInvalidLengthRpc
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
if m.PrevKv == nil {
m.PrevKv = &mvccpb.KeyValue{}
}
if err := m.PrevKv.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
return err
}
iNdEx = postIndex
default:
iNdEx = preIndex
skippy, err := skipRpc(dAtA[iNdEx:])
if err != nil {
return err
}
if (skippy < 0) || (iNdEx+skippy) < 0 {
return ErrInvalidLengthRpc
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
iNdEx += skippy
}
}
if iNdEx > l {
return io.ErrUnexpectedEOF
}
return nil
}
func (m *DeleteRangeRequest) Unmarshal(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0
for iNdEx < l {
preIndex := iNdEx
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
fieldNum := int32(wire >> 3)
wireType := int(wire & 0x7)
if wireType == 4 {
return fmt.Errorf("proto: DeleteRangeRequest: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: DeleteRangeRequest: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
case 1:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Key", wireType)
}
var byteLen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
byteLen |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
if byteLen < 0 {
return ErrInvalidLengthRpc
}
postIndex := iNdEx + byteLen
if postIndex < 0 {
return ErrInvalidLengthRpc
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.Key = append(m.Key[:0], dAtA[iNdEx:postIndex]...)
if m.Key == nil {
m.Key = []byte{}
}
iNdEx = postIndex
case 2:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field RangeEnd", wireType)
}
var byteLen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
byteLen |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
if byteLen < 0 {
return ErrInvalidLengthRpc
}
postIndex := iNdEx + byteLen
if postIndex < 0 {
return ErrInvalidLengthRpc
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.RangeEnd = append(m.RangeEnd[:0], dAtA[iNdEx:postIndex]...)
if m.RangeEnd == nil {
m.RangeEnd = []byte{}
}
iNdEx = postIndex
case 3:
if wireType != 0 {
return fmt.Errorf("proto: wrong wireType = %d for field PrevKv", wireType)
}
var v int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
v |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
m.PrevKv = bool(v != 0)
default:
iNdEx = preIndex
skippy, err := skipRpc(dAtA[iNdEx:])
if err != nil {
return err
}
if (skippy < 0) || (iNdEx+skippy) < 0 {
return ErrInvalidLengthRpc
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
iNdEx += skippy
}
}
if iNdEx > l {
return io.ErrUnexpectedEOF
}
return nil
}
func (m *DeleteRangeResponse) Unmarshal(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0
for iNdEx < l {
preIndex := iNdEx
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
fieldNum := int32(wire >> 3)
wireType := int(wire & 0x7)
if wireType == 4 {
return fmt.Errorf("proto: DeleteRangeResponse: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: DeleteRangeResponse: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
case 1:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Header", wireType)
}
var msglen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
msglen |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
if msglen < 0 {
return ErrInvalidLengthRpc
}
postIndex := iNdEx + msglen
if postIndex < 0 {
return ErrInvalidLengthRpc
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
if m.Header == nil {
m.Header = &ResponseHeader{}
}
if err := m.Header.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
return err
}
iNdEx = postIndex
case 2:
if wireType != 0 {
return fmt.Errorf("proto: wrong wireType = %d for field Deleted", wireType)
}
m.Deleted = 0
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
m.Deleted |= int64(b&0x7F) << shift
if b < 0x80 {
break
}
}
case 3:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field PrevKvs", wireType)
}
var msglen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
msglen |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
if msglen < 0 {
return ErrInvalidLengthRpc
}
postIndex := iNdEx + msglen
if postIndex < 0 {
return ErrInvalidLengthRpc
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.PrevKvs = append(m.PrevKvs, &mvccpb.KeyValue{})
if err := m.PrevKvs[len(m.PrevKvs)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
return err
}
iNdEx = postIndex
default:
iNdEx = preIndex
skippy, err := skipRpc(dAtA[iNdEx:])
if err != nil {
return err
}
if (skippy < 0) || (iNdEx+skippy) < 0 {
return ErrInvalidLengthRpc
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
iNdEx += skippy
}
}
if iNdEx > l {
return io.ErrUnexpectedEOF
}
return nil
}
func (m *RequestOp) Unmarshal(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0
for iNdEx < l {
preIndex := iNdEx
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
fieldNum := int32(wire >> 3)
wireType := int(wire & 0x7)
if wireType == 4 {
return fmt.Errorf("proto: RequestOp: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: RequestOp: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
case 1:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field RequestRange", wireType)
}
var msglen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
msglen |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
if msglen < 0 {
return ErrInvalidLengthRpc
}
postIndex := iNdEx + msglen
if postIndex < 0 {
return ErrInvalidLengthRpc
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
v := &RangeRequest{}
if err := v.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
return err
}
m.Request = &RequestOp_RequestRange{v}
iNdEx = postIndex
case 2:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field RequestPut", wireType)
}
var msglen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
msglen |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
if msglen < 0 {
return ErrInvalidLengthRpc
}
postIndex := iNdEx + msglen
if postIndex < 0 {
return ErrInvalidLengthRpc
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
v := &PutRequest{}
if err := v.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
return err
}
m.Request = &RequestOp_RequestPut{v}
iNdEx = postIndex
case 3:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field RequestDeleteRange", wireType)
}
var msglen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
msglen |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
if msglen < 0 {
return ErrInvalidLengthRpc
}
postIndex := iNdEx + msglen
if postIndex < 0 {
return ErrInvalidLengthRpc
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
v := &DeleteRangeRequest{}
if err := v.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
return err
}
m.Request = &RequestOp_RequestDeleteRange{v}
iNdEx = postIndex
case 4:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field RequestTxn", wireType)
}
var msglen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
msglen |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
if msglen < 0 {
return ErrInvalidLengthRpc
}
postIndex := iNdEx + msglen
if postIndex < 0 {
return ErrInvalidLengthRpc
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
v := &TxnRequest{}
if err := v.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
return err
}
m.Request = &RequestOp_RequestTxn{v}
iNdEx = postIndex
default:
iNdEx = preIndex
skippy, err := skipRpc(dAtA[iNdEx:])
if err != nil {
return err
}
if (skippy < 0) || (iNdEx+skippy) < 0 {
return ErrInvalidLengthRpc
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
iNdEx += skippy
}
}
if iNdEx > l {
return io.ErrUnexpectedEOF
}
return nil
}
func (m *ResponseOp) Unmarshal(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0
for iNdEx < l {
preIndex := iNdEx
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
fieldNum := int32(wire >> 3)
wireType := int(wire & 0x7)
if wireType == 4 {
return fmt.Errorf("proto: ResponseOp: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: ResponseOp: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
case 1:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field ResponseRange", wireType)
}
var msglen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
msglen |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
if msglen < 0 {
return ErrInvalidLengthRpc
}
postIndex := iNdEx + msglen
if postIndex < 0 {
return ErrInvalidLengthRpc
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
v := &RangeResponse{}
if err := v.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
return err
}
m.Response = &ResponseOp_ResponseRange{v}
iNdEx = postIndex
case 2:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field ResponsePut", wireType)
}
var msglen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
msglen |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
if msglen < 0 {
return ErrInvalidLengthRpc
}
postIndex := iNdEx + msglen
if postIndex < 0 {
return ErrInvalidLengthRpc
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
v := &PutResponse{}
if err := v.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
return err
}
m.Response = &ResponseOp_ResponsePut{v}
iNdEx = postIndex
case 3:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field ResponseDeleteRange", wireType)
}
var msglen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
msglen |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
if msglen < 0 {
return ErrInvalidLengthRpc
}
postIndex := iNdEx + msglen
if postIndex < 0 {
return ErrInvalidLengthRpc
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
v := &DeleteRangeResponse{}
if err := v.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
return err
}
m.Response = &ResponseOp_ResponseDeleteRange{v}
iNdEx = postIndex
case 4:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field ResponseTxn", wireType)
}
var msglen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
msglen |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
if msglen < 0 {
return ErrInvalidLengthRpc
}
postIndex := iNdEx + msglen
if postIndex < 0 {
return ErrInvalidLengthRpc
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
v := &TxnResponse{}
if err := v.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
return err
}
m.Response = &ResponseOp_ResponseTxn{v}
iNdEx = postIndex
default:
iNdEx = preIndex
skippy, err := skipRpc(dAtA[iNdEx:])
if err != nil {
return err
}
if (skippy < 0) || (iNdEx+skippy) < 0 {
return ErrInvalidLengthRpc
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
iNdEx += skippy
}
}
if iNdEx > l {
return io.ErrUnexpectedEOF
}
return nil
}
func (m *Compare) Unmarshal(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0
for iNdEx < l {
preIndex := iNdEx
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
fieldNum := int32(wire >> 3)
wireType := int(wire & 0x7)
if wireType == 4 {
return fmt.Errorf("proto: Compare: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: Compare: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
case 1:
if wireType != 0 {
return fmt.Errorf("proto: wrong wireType = %d for field Result", wireType)
}
m.Result = 0
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
m.Result |= Compare_CompareResult(b&0x7F) << shift
if b < 0x80 {
break
}
}
case 2:
if wireType != 0 {
return fmt.Errorf("proto: wrong wireType = %d for field Target", wireType)
}
m.Target = 0
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
m.Target |= Compare_CompareTarget(b&0x7F) << shift
if b < 0x80 {
break
}
}
case 3:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Key", wireType)
}
var byteLen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
byteLen |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
if byteLen < 0 {
return ErrInvalidLengthRpc
}
postIndex := iNdEx + byteLen
if postIndex < 0 {
return ErrInvalidLengthRpc
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.Key = append(m.Key[:0], dAtA[iNdEx:postIndex]...)
if m.Key == nil {
m.Key = []byte{}
}
iNdEx = postIndex
case 4:
if wireType != 0 {
return fmt.Errorf("proto: wrong wireType = %d for field Version", wireType)
}
var v int64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
v |= int64(b&0x7F) << shift
if b < 0x80 {
break
}
}
m.TargetUnion = &Compare_Version{v}
case 5:
if wireType != 0 {
return fmt.Errorf("proto: wrong wireType = %d for field CreateRevision", wireType)
}
var v int64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
v |= int64(b&0x7F) << shift
if b < 0x80 {
break
}
}
m.TargetUnion = &Compare_CreateRevision{v}
case 6:
if wireType != 0 {
return fmt.Errorf("proto: wrong wireType = %d for field ModRevision", wireType)
}
var v int64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
v |= int64(b&0x7F) << shift
if b < 0x80 {
break
}
}
m.TargetUnion = &Compare_ModRevision{v}
case 7:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Value", wireType)
}
var byteLen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
byteLen |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
if byteLen < 0 {
return ErrInvalidLengthRpc
}
postIndex := iNdEx + byteLen
if postIndex < 0 {
return ErrInvalidLengthRpc
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
v := make([]byte, postIndex-iNdEx)
copy(v, dAtA[iNdEx:postIndex])
m.TargetUnion = &Compare_Value{v}
iNdEx = postIndex
case 8:
if wireType != 0 {
return fmt.Errorf("proto: wrong wireType = %d for field Lease", wireType)
}
var v int64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
v |= int64(b&0x7F) << shift
if b < 0x80 {
break
}
}
m.TargetUnion = &Compare_Lease{v}
case 64:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field RangeEnd", wireType)
}
var byteLen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
byteLen |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
if byteLen < 0 {
return ErrInvalidLengthRpc
}
postIndex := iNdEx + byteLen
if postIndex < 0 {
return ErrInvalidLengthRpc
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.RangeEnd = append(m.RangeEnd[:0], dAtA[iNdEx:postIndex]...)
if m.RangeEnd == nil {
m.RangeEnd = []byte{}
}
iNdEx = postIndex
default:
iNdEx = preIndex
skippy, err := skipRpc(dAtA[iNdEx:])
if err != nil {
return err
}
if (skippy < 0) || (iNdEx+skippy) < 0 {
return ErrInvalidLengthRpc
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
iNdEx += skippy
}
}
if iNdEx > l {
return io.ErrUnexpectedEOF
}
return nil
}
func (m *TxnRequest) Unmarshal(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0
for iNdEx < l {
preIndex := iNdEx
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
fieldNum := int32(wire >> 3)
wireType := int(wire & 0x7)
if wireType == 4 {
return fmt.Errorf("proto: TxnRequest: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: TxnRequest: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
case 1:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Compare", wireType)
}
var msglen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
msglen |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
if msglen < 0 {
return ErrInvalidLengthRpc
}
postIndex := iNdEx + msglen
if postIndex < 0 {
return ErrInvalidLengthRpc
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.Compare = append(m.Compare, &Compare{})
if err := m.Compare[len(m.Compare)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
return err
}
iNdEx = postIndex
case 2:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Success", wireType)
}
var msglen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
msglen |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
if msglen < 0 {
return ErrInvalidLengthRpc
}
postIndex := iNdEx + msglen
if postIndex < 0 {
return ErrInvalidLengthRpc
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.Success = append(m.Success, &RequestOp{})
if err := m.Success[len(m.Success)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
return err
}
iNdEx = postIndex
case 3:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Failure", wireType)
}
var msglen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
msglen |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
if msglen < 0 {
return ErrInvalidLengthRpc
}
postIndex := iNdEx + msglen
if postIndex < 0 {
return ErrInvalidLengthRpc
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.Failure = append(m.Failure, &RequestOp{})
if err := m.Failure[len(m.Failure)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
return err
}
iNdEx = postIndex
default:
iNdEx = preIndex
skippy, err := skipRpc(dAtA[iNdEx:])
if err != nil {
return err
}
if (skippy < 0) || (iNdEx+skippy) < 0 {
return ErrInvalidLengthRpc
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
iNdEx += skippy
}
}
if iNdEx > l {
return io.ErrUnexpectedEOF
}
return nil
}
func (m *TxnResponse) Unmarshal(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0
for iNdEx < l {
preIndex := iNdEx
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
fieldNum := int32(wire >> 3)
wireType := int(wire & 0x7)
if wireType == 4 {
return fmt.Errorf("proto: TxnResponse: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: TxnResponse: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
case 1:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Header", wireType)
}
var msglen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
msglen |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
if msglen < 0 {
return ErrInvalidLengthRpc
}
postIndex := iNdEx + msglen
if postIndex < 0 {
return ErrInvalidLengthRpc
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
if m.Header == nil {
m.Header = &ResponseHeader{}
}
if err := m.Header.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
return err
}
iNdEx = postIndex
case 2:
if wireType != 0 {
return fmt.Errorf("proto: wrong wireType = %d for field Succeeded", wireType)
}
var v int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
v |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
m.Succeeded = bool(v != 0)
case 3:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Responses", wireType)
}
var msglen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
msglen |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
if msglen < 0 {
return ErrInvalidLengthRpc
}
postIndex := iNdEx + msglen
if postIndex < 0 {
return ErrInvalidLengthRpc
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.Responses = append(m.Responses, &ResponseOp{})
if err := m.Responses[len(m.Responses)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
return err
}
iNdEx = postIndex
default:
iNdEx = preIndex
skippy, err := skipRpc(dAtA[iNdEx:])
if err != nil {
return err
}
if (skippy < 0) || (iNdEx+skippy) < 0 {
return ErrInvalidLengthRpc
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
iNdEx += skippy
}
}
if iNdEx > l {
return io.ErrUnexpectedEOF
}
return nil
}
func (m *CompactionRequest) Unmarshal(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0
for iNdEx < l {
preIndex := iNdEx
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
fieldNum := int32(wire >> 3)
wireType := int(wire & 0x7)
if wireType == 4 {
return fmt.Errorf("proto: CompactionRequest: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: CompactionRequest: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
case 1:
if wireType != 0 {
return fmt.Errorf("proto: wrong wireType = %d for field Revision", wireType)
}
m.Revision = 0
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
m.Revision |= int64(b&0x7F) << shift
if b < 0x80 {
break
}
}
case 2:
if wireType != 0 {
return fmt.Errorf("proto: wrong wireType = %d for field Physical", wireType)
}
var v int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
v |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
m.Physical = bool(v != 0)
default:
iNdEx = preIndex
skippy, err := skipRpc(dAtA[iNdEx:])
if err != nil {
return err
}
if (skippy < 0) || (iNdEx+skippy) < 0 {
return ErrInvalidLengthRpc
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
iNdEx += skippy
}
}
if iNdEx > l {
return io.ErrUnexpectedEOF
}
return nil
}
func (m *CompactionResponse) Unmarshal(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0
for iNdEx < l {
preIndex := iNdEx
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
fieldNum := int32(wire >> 3)
wireType := int(wire & 0x7)
if wireType == 4 {
return fmt.Errorf("proto: CompactionResponse: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: CompactionResponse: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
case 1:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Header", wireType)
}
var msglen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
msglen |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
if msglen < 0 {
return ErrInvalidLengthRpc
}
postIndex := iNdEx + msglen
if postIndex < 0 {
return ErrInvalidLengthRpc
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
if m.Header == nil {
m.Header = &ResponseHeader{}
}
if err := m.Header.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
return err
}
iNdEx = postIndex
default:
iNdEx = preIndex
skippy, err := skipRpc(dAtA[iNdEx:])
if err != nil {
return err
}
if (skippy < 0) || (iNdEx+skippy) < 0 {
return ErrInvalidLengthRpc
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
iNdEx += skippy
}
}
if iNdEx > l {
return io.ErrUnexpectedEOF
}
return nil
}
func (m *HashRequest) Unmarshal(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0
for iNdEx < l {
preIndex := iNdEx
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
fieldNum := int32(wire >> 3)
wireType := int(wire & 0x7)
if wireType == 4 {
return fmt.Errorf("proto: HashRequest: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: HashRequest: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
default:
iNdEx = preIndex
skippy, err := skipRpc(dAtA[iNdEx:])
if err != nil {
return err
}
if (skippy < 0) || (iNdEx+skippy) < 0 {
return ErrInvalidLengthRpc
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
iNdEx += skippy
}
}
if iNdEx > l {
return io.ErrUnexpectedEOF
}
return nil
}
func (m *HashKVRequest) Unmarshal(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0
for iNdEx < l {
preIndex := iNdEx
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
fieldNum := int32(wire >> 3)
wireType := int(wire & 0x7)
if wireType == 4 {
return fmt.Errorf("proto: HashKVRequest: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: HashKVRequest: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
case 1:
if wireType != 0 {
return fmt.Errorf("proto: wrong wireType = %d for field Revision", wireType)
}
m.Revision = 0
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
m.Revision |= int64(b&0x7F) << shift
if b < 0x80 {
break
}
}
default:
iNdEx = preIndex
skippy, err := skipRpc(dAtA[iNdEx:])
if err != nil {
return err
}
if (skippy < 0) || (iNdEx+skippy) < 0 {
return ErrInvalidLengthRpc
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
iNdEx += skippy
}
}
if iNdEx > l {
return io.ErrUnexpectedEOF
}
return nil
}
func (m *HashKVResponse) Unmarshal(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0
for iNdEx < l {
preIndex := iNdEx
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
fieldNum := int32(wire >> 3)
wireType := int(wire & 0x7)
if wireType == 4 {
return fmt.Errorf("proto: HashKVResponse: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: HashKVResponse: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
case 1:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Header", wireType)
}
var msglen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
msglen |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
if msglen < 0 {
return ErrInvalidLengthRpc
}
postIndex := iNdEx + msglen
if postIndex < 0 {
return ErrInvalidLengthRpc
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
if m.Header == nil {
m.Header = &ResponseHeader{}
}
if err := m.Header.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
return err
}
iNdEx = postIndex
case 2:
if wireType != 0 {
return fmt.Errorf("proto: wrong wireType = %d for field Hash", wireType)
}
m.Hash = 0
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
m.Hash |= uint32(b&0x7F) << shift
if b < 0x80 {
break
}
}
case 3:
if wireType != 0 {
return fmt.Errorf("proto: wrong wireType = %d for field CompactRevision", wireType)
}
m.CompactRevision = 0
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
m.CompactRevision |= int64(b&0x7F) << shift
if b < 0x80 {
break
}
}
case 4:
if wireType != 0 {
return fmt.Errorf("proto: wrong wireType = %d for field HashRevision", wireType)
}
m.HashRevision = 0
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
m.HashRevision |= int64(b&0x7F) << shift
if b < 0x80 {
break
}
}
default:
iNdEx = preIndex
skippy, err := skipRpc(dAtA[iNdEx:])
if err != nil {
return err
}
if (skippy < 0) || (iNdEx+skippy) < 0 {
return ErrInvalidLengthRpc
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
iNdEx += skippy
}
}
if iNdEx > l {
return io.ErrUnexpectedEOF
}
return nil
}
func (m *HashResponse) Unmarshal(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0
for iNdEx < l {
preIndex := iNdEx
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
fieldNum := int32(wire >> 3)
wireType := int(wire & 0x7)
if wireType == 4 {
return fmt.Errorf("proto: HashResponse: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: HashResponse: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
case 1:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Header", wireType)
}
var msglen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
msglen |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
if msglen < 0 {
return ErrInvalidLengthRpc
}
postIndex := iNdEx + msglen
if postIndex < 0 {
return ErrInvalidLengthRpc
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
if m.Header == nil {
m.Header = &ResponseHeader{}
}
if err := m.Header.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
return err
}
iNdEx = postIndex
case 2:
if wireType != 0 {
return fmt.Errorf("proto: wrong wireType = %d for field Hash", wireType)
}
m.Hash = 0
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
m.Hash |= uint32(b&0x7F) << shift
if b < 0x80 {
break
}
}
default:
iNdEx = preIndex
skippy, err := skipRpc(dAtA[iNdEx:])
if err != nil {
return err
}
if (skippy < 0) || (iNdEx+skippy) < 0 {
return ErrInvalidLengthRpc
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
iNdEx += skippy
}
}
if iNdEx > l {
return io.ErrUnexpectedEOF
}
return nil
}
func (m *SnapshotRequest) Unmarshal(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0
for iNdEx < l {
preIndex := iNdEx
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
fieldNum := int32(wire >> 3)
wireType := int(wire & 0x7)
if wireType == 4 {
return fmt.Errorf("proto: SnapshotRequest: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: SnapshotRequest: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
default:
iNdEx = preIndex
skippy, err := skipRpc(dAtA[iNdEx:])
if err != nil {
return err
}
if (skippy < 0) || (iNdEx+skippy) < 0 {
return ErrInvalidLengthRpc
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
iNdEx += skippy
}
}
if iNdEx > l {
return io.ErrUnexpectedEOF
}
return nil
}
func (m *SnapshotResponse) Unmarshal(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0
for iNdEx < l {
preIndex := iNdEx
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
fieldNum := int32(wire >> 3)
wireType := int(wire & 0x7)
if wireType == 4 {
return fmt.Errorf("proto: SnapshotResponse: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: SnapshotResponse: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
case 1:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Header", wireType)
}
var msglen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
msglen |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
if msglen < 0 {
return ErrInvalidLengthRpc
}
postIndex := iNdEx + msglen
if postIndex < 0 {
return ErrInvalidLengthRpc
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
if m.Header == nil {
m.Header = &ResponseHeader{}
}
if err := m.Header.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
return err
}
iNdEx = postIndex
case 2:
if wireType != 0 {
return fmt.Errorf("proto: wrong wireType = %d for field RemainingBytes", wireType)
}
m.RemainingBytes = 0
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
m.RemainingBytes |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
case 3:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Blob", wireType)
}
var byteLen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
byteLen |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
if byteLen < 0 {
return ErrInvalidLengthRpc
}
postIndex := iNdEx + byteLen
if postIndex < 0 {
return ErrInvalidLengthRpc
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.Blob = append(m.Blob[:0], dAtA[iNdEx:postIndex]...)
if m.Blob == nil {
m.Blob = []byte{}
}
iNdEx = postIndex
case 4:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Version", wireType)
}
var stringLen uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
stringLen |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
intStringLen := int(stringLen)
if intStringLen < 0 {
return ErrInvalidLengthRpc
}
postIndex := iNdEx + intStringLen
if postIndex < 0 {
return ErrInvalidLengthRpc
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.Version = string(dAtA[iNdEx:postIndex])
iNdEx = postIndex
default:
iNdEx = preIndex
skippy, err := skipRpc(dAtA[iNdEx:])
if err != nil {
return err
}
if (skippy < 0) || (iNdEx+skippy) < 0 {
return ErrInvalidLengthRpc
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
iNdEx += skippy
}
}
if iNdEx > l {
return io.ErrUnexpectedEOF
}
return nil
}
func (m *WatchRequest) Unmarshal(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0
for iNdEx < l {
preIndex := iNdEx
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
fieldNum := int32(wire >> 3)
wireType := int(wire & 0x7)
if wireType == 4 {
return fmt.Errorf("proto: WatchRequest: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: WatchRequest: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
case 1:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field CreateRequest", wireType)
}
var msglen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
msglen |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
if msglen < 0 {
return ErrInvalidLengthRpc
}
postIndex := iNdEx + msglen
if postIndex < 0 {
return ErrInvalidLengthRpc
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
v := &WatchCreateRequest{}
if err := v.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
return err
}
m.RequestUnion = &WatchRequest_CreateRequest{v}
iNdEx = postIndex
case 2:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field CancelRequest", wireType)
}
var msglen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
msglen |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
if msglen < 0 {
return ErrInvalidLengthRpc
}
postIndex := iNdEx + msglen
if postIndex < 0 {
return ErrInvalidLengthRpc
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
v := &WatchCancelRequest{}
if err := v.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
return err
}
m.RequestUnion = &WatchRequest_CancelRequest{v}
iNdEx = postIndex
case 3:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field ProgressRequest", wireType)
}
var msglen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
msglen |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
if msglen < 0 {
return ErrInvalidLengthRpc
}
postIndex := iNdEx + msglen
if postIndex < 0 {
return ErrInvalidLengthRpc
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
v := &WatchProgressRequest{}
if err := v.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
return err
}
m.RequestUnion = &WatchRequest_ProgressRequest{v}
iNdEx = postIndex
default:
iNdEx = preIndex
skippy, err := skipRpc(dAtA[iNdEx:])
if err != nil {
return err
}
if (skippy < 0) || (iNdEx+skippy) < 0 {
return ErrInvalidLengthRpc
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
iNdEx += skippy
}
}
if iNdEx > l {
return io.ErrUnexpectedEOF
}
return nil
}
func (m *WatchCreateRequest) Unmarshal(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0
for iNdEx < l {
preIndex := iNdEx
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
fieldNum := int32(wire >> 3)
wireType := int(wire & 0x7)
if wireType == 4 {
return fmt.Errorf("proto: WatchCreateRequest: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: WatchCreateRequest: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
case 1:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Key", wireType)
}
var byteLen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
byteLen |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
if byteLen < 0 {
return ErrInvalidLengthRpc
}
postIndex := iNdEx + byteLen
if postIndex < 0 {
return ErrInvalidLengthRpc
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.Key = append(m.Key[:0], dAtA[iNdEx:postIndex]...)
if m.Key == nil {
m.Key = []byte{}
}
iNdEx = postIndex
case 2:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field RangeEnd", wireType)
}
var byteLen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
byteLen |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
if byteLen < 0 {
return ErrInvalidLengthRpc
}
postIndex := iNdEx + byteLen
if postIndex < 0 {
return ErrInvalidLengthRpc
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.RangeEnd = append(m.RangeEnd[:0], dAtA[iNdEx:postIndex]...)
if m.RangeEnd == nil {
m.RangeEnd = []byte{}
}
iNdEx = postIndex
case 3:
if wireType != 0 {
return fmt.Errorf("proto: wrong wireType = %d for field StartRevision", wireType)
}
m.StartRevision = 0
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
m.StartRevision |= int64(b&0x7F) << shift
if b < 0x80 {
break
}
}
case 4:
if wireType != 0 {
return fmt.Errorf("proto: wrong wireType = %d for field ProgressNotify", wireType)
}
var v int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
v |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
m.ProgressNotify = bool(v != 0)
case 5:
if wireType == 0 {
var v WatchCreateRequest_FilterType
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
v |= WatchCreateRequest_FilterType(b&0x7F) << shift
if b < 0x80 {
break
}
}
m.Filters = append(m.Filters, v)
} else if wireType == 2 {
var packedLen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
packedLen |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
if packedLen < 0 {
return ErrInvalidLengthRpc
}
postIndex := iNdEx + packedLen
if postIndex < 0 {
return ErrInvalidLengthRpc
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
var elementCount int
if elementCount != 0 && len(m.Filters) == 0 {
m.Filters = make([]WatchCreateRequest_FilterType, 0, elementCount)
}
for iNdEx < postIndex {
var v WatchCreateRequest_FilterType
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
v |= WatchCreateRequest_FilterType(b&0x7F) << shift
if b < 0x80 {
break
}
}
m.Filters = append(m.Filters, v)
}
} else {
return fmt.Errorf("proto: wrong wireType = %d for field Filters", wireType)
}
case 6:
if wireType != 0 {
return fmt.Errorf("proto: wrong wireType = %d for field PrevKv", wireType)
}
var v int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
v |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
m.PrevKv = bool(v != 0)
case 7:
if wireType != 0 {
return fmt.Errorf("proto: wrong wireType = %d for field WatchId", wireType)
}
m.WatchId = 0
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
m.WatchId |= int64(b&0x7F) << shift
if b < 0x80 {
break
}
}
case 8:
if wireType != 0 {
return fmt.Errorf("proto: wrong wireType = %d for field Fragment", wireType)
}
var v int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
v |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
m.Fragment = bool(v != 0)
default:
iNdEx = preIndex
skippy, err := skipRpc(dAtA[iNdEx:])
if err != nil {
return err
}
if (skippy < 0) || (iNdEx+skippy) < 0 {
return ErrInvalidLengthRpc
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
iNdEx += skippy
}
}
if iNdEx > l {
return io.ErrUnexpectedEOF
}
return nil
}
func (m *WatchCancelRequest) Unmarshal(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0
for iNdEx < l {
preIndex := iNdEx
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
fieldNum := int32(wire >> 3)
wireType := int(wire & 0x7)
if wireType == 4 {
return fmt.Errorf("proto: WatchCancelRequest: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: WatchCancelRequest: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
case 1:
if wireType != 0 {
return fmt.Errorf("proto: wrong wireType = %d for field WatchId", wireType)
}
m.WatchId = 0
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
m.WatchId |= int64(b&0x7F) << shift
if b < 0x80 {
break
}
}
default:
iNdEx = preIndex
skippy, err := skipRpc(dAtA[iNdEx:])
if err != nil {
return err
}
if (skippy < 0) || (iNdEx+skippy) < 0 {
return ErrInvalidLengthRpc
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
iNdEx += skippy
}
}
if iNdEx > l {
return io.ErrUnexpectedEOF
}
return nil
}
func (m *WatchProgressRequest) Unmarshal(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0
for iNdEx < l {
preIndex := iNdEx
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
fieldNum := int32(wire >> 3)
wireType := int(wire & 0x7)
if wireType == 4 {
return fmt.Errorf("proto: WatchProgressRequest: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: WatchProgressRequest: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
default:
iNdEx = preIndex
skippy, err := skipRpc(dAtA[iNdEx:])
if err != nil {
return err
}
if (skippy < 0) || (iNdEx+skippy) < 0 {
return ErrInvalidLengthRpc
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
iNdEx += skippy
}
}
if iNdEx > l {
return io.ErrUnexpectedEOF
}
return nil
}
func (m *WatchResponse) Unmarshal(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0
for iNdEx < l {
preIndex := iNdEx
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
fieldNum := int32(wire >> 3)
wireType := int(wire & 0x7)
if wireType == 4 {
return fmt.Errorf("proto: WatchResponse: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: WatchResponse: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
case 1:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Header", wireType)
}
var msglen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
msglen |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
if msglen < 0 {
return ErrInvalidLengthRpc
}
postIndex := iNdEx + msglen
if postIndex < 0 {
return ErrInvalidLengthRpc
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
if m.Header == nil {
m.Header = &ResponseHeader{}
}
if err := m.Header.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
return err
}
iNdEx = postIndex
case 2:
if wireType != 0 {
return fmt.Errorf("proto: wrong wireType = %d for field WatchId", wireType)
}
m.WatchId = 0
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
m.WatchId |= int64(b&0x7F) << shift
if b < 0x80 {
break
}
}
case 3:
if wireType != 0 {
return fmt.Errorf("proto: wrong wireType = %d for field Created", wireType)
}
var v int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
v |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
m.Created = bool(v != 0)
case 4:
if wireType != 0 {
return fmt.Errorf("proto: wrong wireType = %d for field Canceled", wireType)
}
var v int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
v |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
m.Canceled = bool(v != 0)
case 5:
if wireType != 0 {
return fmt.Errorf("proto: wrong wireType = %d for field CompactRevision", wireType)
}
m.CompactRevision = 0
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
m.CompactRevision |= int64(b&0x7F) << shift
if b < 0x80 {
break
}
}
case 6:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field CancelReason", wireType)
}
var stringLen uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
stringLen |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
intStringLen := int(stringLen)
if intStringLen < 0 {
return ErrInvalidLengthRpc
}
postIndex := iNdEx + intStringLen
if postIndex < 0 {
return ErrInvalidLengthRpc
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.CancelReason = string(dAtA[iNdEx:postIndex])
iNdEx = postIndex
case 7:
if wireType != 0 {
return fmt.Errorf("proto: wrong wireType = %d for field Fragment", wireType)
}
var v int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
v |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
m.Fragment = bool(v != 0)
case 11:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Events", wireType)
}
var msglen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
msglen |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
if msglen < 0 {
return ErrInvalidLengthRpc
}
postIndex := iNdEx + msglen
if postIndex < 0 {
return ErrInvalidLengthRpc
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.Events = append(m.Events, &mvccpb.Event{})
if err := m.Events[len(m.Events)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
return err
}
iNdEx = postIndex
default:
iNdEx = preIndex
skippy, err := skipRpc(dAtA[iNdEx:])
if err != nil {
return err
}
if (skippy < 0) || (iNdEx+skippy) < 0 {
return ErrInvalidLengthRpc
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
iNdEx += skippy
}
}
if iNdEx > l {
return io.ErrUnexpectedEOF
}
return nil
}
func (m *LeaseGrantRequest) Unmarshal(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0
for iNdEx < l {
preIndex := iNdEx
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
fieldNum := int32(wire >> 3)
wireType := int(wire & 0x7)
if wireType == 4 {
return fmt.Errorf("proto: LeaseGrantRequest: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: LeaseGrantRequest: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
case 1:
if wireType != 0 {
return fmt.Errorf("proto: wrong wireType = %d for field TTL", wireType)
}
m.TTL = 0
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
m.TTL |= int64(b&0x7F) << shift
if b < 0x80 {
break
}
}
case 2:
if wireType != 0 {
return fmt.Errorf("proto: wrong wireType = %d for field ID", wireType)
}
m.ID = 0
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
m.ID |= int64(b&0x7F) << shift
if b < 0x80 {
break
}
}
default:
iNdEx = preIndex
skippy, err := skipRpc(dAtA[iNdEx:])
if err != nil {
return err
}
if (skippy < 0) || (iNdEx+skippy) < 0 {
return ErrInvalidLengthRpc
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
iNdEx += skippy
}
}
if iNdEx > l {
return io.ErrUnexpectedEOF
}
return nil
}
func (m *LeaseGrantResponse) Unmarshal(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0
for iNdEx < l {
preIndex := iNdEx
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
fieldNum := int32(wire >> 3)
wireType := int(wire & 0x7)
if wireType == 4 {
return fmt.Errorf("proto: LeaseGrantResponse: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: LeaseGrantResponse: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
case 1:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Header", wireType)
}
var msglen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
msglen |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
if msglen < 0 {
return ErrInvalidLengthRpc
}
postIndex := iNdEx + msglen
if postIndex < 0 {
return ErrInvalidLengthRpc
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
if m.Header == nil {
m.Header = &ResponseHeader{}
}
if err := m.Header.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
return err
}
iNdEx = postIndex
case 2:
if wireType != 0 {
return fmt.Errorf("proto: wrong wireType = %d for field ID", wireType)
}
m.ID = 0
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
m.ID |= int64(b&0x7F) << shift
if b < 0x80 {
break
}
}
case 3:
if wireType != 0 {
return fmt.Errorf("proto: wrong wireType = %d for field TTL", wireType)
}
m.TTL = 0
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
m.TTL |= int64(b&0x7F) << shift
if b < 0x80 {
break
}
}
case 4:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Error", wireType)
}
var stringLen uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
stringLen |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
intStringLen := int(stringLen)
if intStringLen < 0 {
return ErrInvalidLengthRpc
}
postIndex := iNdEx + intStringLen
if postIndex < 0 {
return ErrInvalidLengthRpc
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.Error = string(dAtA[iNdEx:postIndex])
iNdEx = postIndex
default:
iNdEx = preIndex
skippy, err := skipRpc(dAtA[iNdEx:])
if err != nil {
return err
}
if (skippy < 0) || (iNdEx+skippy) < 0 {
return ErrInvalidLengthRpc
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
iNdEx += skippy
}
}
if iNdEx > l {
return io.ErrUnexpectedEOF
}
return nil
}
func (m *LeaseRevokeRequest) Unmarshal(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0
for iNdEx < l {
preIndex := iNdEx
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
fieldNum := int32(wire >> 3)
wireType := int(wire & 0x7)
if wireType == 4 {
return fmt.Errorf("proto: LeaseRevokeRequest: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: LeaseRevokeRequest: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
case 1:
if wireType != 0 {
return fmt.Errorf("proto: wrong wireType = %d for field ID", wireType)
}
m.ID = 0
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
m.ID |= int64(b&0x7F) << shift
if b < 0x80 {
break
}
}
default:
iNdEx = preIndex
skippy, err := skipRpc(dAtA[iNdEx:])
if err != nil {
return err
}
if (skippy < 0) || (iNdEx+skippy) < 0 {
return ErrInvalidLengthRpc
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
iNdEx += skippy
}
}
if iNdEx > l {
return io.ErrUnexpectedEOF
}
return nil
}
func (m *LeaseRevokeResponse) Unmarshal(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0
for iNdEx < l {
preIndex := iNdEx
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
fieldNum := int32(wire >> 3)
wireType := int(wire & 0x7)
if wireType == 4 {
return fmt.Errorf("proto: LeaseRevokeResponse: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: LeaseRevokeResponse: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
case 1:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Header", wireType)
}
var msglen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
msglen |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
if msglen < 0 {
return ErrInvalidLengthRpc
}
postIndex := iNdEx + msglen
if postIndex < 0 {
return ErrInvalidLengthRpc
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
if m.Header == nil {
m.Header = &ResponseHeader{}
}
if err := m.Header.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
return err
}
iNdEx = postIndex
default:
iNdEx = preIndex
skippy, err := skipRpc(dAtA[iNdEx:])
if err != nil {
return err
}
if (skippy < 0) || (iNdEx+skippy) < 0 {
return ErrInvalidLengthRpc
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
iNdEx += skippy
}
}
if iNdEx > l {
return io.ErrUnexpectedEOF
}
return nil
}
func (m *LeaseCheckpoint) Unmarshal(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0
for iNdEx < l {
preIndex := iNdEx
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
fieldNum := int32(wire >> 3)
wireType := int(wire & 0x7)
if wireType == 4 {
return fmt.Errorf("proto: LeaseCheckpoint: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: LeaseCheckpoint: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
case 1:
if wireType != 0 {
return fmt.Errorf("proto: wrong wireType = %d for field ID", wireType)
}
m.ID = 0
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
m.ID |= int64(b&0x7F) << shift
if b < 0x80 {
break
}
}
case 2:
if wireType != 0 {
return fmt.Errorf("proto: wrong wireType = %d for field Remaining_TTL", wireType)
}
m.Remaining_TTL = 0
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
m.Remaining_TTL |= int64(b&0x7F) << shift
if b < 0x80 {
break
}
}
default:
iNdEx = preIndex
skippy, err := skipRpc(dAtA[iNdEx:])
if err != nil {
return err
}
if (skippy < 0) || (iNdEx+skippy) < 0 {
return ErrInvalidLengthRpc
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
iNdEx += skippy
}
}
if iNdEx > l {
return io.ErrUnexpectedEOF
}
return nil
}
func (m *LeaseCheckpointRequest) Unmarshal(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0
for iNdEx < l {
preIndex := iNdEx
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
fieldNum := int32(wire >> 3)
wireType := int(wire & 0x7)
if wireType == 4 {
return fmt.Errorf("proto: LeaseCheckpointRequest: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: LeaseCheckpointRequest: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
case 1:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Checkpoints", wireType)
}
var msglen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
msglen |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
if msglen < 0 {
return ErrInvalidLengthRpc
}
postIndex := iNdEx + msglen
if postIndex < 0 {
return ErrInvalidLengthRpc
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.Checkpoints = append(m.Checkpoints, &LeaseCheckpoint{})
if err := m.Checkpoints[len(m.Checkpoints)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
return err
}
iNdEx = postIndex
default:
iNdEx = preIndex
skippy, err := skipRpc(dAtA[iNdEx:])
if err != nil {
return err
}
if (skippy < 0) || (iNdEx+skippy) < 0 {
return ErrInvalidLengthRpc
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
iNdEx += skippy
}
}
if iNdEx > l {
return io.ErrUnexpectedEOF
}
return nil
}
func (m *LeaseCheckpointResponse) Unmarshal(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0
for iNdEx < l {
preIndex := iNdEx
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
fieldNum := int32(wire >> 3)
wireType := int(wire & 0x7)
if wireType == 4 {
return fmt.Errorf("proto: LeaseCheckpointResponse: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: LeaseCheckpointResponse: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
case 1:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Header", wireType)
}
var msglen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
msglen |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
if msglen < 0 {
return ErrInvalidLengthRpc
}
postIndex := iNdEx + msglen
if postIndex < 0 {
return ErrInvalidLengthRpc
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
if m.Header == nil {
m.Header = &ResponseHeader{}
}
if err := m.Header.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
return err
}
iNdEx = postIndex
default:
iNdEx = preIndex
skippy, err := skipRpc(dAtA[iNdEx:])
if err != nil {
return err
}
if (skippy < 0) || (iNdEx+skippy) < 0 {
return ErrInvalidLengthRpc
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
iNdEx += skippy
}
}
if iNdEx > l {
return io.ErrUnexpectedEOF
}
return nil
}
func (m *LeaseKeepAliveRequest) Unmarshal(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0
for iNdEx < l {
preIndex := iNdEx
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
fieldNum := int32(wire >> 3)
wireType := int(wire & 0x7)
if wireType == 4 {
return fmt.Errorf("proto: LeaseKeepAliveRequest: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: LeaseKeepAliveRequest: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
case 1:
if wireType != 0 {
return fmt.Errorf("proto: wrong wireType = %d for field ID", wireType)
}
m.ID = 0
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
m.ID |= int64(b&0x7F) << shift
if b < 0x80 {
break
}
}
default:
iNdEx = preIndex
skippy, err := skipRpc(dAtA[iNdEx:])
if err != nil {
return err
}
if (skippy < 0) || (iNdEx+skippy) < 0 {
return ErrInvalidLengthRpc
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
iNdEx += skippy
}
}
if iNdEx > l {
return io.ErrUnexpectedEOF
}
return nil
}
func (m *LeaseKeepAliveResponse) Unmarshal(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0
for iNdEx < l {
preIndex := iNdEx
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
fieldNum := int32(wire >> 3)
wireType := int(wire & 0x7)
if wireType == 4 {
return fmt.Errorf("proto: LeaseKeepAliveResponse: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: LeaseKeepAliveResponse: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
case 1:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Header", wireType)
}
var msglen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
msglen |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
if msglen < 0 {
return ErrInvalidLengthRpc
}
postIndex := iNdEx + msglen
if postIndex < 0 {
return ErrInvalidLengthRpc
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
if m.Header == nil {
m.Header = &ResponseHeader{}
}
if err := m.Header.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
return err
}
iNdEx = postIndex
case 2:
if wireType != 0 {
return fmt.Errorf("proto: wrong wireType = %d for field ID", wireType)
}
m.ID = 0
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
m.ID |= int64(b&0x7F) << shift
if b < 0x80 {
break
}
}
case 3:
if wireType != 0 {
return fmt.Errorf("proto: wrong wireType = %d for field TTL", wireType)
}
m.TTL = 0
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
m.TTL |= int64(b&0x7F) << shift
if b < 0x80 {
break
}
}
default:
iNdEx = preIndex
skippy, err := skipRpc(dAtA[iNdEx:])
if err != nil {
return err
}
if (skippy < 0) || (iNdEx+skippy) < 0 {
return ErrInvalidLengthRpc
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
iNdEx += skippy
}
}
if iNdEx > l {
return io.ErrUnexpectedEOF
}
return nil
}
func (m *LeaseTimeToLiveRequest) Unmarshal(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0
for iNdEx < l {
preIndex := iNdEx
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
fieldNum := int32(wire >> 3)
wireType := int(wire & 0x7)
if wireType == 4 {
return fmt.Errorf("proto: LeaseTimeToLiveRequest: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: LeaseTimeToLiveRequest: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
case 1:
if wireType != 0 {
return fmt.Errorf("proto: wrong wireType = %d for field ID", wireType)
}
m.ID = 0
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
m.ID |= int64(b&0x7F) << shift
if b < 0x80 {
break
}
}
case 2:
if wireType != 0 {
return fmt.Errorf("proto: wrong wireType = %d for field Keys", wireType)
}
var v int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
v |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
m.Keys = bool(v != 0)
default:
iNdEx = preIndex
skippy, err := skipRpc(dAtA[iNdEx:])
if err != nil {
return err
}
if (skippy < 0) || (iNdEx+skippy) < 0 {
return ErrInvalidLengthRpc
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
iNdEx += skippy
}
}
if iNdEx > l {
return io.ErrUnexpectedEOF
}
return nil
}
func (m *LeaseTimeToLiveResponse) Unmarshal(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0
for iNdEx < l {
preIndex := iNdEx
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
fieldNum := int32(wire >> 3)
wireType := int(wire & 0x7)
if wireType == 4 {
return fmt.Errorf("proto: LeaseTimeToLiveResponse: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: LeaseTimeToLiveResponse: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
case 1:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Header", wireType)
}
var msglen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
msglen |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
if msglen < 0 {
return ErrInvalidLengthRpc
}
postIndex := iNdEx + msglen
if postIndex < 0 {
return ErrInvalidLengthRpc
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
if m.Header == nil {
m.Header = &ResponseHeader{}
}
if err := m.Header.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
return err
}
iNdEx = postIndex
case 2:
if wireType != 0 {
return fmt.Errorf("proto: wrong wireType = %d for field ID", wireType)
}
m.ID = 0
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
m.ID |= int64(b&0x7F) << shift
if b < 0x80 {
break
}
}
case 3:
if wireType != 0 {
return fmt.Errorf("proto: wrong wireType = %d for field TTL", wireType)
}
m.TTL = 0
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
m.TTL |= int64(b&0x7F) << shift
if b < 0x80 {
break
}
}
case 4:
if wireType != 0 {
return fmt.Errorf("proto: wrong wireType = %d for field GrantedTTL", wireType)
}
m.GrantedTTL = 0
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
m.GrantedTTL |= int64(b&0x7F) << shift
if b < 0x80 {
break
}
}
case 5:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Keys", wireType)
}
var byteLen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
byteLen |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
if byteLen < 0 {
return ErrInvalidLengthRpc
}
postIndex := iNdEx + byteLen
if postIndex < 0 {
return ErrInvalidLengthRpc
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.Keys = append(m.Keys, make([]byte, postIndex-iNdEx))
copy(m.Keys[len(m.Keys)-1], dAtA[iNdEx:postIndex])
iNdEx = postIndex
default:
iNdEx = preIndex
skippy, err := skipRpc(dAtA[iNdEx:])
if err != nil {
return err
}
if (skippy < 0) || (iNdEx+skippy) < 0 {
return ErrInvalidLengthRpc
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
iNdEx += skippy
}
}
if iNdEx > l {
return io.ErrUnexpectedEOF
}
return nil
}
func (m *LeaseLeasesRequest) Unmarshal(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0
for iNdEx < l {
preIndex := iNdEx
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
fieldNum := int32(wire >> 3)
wireType := int(wire & 0x7)
if wireType == 4 {
return fmt.Errorf("proto: LeaseLeasesRequest: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: LeaseLeasesRequest: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
default:
iNdEx = preIndex
skippy, err := skipRpc(dAtA[iNdEx:])
if err != nil {
return err
}
if (skippy < 0) || (iNdEx+skippy) < 0 {
return ErrInvalidLengthRpc
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
iNdEx += skippy
}
}
if iNdEx > l {
return io.ErrUnexpectedEOF
}
return nil
}
func (m *LeaseStatus) Unmarshal(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0
for iNdEx < l {
preIndex := iNdEx
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
fieldNum := int32(wire >> 3)
wireType := int(wire & 0x7)
if wireType == 4 {
return fmt.Errorf("proto: LeaseStatus: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: LeaseStatus: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
case 1:
if wireType != 0 {
return fmt.Errorf("proto: wrong wireType = %d for field ID", wireType)
}
m.ID = 0
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
m.ID |= int64(b&0x7F) << shift
if b < 0x80 {
break
}
}
default:
iNdEx = preIndex
skippy, err := skipRpc(dAtA[iNdEx:])
if err != nil {
return err
}
if (skippy < 0) || (iNdEx+skippy) < 0 {
return ErrInvalidLengthRpc
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
iNdEx += skippy
}
}
if iNdEx > l {
return io.ErrUnexpectedEOF
}
return nil
}
func (m *LeaseLeasesResponse) Unmarshal(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0
for iNdEx < l {
preIndex := iNdEx
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
fieldNum := int32(wire >> 3)
wireType := int(wire & 0x7)
if wireType == 4 {
return fmt.Errorf("proto: LeaseLeasesResponse: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: LeaseLeasesResponse: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
case 1:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Header", wireType)
}
var msglen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
msglen |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
if msglen < 0 {
return ErrInvalidLengthRpc
}
postIndex := iNdEx + msglen
if postIndex < 0 {
return ErrInvalidLengthRpc
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
if m.Header == nil {
m.Header = &ResponseHeader{}
}
if err := m.Header.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
return err
}
iNdEx = postIndex
case 2:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Leases", wireType)
}
var msglen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
msglen |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
if msglen < 0 {
return ErrInvalidLengthRpc
}
postIndex := iNdEx + msglen
if postIndex < 0 {
return ErrInvalidLengthRpc
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.Leases = append(m.Leases, &LeaseStatus{})
if err := m.Leases[len(m.Leases)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
return err
}
iNdEx = postIndex
default:
iNdEx = preIndex
skippy, err := skipRpc(dAtA[iNdEx:])
if err != nil {
return err
}
if (skippy < 0) || (iNdEx+skippy) < 0 {
return ErrInvalidLengthRpc
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
iNdEx += skippy
}
}
if iNdEx > l {
return io.ErrUnexpectedEOF
}
return nil
}
func (m *Member) Unmarshal(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0
for iNdEx < l {
preIndex := iNdEx
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
fieldNum := int32(wire >> 3)
wireType := int(wire & 0x7)
if wireType == 4 {
return fmt.Errorf("proto: Member: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: Member: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
case 1:
if wireType != 0 {
return fmt.Errorf("proto: wrong wireType = %d for field ID", wireType)
}
m.ID = 0
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
m.ID |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
case 2:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType)
}
var stringLen uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
stringLen |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
intStringLen := int(stringLen)
if intStringLen < 0 {
return ErrInvalidLengthRpc
}
postIndex := iNdEx + intStringLen
if postIndex < 0 {
return ErrInvalidLengthRpc
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.Name = string(dAtA[iNdEx:postIndex])
iNdEx = postIndex
case 3:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field PeerURLs", wireType)
}
var stringLen uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
stringLen |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
intStringLen := int(stringLen)
if intStringLen < 0 {
return ErrInvalidLengthRpc
}
postIndex := iNdEx + intStringLen
if postIndex < 0 {
return ErrInvalidLengthRpc
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.PeerURLs = append(m.PeerURLs, string(dAtA[iNdEx:postIndex]))
iNdEx = postIndex
case 4:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field ClientURLs", wireType)
}
var stringLen uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
stringLen |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
intStringLen := int(stringLen)
if intStringLen < 0 {
return ErrInvalidLengthRpc
}
postIndex := iNdEx + intStringLen
if postIndex < 0 {
return ErrInvalidLengthRpc
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.ClientURLs = append(m.ClientURLs, string(dAtA[iNdEx:postIndex]))
iNdEx = postIndex
case 5:
if wireType != 0 {
return fmt.Errorf("proto: wrong wireType = %d for field IsLearner", wireType)
}
var v int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
v |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
m.IsLearner = bool(v != 0)
default:
iNdEx = preIndex
skippy, err := skipRpc(dAtA[iNdEx:])
if err != nil {
return err
}
if (skippy < 0) || (iNdEx+skippy) < 0 {
return ErrInvalidLengthRpc
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
iNdEx += skippy
}
}
if iNdEx > l {
return io.ErrUnexpectedEOF
}
return nil
}
func (m *MemberAddRequest) Unmarshal(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0
for iNdEx < l {
preIndex := iNdEx
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
fieldNum := int32(wire >> 3)
wireType := int(wire & 0x7)
if wireType == 4 {
return fmt.Errorf("proto: MemberAddRequest: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: MemberAddRequest: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
case 1:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field PeerURLs", wireType)
}
var stringLen uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
stringLen |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
intStringLen := int(stringLen)
if intStringLen < 0 {
return ErrInvalidLengthRpc
}
postIndex := iNdEx + intStringLen
if postIndex < 0 {
return ErrInvalidLengthRpc
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.PeerURLs = append(m.PeerURLs, string(dAtA[iNdEx:postIndex]))
iNdEx = postIndex
case 2:
if wireType != 0 {
return fmt.Errorf("proto: wrong wireType = %d for field IsLearner", wireType)
}
var v int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
v |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
m.IsLearner = bool(v != 0)
default:
iNdEx = preIndex
skippy, err := skipRpc(dAtA[iNdEx:])
if err != nil {
return err
}
if (skippy < 0) || (iNdEx+skippy) < 0 {
return ErrInvalidLengthRpc
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
iNdEx += skippy
}
}
if iNdEx > l {
return io.ErrUnexpectedEOF
}
return nil
}
func (m *MemberAddResponse) Unmarshal(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0
for iNdEx < l {
preIndex := iNdEx
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
fieldNum := int32(wire >> 3)
wireType := int(wire & 0x7)
if wireType == 4 {
return fmt.Errorf("proto: MemberAddResponse: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: MemberAddResponse: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
case 1:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Header", wireType)
}
var msglen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
msglen |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
if msglen < 0 {
return ErrInvalidLengthRpc
}
postIndex := iNdEx + msglen
if postIndex < 0 {
return ErrInvalidLengthRpc
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
if m.Header == nil {
m.Header = &ResponseHeader{}
}
if err := m.Header.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
return err
}
iNdEx = postIndex
case 2:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Member", wireType)
}
var msglen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
msglen |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
if msglen < 0 {
return ErrInvalidLengthRpc
}
postIndex := iNdEx + msglen
if postIndex < 0 {
return ErrInvalidLengthRpc
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
if m.Member == nil {
m.Member = &Member{}
}
if err := m.Member.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
return err
}
iNdEx = postIndex
case 3:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Members", wireType)
}
var msglen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
msglen |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
if msglen < 0 {
return ErrInvalidLengthRpc
}
postIndex := iNdEx + msglen
if postIndex < 0 {
return ErrInvalidLengthRpc
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.Members = append(m.Members, &Member{})
if err := m.Members[len(m.Members)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
return err
}
iNdEx = postIndex
default:
iNdEx = preIndex
skippy, err := skipRpc(dAtA[iNdEx:])
if err != nil {
return err
}
if (skippy < 0) || (iNdEx+skippy) < 0 {
return ErrInvalidLengthRpc
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
iNdEx += skippy
}
}
if iNdEx > l {
return io.ErrUnexpectedEOF
}
return nil
}
func (m *MemberRemoveRequest) Unmarshal(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0
for iNdEx < l {
preIndex := iNdEx
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
fieldNum := int32(wire >> 3)
wireType := int(wire & 0x7)
if wireType == 4 {
return fmt.Errorf("proto: MemberRemoveRequest: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: MemberRemoveRequest: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
case 1:
if wireType != 0 {
return fmt.Errorf("proto: wrong wireType = %d for field ID", wireType)
}
m.ID = 0
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
m.ID |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
default:
iNdEx = preIndex
skippy, err := skipRpc(dAtA[iNdEx:])
if err != nil {
return err
}
if (skippy < 0) || (iNdEx+skippy) < 0 {
return ErrInvalidLengthRpc
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
iNdEx += skippy
}
}
if iNdEx > l {
return io.ErrUnexpectedEOF
}
return nil
}
func (m *MemberRemoveResponse) Unmarshal(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0
for iNdEx < l {
preIndex := iNdEx
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
fieldNum := int32(wire >> 3)
wireType := int(wire & 0x7)
if wireType == 4 {
return fmt.Errorf("proto: MemberRemoveResponse: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: MemberRemoveResponse: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
case 1:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Header", wireType)
}
var msglen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
msglen |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
if msglen < 0 {
return ErrInvalidLengthRpc
}
postIndex := iNdEx + msglen
if postIndex < 0 {
return ErrInvalidLengthRpc
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
if m.Header == nil {
m.Header = &ResponseHeader{}
}
if err := m.Header.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
return err
}
iNdEx = postIndex
case 2:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Members", wireType)
}
var msglen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
msglen |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
if msglen < 0 {
return ErrInvalidLengthRpc
}
postIndex := iNdEx + msglen
if postIndex < 0 {
return ErrInvalidLengthRpc
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.Members = append(m.Members, &Member{})
if err := m.Members[len(m.Members)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
return err
}
iNdEx = postIndex
default:
iNdEx = preIndex
skippy, err := skipRpc(dAtA[iNdEx:])
if err != nil {
return err
}
if (skippy < 0) || (iNdEx+skippy) < 0 {
return ErrInvalidLengthRpc
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
iNdEx += skippy
}
}
if iNdEx > l {
return io.ErrUnexpectedEOF
}
return nil
}
func (m *MemberUpdateRequest) Unmarshal(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0
for iNdEx < l {
preIndex := iNdEx
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
fieldNum := int32(wire >> 3)
wireType := int(wire & 0x7)
if wireType == 4 {
return fmt.Errorf("proto: MemberUpdateRequest: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: MemberUpdateRequest: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
case 1:
if wireType != 0 {
return fmt.Errorf("proto: wrong wireType = %d for field ID", wireType)
}
m.ID = 0
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
m.ID |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
case 2:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field PeerURLs", wireType)
}
var stringLen uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
stringLen |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
intStringLen := int(stringLen)
if intStringLen < 0 {
return ErrInvalidLengthRpc
}
postIndex := iNdEx + intStringLen
if postIndex < 0 {
return ErrInvalidLengthRpc
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.PeerURLs = append(m.PeerURLs, string(dAtA[iNdEx:postIndex]))
iNdEx = postIndex
default:
iNdEx = preIndex
skippy, err := skipRpc(dAtA[iNdEx:])
if err != nil {
return err
}
if (skippy < 0) || (iNdEx+skippy) < 0 {
return ErrInvalidLengthRpc
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
iNdEx += skippy
}
}
if iNdEx > l {
return io.ErrUnexpectedEOF
}
return nil
}
func (m *MemberUpdateResponse) Unmarshal(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0
for iNdEx < l {
preIndex := iNdEx
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
fieldNum := int32(wire >> 3)
wireType := int(wire & 0x7)
if wireType == 4 {
return fmt.Errorf("proto: MemberUpdateResponse: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: MemberUpdateResponse: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
case 1:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Header", wireType)
}
var msglen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
msglen |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
if msglen < 0 {
return ErrInvalidLengthRpc
}
postIndex := iNdEx + msglen
if postIndex < 0 {
return ErrInvalidLengthRpc
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
if m.Header == nil {
m.Header = &ResponseHeader{}
}
if err := m.Header.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
return err
}
iNdEx = postIndex
case 2:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Members", wireType)
}
var msglen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
msglen |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
if msglen < 0 {
return ErrInvalidLengthRpc
}
postIndex := iNdEx + msglen
if postIndex < 0 {
return ErrInvalidLengthRpc
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.Members = append(m.Members, &Member{})
if err := m.Members[len(m.Members)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
return err
}
iNdEx = postIndex
default:
iNdEx = preIndex
skippy, err := skipRpc(dAtA[iNdEx:])
if err != nil {
return err
}
if (skippy < 0) || (iNdEx+skippy) < 0 {
return ErrInvalidLengthRpc
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
iNdEx += skippy
}
}
if iNdEx > l {
return io.ErrUnexpectedEOF
}
return nil
}
func (m *MemberListRequest) Unmarshal(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0
for iNdEx < l {
preIndex := iNdEx
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
fieldNum := int32(wire >> 3)
wireType := int(wire & 0x7)
if wireType == 4 {
return fmt.Errorf("proto: MemberListRequest: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: MemberListRequest: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
case 1:
if wireType != 0 {
return fmt.Errorf("proto: wrong wireType = %d for field Linearizable", wireType)
}
var v int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
v |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
m.Linearizable = bool(v != 0)
default:
iNdEx = preIndex
skippy, err := skipRpc(dAtA[iNdEx:])
if err != nil {
return err
}
if (skippy < 0) || (iNdEx+skippy) < 0 {
return ErrInvalidLengthRpc
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
iNdEx += skippy
}
}
if iNdEx > l {
return io.ErrUnexpectedEOF
}
return nil
}
func (m *MemberListResponse) Unmarshal(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0
for iNdEx < l {
preIndex := iNdEx
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
fieldNum := int32(wire >> 3)
wireType := int(wire & 0x7)
if wireType == 4 {
return fmt.Errorf("proto: MemberListResponse: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: MemberListResponse: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
case 1:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Header", wireType)
}
var msglen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
msglen |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
if msglen < 0 {
return ErrInvalidLengthRpc
}
postIndex := iNdEx + msglen
if postIndex < 0 {
return ErrInvalidLengthRpc
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
if m.Header == nil {
m.Header = &ResponseHeader{}
}
if err := m.Header.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
return err
}
iNdEx = postIndex
case 2:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Members", wireType)
}
var msglen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
msglen |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
if msglen < 0 {
return ErrInvalidLengthRpc
}
postIndex := iNdEx + msglen
if postIndex < 0 {
return ErrInvalidLengthRpc
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.Members = append(m.Members, &Member{})
if err := m.Members[len(m.Members)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
return err
}
iNdEx = postIndex
default:
iNdEx = preIndex
skippy, err := skipRpc(dAtA[iNdEx:])
if err != nil {
return err
}
if (skippy < 0) || (iNdEx+skippy) < 0 {
return ErrInvalidLengthRpc
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
iNdEx += skippy
}
}
if iNdEx > l {
return io.ErrUnexpectedEOF
}
return nil
}
func (m *MemberPromoteRequest) Unmarshal(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0
for iNdEx < l {
preIndex := iNdEx
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
fieldNum := int32(wire >> 3)
wireType := int(wire & 0x7)
if wireType == 4 {
return fmt.Errorf("proto: MemberPromoteRequest: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: MemberPromoteRequest: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
case 1:
if wireType != 0 {
return fmt.Errorf("proto: wrong wireType = %d for field ID", wireType)
}
m.ID = 0
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
m.ID |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
default:
iNdEx = preIndex
skippy, err := skipRpc(dAtA[iNdEx:])
if err != nil {
return err
}
if (skippy < 0) || (iNdEx+skippy) < 0 {
return ErrInvalidLengthRpc
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
iNdEx += skippy
}
}
if iNdEx > l {
return io.ErrUnexpectedEOF
}
return nil
}
func (m *MemberPromoteResponse) Unmarshal(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0
for iNdEx < l {
preIndex := iNdEx
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
fieldNum := int32(wire >> 3)
wireType := int(wire & 0x7)
if wireType == 4 {
return fmt.Errorf("proto: MemberPromoteResponse: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: MemberPromoteResponse: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
case 1:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Header", wireType)
}
var msglen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
msglen |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
if msglen < 0 {
return ErrInvalidLengthRpc
}
postIndex := iNdEx + msglen
if postIndex < 0 {
return ErrInvalidLengthRpc
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
if m.Header == nil {
m.Header = &ResponseHeader{}
}
if err := m.Header.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
return err
}
iNdEx = postIndex
case 2:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Members", wireType)
}
var msglen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
msglen |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
if msglen < 0 {
return ErrInvalidLengthRpc
}
postIndex := iNdEx + msglen
if postIndex < 0 {
return ErrInvalidLengthRpc
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.Members = append(m.Members, &Member{})
if err := m.Members[len(m.Members)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
return err
}
iNdEx = postIndex
default:
iNdEx = preIndex
skippy, err := skipRpc(dAtA[iNdEx:])
if err != nil {
return err
}
if (skippy < 0) || (iNdEx+skippy) < 0 {
return ErrInvalidLengthRpc
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
iNdEx += skippy
}
}
if iNdEx > l {
return io.ErrUnexpectedEOF
}
return nil
}
func (m *DefragmentRequest) Unmarshal(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0
for iNdEx < l {
preIndex := iNdEx
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
fieldNum := int32(wire >> 3)
wireType := int(wire & 0x7)
if wireType == 4 {
return fmt.Errorf("proto: DefragmentRequest: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: DefragmentRequest: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
default:
iNdEx = preIndex
skippy, err := skipRpc(dAtA[iNdEx:])
if err != nil {
return err
}
if (skippy < 0) || (iNdEx+skippy) < 0 {
return ErrInvalidLengthRpc
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
iNdEx += skippy
}
}
if iNdEx > l {
return io.ErrUnexpectedEOF
}
return nil
}
func (m *DefragmentResponse) Unmarshal(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0
for iNdEx < l {
preIndex := iNdEx
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
fieldNum := int32(wire >> 3)
wireType := int(wire & 0x7)
if wireType == 4 {
return fmt.Errorf("proto: DefragmentResponse: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: DefragmentResponse: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
case 1:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Header", wireType)
}
var msglen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
msglen |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
if msglen < 0 {
return ErrInvalidLengthRpc
}
postIndex := iNdEx + msglen
if postIndex < 0 {
return ErrInvalidLengthRpc
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
if m.Header == nil {
m.Header = &ResponseHeader{}
}
if err := m.Header.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
return err
}
iNdEx = postIndex
default:
iNdEx = preIndex
skippy, err := skipRpc(dAtA[iNdEx:])
if err != nil {
return err
}
if (skippy < 0) || (iNdEx+skippy) < 0 {
return ErrInvalidLengthRpc
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
iNdEx += skippy
}
}
if iNdEx > l {
return io.ErrUnexpectedEOF
}
return nil
}
func (m *MoveLeaderRequest) Unmarshal(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0
for iNdEx < l {
preIndex := iNdEx
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
fieldNum := int32(wire >> 3)
wireType := int(wire & 0x7)
if wireType == 4 {
return fmt.Errorf("proto: MoveLeaderRequest: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: MoveLeaderRequest: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
case 1:
if wireType != 0 {
return fmt.Errorf("proto: wrong wireType = %d for field TargetID", wireType)
}
m.TargetID = 0
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
m.TargetID |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
default:
iNdEx = preIndex
skippy, err := skipRpc(dAtA[iNdEx:])
if err != nil {
return err
}
if (skippy < 0) || (iNdEx+skippy) < 0 {
return ErrInvalidLengthRpc
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
iNdEx += skippy
}
}
if iNdEx > l {
return io.ErrUnexpectedEOF
}
return nil
}
func (m *MoveLeaderResponse) Unmarshal(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0
for iNdEx < l {
preIndex := iNdEx
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
fieldNum := int32(wire >> 3)
wireType := int(wire & 0x7)
if wireType == 4 {
return fmt.Errorf("proto: MoveLeaderResponse: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: MoveLeaderResponse: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
case 1:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Header", wireType)
}
var msglen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
msglen |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
if msglen < 0 {
return ErrInvalidLengthRpc
}
postIndex := iNdEx + msglen
if postIndex < 0 {
return ErrInvalidLengthRpc
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
if m.Header == nil {
m.Header = &ResponseHeader{}
}
if err := m.Header.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
return err
}
iNdEx = postIndex
default:
iNdEx = preIndex
skippy, err := skipRpc(dAtA[iNdEx:])
if err != nil {
return err
}
if (skippy < 0) || (iNdEx+skippy) < 0 {
return ErrInvalidLengthRpc
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
iNdEx += skippy
}
}
if iNdEx > l {
return io.ErrUnexpectedEOF
}
return nil
}
func (m *AlarmRequest) Unmarshal(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0
for iNdEx < l {
preIndex := iNdEx
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
fieldNum := int32(wire >> 3)
wireType := int(wire & 0x7)
if wireType == 4 {
return fmt.Errorf("proto: AlarmRequest: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: AlarmRequest: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
case 1:
if wireType != 0 {
return fmt.Errorf("proto: wrong wireType = %d for field Action", wireType)
}
m.Action = 0
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
m.Action |= AlarmRequest_AlarmAction(b&0x7F) << shift
if b < 0x80 {
break
}
}
case 2:
if wireType != 0 {
return fmt.Errorf("proto: wrong wireType = %d for field MemberID", wireType)
}
m.MemberID = 0
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
m.MemberID |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
case 3:
if wireType != 0 {
return fmt.Errorf("proto: wrong wireType = %d for field Alarm", wireType)
}
m.Alarm = 0
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
m.Alarm |= AlarmType(b&0x7F) << shift
if b < 0x80 {
break
}
}
default:
iNdEx = preIndex
skippy, err := skipRpc(dAtA[iNdEx:])
if err != nil {
return err
}
if (skippy < 0) || (iNdEx+skippy) < 0 {
return ErrInvalidLengthRpc
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
iNdEx += skippy
}
}
if iNdEx > l {
return io.ErrUnexpectedEOF
}
return nil
}
func (m *AlarmMember) Unmarshal(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0
for iNdEx < l {
preIndex := iNdEx
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
fieldNum := int32(wire >> 3)
wireType := int(wire & 0x7)
if wireType == 4 {
return fmt.Errorf("proto: AlarmMember: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: AlarmMember: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
case 1:
if wireType != 0 {
return fmt.Errorf("proto: wrong wireType = %d for field MemberID", wireType)
}
m.MemberID = 0
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
m.MemberID |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
case 2:
if wireType != 0 {
return fmt.Errorf("proto: wrong wireType = %d for field Alarm", wireType)
}
m.Alarm = 0
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
m.Alarm |= AlarmType(b&0x7F) << shift
if b < 0x80 {
break
}
}
default:
iNdEx = preIndex
skippy, err := skipRpc(dAtA[iNdEx:])
if err != nil {
return err
}
if (skippy < 0) || (iNdEx+skippy) < 0 {
return ErrInvalidLengthRpc
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
iNdEx += skippy
}
}
if iNdEx > l {
return io.ErrUnexpectedEOF
}
return nil
}
func (m *AlarmResponse) Unmarshal(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0
for iNdEx < l {
preIndex := iNdEx
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
fieldNum := int32(wire >> 3)
wireType := int(wire & 0x7)
if wireType == 4 {
return fmt.Errorf("proto: AlarmResponse: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: AlarmResponse: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
case 1:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Header", wireType)
}
var msglen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
msglen |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
if msglen < 0 {
return ErrInvalidLengthRpc
}
postIndex := iNdEx + msglen
if postIndex < 0 {
return ErrInvalidLengthRpc
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
if m.Header == nil {
m.Header = &ResponseHeader{}
}
if err := m.Header.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
return err
}
iNdEx = postIndex
case 2:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Alarms", wireType)
}
var msglen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
msglen |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
if msglen < 0 {
return ErrInvalidLengthRpc
}
postIndex := iNdEx + msglen
if postIndex < 0 {
return ErrInvalidLengthRpc
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.Alarms = append(m.Alarms, &AlarmMember{})
if err := m.Alarms[len(m.Alarms)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
return err
}
iNdEx = postIndex
default:
iNdEx = preIndex
skippy, err := skipRpc(dAtA[iNdEx:])
if err != nil {
return err
}
if (skippy < 0) || (iNdEx+skippy) < 0 {
return ErrInvalidLengthRpc
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
iNdEx += skippy
}
}
if iNdEx > l {
return io.ErrUnexpectedEOF
}
return nil
}
func (m *DowngradeRequest) Unmarshal(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0
for iNdEx < l {
preIndex := iNdEx
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
fieldNum := int32(wire >> 3)
wireType := int(wire & 0x7)
if wireType == 4 {
return fmt.Errorf("proto: DowngradeRequest: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: DowngradeRequest: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
case 1:
if wireType != 0 {
return fmt.Errorf("proto: wrong wireType = %d for field Action", wireType)
}
m.Action = 0
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
m.Action |= DowngradeRequest_DowngradeAction(b&0x7F) << shift
if b < 0x80 {
break
}
}
case 2:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Version", wireType)
}
var stringLen uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
stringLen |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
intStringLen := int(stringLen)
if intStringLen < 0 {
return ErrInvalidLengthRpc
}
postIndex := iNdEx + intStringLen
if postIndex < 0 {
return ErrInvalidLengthRpc
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.Version = string(dAtA[iNdEx:postIndex])
iNdEx = postIndex
default:
iNdEx = preIndex
skippy, err := skipRpc(dAtA[iNdEx:])
if err != nil {
return err
}
if (skippy < 0) || (iNdEx+skippy) < 0 {
return ErrInvalidLengthRpc
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
iNdEx += skippy
}
}
if iNdEx > l {
return io.ErrUnexpectedEOF
}
return nil
}
func (m *DowngradeResponse) Unmarshal(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0
for iNdEx < l {
preIndex := iNdEx
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
fieldNum := int32(wire >> 3)
wireType := int(wire & 0x7)
if wireType == 4 {
return fmt.Errorf("proto: DowngradeResponse: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: DowngradeResponse: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
case 1:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Header", wireType)
}
var msglen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
msglen |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
if msglen < 0 {
return ErrInvalidLengthRpc
}
postIndex := iNdEx + msglen
if postIndex < 0 {
return ErrInvalidLengthRpc
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
if m.Header == nil {
m.Header = &ResponseHeader{}
}
if err := m.Header.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
return err
}
iNdEx = postIndex
case 2:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Version", wireType)
}
var stringLen uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
stringLen |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
intStringLen := int(stringLen)
if intStringLen < 0 {
return ErrInvalidLengthRpc
}
postIndex := iNdEx + intStringLen
if postIndex < 0 {
return ErrInvalidLengthRpc
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.Version = string(dAtA[iNdEx:postIndex])
iNdEx = postIndex
default:
iNdEx = preIndex
skippy, err := skipRpc(dAtA[iNdEx:])
if err != nil {
return err
}
if (skippy < 0) || (iNdEx+skippy) < 0 {
return ErrInvalidLengthRpc
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
iNdEx += skippy
}
}
if iNdEx > l {
return io.ErrUnexpectedEOF
}
return nil
}
func (m *DowngradeVersionTestRequest) Unmarshal(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0
for iNdEx < l {
preIndex := iNdEx
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
fieldNum := int32(wire >> 3)
wireType := int(wire & 0x7)
if wireType == 4 {
return fmt.Errorf("proto: DowngradeVersionTestRequest: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: DowngradeVersionTestRequest: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
case 1:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Ver", wireType)
}
var stringLen uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
stringLen |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
intStringLen := int(stringLen)
if intStringLen < 0 {
return ErrInvalidLengthRpc
}
postIndex := iNdEx + intStringLen
if postIndex < 0 {
return ErrInvalidLengthRpc
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.Ver = string(dAtA[iNdEx:postIndex])
iNdEx = postIndex
default:
iNdEx = preIndex
skippy, err := skipRpc(dAtA[iNdEx:])
if err != nil {
return err
}
if (skippy < 0) || (iNdEx+skippy) < 0 {
return ErrInvalidLengthRpc
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
iNdEx += skippy
}
}
if iNdEx > l {
return io.ErrUnexpectedEOF
}
return nil
}
func (m *StatusRequest) Unmarshal(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0
for iNdEx < l {
preIndex := iNdEx
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
fieldNum := int32(wire >> 3)
wireType := int(wire & 0x7)
if wireType == 4 {
return fmt.Errorf("proto: StatusRequest: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: StatusRequest: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
default:
iNdEx = preIndex
skippy, err := skipRpc(dAtA[iNdEx:])
if err != nil {
return err
}
if (skippy < 0) || (iNdEx+skippy) < 0 {
return ErrInvalidLengthRpc
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
iNdEx += skippy
}
}
if iNdEx > l {
return io.ErrUnexpectedEOF
}
return nil
}
func (m *StatusResponse) Unmarshal(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0
for iNdEx < l {
preIndex := iNdEx
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
fieldNum := int32(wire >> 3)
wireType := int(wire & 0x7)
if wireType == 4 {
return fmt.Errorf("proto: StatusResponse: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: StatusResponse: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
case 1:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Header", wireType)
}
var msglen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
msglen |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
if msglen < 0 {
return ErrInvalidLengthRpc
}
postIndex := iNdEx + msglen
if postIndex < 0 {
return ErrInvalidLengthRpc
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
if m.Header == nil {
m.Header = &ResponseHeader{}
}
if err := m.Header.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
return err
}
iNdEx = postIndex
case 2:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Version", wireType)
}
var stringLen uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
stringLen |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
intStringLen := int(stringLen)
if intStringLen < 0 {
return ErrInvalidLengthRpc
}
postIndex := iNdEx + intStringLen
if postIndex < 0 {
return ErrInvalidLengthRpc
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.Version = string(dAtA[iNdEx:postIndex])
iNdEx = postIndex
case 3:
if wireType != 0 {
return fmt.Errorf("proto: wrong wireType = %d for field DbSize", wireType)
}
m.DbSize = 0
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
m.DbSize |= int64(b&0x7F) << shift
if b < 0x80 {
break
}
}
case 4:
if wireType != 0 {
return fmt.Errorf("proto: wrong wireType = %d for field Leader", wireType)
}
m.Leader = 0
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
m.Leader |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
case 5:
if wireType != 0 {
return fmt.Errorf("proto: wrong wireType = %d for field RaftIndex", wireType)
}
m.RaftIndex = 0
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
m.RaftIndex |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
case 6:
if wireType != 0 {
return fmt.Errorf("proto: wrong wireType = %d for field RaftTerm", wireType)
}
m.RaftTerm = 0
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
m.RaftTerm |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
case 7:
if wireType != 0 {
return fmt.Errorf("proto: wrong wireType = %d for field RaftAppliedIndex", wireType)
}
m.RaftAppliedIndex = 0
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
m.RaftAppliedIndex |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
case 8:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Errors", wireType)
}
var stringLen uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
stringLen |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
intStringLen := int(stringLen)
if intStringLen < 0 {
return ErrInvalidLengthRpc
}
postIndex := iNdEx + intStringLen
if postIndex < 0 {
return ErrInvalidLengthRpc
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.Errors = append(m.Errors, string(dAtA[iNdEx:postIndex]))
iNdEx = postIndex
case 9:
if wireType != 0 {
return fmt.Errorf("proto: wrong wireType = %d for field DbSizeInUse", wireType)
}
m.DbSizeInUse = 0
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
m.DbSizeInUse |= int64(b&0x7F) << shift
if b < 0x80 {
break
}
}
case 10:
if wireType != 0 {
return fmt.Errorf("proto: wrong wireType = %d for field IsLearner", wireType)
}
var v int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
v |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
m.IsLearner = bool(v != 0)
case 11:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field StorageVersion", wireType)
}
var stringLen uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
stringLen |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
intStringLen := int(stringLen)
if intStringLen < 0 {
return ErrInvalidLengthRpc
}
postIndex := iNdEx + intStringLen
if postIndex < 0 {
return ErrInvalidLengthRpc
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.StorageVersion = string(dAtA[iNdEx:postIndex])
iNdEx = postIndex
case 12:
if wireType != 0 {
return fmt.Errorf("proto: wrong wireType = %d for field DbSizeQuota", wireType)
}
m.DbSizeQuota = 0
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
m.DbSizeQuota |= int64(b&0x7F) << shift
if b < 0x80 {
break
}
}
case 13:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field DowngradeInfo", wireType)
}
var msglen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
msglen |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
if msglen < 0 {
return ErrInvalidLengthRpc
}
postIndex := iNdEx + msglen
if postIndex < 0 {
return ErrInvalidLengthRpc
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
if m.DowngradeInfo == nil {
m.DowngradeInfo = &DowngradeInfo{}
}
if err := m.DowngradeInfo.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
return err
}
iNdEx = postIndex
default:
iNdEx = preIndex
skippy, err := skipRpc(dAtA[iNdEx:])
if err != nil {
return err
}
if (skippy < 0) || (iNdEx+skippy) < 0 {
return ErrInvalidLengthRpc
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
iNdEx += skippy
}
}
if iNdEx > l {
return io.ErrUnexpectedEOF
}
return nil
}
func (m *DowngradeInfo) Unmarshal(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0
for iNdEx < l {
preIndex := iNdEx
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
fieldNum := int32(wire >> 3)
wireType := int(wire & 0x7)
if wireType == 4 {
return fmt.Errorf("proto: DowngradeInfo: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: DowngradeInfo: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
case 1:
if wireType != 0 {
return fmt.Errorf("proto: wrong wireType = %d for field Enabled", wireType)
}
var v int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
v |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
m.Enabled = bool(v != 0)
case 2:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field TargetVersion", wireType)
}
var stringLen uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
stringLen |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
intStringLen := int(stringLen)
if intStringLen < 0 {
return ErrInvalidLengthRpc
}
postIndex := iNdEx + intStringLen
if postIndex < 0 {
return ErrInvalidLengthRpc
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.TargetVersion = string(dAtA[iNdEx:postIndex])
iNdEx = postIndex
default:
iNdEx = preIndex
skippy, err := skipRpc(dAtA[iNdEx:])
if err != nil {
return err
}
if (skippy < 0) || (iNdEx+skippy) < 0 {
return ErrInvalidLengthRpc
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
iNdEx += skippy
}
}
if iNdEx > l {
return io.ErrUnexpectedEOF
}
return nil
}
func (m *AuthEnableRequest) Unmarshal(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0
for iNdEx < l {
preIndex := iNdEx
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
fieldNum := int32(wire >> 3)
wireType := int(wire & 0x7)
if wireType == 4 {
return fmt.Errorf("proto: AuthEnableRequest: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: AuthEnableRequest: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
default:
iNdEx = preIndex
skippy, err := skipRpc(dAtA[iNdEx:])
if err != nil {
return err
}
if (skippy < 0) || (iNdEx+skippy) < 0 {
return ErrInvalidLengthRpc
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
iNdEx += skippy
}
}
if iNdEx > l {
return io.ErrUnexpectedEOF
}
return nil
}
func (m *AuthDisableRequest) Unmarshal(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0
for iNdEx < l {
preIndex := iNdEx
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
fieldNum := int32(wire >> 3)
wireType := int(wire & 0x7)
if wireType == 4 {
return fmt.Errorf("proto: AuthDisableRequest: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: AuthDisableRequest: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
default:
iNdEx = preIndex
skippy, err := skipRpc(dAtA[iNdEx:])
if err != nil {
return err
}
if (skippy < 0) || (iNdEx+skippy) < 0 {
return ErrInvalidLengthRpc
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
iNdEx += skippy
}
}
if iNdEx > l {
return io.ErrUnexpectedEOF
}
return nil
}
func (m *AuthStatusRequest) Unmarshal(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0
for iNdEx < l {
preIndex := iNdEx
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
fieldNum := int32(wire >> 3)
wireType := int(wire & 0x7)
if wireType == 4 {
return fmt.Errorf("proto: AuthStatusRequest: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: AuthStatusRequest: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
default:
iNdEx = preIndex
skippy, err := skipRpc(dAtA[iNdEx:])
if err != nil {
return err
}
if (skippy < 0) || (iNdEx+skippy) < 0 {
return ErrInvalidLengthRpc
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
iNdEx += skippy
}
}
if iNdEx > l {
return io.ErrUnexpectedEOF
}
return nil
}
func (m *AuthenticateRequest) Unmarshal(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0
for iNdEx < l {
preIndex := iNdEx
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
fieldNum := int32(wire >> 3)
wireType := int(wire & 0x7)
if wireType == 4 {
return fmt.Errorf("proto: AuthenticateRequest: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: AuthenticateRequest: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
case 1:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType)
}
var stringLen uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
stringLen |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
intStringLen := int(stringLen)
if intStringLen < 0 {
return ErrInvalidLengthRpc
}
postIndex := iNdEx + intStringLen
if postIndex < 0 {
return ErrInvalidLengthRpc
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.Name = string(dAtA[iNdEx:postIndex])
iNdEx = postIndex
case 2:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Password", wireType)
}
var stringLen uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
stringLen |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
intStringLen := int(stringLen)
if intStringLen < 0 {
return ErrInvalidLengthRpc
}
postIndex := iNdEx + intStringLen
if postIndex < 0 {
return ErrInvalidLengthRpc
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.Password = string(dAtA[iNdEx:postIndex])
iNdEx = postIndex
default:
iNdEx = preIndex
skippy, err := skipRpc(dAtA[iNdEx:])
if err != nil {
return err
}
if (skippy < 0) || (iNdEx+skippy) < 0 {
return ErrInvalidLengthRpc
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
iNdEx += skippy
}
}
if iNdEx > l {
return io.ErrUnexpectedEOF
}
return nil
}
func (m *AuthUserAddRequest) Unmarshal(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0
for iNdEx < l {
preIndex := iNdEx
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
fieldNum := int32(wire >> 3)
wireType := int(wire & 0x7)
if wireType == 4 {
return fmt.Errorf("proto: AuthUserAddRequest: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: AuthUserAddRequest: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
case 1:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType)
}
var stringLen uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
stringLen |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
intStringLen := int(stringLen)
if intStringLen < 0 {
return ErrInvalidLengthRpc
}
postIndex := iNdEx + intStringLen
if postIndex < 0 {
return ErrInvalidLengthRpc
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.Name = string(dAtA[iNdEx:postIndex])
iNdEx = postIndex
case 2:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Password", wireType)
}
var stringLen uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
stringLen |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
intStringLen := int(stringLen)
if intStringLen < 0 {
return ErrInvalidLengthRpc
}
postIndex := iNdEx + intStringLen
if postIndex < 0 {
return ErrInvalidLengthRpc
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.Password = string(dAtA[iNdEx:postIndex])
iNdEx = postIndex
case 3:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Options", wireType)
}
var msglen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
msglen |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
if msglen < 0 {
return ErrInvalidLengthRpc
}
postIndex := iNdEx + msglen
if postIndex < 0 {
return ErrInvalidLengthRpc
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
if m.Options == nil {
m.Options = &authpb.UserAddOptions{}
}
if err := m.Options.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
return err
}
iNdEx = postIndex
case 4:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field HashedPassword", wireType)
}
var stringLen uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
stringLen |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
intStringLen := int(stringLen)
if intStringLen < 0 {
return ErrInvalidLengthRpc
}
postIndex := iNdEx + intStringLen
if postIndex < 0 {
return ErrInvalidLengthRpc
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.HashedPassword = string(dAtA[iNdEx:postIndex])
iNdEx = postIndex
default:
iNdEx = preIndex
skippy, err := skipRpc(dAtA[iNdEx:])
if err != nil {
return err
}
if (skippy < 0) || (iNdEx+skippy) < 0 {
return ErrInvalidLengthRpc
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
iNdEx += skippy
}
}
if iNdEx > l {
return io.ErrUnexpectedEOF
}
return nil
}
func (m *AuthUserGetRequest) Unmarshal(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0
for iNdEx < l {
preIndex := iNdEx
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
fieldNum := int32(wire >> 3)
wireType := int(wire & 0x7)
if wireType == 4 {
return fmt.Errorf("proto: AuthUserGetRequest: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: AuthUserGetRequest: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
case 1:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType)
}
var stringLen uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
stringLen |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
intStringLen := int(stringLen)
if intStringLen < 0 {
return ErrInvalidLengthRpc
}
postIndex := iNdEx + intStringLen
if postIndex < 0 {
return ErrInvalidLengthRpc
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.Name = string(dAtA[iNdEx:postIndex])
iNdEx = postIndex
default:
iNdEx = preIndex
skippy, err := skipRpc(dAtA[iNdEx:])
if err != nil {
return err
}
if (skippy < 0) || (iNdEx+skippy) < 0 {
return ErrInvalidLengthRpc
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
iNdEx += skippy
}
}
if iNdEx > l {
return io.ErrUnexpectedEOF
}
return nil
}
func (m *AuthUserDeleteRequest) Unmarshal(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0
for iNdEx < l {
preIndex := iNdEx
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
fieldNum := int32(wire >> 3)
wireType := int(wire & 0x7)
if wireType == 4 {
return fmt.Errorf("proto: AuthUserDeleteRequest: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: AuthUserDeleteRequest: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
case 1:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType)
}
var stringLen uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
stringLen |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
intStringLen := int(stringLen)
if intStringLen < 0 {
return ErrInvalidLengthRpc
}
postIndex := iNdEx + intStringLen
if postIndex < 0 {
return ErrInvalidLengthRpc
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.Name = string(dAtA[iNdEx:postIndex])
iNdEx = postIndex
default:
iNdEx = preIndex
skippy, err := skipRpc(dAtA[iNdEx:])
if err != nil {
return err
}
if (skippy < 0) || (iNdEx+skippy) < 0 {
return ErrInvalidLengthRpc
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
iNdEx += skippy
}
}
if iNdEx > l {
return io.ErrUnexpectedEOF
}
return nil
}
func (m *AuthUserChangePasswordRequest) Unmarshal(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0
for iNdEx < l {
preIndex := iNdEx
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
fieldNum := int32(wire >> 3)
wireType := int(wire & 0x7)
if wireType == 4 {
return fmt.Errorf("proto: AuthUserChangePasswordRequest: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: AuthUserChangePasswordRequest: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
case 1:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType)
}
var stringLen uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
stringLen |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
intStringLen := int(stringLen)
if intStringLen < 0 {
return ErrInvalidLengthRpc
}
postIndex := iNdEx + intStringLen
if postIndex < 0 {
return ErrInvalidLengthRpc
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.Name = string(dAtA[iNdEx:postIndex])
iNdEx = postIndex
case 2:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Password", wireType)
}
var stringLen uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
stringLen |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
intStringLen := int(stringLen)
if intStringLen < 0 {
return ErrInvalidLengthRpc
}
postIndex := iNdEx + intStringLen
if postIndex < 0 {
return ErrInvalidLengthRpc
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.Password = string(dAtA[iNdEx:postIndex])
iNdEx = postIndex
case 3:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field HashedPassword", wireType)
}
var stringLen uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
stringLen |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
intStringLen := int(stringLen)
if intStringLen < 0 {
return ErrInvalidLengthRpc
}
postIndex := iNdEx + intStringLen
if postIndex < 0 {
return ErrInvalidLengthRpc
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.HashedPassword = string(dAtA[iNdEx:postIndex])
iNdEx = postIndex
default:
iNdEx = preIndex
skippy, err := skipRpc(dAtA[iNdEx:])
if err != nil {
return err
}
if (skippy < 0) || (iNdEx+skippy) < 0 {
return ErrInvalidLengthRpc
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
iNdEx += skippy
}
}
if iNdEx > l {
return io.ErrUnexpectedEOF
}
return nil
}
func (m *AuthUserGrantRoleRequest) Unmarshal(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0
for iNdEx < l {
preIndex := iNdEx
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
fieldNum := int32(wire >> 3)
wireType := int(wire & 0x7)
if wireType == 4 {
return fmt.Errorf("proto: AuthUserGrantRoleRequest: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: AuthUserGrantRoleRequest: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
case 1:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field User", wireType)
}
var stringLen uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
stringLen |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
intStringLen := int(stringLen)
if intStringLen < 0 {
return ErrInvalidLengthRpc
}
postIndex := iNdEx + intStringLen
if postIndex < 0 {
return ErrInvalidLengthRpc
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.User = string(dAtA[iNdEx:postIndex])
iNdEx = postIndex
case 2:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Role", wireType)
}
var stringLen uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
stringLen |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
intStringLen := int(stringLen)
if intStringLen < 0 {
return ErrInvalidLengthRpc
}
postIndex := iNdEx + intStringLen
if postIndex < 0 {
return ErrInvalidLengthRpc
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.Role = string(dAtA[iNdEx:postIndex])
iNdEx = postIndex
default:
iNdEx = preIndex
skippy, err := skipRpc(dAtA[iNdEx:])
if err != nil {
return err
}
if (skippy < 0) || (iNdEx+skippy) < 0 {
return ErrInvalidLengthRpc
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
iNdEx += skippy
}
}
if iNdEx > l {
return io.ErrUnexpectedEOF
}
return nil
}
func (m *AuthUserRevokeRoleRequest) Unmarshal(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0
for iNdEx < l {
preIndex := iNdEx
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
fieldNum := int32(wire >> 3)
wireType := int(wire & 0x7)
if wireType == 4 {
return fmt.Errorf("proto: AuthUserRevokeRoleRequest: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: AuthUserRevokeRoleRequest: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
case 1:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType)
}
var stringLen uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
stringLen |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
intStringLen := int(stringLen)
if intStringLen < 0 {
return ErrInvalidLengthRpc
}
postIndex := iNdEx + intStringLen
if postIndex < 0 {
return ErrInvalidLengthRpc
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.Name = string(dAtA[iNdEx:postIndex])
iNdEx = postIndex
case 2:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Role", wireType)
}
var stringLen uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
stringLen |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
intStringLen := int(stringLen)
if intStringLen < 0 {
return ErrInvalidLengthRpc
}
postIndex := iNdEx + intStringLen
if postIndex < 0 {
return ErrInvalidLengthRpc
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.Role = string(dAtA[iNdEx:postIndex])
iNdEx = postIndex
default:
iNdEx = preIndex
skippy, err := skipRpc(dAtA[iNdEx:])
if err != nil {
return err
}
if (skippy < 0) || (iNdEx+skippy) < 0 {
return ErrInvalidLengthRpc
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
iNdEx += skippy
}
}
if iNdEx > l {
return io.ErrUnexpectedEOF
}
return nil
}
func (m *AuthRoleAddRequest) Unmarshal(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0
for iNdEx < l {
preIndex := iNdEx
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
fieldNum := int32(wire >> 3)
wireType := int(wire & 0x7)
if wireType == 4 {
return fmt.Errorf("proto: AuthRoleAddRequest: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: AuthRoleAddRequest: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
case 1:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType)
}
var stringLen uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
stringLen |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
intStringLen := int(stringLen)
if intStringLen < 0 {
return ErrInvalidLengthRpc
}
postIndex := iNdEx + intStringLen
if postIndex < 0 {
return ErrInvalidLengthRpc
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.Name = string(dAtA[iNdEx:postIndex])
iNdEx = postIndex
default:
iNdEx = preIndex
skippy, err := skipRpc(dAtA[iNdEx:])
if err != nil {
return err
}
if (skippy < 0) || (iNdEx+skippy) < 0 {
return ErrInvalidLengthRpc
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
iNdEx += skippy
}
}
if iNdEx > l {
return io.ErrUnexpectedEOF
}
return nil
}
func (m *AuthRoleGetRequest) Unmarshal(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0
for iNdEx < l {
preIndex := iNdEx
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
fieldNum := int32(wire >> 3)
wireType := int(wire & 0x7)
if wireType == 4 {
return fmt.Errorf("proto: AuthRoleGetRequest: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: AuthRoleGetRequest: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
case 1:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Role", wireType)
}
var stringLen uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
stringLen |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
intStringLen := int(stringLen)
if intStringLen < 0 {
return ErrInvalidLengthRpc
}
postIndex := iNdEx + intStringLen
if postIndex < 0 {
return ErrInvalidLengthRpc
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.Role = string(dAtA[iNdEx:postIndex])
iNdEx = postIndex
default:
iNdEx = preIndex
skippy, err := skipRpc(dAtA[iNdEx:])
if err != nil {
return err
}
if (skippy < 0) || (iNdEx+skippy) < 0 {
return ErrInvalidLengthRpc
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
iNdEx += skippy
}
}
if iNdEx > l {
return io.ErrUnexpectedEOF
}
return nil
}
func (m *AuthUserListRequest) Unmarshal(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0
for iNdEx < l {
preIndex := iNdEx
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
fieldNum := int32(wire >> 3)
wireType := int(wire & 0x7)
if wireType == 4 {
return fmt.Errorf("proto: AuthUserListRequest: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: AuthUserListRequest: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
default:
iNdEx = preIndex
skippy, err := skipRpc(dAtA[iNdEx:])
if err != nil {
return err
}
if (skippy < 0) || (iNdEx+skippy) < 0 {
return ErrInvalidLengthRpc
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
iNdEx += skippy
}
}
if iNdEx > l {
return io.ErrUnexpectedEOF
}
return nil
}
func (m *AuthRoleListRequest) Unmarshal(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0
for iNdEx < l {
preIndex := iNdEx
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
fieldNum := int32(wire >> 3)
wireType := int(wire & 0x7)
if wireType == 4 {
return fmt.Errorf("proto: AuthRoleListRequest: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: AuthRoleListRequest: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
default:
iNdEx = preIndex
skippy, err := skipRpc(dAtA[iNdEx:])
if err != nil {
return err
}
if (skippy < 0) || (iNdEx+skippy) < 0 {
return ErrInvalidLengthRpc
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
iNdEx += skippy
}
}
if iNdEx > l {
return io.ErrUnexpectedEOF
}
return nil
}
func (m *AuthRoleDeleteRequest) Unmarshal(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0
for iNdEx < l {
preIndex := iNdEx
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
fieldNum := int32(wire >> 3)
wireType := int(wire & 0x7)
if wireType == 4 {
return fmt.Errorf("proto: AuthRoleDeleteRequest: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: AuthRoleDeleteRequest: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
case 1:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Role", wireType)
}
var stringLen uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
stringLen |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
intStringLen := int(stringLen)
if intStringLen < 0 {
return ErrInvalidLengthRpc
}
postIndex := iNdEx + intStringLen
if postIndex < 0 {
return ErrInvalidLengthRpc
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.Role = string(dAtA[iNdEx:postIndex])
iNdEx = postIndex
default:
iNdEx = preIndex
skippy, err := skipRpc(dAtA[iNdEx:])
if err != nil {
return err
}
if (skippy < 0) || (iNdEx+skippy) < 0 {
return ErrInvalidLengthRpc
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
iNdEx += skippy
}
}
if iNdEx > l {
return io.ErrUnexpectedEOF
}
return nil
}
func (m *AuthRoleGrantPermissionRequest) Unmarshal(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0
for iNdEx < l {
preIndex := iNdEx
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
fieldNum := int32(wire >> 3)
wireType := int(wire & 0x7)
if wireType == 4 {
return fmt.Errorf("proto: AuthRoleGrantPermissionRequest: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: AuthRoleGrantPermissionRequest: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
case 1:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType)
}
var stringLen uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
stringLen |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
intStringLen := int(stringLen)
if intStringLen < 0 {
return ErrInvalidLengthRpc
}
postIndex := iNdEx + intStringLen
if postIndex < 0 {
return ErrInvalidLengthRpc
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.Name = string(dAtA[iNdEx:postIndex])
iNdEx = postIndex
case 2:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Perm", wireType)
}
var msglen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
msglen |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
if msglen < 0 {
return ErrInvalidLengthRpc
}
postIndex := iNdEx + msglen
if postIndex < 0 {
return ErrInvalidLengthRpc
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
if m.Perm == nil {
m.Perm = &authpb.Permission{}
}
if err := m.Perm.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
return err
}
iNdEx = postIndex
default:
iNdEx = preIndex
skippy, err := skipRpc(dAtA[iNdEx:])
if err != nil {
return err
}
if (skippy < 0) || (iNdEx+skippy) < 0 {
return ErrInvalidLengthRpc
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
iNdEx += skippy
}
}
if iNdEx > l {
return io.ErrUnexpectedEOF
}
return nil
}
func (m *AuthRoleRevokePermissionRequest) Unmarshal(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0
for iNdEx < l {
preIndex := iNdEx
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
fieldNum := int32(wire >> 3)
wireType := int(wire & 0x7)
if wireType == 4 {
return fmt.Errorf("proto: AuthRoleRevokePermissionRequest: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: AuthRoleRevokePermissionRequest: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
case 1:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Role", wireType)
}
var stringLen uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
stringLen |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
intStringLen := int(stringLen)
if intStringLen < 0 {
return ErrInvalidLengthRpc
}
postIndex := iNdEx + intStringLen
if postIndex < 0 {
return ErrInvalidLengthRpc
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.Role = string(dAtA[iNdEx:postIndex])
iNdEx = postIndex
case 2:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Key", wireType)
}
var byteLen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
byteLen |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
if byteLen < 0 {
return ErrInvalidLengthRpc
}
postIndex := iNdEx + byteLen
if postIndex < 0 {
return ErrInvalidLengthRpc
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.Key = append(m.Key[:0], dAtA[iNdEx:postIndex]...)
if m.Key == nil {
m.Key = []byte{}
}
iNdEx = postIndex
case 3:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field RangeEnd", wireType)
}
var byteLen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
byteLen |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
if byteLen < 0 {
return ErrInvalidLengthRpc
}
postIndex := iNdEx + byteLen
if postIndex < 0 {
return ErrInvalidLengthRpc
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.RangeEnd = append(m.RangeEnd[:0], dAtA[iNdEx:postIndex]...)
if m.RangeEnd == nil {
m.RangeEnd = []byte{}
}
iNdEx = postIndex
default:
iNdEx = preIndex
skippy, err := skipRpc(dAtA[iNdEx:])
if err != nil {
return err
}
if (skippy < 0) || (iNdEx+skippy) < 0 {
return ErrInvalidLengthRpc
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
iNdEx += skippy
}
}
if iNdEx > l {
return io.ErrUnexpectedEOF
}
return nil
}
func (m *AuthEnableResponse) Unmarshal(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0
for iNdEx < l {
preIndex := iNdEx
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
fieldNum := int32(wire >> 3)
wireType := int(wire & 0x7)
if wireType == 4 {
return fmt.Errorf("proto: AuthEnableResponse: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: AuthEnableResponse: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
case 1:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Header", wireType)
}
var msglen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
msglen |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
if msglen < 0 {
return ErrInvalidLengthRpc
}
postIndex := iNdEx + msglen
if postIndex < 0 {
return ErrInvalidLengthRpc
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
if m.Header == nil {
m.Header = &ResponseHeader{}
}
if err := m.Header.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
return err
}
iNdEx = postIndex
default:
iNdEx = preIndex
skippy, err := skipRpc(dAtA[iNdEx:])
if err != nil {
return err
}
if (skippy < 0) || (iNdEx+skippy) < 0 {
return ErrInvalidLengthRpc
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
iNdEx += skippy
}
}
if iNdEx > l {
return io.ErrUnexpectedEOF
}
return nil
}
func (m *AuthDisableResponse) Unmarshal(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0
for iNdEx < l {
preIndex := iNdEx
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
fieldNum := int32(wire >> 3)
wireType := int(wire & 0x7)
if wireType == 4 {
return fmt.Errorf("proto: AuthDisableResponse: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: AuthDisableResponse: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
case 1:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Header", wireType)
}
var msglen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
msglen |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
if msglen < 0 {
return ErrInvalidLengthRpc
}
postIndex := iNdEx + msglen
if postIndex < 0 {
return ErrInvalidLengthRpc
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
if m.Header == nil {
m.Header = &ResponseHeader{}
}
if err := m.Header.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
return err
}
iNdEx = postIndex
default:
iNdEx = preIndex
skippy, err := skipRpc(dAtA[iNdEx:])
if err != nil {
return err
}
if (skippy < 0) || (iNdEx+skippy) < 0 {
return ErrInvalidLengthRpc
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
iNdEx += skippy
}
}
if iNdEx > l {
return io.ErrUnexpectedEOF
}
return nil
}
func (m *AuthStatusResponse) Unmarshal(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0
for iNdEx < l {
preIndex := iNdEx
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
fieldNum := int32(wire >> 3)
wireType := int(wire & 0x7)
if wireType == 4 {
return fmt.Errorf("proto: AuthStatusResponse: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: AuthStatusResponse: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
case 1:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Header", wireType)
}
var msglen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
msglen |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
if msglen < 0 {
return ErrInvalidLengthRpc
}
postIndex := iNdEx + msglen
if postIndex < 0 {
return ErrInvalidLengthRpc
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
if m.Header == nil {
m.Header = &ResponseHeader{}
}
if err := m.Header.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
return err
}
iNdEx = postIndex
case 2:
if wireType != 0 {
return fmt.Errorf("proto: wrong wireType = %d for field Enabled", wireType)
}
var v int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
v |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
m.Enabled = bool(v != 0)
case 3:
if wireType != 0 {
return fmt.Errorf("proto: wrong wireType = %d for field AuthRevision", wireType)
}
m.AuthRevision = 0
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
m.AuthRevision |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
default:
iNdEx = preIndex
skippy, err := skipRpc(dAtA[iNdEx:])
if err != nil {
return err
}
if (skippy < 0) || (iNdEx+skippy) < 0 {
return ErrInvalidLengthRpc
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
iNdEx += skippy
}
}
if iNdEx > l {
return io.ErrUnexpectedEOF
}
return nil
}
func (m *AuthenticateResponse) Unmarshal(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0
for iNdEx < l {
preIndex := iNdEx
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
fieldNum := int32(wire >> 3)
wireType := int(wire & 0x7)
if wireType == 4 {
return fmt.Errorf("proto: AuthenticateResponse: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: AuthenticateResponse: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
case 1:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Header", wireType)
}
var msglen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
msglen |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
if msglen < 0 {
return ErrInvalidLengthRpc
}
postIndex := iNdEx + msglen
if postIndex < 0 {
return ErrInvalidLengthRpc
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
if m.Header == nil {
m.Header = &ResponseHeader{}
}
if err := m.Header.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
return err
}
iNdEx = postIndex
case 2:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Token", wireType)
}
var stringLen uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
stringLen |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
intStringLen := int(stringLen)
if intStringLen < 0 {
return ErrInvalidLengthRpc
}
postIndex := iNdEx + intStringLen
if postIndex < 0 {
return ErrInvalidLengthRpc
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.Token = string(dAtA[iNdEx:postIndex])
iNdEx = postIndex
default:
iNdEx = preIndex
skippy, err := skipRpc(dAtA[iNdEx:])
if err != nil {
return err
}
if (skippy < 0) || (iNdEx+skippy) < 0 {
return ErrInvalidLengthRpc
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
iNdEx += skippy
}
}
if iNdEx > l {
return io.ErrUnexpectedEOF
}
return nil
}
func (m *AuthUserAddResponse) Unmarshal(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0
for iNdEx < l {
preIndex := iNdEx
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
fieldNum := int32(wire >> 3)
wireType := int(wire & 0x7)
if wireType == 4 {
return fmt.Errorf("proto: AuthUserAddResponse: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: AuthUserAddResponse: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
case 1:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Header", wireType)
}
var msglen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
msglen |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
if msglen < 0 {
return ErrInvalidLengthRpc
}
postIndex := iNdEx + msglen
if postIndex < 0 {
return ErrInvalidLengthRpc
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
if m.Header == nil {
m.Header = &ResponseHeader{}
}
if err := m.Header.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
return err
}
iNdEx = postIndex
default:
iNdEx = preIndex
skippy, err := skipRpc(dAtA[iNdEx:])
if err != nil {
return err
}
if (skippy < 0) || (iNdEx+skippy) < 0 {
return ErrInvalidLengthRpc
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
iNdEx += skippy
}
}
if iNdEx > l {
return io.ErrUnexpectedEOF
}
return nil
}
func (m *AuthUserGetResponse) Unmarshal(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0
for iNdEx < l {
preIndex := iNdEx
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
fieldNum := int32(wire >> 3)
wireType := int(wire & 0x7)
if wireType == 4 {
return fmt.Errorf("proto: AuthUserGetResponse: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: AuthUserGetResponse: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
case 1:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Header", wireType)
}
var msglen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
msglen |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
if msglen < 0 {
return ErrInvalidLengthRpc
}
postIndex := iNdEx + msglen
if postIndex < 0 {
return ErrInvalidLengthRpc
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
if m.Header == nil {
m.Header = &ResponseHeader{}
}
if err := m.Header.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
return err
}
iNdEx = postIndex
case 2:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Roles", wireType)
}
var stringLen uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
stringLen |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
intStringLen := int(stringLen)
if intStringLen < 0 {
return ErrInvalidLengthRpc
}
postIndex := iNdEx + intStringLen
if postIndex < 0 {
return ErrInvalidLengthRpc
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.Roles = append(m.Roles, string(dAtA[iNdEx:postIndex]))
iNdEx = postIndex
default:
iNdEx = preIndex
skippy, err := skipRpc(dAtA[iNdEx:])
if err != nil {
return err
}
if (skippy < 0) || (iNdEx+skippy) < 0 {
return ErrInvalidLengthRpc
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
iNdEx += skippy
}
}
if iNdEx > l {
return io.ErrUnexpectedEOF
}
return nil
}
func (m *AuthUserDeleteResponse) Unmarshal(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0
for iNdEx < l {
preIndex := iNdEx
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
fieldNum := int32(wire >> 3)
wireType := int(wire & 0x7)
if wireType == 4 {
return fmt.Errorf("proto: AuthUserDeleteResponse: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: AuthUserDeleteResponse: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
case 1:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Header", wireType)
}
var msglen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
msglen |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
if msglen < 0 {
return ErrInvalidLengthRpc
}
postIndex := iNdEx + msglen
if postIndex < 0 {
return ErrInvalidLengthRpc
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
if m.Header == nil {
m.Header = &ResponseHeader{}
}
if err := m.Header.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
return err
}
iNdEx = postIndex
default:
iNdEx = preIndex
skippy, err := skipRpc(dAtA[iNdEx:])
if err != nil {
return err
}
if (skippy < 0) || (iNdEx+skippy) < 0 {
return ErrInvalidLengthRpc
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
iNdEx += skippy
}
}
if iNdEx > l {
return io.ErrUnexpectedEOF
}
return nil
}
func (m *AuthUserChangePasswordResponse) Unmarshal(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0
for iNdEx < l {
preIndex := iNdEx
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
fieldNum := int32(wire >> 3)
wireType := int(wire & 0x7)
if wireType == 4 {
return fmt.Errorf("proto: AuthUserChangePasswordResponse: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: AuthUserChangePasswordResponse: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
case 1:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Header", wireType)
}
var msglen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
msglen |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
if msglen < 0 {
return ErrInvalidLengthRpc
}
postIndex := iNdEx + msglen
if postIndex < 0 {
return ErrInvalidLengthRpc
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
if m.Header == nil {
m.Header = &ResponseHeader{}
}
if err := m.Header.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
return err
}
iNdEx = postIndex
default:
iNdEx = preIndex
skippy, err := skipRpc(dAtA[iNdEx:])
if err != nil {
return err
}
if (skippy < 0) || (iNdEx+skippy) < 0 {
return ErrInvalidLengthRpc
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
iNdEx += skippy
}
}
if iNdEx > l {
return io.ErrUnexpectedEOF
}
return nil
}
func (m *AuthUserGrantRoleResponse) Unmarshal(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0
for iNdEx < l {
preIndex := iNdEx
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
fieldNum := int32(wire >> 3)
wireType := int(wire & 0x7)
if wireType == 4 {
return fmt.Errorf("proto: AuthUserGrantRoleResponse: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: AuthUserGrantRoleResponse: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
case 1:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Header", wireType)
}
var msglen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
msglen |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
if msglen < 0 {
return ErrInvalidLengthRpc
}
postIndex := iNdEx + msglen
if postIndex < 0 {
return ErrInvalidLengthRpc
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
if m.Header == nil {
m.Header = &ResponseHeader{}
}
if err := m.Header.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
return err
}
iNdEx = postIndex
default:
iNdEx = preIndex
skippy, err := skipRpc(dAtA[iNdEx:])
if err != nil {
return err
}
if (skippy < 0) || (iNdEx+skippy) < 0 {
return ErrInvalidLengthRpc
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
iNdEx += skippy
}
}
if iNdEx > l {
return io.ErrUnexpectedEOF
}
return nil
}
func (m *AuthUserRevokeRoleResponse) Unmarshal(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0
for iNdEx < l {
preIndex := iNdEx
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
fieldNum := int32(wire >> 3)
wireType := int(wire & 0x7)
if wireType == 4 {
return fmt.Errorf("proto: AuthUserRevokeRoleResponse: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: AuthUserRevokeRoleResponse: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
case 1:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Header", wireType)
}
var msglen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
msglen |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
if msglen < 0 {
return ErrInvalidLengthRpc
}
postIndex := iNdEx + msglen
if postIndex < 0 {
return ErrInvalidLengthRpc
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
if m.Header == nil {
m.Header = &ResponseHeader{}
}
if err := m.Header.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
return err
}
iNdEx = postIndex
default:
iNdEx = preIndex
skippy, err := skipRpc(dAtA[iNdEx:])
if err != nil {
return err
}
if (skippy < 0) || (iNdEx+skippy) < 0 {
return ErrInvalidLengthRpc
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
iNdEx += skippy
}
}
if iNdEx > l {
return io.ErrUnexpectedEOF
}
return nil
}
func (m *AuthRoleAddResponse) Unmarshal(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0
for iNdEx < l {
preIndex := iNdEx
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
fieldNum := int32(wire >> 3)
wireType := int(wire & 0x7)
if wireType == 4 {
return fmt.Errorf("proto: AuthRoleAddResponse: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: AuthRoleAddResponse: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
case 1:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Header", wireType)
}
var msglen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
msglen |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
if msglen < 0 {
return ErrInvalidLengthRpc
}
postIndex := iNdEx + msglen
if postIndex < 0 {
return ErrInvalidLengthRpc
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
if m.Header == nil {
m.Header = &ResponseHeader{}
}
if err := m.Header.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
return err
}
iNdEx = postIndex
default:
iNdEx = preIndex
skippy, err := skipRpc(dAtA[iNdEx:])
if err != nil {
return err
}
if (skippy < 0) || (iNdEx+skippy) < 0 {
return ErrInvalidLengthRpc
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
iNdEx += skippy
}
}
if iNdEx > l {
return io.ErrUnexpectedEOF
}
return nil
}
func (m *AuthRoleGetResponse) Unmarshal(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0
for iNdEx < l {
preIndex := iNdEx
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
fieldNum := int32(wire >> 3)
wireType := int(wire & 0x7)
if wireType == 4 {
return fmt.Errorf("proto: AuthRoleGetResponse: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: AuthRoleGetResponse: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
case 1:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Header", wireType)
}
var msglen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
msglen |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
if msglen < 0 {
return ErrInvalidLengthRpc
}
postIndex := iNdEx + msglen
if postIndex < 0 {
return ErrInvalidLengthRpc
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
if m.Header == nil {
m.Header = &ResponseHeader{}
}
if err := m.Header.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
return err
}
iNdEx = postIndex
case 2:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Perm", wireType)
}
var msglen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
msglen |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
if msglen < 0 {
return ErrInvalidLengthRpc
}
postIndex := iNdEx + msglen
if postIndex < 0 {
return ErrInvalidLengthRpc
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.Perm = append(m.Perm, &authpb.Permission{})
if err := m.Perm[len(m.Perm)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
return err
}
iNdEx = postIndex
default:
iNdEx = preIndex
skippy, err := skipRpc(dAtA[iNdEx:])
if err != nil {
return err
}
if (skippy < 0) || (iNdEx+skippy) < 0 {
return ErrInvalidLengthRpc
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
iNdEx += skippy
}
}
if iNdEx > l {
return io.ErrUnexpectedEOF
}
return nil
}
func (m *AuthRoleListResponse) Unmarshal(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0
for iNdEx < l {
preIndex := iNdEx
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
fieldNum := int32(wire >> 3)
wireType := int(wire & 0x7)
if wireType == 4 {
return fmt.Errorf("proto: AuthRoleListResponse: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: AuthRoleListResponse: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
case 1:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Header", wireType)
}
var msglen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
msglen |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
if msglen < 0 {
return ErrInvalidLengthRpc
}
postIndex := iNdEx + msglen
if postIndex < 0 {
return ErrInvalidLengthRpc
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
if m.Header == nil {
m.Header = &ResponseHeader{}
}
if err := m.Header.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
return err
}
iNdEx = postIndex
case 2:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Roles", wireType)
}
var stringLen uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
stringLen |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
intStringLen := int(stringLen)
if intStringLen < 0 {
return ErrInvalidLengthRpc
}
postIndex := iNdEx + intStringLen
if postIndex < 0 {
return ErrInvalidLengthRpc
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.Roles = append(m.Roles, string(dAtA[iNdEx:postIndex]))
iNdEx = postIndex
default:
iNdEx = preIndex
skippy, err := skipRpc(dAtA[iNdEx:])
if err != nil {
return err
}
if (skippy < 0) || (iNdEx+skippy) < 0 {
return ErrInvalidLengthRpc
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
iNdEx += skippy
}
}
if iNdEx > l {
return io.ErrUnexpectedEOF
}
return nil
}
func (m *AuthUserListResponse) Unmarshal(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0
for iNdEx < l {
preIndex := iNdEx
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
fieldNum := int32(wire >> 3)
wireType := int(wire & 0x7)
if wireType == 4 {
return fmt.Errorf("proto: AuthUserListResponse: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: AuthUserListResponse: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
case 1:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Header", wireType)
}
var msglen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
msglen |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
if msglen < 0 {
return ErrInvalidLengthRpc
}
postIndex := iNdEx + msglen
if postIndex < 0 {
return ErrInvalidLengthRpc
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
if m.Header == nil {
m.Header = &ResponseHeader{}
}
if err := m.Header.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
return err
}
iNdEx = postIndex
case 2:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Users", wireType)
}
var stringLen uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
stringLen |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
intStringLen := int(stringLen)
if intStringLen < 0 {
return ErrInvalidLengthRpc
}
postIndex := iNdEx + intStringLen
if postIndex < 0 {
return ErrInvalidLengthRpc
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.Users = append(m.Users, string(dAtA[iNdEx:postIndex]))
iNdEx = postIndex
default:
iNdEx = preIndex
skippy, err := skipRpc(dAtA[iNdEx:])
if err != nil {
return err
}
if (skippy < 0) || (iNdEx+skippy) < 0 {
return ErrInvalidLengthRpc
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
iNdEx += skippy
}
}
if iNdEx > l {
return io.ErrUnexpectedEOF
}
return nil
}
func (m *AuthRoleDeleteResponse) Unmarshal(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0
for iNdEx < l {
preIndex := iNdEx
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
fieldNum := int32(wire >> 3)
wireType := int(wire & 0x7)
if wireType == 4 {
return fmt.Errorf("proto: AuthRoleDeleteResponse: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: AuthRoleDeleteResponse: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
case 1:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Header", wireType)
}
var msglen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
msglen |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
if msglen < 0 {
return ErrInvalidLengthRpc
}
postIndex := iNdEx + msglen
if postIndex < 0 {
return ErrInvalidLengthRpc
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
if m.Header == nil {
m.Header = &ResponseHeader{}
}
if err := m.Header.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
return err
}
iNdEx = postIndex
default:
iNdEx = preIndex
skippy, err := skipRpc(dAtA[iNdEx:])
if err != nil {
return err
}
if (skippy < 0) || (iNdEx+skippy) < 0 {
return ErrInvalidLengthRpc
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
iNdEx += skippy
}
}
if iNdEx > l {
return io.ErrUnexpectedEOF
}
return nil
}
func (m *AuthRoleGrantPermissionResponse) Unmarshal(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0
for iNdEx < l {
preIndex := iNdEx
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
fieldNum := int32(wire >> 3)
wireType := int(wire & 0x7)
if wireType == 4 {
return fmt.Errorf("proto: AuthRoleGrantPermissionResponse: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: AuthRoleGrantPermissionResponse: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
case 1:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Header", wireType)
}
var msglen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
msglen |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
if msglen < 0 {
return ErrInvalidLengthRpc
}
postIndex := iNdEx + msglen
if postIndex < 0 {
return ErrInvalidLengthRpc
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
if m.Header == nil {
m.Header = &ResponseHeader{}
}
if err := m.Header.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
return err
}
iNdEx = postIndex
default:
iNdEx = preIndex
skippy, err := skipRpc(dAtA[iNdEx:])
if err != nil {
return err
}
if (skippy < 0) || (iNdEx+skippy) < 0 {
return ErrInvalidLengthRpc
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
iNdEx += skippy
}
}
if iNdEx > l {
return io.ErrUnexpectedEOF
}
return nil
}
func (m *AuthRoleRevokePermissionResponse) Unmarshal(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0
for iNdEx < l {
preIndex := iNdEx
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
fieldNum := int32(wire >> 3)
wireType := int(wire & 0x7)
if wireType == 4 {
return fmt.Errorf("proto: AuthRoleRevokePermissionResponse: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: AuthRoleRevokePermissionResponse: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
case 1:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Header", wireType)
}
var msglen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
msglen |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
if msglen < 0 {
return ErrInvalidLengthRpc
}
postIndex := iNdEx + msglen
if postIndex < 0 {
return ErrInvalidLengthRpc
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
if m.Header == nil {
m.Header = &ResponseHeader{}
}
if err := m.Header.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
return err
}
iNdEx = postIndex
default:
iNdEx = preIndex
skippy, err := skipRpc(dAtA[iNdEx:])
if err != nil {
return err
}
if (skippy < 0) || (iNdEx+skippy) < 0 {
return ErrInvalidLengthRpc
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
iNdEx += skippy
}
}
if iNdEx > l {
return io.ErrUnexpectedEOF
}
return nil
}
func skipRpc(dAtA []byte) (n int, err error) {
l := len(dAtA)
iNdEx := 0
depth := 0
for iNdEx < l {
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return 0, ErrIntOverflowRpc
}
if iNdEx >= l {
return 0, io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= (uint64(b) & 0x7F) << shift
if b < 0x80 {
break
}
}
wireType := int(wire & 0x7)
switch wireType {
case 0:
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return 0, ErrIntOverflowRpc
}
if iNdEx >= l {
return 0, io.ErrUnexpectedEOF
}
iNdEx++
if dAtA[iNdEx-1] < 0x80 {
break
}
}
case 1:
iNdEx += 8
case 2:
var length int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return 0, ErrIntOverflowRpc
}
if iNdEx >= l {
return 0, io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
length |= (int(b) & 0x7F) << shift
if b < 0x80 {
break
}
}
if length < 0 {
return 0, ErrInvalidLengthRpc
}
iNdEx += length
case 3:
depth++
case 4:
if depth == 0 {
return 0, ErrUnexpectedEndOfGroupRpc
}
depth--
case 5:
iNdEx += 4
default:
return 0, fmt.Errorf("proto: illegal wireType %d", wireType)
}
if iNdEx < 0 {
return 0, ErrInvalidLengthRpc
}
if depth == 0 {
return iNdEx, nil
}
}
return 0, io.ErrUnexpectedEOF
}
var (
ErrInvalidLengthRpc = fmt.Errorf("proto: negative length found during unmarshaling")
ErrIntOverflowRpc = fmt.Errorf("proto: integer overflow")
ErrUnexpectedEndOfGroupRpc = fmt.Errorf("proto: unexpected end of group")
)
================================================
FILE: api/etcdserverpb/rpc.proto
================================================
syntax = "proto3";
package etcdserverpb;
import "etcd/api/mvccpb/kv.proto";
import "etcd/api/authpb/auth.proto";
import "etcd/api/versionpb/version.proto";
// for grpc-gateway
import "google/api/annotations.proto";
import "protoc-gen-openapiv2/options/annotations.proto";
option go_package = "go.etcd.io/etcd/api/v3/etcdserverpb";
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_swagger) = {
security_definitions: {
security: {
key: "ApiKey";
value: {
type: TYPE_API_KEY;
in: IN_HEADER;
name: "Authorization";
}
}
}
security: {
security_requirement: {
key: "ApiKey";
value: {};
}
}
};
service KV {
// Range gets the keys in the range from the key-value store.
rpc Range(RangeRequest) returns (RangeResponse) {
option (google.api.http) = {
post: "/v3/kv/range"
body: "*"
};
}
// Put puts the given key into the key-value store.
// A put request increments the revision of the key-value store
// and generates one event in the event history.
rpc Put(PutRequest) returns (PutResponse) {
option (google.api.http) = {
post: "/v3/kv/put"
body: "*"
};
}
// DeleteRange deletes the given range from the key-value store.
// A delete request increments the revision of the key-value store
// and generates a delete event in the event history for every deleted key.
rpc DeleteRange(DeleteRangeRequest) returns (DeleteRangeResponse) {
option (google.api.http) = {
post: "/v3/kv/deleterange"
body: "*"
};
}
// Txn processes multiple requests in a single transaction.
// A txn request increments the revision of the key-value store
// and generates events with the same revision for every completed request.
// It is not allowed to modify the same key several times within one txn.
rpc Txn(TxnRequest) returns (TxnResponse) {
option (google.api.http) = {
post: "/v3/kv/txn"
body: "*"
};
}
// Compact compacts the event history in the etcd key-value store. The key-value
// store should be periodically compacted or the event history will continue to grow
// indefinitely.
rpc Compact(CompactionRequest) returns (CompactionResponse) {
option (google.api.http) = {
post: "/v3/kv/compaction"
body: "*"
};
}
}
service Watch {
// Watch watches for events happening or that have happened. Both input and output
// are streams; the input stream is for creating and canceling watchers and the output
// stream sends events. One watch RPC can watch on multiple key ranges, streaming events
// for several watches at once. The entire event history can be watched starting from the
// last compaction revision.
rpc Watch(stream WatchRequest) returns (stream WatchResponse) {
option (google.api.http) = {
post: "/v3/watch"
body: "*"
};
}
}
service Lease {
// LeaseGrant creates a lease which expires if the server does not receive a keepAlive
// within a given time to live period. All keys attached to the lease will be expired and
// deleted if the lease expires. Each expired key generates a delete event in the event history.
rpc LeaseGrant(LeaseGrantRequest) returns (LeaseGrantResponse) {
option (google.api.http) = {
post: "/v3/lease/grant"
body: "*"
};
}
// LeaseRevoke revokes a lease. All keys attached to the lease will expire and be deleted.
rpc LeaseRevoke(LeaseRevokeRequest) returns (LeaseRevokeResponse) {
option (google.api.http) = {
post: "/v3/lease/revoke"
body: "*"
additional_bindings {
post: "/v3/kv/lease/revoke"
body: "*"
}
};
}
// LeaseKeepAlive keeps the lease alive by streaming keep alive requests from the client
// to the server and streaming keep alive responses from the server to the client.
rpc LeaseKeepAlive(stream LeaseKeepAliveRequest) returns (stream LeaseKeepAliveResponse) {
option (google.api.http) = {
post: "/v3/lease/keepalive"
body: "*"
};
}
// LeaseTimeToLive retrieves lease information.
rpc LeaseTimeToLive(LeaseTimeToLiveRequest) returns (LeaseTimeToLiveResponse) {
option (google.api.http) = {
post: "/v3/lease/timetolive"
body: "*"
additional_bindings {
post: "/v3/kv/lease/timetolive"
body: "*"
}
};
}
// LeaseLeases lists all existing leases.
rpc LeaseLeases(LeaseLeasesRequest) returns (LeaseLeasesResponse) {
option (google.api.http) = {
post: "/v3/lease/leases"
body: "*"
additional_bindings {
post: "/v3/kv/lease/leases"
body: "*"
}
};
}
}
service Cluster {
// MemberAdd adds a member into the cluster.
rpc MemberAdd(MemberAddRequest) returns (MemberAddResponse) {
option (google.api.http) = {
post: "/v3/cluster/member/add"
body: "*"
};
}
// MemberRemove removes an existing member from the cluster.
rpc MemberRemove(MemberRemoveRequest) returns (MemberRemoveResponse) {
option (google.api.http) = {
post: "/v3/cluster/member/remove"
body: "*"
};
}
// MemberUpdate updates the member configuration.
rpc MemberUpdate(MemberUpdateRequest) returns (MemberUpdateResponse) {
option (google.api.http) = {
post: "/v3/cluster/member/update"
body: "*"
};
}
// MemberList lists all the members in the cluster.
rpc MemberList(MemberListRequest) returns (MemberListResponse) {
option (google.api.http) = {
post: "/v3/cluster/member/list"
body: "*"
};
}
// MemberPromote promotes a member from raft learner (non-voting) to raft voting member.
rpc MemberPromote(MemberPromoteRequest) returns (MemberPromoteResponse) {
option (google.api.http) = {
post: "/v3/cluster/member/promote"
body: "*"
};
}
}
service Maintenance {
// Alarm activates, deactivates, and queries alarms regarding cluster health.
rpc Alarm(AlarmRequest) returns (AlarmResponse) {
option (google.api.http) = {
post: "/v3/maintenance/alarm"
body: "*"
};
}
// Status gets the status of the member.
rpc Status(StatusRequest) returns (StatusResponse) {
option (google.api.http) = {
post: "/v3/maintenance/status"
body: "*"
};
}
// Defragment defragments a member's backend database to recover storage space.
rpc Defragment(DefragmentRequest) returns (DefragmentResponse) {
option (google.api.http) = {
post: "/v3/maintenance/defragment"
body: "*"
};
}
// Hash computes the hash of whole backend keyspace,
// including key, lease, and other buckets in storage.
// This is designed for testing ONLY!
// Do not rely on this in production with ongoing transactions,
// since Hash operation does not hold MVCC locks.
// Use "HashKV" API instead for "key" bucket consistency checks.
rpc Hash(HashRequest) returns (HashResponse) {
option (google.api.http) = {
post: "/v3/maintenance/hash"
body: "*"
};
}
// HashKV computes the hash of all MVCC keys up to a given revision.
// It only iterates "key" bucket in backend storage.
rpc HashKV(HashKVRequest) returns (HashKVResponse) {
option (google.api.http) = {
post: "/v3/maintenance/hashkv"
body: "*"
};
}
// Snapshot sends a snapshot of the entire backend from a member over a stream to a client.
rpc Snapshot(SnapshotRequest) returns (stream SnapshotResponse) {
option (google.api.http) = {
post: "/v3/maintenance/snapshot"
body: "*"
};
}
// MoveLeader requests current leader node to transfer its leadership to transferee.
rpc MoveLeader(MoveLeaderRequest) returns (MoveLeaderResponse) {
option (google.api.http) = {
post: "/v3/maintenance/transfer-leadership"
body: "*"
};
}
// Downgrade requests downgrades, verifies feasibility or cancels downgrade
// on the cluster version.
// Supported since etcd 3.5.
rpc Downgrade(DowngradeRequest) returns (DowngradeResponse) {
option (google.api.http) = {
post: "/v3/maintenance/downgrade"
body: "*"
};
}
}
service Auth {
// AuthEnable enables authentication.
rpc AuthEnable(AuthEnableRequest) returns (AuthEnableResponse) {
option (google.api.http) = {
post: "/v3/auth/enable"
body: "*"
};
}
// AuthDisable disables authentication.
rpc AuthDisable(AuthDisableRequest) returns (AuthDisableResponse) {
option (google.api.http) = {
post: "/v3/auth/disable"
body: "*"
};
}
// AuthStatus displays authentication status.
rpc AuthStatus(AuthStatusRequest) returns (AuthStatusResponse) {
option (google.api.http) = {
post: "/v3/auth/status"
body: "*"
};
}
// Authenticate processes an authenticate request.
rpc Authenticate(AuthenticateRequest) returns (AuthenticateResponse) {
option (google.api.http) = {
post: "/v3/auth/authenticate"
body: "*"
};
}
// UserAdd adds a new user. User name cannot be empty.
rpc UserAdd(AuthUserAddRequest) returns (AuthUserAddResponse) {
option (google.api.http) = {
post: "/v3/auth/user/add"
body: "*"
};
}
// UserGet gets detailed user information.
rpc UserGet(AuthUserGetRequest) returns (AuthUserGetResponse) {
option (google.api.http) = {
post: "/v3/auth/user/get"
body: "*"
};
}
// UserList gets a list of all users.
rpc UserList(AuthUserListRequest) returns (AuthUserListResponse) {
option (google.api.http) = {
post: "/v3/auth/user/list"
body: "*"
};
}
// UserDelete deletes a specified user.
rpc UserDelete(AuthUserDeleteRequest) returns (AuthUserDeleteResponse) {
option (google.api.http) = {
post: "/v3/auth/user/delete"
body: "*"
};
}
// UserChangePassword changes the password of a specified user.
rpc UserChangePassword(AuthUserChangePasswordRequest) returns (AuthUserChangePasswordResponse) {
option (google.api.http) = {
post: "/v3/auth/user/changepw"
body: "*"
};
}
// UserGrantRole grants a role to a specified user.
rpc UserGrantRole(AuthUserGrantRoleRequest) returns (AuthUserGrantRoleResponse) {
option (google.api.http) = {
post: "/v3/auth/user/grant"
body: "*"
};
}
// UserRevokeRole revokes a role of specified user.
rpc UserRevokeRole(AuthUserRevokeRoleRequest) returns (AuthUserRevokeRoleResponse) {
option (google.api.http) = {
post: "/v3/auth/user/revoke"
body: "*"
};
}
// RoleAdd adds a new role. Role name cannot be empty.
rpc RoleAdd(AuthRoleAddRequest) returns (AuthRoleAddResponse) {
option (google.api.http) = {
post: "/v3/auth/role/add"
body: "*"
};
}
// RoleGet gets detailed role information.
rpc RoleGet(AuthRoleGetRequest) returns (AuthRoleGetResponse) {
option (google.api.http) = {
post: "/v3/auth/role/get"
body: "*"
};
}
// RoleList gets lists of all roles.
rpc RoleList(AuthRoleListRequest) returns (AuthRoleListResponse) {
option (google.api.http) = {
post: "/v3/auth/role/list"
body: "*"
};
}
// RoleDelete deletes a specified role.
rpc RoleDelete(AuthRoleDeleteRequest) returns (AuthRoleDeleteResponse) {
option (google.api.http) = {
post: "/v3/auth/role/delete"
body: "*"
};
}
// RoleGrantPermission grants a permission of a specified key or range to a specified role.
rpc RoleGrantPermission(AuthRoleGrantPermissionRequest) returns (AuthRoleGrantPermissionResponse) {
option (google.api.http) = {
post: "/v3/auth/role/grant"
body: "*"
};
}
// RoleRevokePermission revokes a key or range permission of a specified role.
rpc RoleRevokePermission(AuthRoleRevokePermissionRequest) returns (AuthRoleRevokePermissionResponse) {
option (google.api.http) = {
post: "/v3/auth/role/revoke"
body: "*"
};
}
}
message ResponseHeader {
option (versionpb.etcd_version_msg) = "3.0";
// cluster_id is the ID of the cluster which sent the response.
uint64 cluster_id = 1;
// member_id is the ID of the member which sent the response.
uint64 member_id = 2;
// revision is the key-value store revision when the request was applied, and it's
// unset (so 0) in case of calls not interacting with key-value store.
// For watch progress responses, the header.revision indicates progress. All future events
// received in this stream are guaranteed to have a higher revision number than the
// header.revision number.
int64 revision = 3;
// raft_term is the raft term when the request was applied.
uint64 raft_term = 4;
}
message RangeRequest {
option (versionpb.etcd_version_msg) = "3.0";
enum SortOrder {
option (versionpb.etcd_version_enum) = "3.0";
NONE = 0; // default, no sorting
ASCEND = 1; // lowest target value first
DESCEND = 2; // highest target value first
}
enum SortTarget {
option (versionpb.etcd_version_enum) = "3.0";
KEY = 0;
VERSION = 1;
CREATE = 2;
MOD = 3;
VALUE = 4;
}
// key is the first key for the range. If range_end is not given, the request only looks up key.
bytes key = 1;
// range_end is the upper bound on the requested range [key, range_end).
// If range_end is '\0', the range is all keys >= key.
// If range_end is key plus one (e.g., "aa"+1 == "ab", "a\xff"+1 == "b"),
// then the range request gets all keys prefixed with key.
// If both key and range_end are '\0', then the range request returns all keys.
bytes range_end = 2;
// limit is a limit on the number of keys returned for the request. When limit is set to 0,
// it is treated as no limit.
int64 limit = 3;
// revision is the point-in-time of the key-value store to use for the range.
// If revision is less or equal to zero, the range is over the newest key-value store.
// If the revision has been compacted, ErrCompacted is returned as a response.
int64 revision = 4;
// sort_order is the order for returned sorted results.
SortOrder sort_order = 5;
// sort_target is the key-value field to use for sorting.
SortTarget sort_target = 6;
// serializable sets the range request to use serializable member-local reads.
// Range requests are linearizable by default; linearizable requests have higher
// latency and lower throughput than serializable requests but reflect the current
// consensus of the cluster. For better performance, in exchange for possible stale reads,
// a serializable range request is served locally without needing to reach consensus
// with other nodes in the cluster.
bool serializable = 7;
// keys_only when set returns only the keys and not the values.
bool keys_only = 8;
// count_only when set returns only the count of the keys in the range.
bool count_only = 9;
// min_mod_revision is the lower bound for returned key mod revisions; all keys with
// lesser mod revisions will be filtered away.
int64 min_mod_revision = 10 [(versionpb.etcd_version_field)="3.1"];
// max_mod_revision is the upper bound for returned key mod revisions; all keys with
// greater mod revisions will be filtered away.
int64 max_mod_revision = 11 [(versionpb.etcd_version_field)="3.1"];
// min_create_revision is the lower bound for returned key create revisions; all keys with
// lesser create revisions will be filtered away.
int64 min_create_revision = 12 [(versionpb.etcd_version_field)="3.1"];
// max_create_revision is the upper bound for returned key create revisions; all keys with
// greater create revisions will be filtered away.
int64 max_create_revision = 13 [(versionpb.etcd_version_field)="3.1"];
}
message RangeResponse {
option (versionpb.etcd_version_msg) = "3.0";
ResponseHeader header = 1;
// kvs is the list of key-value pairs matched by the range request.
// kvs is empty when count is requested.
repeated mvccpb.KeyValue kvs = 2;
// more indicates if there are more keys to return in the requested range.
bool more = 3;
// count is set to the actual number of keys within the range when requested.
// Unlike Kvs, it is unaffected by limits and filters (e.g., Min/Max, Create/Modify, Revisions)
// and reflects the full count within the specified range.
int64 count = 4;
}
message PutRequest {
option (versionpb.etcd_version_msg) = "3.0";
// key is the key, in bytes, to put into the key-value store.
bytes key = 1;
// value is the value, in bytes, to associate with the key in the key-value store.
bytes value = 2;
// lease is the lease ID to associate with the key in the key-value store. A lease
// value of 0 indicates no lease.
int64 lease = 3;
// If prev_kv is set, etcd gets the previous key-value pair before changing it.
// The previous key-value pair will be returned in the put response.
bool prev_kv = 4 [(versionpb.etcd_version_field)="3.1"];
// If ignore_value is set, etcd updates the key using its current value.
// Returns an error if the key does not exist.
bool ignore_value = 5 [(versionpb.etcd_version_field)="3.2"];
// If ignore_lease is set, etcd updates the key using its current lease.
// Returns an error if the key does not exist.
bool ignore_lease = 6 [(versionpb.etcd_version_field)="3.2"];
}
message PutResponse {
option (versionpb.etcd_version_msg) = "3.0";
ResponseHeader header = 1;
// if prev_kv is set in the request, the previous key-value pair will be returned.
mvccpb.KeyValue prev_kv = 2 [(versionpb.etcd_version_field)="3.1"];
}
message DeleteRangeRequest {
option (versionpb.etcd_version_msg) = "3.0";
// key is the first key to delete in the range.
bytes key = 1;
// range_end is the key following the last key to delete for the range [key, range_end).
// If range_end is not given, the range is defined to contain only the key argument.
// If range_end is one bit larger than the given key, then the range is all the keys
// with the prefix (the given key).
// If range_end is '\0', the range is all keys greater than or equal to the key argument.
bytes range_end = 2;
// If prev_kv is set, etcd gets the previous key-value pairs before deleting it.
// The previous key-value pairs will be returned in the delete response.
bool prev_kv = 3 [(versionpb.etcd_version_field)="3.1"];
}
message DeleteRangeResponse {
option (versionpb.etcd_version_msg) = "3.0";
ResponseHeader header = 1;
// deleted is the number of keys deleted by the delete range request.
int64 deleted = 2;
// if prev_kv is set in the request, the previous key-value pairs will be returned.
repeated mvccpb.KeyValue prev_kvs = 3 [(versionpb.etcd_version_field)="3.1"];
}
message RequestOp {
option (versionpb.etcd_version_msg) = "3.0";
// request is a union of request types accepted by a transaction.
oneof request {
RangeRequest request_range = 1;
PutRequest request_put = 2;
DeleteRangeRequest request_delete_range = 3;
TxnRequest request_txn = 4 [(versionpb.etcd_version_field)="3.3"];
}
}
message ResponseOp {
option (versionpb.etcd_version_msg) = "3.0";
// response is a union of response types returned by a transaction.
oneof response {
RangeResponse response_range = 1;
PutResponse response_put = 2;
DeleteRangeResponse response_delete_range = 3;
TxnResponse response_txn = 4 [(versionpb.etcd_version_field)="3.3"];
}
}
message Compare {
option (versionpb.etcd_version_msg) = "3.0";
enum CompareResult {
option (versionpb.etcd_version_enum) = "3.0";
EQUAL = 0;
GREATER = 1;
LESS = 2;
NOT_EQUAL = 3 [(versionpb.etcd_version_enum_value)="3.1"];
}
enum CompareTarget {
option (versionpb.etcd_version_enum) = "3.0";
VERSION = 0;
CREATE = 1;
MOD = 2;
VALUE = 3;
LEASE = 4 [(versionpb.etcd_version_enum_value)="3.3"];
}
// result is logical comparison operation for this comparison.
CompareResult result = 1;
// target is the key-value field to inspect for the comparison.
CompareTarget target = 2;
// key is the subject key for the comparison operation.
bytes key = 3;
oneof target_union {
// version is the version of the given key
int64 version = 4;
// create_revision is the creation revision of the given key
int64 create_revision = 5;
// mod_revision is the last modified revision of the given key.
int64 mod_revision = 6;
// value is the value of the given key, in bytes.
bytes value = 7;
// lease is the lease id of the given key.
int64 lease = 8 [(versionpb.etcd_version_field)="3.3"];
// leave room for more target_union field tags, jump to 64
}
// range_end compares the given target to all keys in the range [key, range_end).
// See RangeRequest for more details on key ranges.
bytes range_end = 64 [(versionpb.etcd_version_field)="3.3"];
// TODO: fill out with most of the rest of RangeRequest fields when needed.
}
// From google paxosdb paper:
// Our implementation hinges around a powerful primitive which we call MultiOp. All other database
// operations except for iteration are implemented as a single call to MultiOp. A MultiOp is applied atomically
// and consists of three components:
// 1. A list of tests called guard. Each test in guard checks a single entry in the database. It may check
// for the absence or presence of a value, or compare with a given value. Two different tests in the guard
// may apply to the same or different entries in the database. All tests in the guard are applied and
// MultiOp returns the results. If all tests are true, MultiOp executes t op (see item 2 below), otherwise
// it executes f op (see item 3 below).
// 2. A list of database operations called t op. Each operation in the list is either an insert, delete, or
// lookup operation, and applies to a single database entry. Two different operations in the list may apply
// to the same or different entries in the database. These operations are executed
// if guard evaluates to
// true.
// 3. A list of database operations called f op. Like t op, but executed if guard evaluates to false.
message TxnRequest {
option (versionpb.etcd_version_msg) = "3.0";
// compare is a list of predicates representing a conjunction of terms.
// If the comparisons succeed, then the success requests will be processed in order,
// and the response will contain their respective responses in order.
// If the comparisons fail, then the failure requests will be processed in order,
// and the response will contain their respective responses in order.
repeated Compare compare = 1;
// success is a list of requests which will be applied when compare evaluates to true.
repeated RequestOp success = 2;
// failure is a list of requests which will be applied when compare evaluates to false.
repeated RequestOp failure = 3;
}
message TxnResponse {
option (versionpb.etcd_version_msg) = "3.0";
ResponseHeader header = 1;
// succeeded is set to true if the compare evaluated to true or false otherwise.
bool succeeded = 2;
// responses is a list of responses corresponding to the results from applying
// success if succeeded is true or failure if succeeded is false.
repeated ResponseOp responses = 3;
}
// CompactionRequest compacts the key-value store up to a given revision. All superseded keys
// with a revision less than the compaction revision will be removed.
message CompactionRequest {
option (versionpb.etcd_version_msg) = "3.0";
// revision is the key-value store revision for the compaction operation.
int64 revision = 1;
// physical is set so the RPC will wait until the compaction is physically
// applied to the local database such that compacted entries are totally
// removed from the backend database.
bool physical = 2;
}
message CompactionResponse {
option (versionpb.etcd_version_msg) = "3.0";
ResponseHeader header = 1;
}
message HashRequest {
option (versionpb.etcd_version_msg) = "3.0";
}
message HashKVRequest {
option (versionpb.etcd_version_msg) = "3.3";
// revision is the key-value store revision for the hash operation.
int64 revision = 1;
}
message HashKVResponse {
option (versionpb.etcd_version_msg) = "3.3";
ResponseHeader header = 1;
// hash is the hash value computed from the responding member's MVCC keys up to a given revision.
uint32 hash = 2;
// compact_revision is the compacted revision of key-value store when hash begins.
int64 compact_revision = 3;
// hash_revision is the revision up to which the hash is calculated.
int64 hash_revision = 4 [(versionpb.etcd_version_field)="3.6"];
}
message HashResponse {
option (versionpb.etcd_version_msg) = "3.0";
ResponseHeader header = 1;
// hash is the hash value computed from the responding member's KV's backend.
uint32 hash = 2;
}
message SnapshotRequest {
option (versionpb.etcd_version_msg) = "3.3";
}
message SnapshotResponse {
option (versionpb.etcd_version_msg) = "3.3";
// header has the current key-value store information. The first header in the snapshot
// stream indicates the point in time of the snapshot.
ResponseHeader header = 1;
// remaining_bytes is the number of blob bytes to be sent after this message
uint64 remaining_bytes = 2;
// blob contains the next chunk of the snapshot in the snapshot stream.
bytes blob = 3;
// local version of server that created the snapshot.
// In cluster with binaries with different version, each cluster can return different result.
// Informs which etcd server version should be used when restoring the snapshot.
string version = 4 [(versionpb.etcd_version_field)="3.6"];
}
message WatchRequest {
option (versionpb.etcd_version_msg) = "3.0";
// request_union is a request to either create a new watcher or cancel an existing watcher.
oneof request_union {
WatchCreateRequest create_request = 1;
WatchCancelRequest cancel_request = 2;
WatchProgressRequest progress_request = 3 [(versionpb.etcd_version_field)="3.4"];
}
}
message WatchCreateRequest {
option (versionpb.etcd_version_msg) = "3.0";
// key is the key to register for watching.
bytes key = 1;
// range_end is the end of the range [key, range_end) to watch. If range_end is not given,
// only the key argument is watched. If range_end is equal to '\0', all keys greater than
// or equal to the key argument are watched.
// If the range_end is one bit larger than the given key,
// then all keys with the prefix (the given key) will be watched.
bytes range_end = 2;
// start_revision is an optional revision to watch from (inclusive). No start_revision is "now".
int64 start_revision = 3;
// progress_notify is set so that the etcd server will periodically send a WatchResponse with
// no events to the new watcher if there are no recent events. It is useful when clients
// wish to recover a disconnected watcher starting from a recent known revision.
// The etcd server may decide how often it will send notifications based on current load.
bool progress_notify = 4;
enum FilterType {
option (versionpb.etcd_version_enum) = "3.1";
// filter out put event.
NOPUT = 0;
// filter out delete event.
NODELETE = 1;
}
// filters filter the events at server side before it sends back to the watcher.
repeated FilterType filters = 5 [(versionpb.etcd_version_field)="3.1"];
// If prev_kv is set, created watcher gets the previous KV before the event happens.
// If the previous KV is already compacted, nothing will be returned.
bool prev_kv = 6 [(versionpb.etcd_version_field)="3.1"];
// If watch_id is provided and non-zero, it will be assigned to this watcher.
// Since creating a watcher in etcd is not a synchronous operation,
// this can be used ensure that ordering is correct when creating multiple
// watchers on the same stream. Creating a watcher with an ID already in
// use on the stream will cause an error to be returned.
int64 watch_id = 7 [(versionpb.etcd_version_field)="3.4"];
// fragment enables splitting large revisions into multiple watch responses.
bool fragment = 8 [(versionpb.etcd_version_field)="3.4"];
}
message WatchCancelRequest {
option (versionpb.etcd_version_msg) = "3.1";
// watch_id is the watcher id to cancel so that no more events are transmitted.
int64 watch_id = 1 [(versionpb.etcd_version_field)="3.1"];
}
// Requests the a watch stream progress status be sent in the watch response stream as soon as
// possible.
message WatchProgressRequest {
option (versionpb.etcd_version_msg) = "3.4";
}
message WatchResponse {
option (versionpb.etcd_version_msg) = "3.0";
ResponseHeader header = 1;
// watch_id is the ID of the watcher that corresponds to the response.
int64 watch_id = 2;
// created is set to true if the response is for a create watch request.
// The client should record the watch_id and expect to receive events for
// the created watcher from the same stream.
// All events sent to the created watcher will attach with the same watch_id.
bool created = 3;
// canceled is set to true if the response is for a cancel watch request
// or if the start_revision has already been compacted.
// No further events will be sent to the canceled watcher.
bool canceled = 4;
// compact_revision is set to the minimum index if a watcher tries to watch
// at a compacted index.
//
// This happens when creating a watcher at a compacted revision or the watcher cannot
// catch up with the progress of the key-value store.
//
// The client should treat the watcher as canceled and should not try to create any
// watcher with the same start_revision again.
int64 compact_revision = 5;
// cancel_reason indicates the reason for canceling the watcher.
string cancel_reason = 6 [(versionpb.etcd_version_field)="3.4"];
// framgment is true if large watch response was split over multiple responses.
bool fragment = 7 [(versionpb.etcd_version_field)="3.4"];
repeated mvccpb.Event events = 11;
}
message LeaseGrantRequest {
option (versionpb.etcd_version_msg) = "3.0";
// TTL is the advisory time-to-live in seconds. Expired lease will return -1.
int64 TTL = 1;
// ID is the requested ID for the lease. If ID is set to 0, the lessor chooses an ID.
int64 ID = 2;
}
message LeaseGrantResponse {
option (versionpb.etcd_version_msg) = "3.0";
ResponseHeader header = 1;
// ID is the lease ID for the granted lease.
int64 ID = 2;
// TTL is the server chosen lease time-to-live in seconds.
int64 TTL = 3;
string error = 4;
}
message LeaseRevokeRequest {
option (versionpb.etcd_version_msg) = "3.0";
// ID is the lease ID to revoke. When the ID is revoked, all associated keys will be deleted.
int64 ID = 1;
}
message LeaseRevokeResponse {
option (versionpb.etcd_version_msg) = "3.0";
ResponseHeader header = 1;
}
message LeaseCheckpoint {
option (versionpb.etcd_version_msg) = "3.4";
// ID is the lease ID to checkpoint.
int64 ID = 1;
// Remaining_TTL is the remaining time until expiry of the lease.
int64 remaining_TTL = 2;
}
message LeaseCheckpointRequest {
option (versionpb.etcd_version_msg) = "3.4";
repeated LeaseCheckpoint checkpoints = 1;
}
message LeaseCheckpointResponse {
option (versionpb.etcd_version_msg) = "3.4";
ResponseHeader header = 1;
}
message LeaseKeepAliveRequest {
option (versionpb.etcd_version_msg) = "3.0";
// ID is the lease ID for the lease to keep alive.
int64 ID = 1;
}
message LeaseKeepAliveResponse {
option (versionpb.etcd_version_msg) = "3.0";
ResponseHeader header = 1;
// ID is the lease ID from the keep alive request.
int64 ID = 2;
// TTL is the new time-to-live for the lease.
int64 TTL = 3;
}
message LeaseTimeToLiveRequest {
option (versionpb.etcd_version_msg) = "3.1";
// ID is the lease ID for the lease.
int64 ID = 1;
// keys is true to query all the keys attached to this lease.
bool keys = 2;
}
message LeaseTimeToLiveResponse {
option (versionpb.etcd_version_msg) = "3.1";
ResponseHeader header = 1;
// ID is the lease ID from the keep alive request.
int64 ID = 2;
// TTL is the remaining TTL in seconds for the lease; the lease will expire in under TTL+1 seconds.
int64 TTL = 3;
// GrantedTTL is the initial granted time in seconds upon lease creation/renewal.
int64 grantedTTL = 4;
// Keys is the list of keys attached to this lease.
repeated bytes keys = 5;
}
message LeaseLeasesRequest {
option (versionpb.etcd_version_msg) = "3.3";
}
message LeaseStatus {
option (versionpb.etcd_version_msg) = "3.3";
int64 ID = 1;
// TODO: int64 TTL = 2;
}
message LeaseLeasesResponse {
option (versionpb.etcd_version_msg) = "3.3";
ResponseHeader header = 1;
repeated LeaseStatus leases = 2;
}
message Member {
option (versionpb.etcd_version_msg) = "3.0";
// ID is the member ID for this member.
uint64 ID = 1;
// name is the human-readable name of the member. If the member is not started, the name will be an empty string.
string name = 2;
// peerURLs is the list of URLs the member exposes to the cluster for communication.
repeated string peerURLs = 3;
// clientURLs is the list of URLs the member exposes to clients for communication. If the member is not started, clientURLs will be empty.
repeated string clientURLs = 4;
// isLearner indicates if the member is raft learner.
bool isLearner = 5 [(versionpb.etcd_version_field)="3.4"];
}
message MemberAddRequest {
option (versionpb.etcd_version_msg) = "3.0";
// peerURLs is the list of URLs the added member will use to communicate with the cluster.
repeated string peerURLs = 1;
// isLearner indicates if the added member is raft learner.
bool isLearner = 2 [(versionpb.etcd_version_field)="3.4"];
}
message MemberAddResponse {
option (versionpb.etcd_version_msg) = "3.0";
ResponseHeader header = 1;
// member is the member information for the added member.
Member member = 2;
// members is a list of all members after adding the new member.
repeated Member members = 3;
}
message MemberRemoveRequest {
option (versionpb.etcd_version_msg) = "3.0";
// ID is the member ID of the member to remove.
uint64 ID = 1;
}
message MemberRemoveResponse {
option (versionpb.etcd_version_msg) = "3.0";
ResponseHeader header = 1;
// members is a list of all members after removing the member.
repeated Member members = 2;
}
message MemberUpdateRequest {
option (versionpb.etcd_version_msg) = "3.0";
// ID is the member ID of the member to update.
uint64 ID = 1;
// peerURLs is the new list of URLs the member will use to communicate with the cluster.
repeated string peerURLs = 2;
}
message MemberUpdateResponse{
option (versionpb.etcd_version_msg) = "3.0";
ResponseHeader header = 1;
// members is a list of all members after updating the member.
repeated Member members = 2 [(versionpb.etcd_version_field)="3.1"];
}
message MemberListRequest {
option (versionpb.etcd_version_msg) = "3.0";
bool linearizable = 1 [(versionpb.etcd_version_field)="3.5"];
}
message MemberListResponse {
option (versionpb.etcd_version_msg) = "3.0";
ResponseHeader header = 1;
// members is a list of all members associated with the cluster.
repeated Member members = 2;
}
message MemberPromoteRequest {
option (versionpb.etcd_version_msg) = "3.4";
// ID is the member ID of the member to promote.
uint64 ID = 1;
}
message MemberPromoteResponse {
option (versionpb.etcd_version_msg) = "3.4";
ResponseHeader header = 1;
// members is a list of all members after promoting the member.
repeated Member members = 2;
}
message DefragmentRequest {
option (versionpb.etcd_version_msg) = "3.0";
}
message DefragmentResponse {
option (versionpb.etcd_version_msg) = "3.0";
ResponseHeader header = 1;
}
message MoveLeaderRequest {
option (versionpb.etcd_version_msg) = "3.3";
// targetID is the node ID for the new leader.
uint64 targetID = 1;
}
message MoveLeaderResponse {
option (versionpb.etcd_version_msg) = "3.3";
ResponseHeader header = 1;
}
enum AlarmType {
option (versionpb.etcd_version_enum) = "3.0";
NONE = 0; // default, used to query if any alarm is active
NOSPACE = 1; // space quota is exhausted
CORRUPT = 2 [(versionpb.etcd_version_enum_value)="3.3"]; // kv store corruption detected
}
message AlarmRequest {
option (versionpb.etcd_version_msg) = "3.0";
enum AlarmAction {
option (versionpb.etcd_version_enum) = "3.0";
GET = 0;
ACTIVATE = 1;
DEACTIVATE = 2;
}
// action is the kind of alarm request to issue. The action
// may GET alarm statuses, ACTIVATE an alarm, or DEACTIVATE a
// raised alarm.
AlarmAction action = 1;
// memberID is the ID of the member associated with the alarm. If memberID is 0, the
// alarm request covers all members.
uint64 memberID = 2;
// alarm is the type of alarm to consider for this request.
AlarmType alarm = 3;
}
message AlarmMember {
option (versionpb.etcd_version_msg) = "3.0";
// memberID is the ID of the member associated with the raised alarm.
uint64 memberID = 1;
// alarm is the type of alarm which has been raised.
AlarmType alarm = 2;
}
message AlarmResponse {
option (versionpb.etcd_version_msg) = "3.0";
ResponseHeader header = 1;
// alarms is a list of alarms associated with the alarm request.
repeated AlarmMember alarms = 2;
}
message DowngradeRequest {
option (versionpb.etcd_version_msg) = "3.5";
enum DowngradeAction {
option (versionpb.etcd_version_enum) = "3.5";
VALIDATE = 0;
ENABLE = 1;
CANCEL = 2;
}
// action is the kind of downgrade request to issue. The action may
// VALIDATE the target version, DOWNGRADE the cluster version,
// or CANCEL the current downgrading job.
DowngradeAction action = 1;
// version is the target version to downgrade.
string version = 2;
}
message DowngradeResponse {
option (versionpb.etcd_version_msg) = "3.5";
ResponseHeader header = 1;
// version is the current cluster version.
string version = 2;
}
// DowngradeVersionTestRequest is used for test only. The version in
// this request will be read as the WAL record version.If the downgrade
// target version is less than this version, then the downgrade(online)
// or migration(offline) isn't safe, so shouldn't be allowed.
message DowngradeVersionTestRequest {
option (versionpb.etcd_version_msg) = "3.6";
string ver = 1;
}
message StatusRequest {
option (versionpb.etcd_version_msg) = "3.0";
}
message StatusResponse {
option (versionpb.etcd_version_msg) = "3.0";
ResponseHeader header = 1;
// version is the cluster protocol version used by the responding member.
string version = 2;
// dbSize is the size of the backend database physically allocated, in bytes, of the responding member.
int64 dbSize = 3;
// leader is the member ID which the responding member believes is the current leader.
uint64 leader = 4;
// raftIndex is the current raft committed index of the responding member.
uint64 raftIndex = 5;
// raftTerm is the current raft term of the responding member.
uint64 raftTerm = 6;
// raftAppliedIndex is the current raft applied index of the responding member.
uint64 raftAppliedIndex = 7 [(versionpb.etcd_version_field)="3.4"];
// errors contains alarm/health information and status.
repeated string errors = 8 [(versionpb.etcd_version_field)="3.4"];
// dbSizeInUse is the size of the backend database logically in use, in bytes, of the responding member.
int64 dbSizeInUse = 9 [(versionpb.etcd_version_field)="3.4"];
// isLearner indicates if the member is raft learner.
bool isLearner = 10 [(versionpb.etcd_version_field)="3.4"];
// storageVersion is the version of the db file. It might be updated with delay in relationship to the target cluster version.
string storageVersion = 11 [(versionpb.etcd_version_field)="3.6"];
// dbSizeQuota is the configured etcd storage quota in bytes (the value passed to etcd instance by flag --quota-backend-bytes)
int64 dbSizeQuota = 12 [(versionpb.etcd_version_field)="3.6"];
// downgradeInfo indicates if there is downgrade process.
DowngradeInfo downgradeInfo = 13 [(versionpb.etcd_version_field)="3.6"];
}
message DowngradeInfo {
// enabled indicates whether the cluster is enabled to downgrade.
bool enabled = 1;
// targetVersion is the target downgrade version.
string targetVersion = 2;
}
message AuthEnableRequest {
option (versionpb.etcd_version_msg) = "3.0";
}
message AuthDisableRequest {
option (versionpb.etcd_version_msg) = "3.0";
}
message AuthStatusRequest {
option (versionpb.etcd_version_msg) = "3.5";
}
message AuthenticateRequest {
option (versionpb.etcd_version_msg) = "3.0";
string name = 1;
string password = 2;
}
message AuthUserAddRequest {
option (versionpb.etcd_version_msg) = "3.0";
string name = 1;
string password = 2;
authpb.UserAddOptions options = 3 [(versionpb.etcd_version_field)="3.4"];
string hashedPassword = 4 [(versionpb.etcd_version_field)="3.5"];
}
message AuthUserGetRequest {
option (versionpb.etcd_version_msg) = "3.0";
string name = 1;
}
message AuthUserDeleteRequest {
option (versionpb.etcd_version_msg) = "3.0";
// name is the name of the user to delete.
string name = 1;
}
message AuthUserChangePasswordRequest {
option (versionpb.etcd_version_msg) = "3.0";
// name is the name of the user whose password is being changed.
string name = 1;
// password is the new password for the user. Note that this field will be removed in the API layer.
string password = 2;
// hashedPassword is the new password for the user. Note that this field will be initialized in the API layer.
string hashedPassword = 3 [(versionpb.etcd_version_field)="3.5"];
}
message AuthUserGrantRoleRequest {
option (versionpb.etcd_version_msg) = "3.0";
// user is the name of the user which should be granted a given role.
string user = 1;
// role is the name of the role to grant to the user.
string role = 2;
}
message AuthUserRevokeRoleRequest {
option (versionpb.etcd_version_msg) = "3.0";
string name = 1;
string role = 2;
}
message AuthRoleAddRequest {
option (versionpb.etcd_version_msg) = "3.0";
// name is the name of the role to add to the authentication system.
string name = 1;
}
message AuthRoleGetRequest {
option (versionpb.etcd_version_msg) = "3.0";
string role = 1;
}
message AuthUserListRequest {
option (versionpb.etcd_version_msg) = "3.0";
}
message AuthRoleListRequest {
option (versionpb.etcd_version_msg) = "3.0";
}
message AuthRoleDeleteRequest {
option (versionpb.etcd_version_msg) = "3.0";
string role = 1;
}
message AuthRoleGrantPermissionRequest {
option (versionpb.etcd_version_msg) = "3.0";
// name is the name of the role which will be granted the permission.
string name = 1;
// perm is the permission to grant to the role.
authpb.Permission perm = 2;
}
message AuthRoleRevokePermissionRequest {
option (versionpb.etcd_version_msg) = "3.0";
string role = 1;
bytes key = 2;
bytes range_end = 3;
}
message AuthEnableResponse {
option (versionpb.etcd_version_msg) = "3.0";
ResponseHeader header = 1;
}
message AuthDisableResponse {
option (versionpb.etcd_version_msg) = "3.0";
ResponseHeader header = 1;
}
message AuthStatusResponse {
option (versionpb.etcd_version_msg) = "3.5";
ResponseHeader header = 1;
bool enabled = 2;
// authRevision is the current revision of auth store
uint64 authRevision = 3;
}
message AuthenticateResponse {
option (versionpb.etcd_version_msg) = "3.0";
ResponseHeader header = 1;
// token is an authorized token that can be used in succeeding RPCs
string token = 2;
}
message AuthUserAddResponse {
option (versionpb.etcd_version_msg) = "3.0";
ResponseHeader header = 1;
}
message AuthUserGetResponse {
option (versionpb.etcd_version_msg) = "3.0";
ResponseHeader header = 1;
repeated string roles = 2;
}
message AuthUserDeleteResponse {
option (versionpb.etcd_version_msg) = "3.0";
ResponseHeader header = 1;
}
message AuthUserChangePasswordResponse {
option (versionpb.etcd_version_msg) = "3.0";
ResponseHeader header = 1;
}
message AuthUserGrantRoleResponse {
option (versionpb.etcd_version_msg) = "3.0";
ResponseHeader header = 1;
}
message AuthUserRevokeRoleResponse {
option (versionpb.etcd_version_msg) = "3.0";
ResponseHeader header = 1;
}
message AuthRoleAddResponse {
option (versionpb.etcd_version_msg) = "3.0";
ResponseHeader header = 1;
}
message AuthRoleGetResponse {
ResponseHeader header = 1 [(versionpb.etcd_version_field)="3.0"];
repeated authpb.Permission perm = 2 [(versionpb.etcd_version_field)="3.0"];
}
message AuthRoleListResponse {
option (versionpb.etcd_version_msg) = "3.0";
ResponseHeader header = 1;
repeated string roles = 2;
}
message AuthUserListResponse {
option (versionpb.etcd_version_msg) = "3.0";
ResponseHeader header = 1;
repeated string users = 2;
}
message AuthRoleDeleteResponse {
option (versionpb.etcd_version_msg) = "3.0";
ResponseHeader header = 1;
}
message AuthRoleGrantPermissionResponse {
option (versionpb.etcd_version_msg) = "3.0";
ResponseHeader header = 1;
}
message AuthRoleRevokePermissionResponse {
option (versionpb.etcd_version_msg) = "3.0";
ResponseHeader header = 1;
}
================================================
FILE: api/etcdserverpb/rpc_grpc.pb.go
================================================
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
// versions:
// - protoc-gen-go-grpc v1.6.1
// - protoc v3.20.3
// source: rpc.proto
package etcdserverpb
import (
context "context"
grpc "google.golang.org/grpc"
codes "google.golang.org/grpc/codes"
status "google.golang.org/grpc/status"
)
// This is a compile-time assertion to ensure that this generated file
// is compatible with the grpc package it is being compiled against.
// Requires gRPC-Go v1.64.0 or later.
const _ = grpc.SupportPackageIsVersion9
const (
KV_Range_FullMethodName = "/etcdserverpb.KV/Range"
KV_Put_FullMethodName = "/etcdserverpb.KV/Put"
KV_DeleteRange_FullMethodName = "/etcdserverpb.KV/DeleteRange"
KV_Txn_FullMethodName = "/etcdserverpb.KV/Txn"
KV_Compact_FullMethodName = "/etcdserverpb.KV/Compact"
)
// KVClient is the client API for KV service.
//
// 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 KVClient interface {
// Range gets the keys in the range from the key-value store.
Range(ctx context.Context, in *RangeRequest, opts ...grpc.CallOption) (*RangeResponse, error)
// Put puts the given key into the key-value store.
// A put request increments the revision of the key-value store
// and generates one event in the event history.
Put(ctx context.Context, in *PutRequest, opts ...grpc.CallOption) (*PutResponse, error)
// DeleteRange deletes the given range from the key-value store.
// A delete request increments the revision of the key-value store
// and generates a delete event in the event history for every deleted key.
DeleteRange(ctx context.Context, in *DeleteRangeRequest, opts ...grpc.CallOption) (*DeleteRangeResponse, error)
// Txn processes multiple requests in a single transaction.
// A txn request increments the revision of the key-value store
// and generates events with the same revision for every completed request.
// It is not allowed to modify the same key several times within one txn.
Txn(ctx context.Context, in *TxnRequest, opts ...grpc.CallOption) (*TxnResponse, error)
// Compact compacts the event history in the etcd key-value store. The key-value
// store should be periodically compacted or the event history will continue to grow
// indefinitely.
Compact(ctx context.Context, in *CompactionRequest, opts ...grpc.CallOption) (*CompactionResponse, error)
}
type kVClient struct {
cc grpc.ClientConnInterface
}
func NewKVClient(cc grpc.ClientConnInterface) KVClient {
return &kVClient{cc}
}
func (c *kVClient) Range(ctx context.Context, in *RangeRequest, opts ...grpc.CallOption) (*RangeResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(RangeResponse)
err := c.cc.Invoke(ctx, KV_Range_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *kVClient) Put(ctx context.Context, in *PutRequest, opts ...grpc.CallOption) (*PutResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(PutResponse)
err := c.cc.Invoke(ctx, KV_Put_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *kVClient) DeleteRange(ctx context.Context, in *DeleteRangeRequest, opts ...grpc.CallOption) (*DeleteRangeResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(DeleteRangeResponse)
err := c.cc.Invoke(ctx, KV_DeleteRange_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *kVClient) Txn(ctx context.Context, in *TxnRequest, opts ...grpc.CallOption) (*TxnResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(TxnResponse)
err := c.cc.Invoke(ctx, KV_Txn_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *kVClient) Compact(ctx context.Context, in *CompactionRequest, opts ...grpc.CallOption) (*CompactionResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(CompactionResponse)
err := c.cc.Invoke(ctx, KV_Compact_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
// KVServer is the server API for KV service.
// All implementations must embed UnimplementedKVServer
// for forward compatibility.
type KVServer interface {
// Range gets the keys in the range from the key-value store.
Range(context.Context, *RangeRequest) (*RangeResponse, error)
// Put puts the given key into the key-value store.
// A put request increments the revision of the key-value store
// and generates one event in the event history.
Put(context.Context, *PutRequest) (*PutResponse, error)
// DeleteRange deletes the given range from the key-value store.
// A delete request increments the revision of the key-value store
// and generates a delete event in the event history for every deleted key.
DeleteRange(context.Context, *DeleteRangeRequest) (*DeleteRangeResponse, error)
// Txn processes multiple requests in a single transaction.
// A txn request increments the revision of the key-value store
// and generates events with the same revision for every completed request.
// It is not allowed to modify the same key several times within one txn.
Txn(context.Context, *TxnRequest) (*TxnResponse, error)
// Compact compacts the event history in the etcd key-value store. The key-value
// store should be periodically compacted or the event history will continue to grow
// indefinitely.
Compact(context.Context, *CompactionRequest) (*CompactionResponse, error)
mustEmbedUnimplementedKVServer()
}
// UnimplementedKVServer must be embedded to have
// forward compatible implementations.
//
// NOTE: this should be embedded by value instead of pointer to avoid a nil
// pointer dereference when methods are called.
type UnimplementedKVServer struct{}
func (UnimplementedKVServer) Range(context.Context, *RangeRequest) (*RangeResponse, error) {
return nil, status.Error(codes.Unimplemented, "method Range not implemented")
}
func (UnimplementedKVServer) Put(context.Context, *PutRequest) (*PutResponse, error) {
return nil, status.Error(codes.Unimplemented, "method Put not implemented")
}
func (UnimplementedKVServer) DeleteRange(context.Context, *DeleteRangeRequest) (*DeleteRangeResponse, error) {
return nil, status.Error(codes.Unimplemented, "method DeleteRange not implemented")
}
func (UnimplementedKVServer) Txn(context.Context, *TxnRequest) (*TxnResponse, error) {
return nil, status.Error(codes.Unimplemented, "method Txn not implemented")
}
func (UnimplementedKVServer) Compact(context.Context, *CompactionRequest) (*CompactionResponse, error) {
return nil, status.Error(codes.Unimplemented, "method Compact not implemented")
}
func (UnimplementedKVServer) mustEmbedUnimplementedKVServer() {}
func (UnimplementedKVServer) testEmbeddedByValue() {}
// UnsafeKVServer may be embedded to opt out of forward compatibility for this service.
// Use of this interface is not recommended, as added methods to KVServer will
// result in compilation errors.
type UnsafeKVServer interface {
mustEmbedUnimplementedKVServer()
}
func RegisterKVServer(s grpc.ServiceRegistrar, srv KVServer) {
// If the following call panics, it indicates UnimplementedKVServer was
// embedded by pointer and is nil. This will cause panics if an
// unimplemented method is ever invoked, so we test this at initialization
// time to prevent it from happening at runtime later due to I/O.
if t, ok := srv.(interface{ testEmbeddedByValue() }); ok {
t.testEmbeddedByValue()
}
s.RegisterService(&KV_ServiceDesc, srv)
}
func _KV_Range_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(RangeRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(KVServer).Range(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: KV_Range_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(KVServer).Range(ctx, req.(*RangeRequest))
}
return interceptor(ctx, in, info, handler)
}
func _KV_Put_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(PutRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(KVServer).Put(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: KV_Put_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(KVServer).Put(ctx, req.(*PutRequest))
}
return interceptor(ctx, in, info, handler)
}
func _KV_DeleteRange_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(DeleteRangeRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(KVServer).DeleteRange(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: KV_DeleteRange_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(KVServer).DeleteRange(ctx, req.(*DeleteRangeRequest))
}
return interceptor(ctx, in, info, handler)
}
func _KV_Txn_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(TxnRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(KVServer).Txn(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: KV_Txn_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(KVServer).Txn(ctx, req.(*TxnRequest))
}
return interceptor(ctx, in, info, handler)
}
func _KV_Compact_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(CompactionRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(KVServer).Compact(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: KV_Compact_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(KVServer).Compact(ctx, req.(*CompactionRequest))
}
return interceptor(ctx, in, info, handler)
}
// KV_ServiceDesc is the grpc.ServiceDesc for KV service.
// It's only intended for direct use with grpc.RegisterService,
// and not to be introspected or modified (even as a copy)
var KV_ServiceDesc = grpc.ServiceDesc{
ServiceName: "etcdserverpb.KV",
HandlerType: (*KVServer)(nil),
Methods: []grpc.MethodDesc{
{
MethodName: "Range",
Handler: _KV_Range_Handler,
},
{
MethodName: "Put",
Handler: _KV_Put_Handler,
},
{
MethodName: "DeleteRange",
Handler: _KV_DeleteRange_Handler,
},
{
MethodName: "Txn",
Handler: _KV_Txn_Handler,
},
{
MethodName: "Compact",
Handler: _KV_Compact_Handler,
},
},
Streams: []grpc.StreamDesc{},
Metadata: "rpc.proto",
}
const (
Watch_Watch_FullMethodName = "/etcdserverpb.Watch/Watch"
)
// WatchClient is the client API for Watch service.
//
// 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 WatchClient interface {
// Watch watches for events happening or that have happened. Both input and output
// are streams; the input stream is for creating and canceling watchers and the output
// stream sends events. One watch RPC can watch on multiple key ranges, streaming events
// for several watches at once. The entire event history can be watched starting from the
// last compaction revision.
Watch(ctx context.Context, opts ...grpc.CallOption) (grpc.BidiStreamingClient[WatchRequest, WatchResponse], error)
}
type watchClient struct {
cc grpc.ClientConnInterface
}
func NewWatchClient(cc grpc.ClientConnInterface) WatchClient {
return &watchClient{cc}
}
func (c *watchClient) Watch(ctx context.Context, opts ...grpc.CallOption) (grpc.BidiStreamingClient[WatchRequest, WatchResponse], error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
stream, err := c.cc.NewStream(ctx, &Watch_ServiceDesc.Streams[0], Watch_Watch_FullMethodName, cOpts...)
if err != nil {
return nil, err
}
x := &grpc.GenericClientStream[WatchRequest, WatchResponse]{ClientStream: stream}
return x, nil
}
// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name.
type Watch_WatchClient = grpc.BidiStreamingClient[WatchRequest, WatchResponse]
// WatchServer is the server API for Watch service.
// All implementations must embed UnimplementedWatchServer
// for forward compatibility.
type WatchServer interface {
// Watch watches for events happening or that have happened. Both input and output
// are streams; the input stream is for creating and canceling watchers and the output
// stream sends events. One watch RPC can watch on multiple key ranges, streaming events
// for several watches at once. The entire event history can be watched starting from the
// last compaction revision.
Watch(grpc.BidiStreamingServer[WatchRequest, WatchResponse]) error
mustEmbedUnimplementedWatchServer()
}
// UnimplementedWatchServer must be embedded to have
// forward compatible implementations.
//
// NOTE: this should be embedded by value instead of pointer to avoid a nil
// pointer dereference when methods are called.
type UnimplementedWatchServer struct{}
func (UnimplementedWatchServer) Watch(grpc.BidiStreamingServer[WatchRequest, WatchResponse]) error {
return status.Error(codes.Unimplemented, "method Watch not implemented")
}
func (UnimplementedWatchServer) mustEmbedUnimplementedWatchServer() {}
func (UnimplementedWatchServer) testEmbeddedByValue() {}
// UnsafeWatchServer may be embedded to opt out of forward compatibility for this service.
// Use of this interface is not recommended, as added methods to WatchServer will
// result in compilation errors.
type UnsafeWatchServer interface {
mustEmbedUnimplementedWatchServer()
}
func RegisterWatchServer(s grpc.ServiceRegistrar, srv WatchServer) {
// If the following call panics, it indicates UnimplementedWatchServer was
// embedded by pointer and is nil. This will cause panics if an
// unimplemented method is ever invoked, so we test this at initialization
// time to prevent it from happening at runtime later due to I/O.
if t, ok := srv.(interface{ testEmbeddedByValue() }); ok {
t.testEmbeddedByValue()
}
s.RegisterService(&Watch_ServiceDesc, srv)
}
func _Watch_Watch_Handler(srv interface{}, stream grpc.ServerStream) error {
return srv.(WatchServer).Watch(&grpc.GenericServerStream[WatchRequest, WatchResponse]{ServerStream: stream})
}
// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name.
type Watch_WatchServer = grpc.BidiStreamingServer[WatchRequest, WatchResponse]
// Watch_ServiceDesc is the grpc.ServiceDesc for Watch service.
// It's only intended for direct use with grpc.RegisterService,
// and not to be introspected or modified (even as a copy)
var Watch_ServiceDesc = grpc.ServiceDesc{
ServiceName: "etcdserverpb.Watch",
HandlerType: (*WatchServer)(nil),
Methods: []grpc.MethodDesc{},
Streams: []grpc.StreamDesc{
{
StreamName: "Watch",
Handler: _Watch_Watch_Handler,
ServerStreams: true,
ClientStreams: true,
},
},
Metadata: "rpc.proto",
}
const (
Lease_LeaseGrant_FullMethodName = "/etcdserverpb.Lease/LeaseGrant"
Lease_LeaseRevoke_FullMethodName = "/etcdserverpb.Lease/LeaseRevoke"
Lease_LeaseKeepAlive_FullMethodName = "/etcdserverpb.Lease/LeaseKeepAlive"
Lease_LeaseTimeToLive_FullMethodName = "/etcdserverpb.Lease/LeaseTimeToLive"
Lease_LeaseLeases_FullMethodName = "/etcdserverpb.Lease/LeaseLeases"
)
// LeaseClient is the client API for Lease service.
//
// 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 LeaseClient interface {
// LeaseGrant creates a lease which expires if the server does not receive a keepAlive
// within a given time to live period. All keys attached to the lease will be expired and
// deleted if the lease expires. Each expired key generates a delete event in the event history.
LeaseGrant(ctx context.Context, in *LeaseGrantRequest, opts ...grpc.CallOption) (*LeaseGrantResponse, error)
// LeaseRevoke revokes a lease. All keys attached to the lease will expire and be deleted.
LeaseRevoke(ctx context.Context, in *LeaseRevokeRequest, opts ...grpc.CallOption) (*LeaseRevokeResponse, error)
// LeaseKeepAlive keeps the lease alive by streaming keep alive requests from the client
// to the server and streaming keep alive responses from the server to the client.
LeaseKeepAlive(ctx context.Context, opts ...grpc.CallOption) (grpc.BidiStreamingClient[LeaseKeepAliveRequest, LeaseKeepAliveResponse], error)
// LeaseTimeToLive retrieves lease information.
LeaseTimeToLive(ctx context.Context, in *LeaseTimeToLiveRequest, opts ...grpc.CallOption) (*LeaseTimeToLiveResponse, error)
// LeaseLeases lists all existing leases.
LeaseLeases(ctx context.Context, in *LeaseLeasesRequest, opts ...grpc.CallOption) (*LeaseLeasesResponse, error)
}
type leaseClient struct {
cc grpc.ClientConnInterface
}
func NewLeaseClient(cc grpc.ClientConnInterface) LeaseClient {
return &leaseClient{cc}
}
func (c *leaseClient) LeaseGrant(ctx context.Context, in *LeaseGrantRequest, opts ...grpc.CallOption) (*LeaseGrantResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(LeaseGrantResponse)
err := c.cc.Invoke(ctx, Lease_LeaseGrant_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *leaseClient) LeaseRevoke(ctx context.Context, in *LeaseRevokeRequest, opts ...grpc.CallOption) (*LeaseRevokeResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(LeaseRevokeResponse)
err := c.cc.Invoke(ctx, Lease_LeaseRevoke_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *leaseClient) LeaseKeepAlive(ctx context.Context, opts ...grpc.CallOption) (grpc.BidiStreamingClient[LeaseKeepAliveRequest, LeaseKeepAliveResponse], error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
stream, err := c.cc.NewStream(ctx, &Lease_ServiceDesc.Streams[0], Lease_LeaseKeepAlive_FullMethodName, cOpts...)
if err != nil {
return nil, err
}
x := &grpc.GenericClientStream[LeaseKeepAliveRequest, LeaseKeepAliveResponse]{ClientStream: stream}
return x, nil
}
// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name.
type Lease_LeaseKeepAliveClient = grpc.BidiStreamingClient[LeaseKeepAliveRequest, LeaseKeepAliveResponse]
func (c *leaseClient) LeaseTimeToLive(ctx context.Context, in *LeaseTimeToLiveRequest, opts ...grpc.CallOption) (*LeaseTimeToLiveResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(LeaseTimeToLiveResponse)
err := c.cc.Invoke(ctx, Lease_LeaseTimeToLive_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *leaseClient) LeaseLeases(ctx context.Context, in *LeaseLeasesRequest, opts ...grpc.CallOption) (*LeaseLeasesResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(LeaseLeasesResponse)
err := c.cc.Invoke(ctx, Lease_LeaseLeases_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
// LeaseServer is the server API for Lease service.
// All implementations must embed UnimplementedLeaseServer
// for forward compatibility.
type LeaseServer interface {
// LeaseGrant creates a lease which expires if the server does not receive a keepAlive
// within a given time to live period. All keys attached to the lease will be expired and
// deleted if the lease expires. Each expired key generates a delete event in the event history.
LeaseGrant(context.Context, *LeaseGrantRequest) (*LeaseGrantResponse, error)
// LeaseRevoke revokes a lease. All keys attached to the lease will expire and be deleted.
LeaseRevoke(context.Context, *LeaseRevokeRequest) (*LeaseRevokeResponse, error)
// LeaseKeepAlive keeps the lease alive by streaming keep alive requests from the client
// to the server and streaming keep alive responses from the server to the client.
LeaseKeepAlive(grpc.BidiStreamingServer[LeaseKeepAliveRequest, LeaseKeepAliveResponse]) error
// LeaseTimeToLive retrieves lease information.
LeaseTimeToLive(context.Context, *LeaseTimeToLiveRequest) (*LeaseTimeToLiveResponse, error)
// LeaseLeases lists all existing leases.
LeaseLeases(context.Context, *LeaseLeasesRequest) (*LeaseLeasesResponse, error)
mustEmbedUnimplementedLeaseServer()
}
// UnimplementedLeaseServer must be embedded to have
// forward compatible implementations.
//
// NOTE: this should be embedded by value instead of pointer to avoid a nil
// pointer dereference when methods are called.
type UnimplementedLeaseServer struct{}
func (UnimplementedLeaseServer) LeaseGrant(context.Context, *LeaseGrantRequest) (*LeaseGrantResponse, error) {
return nil, status.Error(codes.Unimplemented, "method LeaseGrant not implemented")
}
func (UnimplementedLeaseServer) LeaseRevoke(context.Context, *LeaseRevokeRequest) (*LeaseRevokeResponse, error) {
return nil, status.Error(codes.Unimplemented, "method LeaseRevoke not implemented")
}
func (UnimplementedLeaseServer) LeaseKeepAlive(grpc.BidiStreamingServer[LeaseKeepAliveRequest, LeaseKeepAliveResponse]) error {
return status.Error(codes.Unimplemented, "method LeaseKeepAlive not implemented")
}
func (UnimplementedLeaseServer) LeaseTimeToLive(context.Context, *LeaseTimeToLiveRequest) (*LeaseTimeToLiveResponse, error) {
return nil, status.Error(codes.Unimplemented, "method LeaseTimeToLive not implemented")
}
func (UnimplementedLeaseServer) LeaseLeases(context.Context, *LeaseLeasesRequest) (*LeaseLeasesResponse, error) {
return nil, status.Error(codes.Unimplemented, "method LeaseLeases not implemented")
}
func (UnimplementedLeaseServer) mustEmbedUnimplementedLeaseServer() {}
func (UnimplementedLeaseServer) testEmbeddedByValue() {}
// UnsafeLeaseServer may be embedded to opt out of forward compatibility for this service.
// Use of this interface is not recommended, as added methods to LeaseServer will
// result in compilation errors.
type UnsafeLeaseServer interface {
mustEmbedUnimplementedLeaseServer()
}
func RegisterLeaseServer(s grpc.ServiceRegistrar, srv LeaseServer) {
// If the following call panics, it indicates UnimplementedLeaseServer was
// embedded by pointer and is nil. This will cause panics if an
// unimplemented method is ever invoked, so we test this at initialization
// time to prevent it from happening at runtime later due to I/O.
if t, ok := srv.(interface{ testEmbeddedByValue() }); ok {
t.testEmbeddedByValue()
}
s.RegisterService(&Lease_ServiceDesc, srv)
}
func _Lease_LeaseGrant_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(LeaseGrantRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(LeaseServer).LeaseGrant(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: Lease_LeaseGrant_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(LeaseServer).LeaseGrant(ctx, req.(*LeaseGrantRequest))
}
return interceptor(ctx, in, info, handler)
}
func _Lease_LeaseRevoke_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(LeaseRevokeRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(LeaseServer).LeaseRevoke(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: Lease_LeaseRevoke_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(LeaseServer).LeaseRevoke(ctx, req.(*LeaseRevokeRequest))
}
return interceptor(ctx, in, info, handler)
}
func _Lease_LeaseKeepAlive_Handler(srv interface{}, stream grpc.ServerStream) error {
return srv.(LeaseServer).LeaseKeepAlive(&grpc.GenericServerStream[LeaseKeepAliveRequest, LeaseKeepAliveResponse]{ServerStream: stream})
}
// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name.
type Lease_LeaseKeepAliveServer = grpc.BidiStreamingServer[LeaseKeepAliveRequest, LeaseKeepAliveResponse]
func _Lease_LeaseTimeToLive_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(LeaseTimeToLiveRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(LeaseServer).LeaseTimeToLive(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: Lease_LeaseTimeToLive_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(LeaseServer).LeaseTimeToLive(ctx, req.(*LeaseTimeToLiveRequest))
}
return interceptor(ctx, in, info, handler)
}
func _Lease_LeaseLeases_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(LeaseLeasesRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(LeaseServer).LeaseLeases(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: Lease_LeaseLeases_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(LeaseServer).LeaseLeases(ctx, req.(*LeaseLeasesRequest))
}
return interceptor(ctx, in, info, handler)
}
// Lease_ServiceDesc is the grpc.ServiceDesc for Lease service.
// It's only intended for direct use with grpc.RegisterService,
// and not to be introspected or modified (even as a copy)
var Lease_ServiceDesc = grpc.ServiceDesc{
ServiceName: "etcdserverpb.Lease",
HandlerType: (*LeaseServer)(nil),
Methods: []grpc.MethodDesc{
{
MethodName: "LeaseGrant",
Handler: _Lease_LeaseGrant_Handler,
},
{
MethodName: "LeaseRevoke",
Handler: _Lease_LeaseRevoke_Handler,
},
{
MethodName: "LeaseTimeToLive",
Handler: _Lease_LeaseTimeToLive_Handler,
},
{
MethodName: "LeaseLeases",
Handler: _Lease_LeaseLeases_Handler,
},
},
Streams: []grpc.StreamDesc{
{
StreamName: "LeaseKeepAlive",
Handler: _Lease_LeaseKeepAlive_Handler,
ServerStreams: true,
ClientStreams: true,
},
},
Metadata: "rpc.proto",
}
const (
Cluster_MemberAdd_FullMethodName = "/etcdserverpb.Cluster/MemberAdd"
Cluster_MemberRemove_FullMethodName = "/etcdserverpb.Cluster/MemberRemove"
Cluster_MemberUpdate_FullMethodName = "/etcdserverpb.Cluster/MemberUpdate"
Cluster_MemberList_FullMethodName = "/etcdserverpb.Cluster/MemberList"
Cluster_MemberPromote_FullMethodName = "/etcdserverpb.Cluster/MemberPromote"
)
// ClusterClient is the client API for Cluster service.
//
// 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 ClusterClient interface {
// MemberAdd adds a member into the cluster.
MemberAdd(ctx context.Context, in *MemberAddRequest, opts ...grpc.CallOption) (*MemberAddResponse, error)
// MemberRemove removes an existing member from the cluster.
MemberRemove(ctx context.Context, in *MemberRemoveRequest, opts ...grpc.CallOption) (*MemberRemoveResponse, error)
// MemberUpdate updates the member configuration.
MemberUpdate(ctx context.Context, in *MemberUpdateRequest, opts ...grpc.CallOption) (*MemberUpdateResponse, error)
// MemberList lists all the members in the cluster.
MemberList(ctx context.Context, in *MemberListRequest, opts ...grpc.CallOption) (*MemberListResponse, error)
// MemberPromote promotes a member from raft learner (non-voting) to raft voting member.
MemberPromote(ctx context.Context, in *MemberPromoteRequest, opts ...grpc.CallOption) (*MemberPromoteResponse, error)
}
type clusterClient struct {
cc grpc.ClientConnInterface
}
func NewClusterClient(cc grpc.ClientConnInterface) ClusterClient {
return &clusterClient{cc}
}
func (c *clusterClient) MemberAdd(ctx context.Context, in *MemberAddRequest, opts ...grpc.CallOption) (*MemberAddResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(MemberAddResponse)
err := c.cc.Invoke(ctx, Cluster_MemberAdd_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *clusterClient) MemberRemove(ctx context.Context, in *MemberRemoveRequest, opts ...grpc.CallOption) (*MemberRemoveResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(MemberRemoveResponse)
err := c.cc.Invoke(ctx, Cluster_MemberRemove_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *clusterClient) MemberUpdate(ctx context.Context, in *MemberUpdateRequest, opts ...grpc.CallOption) (*MemberUpdateResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(MemberUpdateResponse)
err := c.cc.Invoke(ctx, Cluster_MemberUpdate_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *clusterClient) MemberList(ctx context.Context, in *MemberListRequest, opts ...grpc.CallOption) (*MemberListResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(MemberListResponse)
err := c.cc.Invoke(ctx, Cluster_MemberList_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *clusterClient) MemberPromote(ctx context.Context, in *MemberPromoteRequest, opts ...grpc.CallOption) (*MemberPromoteResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(MemberPromoteResponse)
err := c.cc.Invoke(ctx, Cluster_MemberPromote_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
// ClusterServer is the server API for Cluster service.
// All implementations must embed UnimplementedClusterServer
// for forward compatibility.
type ClusterServer interface {
// MemberAdd adds a member into the cluster.
MemberAdd(context.Context, *MemberAddRequest) (*MemberAddResponse, error)
// MemberRemove removes an existing member from the cluster.
MemberRemove(context.Context, *MemberRemoveRequest) (*MemberRemoveResponse, error)
// MemberUpdate updates the member configuration.
MemberUpdate(context.Context, *MemberUpdateRequest) (*MemberUpdateResponse, error)
// MemberList lists all the members in the cluster.
MemberList(context.Context, *MemberListRequest) (*MemberListResponse, error)
// MemberPromote promotes a member from raft learner (non-voting) to raft voting member.
MemberPromote(context.Context, *MemberPromoteRequest) (*MemberPromoteResponse, error)
mustEmbedUnimplementedClusterServer()
}
// UnimplementedClusterServer must be embedded to have
// forward compatible implementations.
//
// NOTE: this should be embedded by value instead of pointer to avoid a nil
// pointer dereference when methods are called.
type UnimplementedClusterServer struct{}
func (UnimplementedClusterServer) MemberAdd(context.Context, *MemberAddRequest) (*MemberAddResponse, error) {
return nil, status.Error(codes.Unimplemented, "method MemberAdd not implemented")
}
func (UnimplementedClusterServer) MemberRemove(context.Context, *MemberRemoveRequest) (*MemberRemoveResponse, error) {
return nil, status.Error(codes.Unimplemented, "method MemberRemove not implemented")
}
func (UnimplementedClusterServer) MemberUpdate(context.Context, *MemberUpdateRequest) (*MemberUpdateResponse, error) {
return nil, status.Error(codes.Unimplemented, "method MemberUpdate not implemented")
}
func (UnimplementedClusterServer) MemberList(context.Context, *MemberListRequest) (*MemberListResponse, error) {
return nil, status.Error(codes.Unimplemented, "method MemberList not implemented")
}
func (UnimplementedClusterServer) MemberPromote(context.Context, *MemberPromoteRequest) (*MemberPromoteResponse, error) {
return nil, status.Error(codes.Unimplemented, "method MemberPromote not implemented")
}
func (UnimplementedClusterServer) mustEmbedUnimplementedClusterServer() {}
func (UnimplementedClusterServer) testEmbeddedByValue() {}
// UnsafeClusterServer may be embedded to opt out of forward compatibility for this service.
// Use of this interface is not recommended, as added methods to ClusterServer will
// result in compilation errors.
type UnsafeClusterServer interface {
mustEmbedUnimplementedClusterServer()
}
func RegisterClusterServer(s grpc.ServiceRegistrar, srv ClusterServer) {
// If the following call panics, it indicates UnimplementedClusterServer was
// embedded by pointer and is nil. This will cause panics if an
// unimplemented method is ever invoked, so we test this at initialization
// time to prevent it from happening at runtime later due to I/O.
if t, ok := srv.(interface{ testEmbeddedByValue() }); ok {
t.testEmbeddedByValue()
}
s.RegisterService(&Cluster_ServiceDesc, srv)
}
func _Cluster_MemberAdd_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(MemberAddRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(ClusterServer).MemberAdd(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: Cluster_MemberAdd_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(ClusterServer).MemberAdd(ctx, req.(*MemberAddRequest))
}
return interceptor(ctx, in, info, handler)
}
func _Cluster_MemberRemove_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(MemberRemoveRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(ClusterServer).MemberRemove(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: Cluster_MemberRemove_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(ClusterServer).MemberRemove(ctx, req.(*MemberRemoveRequest))
}
return interceptor(ctx, in, info, handler)
}
func _Cluster_MemberUpdate_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(MemberUpdateRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(ClusterServer).MemberUpdate(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: Cluster_MemberUpdate_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(ClusterServer).MemberUpdate(ctx, req.(*MemberUpdateRequest))
}
return interceptor(ctx, in, info, handler)
}
func _Cluster_MemberList_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(MemberListRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(ClusterServer).MemberList(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: Cluster_MemberList_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(ClusterServer).MemberList(ctx, req.(*MemberListRequest))
}
return interceptor(ctx, in, info, handler)
}
func _Cluster_MemberPromote_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(MemberPromoteRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(ClusterServer).MemberPromote(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: Cluster_MemberPromote_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(ClusterServer).MemberPromote(ctx, req.(*MemberPromoteRequest))
}
return interceptor(ctx, in, info, handler)
}
// Cluster_ServiceDesc is the grpc.ServiceDesc for Cluster service.
// It's only intended for direct use with grpc.RegisterService,
// and not to be introspected or modified (even as a copy)
var Cluster_ServiceDesc = grpc.ServiceDesc{
ServiceName: "etcdserverpb.Cluster",
HandlerType: (*ClusterServer)(nil),
Methods: []grpc.MethodDesc{
{
MethodName: "MemberAdd",
Handler: _Cluster_MemberAdd_Handler,
},
{
MethodName: "MemberRemove",
Handler: _Cluster_MemberRemove_Handler,
},
{
MethodName: "MemberUpdate",
Handler: _Cluster_MemberUpdate_Handler,
},
{
MethodName: "MemberList",
Handler: _Cluster_MemberList_Handler,
},
{
MethodName: "MemberPromote",
Handler: _Cluster_MemberPromote_Handler,
},
},
Streams: []grpc.StreamDesc{},
Metadata: "rpc.proto",
}
const (
Maintenance_Alarm_FullMethodName = "/etcdserverpb.Maintenance/Alarm"
Maintenance_Status_FullMethodName = "/etcdserverpb.Maintenance/Status"
Maintenance_Defragment_FullMethodName = "/etcdserverpb.Maintenance/Defragment"
Maintenance_Hash_FullMethodName = "/etcdserverpb.Maintenance/Hash"
Maintenance_HashKV_FullMethodName = "/etcdserverpb.Maintenance/HashKV"
Maintenance_Snapshot_FullMethodName = "/etcdserverpb.Maintenance/Snapshot"
Maintenance_MoveLeader_FullMethodName = "/etcdserverpb.Maintenance/MoveLeader"
Maintenance_Downgrade_FullMethodName = "/etcdserverpb.Maintenance/Downgrade"
)
// MaintenanceClient is the client API for Maintenance service.
//
// 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 MaintenanceClient interface {
// Alarm activates, deactivates, and queries alarms regarding cluster health.
Alarm(ctx context.Context, in *AlarmRequest, opts ...grpc.CallOption) (*AlarmResponse, error)
// Status gets the status of the member.
Status(ctx context.Context, in *StatusRequest, opts ...grpc.CallOption) (*StatusResponse, error)
// Defragment defragments a member's backend database to recover storage space.
Defragment(ctx context.Context, in *DefragmentRequest, opts ...grpc.CallOption) (*DefragmentResponse, error)
// Hash computes the hash of whole backend keyspace,
// including key, lease, and other buckets in storage.
// This is designed for testing ONLY!
// Do not rely on this in production with ongoing transactions,
// since Hash operation does not hold MVCC locks.
// Use "HashKV" API instead for "key" bucket consistency checks.
Hash(ctx context.Context, in *HashRequest, opts ...grpc.CallOption) (*HashResponse, error)
// HashKV computes the hash of all MVCC keys up to a given revision.
// It only iterates "key" bucket in backend storage.
HashKV(ctx context.Context, in *HashKVRequest, opts ...grpc.CallOption) (*HashKVResponse, error)
// Snapshot sends a snapshot of the entire backend from a member over a stream to a client.
Snapshot(ctx context.Context, in *SnapshotRequest, opts ...grpc.CallOption) (grpc.ServerStreamingClient[SnapshotResponse], error)
// MoveLeader requests current leader node to transfer its leadership to transferee.
MoveLeader(ctx context.Context, in *MoveLeaderRequest, opts ...grpc.CallOption) (*MoveLeaderResponse, error)
// Downgrade requests downgrades, verifies feasibility or cancels downgrade
// on the cluster version.
// Supported since etcd 3.5.
Downgrade(ctx context.Context, in *DowngradeRequest, opts ...grpc.CallOption) (*DowngradeResponse, error)
}
type maintenanceClient struct {
cc grpc.ClientConnInterface
}
func NewMaintenanceClient(cc grpc.ClientConnInterface) MaintenanceClient {
return &maintenanceClient{cc}
}
func (c *maintenanceClient) Alarm(ctx context.Context, in *AlarmRequest, opts ...grpc.CallOption) (*AlarmResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(AlarmResponse)
err := c.cc.Invoke(ctx, Maintenance_Alarm_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *maintenanceClient) Status(ctx context.Context, in *StatusRequest, opts ...grpc.CallOption) (*StatusResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(StatusResponse)
err := c.cc.Invoke(ctx, Maintenance_Status_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *maintenanceClient) Defragment(ctx context.Context, in *DefragmentRequest, opts ...grpc.CallOption) (*DefragmentResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(DefragmentResponse)
err := c.cc.Invoke(ctx, Maintenance_Defragment_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *maintenanceClient) Hash(ctx context.Context, in *HashRequest, opts ...grpc.CallOption) (*HashResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(HashResponse)
err := c.cc.Invoke(ctx, Maintenance_Hash_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *maintenanceClient) HashKV(ctx context.Context, in *HashKVRequest, opts ...grpc.CallOption) (*HashKVResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(HashKVResponse)
err := c.cc.Invoke(ctx, Maintenance_HashKV_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *maintenanceClient) Snapshot(ctx context.Context, in *SnapshotRequest, opts ...grpc.CallOption) (grpc.ServerStreamingClient[SnapshotResponse], error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
stream, err := c.cc.NewStream(ctx, &Maintenance_ServiceDesc.Streams[0], Maintenance_Snapshot_FullMethodName, cOpts...)
if err != nil {
return nil, err
}
x := &grpc.GenericClientStream[SnapshotRequest, SnapshotResponse]{ClientStream: stream}
if err := x.ClientStream.SendMsg(in); err != nil {
return nil, err
}
if err := x.ClientStream.CloseSend(); err != nil {
return nil, err
}
return x, nil
}
// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name.
type Maintenance_SnapshotClient = grpc.ServerStreamingClient[SnapshotResponse]
func (c *maintenanceClient) MoveLeader(ctx context.Context, in *MoveLeaderRequest, opts ...grpc.CallOption) (*MoveLeaderResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(MoveLeaderResponse)
err := c.cc.Invoke(ctx, Maintenance_MoveLeader_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *maintenanceClient) Downgrade(ctx context.Context, in *DowngradeRequest, opts ...grpc.CallOption) (*DowngradeResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(DowngradeResponse)
err := c.cc.Invoke(ctx, Maintenance_Downgrade_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
// MaintenanceServer is the server API for Maintenance service.
// All implementations must embed UnimplementedMaintenanceServer
// for forward compatibility.
type MaintenanceServer interface {
// Alarm activates, deactivates, and queries alarms regarding cluster health.
Alarm(context.Context, *AlarmRequest) (*AlarmResponse, error)
// Status gets the status of the member.
Status(context.Context, *StatusRequest) (*StatusResponse, error)
// Defragment defragments a member's backend database to recover storage space.
Defragment(context.Context, *DefragmentRequest) (*DefragmentResponse, error)
// Hash computes the hash of whole backend keyspace,
// including key, lease, and other buckets in storage.
// This is designed for testing ONLY!
// Do not rely on this in production with ongoing transactions,
// since Hash operation does not hold MVCC locks.
// Use "HashKV" API instead for "key" bucket consistency checks.
Hash(context.Context, *HashRequest) (*HashResponse, error)
// HashKV computes the hash of all MVCC keys up to a given revision.
// It only iterates "key" bucket in backend storage.
HashKV(context.Context, *HashKVRequest) (*HashKVResponse, error)
// Snapshot sends a snapshot of the entire backend from a member over a stream to a client.
Snapshot(*SnapshotRequest, grpc.ServerStreamingServer[SnapshotResponse]) error
// MoveLeader requests current leader node to transfer its leadership to transferee.
MoveLeader(context.Context, *MoveLeaderRequest) (*MoveLeaderResponse, error)
// Downgrade requests downgrades, verifies feasibility or cancels downgrade
// on the cluster version.
// Supported since etcd 3.5.
Downgrade(context.Context, *DowngradeRequest) (*DowngradeResponse, error)
mustEmbedUnimplementedMaintenanceServer()
}
// UnimplementedMaintenanceServer must be embedded to have
// forward compatible implementations.
//
// NOTE: this should be embedded by value instead of pointer to avoid a nil
// pointer dereference when methods are called.
type UnimplementedMaintenanceServer struct{}
func (UnimplementedMaintenanceServer) Alarm(context.Context, *AlarmRequest) (*AlarmResponse, error) {
return nil, status.Error(codes.Unimplemented, "method Alarm not implemented")
}
func (UnimplementedMaintenanceServer) Status(context.Context, *StatusRequest) (*StatusResponse, error) {
return nil, status.Error(codes.Unimplemented, "method Status not implemented")
}
func (UnimplementedMaintenanceServer) Defragment(context.Context, *DefragmentRequest) (*DefragmentResponse, error) {
return nil, status.Error(codes.Unimplemented, "method Defragment not implemented")
}
func (UnimplementedMaintenanceServer) Hash(context.Context, *HashRequest) (*HashResponse, error) {
return nil, status.Error(codes.Unimplemented, "method Hash not implemented")
}
func (UnimplementedMaintenanceServer) HashKV(context.Context, *HashKVRequest) (*HashKVResponse, error) {
return nil, status.Error(codes.Unimplemented, "method HashKV not implemented")
}
func (UnimplementedMaintenanceServer) Snapshot(*SnapshotRequest, grpc.ServerStreamingServer[SnapshotResponse]) error {
return status.Error(codes.Unimplemented, "method Snapshot not implemented")
}
func (UnimplementedMaintenanceServer) MoveLeader(context.Context, *MoveLeaderRequest) (*MoveLeaderResponse, error) {
return nil, status.Error(codes.Unimplemented, "method MoveLeader not implemented")
}
func (UnimplementedMaintenanceServer) Downgrade(context.Context, *DowngradeRequest) (*DowngradeResponse, error) {
return nil, status.Error(codes.Unimplemented, "method Downgrade not implemented")
}
func (UnimplementedMaintenanceServer) mustEmbedUnimplementedMaintenanceServer() {}
func (UnimplementedMaintenanceServer) testEmbeddedByValue() {}
// UnsafeMaintenanceServer may be embedded to opt out of forward compatibility for this service.
// Use of this interface is not recommended, as added methods to MaintenanceServer will
// result in compilation errors.
type UnsafeMaintenanceServer interface {
mustEmbedUnimplementedMaintenanceServer()
}
func RegisterMaintenanceServer(s grpc.ServiceRegistrar, srv MaintenanceServer) {
// If the following call panics, it indicates UnimplementedMaintenanceServer was
// embedded by pointer and is nil. This will cause panics if an
// unimplemented method is ever invoked, so we test this at initialization
// time to prevent it from happening at runtime later due to I/O.
if t, ok := srv.(interface{ testEmbeddedByValue() }); ok {
t.testEmbeddedByValue()
}
s.RegisterService(&Maintenance_ServiceDesc, srv)
}
func _Maintenance_Alarm_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(AlarmRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(MaintenanceServer).Alarm(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: Maintenance_Alarm_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(MaintenanceServer).Alarm(ctx, req.(*AlarmRequest))
}
return interceptor(ctx, in, info, handler)
}
func _Maintenance_Status_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(StatusRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(MaintenanceServer).Status(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: Maintenance_Status_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(MaintenanceServer).Status(ctx, req.(*StatusRequest))
}
return interceptor(ctx, in, info, handler)
}
func _Maintenance_Defragment_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(DefragmentRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(MaintenanceServer).Defragment(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: Maintenance_Defragment_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(MaintenanceServer).Defragment(ctx, req.(*DefragmentRequest))
}
return interceptor(ctx, in, info, handler)
}
func _Maintenance_Hash_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(HashRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(MaintenanceServer).Hash(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: Maintenance_Hash_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(MaintenanceServer).Hash(ctx, req.(*HashRequest))
}
return interceptor(ctx, in, info, handler)
}
func _Maintenance_HashKV_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(HashKVRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(MaintenanceServer).HashKV(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: Maintenance_HashKV_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(MaintenanceServer).HashKV(ctx, req.(*HashKVRequest))
}
return interceptor(ctx, in, info, handler)
}
func _Maintenance_Snapshot_Handler(srv interface{}, stream grpc.ServerStream) error {
m := new(SnapshotRequest)
if err := stream.RecvMsg(m); err != nil {
return err
}
return srv.(MaintenanceServer).Snapshot(m, &grpc.GenericServerStream[SnapshotRequest, SnapshotResponse]{ServerStream: stream})
}
// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name.
type Maintenance_SnapshotServer = grpc.ServerStreamingServer[SnapshotResponse]
func _Maintenance_MoveLeader_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(MoveLeaderRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(MaintenanceServer).MoveLeader(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: Maintenance_MoveLeader_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(MaintenanceServer).MoveLeader(ctx, req.(*MoveLeaderRequest))
}
return interceptor(ctx, in, info, handler)
}
func _Maintenance_Downgrade_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(DowngradeRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(MaintenanceServer).Downgrade(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: Maintenance_Downgrade_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(MaintenanceServer).Downgrade(ctx, req.(*DowngradeRequest))
}
return interceptor(ctx, in, info, handler)
}
// Maintenance_ServiceDesc is the grpc.ServiceDesc for Maintenance service.
// It's only intended for direct use with grpc.RegisterService,
// and not to be introspected or modified (even as a copy)
var Maintenance_ServiceDesc = grpc.ServiceDesc{
ServiceName: "etcdserverpb.Maintenance",
HandlerType: (*MaintenanceServer)(nil),
Methods: []grpc.MethodDesc{
{
MethodName: "Alarm",
Handler: _Maintenance_Alarm_Handler,
},
{
MethodName: "Status",
Handler: _Maintenance_Status_Handler,
},
{
MethodName: "Defragment",
Handler: _Maintenance_Defragment_Handler,
},
{
MethodName: "Hash",
Handler: _Maintenance_Hash_Handler,
},
{
MethodName: "HashKV",
Handler: _Maintenance_HashKV_Handler,
},
{
MethodName: "MoveLeader",
Handler: _Maintenance_MoveLeader_Handler,
},
{
MethodName: "Downgrade",
Handler: _Maintenance_Downgrade_Handler,
},
},
Streams: []grpc.StreamDesc{
{
StreamName: "Snapshot",
Handler: _Maintenance_Snapshot_Handler,
ServerStreams: true,
},
},
Metadata: "rpc.proto",
}
const (
Auth_AuthEnable_FullMethodName = "/etcdserverpb.Auth/AuthEnable"
Auth_AuthDisable_FullMethodName = "/etcdserverpb.Auth/AuthDisable"
Auth_AuthStatus_FullMethodName = "/etcdserverpb.Auth/AuthStatus"
Auth_Authenticate_FullMethodName = "/etcdserverpb.Auth/Authenticate"
Auth_UserAdd_FullMethodName = "/etcdserverpb.Auth/UserAdd"
Auth_UserGet_FullMethodName = "/etcdserverpb.Auth/UserGet"
Auth_UserList_FullMethodName = "/etcdserverpb.Auth/UserList"
Auth_UserDelete_FullMethodName = "/etcdserverpb.Auth/UserDelete"
Auth_UserChangePassword_FullMethodName = "/etcdserverpb.Auth/UserChangePassword"
Auth_UserGrantRole_FullMethodName = "/etcdserverpb.Auth/UserGrantRole"
Auth_UserRevokeRole_FullMethodName = "/etcdserverpb.Auth/UserRevokeRole"
Auth_RoleAdd_FullMethodName = "/etcdserverpb.Auth/RoleAdd"
Auth_RoleGet_FullMethodName = "/etcdserverpb.Auth/RoleGet"
Auth_RoleList_FullMethodName = "/etcdserverpb.Auth/RoleList"
Auth_RoleDelete_FullMethodName = "/etcdserverpb.Auth/RoleDelete"
Auth_RoleGrantPermission_FullMethodName = "/etcdserverpb.Auth/RoleGrantPermission"
Auth_RoleRevokePermission_FullMethodName = "/etcdserverpb.Auth/RoleRevokePermission"
)
// AuthClient is the client API for Auth service.
//
// 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 AuthClient interface {
// AuthEnable enables authentication.
AuthEnable(ctx context.Context, in *AuthEnableRequest, opts ...grpc.CallOption) (*AuthEnableResponse, error)
// AuthDisable disables authentication.
AuthDisable(ctx context.Context, in *AuthDisableRequest, opts ...grpc.CallOption) (*AuthDisableResponse, error)
// AuthStatus displays authentication status.
AuthStatus(ctx context.Context, in *AuthStatusRequest, opts ...grpc.CallOption) (*AuthStatusResponse, error)
// Authenticate processes an authenticate request.
Authenticate(ctx context.Context, in *AuthenticateRequest, opts ...grpc.CallOption) (*AuthenticateResponse, error)
// UserAdd adds a new user. User name cannot be empty.
UserAdd(ctx context.Context, in *AuthUserAddRequest, opts ...grpc.CallOption) (*AuthUserAddResponse, error)
// UserGet gets detailed user information.
UserGet(ctx context.Context, in *AuthUserGetRequest, opts ...grpc.CallOption) (*AuthUserGetResponse, error)
// UserList gets a list of all users.
UserList(ctx context.Context, in *AuthUserListRequest, opts ...grpc.CallOption) (*AuthUserListResponse, error)
// UserDelete deletes a specified user.
UserDelete(ctx context.Context, in *AuthUserDeleteRequest, opts ...grpc.CallOption) (*AuthUserDeleteResponse, error)
// UserChangePassword changes the password of a specified user.
UserChangePassword(ctx context.Context, in *AuthUserChangePasswordRequest, opts ...grpc.CallOption) (*AuthUserChangePasswordResponse, error)
// UserGrantRole grants a role to a specified user.
UserGrantRole(ctx context.Context, in *AuthUserGrantRoleRequest, opts ...grpc.CallOption) (*AuthUserGrantRoleResponse, error)
// UserRevokeRole revokes a role of specified user.
UserRevokeRole(ctx context.Context, in *AuthUserRevokeRoleRequest, opts ...grpc.CallOption) (*AuthUserRevokeRoleResponse, error)
// RoleAdd adds a new role. Role name cannot be empty.
RoleAdd(ctx context.Context, in *AuthRoleAddRequest, opts ...grpc.CallOption) (*AuthRoleAddResponse, error)
// RoleGet gets detailed role information.
RoleGet(ctx context.Context, in *AuthRoleGetRequest, opts ...grpc.CallOption) (*AuthRoleGetResponse, error)
// RoleList gets lists of all roles.
RoleList(ctx context.Context, in *AuthRoleListRequest, opts ...grpc.CallOption) (*AuthRoleListResponse, error)
// RoleDelete deletes a specified role.
RoleDelete(ctx context.Context, in *AuthRoleDeleteRequest, opts ...grpc.CallOption) (*AuthRoleDeleteResponse, error)
// RoleGrantPermission grants a permission of a specified key or range to a specified role.
RoleGrantPermission(ctx context.Context, in *AuthRoleGrantPermissionRequest, opts ...grpc.CallOption) (*AuthRoleGrantPermissionResponse, error)
// RoleRevokePermission revokes a key or range permission of a specified role.
RoleRevokePermission(ctx context.Context, in *AuthRoleRevokePermissionRequest, opts ...grpc.CallOption) (*AuthRoleRevokePermissionResponse, error)
}
type authClient struct {
cc grpc.ClientConnInterface
}
func NewAuthClient(cc grpc.ClientConnInterface) AuthClient {
return &authClient{cc}
}
func (c *authClient) AuthEnable(ctx context.Context, in *AuthEnableRequest, opts ...grpc.CallOption) (*AuthEnableResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(AuthEnableResponse)
err := c.cc.Invoke(ctx, Auth_AuthEnable_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *authClient) AuthDisable(ctx context.Context, in *AuthDisableRequest, opts ...grpc.CallOption) (*AuthDisableResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(AuthDisableResponse)
err := c.cc.Invoke(ctx, Auth_AuthDisable_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *authClient) AuthStatus(ctx context.Context, in *AuthStatusRequest, opts ...grpc.CallOption) (*AuthStatusResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(AuthStatusResponse)
err := c.cc.Invoke(ctx, Auth_AuthStatus_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *authClient) Authenticate(ctx context.Context, in *AuthenticateRequest, opts ...grpc.CallOption) (*AuthenticateResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(AuthenticateResponse)
err := c.cc.Invoke(ctx, Auth_Authenticate_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *authClient) UserAdd(ctx context.Context, in *AuthUserAddRequest, opts ...grpc.CallOption) (*AuthUserAddResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(AuthUserAddResponse)
err := c.cc.Invoke(ctx, Auth_UserAdd_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *authClient) UserGet(ctx context.Context, in *AuthUserGetRequest, opts ...grpc.CallOption) (*AuthUserGetResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(AuthUserGetResponse)
err := c.cc.Invoke(ctx, Auth_UserGet_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *authClient) UserList(ctx context.Context, in *AuthUserListRequest, opts ...grpc.CallOption) (*AuthUserListResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(AuthUserListResponse)
err := c.cc.Invoke(ctx, Auth_UserList_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *authClient) UserDelete(ctx context.Context, in *AuthUserDeleteRequest, opts ...grpc.CallOption) (*AuthUserDeleteResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(AuthUserDeleteResponse)
err := c.cc.Invoke(ctx, Auth_UserDelete_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *authClient) UserChangePassword(ctx context.Context, in *AuthUserChangePasswordRequest, opts ...grpc.CallOption) (*AuthUserChangePasswordResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(AuthUserChangePasswordResponse)
err := c.cc.Invoke(ctx, Auth_UserChangePassword_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *authClient) UserGrantRole(ctx context.Context, in *AuthUserGrantRoleRequest, opts ...grpc.CallOption) (*AuthUserGrantRoleResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(AuthUserGrantRoleResponse)
err := c.cc.Invoke(ctx, Auth_UserGrantRole_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *authClient) UserRevokeRole(ctx context.Context, in *AuthUserRevokeRoleRequest, opts ...grpc.CallOption) (*AuthUserRevokeRoleResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(AuthUserRevokeRoleResponse)
err := c.cc.Invoke(ctx, Auth_UserRevokeRole_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *authClient) RoleAdd(ctx context.Context, in *AuthRoleAddRequest, opts ...grpc.CallOption) (*AuthRoleAddResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(AuthRoleAddResponse)
err := c.cc.Invoke(ctx, Auth_RoleAdd_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *authClient) RoleGet(ctx context.Context, in *AuthRoleGetRequest, opts ...grpc.CallOption) (*AuthRoleGetResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(AuthRoleGetResponse)
err := c.cc.Invoke(ctx, Auth_RoleGet_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *authClient) RoleList(ctx context.Context, in *AuthRoleListRequest, opts ...grpc.CallOption) (*AuthRoleListResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(AuthRoleListResponse)
err := c.cc.Invoke(ctx, Auth_RoleList_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *authClient) RoleDelete(ctx context.Context, in *AuthRoleDeleteRequest, opts ...grpc.CallOption) (*AuthRoleDeleteResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(AuthRoleDeleteResponse)
err := c.cc.Invoke(ctx, Auth_RoleDelete_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *authClient) RoleGrantPermission(ctx context.Context, in *AuthRoleGrantPermissionRequest, opts ...grpc.CallOption) (*AuthRoleGrantPermissionResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(AuthRoleGrantPermissionResponse)
err := c.cc.Invoke(ctx, Auth_RoleGrantPermission_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *authClient) RoleRevokePermission(ctx context.Context, in *AuthRoleRevokePermissionRequest, opts ...grpc.CallOption) (*AuthRoleRevokePermissionResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(AuthRoleRevokePermissionResponse)
err := c.cc.Invoke(ctx, Auth_RoleRevokePermission_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
// AuthServer is the server API for Auth service.
// All implementations must embed UnimplementedAuthServer
// for forward compatibility.
type AuthServer interface {
// AuthEnable enables authentication.
AuthEnable(context.Context, *AuthEnableRequest) (*AuthEnableResponse, error)
// AuthDisable disables authentication.
AuthDisable(context.Context, *AuthDisableRequest) (*AuthDisableResponse, error)
// AuthStatus displays authentication status.
AuthStatus(context.Context, *AuthStatusRequest) (*AuthStatusResponse, error)
// Authenticate processes an authenticate request.
Authenticate(context.Context, *AuthenticateRequest) (*AuthenticateResponse, error)
// UserAdd adds a new user. User name cannot be empty.
UserAdd(context.Context, *AuthUserAddRequest) (*AuthUserAddResponse, error)
// UserGet gets detailed user information.
UserGet(context.Context, *AuthUserGetRequest) (*AuthUserGetResponse, error)
// UserList gets a list of all users.
UserList(context.Context, *AuthUserListRequest) (*AuthUserListResponse, error)
// UserDelete deletes a specified user.
UserDelete(context.Context, *AuthUserDeleteRequest) (*AuthUserDeleteResponse, error)
// UserChangePassword changes the password of a specified user.
UserChangePassword(context.Context, *AuthUserChangePasswordRequest) (*AuthUserChangePasswordResponse, error)
// UserGrantRole grants a role to a specified user.
UserGrantRole(context.Context, *AuthUserGrantRoleRequest) (*AuthUserGrantRoleResponse, error)
// UserRevokeRole revokes a role of specified user.
UserRevokeRole(context.Context, *AuthUserRevokeRoleRequest) (*AuthUserRevokeRoleResponse, error)
// RoleAdd adds a new role. Role name cannot be empty.
RoleAdd(context.Context, *AuthRoleAddRequest) (*AuthRoleAddResponse, error)
// RoleGet gets detailed role information.
RoleGet(context.Context, *AuthRoleGetRequest) (*AuthRoleGetResponse, error)
// RoleList gets lists of all roles.
RoleList(context.Context, *AuthRoleListRequest) (*AuthRoleListResponse, error)
// RoleDelete deletes a specified role.
RoleDelete(context.Context, *AuthRoleDeleteRequest) (*AuthRoleDeleteResponse, error)
// RoleGrantPermission grants a permission of a specified key or range to a specified role.
RoleGrantPermission(context.Context, *AuthRoleGrantPermissionRequest) (*AuthRoleGrantPermissionResponse, error)
// RoleRevokePermission revokes a key or range permission of a specified role.
RoleRevokePermission(context.Context, *AuthRoleRevokePermissionRequest) (*AuthRoleRevokePermissionResponse, error)
mustEmbedUnimplementedAuthServer()
}
// UnimplementedAuthServer must be embedded to have
// forward compatible implementations.
//
// NOTE: this should be embedded by value instead of pointer to avoid a nil
// pointer dereference when methods are called.
type UnimplementedAuthServer struct{}
func (UnimplementedAuthServer) AuthEnable(context.Context, *AuthEnableRequest) (*AuthEnableResponse, error) {
return nil, status.Error(codes.Unimplemented, "method AuthEnable not implemented")
}
func (UnimplementedAuthServer) AuthDisable(context.Context, *AuthDisableRequest) (*AuthDisableResponse, error) {
return nil, status.Error(codes.Unimplemented, "method AuthDisable not implemented")
}
func (UnimplementedAuthServer) AuthStatus(context.Context, *AuthStatusRequest) (*AuthStatusResponse, error) {
return nil, status.Error(codes.Unimplemented, "method AuthStatus not implemented")
}
func (UnimplementedAuthServer) Authenticate(context.Context, *AuthenticateRequest) (*AuthenticateResponse, error) {
return nil, status.Error(codes.Unimplemented, "method Authenticate not implemented")
}
func (UnimplementedAuthServer) UserAdd(context.Context, *AuthUserAddRequest) (*AuthUserAddResponse, error) {
return nil, status.Error(codes.Unimplemented, "method UserAdd not implemented")
}
func (UnimplementedAuthServer) UserGet(context.Context, *AuthUserGetRequest) (*AuthUserGetResponse, error) {
return nil, status.Error(codes.Unimplemented, "method UserGet not implemented")
}
func (UnimplementedAuthServer) UserList(context.Context, *AuthUserListRequest) (*AuthUserListResponse, error) {
return nil, status.Error(codes.Unimplemented, "method UserList not implemented")
}
func (UnimplementedAuthServer) UserDelete(context.Context, *AuthUserDeleteRequest) (*AuthUserDeleteResponse, error) {
return nil, status.Error(codes.Unimplemented, "method UserDelete not implemented")
}
func (UnimplementedAuthServer) UserChangePassword(context.Context, *AuthUserChangePasswordRequest) (*AuthUserChangePasswordResponse, error) {
return nil, status.Error(codes.Unimplemented, "method UserChangePassword not implemented")
}
func (UnimplementedAuthServer) UserGrantRole(context.Context, *AuthUserGrantRoleRequest) (*AuthUserGrantRoleResponse, error) {
return nil, status.Error(codes.Unimplemented, "method UserGrantRole not implemented")
}
func (UnimplementedAuthServer) UserRevokeRole(context.Context, *AuthUserRevokeRoleRequest) (*AuthUserRevokeRoleResponse, error) {
return nil, status.Error(codes.Unimplemented, "method UserRevokeRole not implemented")
}
func (UnimplementedAuthServer) RoleAdd(context.Context, *AuthRoleAddRequest) (*AuthRoleAddResponse, error) {
return nil, status.Error(codes.Unimplemented, "method RoleAdd not implemented")
}
func (UnimplementedAuthServer) RoleGet(context.Context, *AuthRoleGetRequest) (*AuthRoleGetResponse, error) {
return nil, status.Error(codes.Unimplemented, "method RoleGet not implemented")
}
func (UnimplementedAuthServer) RoleList(context.Context, *AuthRoleListRequest) (*AuthRoleListResponse, error) {
return nil, status.Error(codes.Unimplemented, "method RoleList not implemented")
}
func (UnimplementedAuthServer) RoleDelete(context.Context, *AuthRoleDeleteRequest) (*AuthRoleDeleteResponse, error) {
return nil, status.Error(codes.Unimplemented, "method RoleDelete not implemented")
}
func (UnimplementedAuthServer) RoleGrantPermission(context.Context, *AuthRoleGrantPermissionRequest) (*AuthRoleGrantPermissionResponse, error) {
return nil, status.Error(codes.Unimplemented, "method RoleGrantPermission not implemented")
}
func (UnimplementedAuthServer) RoleRevokePermission(context.Context, *AuthRoleRevokePermissionRequest) (*AuthRoleRevokePermissionResponse, error) {
return nil, status.Error(codes.Unimplemented, "method RoleRevokePermission not implemented")
}
func (UnimplementedAuthServer) mustEmbedUnimplementedAuthServer() {}
func (UnimplementedAuthServer) testEmbeddedByValue() {}
// UnsafeAuthServer may be embedded to opt out of forward compatibility for this service.
// Use of this interface is not recommended, as added methods to AuthServer will
// result in compilation errors.
type UnsafeAuthServer interface {
mustEmbedUnimplementedAuthServer()
}
func RegisterAuthServer(s grpc.ServiceRegistrar, srv AuthServer) {
// If the following call panics, it indicates UnimplementedAuthServer was
// embedded by pointer and is nil. This will cause panics if an
// unimplemented method is ever invoked, so we test this at initialization
// time to prevent it from happening at runtime later due to I/O.
if t, ok := srv.(interface{ testEmbeddedByValue() }); ok {
t.testEmbeddedByValue()
}
s.RegisterService(&Auth_ServiceDesc, srv)
}
func _Auth_AuthEnable_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(AuthEnableRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(AuthServer).AuthEnable(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: Auth_AuthEnable_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(AuthServer).AuthEnable(ctx, req.(*AuthEnableRequest))
}
return interceptor(ctx, in, info, handler)
}
func _Auth_AuthDisable_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(AuthDisableRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(AuthServer).AuthDisable(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: Auth_AuthDisable_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(AuthServer).AuthDisable(ctx, req.(*AuthDisableRequest))
}
return interceptor(ctx, in, info, handler)
}
func _Auth_AuthStatus_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(AuthStatusRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(AuthServer).AuthStatus(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: Auth_AuthStatus_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(AuthServer).AuthStatus(ctx, req.(*AuthStatusRequest))
}
return interceptor(ctx, in, info, handler)
}
func _Auth_Authenticate_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(AuthenticateRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(AuthServer).Authenticate(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: Auth_Authenticate_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(AuthServer).Authenticate(ctx, req.(*AuthenticateRequest))
}
return interceptor(ctx, in, info, handler)
}
func _Auth_UserAdd_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(AuthUserAddRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(AuthServer).UserAdd(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: Auth_UserAdd_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(AuthServer).UserAdd(ctx, req.(*AuthUserAddRequest))
}
return interceptor(ctx, in, info, handler)
}
func _Auth_UserGet_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(AuthUserGetRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(AuthServer).UserGet(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: Auth_UserGet_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(AuthServer).UserGet(ctx, req.(*AuthUserGetRequest))
}
return interceptor(ctx, in, info, handler)
}
func _Auth_UserList_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(AuthUserListRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(AuthServer).UserList(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: Auth_UserList_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(AuthServer).UserList(ctx, req.(*AuthUserListRequest))
}
return interceptor(ctx, in, info, handler)
}
func _Auth_UserDelete_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(AuthUserDeleteRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(AuthServer).UserDelete(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: Auth_UserDelete_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(AuthServer).UserDelete(ctx, req.(*AuthUserDeleteRequest))
}
return interceptor(ctx, in, info, handler)
}
func _Auth_UserChangePassword_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(AuthUserChangePasswordRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(AuthServer).UserChangePassword(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: Auth_UserChangePassword_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(AuthServer).UserChangePassword(ctx, req.(*AuthUserChangePasswordRequest))
}
return interceptor(ctx, in, info, handler)
}
func _Auth_UserGrantRole_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(AuthUserGrantRoleRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(AuthServer).UserGrantRole(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: Auth_UserGrantRole_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(AuthServer).UserGrantRole(ctx, req.(*AuthUserGrantRoleRequest))
}
return interceptor(ctx, in, info, handler)
}
func _Auth_UserRevokeRole_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(AuthUserRevokeRoleRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(AuthServer).UserRevokeRole(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: Auth_UserRevokeRole_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(AuthServer).UserRevokeRole(ctx, req.(*AuthUserRevokeRoleRequest))
}
return interceptor(ctx, in, info, handler)
}
func _Auth_RoleAdd_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(AuthRoleAddRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(AuthServer).RoleAdd(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: Auth_RoleAdd_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(AuthServer).RoleAdd(ctx, req.(*AuthRoleAddRequest))
}
return interceptor(ctx, in, info, handler)
}
func _Auth_RoleGet_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(AuthRoleGetRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(AuthServer).RoleGet(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: Auth_RoleGet_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(AuthServer).RoleGet(ctx, req.(*AuthRoleGetRequest))
}
return interceptor(ctx, in, info, handler)
}
func _Auth_RoleList_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(AuthRoleListRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(AuthServer).RoleList(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: Auth_RoleList_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(AuthServer).RoleList(ctx, req.(*AuthRoleListRequest))
}
return interceptor(ctx, in, info, handler)
}
func _Auth_RoleDelete_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(AuthRoleDeleteRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(AuthServer).RoleDelete(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: Auth_RoleDelete_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(AuthServer).RoleDelete(ctx, req.(*AuthRoleDeleteRequest))
}
return interceptor(ctx, in, info, handler)
}
func _Auth_RoleGrantPermission_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(AuthRoleGrantPermissionRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(AuthServer).RoleGrantPermission(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: Auth_RoleGrantPermission_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(AuthServer).RoleGrantPermission(ctx, req.(*AuthRoleGrantPermissionRequest))
}
return interceptor(ctx, in, info, handler)
}
func _Auth_RoleRevokePermission_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(AuthRoleRevokePermissionRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(AuthServer).RoleRevokePermission(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: Auth_RoleRevokePermission_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(AuthServer).RoleRevokePermission(ctx, req.(*AuthRoleRevokePermissionRequest))
}
return interceptor(ctx, in, info, handler)
}
// Auth_ServiceDesc is the grpc.ServiceDesc for Auth service.
// It's only intended for direct use with grpc.RegisterService,
// and not to be introspected or modified (even as a copy)
var Auth_ServiceDesc = grpc.ServiceDesc{
ServiceName: "etcdserverpb.Auth",
HandlerType: (*AuthServer)(nil),
Methods: []grpc.MethodDesc{
{
MethodName: "AuthEnable",
Handler: _Auth_AuthEnable_Handler,
},
{
MethodName: "AuthDisable",
Handler: _Auth_AuthDisable_Handler,
},
{
MethodName: "AuthStatus",
Handler: _Auth_AuthStatus_Handler,
},
{
MethodName: "Authenticate",
Handler: _Auth_Authenticate_Handler,
},
{
MethodName: "UserAdd",
Handler: _Auth_UserAdd_Handler,
},
{
MethodName: "UserGet",
Handler: _Auth_UserGet_Handler,
},
{
MethodName: "UserList",
Handler: _Auth_UserList_Handler,
},
{
MethodName: "UserDelete",
Handler: _Auth_UserDelete_Handler,
},
{
MethodName: "UserChangePassword",
Handler: _Auth_UserChangePassword_Handler,
},
{
MethodName: "UserGrantRole",
Handler: _Auth_UserGrantRole_Handler,
},
{
MethodName: "UserRevokeRole",
Handler: _Auth_UserRevokeRole_Handler,
},
{
MethodName: "RoleAdd",
Handler: _Auth_RoleAdd_Handler,
},
{
MethodName: "RoleGet",
Handler: _Auth_RoleGet_Handler,
},
{
MethodName: "RoleList",
Handler: _Auth_RoleList_Handler,
},
{
MethodName: "RoleDelete",
Handler: _Auth_RoleDelete_Handler,
},
{
MethodName: "RoleGrantPermission",
Handler: _Auth_RoleGrantPermission_Handler,
},
{
MethodName: "RoleRevokePermission",
Handler: _Auth_RoleRevokePermission_Handler,
},
},
Streams: []grpc.StreamDesc{},
Metadata: "rpc.proto",
}
================================================
FILE: api/go.mod
================================================
module go.etcd.io/etcd/api/v3
go 1.26
toolchain go1.26.1
require (
github.com/coreos/go-semver v0.3.1
github.com/golang/protobuf v1.5.4
github.com/grpc-ecosystem/grpc-gateway/v2 v2.28.0
github.com/stretchr/testify v1.11.1
google.golang.org/genproto/googleapis/api v0.0.0-20260209200024-4cfbd4190f57
google.golang.org/grpc v1.79.2
google.golang.org/protobuf v1.36.11
)
require (
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
github.com/rogpeppe/go-internal v1.14.1 // indirect
go.opentelemetry.io/otel v1.42.0 // indirect
go.opentelemetry.io/otel/sdk/metric v1.42.0 // indirect
golang.org/x/net v0.51.0 // indirect
golang.org/x/sys v0.41.0 // indirect
golang.org/x/text v0.34.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20260209200024-4cfbd4190f57 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
================================================
FILE: api/go.sum
================================================
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/coreos/go-semver v0.3.1 h1:yi21YpKnrx1gt5R+la8n5WgS0kCrsPp33dmEyHReZr4=
github.com/coreos/go-semver v0.3.1/go.mod h1:irMmmIw/7yzSRPWryHsK7EYSg09caPQL03VsM8rvUec=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
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/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
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/otel v1.42.0 h1:lSQGzTgVR3+sgJDAU/7/ZMjN9Z+vUip7leaqBKy4sho=
go.opentelemetry.io/otel v1.42.0/go.mod h1:lJNsdRMxCUIWuMlVJWzecSMuNjE7dOYyWlqOXWkdqCc=
go.opentelemetry.io/otel/metric v1.42.0 h1:2jXG+3oZLNXEPfNmnpxKDeZsFI5o4J+nz6xUlaFdF/4=
go.opentelemetry.io/otel/metric v1.42.0/go.mod h1:RlUN/7vTU7Ao/diDkEpQpnz3/92J9ko05BIwxYa2SSI=
go.opentelemetry.io/otel/sdk v1.42.0 h1:LyC8+jqk6UJwdrI/8VydAq/hvkFKNHZVIWuslJXYsDo=
go.opentelemetry.io/otel/sdk v1.42.0/go.mod h1:rGHCAxd9DAph0joO4W6OPwxjNTYWghRWmkHuGbayMts=
go.opentelemetry.io/otel/sdk/metric v1.42.0 h1:D/1QR46Clz6ajyZ3G8SgNlTJKBdGp84q9RKCAZ3YGuA=
go.opentelemetry.io/otel/sdk/metric v1.42.0/go.mod h1:Ua6AAlDKdZ7tdvaQKfSmnFTdHx37+J4ba8MwVCYM5hc=
go.opentelemetry.io/otel/trace v1.42.0 h1:OUCgIPt+mzOnaUTpOQcBiM/PLQ/Op7oq6g4LenLmOYY=
go.opentelemetry.io/otel/trace v1.42.0/go.mod h1:f3K9S+IFqnumBkKhRJMeaZeNk9epyhnCmQh/EysQCdc=
golang.org/x/net v0.51.0 h1:94R/GTO7mt3/4wIKpcR5gkGmRLOuE/2hNGeWq/GBIFo=
golang.org/x/net v0.51.0/go.mod h1:aamm+2QF5ogm02fjy5Bb7CQ0WMt1/WVM7FtyaTLlA9Y=
golang.org/x/sys v0.41.0 h1:Ivj+2Cp/ylzLiEU89QhWblYnOE9zerudt9Ftecq2C6k=
golang.org/x/sys v0.41.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
golang.org/x/text v0.34.0 h1:oL/Qq0Kdaqxa1KbNeMKwQq0reLCCaFtqu2eNuSeNHbk=
golang.org/x/text v0.34.0/go.mod h1:homfLqTYRFyVYemLBFl5GgL/DWEiH5wcsQ5gSh1yziA=
gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk=
gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=
google.golang.org/genproto/googleapis/api v0.0.0-20260209200024-4cfbd4190f57 h1:JLQynH/LBHfCTSbDWl+py8C+Rg/k1OVH3xfcaiANuF0=
google.golang.org/genproto/googleapis/api v0.0.0-20260209200024-4cfbd4190f57/go.mod h1:kSJwQxqmFXeo79zOmbrALdflXQeAYcUbgS7PbpMknCY=
google.golang.org/genproto/googleapis/rpc v0.0.0-20260209200024-4cfbd4190f57 h1:mWPCjDEyshlQYzBpMNHaEof6UX1PmHcaUODUywQ0uac=
google.golang.org/genproto/googleapis/rpc v0.0.0-20260209200024-4cfbd4190f57/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ=
google.golang.org/grpc v1.79.2 h1:fRMD94s2tITpyJGtBBn7MkMseNpOZU8ZxgC3MMBaXRU=
google.golang.org/grpc v1.79.2/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ=
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
================================================
FILE: api/membershippb/membership.pb.go
================================================
// Code generated by protoc-gen-gogo. DO NOT EDIT.
// source: membership.proto
package membershippb
import (
fmt "fmt"
io "io"
math "math"
math_bits "math/bits"
proto "github.com/golang/protobuf/proto"
_ "go.etcd.io/etcd/api/v3/versionpb"
)
// Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal
var _ = fmt.Errorf
var _ = math.Inf
// This is a compile-time assertion to ensure that this generated file
// is compatible with the proto package it is being compiled against.
// A compilation error at this line likely means your copy of the
// proto package needs to be updated.
const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
// RaftAttributes represents the raft related attributes of an etcd member.
type RaftAttributes struct {
// peerURLs is the list of peers in the raft cluster.
PeerUrls []string `protobuf:"bytes,1,rep,name=peer_urls,json=peerUrls,proto3" json:"peer_urls,omitempty"`
// isLearner indicates if the member is raft learner.
IsLearner bool `protobuf:"varint,2,opt,name=is_learner,json=isLearner,proto3" json:"is_learner,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *RaftAttributes) Reset() { *m = RaftAttributes{} }
func (m *RaftAttributes) String() string { return proto.CompactTextString(m) }
func (*RaftAttributes) ProtoMessage() {}
func (*RaftAttributes) Descriptor() ([]byte, []int) {
return fileDescriptor_949fe0d019050ef5, []int{0}
}
func (m *RaftAttributes) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
}
func (m *RaftAttributes) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
if deterministic {
return xxx_messageInfo_RaftAttributes.Marshal(b, m, deterministic)
} else {
b = b[:cap(b)]
n, err := m.MarshalToSizedBuffer(b)
if err != nil {
return nil, err
}
return b[:n], nil
}
}
func (m *RaftAttributes) XXX_Merge(src proto.Message) {
xxx_messageInfo_RaftAttributes.Merge(m, src)
}
func (m *RaftAttributes) XXX_Size() int {
return m.Size()
}
func (m *RaftAttributes) XXX_DiscardUnknown() {
xxx_messageInfo_RaftAttributes.DiscardUnknown(m)
}
var xxx_messageInfo_RaftAttributes proto.InternalMessageInfo
func (m *RaftAttributes) GetPeerUrls() []string {
if m != nil {
return m.PeerUrls
}
return nil
}
func (m *RaftAttributes) GetIsLearner() bool {
if m != nil {
return m.IsLearner
}
return false
}
// Attributes represents all the non-raft related attributes of an etcd member.
type Attributes struct {
Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
ClientUrls []string `protobuf:"bytes,2,rep,name=client_urls,json=clientUrls,proto3" json:"client_urls,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *Attributes) Reset() { *m = Attributes{} }
func (m *Attributes) String() string { return proto.CompactTextString(m) }
func (*Attributes) ProtoMessage() {}
func (*Attributes) Descriptor() ([]byte, []int) {
return fileDescriptor_949fe0d019050ef5, []int{1}
}
func (m *Attributes) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
}
func (m *Attributes) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
if deterministic {
return xxx_messageInfo_Attributes.Marshal(b, m, deterministic)
} else {
b = b[:cap(b)]
n, err := m.MarshalToSizedBuffer(b)
if err != nil {
return nil, err
}
return b[:n], nil
}
}
func (m *Attributes) XXX_Merge(src proto.Message) {
xxx_messageInfo_Attributes.Merge(m, src)
}
func (m *Attributes) XXX_Size() int {
return m.Size()
}
func (m *Attributes) XXX_DiscardUnknown() {
xxx_messageInfo_Attributes.DiscardUnknown(m)
}
var xxx_messageInfo_Attributes proto.InternalMessageInfo
func (m *Attributes) GetName() string {
if m != nil {
return m.Name
}
return ""
}
func (m *Attributes) GetClientUrls() []string {
if m != nil {
return m.ClientUrls
}
return nil
}
type Member struct {
ID uint64 `protobuf:"varint,1,opt,name=ID,proto3" json:"ID,omitempty"`
RaftAttributes *RaftAttributes `protobuf:"bytes,2,opt,name=raft_attributes,json=raftAttributes,proto3" json:"raft_attributes,omitempty"`
MemberAttributes *Attributes `protobuf:"bytes,3,opt,name=member_attributes,json=memberAttributes,proto3" json:"member_attributes,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *Member) Reset() { *m = Member{} }
func (m *Member) String() string { return proto.CompactTextString(m) }
func (*Member) ProtoMessage() {}
func (*Member) Descriptor() ([]byte, []int) {
return fileDescriptor_949fe0d019050ef5, []int{2}
}
func (m *Member) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
}
func (m *Member) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
if deterministic {
return xxx_messageInfo_Member.Marshal(b, m, deterministic)
} else {
b = b[:cap(b)]
n, err := m.MarshalToSizedBuffer(b)
if err != nil {
return nil, err
}
return b[:n], nil
}
}
func (m *Member) XXX_Merge(src proto.Message) {
xxx_messageInfo_Member.Merge(m, src)
}
func (m *Member) XXX_Size() int {
return m.Size()
}
func (m *Member) XXX_DiscardUnknown() {
xxx_messageInfo_Member.DiscardUnknown(m)
}
var xxx_messageInfo_Member proto.InternalMessageInfo
func (m *Member) GetID() uint64 {
if m != nil {
return m.ID
}
return 0
}
func (m *Member) GetRaftAttributes() *RaftAttributes {
if m != nil {
return m.RaftAttributes
}
return nil
}
func (m *Member) GetMemberAttributes() *Attributes {
if m != nil {
return m.MemberAttributes
}
return nil
}
type ClusterVersionSetRequest struct {
Ver string `protobuf:"bytes,1,opt,name=ver,proto3" json:"ver,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *ClusterVersionSetRequest) Reset() { *m = ClusterVersionSetRequest{} }
func (m *ClusterVersionSetRequest) String() string { return proto.CompactTextString(m) }
func (*ClusterVersionSetRequest) ProtoMessage() {}
func (*ClusterVersionSetRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_949fe0d019050ef5, []int{3}
}
func (m *ClusterVersionSetRequest) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
}
func (m *ClusterVersionSetRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
if deterministic {
return xxx_messageInfo_ClusterVersionSetRequest.Marshal(b, m, deterministic)
} else {
b = b[:cap(b)]
n, err := m.MarshalToSizedBuffer(b)
if err != nil {
return nil, err
}
return b[:n], nil
}
}
func (m *ClusterVersionSetRequest) XXX_Merge(src proto.Message) {
xxx_messageInfo_ClusterVersionSetRequest.Merge(m, src)
}
func (m *ClusterVersionSetRequest) XXX_Size() int {
return m.Size()
}
func (m *ClusterVersionSetRequest) XXX_DiscardUnknown() {
xxx_messageInfo_ClusterVersionSetRequest.DiscardUnknown(m)
}
var xxx_messageInfo_ClusterVersionSetRequest proto.InternalMessageInfo
func (m *ClusterVersionSetRequest) GetVer() string {
if m != nil {
return m.Ver
}
return ""
}
type ClusterMemberAttrSetRequest struct {
Member_ID uint64 `protobuf:"varint,1,opt,name=member_ID,json=memberID,proto3" json:"member_ID,omitempty"`
MemberAttributes *Attributes `protobuf:"bytes,2,opt,name=member_attributes,json=memberAttributes,proto3" json:"member_attributes,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *ClusterMemberAttrSetRequest) Reset() { *m = ClusterMemberAttrSetRequest{} }
func (m *ClusterMemberAttrSetRequest) String() string { return proto.CompactTextString(m) }
func (*ClusterMemberAttrSetRequest) ProtoMessage() {}
func (*ClusterMemberAttrSetRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_949fe0d019050ef5, []int{4}
}
func (m *ClusterMemberAttrSetRequest) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
}
func (m *ClusterMemberAttrSetRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
if deterministic {
return xxx_messageInfo_ClusterMemberAttrSetRequest.Marshal(b, m, deterministic)
} else {
b = b[:cap(b)]
n, err := m.MarshalToSizedBuffer(b)
if err != nil {
return nil, err
}
return b[:n], nil
}
}
func (m *ClusterMemberAttrSetRequest) XXX_Merge(src proto.Message) {
xxx_messageInfo_ClusterMemberAttrSetRequest.Merge(m, src)
}
func (m *ClusterMemberAttrSetRequest) XXX_Size() int {
return m.Size()
}
func (m *ClusterMemberAttrSetRequest) XXX_DiscardUnknown() {
xxx_messageInfo_ClusterMemberAttrSetRequest.DiscardUnknown(m)
}
var xxx_messageInfo_ClusterMemberAttrSetRequest proto.InternalMessageInfo
func (m *ClusterMemberAttrSetRequest) GetMember_ID() uint64 {
if m != nil {
return m.Member_ID
}
return 0
}
func (m *ClusterMemberAttrSetRequest) GetMemberAttributes() *Attributes {
if m != nil {
return m.MemberAttributes
}
return nil
}
type DowngradeInfoSetRequest struct {
Enabled bool `protobuf:"varint,1,opt,name=enabled,proto3" json:"enabled,omitempty"`
Ver string `protobuf:"bytes,2,opt,name=ver,proto3" json:"ver,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *DowngradeInfoSetRequest) Reset() { *m = DowngradeInfoSetRequest{} }
func (m *DowngradeInfoSetRequest) String() string { return proto.CompactTextString(m) }
func (*DowngradeInfoSetRequest) ProtoMessage() {}
func (*DowngradeInfoSetRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_949fe0d019050ef5, []int{5}
}
func (m *DowngradeInfoSetRequest) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
}
func (m *DowngradeInfoSetRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
if deterministic {
return xxx_messageInfo_DowngradeInfoSetRequest.Marshal(b, m, deterministic)
} else {
b = b[:cap(b)]
n, err := m.MarshalToSizedBuffer(b)
if err != nil {
return nil, err
}
return b[:n], nil
}
}
func (m *DowngradeInfoSetRequest) XXX_Merge(src proto.Message) {
xxx_messageInfo_DowngradeInfoSetRequest.Merge(m, src)
}
func (m *DowngradeInfoSetRequest) XXX_Size() int {
return m.Size()
}
func (m *DowngradeInfoSetRequest) XXX_DiscardUnknown() {
xxx_messageInfo_DowngradeInfoSetRequest.DiscardUnknown(m)
}
var xxx_messageInfo_DowngradeInfoSetRequest proto.InternalMessageInfo
func (m *DowngradeInfoSetRequest) GetEnabled() bool {
if m != nil {
return m.Enabled
}
return false
}
func (m *DowngradeInfoSetRequest) GetVer() string {
if m != nil {
return m.Ver
}
return ""
}
func init() {
proto.RegisterType((*RaftAttributes)(nil), "membershippb.RaftAttributes")
proto.RegisterType((*Attributes)(nil), "membershippb.Attributes")
proto.RegisterType((*Member)(nil), "membershippb.Member")
proto.RegisterType((*ClusterVersionSetRequest)(nil), "membershippb.ClusterVersionSetRequest")
proto.RegisterType((*ClusterMemberAttrSetRequest)(nil), "membershippb.ClusterMemberAttrSetRequest")
proto.RegisterType((*DowngradeInfoSetRequest)(nil), "membershippb.DowngradeInfoSetRequest")
}
func init() { proto.RegisterFile("membership.proto", fileDescriptor_949fe0d019050ef5) }
var fileDescriptor_949fe0d019050ef5 = []byte{
// 401 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x52, 0x3f, 0xcf, 0xd2, 0x40,
0x18, 0xf7, 0xda, 0x37, 0xd0, 0x3e, 0x18, 0xc4, 0x5b, 0x6c, 0x44, 0x6b, 0x83, 0x0b, 0x53, 0x9b,
0x48, 0x58, 0xdc, 0x54, 0x18, 0x30, 0xe2, 0x70, 0x06, 0x07, 0x17, 0x72, 0x85, 0x07, 0xbc, 0xa4,
0xb4, 0xf5, 0xee, 0x8a, 0xbb, 0xa3, 0x9f, 0xc0, 0x6f, 0xe1, 0xe4, 0x77, 0x70, 0xf4, 0x23, 0x18,
0xfc, 0x22, 0xa6, 0xd7, 0x42, 0x4b, 0x74, 0x7a, 0xb7, 0xe7, 0x7e, 0xb9, 0xe7, 0xf7, 0x2f, 0x0f,
0x0c, 0x0e, 0x78, 0x88, 0x51, 0xaa, 0x8f, 0x22, 0x0f, 0x73, 0x99, 0xe9, 0x8c, 0xde, 0x6d, 0x90,
0x3c, 0x7e, 0x18, 0xa0, 0xde, 0x6c, 0x23, 0x9e, 0x8b, 0xe8, 0x88, 0x52, 0x89, 0x2c, 0xcd, 0xe3,
0xf3, 0x54, 0xfd, 0x1f, 0xad, 0xa0, 0xcf, 0xf8, 0x4e, 0xbf, 0xd0, 0x5a, 0x8a, 0xb8, 0xd0, 0xa8,
0xe8, 0x10, 0xdc, 0x1c, 0x51, 0xae, 0x0b, 0x99, 0x28, 0x8f, 0x04, 0xf6, 0xd8, 0x65, 0x4e, 0x09,
0xac, 0x64, 0xa2, 0xe8, 0x63, 0x00, 0xa1, 0xd6, 0x09, 0x72, 0x99, 0xa2, 0xf4, 0xac, 0x80, 0x8c,
0x1d, 0xe6, 0x0a, 0xf5, 0xa6, 0x02, 0x9e, 0x77, 0xbf, 0xfc, 0xf0, 0xec, 0x49, 0x38, 0x1d, 0xbd,
0x06, 0x68, 0x51, 0x52, 0xb8, 0x49, 0xf9, 0x01, 0x3d, 0x12, 0x90, 0xb1, 0xcb, 0xcc, 0x4c, 0x9f,
0x40, 0x6f, 0x93, 0x08, 0x4c, 0x75, 0x25, 0x64, 0x19, 0x21, 0xa8, 0xa0, 0x52, 0xaa, 0xe1, 0xfa,
0x4e, 0xa0, 0xb3, 0x34, 0xa9, 0x68, 0x1f, 0xac, 0xc5, 0xcc, 0xd0, 0xdc, 0x30, 0x6b, 0x31, 0xa3,
0x73, 0xb8, 0x27, 0xf9, 0x4e, 0xaf, 0xf9, 0x45, 0xcb, 0x78, 0xea, 0x3d, 0x7b, 0x14, 0xb6, 0x7b,
0x08, 0xaf, 0x23, 0xb2, 0xbe, 0xbc, 0x8e, 0x3c, 0x87, 0xfb, 0xd5, 0xf7, 0x36, 0x91, 0x6d, 0x88,
0xbc, 0x6b, 0xa2, 0x16, 0x49, 0xdd, 0x7d, 0x83, 0x34, 0x8e, 0xa7, 0xe0, 0xbd, 0x4a, 0x0a, 0xa5,
0x51, 0xbe, 0xaf, 0xca, 0x7e, 0x87, 0x9a, 0xe1, 0xa7, 0x02, 0x95, 0xa6, 0x03, 0xb0, 0x8f, 0x28,
0xeb, 0x2a, 0xca, 0xb1, 0x59, 0xfb, 0x4a, 0x60, 0x58, 0xef, 0x2d, 0x2f, 0xdc, 0xad, 0xd5, 0x21,
0xb8, 0xb5, 0xcd, 0x4b, 0x09, 0x4e, 0x05, 0x98, 0x2a, 0xfe, 0x93, 0xc1, 0xba, 0x7d, 0x86, 0xb7,
0xf0, 0x60, 0x96, 0x7d, 0x4e, 0xf7, 0x92, 0x6f, 0x71, 0x91, 0xee, 0xb2, 0x96, 0x0f, 0x0f, 0xba,
0x98, 0xf2, 0x38, 0xc1, 0xad, 0x71, 0xe1, 0xb0, 0xf3, 0xf3, 0x1c, 0xce, 0xfa, 0x37, 0xdc, 0xcb,
0xe9, 0xcf, 0x93, 0x4f, 0x7e, 0x9d, 0x7c, 0xf2, 0xfb, 0xe4, 0x93, 0x6f, 0x7f, 0xfc, 0x3b, 0x1f,
0x9e, 0xee, 0xb3, 0xb0, 0xbc, 0xcf, 0x50, 0x64, 0x51, 0x73, 0xa7, 0x93, 0xa8, 0x6d, 0x36, 0xee,
0x98, 0x33, 0x9d, 0xfc, 0x0d, 0x00, 0x00, 0xff, 0xff, 0x15, 0x23, 0xc3, 0x3f, 0xea, 0x02, 0x00,
0x00,
}
func (m *RaftAttributes) Marshal() (dAtA []byte, err error) {
size := m.Size()
dAtA = make([]byte, size)
n, err := m.MarshalToSizedBuffer(dAtA[:size])
if err != nil {
return nil, err
}
return dAtA[:n], nil
}
func (m *RaftAttributes) MarshalTo(dAtA []byte) (int, error) {
size := m.Size()
return m.MarshalToSizedBuffer(dAtA[:size])
}
func (m *RaftAttributes) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA)
_ = i
var l int
_ = l
if m.XXX_unrecognized != nil {
i -= len(m.XXX_unrecognized)
copy(dAtA[i:], m.XXX_unrecognized)
}
if m.IsLearner {
i--
if m.IsLearner {
dAtA[i] = 1
} else {
dAtA[i] = 0
}
i--
dAtA[i] = 0x10
}
if len(m.PeerUrls) > 0 {
for iNdEx := len(m.PeerUrls) - 1; iNdEx >= 0; iNdEx-- {
i -= len(m.PeerUrls[iNdEx])
copy(dAtA[i:], m.PeerUrls[iNdEx])
i = encodeVarintMembership(dAtA, i, uint64(len(m.PeerUrls[iNdEx])))
i--
dAtA[i] = 0xa
}
}
return len(dAtA) - i, nil
}
func (m *Attributes) Marshal() (dAtA []byte, err error) {
size := m.Size()
dAtA = make([]byte, size)
n, err := m.MarshalToSizedBuffer(dAtA[:size])
if err != nil {
return nil, err
}
return dAtA[:n], nil
}
func (m *Attributes) MarshalTo(dAtA []byte) (int, error) {
size := m.Size()
return m.MarshalToSizedBuffer(dAtA[:size])
}
func (m *Attributes) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA)
_ = i
var l int
_ = l
if m.XXX_unrecognized != nil {
i -= len(m.XXX_unrecognized)
copy(dAtA[i:], m.XXX_unrecognized)
}
if len(m.ClientUrls) > 0 {
for iNdEx := len(m.ClientUrls) - 1; iNdEx >= 0; iNdEx-- {
i -= len(m.ClientUrls[iNdEx])
copy(dAtA[i:], m.ClientUrls[iNdEx])
i = encodeVarintMembership(dAtA, i, uint64(len(m.ClientUrls[iNdEx])))
i--
dAtA[i] = 0x12
}
}
if len(m.Name) > 0 {
i -= len(m.Name)
copy(dAtA[i:], m.Name)
i = encodeVarintMembership(dAtA, i, uint64(len(m.Name)))
i--
dAtA[i] = 0xa
}
return len(dAtA) - i, nil
}
func (m *Member) Marshal() (dAtA []byte, err error) {
size := m.Size()
dAtA = make([]byte, size)
n, err := m.MarshalToSizedBuffer(dAtA[:size])
if err != nil {
return nil, err
}
return dAtA[:n], nil
}
func (m *Member) MarshalTo(dAtA []byte) (int, error) {
size := m.Size()
return m.MarshalToSizedBuffer(dAtA[:size])
}
func (m *Member) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA)
_ = i
var l int
_ = l
if m.XXX_unrecognized != nil {
i -= len(m.XXX_unrecognized)
copy(dAtA[i:], m.XXX_unrecognized)
}
if m.MemberAttributes != nil {
{
size, err := m.MemberAttributes.MarshalToSizedBuffer(dAtA[:i])
if err != nil {
return 0, err
}
i -= size
i = encodeVarintMembership(dAtA, i, uint64(size))
}
i--
dAtA[i] = 0x1a
}
if m.RaftAttributes != nil {
{
size, err := m.RaftAttributes.MarshalToSizedBuffer(dAtA[:i])
if err != nil {
return 0, err
}
i -= size
i = encodeVarintMembership(dAtA, i, uint64(size))
}
i--
dAtA[i] = 0x12
}
if m.ID != 0 {
i = encodeVarintMembership(dAtA, i, uint64(m.ID))
i--
dAtA[i] = 0x8
}
return len(dAtA) - i, nil
}
func (m *ClusterVersionSetRequest) Marshal() (dAtA []byte, err error) {
size := m.Size()
dAtA = make([]byte, size)
n, err := m.MarshalToSizedBuffer(dAtA[:size])
if err != nil {
return nil, err
}
return dAtA[:n], nil
}
func (m *ClusterVersionSetRequest) MarshalTo(dAtA []byte) (int, error) {
size := m.Size()
return m.MarshalToSizedBuffer(dAtA[:size])
}
func (m *ClusterVersionSetRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA)
_ = i
var l int
_ = l
if m.XXX_unrecognized != nil {
i -= len(m.XXX_unrecognized)
copy(dAtA[i:], m.XXX_unrecognized)
}
if len(m.Ver) > 0 {
i -= len(m.Ver)
copy(dAtA[i:], m.Ver)
i = encodeVarintMembership(dAtA, i, uint64(len(m.Ver)))
i--
dAtA[i] = 0xa
}
return len(dAtA) - i, nil
}
func (m *ClusterMemberAttrSetRequest) Marshal() (dAtA []byte, err error) {
size := m.Size()
dAtA = make([]byte, size)
n, err := m.MarshalToSizedBuffer(dAtA[:size])
if err != nil {
return nil, err
}
return dAtA[:n], nil
}
func (m *ClusterMemberAttrSetRequest) MarshalTo(dAtA []byte) (int, error) {
size := m.Size()
return m.MarshalToSizedBuffer(dAtA[:size])
}
func (m *ClusterMemberAttrSetRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA)
_ = i
var l int
_ = l
if m.XXX_unrecognized != nil {
i -= len(m.XXX_unrecognized)
copy(dAtA[i:], m.XXX_unrecognized)
}
if m.MemberAttributes != nil {
{
size, err := m.MemberAttributes.MarshalToSizedBuffer(dAtA[:i])
if err != nil {
return 0, err
}
i -= size
i = encodeVarintMembership(dAtA, i, uint64(size))
}
i--
dAtA[i] = 0x12
}
if m.Member_ID != 0 {
i = encodeVarintMembership(dAtA, i, uint64(m.Member_ID))
i--
dAtA[i] = 0x8
}
return len(dAtA) - i, nil
}
func (m *DowngradeInfoSetRequest) Marshal() (dAtA []byte, err error) {
size := m.Size()
dAtA = make([]byte, size)
n, err := m.MarshalToSizedBuffer(dAtA[:size])
if err != nil {
return nil, err
}
return dAtA[:n], nil
}
func (m *DowngradeInfoSetRequest) MarshalTo(dAtA []byte) (int, error) {
size := m.Size()
return m.MarshalToSizedBuffer(dAtA[:size])
}
func (m *DowngradeInfoSetRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA)
_ = i
var l int
_ = l
if m.XXX_unrecognized != nil {
i -= len(m.XXX_unrecognized)
copy(dAtA[i:], m.XXX_unrecognized)
}
if len(m.Ver) > 0 {
i -= len(m.Ver)
copy(dAtA[i:], m.Ver)
i = encodeVarintMembership(dAtA, i, uint64(len(m.Ver)))
i--
dAtA[i] = 0x12
}
if m.Enabled {
i--
if m.Enabled {
dAtA[i] = 1
} else {
dAtA[i] = 0
}
i--
dAtA[i] = 0x8
}
return len(dAtA) - i, nil
}
func encodeVarintMembership(dAtA []byte, offset int, v uint64) int {
offset -= sovMembership(v)
base := offset
for v >= 1<<7 {
dAtA[offset] = uint8(v&0x7f | 0x80)
v >>= 7
offset++
}
dAtA[offset] = uint8(v)
return base
}
func (m *RaftAttributes) Size() (n int) {
if m == nil {
return 0
}
var l int
_ = l
if len(m.PeerUrls) > 0 {
for _, s := range m.PeerUrls {
l = len(s)
n += 1 + l + sovMembership(uint64(l))
}
}
if m.IsLearner {
n += 2
}
if m.XXX_unrecognized != nil {
n += len(m.XXX_unrecognized)
}
return n
}
func (m *Attributes) Size() (n int) {
if m == nil {
return 0
}
var l int
_ = l
l = len(m.Name)
if l > 0 {
n += 1 + l + sovMembership(uint64(l))
}
if len(m.ClientUrls) > 0 {
for _, s := range m.ClientUrls {
l = len(s)
n += 1 + l + sovMembership(uint64(l))
}
}
if m.XXX_unrecognized != nil {
n += len(m.XXX_unrecognized)
}
return n
}
func (m *Member) Size() (n int) {
if m == nil {
return 0
}
var l int
_ = l
if m.ID != 0 {
n += 1 + sovMembership(uint64(m.ID))
}
if m.RaftAttributes != nil {
l = m.RaftAttributes.Size()
n += 1 + l + sovMembership(uint64(l))
}
if m.MemberAttributes != nil {
l = m.MemberAttributes.Size()
n += 1 + l + sovMembership(uint64(l))
}
if m.XXX_unrecognized != nil {
n += len(m.XXX_unrecognized)
}
return n
}
func (m *ClusterVersionSetRequest) Size() (n int) {
if m == nil {
return 0
}
var l int
_ = l
l = len(m.Ver)
if l > 0 {
n += 1 + l + sovMembership(uint64(l))
}
if m.XXX_unrecognized != nil {
n += len(m.XXX_unrecognized)
}
return n
}
func (m *ClusterMemberAttrSetRequest) Size() (n int) {
if m == nil {
return 0
}
var l int
_ = l
if m.Member_ID != 0 {
n += 1 + sovMembership(uint64(m.Member_ID))
}
if m.MemberAttributes != nil {
l = m.MemberAttributes.Size()
n += 1 + l + sovMembership(uint64(l))
}
if m.XXX_unrecognized != nil {
n += len(m.XXX_unrecognized)
}
return n
}
func (m *DowngradeInfoSetRequest) Size() (n int) {
if m == nil {
return 0
}
var l int
_ = l
if m.Enabled {
n += 2
}
l = len(m.Ver)
if l > 0 {
n += 1 + l + sovMembership(uint64(l))
}
if m.XXX_unrecognized != nil {
n += len(m.XXX_unrecognized)
}
return n
}
func sovMembership(x uint64) (n int) {
return (math_bits.Len64(x|1) + 6) / 7
}
func sozMembership(x uint64) (n int) {
return sovMembership(uint64((x << 1) ^ uint64((int64(x) >> 63))))
}
func (m *RaftAttributes) Unmarshal(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0
for iNdEx < l {
preIndex := iNdEx
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowMembership
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
fieldNum := int32(wire >> 3)
wireType := int(wire & 0x7)
if wireType == 4 {
return fmt.Errorf("proto: RaftAttributes: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: RaftAttributes: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
case 1:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field PeerUrls", wireType)
}
var stringLen uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowMembership
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
stringLen |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
intStringLen := int(stringLen)
if intStringLen < 0 {
return ErrInvalidLengthMembership
}
postIndex := iNdEx + intStringLen
if postIndex < 0 {
return ErrInvalidLengthMembership
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.PeerUrls = append(m.PeerUrls, string(dAtA[iNdEx:postIndex]))
iNdEx = postIndex
case 2:
if wireType != 0 {
return fmt.Errorf("proto: wrong wireType = %d for field IsLearner", wireType)
}
var v int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowMembership
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
v |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
m.IsLearner = bool(v != 0)
default:
iNdEx = preIndex
skippy, err := skipMembership(dAtA[iNdEx:])
if err != nil {
return err
}
if (skippy < 0) || (iNdEx+skippy) < 0 {
return ErrInvalidLengthMembership
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
iNdEx += skippy
}
}
if iNdEx > l {
return io.ErrUnexpectedEOF
}
return nil
}
func (m *Attributes) Unmarshal(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0
for iNdEx < l {
preIndex := iNdEx
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowMembership
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
fieldNum := int32(wire >> 3)
wireType := int(wire & 0x7)
if wireType == 4 {
return fmt.Errorf("proto: Attributes: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: Attributes: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
case 1:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType)
}
var stringLen uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowMembership
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
stringLen |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
intStringLen := int(stringLen)
if intStringLen < 0 {
return ErrInvalidLengthMembership
}
postIndex := iNdEx + intStringLen
if postIndex < 0 {
return ErrInvalidLengthMembership
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.Name = string(dAtA[iNdEx:postIndex])
iNdEx = postIndex
case 2:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field ClientUrls", wireType)
}
var stringLen uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowMembership
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
stringLen |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
intStringLen := int(stringLen)
if intStringLen < 0 {
return ErrInvalidLengthMembership
}
postIndex := iNdEx + intStringLen
if postIndex < 0 {
return ErrInvalidLengthMembership
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.ClientUrls = append(m.ClientUrls, string(dAtA[iNdEx:postIndex]))
iNdEx = postIndex
default:
iNdEx = preIndex
skippy, err := skipMembership(dAtA[iNdEx:])
if err != nil {
return err
}
if (skippy < 0) || (iNdEx+skippy) < 0 {
return ErrInvalidLengthMembership
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
iNdEx += skippy
}
}
if iNdEx > l {
return io.ErrUnexpectedEOF
}
return nil
}
func (m *Member) Unmarshal(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0
for iNdEx < l {
preIndex := iNdEx
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowMembership
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
fieldNum := int32(wire >> 3)
wireType := int(wire & 0x7)
if wireType == 4 {
return fmt.Errorf("proto: Member: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: Member: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
case 1:
if wireType != 0 {
return fmt.Errorf("proto: wrong wireType = %d for field ID", wireType)
}
m.ID = 0
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowMembership
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
m.ID |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
case 2:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field RaftAttributes", wireType)
}
var msglen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowMembership
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
msglen |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
if msglen < 0 {
return ErrInvalidLengthMembership
}
postIndex := iNdEx + msglen
if postIndex < 0 {
return ErrInvalidLengthMembership
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
if m.RaftAttributes == nil {
m.RaftAttributes = &RaftAttributes{}
}
if err := m.RaftAttributes.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
return err
}
iNdEx = postIndex
case 3:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field MemberAttributes", wireType)
}
var msglen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowMembership
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
msglen |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
if msglen < 0 {
return ErrInvalidLengthMembership
}
postIndex := iNdEx + msglen
if postIndex < 0 {
return ErrInvalidLengthMembership
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
if m.MemberAttributes == nil {
m.MemberAttributes = &Attributes{}
}
if err := m.MemberAttributes.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
return err
}
iNdEx = postIndex
default:
iNdEx = preIndex
skippy, err := skipMembership(dAtA[iNdEx:])
if err != nil {
return err
}
if (skippy < 0) || (iNdEx+skippy) < 0 {
return ErrInvalidLengthMembership
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
iNdEx += skippy
}
}
if iNdEx > l {
return io.ErrUnexpectedEOF
}
return nil
}
func (m *ClusterVersionSetRequest) Unmarshal(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0
for iNdEx < l {
preIndex := iNdEx
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowMembership
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
fieldNum := int32(wire >> 3)
wireType := int(wire & 0x7)
if wireType == 4 {
return fmt.Errorf("proto: ClusterVersionSetRequest: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: ClusterVersionSetRequest: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
case 1:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Ver", wireType)
}
var stringLen uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowMembership
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
stringLen |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
intStringLen := int(stringLen)
if intStringLen < 0 {
return ErrInvalidLengthMembership
}
postIndex := iNdEx + intStringLen
if postIndex < 0 {
return ErrInvalidLengthMembership
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.Ver = string(dAtA[iNdEx:postIndex])
iNdEx = postIndex
default:
iNdEx = preIndex
skippy, err := skipMembership(dAtA[iNdEx:])
if err != nil {
return err
}
if (skippy < 0) || (iNdEx+skippy) < 0 {
return ErrInvalidLengthMembership
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
iNdEx += skippy
}
}
if iNdEx > l {
return io.ErrUnexpectedEOF
}
return nil
}
func (m *ClusterMemberAttrSetRequest) Unmarshal(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0
for iNdEx < l {
preIndex := iNdEx
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowMembership
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
fieldNum := int32(wire >> 3)
wireType := int(wire & 0x7)
if wireType == 4 {
return fmt.Errorf("proto: ClusterMemberAttrSetRequest: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: ClusterMemberAttrSetRequest: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
case 1:
if wireType != 0 {
return fmt.Errorf("proto: wrong wireType = %d for field Member_ID", wireType)
}
m.Member_ID = 0
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowMembership
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
m.Member_ID |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
case 2:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field MemberAttributes", wireType)
}
var msglen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowMembership
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
msglen |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
if msglen < 0 {
return ErrInvalidLengthMembership
}
postIndex := iNdEx + msglen
if postIndex < 0 {
return ErrInvalidLengthMembership
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
if m.MemberAttributes == nil {
m.MemberAttributes = &Attributes{}
}
if err := m.MemberAttributes.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
return err
}
iNdEx = postIndex
default:
iNdEx = preIndex
skippy, err := skipMembership(dAtA[iNdEx:])
if err != nil {
return err
}
if (skippy < 0) || (iNdEx+skippy) < 0 {
return ErrInvalidLengthMembership
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
iNdEx += skippy
}
}
if iNdEx > l {
return io.ErrUnexpectedEOF
}
return nil
}
func (m *DowngradeInfoSetRequest) Unmarshal(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0
for iNdEx < l {
preIndex := iNdEx
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowMembership
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
fieldNum := int32(wire >> 3)
wireType := int(wire & 0x7)
if wireType == 4 {
return fmt.Errorf("proto: DowngradeInfoSetRequest: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: DowngradeInfoSetRequest: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
case 1:
if wireType != 0 {
return fmt.Errorf("proto: wrong wireType = %d for field Enabled", wireType)
}
var v int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowMembership
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
v |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
m.Enabled = bool(v != 0)
case 2:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Ver", wireType)
}
var stringLen uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowMembership
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
stringLen |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
intStringLen := int(stringLen)
if intStringLen < 0 {
return ErrInvalidLengthMembership
}
postIndex := iNdEx + intStringLen
if postIndex < 0 {
return ErrInvalidLengthMembership
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.Ver = string(dAtA[iNdEx:postIndex])
iNdEx = postIndex
default:
iNdEx = preIndex
skippy, err := skipMembership(dAtA[iNdEx:])
if err != nil {
return err
}
if (skippy < 0) || (iNdEx+skippy) < 0 {
return ErrInvalidLengthMembership
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
iNdEx += skippy
}
}
if iNdEx > l {
return io.ErrUnexpectedEOF
}
return nil
}
func skipMembership(dAtA []byte) (n int, err error) {
l := len(dAtA)
iNdEx := 0
depth := 0
for iNdEx < l {
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return 0, ErrIntOverflowMembership
}
if iNdEx >= l {
return 0, io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= (uint64(b) & 0x7F) << shift
if b < 0x80 {
break
}
}
wireType := int(wire & 0x7)
switch wireType {
case 0:
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return 0, ErrIntOverflowMembership
}
if iNdEx >= l {
return 0, io.ErrUnexpectedEOF
}
iNdEx++
if dAtA[iNdEx-1] < 0x80 {
break
}
}
case 1:
iNdEx += 8
case 2:
var length int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return 0, ErrIntOverflowMembership
}
if iNdEx >= l {
return 0, io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
length |= (int(b) & 0x7F) << shift
if b < 0x80 {
break
}
}
if length < 0 {
return 0, ErrInvalidLengthMembership
}
iNdEx += length
case 3:
depth++
case 4:
if depth == 0 {
return 0, ErrUnexpectedEndOfGroupMembership
}
depth--
case 5:
iNdEx += 4
default:
return 0, fmt.Errorf("proto: illegal wireType %d", wireType)
}
if iNdEx < 0 {
return 0, ErrInvalidLengthMembership
}
if depth == 0 {
return iNdEx, nil
}
}
return 0, io.ErrUnexpectedEOF
}
var (
ErrInvalidLengthMembership = fmt.Errorf("proto: negative length found during unmarshaling")
ErrIntOverflowMembership = fmt.Errorf("proto: integer overflow")
ErrUnexpectedEndOfGroupMembership = fmt.Errorf("proto: unexpected end of group")
)
================================================
FILE: api/membershippb/membership.proto
================================================
syntax = "proto3";
package membershippb;
import "etcd/api/versionpb/version.proto";
option go_package = "go.etcd.io/etcd/api/v3/membershippb";
// RaftAttributes represents the raft related attributes of an etcd member.
message RaftAttributes {
option (versionpb.etcd_version_msg) = "3.5";
// peerURLs is the list of peers in the raft cluster.
repeated string peer_urls = 1;
// isLearner indicates if the member is raft learner.
bool is_learner = 2;
}
// Attributes represents all the non-raft related attributes of an etcd member.
message Attributes {
option (versionpb.etcd_version_msg) = "3.5";
string name = 1;
repeated string client_urls = 2;
}
message Member {
option (versionpb.etcd_version_msg) = "3.5";
uint64 ID = 1;
RaftAttributes raft_attributes = 2;
Attributes member_attributes = 3;
}
message ClusterVersionSetRequest {
option (versionpb.etcd_version_msg) = "3.5";
string ver = 1;
}
message ClusterMemberAttrSetRequest {
option (versionpb.etcd_version_msg) = "3.5";
uint64 member_ID = 1;
Attributes member_attributes = 2;
}
message DowngradeInfoSetRequest {
option (versionpb.etcd_version_msg) = "3.5";
bool enabled = 1;
string ver = 2;
}
================================================
FILE: api/mvccpb/deprecated.go
================================================
// Copyright 2026 The etcd Authors
//
// 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 mvccpb
const (
// PUT is an alias of Event_PUT
// Deprecated: use Event_PUT instead. Will be removed in v3.8.
PUT = Event_PUT
// DELETE is an alias of Permission_WRITE
// Deprecated: use Event_DELETE instead. Will be removed in v3.8.
DELETE = Event_DELETE
)
================================================
FILE: api/mvccpb/kv.pb.go
================================================
// Code generated by protoc-gen-gogo. DO NOT EDIT.
// source: kv.proto
package mvccpb
import (
fmt "fmt"
io "io"
math "math"
math_bits "math/bits"
proto "github.com/golang/protobuf/proto"
)
// Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal
var _ = fmt.Errorf
var _ = math.Inf
// This is a compile-time assertion to ensure that this generated file
// is compatible with the proto package it is being compiled against.
// A compilation error at this line likely means your copy of the
// proto package needs to be updated.
const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
type Event_EventType int32
const (
Event_PUT Event_EventType = 0
Event_DELETE Event_EventType = 1
)
var Event_EventType_name = map[int32]string{
0: "PUT",
1: "DELETE",
}
var Event_EventType_value = map[string]int32{
"PUT": 0,
"DELETE": 1,
}
func (x Event_EventType) String() string {
return proto.EnumName(Event_EventType_name, int32(x))
}
func (Event_EventType) EnumDescriptor() ([]byte, []int) {
return fileDescriptor_2216fe83c9c12408, []int{1, 0}
}
type KeyValue struct {
// key is the key in bytes. An empty key is not allowed.
Key []byte `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"`
// create_revision is the revision of last creation on this key.
CreateRevision int64 `protobuf:"varint,2,opt,name=create_revision,json=createRevision,proto3" json:"create_revision,omitempty"`
// mod_revision is the revision of last modification on this key.
ModRevision int64 `protobuf:"varint,3,opt,name=mod_revision,json=modRevision,proto3" json:"mod_revision,omitempty"`
// version is the version of the key. A deletion resets
// the version to zero and any modification of the key
// increases its version.
Version int64 `protobuf:"varint,4,opt,name=version,proto3" json:"version,omitempty"`
// value is the value held by the key, in bytes.
Value []byte `protobuf:"bytes,5,opt,name=value,proto3" json:"value,omitempty"`
// lease is the ID of the lease that attached to key.
// When the attached lease expires, the key will be deleted.
// If lease is 0, then no lease is attached to the key.
Lease int64 `protobuf:"varint,6,opt,name=lease,proto3" json:"lease,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *KeyValue) Reset() { *m = KeyValue{} }
func (m *KeyValue) String() string { return proto.CompactTextString(m) }
func (*KeyValue) ProtoMessage() {}
func (*KeyValue) Descriptor() ([]byte, []int) {
return fileDescriptor_2216fe83c9c12408, []int{0}
}
func (m *KeyValue) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
}
func (m *KeyValue) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
if deterministic {
return xxx_messageInfo_KeyValue.Marshal(b, m, deterministic)
} else {
b = b[:cap(b)]
n, err := m.MarshalToSizedBuffer(b)
if err != nil {
return nil, err
}
return b[:n], nil
}
}
func (m *KeyValue) XXX_Merge(src proto.Message) {
xxx_messageInfo_KeyValue.Merge(m, src)
}
func (m *KeyValue) XXX_Size() int {
return m.Size()
}
func (m *KeyValue) XXX_DiscardUnknown() {
xxx_messageInfo_KeyValue.DiscardUnknown(m)
}
var xxx_messageInfo_KeyValue proto.InternalMessageInfo
func (m *KeyValue) GetKey() []byte {
if m != nil {
return m.Key
}
return nil
}
func (m *KeyValue) GetCreateRevision() int64 {
if m != nil {
return m.CreateRevision
}
return 0
}
func (m *KeyValue) GetModRevision() int64 {
if m != nil {
return m.ModRevision
}
return 0
}
func (m *KeyValue) GetVersion() int64 {
if m != nil {
return m.Version
}
return 0
}
func (m *KeyValue) GetValue() []byte {
if m != nil {
return m.Value
}
return nil
}
func (m *KeyValue) GetLease() int64 {
if m != nil {
return m.Lease
}
return 0
}
type Event struct {
// type is the kind of event. If type is a PUT, it indicates
// new data has been stored to the key. If type is a DELETE,
// it indicates the key was deleted.
Type Event_EventType `protobuf:"varint,1,opt,name=type,proto3,enum=mvccpb.Event_EventType" json:"type,omitempty"`
// kv holds the KeyValue for the event.
// A PUT event contains current kv pair.
// A PUT event with kv.Version=1 indicates the creation of a key.
// A DELETE/EXPIRE event contains the deleted key with
// its modification revision set to the revision of deletion.
Kv *KeyValue `protobuf:"bytes,2,opt,name=kv,proto3" json:"kv,omitempty"`
// prev_kv holds the key-value pair before the event happens.
PrevKv *KeyValue `protobuf:"bytes,3,opt,name=prev_kv,json=prevKv,proto3" json:"prev_kv,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *Event) Reset() { *m = Event{} }
func (m *Event) String() string { return proto.CompactTextString(m) }
func (*Event) ProtoMessage() {}
func (*Event) Descriptor() ([]byte, []int) {
return fileDescriptor_2216fe83c9c12408, []int{1}
}
func (m *Event) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
}
func (m *Event) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
if deterministic {
return xxx_messageInfo_Event.Marshal(b, m, deterministic)
} else {
b = b[:cap(b)]
n, err := m.MarshalToSizedBuffer(b)
if err != nil {
return nil, err
}
return b[:n], nil
}
}
func (m *Event) XXX_Merge(src proto.Message) {
xxx_messageInfo_Event.Merge(m, src)
}
func (m *Event) XXX_Size() int {
return m.Size()
}
func (m *Event) XXX_DiscardUnknown() {
xxx_messageInfo_Event.DiscardUnknown(m)
}
var xxx_messageInfo_Event proto.InternalMessageInfo
func (m *Event) GetType() Event_EventType {
if m != nil {
return m.Type
}
return Event_PUT
}
func (m *Event) GetKv() *KeyValue {
if m != nil {
return m.Kv
}
return nil
}
func (m *Event) GetPrevKv() *KeyValue {
if m != nil {
return m.PrevKv
}
return nil
}
func init() {
proto.RegisterEnum("mvccpb.Event_EventType", Event_EventType_name, Event_EventType_value)
proto.RegisterType((*KeyValue)(nil), "mvccpb.KeyValue")
proto.RegisterType((*Event)(nil), "mvccpb.Event")
}
func init() { proto.RegisterFile("kv.proto", fileDescriptor_2216fe83c9c12408) }
var fileDescriptor_2216fe83c9c12408 = []byte{
// 308 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x6c, 0x91, 0xc1, 0x4e, 0xb3, 0x40,
0x14, 0x85, 0x3b, 0xa5, 0xa5, 0xfd, 0x6f, 0x9b, 0xfe, 0x64, 0x62, 0x22, 0x1b, 0x09, 0x76, 0x63,
0x8d, 0x09, 0x24, 0xed, 0x1b, 0x18, 0x59, 0xd5, 0x85, 0x21, 0xe8, 0xc2, 0x4d, 0x43, 0xe1, 0xc6,
0x10, 0x4a, 0x67, 0x42, 0xf1, 0x26, 0xbc, 0x89, 0x7b, 0xf7, 0x3e, 0x87, 0x4b, 0x1f, 0xc1, 0xe0,
0x8b, 0x18, 0x66, 0xa4, 0x6e, 0xdc, 0xc0, 0x9c, 0x73, 0xbe, 0xcc, 0x3d, 0x37, 0x03, 0xe3, 0x9c,
0x3c, 0x59, 0x8a, 0x4a, 0x70, 0xb3, 0xa0, 0x24, 0x91, 0xdb, 0xf9, 0x1b, 0x83, 0xf1, 0x1a, 0xeb,
0x87, 0x78, 0xf7, 0x8c, 0xdc, 0x02, 0x23, 0xc7, 0xda, 0x66, 0x2e, 0x5b, 0x4c, 0xc3, 0xf6, 0xc8,
0x2f, 0xe0, 0x7f, 0x52, 0x62, 0x5c, 0xe1, 0xa6, 0x44, 0xca, 0x0e, 0x99, 0xd8, 0xdb, 0x7d, 0x97,
0x2d, 0x8c, 0x70, 0xa6, 0xed, 0xf0, 0xc7, 0xe5, 0xe7, 0x30, 0x2d, 0x44, 0xfa, 0x4b, 0x19, 0x8a,
0x9a, 0x14, 0x22, 0x3d, 0x22, 0x36, 0x8c, 0x08, 0x4b, 0x95, 0x0e, 0x54, 0xda, 0x49, 0x7e, 0x02,
0x43, 0x6a, 0x0b, 0xd8, 0x43, 0x35, 0x59, 0x8b, 0xd6, 0xdd, 0x61, 0x7c, 0x40, 0xdb, 0x54, 0xb4,
0x16, 0xf3, 0x57, 0x06, 0xc3, 0x80, 0x70, 0x5f, 0xf1, 0x2b, 0x18, 0x54, 0xb5, 0x44, 0x55, 0x77,
0xb6, 0x3c, 0xf5, 0xf4, 0x46, 0x9e, 0x0a, 0xf5, 0x37, 0xaa, 0x25, 0x86, 0x0a, 0xe2, 0x2e, 0xf4,
0x73, 0x52, 0xdd, 0x27, 0x4b, 0xab, 0x43, 0xbb, 0xc5, 0xc3, 0x7e, 0x4e, 0xfc, 0x12, 0x46, 0xb2,
0x44, 0xda, 0xe4, 0xa4, 0xca, 0xff, 0x85, 0x99, 0x2d, 0xb0, 0xa6, 0xb9, 0x0b, 0xff, 0x8e, 0xf7,
0xf3, 0x11, 0x18, 0x77, 0xf7, 0x91, 0xd5, 0xe3, 0x00, 0xe6, 0x4d, 0x70, 0x1b, 0x44, 0x81, 0xc5,
0xae, 0xfd, 0xf7, 0xc6, 0x61, 0x1f, 0x8d, 0xc3, 0x3e, 0x1b, 0x87, 0xbd, 0x7c, 0x39, 0xbd, 0xc7,
0xb3, 0x27, 0xe1, 0x61, 0x95, 0xa4, 0x5e, 0x26, 0xfc, 0xf6, 0xef, 0xc7, 0x32, 0xf3, 0x69, 0xe5,
0xeb, 0x19, 0x5b, 0x53, 0x3d, 0xcb, 0xea, 0x3b, 0x00, 0x00, 0xff, 0xff, 0xcb, 0xc0, 0x08, 0x63,
0xa2, 0x01, 0x00, 0x00,
}
func (m *KeyValue) Marshal() (dAtA []byte, err error) {
size := m.Size()
dAtA = make([]byte, size)
n, err := m.MarshalToSizedBuffer(dAtA[:size])
if err != nil {
return nil, err
}
return dAtA[:n], nil
}
func (m *KeyValue) MarshalTo(dAtA []byte) (int, error) {
size := m.Size()
return m.MarshalToSizedBuffer(dAtA[:size])
}
func (m *KeyValue) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA)
_ = i
var l int
_ = l
if m.XXX_unrecognized != nil {
i -= len(m.XXX_unrecognized)
copy(dAtA[i:], m.XXX_unrecognized)
}
if m.Lease != 0 {
i = encodeVarintKv(dAtA, i, uint64(m.Lease))
i--
dAtA[i] = 0x30
}
if len(m.Value) > 0 {
i -= len(m.Value)
copy(dAtA[i:], m.Value)
i = encodeVarintKv(dAtA, i, uint64(len(m.Value)))
i--
dAtA[i] = 0x2a
}
if m.Version != 0 {
i = encodeVarintKv(dAtA, i, uint64(m.Version))
i--
dAtA[i] = 0x20
}
if m.ModRevision != 0 {
i = encodeVarintKv(dAtA, i, uint64(m.ModRevision))
i--
dAtA[i] = 0x18
}
if m.CreateRevision != 0 {
i = encodeVarintKv(dAtA, i, uint64(m.CreateRevision))
i--
dAtA[i] = 0x10
}
if len(m.Key) > 0 {
i -= len(m.Key)
copy(dAtA[i:], m.Key)
i = encodeVarintKv(dAtA, i, uint64(len(m.Key)))
i--
dAtA[i] = 0xa
}
return len(dAtA) - i, nil
}
func (m *Event) Marshal() (dAtA []byte, err error) {
size := m.Size()
dAtA = make([]byte, size)
n, err := m.MarshalToSizedBuffer(dAtA[:size])
if err != nil {
return nil, err
}
return dAtA[:n], nil
}
func (m *Event) MarshalTo(dAtA []byte) (int, error) {
size := m.Size()
return m.MarshalToSizedBuffer(dAtA[:size])
}
func (m *Event) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA)
_ = i
var l int
_ = l
if m.XXX_unrecognized != nil {
i -= len(m.XXX_unrecognized)
copy(dAtA[i:], m.XXX_unrecognized)
}
if m.PrevKv != nil {
{
size, err := m.PrevKv.MarshalToSizedBuffer(dAtA[:i])
if err != nil {
return 0, err
}
i -= size
i = encodeVarintKv(dAtA, i, uint64(size))
}
i--
dAtA[i] = 0x1a
}
if m.Kv != nil {
{
size, err := m.Kv.MarshalToSizedBuffer(dAtA[:i])
if err != nil {
return 0, err
}
i -= size
i = encodeVarintKv(dAtA, i, uint64(size))
}
i--
dAtA[i] = 0x12
}
if m.Type != 0 {
i = encodeVarintKv(dAtA, i, uint64(m.Type))
i--
dAtA[i] = 0x8
}
return len(dAtA) - i, nil
}
func encodeVarintKv(dAtA []byte, offset int, v uint64) int {
offset -= sovKv(v)
base := offset
for v >= 1<<7 {
dAtA[offset] = uint8(v&0x7f | 0x80)
v >>= 7
offset++
}
dAtA[offset] = uint8(v)
return base
}
func (m *KeyValue) Size() (n int) {
if m == nil {
return 0
}
var l int
_ = l
l = len(m.Key)
if l > 0 {
n += 1 + l + sovKv(uint64(l))
}
if m.CreateRevision != 0 {
n += 1 + sovKv(uint64(m.CreateRevision))
}
if m.ModRevision != 0 {
n += 1 + sovKv(uint64(m.ModRevision))
}
if m.Version != 0 {
n += 1 + sovKv(uint64(m.Version))
}
l = len(m.Value)
if l > 0 {
n += 1 + l + sovKv(uint64(l))
}
if m.Lease != 0 {
n += 1 + sovKv(uint64(m.Lease))
}
if m.XXX_unrecognized != nil {
n += len(m.XXX_unrecognized)
}
return n
}
func (m *Event) Size() (n int) {
if m == nil {
return 0
}
var l int
_ = l
if m.Type != 0 {
n += 1 + sovKv(uint64(m.Type))
}
if m.Kv != nil {
l = m.Kv.Size()
n += 1 + l + sovKv(uint64(l))
}
if m.PrevKv != nil {
l = m.PrevKv.Size()
n += 1 + l + sovKv(uint64(l))
}
if m.XXX_unrecognized != nil {
n += len(m.XXX_unrecognized)
}
return n
}
func sovKv(x uint64) (n int) {
return (math_bits.Len64(x|1) + 6) / 7
}
func sozKv(x uint64) (n int) {
return sovKv(uint64((x << 1) ^ uint64((int64(x) >> 63))))
}
func (m *KeyValue) Unmarshal(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0
for iNdEx < l {
preIndex := iNdEx
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowKv
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
fieldNum := int32(wire >> 3)
wireType := int(wire & 0x7)
if wireType == 4 {
return fmt.Errorf("proto: KeyValue: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: KeyValue: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
case 1:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Key", wireType)
}
var byteLen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowKv
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
byteLen |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
if byteLen < 0 {
return ErrInvalidLengthKv
}
postIndex := iNdEx + byteLen
if postIndex < 0 {
return ErrInvalidLengthKv
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.Key = append(m.Key[:0], dAtA[iNdEx:postIndex]...)
if m.Key == nil {
m.Key = []byte{}
}
iNdEx = postIndex
case 2:
if wireType != 0 {
return fmt.Errorf("proto: wrong wireType = %d for field CreateRevision", wireType)
}
m.CreateRevision = 0
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowKv
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
m.CreateRevision |= int64(b&0x7F) << shift
if b < 0x80 {
break
}
}
case 3:
if wireType != 0 {
return fmt.Errorf("proto: wrong wireType = %d for field ModRevision", wireType)
}
m.ModRevision = 0
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowKv
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
m.ModRevision |= int64(b&0x7F) << shift
if b < 0x80 {
break
}
}
case 4:
if wireType != 0 {
return fmt.Errorf("proto: wrong wireType = %d for field Version", wireType)
}
m.Version = 0
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowKv
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
m.Version |= int64(b&0x7F) << shift
if b < 0x80 {
break
}
}
case 5:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Value", wireType)
}
var byteLen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowKv
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
byteLen |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
if byteLen < 0 {
return ErrInvalidLengthKv
}
postIndex := iNdEx + byteLen
if postIndex < 0 {
return ErrInvalidLengthKv
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.Value = append(m.Value[:0], dAtA[iNdEx:postIndex]...)
if m.Value == nil {
m.Value = []byte{}
}
iNdEx = postIndex
case 6:
if wireType != 0 {
return fmt.Errorf("proto: wrong wireType = %d for field Lease", wireType)
}
m.Lease = 0
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowKv
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
m.Lease |= int64(b&0x7F) << shift
if b < 0x80 {
break
}
}
default:
iNdEx = preIndex
skippy, err := skipKv(dAtA[iNdEx:])
if err != nil {
return err
}
if (skippy < 0) || (iNdEx+skippy) < 0 {
return ErrInvalidLengthKv
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
iNdEx += skippy
}
}
if iNdEx > l {
return io.ErrUnexpectedEOF
}
return nil
}
func (m *Event) Unmarshal(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0
for iNdEx < l {
preIndex := iNdEx
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowKv
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
fieldNum := int32(wire >> 3)
wireType := int(wire & 0x7)
if wireType == 4 {
return fmt.Errorf("proto: Event: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: Event: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
case 1:
if wireType != 0 {
return fmt.Errorf("proto: wrong wireType = %d for field Type", wireType)
}
m.Type = 0
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowKv
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
m.Type |= Event_EventType(b&0x7F) << shift
if b < 0x80 {
break
}
}
case 2:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Kv", wireType)
}
var msglen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowKv
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
msglen |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
if msglen < 0 {
return ErrInvalidLengthKv
}
postIndex := iNdEx + msglen
if postIndex < 0 {
return ErrInvalidLengthKv
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
if m.Kv == nil {
m.Kv = &KeyValue{}
}
if err := m.Kv.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
return err
}
iNdEx = postIndex
case 3:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field PrevKv", wireType)
}
var msglen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowKv
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
msglen |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
if msglen < 0 {
return ErrInvalidLengthKv
}
postIndex := iNdEx + msglen
if postIndex < 0 {
return ErrInvalidLengthKv
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
if m.PrevKv == nil {
m.PrevKv = &KeyValue{}
}
if err := m.PrevKv.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
return err
}
iNdEx = postIndex
default:
iNdEx = preIndex
skippy, err := skipKv(dAtA[iNdEx:])
if err != nil {
return err
}
if (skippy < 0) || (iNdEx+skippy) < 0 {
return ErrInvalidLengthKv
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
iNdEx += skippy
}
}
if iNdEx > l {
return io.ErrUnexpectedEOF
}
return nil
}
func skipKv(dAtA []byte) (n int, err error) {
l := len(dAtA)
iNdEx := 0
depth := 0
for iNdEx < l {
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return 0, ErrIntOverflowKv
}
if iNdEx >= l {
return 0, io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= (uint64(b) & 0x7F) << shift
if b < 0x80 {
break
}
}
wireType := int(wire & 0x7)
switch wireType {
case 0:
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return 0, ErrIntOverflowKv
}
if iNdEx >= l {
return 0, io.ErrUnexpectedEOF
}
iNdEx++
if dAtA[iNdEx-1] < 0x80 {
break
}
}
case 1:
iNdEx += 8
case 2:
var length int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return 0, ErrIntOverflowKv
}
if iNdEx >= l {
return 0, io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
length |= (int(b) & 0x7F) << shift
if b < 0x80 {
break
}
}
if length < 0 {
return 0, ErrInvalidLengthKv
}
iNdEx += length
case 3:
depth++
case 4:
if depth == 0 {
return 0, ErrUnexpectedEndOfGroupKv
}
depth--
case 5:
iNdEx += 4
default:
return 0, fmt.Errorf("proto: illegal wireType %d", wireType)
}
if iNdEx < 0 {
return 0, ErrInvalidLengthKv
}
if depth == 0 {
return iNdEx, nil
}
}
return 0, io.ErrUnexpectedEOF
}
var (
ErrInvalidLengthKv = fmt.Errorf("proto: negative length found during unmarshaling")
ErrIntOverflowKv = fmt.Errorf("proto: integer overflow")
ErrUnexpectedEndOfGroupKv = fmt.Errorf("proto: unexpected end of group")
)
================================================
FILE: api/mvccpb/kv.proto
================================================
syntax = "proto3";
package mvccpb;
option go_package = "go.etcd.io/etcd/api/v3/mvccpb";
message KeyValue {
// key is the key in bytes. An empty key is not allowed.
bytes key = 1;
// create_revision is the revision of last creation on this key.
int64 create_revision = 2;
// mod_revision is the revision of last modification on this key.
int64 mod_revision = 3;
// version is the version of the key. A deletion resets
// the version to zero and any modification of the key
// increases its version.
int64 version = 4;
// value is the value held by the key, in bytes.
bytes value = 5;
// lease is the ID of the lease that attached to key.
// When the attached lease expires, the key will be deleted.
// If lease is 0, then no lease is attached to the key.
int64 lease = 6;
}
message Event {
enum EventType {
PUT = 0;
DELETE = 1;
}
// type is the kind of event. If type is a PUT, it indicates
// new data has been stored to the key. If type is a DELETE,
// it indicates the key was deleted.
EventType type = 1;
// kv holds the KeyValue for the event.
// A PUT event contains current kv pair.
// A PUT event with kv.Version=1 indicates the creation of a key.
// A DELETE/EXPIRE event contains the deleted key with
// its modification revision set to the revision of deletion.
KeyValue kv = 2;
// prev_kv holds the key-value pair before the event happens.
KeyValue prev_kv = 3;
}
================================================
FILE: api/v3rpc/rpctypes/doc.go
================================================
// Copyright 2016 The etcd Authors
//
// 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 rpctypes has types and values shared by the etcd server and client for v3 RPC interaction.
package rpctypes
================================================
FILE: api/v3rpc/rpctypes/error.go
================================================
// Copyright 2015 The etcd Authors
//
// 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 rpctypes
import (
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
)
// server-side error
var (
ErrGRPCEmptyKey = status.Error(codes.InvalidArgument, "etcdserver: key is not provided")
ErrGRPCKeyNotFound = status.Error(codes.InvalidArgument, "etcdserver: key not found")
ErrGRPCValueProvided = status.Error(codes.InvalidArgument, "etcdserver: value is provided")
ErrGRPCLeaseProvided = status.Error(codes.InvalidArgument, "etcdserver: lease is provided")
ErrGRPCTooManyOps = status.Error(codes.InvalidArgument, "etcdserver: too many operations in txn request")
ErrGRPCDuplicateKey = status.Error(codes.InvalidArgument, "etcdserver: duplicate key given in txn request")
ErrGRPCInvalidClientAPIVersion = status.Error(codes.InvalidArgument, "etcdserver: invalid client api version")
ErrGRPCInvalidSortOption = status.Error(codes.InvalidArgument, "etcdserver: invalid sort option")
ErrGRPCCompacted = status.Error(codes.OutOfRange, "etcdserver: mvcc: required revision has been compacted")
ErrGRPCFutureRev = status.Error(codes.OutOfRange, "etcdserver: mvcc: required revision is a future revision")
ErrGRPCNoSpace = status.Error(codes.ResourceExhausted, "etcdserver: mvcc: database space exceeded")
ErrGRPCLeaseNotFound = status.Error(codes.NotFound, "etcdserver: requested lease not found")
ErrGRPCLeaseExist = status.Error(codes.FailedPrecondition, "etcdserver: lease already exists")
ErrGRPCLeaseTTLTooLarge = status.Error(codes.OutOfRange, "etcdserver: too large lease TTL")
ErrGRPCWatchCanceled = status.Error(codes.Canceled, "etcdserver: watch canceled")
ErrGRPCMemberExist = status.Error(codes.FailedPrecondition, "etcdserver: member ID already exist")
ErrGRPCPeerURLExist = status.Error(codes.FailedPrecondition, "etcdserver: Peer URLs already exists")
ErrGRPCMemberNotEnoughStarted = status.Error(codes.FailedPrecondition, "etcdserver: re-configuration failed due to not enough started members")
ErrGRPCMemberBadURLs = status.Error(codes.InvalidArgument, "etcdserver: given member URLs are invalid")
ErrGRPCMemberNotFound = status.Error(codes.NotFound, "etcdserver: member not found")
ErrGRPCMemberNotLearner = status.Error(codes.FailedPrecondition, "etcdserver: can only promote a learner member")
ErrGRPCLearnerNotReady = status.Error(codes.FailedPrecondition, "etcdserver: can only promote a learner member which is in sync with leader")
ErrGRPCTooManyLearners = status.Error(codes.FailedPrecondition, "etcdserver: too many learner members in cluster")
ErrGRPCClusterIDMismatch = status.Error(codes.FailedPrecondition, "etcdserver: cluster ID mismatch")
//revive:disable:var-naming
// Deprecated: Please use ErrGRPCClusterIDMismatch.
ErrGRPCClusterIdMismatch = ErrGRPCClusterIDMismatch
//revive:enable:var-naming
ErrGRPCRequestTooLarge = status.Error(codes.InvalidArgument, "etcdserver: request is too large")
ErrGRPCRequestTooManyRequests = status.Error(codes.ResourceExhausted, "etcdserver: too many requests")
ErrGRPCRootUserNotExist = status.Error(codes.FailedPrecondition, "etcdserver: root user does not exist")
ErrGRPCRootRoleNotExist = status.Error(codes.FailedPrecondition, "etcdserver: root user does not have root role")
ErrGRPCUserAlreadyExist = status.Error(codes.FailedPrecondition, "etcdserver: user name already exists")
ErrGRPCUserEmpty = status.Error(codes.InvalidArgument, "etcdserver: user name is empty")
ErrGRPCUserNotFound = status.Error(codes.FailedPrecondition, "etcdserver: user name not found")
ErrGRPCRoleAlreadyExist = status.Error(codes.FailedPrecondition, "etcdserver: role name already exists")
ErrGRPCRoleNotFound = status.Error(codes.FailedPrecondition, "etcdserver: role name not found")
ErrGRPCRoleEmpty = status.Error(codes.InvalidArgument, "etcdserver: role name is empty")
ErrGRPCAuthFailed = status.Error(codes.InvalidArgument, "etcdserver: authentication failed, invalid user ID or password")
ErrGRPCPermissionNotGiven = status.Error(codes.InvalidArgument, "etcdserver: permission not given")
ErrGRPCPermissionDenied = status.Error(codes.PermissionDenied, "etcdserver: permission denied")
ErrGRPCRoleNotGranted = status.Error(codes.FailedPrecondition, "etcdserver: role is not granted to the user")
ErrGRPCPermissionNotGranted = status.Error(codes.FailedPrecondition, "etcdserver: permission is not granted to the role")
ErrGRPCAuthNotEnabled = status.Error(codes.FailedPrecondition, "etcdserver: authentication is not enabled")
ErrGRPCInvalidAuthToken = status.Error(codes.Unauthenticated, "etcdserver: invalid auth token")
ErrGRPCInvalidAuthMgmt = status.Error(codes.InvalidArgument, "etcdserver: invalid auth management")
ErrGRPCAuthOldRevision = status.Error(codes.InvalidArgument, "etcdserver: revision of auth store is old")
ErrGRPCNoLeader = status.Error(codes.Unavailable, "etcdserver: no leader")
ErrGRPCNotLeader = status.Error(codes.FailedPrecondition, "etcdserver: not leader")
ErrGRPCLeaderChanged = status.Error(codes.Unavailable, "etcdserver: leader changed")
ErrGRPCNotCapable = status.Error(codes.FailedPrecondition, "etcdserver: not capable")
ErrGRPCStopped = status.Error(codes.Unavailable, "etcdserver: server stopped")
ErrGRPCTimeout = status.Error(codes.Unavailable, "etcdserver: request timed out")
ErrGRPCTimeoutDueToLeaderFail = status.Error(codes.Unavailable, "etcdserver: request timed out, possibly due to previous leader failure")
ErrGRPCTimeoutDueToConnectionLost = status.Error(codes.Unavailable, "etcdserver: request timed out, possibly due to connection lost")
ErrGRPCTimeoutWaitAppliedIndex = status.Error(codes.Unavailable, "etcdserver: request timed out, waiting for the applied index took too long")
ErrGRPCUnhealthy = status.Error(codes.Unavailable, "etcdserver: unhealthy cluster")
ErrGRPCCorrupt = status.Error(codes.DataLoss, "etcdserver: corrupt cluster")
ErrGRPCNotSupportedForLearner = status.Error(codes.FailedPrecondition, "etcdserver: rpc not supported for learner")
ErrGRPCBadLeaderTransferee = status.Error(codes.FailedPrecondition, "etcdserver: bad leader transferee")
ErrGRPCWrongDowngradeVersionFormat = status.Error(codes.InvalidArgument, "etcdserver: wrong downgrade target version format")
ErrGRPCInvalidDowngradeTargetVersion = status.Error(codes.InvalidArgument, "etcdserver: invalid downgrade target version")
ErrGRPCClusterVersionUnavailable = status.Error(codes.FailedPrecondition, "etcdserver: cluster version not found during downgrade")
ErrGRPCDowngradeInProcess = status.Error(codes.FailedPrecondition, "etcdserver: cluster has a downgrade job in progress")
ErrGRPCNoInflightDowngrade = status.Error(codes.FailedPrecondition, "etcdserver: no inflight downgrade job")
ErrGRPCCanceled = status.Error(codes.Canceled, "etcdserver: request canceled")
ErrGRPCDeadlineExceeded = status.Error(codes.DeadlineExceeded, "etcdserver: context deadline exceeded")
errStringToError = map[string]error{
ErrorDesc(ErrGRPCEmptyKey): ErrGRPCEmptyKey,
ErrorDesc(ErrGRPCKeyNotFound): ErrGRPCKeyNotFound,
ErrorDesc(ErrGRPCValueProvided): ErrGRPCValueProvided,
ErrorDesc(ErrGRPCLeaseProvided): ErrGRPCLeaseProvided,
ErrorDesc(ErrGRPCTooManyOps): ErrGRPCTooManyOps,
ErrorDesc(ErrGRPCDuplicateKey): ErrGRPCDuplicateKey,
ErrorDesc(ErrGRPCInvalidSortOption): ErrGRPCInvalidSortOption,
ErrorDesc(ErrGRPCCompacted): ErrGRPCCompacted,
ErrorDesc(ErrGRPCFutureRev): ErrGRPCFutureRev,
ErrorDesc(ErrGRPCNoSpace): ErrGRPCNoSpace,
ErrorDesc(ErrGRPCLeaseNotFound): ErrGRPCLeaseNotFound,
ErrorDesc(ErrGRPCLeaseExist): ErrGRPCLeaseExist,
ErrorDesc(ErrGRPCLeaseTTLTooLarge): ErrGRPCLeaseTTLTooLarge,
ErrorDesc(ErrGRPCMemberExist): ErrGRPCMemberExist,
ErrorDesc(ErrGRPCPeerURLExist): ErrGRPCPeerURLExist,
ErrorDesc(ErrGRPCMemberNotEnoughStarted): ErrGRPCMemberNotEnoughStarted,
ErrorDesc(ErrGRPCMemberBadURLs): ErrGRPCMemberBadURLs,
ErrorDesc(ErrGRPCMemberNotFound): ErrGRPCMemberNotFound,
ErrorDesc(ErrGRPCMemberNotLearner): ErrGRPCMemberNotLearner,
ErrorDesc(ErrGRPCLearnerNotReady): ErrGRPCLearnerNotReady,
ErrorDesc(ErrGRPCTooManyLearners): ErrGRPCTooManyLearners,
ErrorDesc(ErrGRPCClusterIDMismatch): ErrGRPCClusterIDMismatch,
ErrorDesc(ErrGRPCRequestTooLarge): ErrGRPCRequestTooLarge,
ErrorDesc(ErrGRPCRequestTooManyRequests): ErrGRPCRequestTooManyRequests,
ErrorDesc(ErrGRPCRootUserNotExist): ErrGRPCRootUserNotExist,
ErrorDesc(ErrGRPCRootRoleNotExist): ErrGRPCRootRoleNotExist,
ErrorDesc(ErrGRPCUserAlreadyExist): ErrGRPCUserAlreadyExist,
ErrorDesc(ErrGRPCUserEmpty): ErrGRPCUserEmpty,
ErrorDesc(ErrGRPCUserNotFound): ErrGRPCUserNotFound,
ErrorDesc(ErrGRPCRoleAlreadyExist): ErrGRPCRoleAlreadyExist,
ErrorDesc(ErrGRPCRoleNotFound): ErrGRPCRoleNotFound,
ErrorDesc(ErrGRPCRoleEmpty): ErrGRPCRoleEmpty,
ErrorDesc(ErrGRPCAuthFailed): ErrGRPCAuthFailed,
ErrorDesc(ErrGRPCPermissionDenied): ErrGRPCPermissionDenied,
ErrorDesc(ErrGRPCRoleNotGranted): ErrGRPCRoleNotGranted,
ErrorDesc(ErrGRPCPermissionNotGranted): ErrGRPCPermissionNotGranted,
ErrorDesc(ErrGRPCAuthNotEnabled): ErrGRPCAuthNotEnabled,
ErrorDesc(ErrGRPCInvalidAuthToken): ErrGRPCInvalidAuthToken,
ErrorDesc(ErrGRPCInvalidAuthMgmt): ErrGRPCInvalidAuthMgmt,
ErrorDesc(ErrGRPCAuthOldRevision): ErrGRPCAuthOldRevision,
ErrorDesc(ErrGRPCNoLeader): ErrGRPCNoLeader,
ErrorDesc(ErrGRPCNotLeader): ErrGRPCNotLeader,
ErrorDesc(ErrGRPCLeaderChanged): ErrGRPCLeaderChanged,
ErrorDesc(ErrGRPCNotCapable): ErrGRPCNotCapable,
ErrorDesc(ErrGRPCStopped): ErrGRPCStopped,
ErrorDesc(ErrGRPCTimeout): ErrGRPCTimeout,
ErrorDesc(ErrGRPCTimeoutDueToLeaderFail): ErrGRPCTimeoutDueToLeaderFail,
ErrorDesc(ErrGRPCTimeoutDueToConnectionLost): ErrGRPCTimeoutDueToConnectionLost,
ErrorDesc(ErrGRPCUnhealthy): ErrGRPCUnhealthy,
ErrorDesc(ErrGRPCCorrupt): ErrGRPCCorrupt,
ErrorDesc(ErrGRPCNotSupportedForLearner): ErrGRPCNotSupportedForLearner,
ErrorDesc(ErrGRPCBadLeaderTransferee): ErrGRPCBadLeaderTransferee,
ErrorDesc(ErrGRPCClusterVersionUnavailable): ErrGRPCClusterVersionUnavailable,
ErrorDesc(ErrGRPCWrongDowngradeVersionFormat): ErrGRPCWrongDowngradeVersionFormat,
ErrorDesc(ErrGRPCInvalidDowngradeTargetVersion): ErrGRPCInvalidDowngradeTargetVersion,
ErrorDesc(ErrGRPCDowngradeInProcess): ErrGRPCDowngradeInProcess,
ErrorDesc(ErrGRPCNoInflightDowngrade): ErrGRPCNoInflightDowngrade,
}
)
// client-side error
var (
ErrEmptyKey = Error(ErrGRPCEmptyKey)
ErrKeyNotFound = Error(ErrGRPCKeyNotFound)
ErrValueProvided = Error(ErrGRPCValueProvided)
ErrLeaseProvided = Error(ErrGRPCLeaseProvided)
ErrTooManyOps = Error(ErrGRPCTooManyOps)
ErrDuplicateKey = Error(ErrGRPCDuplicateKey)
ErrInvalidSortOption = Error(ErrGRPCInvalidSortOption)
ErrCompacted = Error(ErrGRPCCompacted)
ErrFutureRev = Error(ErrGRPCFutureRev)
ErrNoSpace = Error(ErrGRPCNoSpace)
ErrLeaseNotFound = Error(ErrGRPCLeaseNotFound)
ErrLeaseExist = Error(ErrGRPCLeaseExist)
ErrLeaseTTLTooLarge = Error(ErrGRPCLeaseTTLTooLarge)
ErrMemberExist = Error(ErrGRPCMemberExist)
ErrPeerURLExist = Error(ErrGRPCPeerURLExist)
ErrMemberNotEnoughStarted = Error(ErrGRPCMemberNotEnoughStarted)
ErrMemberBadURLs = Error(ErrGRPCMemberBadURLs)
ErrMemberNotFound = Error(ErrGRPCMemberNotFound)
ErrMemberNotLearner = Error(ErrGRPCMemberNotLearner)
ErrMemberLearnerNotReady = Error(ErrGRPCLearnerNotReady)
ErrTooManyLearners = Error(ErrGRPCTooManyLearners)
ErrRequestTooLarge = Error(ErrGRPCRequestTooLarge)
ErrTooManyRequests = Error(ErrGRPCRequestTooManyRequests)
ErrRootUserNotExist = Error(ErrGRPCRootUserNotExist)
ErrRootRoleNotExist = Error(ErrGRPCRootRoleNotExist)
ErrUserAlreadyExist = Error(ErrGRPCUserAlreadyExist)
ErrUserEmpty = Error(ErrGRPCUserEmpty)
ErrUserNotFound = Error(ErrGRPCUserNotFound)
ErrRoleAlreadyExist = Error(ErrGRPCRoleAlreadyExist)
ErrRoleNotFound = Error(ErrGRPCRoleNotFound)
ErrRoleEmpty = Error(ErrGRPCRoleEmpty)
ErrAuthFailed = Error(ErrGRPCAuthFailed)
ErrPermissionDenied = Error(ErrGRPCPermissionDenied)
ErrRoleNotGranted = Error(ErrGRPCRoleNotGranted)
ErrPermissionNotGranted = Error(ErrGRPCPermissionNotGranted)
ErrAuthNotEnabled = Error(ErrGRPCAuthNotEnabled)
ErrInvalidAuthToken = Error(ErrGRPCInvalidAuthToken)
ErrAuthOldRevision = Error(ErrGRPCAuthOldRevision)
ErrInvalidAuthMgmt = Error(ErrGRPCInvalidAuthMgmt)
ErrClusterIDMismatch = Error(ErrGRPCClusterIDMismatch)
//revive:disable:var-naming
// Deprecated: Please use ErrClusterIDMismatch.
ErrClusterIdMismatch = ErrClusterIDMismatch
//revive:enable:var-naming
ErrNoLeader = Error(ErrGRPCNoLeader)
ErrNotLeader = Error(ErrGRPCNotLeader)
ErrLeaderChanged = Error(ErrGRPCLeaderChanged)
ErrNotCapable = Error(ErrGRPCNotCapable)
ErrStopped = Error(ErrGRPCStopped)
ErrTimeout = Error(ErrGRPCTimeout)
ErrTimeoutDueToLeaderFail = Error(ErrGRPCTimeoutDueToLeaderFail)
ErrTimeoutDueToConnectionLost = Error(ErrGRPCTimeoutDueToConnectionLost)
ErrTimeoutWaitAppliedIndex = Error(ErrGRPCTimeoutWaitAppliedIndex)
ErrUnhealthy = Error(ErrGRPCUnhealthy)
ErrCorrupt = Error(ErrGRPCCorrupt)
ErrBadLeaderTransferee = Error(ErrGRPCBadLeaderTransferee)
ErrClusterVersionUnavailable = Error(ErrGRPCClusterVersionUnavailable)
ErrWrongDowngradeVersionFormat = Error(ErrGRPCWrongDowngradeVersionFormat)
ErrInvalidDowngradeTargetVersion = Error(ErrGRPCInvalidDowngradeTargetVersion)
ErrDowngradeInProcess = Error(ErrGRPCDowngradeInProcess)
ErrNoInflightDowngrade = Error(ErrGRPCNoInflightDowngrade)
)
// EtcdError defines gRPC server errors.
// (https://github.com/grpc/grpc-go/blob/master/rpc_util.go#L319-L323)
type EtcdError struct {
code codes.Code
desc string
}
// Code returns grpc/codes.Code.
// TODO: define clientv3/codes.Code.
func (e EtcdError) Code() codes.Code {
return e.code
}
func (e EtcdError) Error() string {
return e.desc
}
func Error(err error) error {
if err == nil {
return nil
}
verr, ok := errStringToError[ErrorDesc(err)]
if !ok { // not gRPC error
return err
}
ev, ok := status.FromError(verr)
var desc string
if ok {
desc = ev.Message()
} else {
desc = verr.Error()
}
return EtcdError{code: ev.Code(), desc: desc}
}
func ErrorDesc(err error) string {
if s, ok := status.FromError(err); ok {
return s.Message()
}
return err.Error()
}
================================================
FILE: api/v3rpc/rpctypes/error_test.go
================================================
// Copyright 2016 The etcd Authors
//
// 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 rpctypes
import (
"errors"
"testing"
"github.com/stretchr/testify/require"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
)
func TestConvert(t *testing.T) {
e1 := status.Error(codes.InvalidArgument, "etcdserver: key is not provided")
e2 := ErrGRPCEmptyKey
var e3 EtcdError
errors.As(ErrEmptyKey, &e3)
require.Equal(t, e1.Error(), e2.Error())
if ev1, ok := status.FromError(e1); ok {
require.Equal(t, ev1.Code(), e3.Code())
}
require.NotEqual(t, e1.Error(), e3.Error())
if ev2, ok := status.FromError(e2); ok {
require.Equal(t, ev2.Code(), e3.Code())
}
}
================================================
FILE: api/v3rpc/rpctypes/md.go
================================================
// Copyright 2016 The etcd Authors
//
// 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 rpctypes
var (
MetadataRequireLeaderKey = "hasleader"
MetadataHasLeader = "true"
MetadataClientAPIVersionKey = "client-api-version"
)
================================================
FILE: api/v3rpc/rpctypes/metadatafields.go
================================================
// Copyright 2018 The etcd Authors
//
// 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 rpctypes
var (
TokenFieldNameGRPC = "token"
TokenFieldNameSwagger = "authorization"
)
// TokenFieldNameGRPCKey is used as a key of context to store token.
type TokenFieldNameGRPCKey struct{}
================================================
FILE: api/version/version.go
================================================
// Copyright 2015 The etcd Authors
//
// 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 version implements etcd version parsing and contains latest version
// information.
package version
import (
"fmt"
"strings"
"github.com/coreos/go-semver/semver"
)
var (
// MinClusterVersion is the min cluster version this etcd binary is compatible with.
MinClusterVersion = "3.0.0"
Version = "3.7.0-alpha.0"
APIVersion = "unknown"
// Git SHA Value will be set during build
GitSHA = "Not provided (use ./build instead of go build)"
)
// Get all constant versions defined in a centralized place.
var (
V3_0 = semver.Version{Major: 3, Minor: 0}
V3_1 = semver.Version{Major: 3, Minor: 1}
V3_2 = semver.Version{Major: 3, Minor: 2}
V3_3 = semver.Version{Major: 3, Minor: 3}
V3_4 = semver.Version{Major: 3, Minor: 4}
V3_5 = semver.Version{Major: 3, Minor: 5}
V3_6 = semver.Version{Major: 3, Minor: 6}
V3_7 = semver.Version{Major: 3, Minor: 7}
V3_8 = semver.Version{Major: 3, Minor: 8}
V4_0 = semver.Version{Major: 4, Minor: 0}
// AllVersions keeps all the versions in ascending order.
AllVersions = []semver.Version{V3_0, V3_1, V3_2, V3_3, V3_4, V3_5, V3_6, V3_7, V4_0}
)
func init() {
ver, err := semver.NewVersion(Version)
if err == nil {
APIVersion = fmt.Sprintf("%d.%d", ver.Major, ver.Minor)
}
}
type Versions struct {
Server string `json:"etcdserver"`
Cluster string `json:"etcdcluster"`
Storage string `json:"storage"`
// TODO: raft state machine version
}
// Cluster only keeps the major.minor.
func Cluster(v string) string {
vs := strings.Split(v, ".")
if len(vs) <= 2 {
return v
}
return fmt.Sprintf("%s.%s", vs[0], vs[1])
}
func Compare(ver1, ver2 semver.Version) int {
return ver1.Compare(ver2)
}
func LessThan(ver1, ver2 semver.Version) bool {
return ver1.LessThan(ver2)
}
func Equal(ver1, ver2 semver.Version) bool {
return ver1.Equal(ver2)
}
================================================
FILE: api/version/version_test.go
================================================
// Copyright 2022 The etcd Authors
//
// 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 version
import (
"testing"
"github.com/coreos/go-semver/semver"
"github.com/stretchr/testify/assert"
)
func TestVersionCompare(t *testing.T) {
cases := []struct {
name string
ver1 semver.Version
ver2 semver.Version
expectedCompareResult int
expectedLessThanResult bool
expectedEqualResult bool
}{
{
name: "ver1 should be great than ver2",
ver1: V3_5,
ver2: V3_4,
expectedCompareResult: 1,
expectedLessThanResult: false,
expectedEqualResult: false,
},
{
name: "ver1(4.0) should be great than ver2",
ver1: V4_0,
ver2: V3_7,
expectedCompareResult: 1,
expectedLessThanResult: false,
expectedEqualResult: false,
},
{
name: "ver1 should be less than ver2",
ver1: V3_5,
ver2: V3_6,
expectedCompareResult: -1,
expectedLessThanResult: true,
expectedEqualResult: false,
},
{
name: "ver1 should be less than ver2 (4.0)",
ver1: V3_5,
ver2: V4_0,
expectedCompareResult: -1,
expectedLessThanResult: true,
expectedEqualResult: false,
},
{
name: "ver1 should be equal to ver2",
ver1: V3_5,
ver2: V3_5,
expectedCompareResult: 0,
expectedLessThanResult: false,
expectedEqualResult: true,
},
}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
compareResult := Compare(tc.ver1, tc.ver2)
lessThanResult := LessThan(tc.ver1, tc.ver2)
equalResult := Equal(tc.ver1, tc.ver2)
assert.Equal(t, tc.expectedCompareResult, compareResult)
assert.Equal(t, tc.expectedLessThanResult, lessThanResult)
assert.Equal(t, tc.expectedEqualResult, equalResult)
})
}
}
================================================
FILE: api/versionpb/version.pb.go
================================================
// Code generated by protoc-gen-gogo. DO NOT EDIT.
// source: version.proto
package versionpb
import (
fmt "fmt"
math "math"
proto "github.com/golang/protobuf/proto"
descriptorpb "google.golang.org/protobuf/types/descriptorpb"
)
// Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal
var _ = fmt.Errorf
var _ = math.Inf
// This is a compile-time assertion to ensure that this generated file
// is compatible with the proto package it is being compiled against.
// A compilation error at this line likely means your copy of the
// proto package needs to be updated.
const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
var E_EtcdVersionMsg = &proto.ExtensionDesc{
ExtendedType: (*descriptorpb.MessageOptions)(nil),
ExtensionType: (*string)(nil),
Field: 50000,
Name: "versionpb.etcd_version_msg",
Tag: "bytes,50000,opt,name=etcd_version_msg",
Filename: "version.proto",
}
var E_EtcdVersionField = &proto.ExtensionDesc{
ExtendedType: (*descriptorpb.FieldOptions)(nil),
ExtensionType: (*string)(nil),
Field: 50001,
Name: "versionpb.etcd_version_field",
Tag: "bytes,50001,opt,name=etcd_version_field",
Filename: "version.proto",
}
var E_EtcdVersionEnum = &proto.ExtensionDesc{
ExtendedType: (*descriptorpb.EnumOptions)(nil),
ExtensionType: (*string)(nil),
Field: 50002,
Name: "versionpb.etcd_version_enum",
Tag: "bytes,50002,opt,name=etcd_version_enum",
Filename: "version.proto",
}
var E_EtcdVersionEnumValue = &proto.ExtensionDesc{
ExtendedType: (*descriptorpb.EnumValueOptions)(nil),
ExtensionType: (*string)(nil),
Field: 50003,
Name: "versionpb.etcd_version_enum_value",
Tag: "bytes,50003,opt,name=etcd_version_enum_value",
Filename: "version.proto",
}
func init() {
proto.RegisterExtension(E_EtcdVersionMsg)
proto.RegisterExtension(E_EtcdVersionField)
proto.RegisterExtension(E_EtcdVersionEnum)
proto.RegisterExtension(E_EtcdVersionEnumValue)
}
func init() { proto.RegisterFile("version.proto", fileDescriptor_7d2c07d79758f814) }
var fileDescriptor_7d2c07d79758f814 = []byte{
// 271 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xe2, 0x2d, 0x4b, 0x2d, 0x2a,
0xce, 0xcc, 0xcf, 0xd3, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0xe2, 0x84, 0x72, 0x0b, 0x92, 0xa4,
0x14, 0xd2, 0xf3, 0xf3, 0xd3, 0x73, 0x52, 0xf5, 0xc1, 0x12, 0x49, 0xa5, 0x69, 0xfa, 0x29, 0xa9,
0xc5, 0xc9, 0x45, 0x99, 0x05, 0x25, 0xf9, 0x45, 0x10, 0xc5, 0x56, 0x7e, 0x5c, 0x02, 0xa9, 0x25,
0xc9, 0x29, 0xf1, 0x50, 0x3d, 0xf1, 0xb9, 0xc5, 0xe9, 0x42, 0xf2, 0x7a, 0x10, 0x6d, 0x7a, 0x30,
0x6d, 0x7a, 0xbe, 0xa9, 0xc5, 0xc5, 0x89, 0xe9, 0xa9, 0xfe, 0x05, 0x25, 0x99, 0xf9, 0x79, 0xc5,
0x12, 0x17, 0xda, 0x98, 0x15, 0x18, 0x35, 0x38, 0x83, 0xf8, 0x40, 0x5a, 0xc3, 0x20, 0x3a, 0x7d,
0x8b, 0xd3, 0x3b, 0x18, 0x19, 0xad, 0x02, 0xb8, 0x84, 0x50, 0xcc, 0x4b, 0xcb, 0x4c, 0xcd, 0x49,
0x11, 0x92, 0xc5, 0x30, 0xd1, 0x0d, 0x24, 0x0e, 0x33, 0xef, 0x22, 0xd4, 0x3c, 0x01, 0x24, 0xf3,
0xc0, 0x0a, 0x40, 0x26, 0xfa, 0x72, 0x09, 0xa2, 0x98, 0x98, 0x9a, 0x57, 0x9a, 0x2b, 0x24, 0x83,
0x61, 0xa0, 0x6b, 0x5e, 0x69, 0x2e, 0xcc, 0xbc, 0x4b, 0x50, 0xf3, 0xf8, 0x91, 0xcc, 0x03, 0xc9,
0x83, 0x8c, 0x8b, 0xe5, 0x12, 0xc7, 0x30, 0x2e, 0xbe, 0x2c, 0x31, 0xa7, 0x34, 0x55, 0x48, 0x11,
0xab, 0xa1, 0x61, 0x20, 0x39, 0x98, 0xc9, 0x97, 0xa1, 0x26, 0x8b, 0xa0, 0x99, 0x0c, 0x56, 0xd4,
0xc1, 0xc8, 0xe8, 0x64, 0x74, 0xe2, 0x91, 0x1c, 0xe3, 0x85, 0x47, 0x72, 0x8c, 0x0f, 0x1e, 0xc9,
0x31, 0xce, 0x78, 0x2c, 0xc7, 0x10, 0xa5, 0x90, 0x9e, 0xaf, 0x07, 0x52, 0xad, 0x97, 0x99, 0xaf,
0x0f, 0xa2, 0xf5, 0x13, 0x0b, 0x32, 0xf5, 0xcb, 0x8c, 0xf5, 0xe1, 0xb1, 0x94, 0xc4, 0x06, 0xb6,
0xcf, 0x18, 0x10, 0x00, 0x00, 0xff, 0xff, 0x16, 0x4f, 0x52, 0x12, 0xc8, 0x01, 0x00, 0x00,
}
================================================
FILE: api/versionpb/version.proto
================================================
syntax = "proto3";
package versionpb;
import "google/protobuf/descriptor.proto";
option go_package = "go.etcd.io/etcd/api/v3/versionpb";
// Indicates etcd version that introduced the message, used to determine minimal etcd version required to interpret wal that includes this message.
extend google.protobuf.MessageOptions {
optional string etcd_version_msg = 50000;
}
// Indicates etcd version that introduced the field, used to determine minimal etcd version required to interpret wal that sets this field.
extend google.protobuf.FieldOptions {
optional string etcd_version_field = 50001;
}
// Indicates etcd version that introduced the enum, used to determine minimal etcd version required to interpret wal that uses this enum.
extend google.protobuf.EnumOptions {
optional string etcd_version_enum = 50002;
}
// Indicates etcd version that introduced the enum value, used to determine minimal etcd version required to interpret wal that sets this enum value.
extend google.protobuf.EnumValueOptions {
optional string etcd_version_enum_value = 50003;
}
================================================
FILE: bill-of-materials.json
================================================
[
{
"project": "github.com/VividCortex/ewma",
"licenses": [
{
"type": "MIT License",
"confidence": 1
}
]
},
{
"project": "github.com/anishathalye/porcupine",
"licenses": [
{
"type": "MIT License",
"confidence": 0.96875
}
]
},
{
"project": "github.com/antithesishq/antithesis-sdk-go",
"licenses": [
{
"type": "MIT License",
"confidence": 1
}
]
},
{
"project": "github.com/beorn7/perks/quantile",
"licenses": [
{
"type": "MIT License",
"confidence": 0.9891304347826086
}
]
},
{
"project": "github.com/bgentry/speakeasy",
"licenses": [
{
"type": "MIT License",
"confidence": 0.9441624365482234
}
]
},
{
"project": "github.com/cenkalti/backoff/v5",
"licenses": [
{
"type": "MIT License",
"confidence": 1
}
]
},
{
"project": "github.com/cespare/xxhash/v2",
"licenses": [
{
"type": "MIT License",
"confidence": 1
}
]
},
{
"project": "github.com/cheggaaa/pb/v3",
"licenses": [
{
"type": "BSD 3-clause \"New\" or \"Revised\" License",
"confidence": 0.9916666666666667
}
]
},
{
"project": "github.com/clipperhouse/displaywidth",
"licenses": [
{
"type": "MIT License",
"confidence": 1
}
]
},
{
"project": "github.com/clipperhouse/stringish",
"licenses": [
{
"type": "MIT License",
"confidence": 1
}
]
},
{
"project": "github.com/clipperhouse/uax29/v2",
"licenses": [
{
"type": "MIT License",
"confidence": 1
}
]
},
{
"project": "github.com/coreos/go-semver/semver",
"licenses": [
{
"type": "Apache License 2.0",
"confidence": 1
}
]
},
{
"project": "github.com/coreos/go-systemd/v22",
"licenses": [
{
"type": "Apache License 2.0",
"confidence": 0.9966703662597114
}
]
},
{
"project": "github.com/creack/pty",
"licenses": [
{
"type": "MIT License",
"confidence": 0.9891304347826086
}
]
},
{
"project": "github.com/davecgh/go-spew/spew",
"licenses": [
{
"type": "ISC License",
"confidence": 0.9850746268656716
}
]
},
{
"project": "github.com/dustin/go-humanize",
"licenses": [
{
"type": "MIT License",
"confidence": 0.96875
}
]
},
{
"project": "github.com/fatih/color",
"licenses": [
{
"type": "MIT License",
"confidence": 1
}
]
},
{
"project": "github.com/go-logr/logr",
"licenses": [
{
"type": "Apache License 2.0",
"confidence": 1
}
]
},
{
"project": "github.com/go-logr/stdr",
"licenses": [
{
"type": "Apache License 2.0",
"confidence": 1
}
]
},
{
"project": "github.com/gogo/protobuf",
"licenses": [
{
"type": "BSD 3-clause \"New\" or \"Revised\" License",
"confidence": 0.9163346613545816
}
]
},
{
"project": "github.com/golang-jwt/jwt/v5",
"licenses": [
{
"type": "MIT License",
"confidence": 0.9891304347826086
}
]
},
{
"project": "github.com/golang/protobuf",
"licenses": [
{
"type": "BSD 3-clause \"New\" or \"Revised\" License",
"confidence": 0.9663865546218487
}
]
},
{
"project": "github.com/google/go-cmp/cmp",
"licenses": [
{
"type": "BSD 3-clause \"New\" or \"Revised\" License",
"confidence": 0.9663865546218487
}
]
},
{
"project": "github.com/google/uuid",
"licenses": [
{
"type": "BSD 3-clause \"New\" or \"Revised\" License",
"confidence": 0.9663865546218487
}
]
},
{
"project": "github.com/gorilla/websocket",
"licenses": [
{
"type": "BSD 2-clause \"Simplified\" License",
"confidence": 0.9852216748768473
}
]
},
{
"project": "github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus",
"licenses": [
{
"type": "Apache License 2.0",
"confidence": 1
}
]
},
{
"project": "github.com/grpc-ecosystem/go-grpc-middleware/v2/interceptors",
"licenses": [
{
"type": "Apache License 2.0",
"confidence": 1
}
]
},
{
"project": "github.com/grpc-ecosystem/grpc-gateway/v2",
"licenses": [
{
"type": "BSD 3-clause \"New\" or \"Revised\" License",
"confidence": 0.979253112033195
}
]
},
{
"project": "github.com/inconshreveable/mousetrap",
"licenses": [
{
"type": "Apache License 2.0",
"confidence": 1
}
]
},
{
"project": "github.com/jonboulle/clockwork",
"licenses": [
{
"type": "Apache License 2.0",
"confidence": 1
}
]
},
{
"project": "github.com/mattn/go-colorable",
"licenses": [
{
"type": "MIT License",
"confidence": 1
}
]
},
{
"project": "github.com/mattn/go-isatty",
"licenses": [
{
"type": "MIT License",
"confidence": 0.9587628865979382
}
]
},
{
"project": "github.com/mattn/go-runewidth",
"licenses": [
{
"type": "MIT License",
"confidence": 1
}
]
},
{
"project": "github.com/munnerz/goautoneg",
"licenses": [
{
"type": "BSD 3-clause \"New\" or \"Revised\" License",
"confidence": 0.9794238683127572
}
]
},
{
"project": "github.com/olekukonko/cat",
"licenses": [
{
"type": "MIT License",
"confidence": 1
}
]
},
{
"project": "github.com/olekukonko/errors",
"licenses": [
{
"type": "MIT License",
"confidence": 1
}
]
},
{
"project": "github.com/olekukonko/ll",
"licenses": [
{
"type": "MIT License",
"confidence": 1
}
]
},
{
"project": "github.com/olekukonko/tablewriter",
"licenses": [
{
"type": "MIT License",
"confidence": 0.9891304347826086
}
]
},
{
"project": "github.com/pmezard/go-difflib/difflib",
"licenses": [
{
"type": "BSD 3-clause \"New\" or \"Revised\" License",
"confidence": 0.9830508474576272
}
]
},
{
"project": "github.com/prometheus/client_golang/internal/github.com/golang/gddo/httputil",
"licenses": [
{
"type": "BSD 3-clause \"New\" or \"Revised\" License",
"confidence": 0.9663865546218487
}
]
},
{
"project": "github.com/prometheus/client_golang/prometheus",
"licenses": [
{
"type": "Apache License 2.0",
"confidence": 1
}
]
},
{
"project": "github.com/prometheus/client_model/go",
"licenses": [
{
"type": "Apache License 2.0",
"confidence": 1
}
]
},
{
"project": "github.com/prometheus/common",
"licenses": [
{
"type": "Apache License 2.0",
"confidence": 1
}
]
},
{
"project": "github.com/prometheus/procfs",
"licenses": [
{
"type": "Apache License 2.0",
"confidence": 1
}
]
},
{
"project": "github.com/sirupsen/logrus",
"licenses": [
{
"type": "MIT License",
"confidence": 1
}
]
},
{
"project": "github.com/soheilhy/cmux",
"licenses": [
{
"type": "Apache License 2.0",
"confidence": 1
}
]
},
{
"project": "github.com/spf13/cobra",
"licenses": [
{
"type": "Apache License 2.0",
"confidence": 0.9573241061130334
}
]
},
{
"project": "github.com/spf13/pflag",
"licenses": [
{
"type": "BSD 3-clause \"New\" or \"Revised\" License",
"confidence": 0.9663865546218487
}
]
},
{
"project": "github.com/stretchr/testify",
"licenses": [
{
"type": "MIT License",
"confidence": 1
}
]
},
{
"project": "github.com/tmc/grpc-websocket-proxy/wsproxy",
"licenses": [
{
"type": "MIT License",
"confidence": 0.9891304347826086
}
]
},
{
"project": "github.com/xiang90/probing",
"licenses": [
{
"type": "MIT License",
"confidence": 1
}
]
},
{
"project": "go.etcd.io/bbolt",
"licenses": [
{
"type": "MIT License",
"confidence": 1
}
]
},
{
"project": "go.etcd.io/etcd/api/v3",
"licenses": [
{
"type": "Apache License 2.0",
"confidence": 0.9988925802879292
}
]
},
{
"project": "go.etcd.io/etcd/cache/v3",
"licenses": [
{
"type": "Apache License 2.0",
"confidence": 0.9988925802879292
}
]
},
{
"project": "go.etcd.io/etcd/client/pkg/v3",
"licenses": [
{
"type": "Apache License 2.0",
"confidence": 0.9988925802879292
}
]
},
{
"project": "go.etcd.io/etcd/client/v3",
"licenses": [
{
"type": "Apache License 2.0",
"confidence": 0.9988925802879292
}
]
},
{
"project": "go.etcd.io/etcd/etcdctl/v3",
"licenses": [
{
"type": "Apache License 2.0",
"confidence": 0.9988925802879292
}
]
},
{
"project": "go.etcd.io/etcd/etcdutl/v3",
"licenses": [
{
"type": "Apache License 2.0",
"confidence": 0.9988925802879292
}
]
},
{
"project": "go.etcd.io/etcd/pkg/v3",
"licenses": [
{
"type": "Apache License 2.0",
"confidence": 0.9988925802879292
}
]
},
{
"project": "go.etcd.io/etcd/server/v3",
"licenses": [
{
"type": "Apache License 2.0",
"confidence": 0.9988925802879292
}
]
},
{
"project": "go.etcd.io/etcd/tests/v3",
"licenses": [
{
"type": "Apache License 2.0",
"confidence": 0.9988925802879292
}
]
},
{
"project": "go.etcd.io/etcd/v3",
"licenses": [
{
"type": "Apache License 2.0",
"confidence": 0.9988925802879292
}
]
},
{
"project": "go.etcd.io/gofail/runtime",
"licenses": [
{
"type": "Apache License 2.0",
"confidence": 1
}
]
},
{
"project": "go.etcd.io/raft/v3",
"licenses": [
{
"type": "Apache License 2.0",
"confidence": 1
}
]
},
{
"project": "go.opentelemetry.io/auto/sdk",
"licenses": [
{
"type": "Apache License 2.0",
"confidence": 1
}
]
},
{
"project": "go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc",
"licenses": [
{
"type": "Apache License 2.0",
"confidence": 0.9647812166488794
}
]
},
{
"project": "go.opentelemetry.io/otel",
"licenses": [
{
"type": "Apache License 2.0",
"confidence": 0.9647812166488794
}
]
},
{
"project": "go.opentelemetry.io/otel/exporters/otlp/otlptrace",
"licenses": [
{
"type": "Apache License 2.0",
"confidence": 0.9647812166488794
}
]
},
{
"project": "go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc",
"licenses": [
{
"type": "Apache License 2.0",
"confidence": 0.9647812166488794
}
]
},
{
"project": "go.opentelemetry.io/otel/metric",
"licenses": [
{
"type": "Apache License 2.0",
"confidence": 0.9647812166488794
}
]
},
{
"project": "go.opentelemetry.io/otel/sdk",
"licenses": [
{
"type": "Apache License 2.0",
"confidence": 0.9647812166488794
}
]
},
{
"project": "go.opentelemetry.io/otel/trace",
"licenses": [
{
"type": "Apache License 2.0",
"confidence": 0.9647812166488794
}
]
},
{
"project": "go.opentelemetry.io/proto/otlp",
"licenses": [
{
"type": "Apache License 2.0",
"confidence": 1
}
]
},
{
"project": "go.uber.org/multierr",
"licenses": [
{
"type": "MIT License",
"confidence": 0.9891304347826086
}
]
},
{
"project": "go.uber.org/zap",
"licenses": [
{
"type": "MIT License",
"confidence": 0.9891304347826086
}
]
},
{
"project": "go.yaml.in/yaml/v2",
"licenses": [
{
"type": "Apache License 2.0",
"confidence": 1
},
{
"type": "MIT License",
"confidence": 0.8975609756097561
}
]
},
{
"project": "golang.org/x/crypto",
"licenses": [
{
"type": "BSD 3-clause \"New\" or \"Revised\" License",
"confidence": 0.9663865546218487
}
]
},
{
"project": "golang.org/x/mod/semver",
"licenses": [
{
"type": "BSD 3-clause \"New\" or \"Revised\" License",
"confidence": 0.9663865546218487
}
]
},
{
"project": "golang.org/x/net",
"licenses": [
{
"type": "BSD 3-clause \"New\" or \"Revised\" License",
"confidence": 0.9663865546218487
}
]
},
{
"project": "golang.org/x/sync/errgroup",
"licenses": [
{
"type": "BSD 3-clause \"New\" or \"Revised\" License",
"confidence": 0.9663865546218487
}
]
},
{
"project": "golang.org/x/sys",
"licenses": [
{
"type": "BSD 3-clause \"New\" or \"Revised\" License",
"confidence": 0.9663865546218487
}
]
},
{
"project": "golang.org/x/text",
"licenses": [
{
"type": "BSD 3-clause \"New\" or \"Revised\" License",
"confidence": 0.9663865546218487
}
]
},
{
"project": "golang.org/x/time/rate",
"licenses": [
{
"type": "BSD 3-clause \"New\" or \"Revised\" License",
"confidence": 0.9663865546218487
}
]
},
{
"project": "golang.org/x/tools",
"licenses": [
{
"type": "BSD 3-clause \"New\" or \"Revised\" License",
"confidence": 0.9663865546218487
}
]
},
{
"project": "google.golang.org/genproto/googleapis/api",
"licenses": [
{
"type": "Apache License 2.0",
"confidence": 1
}
]
},
{
"project": "google.golang.org/genproto/googleapis/rpc",
"licenses": [
{
"type": "Apache License 2.0",
"confidence": 1
}
]
},
{
"project": "google.golang.org/grpc",
"licenses": [
{
"type": "Apache License 2.0",
"confidence": 1
}
]
},
{
"project": "google.golang.org/protobuf",
"licenses": [
{
"type": "BSD 3-clause \"New\" or \"Revised\" License",
"confidence": 0.9663865546218487
}
]
},
{
"project": "gopkg.in/natefinch/lumberjack.v2",
"licenses": [
{
"type": "MIT License",
"confidence": 1
}
]
},
{
"project": "gopkg.in/yaml.v3",
"licenses": [
{
"type": "MIT License",
"confidence": 0.7469879518072289
}
]
},
{
"project": "k8s.io/utils/internal/third_party/forked/golang/golang-lru",
"licenses": [
{
"type": "BSD 3-clause \"New\" or \"Revised\" License",
"confidence": 0.9663865546218487
}
]
},
{
"project": "k8s.io/utils/lru",
"licenses": [
{
"type": "Apache License 2.0",
"confidence": 1
}
]
},
{
"project": "k8s.io/utils/third_party/forked/golang/btree",
"licenses": [
{
"type": "Apache License 2.0",
"confidence": 1
}
]
},
{
"project": "sigs.k8s.io/yaml",
"licenses": [
{
"type": "Apache License 2.0",
"confidence": 1
},
{
"type": "BSD 3-clause \"New\" or \"Revised\" License",
"confidence": 1
}
]
}
]
================================================
FILE: bill-of-materials.override.json
================================================
[
{
"project": "sigs.k8s.io/yaml",
"licenses": [
{
"type": "BSD 3-clause \"New\" or \"Revised\" License"
}
]
},
{
"project": "github.com/grpc-ecosystem/go-grpc-middleware/v2/interceptors",
"licenses": [
{
"type": "Apache License 2.0",
"confidence": 1
}
]
},
{
"project": "github.com/inconshreveable/mousetrap",
"licenses": [
{
"type": "Apache License 2.0"
}
]
}
]
================================================
FILE: cache/LICENSE
================================================
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright 2020 The etcd Authors
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.
================================================
FILE: cache/OWNERS
================================================
# See the OWNERS docs at https://go.k8s.io/owners
labels:
- area/cache
================================================
FILE: cache/README.md
================================================
# etcd cache
Experimental etcd client cache library.
================================================
FILE: cache/cache.go
================================================
// Copyright 2025 The etcd Authors
//
// 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 cache
import (
"bytes"
"context"
"errors"
"fmt"
"sync"
"time"
pb "go.etcd.io/etcd/api/v3/etcdserverpb"
clientv3 "go.etcd.io/etcd/client/v3"
)
var (
// Returned when an option combination isn’t yet handled by the cache (e.g. WithPrevKV, WithProgressNotify for Watch(), WithCountOnly for Get()).
ErrUnsupportedRequest = errors.New("cache: unsupported request parameters")
// Returned when the requested key or key‑range is invalid (empty or reversed) or lies outside c.prefix.
ErrKeyRangeInvalid = errors.New("cache: invalid or out‑of‑range key range")
)
// Cache buffers a single etcd Watch for a given key‐prefix and fan‑outs local watchers.
type Cache struct {
prefix string // prefix is the key-prefix this shard is responsible for ("" = root).
cfg Config // immutable runtime configuration
watcher clientv3.Watcher
kv clientv3.KV
demux *demux // demux fans incoming events out to active watchers and manages resync.
store *store // last‑observed snapshot
ready *ready
stop context.CancelFunc
waitGroup sync.WaitGroup
internalCtx context.Context
}
// New builds a cache shard that watches only the requested prefix.
// For the root cache pass "".
func New(client *clientv3.Client, prefix string, opts ...Option) (*Cache, error) {
cfg := defaultConfig()
for _, opt := range opts {
opt(&cfg)
}
if cfg.HistoryWindowSize <= 0 {
return nil, fmt.Errorf("invalid HistoryWindowSize %d (must be > 0)", cfg.HistoryWindowSize)
}
if cfg.BTreeDegree < 2 {
return nil, fmt.Errorf("invalid BTreeDegree %d (must be >= 2)", cfg.BTreeDegree)
}
internalCtx, cancel := context.WithCancel(context.Background())
cache := &Cache{
prefix: prefix,
cfg: cfg,
watcher: client.Watcher,
kv: client.KV,
store: newStore(cfg.BTreeDegree, cfg.HistoryWindowSize),
ready: newReady(),
stop: cancel,
internalCtx: internalCtx,
}
cache.demux = NewDemux(internalCtx, &cache.waitGroup, cfg.HistoryWindowSize, cfg.ResyncInterval)
cache.waitGroup.Add(1)
go func() {
defer cache.waitGroup.Done()
cache.getWatchLoop()
}()
return cache, nil
}
// Watch registers a cache-backed watcher for a given key or prefix.
// It returns a WatchChan that streams WatchResponses containing events.
func (c *Cache) Watch(ctx context.Context, key string, opts ...clientv3.OpOption) clientv3.WatchChan {
if err := c.WaitReady(ctx); err != nil {
emptyWatchChan := make(chan clientv3.WatchResponse)
close(emptyWatchChan)
return emptyWatchChan
}
op := clientv3.OpWatch(key, opts...)
startRev := op.Rev()
pred, err := c.validateWatch(key, op)
if err != nil {
ch := make(chan clientv3.WatchResponse, 1)
ch <- clientv3.WatchResponse{Canceled: true, CancelReason: err.Error()}
close(ch)
return ch
}
w := newWatcher(c.cfg.PerWatcherBufferSize, pred)
c.demux.Register(w, startRev)
responseChan := make(chan clientv3.WatchResponse)
c.waitGroup.Add(1)
go func() {
defer c.waitGroup.Done()
defer close(responseChan)
defer c.demux.Unregister(w)
for {
select {
case <-ctx.Done():
return
case <-c.internalCtx.Done():
return
case resp, ok := <-w.respCh:
if !ok {
if w.cancelResp != nil {
select {
case <-ctx.Done():
case <-c.internalCtx.Done():
case responseChan <- *w.cancelResp:
}
}
return
}
select {
case <-ctx.Done():
return
case <-c.internalCtx.Done():
return
case responseChan <- resp:
}
}
}
}()
return responseChan
}
func (c *Cache) Get(ctx context.Context, key string, opts ...clientv3.OpOption) (*clientv3.GetResponse, error) {
if c.store.LatestRev() == 0 {
if err := c.WaitReady(ctx); err != nil {
return nil, err
}
}
op := clientv3.OpGet(key, opts...)
if _, err := c.validateGet(key, op); err != nil {
return nil, err
}
startKey := []byte(key)
endKey := op.RangeBytes()
requestedRev := op.Rev()
kvs, latestRev, err := c.store.Get(startKey, endKey, requestedRev)
if err != nil {
return nil, err
}
return &clientv3.GetResponse{
Header: &pb.ResponseHeader{Revision: latestRev},
Kvs: kvs,
Count: int64(len(kvs)),
}, nil
}
// Ready returns true if the snapshot has been loaded and the first watch has been confirmed.
func (c *Cache) Ready() bool {
return c.ready.Ready()
}
// WaitReady blocks until the cache is ready or the ctx is cancelled.
func (c *Cache) WaitReady(ctx context.Context) error {
return c.ready.WaitReady(ctx)
}
func (c *Cache) WaitForRevision(ctx context.Context, rev int64) error {
for {
if c.store.LatestRev() >= rev {
return nil
}
select {
case <-time.After(10 * time.Millisecond):
case <-ctx.Done():
return ctx.Err()
}
}
}
// Close cancels the private context and blocks until all goroutines return.
func (c *Cache) Close() {
c.stop()
c.waitGroup.Wait()
}
func (c *Cache) getWatchLoop() {
cfg := defaultConfig()
ctx := c.internalCtx
backoff := cfg.InitialBackoff
for {
if err := ctx.Err(); err != nil {
return
}
if err := c.getWatch(); err != nil {
fmt.Printf("getWatch failed, will retry after %v: %v\n", backoff, err)
}
select {
case <-ctx.Done():
return
case <-time.After(backoff):
}
}
}
func (c *Cache) getWatch() error {
getResp, err := c.get(c.internalCtx)
if err != nil {
return err
}
return c.watch(getResp.Header.Revision + 1)
}
func (c *Cache) get(ctx context.Context) (*clientv3.GetResponse, error) {
resp, err := c.kv.Get(ctx, c.prefix, clientv3.WithPrefix())
if err != nil {
return nil, err
}
c.store.Restore(resp.Kvs, resp.Header.Revision)
return resp, nil
}
func (c *Cache) watch(rev int64) error {
readyOnce := sync.Once{}
for {
storeW := newWatcher(c.cfg.PerWatcherBufferSize, nil)
c.demux.Register(storeW, rev)
applyErr := make(chan error, 1)
c.waitGroup.Add(1)
go func() {
defer c.waitGroup.Done()
if err := c.applyStorage(storeW); err != nil {
applyErr <- err
}
close(applyErr)
}()
err := c.watchEvents(rev, applyErr, &readyOnce)
c.demux.Unregister(storeW)
if err != nil {
return err
}
}
}
func (c *Cache) applyStorage(storeW *watcher) error {
for {
select {
case <-c.internalCtx.Done():
return nil
case resp, ok := <-storeW.respCh:
if !ok {
return nil
}
if err := c.store.Apply(resp); err != nil {
return err
}
}
}
}
func (c *Cache) watchEvents(rev int64, applyErr <-chan error, readyOnce *sync.Once) error {
watchCh := c.watcher.Watch(
c.internalCtx,
c.prefix,
clientv3.WithPrefix(),
clientv3.WithRev(rev),
clientv3.WithProgressNotify(),
clientv3.WithCreatedNotify(),
)
for {
select {
case <-c.internalCtx.Done():
return c.internalCtx.Err()
case resp, ok := <-watchCh:
if !ok {
return nil
}
readyOnce.Do(func() {
c.demux.Init(rev)
c.ready.Set()
})
if err := resp.Err(); err != nil {
c.ready.Reset()
return err
}
err := c.demux.Broadcast(resp)
if err != nil {
c.ready.Reset()
return err
}
case err := <-applyErr:
c.ready.Reset()
return err
}
}
}
func (c *Cache) validateWatch(key string, op clientv3.Op) (pred KeyPredicate, err error) {
switch {
case op.IsPrevKV():
return nil, fmt.Errorf("%w: PrevKV not supported", ErrUnsupportedRequest)
case op.IsFragment():
return nil, fmt.Errorf("%w: Fragment not supported", ErrUnsupportedRequest)
case op.IsProgressNotify():
return nil, fmt.Errorf("%w: ProgressNotify not supported", ErrUnsupportedRequest)
case op.IsCreatedNotify():
return nil, fmt.Errorf("%w: CreatedNotify not supported", ErrUnsupportedRequest)
case op.IsFilterPut():
return nil, fmt.Errorf("%w: FilterPut not supported", ErrUnsupportedRequest)
case op.IsFilterDelete():
return nil, fmt.Errorf("%w: FilterDelete not supported", ErrUnsupportedRequest)
}
startKey := []byte(key)
endKey := op.RangeBytes() // nil = single key, {0}=FromKey, else explicit range
if err := c.validateRange(startKey, endKey); err != nil {
return nil, err
}
return KeyPredForRange(startKey, endKey), nil
}
func (c *Cache) validateGet(key string, op clientv3.Op) (KeyPredicate, error) {
switch {
case op.IsCountOnly():
return nil, fmt.Errorf("%w: CountOnly not supported", ErrUnsupportedRequest)
case op.IsPrevKV():
return nil, fmt.Errorf("%w: PrevKV not supported", ErrUnsupportedRequest)
case op.IsSortSet():
return nil, fmt.Errorf("%w: SortSet not supported", ErrUnsupportedRequest)
case op.Limit() != 0:
return nil, fmt.Errorf("%w: Limit(%d) not supported", ErrUnsupportedRequest, op.Limit())
case op.MinModRev() != 0:
return nil, fmt.Errorf("%w: MinModRev(%d) not supported", ErrUnsupportedRequest, op.MinModRev())
case op.MaxModRev() != 0:
return nil, fmt.Errorf("%w: MaxModRev(%d) not supported", ErrUnsupportedRequest, op.MaxModRev())
case op.MinCreateRev() != 0:
return nil, fmt.Errorf("%w: MinCreateRev(%d) not supported", ErrUnsupportedRequest, op.MinCreateRev())
case op.MaxCreateRev() != 0:
return nil, fmt.Errorf("%w: MaxCreateRev(%d) not supported", ErrUnsupportedRequest, op.MaxCreateRev())
// cache now only serves serializable reads of the latest revision (rev == 0).
case !op.IsSerializable():
return nil, fmt.Errorf("%w: non-serializable request", ErrUnsupportedRequest)
}
startKey := []byte(key)
endKey := op.RangeBytes()
if err := c.validateRange(startKey, endKey); err != nil {
return nil, err
}
return KeyPredForRange(startKey, endKey), nil
}
func (c *Cache) validateRange(startKey, endKey []byte) error {
prefixStart := []byte(c.prefix)
prefixEnd := []byte(clientv3.GetPrefixRangeEnd(c.prefix))
isSingleKey := len(endKey) == 0
isFromKey := len(endKey) == 1 && endKey[0] == 0
switch {
case isSingleKey:
if c.prefix == "" {
return nil
}
if bytes.Compare(startKey, prefixStart) < 0 || bytes.Compare(startKey, prefixEnd) >= 0 {
return ErrKeyRangeInvalid
}
return nil
case isFromKey:
if c.prefix != "" {
return ErrKeyRangeInvalid
}
return nil
default:
if bytes.Compare(endKey, startKey) <= 0 {
return ErrKeyRangeInvalid
}
if c.prefix == "" {
return nil
}
if bytes.Compare(startKey, prefixStart) < 0 || bytes.Compare(endKey, prefixEnd) > 0 {
return ErrKeyRangeInvalid
}
return nil
}
}
// WaitForNextResync blocks until the next resync loop iteration is complete.
func (c *Cache) WaitForNextResync(ctx context.Context) error {
return c.demux.WaitForNextResync(ctx)
}
================================================
FILE: cache/cache_test.go
================================================
// Copyright 2025 The etcd Authors
//
// 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 cache
import (
"context"
"sync"
"testing"
"time"
"github.com/google/go-cmp/cmp"
pb "go.etcd.io/etcd/api/v3/etcdserverpb"
mvccpb "go.etcd.io/etcd/api/v3/mvccpb"
clientv3 "go.etcd.io/etcd/client/v3"
)
func TestCacheWatchAtomicOrderedDelivery(t *testing.T) {
tests := []struct {
name string
sentBatches [][]*clientv3.Event
wantBatch []*clientv3.Event
}{
{
name: "single_event",
sentBatches: [][]*clientv3.Event{
{event(mvccpb.Event_PUT, "/a", 5)},
},
wantBatch: []*clientv3.Event{
event(mvccpb.Event_PUT, "/a", 5),
},
},
{
name: "same_revision_batch",
sentBatches: [][]*clientv3.Event{
{
event(mvccpb.Event_PUT, "/a", 10),
event(mvccpb.Event_PUT, "/b", 10),
},
},
wantBatch: []*clientv3.Event{
event(mvccpb.Event_PUT, "/a", 10),
event(mvccpb.Event_PUT, "/b", 10),
},
},
{
name: "mixed_revisions_in_single_response",
sentBatches: [][]*clientv3.Event{
{
event(mvccpb.Event_PUT, "/a", 11),
event(mvccpb.Event_PUT, "/b", 11),
event(mvccpb.Event_PUT, "/c", 12),
},
},
wantBatch: []*clientv3.Event{
event(mvccpb.Event_PUT, "/a", 11),
event(mvccpb.Event_PUT, "/b", 11),
event(mvccpb.Event_PUT, "/c", 12),
},
},
{
name: "mixed_event_types_same_revision",
sentBatches: [][]*clientv3.Event{
{
event(mvccpb.Event_PUT, "/x", 5),
event(mvccpb.Event_PUT, "/y", 6),
event(mvccpb.Event_DELETE, "/x", 6),
},
},
wantBatch: []*clientv3.Event{
event(mvccpb.Event_PUT, "/x", 5),
event(mvccpb.Event_PUT, "/y", 6),
event(mvccpb.Event_DELETE, "/x", 6),
},
},
{
name: "all_events_in_one_response",
sentBatches: [][]*clientv3.Event{
{
event(mvccpb.Event_PUT, "/a", 2),
event(mvccpb.Event_PUT, "/b", 2),
event(mvccpb.Event_PUT, "/c", 3),
event(mvccpb.Event_PUT, "/d", 4),
event(mvccpb.Event_PUT, "/e", 4),
event(mvccpb.Event_PUT, "/f", 5),
event(mvccpb.Event_PUT, "/g", 6),
event(mvccpb.Event_PUT, "/h", 6),
event(mvccpb.Event_PUT, "/i", 7),
event(mvccpb.Event_PUT, "/j", 7),
},
},
wantBatch: []*clientv3.Event{
event(mvccpb.Event_PUT, "/a", 2),
event(mvccpb.Event_PUT, "/b", 2),
event(mvccpb.Event_PUT, "/c", 3),
event(mvccpb.Event_PUT, "/d", 4),
event(mvccpb.Event_PUT, "/e", 4),
event(mvccpb.Event_PUT, "/f", 5),
event(mvccpb.Event_PUT, "/g", 6),
event(mvccpb.Event_PUT, "/h", 6),
event(mvccpb.Event_PUT, "/i", 7),
event(mvccpb.Event_PUT, "/j", 7),
},
},
{
name: "one_revision_group_per_response",
sentBatches: [][]*clientv3.Event{
{event(mvccpb.Event_PUT, "/a", 2), event(mvccpb.Event_PUT, "/b", 2)},
{event(mvccpb.Event_PUT, "/c", 3)},
{event(mvccpb.Event_PUT, "/d", 4), event(mvccpb.Event_PUT, "/e", 4)},
{event(mvccpb.Event_PUT, "/f", 5)},
{event(mvccpb.Event_PUT, "/g", 6), event(mvccpb.Event_PUT, "/h", 6)},
{event(mvccpb.Event_PUT, "/i", 7), event(mvccpb.Event_PUT, "/j", 7)},
},
wantBatch: []*clientv3.Event{
event(mvccpb.Event_PUT, "/a", 2),
event(mvccpb.Event_PUT, "/b", 2),
event(mvccpb.Event_PUT, "/c", 3),
event(mvccpb.Event_PUT, "/d", 4),
event(mvccpb.Event_PUT, "/e", 4),
event(mvccpb.Event_PUT, "/f", 5),
event(mvccpb.Event_PUT, "/g", 6),
event(mvccpb.Event_PUT, "/h", 6),
event(mvccpb.Event_PUT, "/i", 7),
event(mvccpb.Event_PUT, "/j", 7),
},
},
{
name: "two_revision_groups_per_response",
sentBatches: [][]*clientv3.Event{
{event(mvccpb.Event_PUT, "/a", 2), event(mvccpb.Event_PUT, "/b", 2), event(mvccpb.Event_PUT, "/c", 3)},
{event(mvccpb.Event_PUT, "/d", 4), event(mvccpb.Event_PUT, "/e", 4), event(mvccpb.Event_PUT, "/f", 5)},
{event(mvccpb.Event_PUT, "/g", 6), event(mvccpb.Event_PUT, "/h", 6)},
{event(mvccpb.Event_PUT, "/i", 7), event(mvccpb.Event_PUT, "/j", 7)},
},
wantBatch: []*clientv3.Event{
event(mvccpb.Event_PUT, "/a", 2),
event(mvccpb.Event_PUT, "/b", 2),
event(mvccpb.Event_PUT, "/c", 3),
event(mvccpb.Event_PUT, "/d", 4),
event(mvccpb.Event_PUT, "/e", 4),
event(mvccpb.Event_PUT, "/f", 5),
event(mvccpb.Event_PUT, "/g", 6),
event(mvccpb.Event_PUT, "/h", 6),
event(mvccpb.Event_PUT, "/i", 7),
event(mvccpb.Event_PUT, "/j", 7),
},
},
{
name: "three_revision_groups_per_response",
sentBatches: [][]*clientv3.Event{
{
event(mvccpb.Event_PUT, "/a", 2), event(mvccpb.Event_PUT, "/b", 2),
event(mvccpb.Event_PUT, "/c", 3),
event(mvccpb.Event_PUT, "/d", 4), event(mvccpb.Event_PUT, "/e", 4),
},
{
event(mvccpb.Event_PUT, "/f", 5),
event(mvccpb.Event_PUT, "/g", 6), event(mvccpb.Event_PUT, "/h", 6),
event(mvccpb.Event_PUT, "/i", 7), event(mvccpb.Event_PUT, "/j", 7),
},
},
wantBatch: []*clientv3.Event{
event(mvccpb.Event_PUT, "/a", 2),
event(mvccpb.Event_PUT, "/b", 2),
event(mvccpb.Event_PUT, "/c", 3),
event(mvccpb.Event_PUT, "/d", 4),
event(mvccpb.Event_PUT, "/e", 4),
event(mvccpb.Event_PUT, "/f", 5),
event(mvccpb.Event_PUT, "/g", 6),
event(mvccpb.Event_PUT, "/h", 6),
event(mvccpb.Event_PUT, "/i", 7),
event(mvccpb.Event_PUT, "/j", 7),
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
mw := newMockWatcher(16)
fakeClient := &clientv3.Client{
Watcher: mw,
KV: newKVStub(),
}
cache, err := New(fakeClient, "")
if err != nil {
t.Fatalf("New cache: %v", err)
}
if err != nil {
t.Fatalf("New cache: %v", err)
}
defer cache.Close()
mw.responses <- clientv3.WatchResponse{}
<-mw.registered
ctxWait, cancelWait := context.WithTimeout(t.Context(), time.Second)
if err := cache.WaitReady(ctxWait); err != nil {
t.Fatalf("cache did not become Ready(): %v", err)
}
cancelWait()
ctx, cancel := context.WithTimeout(t.Context(), 2*time.Second)
defer cancel()
watchCh := cache.Watch(ctx, "", clientv3.WithPrefix())
for _, batch := range tt.sentBatches {
mw.responses <- clientv3.WatchResponse{Events: batch}
}
close(mw.responses)
got := collectAndAssertAtomicEvents(ctx, t, watchCh, len(tt.wantBatch))
if diff := cmp.Diff(tt.wantBatch, got); diff != "" {
t.Fatalf("event mismatch (-want +got):\n%s", diff)
}
})
}
}
func TestValidateWatchRange(t *testing.T) {
type tc struct {
name string
watchKey string
opts []clientv3.OpOption
cachePrefix string
wantErr bool
}
tests := []tc{
{
name: "single key",
watchKey: "/a",
cachePrefix: "",
wantErr: false,
},
{
name: "prefix single key",
watchKey: "/foo/a",
cachePrefix: "/foo",
wantErr: false,
},
{
name: "single key outside prefix returns error",
watchKey: "/z",
cachePrefix: "/foo",
wantErr: true,
},
{
name: "explicit range",
watchKey: "/a",
opts: []clientv3.OpOption{clientv3.WithRange("/b")},
cachePrefix: "",
wantErr: false,
},
{
name: "exact prefix range",
watchKey: "/a",
opts: []clientv3.OpOption{clientv3.WithRange("/b")},
cachePrefix: "/a",
wantErr: false,
},
{
name: "prefix subrange",
watchKey: "/foo",
opts: []clientv3.OpOption{clientv3.WithRange("/foo/a")},
cachePrefix: "/foo",
wantErr: false,
},
{
name: "reverse range returns error",
watchKey: "/b",
opts: []clientv3.OpOption{clientv3.WithRange("/a")},
cachePrefix: "",
wantErr: true,
},
{
name: "empty range returns error",
watchKey: "/foo",
opts: []clientv3.OpOption{clientv3.WithRange("/foo")},
cachePrefix: "",
wantErr: true,
},
{
name: "range starting below cache prefix returns error",
watchKey: "/a",
opts: []clientv3.OpOption{clientv3.WithRange("/foo")},
cachePrefix: "/foo",
wantErr: true,
},
{
name: "range encompassing cache prefix returns error",
watchKey: "/a",
opts: []clientv3.OpOption{clientv3.WithRange("/z")},
cachePrefix: "/foo",
wantErr: true,
},
{
name: "range crossing prefixEnd returns error",
watchKey: "/foo",
opts: []clientv3.OpOption{clientv3.WithRange("/z")},
cachePrefix: "/foo",
wantErr: true,
},
{
name: "empty prefix",
watchKey: "",
opts: []clientv3.OpOption{clientv3.WithPrefix()},
cachePrefix: "",
wantErr: false,
},
{
name: "empty prefix with cachePrefix returns error",
watchKey: "",
opts: []clientv3.OpOption{clientv3.WithPrefix()},
cachePrefix: "/foo",
wantErr: true,
},
{
name: "prefix watch matches cachePrefix exactly",
watchKey: "/foo",
opts: []clientv3.OpOption{clientv3.WithPrefix()},
cachePrefix: "/foo",
wantErr: false,
},
{
name: "prefix watch inside cachePrefix",
watchKey: "/foo/bar",
opts: []clientv3.OpOption{clientv3.WithPrefix()},
cachePrefix: "/foo",
wantErr: false,
},
{
name: "prefix starting below cachePrefix returns error",
watchKey: "/a",
opts: []clientv3.OpOption{clientv3.WithPrefix()},
cachePrefix: "/foo",
wantErr: true,
},
{
name: "prefix starting above shard prefixEnd returns error",
watchKey: "/fop",
opts: []clientv3.OpOption{clientv3.WithPrefix()},
cachePrefix: "/foo",
wantErr: true,
},
{
name: "fromKey open‑ended",
watchKey: "/a",
opts: []clientv3.OpOption{clientv3.WithFromKey()},
cachePrefix: "",
wantErr: false,
},
{
name: "fromKey starting at prefix start",
watchKey: "/foo",
opts: []clientv3.OpOption{clientv3.WithFromKey()},
cachePrefix: "/foo",
wantErr: true,
},
{
name: "fromKey starting below prefixEnd",
watchKey: "/a",
opts: []clientv3.OpOption{clientv3.WithFromKey()},
cachePrefix: "/foo",
wantErr: true,
},
{
name: "fromKey starting above prefixEnd returns error",
watchKey: "/fop",
opts: []clientv3.OpOption{clientv3.WithFromKey()},
cachePrefix: "/foo",
wantErr: true,
},
}
for _, c := range tests {
t.Run(c.name, func(t *testing.T) {
dummyCache := &Cache{prefix: c.cachePrefix}
op := clientv3.OpGet(c.watchKey, c.opts...)
err := dummyCache.validateRange([]byte(c.watchKey), op.RangeBytes())
if gotErr := err != nil; gotErr != c.wantErr {
t.Fatalf("validateWatchRange(%q, %q, %v) err=%v, wantErr=%v",
c.cachePrefix, c.watchKey, c.opts, err, c.wantErr)
}
})
}
}
func TestCacheCompactionResync(t *testing.T) {
firstSnapshot := &clientv3.GetResponse{
Header: &pb.ResponseHeader{Revision: 5},
Kvs: []*mvccpb.KeyValue{
{Key: []byte("foo"), Value: []byte("old_value"), ModRevision: 5, CreateRevision: 5, Version: 1},
{Key: []byte("bar"), Value: []byte("old_bar"), ModRevision: 3, CreateRevision: 3, Version: 1},
},
}
secondSnapshot := &clientv3.GetResponse{
Header: &pb.ResponseHeader{Revision: 20},
Kvs: []*mvccpb.KeyValue{
{Key: []byte("foo"), Value: []byte("new_value"), ModRevision: 20, CreateRevision: 5, Version: 2},
{Key: []byte("baz"), Value: []byte("new_baz"), ModRevision: 18, CreateRevision: 18, Version: 1},
},
}
fakeClient := &clientv3.Client{
Watcher: newMockWatcher(16),
KV: newKVStub(firstSnapshot, secondSnapshot),
}
cache, err := New(fakeClient, "")
if err != nil {
t.Fatalf("New cache: %v", err)
}
defer cache.Close()
mw := fakeClient.Watcher.(*mockWatcher)
t.Log("Phase 1: initial getWatch bootstrap")
mw.triggerCreatedNotify()
<-mw.registered
if err = cache.WaitReady(t.Context()); err != nil {
t.Fatalf("initial WaitReady: %v", err)
}
verifySnapshot(t, cache, []*mvccpb.KeyValue{
{Key: []byte("bar"), Value: []byte("old_bar"), ModRevision: 3, CreateRevision: 3, Version: 1},
{Key: []byte("foo"), Value: []byte("old_value"), ModRevision: 5, CreateRevision: 5, Version: 1},
})
t.Log("Phase 2: simulate compaction")
mw.errorCompacted(10)
waitUntil(t, time.Second, 10*time.Millisecond, func() bool { return !cache.Ready() })
ctxGet, cancelGet := context.WithTimeout(t.Context(), 100*time.Millisecond)
defer cancelGet()
snapshot, err := cache.Get(ctxGet, "foo", clientv3.WithSerializable())
if err != nil {
t.Fatalf("expected Get() to serve from cached snapshot after compaction, got %v", err)
}
if got := snapshot.Header.Revision; got != firstSnapshot.Header.Revision {
t.Fatalf("expected cached revision %d after compaction, got %d", firstSnapshot.Header.Revision, got)
}
if string(snapshot.Kvs[0].Value) != "old_value" {
t.Fatalf("expected cached value 'old_value' during compaction, got %q", string(snapshot.Kvs[0].Value))
}
t.Log("Phase 3: resync after compaction")
mw.resetRegistered()
mw.triggerCreatedNotify()
<-mw.registered
expectSnapshotRev := int64(20)
ctxResync, cancelResync := context.WithTimeout(t.Context(), time.Second)
defer cancelResync()
if err = cache.WaitForRevision(ctxResync, expectSnapshotRev); err != nil {
t.Fatalf("cache failed to resync to rev=%d within 1s: %v", expectSnapshotRev, err)
}
expectedWatchStart := secondSnapshot.Header.Revision + 1
if gotWatchStart := mw.getLastStartRev(); gotWatchStart != expectedWatchStart {
t.Errorf("Watch started at rev=%d; want %d", gotWatchStart, expectedWatchStart)
}
gotSnapshot, err := cache.Get(t.Context(), "foo", clientv3.WithSerializable())
if err != nil {
t.Fatalf("Get after resync: %v", err)
}
if gotSnapshot.Header.Revision != expectSnapshotRev {
t.Errorf("unexpected Snapshot revision: got=%d, want=%d", gotSnapshot.Header.Revision, expectSnapshotRev)
}
verifySnapshot(t, cache, []*mvccpb.KeyValue{
{Key: []byte("baz"), Value: []byte("new_baz"), ModRevision: 18, CreateRevision: 18, Version: 1},
{Key: []byte("foo"), Value: []byte("new_value"), ModRevision: 20, CreateRevision: 5, Version: 2},
})
}
func waitUntil(t *testing.T, timeout, poll time.Duration, cond func() bool) {
deadline := time.Now().Add(timeout)
for time.Now().Before(deadline) {
if cond() {
return
}
time.Sleep(poll)
}
t.Fatalf("condition not satisfied within %s", timeout)
}
type mockWatcher struct {
responses chan clientv3.WatchResponse
registered chan struct{}
closeOnce sync.Once
wg sync.WaitGroup
mu sync.Mutex
lastStartRev int64
}
func newMockWatcher(buf int) *mockWatcher {
return &mockWatcher{
responses: make(chan clientv3.WatchResponse, buf),
registered: make(chan struct{}),
}
}
func (m *mockWatcher) Watch(ctx context.Context, _ string, opts ...clientv3.OpOption) clientv3.WatchChan {
rev := m.extractRev(opts)
m.recordStartRev(rev)
m.signalRegistration()
out := make(chan clientv3.WatchResponse)
m.wg.Add(1)
go m.streamResponses(ctx, out)
return out
}
func (m *mockWatcher) RequestProgress(_ context.Context) error { return nil }
func (m *mockWatcher) Close() error {
m.closeOnce.Do(func() { close(m.responses) })
m.wg.Wait()
return nil
}
func (m *mockWatcher) triggerCreatedNotify() { m.responses <- clientv3.WatchResponse{} }
func (m *mockWatcher) errorCompacted(compRev int64) {
m.responses <- clientv3.WatchResponse{
Canceled: true,
CompactRevision: compRev,
}
}
func (m *mockWatcher) extractRev(opts []clientv3.OpOption) int64 {
var op clientv3.Op
for _, o := range opts {
o(&op)
}
return op.Rev()
}
func (m *mockWatcher) recordStartRev(rev int64) {
m.mu.Lock()
defer m.mu.Unlock()
m.lastStartRev = rev
}
func (m *mockWatcher) getLastStartRev() int64 {
m.mu.Lock()
defer m.mu.Unlock()
return m.lastStartRev
}
func (m *mockWatcher) signalRegistration() {
m.mu.Lock()
defer m.mu.Unlock()
select {
case <-m.registered:
default:
close(m.registered)
}
}
func (m *mockWatcher) resetRegistered() {
m.mu.Lock()
defer m.mu.Unlock()
m.registered = make(chan struct{})
}
func (m *mockWatcher) streamResponses(ctx context.Context, out chan<- clientv3.WatchResponse) {
defer func() {
close(out)
m.wg.Done()
}()
for {
select {
case <-ctx.Done():
return
case resp, ok := <-m.responses:
if !ok {
return
}
out <- resp
if resp.Canceled {
return
}
}
}
}
type kvStub struct {
queued []*clientv3.GetResponse
defaultResp *clientv3.GetResponse
}
func newKVStub(resps ...*clientv3.GetResponse) *kvStub {
queue := append([]*clientv3.GetResponse(nil), resps...)
return &kvStub{
queued: queue,
defaultResp: &clientv3.GetResponse{Header: &pb.ResponseHeader{Revision: 0}},
}
}
func (s *kvStub) Get(ctx context.Context, key string, _ ...clientv3.OpOption) (*clientv3.GetResponse, error) {
if len(s.queued) > 0 {
next := s.queued[0]
s.queued = s.queued[1:]
return next, nil
}
return s.defaultResp, nil
}
func (s *kvStub) Put(ctx context.Context, key, val string, _ ...clientv3.OpOption) (*clientv3.PutResponse, error) {
return nil, nil
}
func (s *kvStub) Delete(ctx context.Context, key string, _ ...clientv3.OpOption) (*clientv3.DeleteResponse, error) {
return nil, nil
}
func (s *kvStub) Compact(ctx context.Context, rev int64, _ ...clientv3.CompactOption) (*clientv3.CompactResponse, error) {
return nil, nil
}
func (s *kvStub) Do(ctx context.Context, op clientv3.Op) (clientv3.OpResponse, error) {
return clientv3.OpResponse{}, nil
}
func (s *kvStub) Txn(ctx context.Context) clientv3.Txn {
return nil
}
func event(eventType mvccpb.Event_EventType, key string, rev int64) *clientv3.Event {
return &clientv3.Event{
Type: eventType,
Kv: &mvccpb.KeyValue{
Key: []byte(key),
ModRevision: rev,
CreateRevision: rev,
Version: 1,
},
}
}
func collectAndAssertAtomicEvents(ctx context.Context, t *testing.T, watchCh clientv3.WatchChan, wantCount int) []*clientv3.Event {
t.Helper()
var events []*clientv3.Event
var lastRevision int64
for {
select {
case <-ctx.Done():
t.Fatalf("timed out waiting for events (%d/%d received)",
len(events), wantCount)
case resp, ok := <-watchCh:
if !ok {
return events
}
if len(resp.Events) != 0 && resp.Events[0].Kv.ModRevision == lastRevision {
t.Fatalf("same revision found as in previous response: %d", lastRevision)
}
for _, ev := range resp.Events {
if ev.Kv.ModRevision < lastRevision {
t.Fatalf("revision went backwards: last %d, now %d", lastRevision, ev.Kv.ModRevision)
}
events = append(events, ev)
lastRevision = ev.Kv.ModRevision
}
if wantCount != 0 && len(events) >= wantCount {
return events
}
}
}
}
func verifySnapshot(t *testing.T, cache *Cache, want []*mvccpb.KeyValue) {
resp, err := cache.Get(t.Context(), "", clientv3.WithPrefix(), clientv3.WithSerializable())
if err != nil {
t.Fatalf("Get all keys: %v", err)
}
if diff := cmp.Diff(want, resp.Kvs); diff != "" {
t.Fatalf("cache snapshot mismatch (-want +got):\n%s", diff)
}
}
================================================
FILE: cache/config.go
================================================
// Copyright 2025 The etcd Authors
//
// 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 cache
import "time"
type Config struct {
// PerWatcherBufferSize caps each watcher’s buffered channel.
// Bigger values tolerate brief client slow-downs at the cost of extra memory.
PerWatcherBufferSize int
// HistoryWindowSize is the max events kept in memory for replay.
// It defines how far back the cache can replay events to lagging watchers
HistoryWindowSize int
// ResyncInterval controls how often the demux attempts to catch a lagging watcher up by replaying events from History.
ResyncInterval time.Duration
// InitialBackoff is the first delay to wait before retrying an upstream etcd Watch after it ends with an error.
InitialBackoff time.Duration
// MaxBackoff caps the exponential back-off between successive upstream watch retries.
MaxBackoff time.Duration
// GetTimeout is the timeout applied to the first Get() used to bootstrap the cache.
GetTimeout time.Duration
// BTreeDegree controls the degree (branching factor) of the in-memory B-tree store.
BTreeDegree int
}
// TODO: tune via performance/load tests.
func defaultConfig() Config {
return Config{
PerWatcherBufferSize: 10,
HistoryWindowSize: 2048,
ResyncInterval: 50 * time.Millisecond,
InitialBackoff: 50 * time.Millisecond,
MaxBackoff: 2 * time.Second,
GetTimeout: 5 * time.Second,
BTreeDegree: 32,
}
}
type Option func(*Config)
func WithPerWatcherBufferSize(n int) Option {
return func(c *Config) { c.PerWatcherBufferSize = n }
}
func WithHistoryWindowSize(n int) Option {
return func(c *Config) { c.HistoryWindowSize = n }
}
func WithResyncInterval(d time.Duration) Option {
return func(c *Config) { c.ResyncInterval = d }
}
func WithInitialBackoff(d time.Duration) Option {
return func(c *Config) { c.InitialBackoff = d }
}
func WithMaxBackoff(d time.Duration) Option {
return func(c *Config) { c.MaxBackoff = d }
}
func WithGetTimeout(d time.Duration) Option {
return func(c *Config) { c.GetTimeout = d }
}
func WithBTreeDegree(n int) Option {
return func(c *Config) { c.BTreeDegree = n }
}
================================================
FILE: cache/demux.go
================================================
// Copyright 2025 The etcd Authors
//
// 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 cache
import (
"context"
"errors"
"sync"
"time"
"go.etcd.io/etcd/api/v3/etcdserverpb"
clientv3 "go.etcd.io/etcd/client/v3"
)
type demux struct {
mu sync.RWMutex
// activeWatchers & laggingWatchers hold the first revision the watcher still needs (nextRev).
activeWatchers map[*watcher]int64
laggingWatchers map[*watcher]int64
resyncInterval time.Duration
// Range of revisions maintained for demux operations, inclusive. Broader than history as event revision is not contious.
// maxRev tracks highest seen revision; minRev sets watcher compaction threshold (updated to evictedRev+1 on history overflow)
minRev, maxRev int64
// History stores events within [minRev, maxRev].
history ringBuffer[[]*clientv3.Event]
// resynced is used to notify that resync loop was completed.
resynced *notifier
}
func NewDemux(ctx context.Context, wg *sync.WaitGroup, historyWindowSize int, resyncInterval time.Duration) *demux {
d := newDemux(historyWindowSize, resyncInterval)
wg.Add(1)
go func() {
defer wg.Done()
d.resyncLoop(ctx)
}()
return d
}
func newDemux(historyWindowSize int, resyncInterval time.Duration) *demux {
return &demux{
activeWatchers: make(map[*watcher]int64),
laggingWatchers: make(map[*watcher]int64),
history: *newRingBuffer(historyWindowSize, func(batch []*clientv3.Event) int64 { return batch[0].Kv.ModRevision }),
resyncInterval: resyncInterval,
resynced: newNotifier(),
}
}
// resyncLoop periodically tries to catch lagging watchers up by replaying events from History.
func (d *demux) resyncLoop(ctx context.Context) {
timer := time.NewTimer(d.resyncInterval)
defer timer.Stop()
for {
select {
case <-ctx.Done():
return
case <-timer.C:
d.resyncLaggingWatchers()
d.resynced.notify()
timer.Reset(d.resyncInterval)
}
}
}
// WaitForNextResync blocks until the next resync loop iteration is complete.
func (d *demux) WaitForNextResync(ctx context.Context) error {
return d.resynced.wait(ctx)
}
func (d *demux) Register(w *watcher, startingRev int64) {
d.mu.Lock()
defer d.mu.Unlock()
if d.maxRev == 0 {
if startingRev == 0 {
d.activeWatchers[w] = 0
} else {
d.laggingWatchers[w] = startingRev
}
return
}
// Special case: 0 means “newest”.
if startingRev == 0 {
startingRev = d.maxRev + 1
}
if startingRev <= d.maxRev {
d.laggingWatchers[w] = startingRev
} else {
d.activeWatchers[w] = startingRev
}
}
func (d *demux) Unregister(w *watcher) {
func() {
d.mu.Lock()
defer d.mu.Unlock()
delete(d.activeWatchers, w)
delete(d.laggingWatchers, w)
}()
w.Stop()
}
func (d *demux) Init(minRev int64) {
d.mu.Lock()
defer d.mu.Unlock()
if minRev == 0 {
return
}
if d.minRev == 0 {
// Watch started for empty demux
d.minRev = minRev
return
}
if d.maxRev == 0 {
// Watch started on initialized demux that never got any event.
d.purge()
d.minRev = minRev
return
}
if minRev == d.maxRev+1 {
// Watch continuing from last revision it observed.
return
}
// Watch opened on revision mismatching dmux last observed revision.
d.purge()
d.minRev = minRev
}
func (d *demux) Broadcast(resp clientv3.WatchResponse) error {
d.mu.Lock()
defer d.mu.Unlock()
if d.minRev == 0 {
return errors.New("demux: not initialized")
}
err := validateRevisions(resp, d.maxRev)
if err != nil {
return err
}
d.updateStoreLocked(resp)
d.broadcastLocked(resp)
return nil
}
func (d *demux) LatestRev() int64 {
d.mu.RLock()
defer d.mu.RUnlock()
return d.maxRev
}
func (d *demux) updateStoreLocked(resp clientv3.WatchResponse) {
if resp.IsProgressNotify() {
d.maxRev = resp.Header.Revision
return
}
if len(resp.Events) == 0 {
return
}
events := resp.Events
batchStart := 0
for end := 1; end < len(events); end++ {
if events[end].Kv.ModRevision != events[batchStart].Kv.ModRevision {
if end > batchStart {
if end+1 == len(events) && d.history.full() {
d.minRev = d.history.PeekOldest() + 1
}
d.history.Append(events[batchStart:end])
}
batchStart = end
}
}
if batchStart < len(events) {
if d.history.full() {
d.minRev = d.history.PeekOldest() + 1
}
d.history.Append(events[batchStart:])
}
d.maxRev = events[len(events)-1].Kv.ModRevision
}
func (d *demux) broadcastLocked(resp clientv3.WatchResponse) {
switch {
case resp.IsProgressNotify():
d.broadcastProgressLocked(resp.Header.Revision)
case len(resp.Events) != 0:
d.broadcastEventsLocked(resp.Events)
default:
}
}
func (d *demux) broadcastProgressLocked(progressRev int64) {
for w, nextRev := range d.activeWatchers {
if nextRev >= progressRev {
continue
}
resp := clientv3.WatchResponse{
Header: etcdserverpb.ResponseHeader{
Revision: progressRev,
},
}
if w.enqueueResponse(resp) {
d.activeWatchers[w] = progressRev + 1
}
}
}
func (d *demux) broadcastEventsLocked(events []*clientv3.Event) {
firstRev := events[0].Kv.ModRevision
lastRev := events[len(events)-1].Kv.ModRevision
for w, nextRev := range d.activeWatchers {
if nextRev != 0 && firstRev > nextRev {
d.laggingWatchers[w] = nextRev
delete(d.activeWatchers, w)
continue
}
sendStart := len(events)
for i, ev := range events {
if ev.Kv.ModRevision >= nextRev {
sendStart = i
break
}
}
if sendStart == len(events) {
continue
}
if !w.enqueueResponse(clientv3.WatchResponse{
Events: events[sendStart:],
}) { // overflow → lagging
d.laggingWatchers[w] = nextRev
delete(d.activeWatchers, w)
} else {
d.activeWatchers[w] = lastRev + 1
}
}
}
// Purge stops all watchers and rebase history on watch errors
func (d *demux) Purge() {
d.mu.Lock()
defer d.mu.Unlock()
d.purge()
}
func (d *demux) purge() {
d.maxRev = 0
d.minRev = 0
d.history.RebaseHistory()
for w := range d.activeWatchers {
w.Stop()
}
for w := range d.laggingWatchers {
w.Stop()
}
d.activeWatchers = make(map[*watcher]int64)
d.laggingWatchers = make(map[*watcher]int64)
}
// Compact is called when etcd reports a compaction at compactRev to rebase history;
// it keeps provably-too-old watchers for later cancellation, stops others, and clients should resubscribe.
func (d *demux) Compact(compactRev int64) {
d.mu.Lock()
defer d.mu.Unlock()
d.purge()
}
func (d *demux) resyncLaggingWatchers() {
d.mu.Lock()
defer d.mu.Unlock()
if d.minRev == 0 {
return
}
for w, nextRev := range d.laggingWatchers {
if nextRev < d.minRev {
w.Compact(nextRev)
delete(d.laggingWatchers, w)
continue
}
// TODO: re-enable key‐predicate in Filter when non‐zero startRev or performance tuning is needed
resyncSuccess := true
d.history.AscendGreaterOrEqual(nextRev, func(rev int64, eventBatch []*clientv3.Event) bool {
resp := clientv3.WatchResponse{
Events: eventBatch,
}
if !w.enqueueResponse(resp) { // buffer overflow: watcher still lagging
resyncSuccess = false
return false
}
nextRev = rev + 1
return true
})
// Send progress to just resync.
if resyncSuccess {
resp := clientv3.WatchResponse{
Header: etcdserverpb.ResponseHeader{Revision: d.maxRev},
}
if d.maxRev > nextRev && w.enqueueResponse(resp) {
nextRev = d.maxRev + 1
}
delete(d.laggingWatchers, w)
d.activeWatchers[w] = nextRev
} else {
d.laggingWatchers[w] = nextRev
}
}
}
func newNotifier() *notifier {
return ¬ifier{
ch: make(chan struct{}),
}
}
type notifier struct {
mu sync.RWMutex
ch chan struct{}
}
func (n *notifier) notify() {
n.mu.Lock()
defer n.mu.Unlock()
previous := n.ch
n.ch = make(chan struct{})
close(previous)
}
func (n *notifier) wait(ctx context.Context) error {
n.mu.RLock()
ch := n.ch
n.mu.RUnlock()
select {
case <-ch:
return nil
case <-ctx.Done():
return ctx.Err()
}
}
================================================
FILE: cache/demux_test.go
================================================
// Copyright 2025 The etcd Authors
//
// 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 cache
import (
"testing"
"time"
"github.com/google/go-cmp/cmp"
"github.com/stretchr/testify/require"
"go.etcd.io/etcd/api/v3/mvccpb"
clientv3 "go.etcd.io/etcd/client/v3"
)
func TestInit(t *testing.T) {
type want struct {
min int64
max int64
historyRevs []int64
}
tests := []struct {
name string
capacity int
initRev int64
eventRevs []int64
shouldReinit bool
reinitRev int64
want want
}{
{
name: "first init sets only min",
capacity: 8,
initRev: 5,
eventRevs: nil,
shouldReinit: false,
want: want{min: 5, max: 0, historyRevs: nil},
},
{
name: "init on empty demux with events",
capacity: 8,
initRev: 5,
eventRevs: []int64{7, 9, 13},
shouldReinit: false,
want: want{min: 5, max: 13, historyRevs: []int64{7, 9, 13}},
},
{
name: "continuation at max+1 preserves range and history",
capacity: 8,
initRev: 10,
eventRevs: []int64{13, 15, 21},
shouldReinit: true,
reinitRev: 22,
want: want{min: 10, max: 21, historyRevs: []int64{13, 15, 21}},
},
{
name: "gap from max triggers purge and clears history",
capacity: 8,
initRev: 10,
eventRevs: []int64{13, 15, 21},
shouldReinit: true,
reinitRev: 30,
want: want{min: 30, max: 0, historyRevs: nil},
},
{
name: "idempotent reinit at same revision clears history",
capacity: 8,
initRev: 7,
eventRevs: []int64{8, 9, 10},
shouldReinit: true,
reinitRev: 7,
want: want{min: 7, max: 0, historyRevs: nil},
},
}
for _, tt := range tests {
tt := tt
t.Run(tt.name, func(t *testing.T) {
d := newDemux(tt.capacity, 10*time.Millisecond)
d.Init(tt.initRev)
if len(tt.eventRevs) > 0 {
if err := d.Broadcast(respWithEventRevs(tt.eventRevs...)); err != nil {
t.Fatalf("Broadcast(%v) failed: %v", tt.eventRevs, err)
}
}
if tt.shouldReinit {
d.Init(tt.reinitRev)
}
if d.minRev != tt.want.min || d.maxRev != tt.want.max {
t.Fatalf("revision range: got(min=%d, max=%d), want(min=%d, max=%d)",
d.minRev, d.maxRev, tt.want.min, tt.want.max)
}
var actualHistoryRevs []int64
d.history.AscendGreaterOrEqual(0, func(rev int64, events []*clientv3.Event) bool {
actualHistoryRevs = append(actualHistoryRevs, rev)
return true
})
if diff := cmp.Diff(tt.want.historyRevs, actualHistoryRevs); diff != "" {
t.Fatalf("history validation failed (-want +got):\n%s", diff)
}
})
}
}
func TestBroadcast(t *testing.T) {
type want struct {
min int64
max int64
shouldError bool
}
tests := []struct {
name string
capacity int
initRev int64
initialRevs []int64
followupRevs []int64
want want
}{
{
name: "history not full",
capacity: 2,
initRev: 1,
initialRevs: []int64{2},
want: want{min: 1, max: 2, shouldError: false},
},
{
name: "history at exact capacity",
capacity: 2,
initRev: 1,
initialRevs: []int64{2, 3},
want: want{min: 1, max: 3, shouldError: false},
},
{
name: "history overflow with eviction",
capacity: 2,
initRev: 1,
initialRevs: []int64{2, 3, 4},
want: want{min: 3, max: 4, shouldError: false},
},
{
name: "history overflow not continuous",
capacity: 2,
initRev: 2,
initialRevs: []int64{4, 8, 16},
want: want{min: 5, max: 16, shouldError: false},
},
{
name: "empty broadcast is no-op",
capacity: 8,
initRev: 10,
initialRevs: []int64{},
want: want{min: 10, max: 0, shouldError: false},
},
{
name: "revisions below maxRev are rejected",
capacity: 8,
initRev: 4,
initialRevs: []int64{5, 6},
followupRevs: []int64{4},
want: want{shouldError: true},
},
{
name: "revisions equal to maxRev are rejected",
capacity: 8,
initRev: 4,
initialRevs: []int64{5, 6},
followupRevs: []int64{6},
want: want{shouldError: true},
},
{
name: "revisions above maxRev are accepted",
capacity: 8,
initRev: 4,
initialRevs: []int64{5, 6},
followupRevs: []int64{9, 14, 17},
want: want{min: 4, max: 17, shouldError: false},
},
}
for _, tt := range tests {
tt := tt
t.Run(tt.name, func(t *testing.T) {
d := newDemux(tt.capacity, 10*time.Millisecond)
d.Init(tt.initRev)
if len(tt.initialRevs) > 0 {
if err := d.Broadcast(respWithEventRevs(tt.initialRevs...)); err != nil {
t.Fatalf("unexpected error broadcasting initial revisions %v: %v", tt.initialRevs, err)
}
}
if len(tt.followupRevs) > 0 {
err := d.Broadcast(respWithEventRevs(tt.followupRevs...))
if tt.want.shouldError {
require.Error(t, err)
return
}
require.NoError(t, err)
}
if d.minRev != tt.want.min || d.maxRev != tt.want.max {
t.Fatalf("revision range: got(min=%d, max=%d), want(min=%d, max=%d)",
d.minRev, d.maxRev, tt.want.min, tt.want.max)
}
})
}
}
func TestBroadcastBatching(t *testing.T) {
tests := []struct {
name string
input []int64
wantRevs []int64
wantSizes []int
}{
{
name: "two groups",
input: []int64{14, 14, 15, 15, 15},
wantRevs: []int64{14},
wantSizes: []int{5},
},
{
name: "single group",
input: []int64{7, 7, 7},
wantRevs: []int64{7},
wantSizes: []int{3},
},
{
name: "all distinct",
input: []int64{1, 2, 3},
wantRevs: []int64{1},
wantSizes: []int{3},
},
}
for _, tt := range tests {
tt := tt
t.Run(tt.name, func(t *testing.T) {
d := newDemux(16, 10*time.Millisecond)
w := newWatcher(len(tt.input)+1, nil)
d.Init(1)
d.Register(w, 0)
d.Broadcast(respWithEventRevs(tt.input...))
gotRevs, gotSizes := readBatches(t, w, len(tt.wantRevs))
if diff := cmp.Diff(tt.wantRevs, gotRevs); diff != "" {
t.Fatalf("revision mismatch (-want +got):\n%s", diff)
}
if diff := cmp.Diff(tt.wantSizes, gotSizes); diff != "" {
t.Fatalf("batch size mismatch (-want +got):\n%s", diff)
}
})
}
}
func TestSlowWatcherResync(t *testing.T) {
tests := []struct {
name string
input []int64
wantInitialRevs []int64
wantInitialSizes []int
wantResyncRevs []int64
wantResyncSizes []int
}{
{
name: "single event overflow",
input: []int64{1, 2, 3},
wantInitialRevs: []int64{1},
wantInitialSizes: []int{3},
wantResyncRevs: []int64{},
wantResyncSizes: []int{},
},
{
name: "multi events batch overflow",
input: []int64{10, 10, 11, 12, 12},
wantInitialRevs: []int64{10},
wantInitialSizes: []int{5},
wantResyncRevs: []int64{},
wantResyncSizes: []int{},
},
}
for _, tt := range tests {
tt := tt
t.Run(tt.name, func(t *testing.T) {
d := newDemux(16, 10*time.Millisecond)
w := newWatcher(1, nil)
d.Init(1)
d.Register(w, 0)
d.Broadcast(respWithEventRevs(tt.input...))
gotInitRevs, gotInitSizes := readBatches(t, w, len(tt.wantInitialRevs))
if diff := cmp.Diff(tt.wantInitialRevs, gotInitRevs); diff != "" {
t.Fatalf("initial revs mismatch (-want +got):\n%s", diff)
}
if diff := cmp.Diff(tt.wantInitialSizes, gotInitSizes); diff != "" {
t.Fatalf("initial batch sizes mismatch (-want +got):\n%s", diff)
}
gotRevs, gotSizes := make([]int64, 0, len(tt.wantResyncRevs)), make([]int, 0, len(tt.wantResyncRevs))
for len(gotRevs) < len(tt.wantResyncRevs) {
d.resyncLaggingWatchers()
revs, sizes := readBatches(t, w, 1)
gotRevs = append(gotRevs, revs...)
gotSizes = append(gotSizes, sizes...)
}
if diff := cmp.Diff(tt.wantResyncRevs, gotRevs); diff != "" {
t.Fatalf("resync revs mismatch (-want +got):\n%s", diff)
}
if diff := cmp.Diff(tt.wantResyncSizes, gotSizes); diff != "" {
t.Fatalf("resync batch sizes mismatch (-want +got):\n%s", diff)
}
})
}
}
func respWithEventRevs(revs ...int64) clientv3.WatchResponse {
events := make([]*clientv3.Event, 0, len(revs))
for _, r := range revs {
kv := &mvccpb.KeyValue{
Key: []byte("k"),
Value: []byte("v"),
ModRevision: r,
}
events = append(events, &clientv3.Event{
Type: clientv3.EventTypePut,
Kv: kv,
})
}
return clientv3.WatchResponse{Events: events}
}
func readBatches(t *testing.T, w *watcher, n int) (revs []int64, sizes []int) {
t.Helper()
timeout := time.After(2 * time.Second)
for len(revs) < n {
select {
case resp := <-w.respCh:
if resp.Canceled {
t.Fatalf("unexpected canceled response in test: %v", resp.CancelReason)
}
if len(resp.Events) == 0 {
continue
}
revs = append(revs, resp.Events[0].Kv.ModRevision)
sizes = append(sizes, len(resp.Events))
case <-timeout:
t.Fatalf("timed out waiting for %d batches; got %d", n, len(revs))
}
}
return revs, sizes
}
================================================
FILE: cache/go.mod
================================================
module go.etcd.io/etcd/cache/v3
go 1.26
toolchain go1.26.1
require (
github.com/google/go-cmp v0.7.0
github.com/stretchr/testify v1.11.1
go.etcd.io/etcd/api/v3 v3.6.0-alpha.0
go.etcd.io/etcd/client/v3 v3.6.0-alpha.0
k8s.io/utils v0.0.0-20260108192941-914a6e750570
)
require (
github.com/coreos/go-semver v0.3.1 // indirect
github.com/coreos/go-systemd/v22 v22.7.0 // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/golang/protobuf v1.5.4 // indirect
github.com/grpc-ecosystem/grpc-gateway/v2 v2.28.0 // indirect
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
go.etcd.io/etcd/client/pkg/v3 v3.6.0-alpha.0 // indirect
go.uber.org/multierr v1.11.0 // indirect
go.uber.org/zap v1.27.1 // indirect
golang.org/x/net v0.51.0 // indirect
golang.org/x/sys v0.41.0 // indirect
golang.org/x/text v0.34.0 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20260209200024-4cfbd4190f57 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20260209200024-4cfbd4190f57 // indirect
google.golang.org/grpc v1.79.2 // indirect
google.golang.org/protobuf v1.36.11 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
replace (
go.etcd.io/etcd/api/v3 => ../api
go.etcd.io/etcd/client/pkg/v3 => ../client/pkg
go.etcd.io/etcd/client/v3 => ../client/v3
)
================================================
FILE: cache/go.sum
================================================
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/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/coreos/go-semver v0.3.1 h1:yi21YpKnrx1gt5R+la8n5WgS0kCrsPp33dmEyHReZr4=
github.com/coreos/go-semver v0.3.1/go.mod h1:irMmmIw/7yzSRPWryHsK7EYSg09caPQL03VsM8rvUec=
github.com/coreos/go-systemd/v22 v22.7.0 h1:LAEzFkke61DFROc7zNLX/WA2i5J8gYqe0rSj9KI28KA=
github.com/coreos/go-systemd/v22 v22.7.0/go.mod h1:xNUYtjHu2EDXbsxz1i41wouACIwT7Ybq9o0BQhMwD0w=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.1.0 h1:QGLs/O40yoNK9vmy4rhUGBVyMf1lISBGtXRpsu/Qu/o=
github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.1.0/go.mod h1:hM2alZsMUni80N33RBe6J0e423LB+odMj7d3EMP9l20=
github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.3 h1:B+8ClL/kCQkRiU82d9xajRPKYMrB7E0MbtzWVi1K4ns=
github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.3/go.mod h1:NbCUVmiS4foBGBHOYlCT25+YmGpJ32dZPi75pGEUpj4=
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/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/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/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.23.2 h1:Je96obch5RDVy3FDMndoUsjAhG5Edi49h0RJWRi/o0o=
github.com/prometheus/client_golang v1.23.2/go.mod h1:Tb1a6LWHB3/SPIzCoaDXI4I8UHKeFTEQ1YCr+0Gyqmg=
github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk=
github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE=
github.com/prometheus/common v0.67.5 h1:pIgK94WWlQt1WLwAC5j2ynLaBRDiinoAb86HZHTUGI4=
github.com/prometheus/common v0.67.5/go.mod h1:SjE/0MzDEEAyrdr5Gqc6G+sXI67maCxzaT3A2+HqjUw=
github.com/prometheus/procfs v0.16.1 h1:hZ15bTNuirocR6u0JZ6BAHHmwS1p8B4P6MRqxtzMyRg=
github.com/prometheus/procfs v0.16.1/go.mod h1:teAbpZRB1iIAJYREa1LsoWUXykVXA1KlTmWl8x/U+Is=
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/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
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/otel v1.42.0 h1:lSQGzTgVR3+sgJDAU/7/ZMjN9Z+vUip7leaqBKy4sho=
go.opentelemetry.io/otel v1.42.0/go.mod h1:lJNsdRMxCUIWuMlVJWzecSMuNjE7dOYyWlqOXWkdqCc=
go.opentelemetry.io/otel/metric v1.42.0 h1:2jXG+3oZLNXEPfNmnpxKDeZsFI5o4J+nz6xUlaFdF/4=
go.opentelemetry.io/otel/metric v1.42.0/go.mod h1:RlUN/7vTU7Ao/diDkEpQpnz3/92J9ko05BIwxYa2SSI=
go.opentelemetry.io/otel/sdk v1.42.0 h1:LyC8+jqk6UJwdrI/8VydAq/hvkFKNHZVIWuslJXYsDo=
go.opentelemetry.io/otel/sdk v1.42.0/go.mod h1:rGHCAxd9DAph0joO4W6OPwxjNTYWghRWmkHuGbayMts=
go.opentelemetry.io/otel/sdk/metric v1.42.0 h1:D/1QR46Clz6ajyZ3G8SgNlTJKBdGp84q9RKCAZ3YGuA=
go.opentelemetry.io/otel/sdk/metric v1.42.0/go.mod h1:Ua6AAlDKdZ7tdvaQKfSmnFTdHx37+J4ba8MwVCYM5hc=
go.opentelemetry.io/otel/trace v1.42.0 h1:OUCgIPt+mzOnaUTpOQcBiM/PLQ/Op7oq6g4LenLmOYY=
go.opentelemetry.io/otel/trace v1.42.0/go.mod h1:f3K9S+IFqnumBkKhRJMeaZeNk9epyhnCmQh/EysQCdc=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
go.uber.org/zap v1.27.1 h1:08RqriUEv8+ArZRYSTXy1LeBScaMpVSTBhCeaZYfMYc=
go.uber.org/zap v1.27.1/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
go.yaml.in/yaml/v2 v2.4.3 h1:6gvOSjQoTB3vt1l+CU+tSyi/HOjfOjRLJ4YwYZGwRO0=
go.yaml.in/yaml/v2 v2.4.3/go.mod h1:zSxWcmIDjOzPXpjlTTbAsKokqkDNAVtZO0WOMiT90s8=
golang.org/x/net v0.51.0 h1:94R/GTO7mt3/4wIKpcR5gkGmRLOuE/2hNGeWq/GBIFo=
golang.org/x/net v0.51.0/go.mod h1:aamm+2QF5ogm02fjy5Bb7CQ0WMt1/WVM7FtyaTLlA9Y=
golang.org/x/sys v0.41.0 h1:Ivj+2Cp/ylzLiEU89QhWblYnOE9zerudt9Ftecq2C6k=
golang.org/x/sys v0.41.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
golang.org/x/text v0.34.0 h1:oL/Qq0Kdaqxa1KbNeMKwQq0reLCCaFtqu2eNuSeNHbk=
golang.org/x/text v0.34.0/go.mod h1:homfLqTYRFyVYemLBFl5GgL/DWEiH5wcsQ5gSh1yziA=
gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk=
gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=
google.golang.org/genproto/googleapis/api v0.0.0-20260209200024-4cfbd4190f57 h1:JLQynH/LBHfCTSbDWl+py8C+Rg/k1OVH3xfcaiANuF0=
google.golang.org/genproto/googleapis/api v0.0.0-20260209200024-4cfbd4190f57/go.mod h1:kSJwQxqmFXeo79zOmbrALdflXQeAYcUbgS7PbpMknCY=
google.golang.org/genproto/googleapis/rpc v0.0.0-20260209200024-4cfbd4190f57 h1:mWPCjDEyshlQYzBpMNHaEof6UX1PmHcaUODUywQ0uac=
google.golang.org/genproto/googleapis/rpc v0.0.0-20260209200024-4cfbd4190f57/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ=
google.golang.org/grpc v1.79.2 h1:fRMD94s2tITpyJGtBBn7MkMseNpOZU8ZxgC3MMBaXRU=
google.golang.org/grpc v1.79.2/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ=
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
k8s.io/utils v0.0.0-20260108192941-914a6e750570 h1:JT4W8lsdrGENg9W+YwwdLJxklIuKWdRm+BC+xt33FOY=
k8s.io/utils v0.0.0-20260108192941-914a6e750570/go.mod h1:xDxuJ0whA3d0I4mf/C4ppKHxXynQ+fxnkmQH0vTHnuk=
================================================
FILE: cache/predicate.go
================================================
// Copyright 2025 The etcd Authors
//
// 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 cache
import "bytes"
type Prefix string
func (prefix Prefix) Match(key []byte) bool {
if prefix == "" {
return true
}
prefixLen := len(prefix)
return len(key) >= prefixLen && string(key[:prefixLen]) == string(prefix)
}
func ExactKey(key []byte) KeyPredicate {
return func(k []byte) bool { return bytes.Equal(k, key) }
}
func FromKey(start []byte) KeyPredicate {
return func(k []byte) bool { return bytes.Compare(k, start) >= 0 }
}
func Range(start, end []byte) KeyPredicate {
return func(k []byte) bool {
return bytes.Compare(k, start) >= 0 &&
bytes.Compare(k, end) < 0
}
}
func KeyPredForRange(start, end []byte) KeyPredicate {
if len(end) == 0 {
return ExactKey(start)
}
if len(end) == 1 && end[0] == 0 {
return FromKey(start)
}
return Range(start, end)
}
================================================
FILE: cache/ready.go
================================================
// Copyright 2025 The etcd Authors
//
// 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 cache
import (
"context"
"sync"
)
// ready tracks readiness state changes and allows callers to wait for a target state
type ready struct {
mu sync.Mutex
isReady bool
stateCh chan struct{} // closed on any state transition, then replaced immediately
}
func newReady() *ready {
return &ready{stateCh: make(chan struct{})}
}
func (r *ready) Ready() bool {
r.mu.Lock()
defer r.mu.Unlock()
return r.isReady
}
func (r *ready) WaitReady(ctx context.Context) error {
return r.waitForState(ctx, func() bool { return r.isReady })
}
func (r *ready) WaitNotReady(ctx context.Context) error {
return r.waitForState(ctx, func() bool { return !r.isReady })
}
func (r *ready) Set() {
r.mu.Lock()
defer r.mu.Unlock()
if !r.isReady {
r.isReady = true
close(r.stateCh)
r.stateCh = make(chan struct{})
}
}
func (r *ready) Reset() {
r.mu.Lock()
defer r.mu.Unlock()
if r.isReady {
r.isReady = false
close(r.stateCh)
r.stateCh = make(chan struct{})
}
}
func (r *ready) waitForState(ctx context.Context, pred func() bool) error {
for {
r.mu.Lock()
if pred() {
r.mu.Unlock()
return ctx.Err()
}
stateChCopy := r.stateCh
r.mu.Unlock()
select {
case <-stateChCopy:
case <-ctx.Done():
return ctx.Err()
}
}
}
================================================
FILE: cache/ready_test.go
================================================
// Copyright 2025 The etcd Authors
//
// 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 cache
import (
"context"
"errors"
"sync"
"testing"
"time"
)
func TestWaitMethods(t *testing.T) {
tests := []struct {
name string
initialReady bool
expectReady bool
waitMethod string
expectBlock bool
}{
{
name: "not_ready_testing_WaitNotReady",
initialReady: false,
expectReady: false,
waitMethod: "not_ready",
expectBlock: false,
},
{
name: "not_ready_testing_WaitReady",
initialReady: false,
expectReady: false,
waitMethod: "ready",
expectBlock: true,
},
{
name: "ready_testing_WaitReady",
initialReady: true,
expectReady: true,
waitMethod: "ready",
expectBlock: false,
},
{
name: "ready_testing_WaitNotReady",
initialReady: true,
expectReady: true,
waitMethod: "not_ready",
expectBlock: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
r := newReady()
if tt.initialReady {
r.Set()
}
if got := r.Ready(); got != tt.expectReady {
t.Fatalf("Ready() = %t; want %t", got, tt.expectReady)
}
ctx, cancel := context.WithTimeout(t.Context(), 100*time.Millisecond)
defer cancel()
var err error
switch tt.waitMethod {
case "ready":
err = r.WaitReady(ctx)
case "not_ready":
err = r.WaitNotReady(ctx)
default:
t.Fatalf("invalid waitMethod: %s", tt.waitMethod)
}
if tt.expectBlock {
if !errors.Is(err, context.DeadlineExceeded) {
t.Fatalf("expected timeout but got: %v", err)
}
} else {
if err != nil {
t.Fatalf("expected immediate return but got error: %v", err)
}
}
})
}
}
func TestSetUnblocksWaiters(t *testing.T) {
testStateTransitionUnblocksWaiters(t, false, true, (*ready).Set, true, "WaitReady")
}
func TestResetUnblocksWaiters(t *testing.T) {
testStateTransitionUnblocksWaiters(t, true, false, (*ready).Reset, false, "WaitNotReady")
}
func testStateTransitionUnblocksWaiters(t *testing.T, initialSet bool, waitForReady bool, transition func(*ready), expectedReady bool, waitMethodName string) {
cases := []struct {
name string
n int
}{
{"one_waiter", 1},
{"several_waiters", 16},
{"many_waiters", 128},
}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
r := newReady()
if initialSet {
r.Set()
}
var startWg sync.WaitGroup
var readyWg sync.WaitGroup
errs := make(chan error, tc.n)
for i := 0; i < tc.n; i++ {
startWg.Add(1)
readyWg.Add(1)
go func() {
defer readyWg.Done()
startWg.Done()
ctx, cancel := context.WithTimeout(t.Context(), time.Second)
defer cancel()
if waitForReady {
errs <- r.WaitReady(ctx)
} else {
errs <- r.WaitNotReady(ctx)
}
}()
}
startWg.Wait()
time.Sleep(50 * time.Millisecond)
transition(r)
readyWg.Wait()
for i := 0; i < tc.n; i++ {
if err := <-errs; err != nil {
t.Fatalf("waiter %d: %s = %v; want: nil", i, waitMethodName, err)
}
}
if r.Ready() != expectedReady {
t.Fatalf("Ready() = %t after transition; want %t", r.Ready(), expectedReady)
}
if waitForReady {
if err := r.WaitReady(t.Context()); err != nil {
t.Fatalf("immediate WaitReady() after transition = %v; want: nil", err)
}
} else {
if err := r.WaitNotReady(t.Context()); err != nil {
t.Fatalf("immediate WaitNotReady() after transition = %v; want: nil", err)
}
}
})
}
}
func TestIdempotentStateTransitions(t *testing.T) {
r := newReady()
r.Set()
r.Set()
if !r.Ready() {
t.Fatalf("Ready() = false after double Set(); want: true")
}
if err := r.WaitReady(t.Context()); err != nil {
t.Fatalf("WaitReady() after double Set() = %v; want: nil", err)
}
r.Reset()
r.Reset()
if r.Ready() {
t.Fatalf("Ready() = true after double Reset(); want: false")
}
if err := r.WaitNotReady(t.Context()); err != nil {
t.Fatalf("WaitNotReady() after double Reset() = %v; want ", err)
}
}
================================================
FILE: cache/ringbuffer.go
================================================
// Copyright 2025 The etcd Authors
//
// 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 cache
type ringBuffer[T any] struct {
buffer []entry[T]
// head is the index immediately after the last non-empty entry in the buffer (i.e., the next write position).
head, tail, size int
revisionOf RevisionOf[T]
}
type entry[T any] struct {
revision int64
item T
}
type (
KeyPredicate = func([]byte) bool
RevisionOf[T any] func(T) int64
IterFunc[T any] func(rev int64, item T) bool
)
func newRingBuffer[T any](capacity int, revisionOf RevisionOf[T]) *ringBuffer[T] {
// assume capacity > 0 – validated by Cache
return &ringBuffer[T]{
buffer: make([]entry[T], capacity),
revisionOf: revisionOf,
}
}
func (r *ringBuffer[T]) Append(item T) {
entry := entry[T]{revision: r.revisionOf(item), item: item}
if r.full() {
r.tail = (r.tail + 1) % len(r.buffer)
} else {
r.size++
}
r.buffer[r.head] = entry
r.head = (r.head + 1) % len(r.buffer)
}
func (r *ringBuffer[T]) full() bool {
return r.size == len(r.buffer)
}
// AscendGreaterOrEqual iterates through entries in ascending order starting from the first entry with revision >= pivot.
// TODO: use binary search on the ring buffer to locate the first entry >= nextRev instead of a full scan
func (r *ringBuffer[T]) AscendGreaterOrEqual(pivot int64, iter IterFunc[T]) {
if r.size == 0 {
return
}
for n, i := 0, r.tail; n < r.size; n, i = n+1, (i+1)%len(r.buffer) {
entry := r.buffer[i]
if entry.revision < pivot {
continue
}
if !iter(entry.revision, entry.item) {
return
}
}
}
// AscendLessThan iterates in ascending order over entries with revision < pivot.
func (r *ringBuffer[T]) AscendLessThan(pivot int64, iter IterFunc[T]) {
if r.size == 0 {
return
}
for n, i := 0, r.tail; n < r.size; n, i = n+1, (i+1)%len(r.buffer) {
entry := r.buffer[i]
if entry.revision >= pivot {
return
}
if !iter(entry.revision, entry.item) {
return
}
}
}
// DescendGreaterThan iterates in descending order over entries with revision > pivot.
func (r *ringBuffer[T]) DescendGreaterThan(pivot int64, iter IterFunc[T]) {
if r.size == 0 {
return
}
for n, i := 0, r.moduloIndex(r.head-1); n < r.size; n, i = n+1, r.moduloIndex(i-1) {
entry := r.buffer[i]
if entry.revision <= pivot {
return
}
if !iter(entry.revision, entry.item) {
return
}
}
}
// DescendLessOrEqual iterates in descending order over entries with revision <= pivot.
func (r *ringBuffer[T]) DescendLessOrEqual(pivot int64, iter IterFunc[T]) {
if r.size == 0 {
return
}
for n, i := 0, r.moduloIndex(r.head-1); n < r.size; n, i = n+1, r.moduloIndex(i-1) {
entry := r.buffer[i]
if entry.revision > pivot {
continue
}
if !iter(entry.revision, entry.item) {
return
}
}
}
// PeekLatest returns the most recently-appended revision (or 0 if empty).
func (r *ringBuffer[T]) PeekLatest() int64 {
if r.size == 0 {
return 0
}
idx := (r.head - 1 + len(r.buffer)) % len(r.buffer)
return r.buffer[idx].revision
}
// PeekOldest returns the oldest revision currently stored (or 0 if empty).
func (r *ringBuffer[T]) PeekOldest() int64 {
if r.size == 0 {
return 0
}
return r.buffer[r.tail].revision
}
func (r *ringBuffer[T]) RebaseHistory() {
r.head, r.tail, r.size = 0, 0, 0
for i := range r.buffer {
r.buffer[i] = entry[T]{}
}
}
func (r *ringBuffer[T]) moduloIndex(index int) int {
return (index + len(r.buffer)) % len(r.buffer)
}
================================================
FILE: cache/ringbuffer_test.go
================================================
// Copyright 2025 The etcd Authors
//
// 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 cache
import (
"fmt"
"testing"
"github.com/google/go-cmp/cmp"
"go.etcd.io/etcd/api/v3/mvccpb"
clientv3 "go.etcd.io/etcd/client/v3"
)
func TestPeekLatestAndOldest(t *testing.T) {
tests := []struct {
name string
capacity int
revs []int64
wantLatestRev int64
wantOldestRev int64
}{
{
name: "empty_buffer",
capacity: 4,
revs: nil,
wantLatestRev: 0,
wantOldestRev: 0,
},
{
name: "single_element",
capacity: 8,
revs: []int64{1},
wantLatestRev: 1,
wantOldestRev: 1,
},
{
name: "ascending_fill",
capacity: 4,
revs: []int64{1, 2, 3, 4},
wantLatestRev: 4,
wantOldestRev: 1,
},
{
name: "overwrite_when_full",
capacity: 3,
revs: []int64{5, 6, 7, 8},
wantLatestRev: 8,
wantOldestRev: 6,
},
}
for _, tt := range tests {
tt := tt
t.Run(tt.name, func(t *testing.T) {
rb := newRingBuffer(tt.capacity, func(batch []*clientv3.Event) int64 { return batch[0].Kv.ModRevision })
for _, r := range tt.revs {
batch, err := makeEventBatch(r, "k", 1)
if err != nil {
t.Fatalf("makeEventBatch(%d, k, 1) failed: %v", r, err)
}
rb.Append(batch)
}
latestRev := rb.PeekLatest()
oldestRev := rb.PeekOldest()
gotLatestRev := latestRev
gotOldestRev := oldestRev
if tt.wantLatestRev != gotLatestRev {
t.Fatalf("PeekLatest()=%d, want=%d", gotLatestRev, tt.wantLatestRev)
}
if tt.wantOldestRev != gotOldestRev {
t.Fatalf("PeekOldest()=%d, want=%d", gotOldestRev, tt.wantOldestRev)
}
})
}
}
func TestIterationMethods(t *testing.T) {
type iterTestCase struct {
method iterMethod
pivot int64
wantIterRevisions []int64
}
tests := []struct {
name string
capacity int
setupRevisions []int64
cases []iterTestCase
}{
{
name: "empty_buffer",
capacity: 4,
setupRevisions: nil,
cases: []iterTestCase{
{ascendGTE, 0, []int64{}},
{ascendLT, 10, []int64{}},
{descendGT, 0, []int64{}},
{descendLTE, 10, []int64{}},
},
},
{
name: "basic_filtering",
capacity: 5,
setupRevisions: []int64{1, 2, 3},
cases: []iterTestCase{
{ascendGTE, 0, []int64{1, 2, 3}},
{ascendGTE, 2, []int64{2, 3}},
{ascendGTE, 100, []int64{}},
{ascendLT, 3, []int64{1, 2}},
{ascendLT, 1, []int64{}},
{ascendLT, 100, []int64{1, 2, 3}},
{descendGT, 1, []int64{3, 2}},
{descendGT, 3, []int64{}},
{descendGT, 0, []int64{3, 2, 1}},
{descendLTE, 2, []int64{2, 1}},
{descendLTE, 3, []int64{3, 2, 1}},
{descendLTE, 0, []int64{}},
},
},
{
name: "overflowed stores only entries within capacity",
capacity: 3,
setupRevisions: []int64{20, 21, 22, 23, 24}, // stored: 22, 23, 24
cases: []iterTestCase{
{ascendGTE, 23, []int64{23, 24}},
{ascendGTE, 0, []int64{22, 23, 24}},
{ascendLT, 23, []int64{22}},
{ascendLT, 25, []int64{22, 23, 24}},
{descendGT, 22, []int64{24, 23}},
{descendGT, 25, []int64{}},
{descendLTE, 23, []int64{23, 22}},
{descendLTE, 24, []int64{24, 23, 22}},
},
},
}
for _, tt := range tests {
tt := tt
t.Run(tt.name, func(t *testing.T) {
rb := setupRingBuffer(t, tt.capacity, tt.setupRevisions)
for _, tc := range tt.cases {
tc := tc
t.Run(fmt.Sprintf("%s_pivot_%d", tc.method, tc.pivot), func(t *testing.T) {
got := collectRevisions(rb, tc.method, tc.pivot)
if diff := cmp.Diff(tc.wantIterRevisions, got); diff != "" {
t.Fatalf("%s(%d) mismatch (-want +got):\n%s", tc.method, tc.pivot, diff)
}
})
}
})
}
}
func TestIterationWithBatching(t *testing.T) {
rb := newRingBuffer(6, func(batch []*clientv3.Event) int64 { return batch[0].Kv.ModRevision })
batchA := []*clientv3.Event{
{Kv: &mvccpb.KeyValue{Key: []byte("key-a"), ModRevision: 5}},
}
batchB := []*clientv3.Event{
{Kv: &mvccpb.KeyValue{Key: []byte("key-b-1"), ModRevision: 10}},
{Kv: &mvccpb.KeyValue{Key: []byte("key-b-2"), ModRevision: 10}},
{Kv: &mvccpb.KeyValue{Key: []byte("key-b-3"), ModRevision: 10}},
}
batchC := []*clientv3.Event{
{Kv: &mvccpb.KeyValue{Key: []byte("key-c"), ModRevision: 12}},
}
rb.Append(batchA)
rb.Append(batchB)
rb.Append(batchC)
tests := []struct {
name string
method iterMethod
pivot int64
want [][]*clientv3.Event
}{
{
name: "ascending_gte_includes_batched_revision",
method: ascendGTE,
pivot: 10,
want: [][]*clientv3.Event{
{
{Kv: &mvccpb.KeyValue{Key: []byte("key-b-1"), ModRevision: 10}},
{Kv: &mvccpb.KeyValue{Key: []byte("key-b-2"), ModRevision: 10}},
{Kv: &mvccpb.KeyValue{Key: []byte("key-b-3"), ModRevision: 10}},
},
{
{Kv: &mvccpb.KeyValue{Key: []byte("key-c"), ModRevision: 12}},
},
},
},
{
name: "ascending_lt_stops_before_batched_revision",
method: ascendLT,
pivot: 10,
want: [][]*clientv3.Event{
{
{Kv: &mvccpb.KeyValue{Key: []byte("key-a"), ModRevision: 5}},
},
},
},
{
name: "all_revisions_with_proper_batch_sizes",
method: ascendGTE,
pivot: 0,
want: [][]*clientv3.Event{
{
{Kv: &mvccpb.KeyValue{Key: []byte("key-a"), ModRevision: 5}},
},
{
{Kv: &mvccpb.KeyValue{Key: []byte("key-b-1"), ModRevision: 10}},
{Kv: &mvccpb.KeyValue{Key: []byte("key-b-2"), ModRevision: 10}},
{Kv: &mvccpb.KeyValue{Key: []byte("key-b-3"), ModRevision: 10}},
},
{
{Kv: &mvccpb.KeyValue{Key: []byte("key-c"), ModRevision: 12}},
},
},
},
}
for _, tt := range tests {
tt := tt
t.Run(tt.name, func(t *testing.T) {
var got [][]*clientv3.Event
rb.iterate(tt.method, tt.pivot, func(rev int64, events []*clientv3.Event) bool {
got = append(got, events)
return true
})
if diff := cmp.Diff(tt.want, got); diff != "" {
t.Fatalf("Events mismatch (-want +got):\n%s", diff)
}
})
}
}
func TestIterationEarlyStop(t *testing.T) {
rb := setupRingBuffer(t, 5, []int64{5, 10, 15, 20})
tests := []struct {
name string
method iterMethod
pivot int64
stopAfter int
want []int64
}{
{
name: "find_first_match_ascending",
method: ascendGTE,
pivot: 10,
stopAfter: 1,
want: []int64{10},
},
{
name: "find_first_two_ascending_lt",
method: ascendLT,
pivot: 20,
stopAfter: 2,
want: []int64{5, 10},
},
{
name: "find_first_two_descending_gt",
method: descendGT,
pivot: 5,
stopAfter: 2,
want: []int64{20, 15},
},
{
name: "find_first_match_descending_lte",
method: descendLTE,
pivot: 15,
stopAfter: 1,
want: []int64{15},
},
}
for _, tt := range tests {
tt := tt
t.Run(tt.name, func(t *testing.T) {
var collected []int64
callCount := 0
rb.iterate(tt.method, tt.pivot, func(rev int64, events []*clientv3.Event) bool {
collected = append(collected, rev)
callCount++
shouldContinue := callCount < tt.stopAfter
if !shouldContinue {
t.Logf("Stopping early after %d items (callback returned false)", callCount)
}
return shouldContinue
})
if diff := cmp.Diff(tt.want, collected); diff != "" {
t.Fatalf("Early stop failed.\nExpected: \nDiff (-want +got):\n%s", diff)
}
if callCount != tt.stopAfter {
t.Fatalf("Expected exactly %d callback calls, got %d", tt.stopAfter, callCount)
}
t.Logf("Successfully stopped early: collected %v after %d callbacks",
collected, callCount)
})
}
}
type iterMethod string
const (
ascendGTE iterMethod = "AscendGreaterOrEqual"
ascendLT iterMethod = "AscendLessThan"
descendGT iterMethod = "DescendGreaterThan"
descendLTE iterMethod = "DescendLessOrEqual"
)
func (r *ringBuffer[T]) iterate(method iterMethod, pivot int64, fn IterFunc[T]) {
switch method {
case ascendGTE:
r.AscendGreaterOrEqual(pivot, fn)
case ascendLT:
r.AscendLessThan(pivot, fn)
case descendGT:
r.DescendGreaterThan(pivot, fn)
case descendLTE:
r.DescendLessOrEqual(pivot, fn)
default:
panic(fmt.Sprintf("unknown iteration method: %s", method))
}
}
func TestAtomicOrdered(t *testing.T) {
tests := []struct {
name string
capacity int
inputs []struct {
rev int64
key string
size int
}
wantRev []int64
wantSize []int
}{
{
name: "unfiltered",
capacity: 5,
inputs: []struct {
rev int64
key string
size int
}{
{5, "a", 1},
{10, "b", 3},
{15, "c", 7},
{20, "d", 11},
},
wantRev: []int64{5, 10, 15, 20},
wantSize: []int{1, 3, 7, 11},
},
{
name: "across_wrap",
capacity: 3,
inputs: []struct {
rev int64
key string
size int
}{
{1, "a", 2},
{2, "b", 1},
{3, "c", 3},
{4, "d", 7},
},
wantRev: []int64{2, 3, 4},
wantSize: []int{1, 3, 7},
},
}
for _, tt := range tests {
tt := tt
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
rb := newRingBuffer(tt.capacity, func(batch []*clientv3.Event) int64 { return batch[0].Kv.ModRevision })
for _, in := range tt.inputs {
batch, err := makeEventBatch(in.rev, in.key, in.size)
if err != nil {
t.Fatalf("makeEventBatch(%d, k, 1) failed: %v", in.rev, err)
}
rb.Append(batch)
}
gotRevs := []int64{}
var gotSizes []int
rb.AscendGreaterOrEqual(0, func(rev int64, events []*clientv3.Event) bool {
gotRevs = append(gotRevs, rev)
gotSizes = append(gotSizes, len(events))
return true
})
if len(gotRevs) != len(tt.wantRev) {
t.Fatalf("len(got) = %d, want %d", len(gotRevs), len(tt.wantRev))
}
for i := range gotRevs {
if gotRevs[i] != tt.wantRev[i] {
t.Errorf("at idx %d: rev = %d, want %d", i, gotRevs[i], tt.wantRev[i])
}
if gotSizes[i] != tt.wantSize[i] {
t.Errorf("at rev %d: events.len = %d, want %d", gotRevs[i], gotSizes[i], tt.wantSize[i])
}
}
})
}
}
func TestRebaseHistory(t *testing.T) {
tests := []struct {
name string
revs []int64
}{
{
name: "rebase_empty_buffer",
revs: nil,
},
{
name: "rebase_after_data",
revs: []int64{7, 8, 9},
},
}
for _, tt := range tests {
tt := tt
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
rb := newRingBuffer(4, func(batch []*clientv3.Event) int64 { return batch[0].Kv.ModRevision })
for _, r := range tt.revs {
batch, err := makeEventBatch(r, "k", 1)
if err != nil {
t.Fatalf("makeEventBatch(%d, k, 1) failed: %v", r, err)
}
rb.Append(batch)
}
rb.RebaseHistory()
oldestRev := rb.PeekOldest()
latestRev := rb.PeekLatest()
if oldestRev != 0 {
t.Fatalf("PeekOldest()=%d, want=%d", oldestRev, 0)
}
if latestRev != 0 {
t.Fatalf("PeekLatest()=%d, want=%d", latestRev, 0)
}
gotRevs := []int64{}
rb.AscendGreaterOrEqual(0, func(rev int64, events []*clientv3.Event) bool {
gotRevs = append(gotRevs, rev)
return true
})
if len(gotRevs) != 0 {
t.Fatalf("AscendGreaterOrEqual() len(events)=%d, want=%d", len(gotRevs), 0)
}
})
}
}
func TestFull(t *testing.T) {
tests := []struct {
name string
capacity int
numAppends int
expectedFull bool
}{
{
name: "empty_buffer",
capacity: 3,
numAppends: 0,
expectedFull: false,
},
{
name: "partially_filled",
capacity: 5,
numAppends: 3,
expectedFull: false,
},
{
name: "exactly_at_capacity",
capacity: 3,
numAppends: 3,
expectedFull: true,
},
{
name: "beyond_capacity_wrapping",
capacity: 3,
numAppends: 5,
expectedFull: true,
},
}
for _, tt := range tests {
tt := tt
t.Run(tt.name, func(t *testing.T) {
rb := newRingBuffer(tt.capacity, func(batch []*clientv3.Event) int64 { return batch[0].Kv.ModRevision })
for i := 1; i <= tt.numAppends; i++ {
batch, err := makeEventBatch(int64(i), "k", 1)
if err != nil {
t.Fatalf("makeEventBatch(%d, k, 1) failed: %v", i, err)
}
rb.Append(batch)
}
if got := rb.full(); got != tt.expectedFull {
t.Fatalf("full()=%t, want=%t (capacity=%d, appends=%d)",
got, tt.expectedFull, tt.capacity, tt.numAppends)
}
})
}
}
func setupRingBuffer(t *testing.T, capacity int, revs []int64) *ringBuffer[[]*clientv3.Event] {
rb := newRingBuffer(capacity, func(batch []*clientv3.Event) int64 { return batch[0].Kv.ModRevision })
for _, r := range revs {
batch, err := makeEventBatch(r, "key", 1)
if err != nil {
t.Fatalf("makeEventBatch(%d, %s, %d) failed: %v", r, "key", 1, err)
}
rb.Append(batch)
}
return rb
}
func collectRevisions(rb *ringBuffer[[]*clientv3.Event], method iterMethod, pivot int64) []int64 {
revs := []int64{}
rb.iterate(method, pivot, func(rev int64, events []*clientv3.Event) bool {
revs = append(revs, rev)
return true
})
return revs
}
func makeEventBatch(rev int64, key string, batchSize int) ([]*clientv3.Event, error) {
if batchSize < 0 {
return nil, fmt.Errorf("invalid batchSize %d", batchSize)
}
events := make([]*clientv3.Event, batchSize)
for i := range events {
events[i] = &clientv3.Event{
Kv: &mvccpb.KeyValue{
Key: []byte(fmt.Sprintf("%s-%d", key, i)),
ModRevision: rev,
},
}
}
return events, nil
}
================================================
FILE: cache/snapshot.go
================================================
// Copyright 2025 The etcd Authors
//
// 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 cache
import (
"k8s.io/utils/third_party/forked/golang/btree"
"go.etcd.io/etcd/api/v3/mvccpb"
)
// snapshot captures a full, point-in-time view of the KV state at rev.
type snapshot struct {
rev int64
tree *btree.BTree[*kvItem]
}
func newClonedSnapshot(rev int64, t *btree.BTree[*kvItem]) *snapshot {
return &snapshot{rev: rev, tree: t.Clone()}
}
func (s *snapshot) Range(startKey, endKey []byte) []*mvccpb.KeyValue {
var out []*mvccpb.KeyValue
switch {
case len(endKey) == 0:
if item, ok := s.tree.Get(probeItemFromKey(startKey)); ok {
out = append(out, item.kv)
}
case isPrefixScan(endKey):
s.tree.AscendGreaterOrEqual(probeItemFromKey(startKey), func(item *kvItem) bool {
out = append(out, item.kv)
return true
})
default:
s.tree.AscendRange(
probeItemFromKey(startKey),
probeItemFromKey(endKey),
func(item *kvItem) bool {
out = append(out, item.kv)
return true
},
)
}
return out
}
func isPrefixScan(endKey []byte) bool {
return len(endKey) == 1 && endKey[0] == 0
}
func probeItemFromKey(key []byte) *kvItem { return &kvItem{key: string(key)} }
================================================
FILE: cache/store.go
================================================
// Copyright 2025 The etcd Authors
//
// 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 cache
import (
"errors"
"fmt"
"sync"
"k8s.io/utils/third_party/forked/golang/btree"
"go.etcd.io/etcd/api/v3/mvccpb"
"go.etcd.io/etcd/api/v3/v3rpc/rpctypes"
clientv3 "go.etcd.io/etcd/client/v3"
)
var ErrNotReady = fmt.Errorf("cache: store not ready")
// The store keeps a bounded history of snapshots using ringBuffer so that
// reads at historical revisions can be served until they fall out of the window.
type store struct {
mu sync.RWMutex
degree int
latest snapshot // latest is the mutable working snapshot
history ringBuffer[*snapshot] // history stores immutable cloned snapshots
}
func newStore(degree int, historyCapacity int) *store {
tree := btree.New[*kvItem](degree, kvItemLess)
return &store{
degree: degree,
latest: snapshot{rev: 0, tree: tree},
history: *newRingBuffer(historyCapacity, func(s *snapshot) int64 { return s.rev }),
}
}
type kvItem struct {
key string
kv *mvccpb.KeyValue
}
func newKVItem(kv *mvccpb.KeyValue) *kvItem {
return &kvItem{key: string(kv.Key), kv: kv}
}
func kvItemLess(a, b *kvItem) bool {
return a.key < b.key
}
func (s *store) Get(startKey, endKey []byte, rev int64) ([]*mvccpb.KeyValue, int64, error) {
snapshot, latestRev, err := s.getSnapshot(rev)
if err != nil {
return nil, 0, err
}
return snapshot.Range(startKey, endKey), latestRev, nil
}
func (s *store) getSnapshot(rev int64) (*snapshot, int64, error) {
s.mu.RLock()
defer s.mu.RUnlock()
if s.latest.rev == 0 {
return nil, 0, ErrNotReady
}
if rev < 0 {
return nil, 0, fmt.Errorf("invalid revision: %d", rev)
}
if rev == 0 {
rev = s.latest.rev
}
if rev > s.latest.rev {
return nil, 0, rpctypes.ErrFutureRev
}
oldestRev := s.history.PeekOldest()
if rev < oldestRev {
return nil, 0, rpctypes.ErrCompacted
}
var targetSnapshot *snapshot
s.history.AscendGreaterOrEqual(rev, func(rev int64, snap *snapshot) bool {
targetSnapshot = snap
return false
})
// If s.history < rev < s.latest.rev serve latest.
if targetSnapshot == nil {
targetSnapshot = &s.latest
}
return targetSnapshot, s.latest.rev, nil
}
// Restore replaces state with the bootstrap snapshot and resets history.
func (s *store) Restore(kvs []*mvccpb.KeyValue, rev int64) {
s.mu.Lock()
defer s.mu.Unlock()
s.latest.tree = btree.New[*kvItem](s.degree, kvItemLess)
for _, kv := range kvs {
s.latest.tree.ReplaceOrInsert(newKVItem(kv))
}
s.history.RebaseHistory()
s.latest.rev = rev
s.history.Append(newClonedSnapshot(rev, s.latest.tree))
}
func (s *store) Apply(resp clientv3.WatchResponse) error {
if resp.Canceled {
return errors.New("canceled")
}
s.mu.Lock()
defer s.mu.Unlock()
if err := validateRevisions(resp, s.latest.rev); err != nil {
return err
}
switch {
case resp.IsProgressNotify():
s.applyProgressNotifyLocked(resp.Header.Revision)
return nil
case len(resp.Events) != 0:
return s.applyEventsLocked(resp.Events)
default:
return nil
}
}
func (s *store) applyProgressNotifyLocked(revision int64) {
if s.latest.rev == 0 {
return
}
s.latest.rev = revision
}
func (s *store) applyEventsLocked(events []*clientv3.Event) error {
for i := 0; i < len(events); {
rev := events[i].Kv.ModRevision
for i < len(events) && events[i].Kv.ModRevision == rev {
ev := events[i]
switch ev.Type {
case clientv3.EventTypeDelete:
if _, ok := s.latest.tree.Delete(&kvItem{key: string(ev.Kv.Key)}); !ok {
return fmt.Errorf("cache: delete non-existent key %s", string(ev.Kv.Key))
}
case clientv3.EventTypePut:
s.latest.tree.ReplaceOrInsert(newKVItem(ev.Kv))
}
i++
}
s.latest.rev = rev
s.history.Append(newClonedSnapshot(rev, s.latest.tree))
}
return nil
}
func (s *store) LatestRev() int64 {
s.mu.RLock()
defer s.mu.RUnlock()
return s.latest.rev
}
func validateRevisions(resp clientv3.WatchResponse, latestRev int64) error {
if resp.IsProgressNotify() {
if resp.Header.Revision < latestRev {
return fmt.Errorf("cache: progress notification out of order (progress %d < latest %d)", resp.Header.Revision, latestRev)
}
return nil
}
events := resp.Events
if len(events) == 0 {
return nil
}
for _, ev := range events {
r := ev.Kv.ModRevision
if r < latestRev {
return fmt.Errorf("cache: stale event batch (rev %d < latest %d)", r, latestRev)
}
if r == latestRev {
return fmt.Errorf("cache: duplicate revision batch breaks atomic guarantee (rev %d == latest %d)", r, latestRev)
}
}
return nil
}
================================================
FILE: cache/store_test.go
================================================
// Copyright 2025 The etcd Authors
//
// 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 cache
import (
"errors"
"testing"
"github.com/google/go-cmp/cmp"
mvccpb "go.etcd.io/etcd/api/v3/mvccpb"
clientv3 "go.etcd.io/etcd/client/v3"
)
func TestStoreGet(t *testing.T) {
tests := []struct {
name string
initialKVs []*mvccpb.KeyValue
initialRev int64
start []byte
end []byte
expectedKVs []*mvccpb.KeyValue
expectedRev int64
expectedErr error
}{
{
name: "empty_store_returns_ErrNotReady",
initialKVs: nil,
start: []byte("a"),
expectedErr: ErrNotReady,
},
{
name: "Get_single_key_hit",
initialKVs: []*mvccpb.KeyValue{makeKV("/b", "2", 5), makeKV("/a", "1", 5), makeKV("/c", "3", 5)},
initialRev: 5,
start: []byte("/b"),
expectedKVs: []*mvccpb.KeyValue{makeKV("/b", "2", 5)},
expectedRev: 5,
},
{
name: "Get_single_key_miss_returns_empty",
initialKVs: []*mvccpb.KeyValue{makeKV("/b", "2", 5), makeKV("/a", "1", 5), makeKV("/c", "3", 5)},
initialRev: 5,
start: []byte("/zzz"),
expectedKVs: nil,
expectedRev: 5,
},
{
name: "Get_explicit_range",
initialKVs: []*mvccpb.KeyValue{makeKV("/a", "1", 10), makeKV("/b", "2", 10), makeKV("/c", "3", 10), makeKV("/d", "4", 10)},
initialRev: 10,
start: []byte("/b"),
end: []byte("/d"),
expectedKVs: []*mvccpb.KeyValue{makeKV("/b", "2", 10), makeKV("/c", "3", 10)},
expectedRev: 10,
},
{
name: "Get_range_includes_prefix_excludes_end",
initialKVs: []*mvccpb.KeyValue{makeKV("/a", "1", 4), makeKV("/aa", "2", 4), makeKV("/ab", "3", 4), makeKV("/b", "4", 4)},
initialRev: 4,
start: []byte("/a"),
end: []byte("/b"),
expectedKVs: []*mvccpb.KeyValue{makeKV("/a", "1", 4), makeKV("/aa", "2", 4), makeKV("/ab", "3", 4)},
expectedRev: 4,
},
{
name: "Get_empty_range_returns_empty",
initialKVs: []*mvccpb.KeyValue{makeKV("/a", "1", 2), makeKV("/b", "2", 2)},
initialRev: 2,
start: []byte("/a"),
end: []byte("/a"),
expectedKVs: nil,
expectedRev: 2,
},
{
name: "Get_invalid_range_returns_empty",
initialKVs: []*mvccpb.KeyValue{makeKV("/a", "1", 6), makeKV("/z", "9", 6)},
initialRev: 6,
start: []byte("/z"),
end: []byte("/a"),
expectedKVs: nil,
expectedRev: 6,
},
{
name: "Get_fromKey_scans_ordered",
initialKVs: []*mvccpb.KeyValue{makeKV("/a", "1", 7), makeKV("/b", "2", 7), makeKV("/c", "3", 7), makeKV("/d", "4", 7)},
initialRev: 7,
start: []byte("/b"),
end: []byte{0},
expectedKVs: []*mvccpb.KeyValue{makeKV("/b", "2", 7), makeKV("/c", "3", 7), makeKV("/d", "4", 7)},
expectedRev: 7,
},
{
name: "Get_fromKey_with_no_results",
initialKVs: []*mvccpb.KeyValue{makeKV("/a", "1", 9), makeKV("/b", "2", 9)},
initialRev: 9,
start: []byte("/zzz"),
end: []byte{0},
expectedKVs: nil,
expectedRev: 9,
},
}
for _, tt := range tests {
test := tt
t.Run(test.name, func(t *testing.T) {
s := newStore(8, 32)
if test.initialKVs != nil {
s.Restore(test.initialKVs, test.initialRev)
}
kvs, rev, err := s.Get(test.start, test.end, 0)
if test.expectedErr != nil {
if !errors.Is(err, test.expectedErr) {
t.Fatalf("Get error = %v; want %v", err, test.expectedErr)
}
return
}
if err != nil {
t.Fatalf("Get returned unexpected error: %v", err)
}
if rev != test.expectedRev {
t.Fatalf("revision=%d; want %d", rev, test.expectedRev)
}
if diff := cmp.Diff(test.expectedKVs, kvs); diff != "" {
t.Fatalf("Get mismatch (-want +got):\n%s", diff)
}
})
}
}
func TestStoreApply(t *testing.T) {
type testCase struct {
name string
initialKVs []*mvccpb.KeyValue
initialRev int64
eventBatches [][]*clientv3.Event
expectedLatestRev int64
expectedSnapshot []*mvccpb.KeyValue
expectErr bool
}
tests := []testCase{
{
name: "put_overwrites_key",
initialKVs: []*mvccpb.KeyValue{makeKV("/k", "v1", 10)},
initialRev: 10,
eventBatches: [][]*clientv3.Event{{makePutEvent("/k", "v2", 11)}},
expectedLatestRev: 11,
expectedSnapshot: []*mvccpb.KeyValue{makeKV("/k", "v2", 11)},
},
{
name: "put_contiguous_revision",
initialKVs: []*mvccpb.KeyValue{makeKV("/a", "A1", 20)},
initialRev: 20,
eventBatches: [][]*clientv3.Event{
{makePutEvent("/a", "A2", 21)},
{makePutEvent("/b", "B1", 22)},
{makePutEvent("/c", "C1", 23)},
},
expectedLatestRev: 23,
expectedSnapshot: []*mvccpb.KeyValue{makeKV("/a", "A2", 21), makeKV("/b", "B1", 22), makeKV("/c", "C1", 23)},
},
{
name: "put_single_non_contiguous_batch",
initialKVs: []*mvccpb.KeyValue{makeKV("/a", "A1", 20)},
initialRev: 20,
eventBatches: [][]*clientv3.Event{{makePutEvent("/a", "A2", 25)}},
expectedLatestRev: 25,
expectedSnapshot: []*mvccpb.KeyValue{makeKV("/a", "A2", 25)},
},
{
name: "put_multiple_non_contiguous_batches",
initialKVs: []*mvccpb.KeyValue{makeKV("/a", "A1", 21), makeKV("/b", "B1", 22)},
initialRev: 22,
eventBatches: [][]*clientv3.Event{
{makePutEvent("/a", "A2", 25)},
{makePutEvent("/b", "B2", 27)},
},
expectedLatestRev: 27,
expectedSnapshot: []*mvccpb.KeyValue{makeKV("/a", "A2", 25), makeKV("/b", "B2", 27)},
},
{
name: "apply_mixed_operations",
initialKVs: []*mvccpb.KeyValue{makeKV("/a", "A1", 20)},
initialRev: 20,
eventBatches: [][]*clientv3.Event{
{makePutEvent("/a", "A2", 21), makePutEvent("/b", "B1", 21), makePutEvent("/c", "C1", 21)},
{makePutEvent("/b", "B2", 22)},
{makeDelEvent("/c", 23), makePutEvent("/a", "A3", 23)},
{makePutEvent("/b", "B3", 24)},
},
expectedLatestRev: 24,
expectedSnapshot: []*mvccpb.KeyValue{makeKV("/a", "A3", 23), makeKV("/b", "B3", 24)},
},
{
name: "delete_same_key",
initialKVs: []*mvccpb.KeyValue{makeKV("/a", "X", 10)},
initialRev: 10,
eventBatches: [][]*clientv3.Event{
{makeDelEvent("/a", 11)},
},
expectedLatestRev: 11,
expectedSnapshot: nil,
},
{
name: "delete_nonexistent_returns_error",
initialKVs: []*mvccpb.KeyValue{makeKV("/p", "X", 5)},
initialRev: 5,
eventBatches: [][]*clientv3.Event{{makeDelEvent("/zzz", 6)}},
expectedLatestRev: 5,
expectedSnapshot: []*mvccpb.KeyValue{makeKV("/p", "X", 5)},
expectErr: true,
},
{
name: "mixed_delete_nonexistent_returns_error",
initialKVs: []*mvccpb.KeyValue{makeKV("/p", "X", 5)},
initialRev: 5,
eventBatches: [][]*clientv3.Event{{makeDelEvent("/zzz", 6), makePutEvent("/r", "Y", 6)}},
expectedLatestRev: 5,
expectedSnapshot: []*mvccpb.KeyValue{makeKV("/p", "X", 5)},
expectErr: true,
},
{
name: "delete_then_delete_again_returns_error",
initialKVs: []*mvccpb.KeyValue{makeKV("/p", "X", 5)},
initialRev: 5,
eventBatches: [][]*clientv3.Event{
{makeDelEvent("/p", 6)},
{makeDelEvent("/p", 7)},
},
expectedLatestRev: 6,
expectedSnapshot: nil,
expectErr: true,
},
{
name: "stale_batch_rejected",
initialKVs: []*mvccpb.KeyValue{makeKV("/x", "1", 20)},
initialRev: 20,
eventBatches: [][]*clientv3.Event{{makePutEvent("/x", "2", 19)}},
expectedLatestRev: 20,
expectedSnapshot: []*mvccpb.KeyValue{makeKV("/x", "1", 20)},
expectErr: true,
},
{
name: "mixed_stale_batch_returns_error",
initialKVs: []*mvccpb.KeyValue{makeKV("/x", "1", 20)},
initialRev: 20,
eventBatches: [][]*clientv3.Event{
{makePutEvent("/x", "should-not-apply", 19)},
{makeDelEvent("/x", 21), makePutEvent("/y", "new", 21)},
{makeDelEvent("/y", 22)},
},
expectedLatestRev: 20,
expectedSnapshot: []*mvccpb.KeyValue{makeKV("/x", "1", 20)},
expectErr: true,
},
}
for _, tt := range tests {
test := tt
t.Run(test.name, func(t *testing.T) {
s := newStore(4, 32)
s.Restore(test.initialKVs, test.initialRev)
var gotErr error
for batchIndex, batch := range test.eventBatches {
resp := clientv3.WatchResponse{Events: batch}
if err := s.Apply(resp); err != nil {
gotErr = err
if !test.expectErr {
t.Fatalf("Apply(batch %d) unexpected error: %v", batchIndex, err)
}
break
}
}
if test.expectErr && gotErr == nil {
t.Fatalf("expected Apply() to error, but got nil")
}
if latest := s.LatestRev(); latest != test.expectedLatestRev {
t.Fatalf("LatestRev=%d; want %d", latest, test.expectedLatestRev)
}
verifyStoreSnapshot(t, s, test.expectedSnapshot, test.expectedLatestRev, 0)
})
}
}
func TestStoreRestore(t *testing.T) {
type restoreSeq struct {
kvs []*mvccpb.KeyValue
rev int64
}
tests := []struct {
name string
seq []restoreSeq
expectedSnap []*mvccpb.KeyValue
expectedRev int64
}{
{
name: "rebuilds_tree_and_resets_rev",
seq: []restoreSeq{
{[]*mvccpb.KeyValue{makeKV("/a", "1", 3), makeKV("/b", "2", 3)}, 3},
{[]*mvccpb.KeyValue{makeKV("/c", "3", 15)}, 15},
},
expectedSnap: []*mvccpb.KeyValue{makeKV("/c", "3", 15)},
expectedRev: 15,
},
{
name: "restore_to_revision_zero_returns_ErrNotReady",
seq: []restoreSeq{
{[]*mvccpb.KeyValue{makeKV("/a", "1", 5)}, 5},
{nil, 0},
},
expectedSnap: nil,
expectedRev: 0,
},
{
name: "restore_empty_ready",
seq: []restoreSeq{{nil, 5}},
expectedSnap: nil,
expectedRev: 5,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
s := newStore(8, 32)
for _, step := range tt.seq {
s.Restore(step.kvs, step.rev)
}
if tt.expectedRev == 0 {
if _, _, err := s.Get([]byte("/"), []byte{0}, 0); !errors.Is(err, ErrNotReady) {
t.Fatalf("Get after restore to rev=0 err=%v; want %v", err, ErrNotReady)
}
return
}
verifyStoreSnapshot(t, s, tt.expectedSnap, tt.expectedRev, 0)
})
}
}
func TestRestoreAppendCloneImmutability(t *testing.T) {
tests := []struct {
name string
initialKVs []*mvccpb.KeyValue
initialRev int64
events []*clientv3.Event
requestedRev int64
expectedSnap []*mvccpb.KeyValue
expectedLatestSnap []*mvccpb.KeyValue
expectedLatestRev int64
}{
{
name: "put_overwrites_key",
initialKVs: []*mvccpb.KeyValue{makeKV("/k", "v1", 5)},
initialRev: 5,
events: []*clientv3.Event{makePutEvent("/k", "v2", 6)},
requestedRev: 5,
expectedSnap: []*mvccpb.KeyValue{makeKV("/k", "v1", 5)},
expectedLatestSnap: []*mvccpb.KeyValue{makeKV("/k", "v2", 6)},
expectedLatestRev: 6,
},
{
name: "delete_key",
initialKVs: []*mvccpb.KeyValue{makeKV("/k", "v1", 5)},
initialRev: 5,
events: []*clientv3.Event{makeDelEvent("/k", 6)},
requestedRev: 5,
expectedSnap: []*mvccpb.KeyValue{makeKV("/k", "v1", 5)},
expectedLatestSnap: nil,
expectedLatestRev: 6,
},
}
for _, tt := range tests {
test := tt
t.Run(test.name, func(t *testing.T) {
s := newStore(8, 32)
if test.initialKVs != nil {
s.Restore(test.initialKVs, test.initialRev)
}
if len(test.events) > 0 {
resp := clientv3.WatchResponse{Events: test.events}
if err := s.Apply(resp); err != nil {
t.Fatalf("Apply failed: %v", err)
}
}
if test.requestedRev != 0 {
verifyStoreSnapshot(t, s, test.expectedSnap, test.expectedLatestRev, test.requestedRev)
}
verifyStoreSnapshot(t, s, test.expectedLatestSnap, test.expectedLatestRev, test.expectedLatestRev)
})
}
}
func makeKV(key, val string, rev int64) *mvccpb.KeyValue {
return &mvccpb.KeyValue{Key: []byte(key), Value: []byte(val), ModRevision: rev, CreateRevision: rev, Version: 1}
}
func makePutEvent(key, val string, rev int64) *clientv3.Event {
return &clientv3.Event{Type: clientv3.EventTypePut, Kv: &mvccpb.KeyValue{Key: []byte(key), Value: []byte(val), ModRevision: rev, CreateRevision: rev, Version: 1}}
}
func makeDelEvent(key string, rev int64) *clientv3.Event {
return &clientv3.Event{Type: clientv3.EventTypeDelete, Kv: &mvccpb.KeyValue{Key: []byte(key), ModRevision: rev}}
}
func verifyStoreSnapshot(t *testing.T, s *store, want []*mvccpb.KeyValue, wantRev int64, requestedRev int64) {
kvs, headerRev, err := s.Get([]byte("/"), []byte{0}, requestedRev)
if err != nil {
t.Fatalf("Get all keys (rev=%d): got error: %v", requestedRev, err)
}
latestRev := s.LatestRev()
if headerRev != latestRev {
t.Fatalf("header rev=%d; want latest %d (requestedRev=%d)", latestRev, wantRev, requestedRev)
}
if diff := cmp.Diff(want, kvs); diff != "" {
t.Fatalf("snapshot mismatch (requestedRev=%d) (-want +got):\n%s", requestedRev, diff)
}
}
================================================
FILE: cache/watcher.go
================================================
// Copyright 2025 The etcd Authors
//
// 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 cache
import (
"sync"
"go.etcd.io/etcd/api/v3/v3rpc/rpctypes"
clientv3 "go.etcd.io/etcd/client/v3"
)
// watcher holds one client’s buffered stream of events.
type watcher struct {
respCh chan clientv3.WatchResponse
cancelResp *clientv3.WatchResponse
keyPred KeyPredicate
stopOnce sync.Once
}
func newWatcher(bufSize int, pred KeyPredicate) *watcher {
return &watcher{
respCh: make(chan clientv3.WatchResponse, bufSize),
keyPred: pred,
}
}
// true -> events delivered (or filtered/duplicate)
// false -> buffer full (caller should mark watcher “lagging”)
func (w *watcher) enqueueResponse(resp clientv3.WatchResponse) bool {
if !resp.IsProgressNotify() && w.keyPred != nil {
filtered := make([]*clientv3.Event, 0, len(resp.Events))
for _, event := range resp.Events {
if w.keyPred(event.Kv.Key) {
filtered = append(filtered, event)
}
}
if len(filtered) == 0 {
return true
}
resp.Events = filtered
}
select {
case w.respCh <- resp:
return true
default:
return false
}
}
func (w *watcher) Compact(compactRev int64) {
resp := &clientv3.WatchResponse{
Canceled: true,
CompactRevision: compactRev,
CancelReason: rpctypes.ErrCompacted.Error(),
}
w.stopOnce.Do(func() {
w.cancelResp = resp
close(w.respCh)
})
}
// Stop closes the event channel atomically.
func (w *watcher) Stop() {
w.stopOnce.Do(func() {
close(w.respCh)
})
}
================================================
FILE: client/pkg/.gomodguard.yaml
================================================
---
blocked:
modules:
- go.etcd.io/etcd:
reason: "Forbidden dependency"
- go.etcd.io/etcd/api/v3:
reason: "Forbidden dependency"
- go.etcd.io/etcd/pkg/v3:
reason: "Forbidden dependency"
- go.etcd.io/etcd/server/v3:
reason: "Forbidden dependency"
- go.etcd.io/etcd/tests/v3:
reason: "Forbidden dependency"
- go.etcd.io/etcd/v3:
reason: "Forbidden dependency"
================================================
FILE: client/pkg/LICENSE
================================================
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright 2020 The etcd Authors
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.
================================================
FILE: client/pkg/fileutil/dir_unix.go
================================================
// Copyright 2016 The etcd Authors
//
// 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.
//go:build !windows
package fileutil
import "os"
const (
// PrivateDirMode grants owner to make/remove files inside the directory.
PrivateDirMode = 0o700
)
// OpenDir opens a directory for syncing.
func OpenDir(path string) (*os.File, error) { return os.Open(path) }
================================================
FILE: client/pkg/fileutil/dir_windows.go
================================================
// Copyright 2016 The etcd Authors
//
// 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.
//go:build windows
package fileutil
import (
"os"
"syscall"
)
const (
// PrivateDirMode grants owner to make/remove files inside the directory.
PrivateDirMode = 0o777
)
// OpenDir opens a directory in windows with write access for syncing.
func OpenDir(path string) (*os.File, error) {
fd, err := openDir(path)
if err != nil {
return nil, err
}
return os.NewFile(uintptr(fd), path), nil
}
func openDir(path string) (fd syscall.Handle, err error) {
if len(path) == 0 {
return syscall.InvalidHandle, syscall.ERROR_FILE_NOT_FOUND
}
pathp, err := syscall.UTF16PtrFromString(path)
if err != nil {
return syscall.InvalidHandle, err
}
access := uint32(syscall.GENERIC_READ | syscall.GENERIC_WRITE)
sharemode := uint32(syscall.FILE_SHARE_READ | syscall.FILE_SHARE_WRITE)
createmode := uint32(syscall.OPEN_EXISTING)
fl := uint32(syscall.FILE_FLAG_BACKUP_SEMANTICS)
return syscall.CreateFile(pathp, access, sharemode, nil, createmode, fl, 0)
}
================================================
FILE: client/pkg/fileutil/doc.go
================================================
// Copyright 2018 The etcd Authors
//
// 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 fileutil implements utility functions related to files and paths.
package fileutil
================================================
FILE: client/pkg/fileutil/filereader.go
================================================
// Copyright 2022 The etcd Authors
//
// 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 fileutil
import (
"bufio"
"io"
"io/fs"
"os"
)
// FileReader is a wrapper of io.Reader. It also provides file info.
type FileReader interface {
io.Reader
FileInfo() (fs.FileInfo, error)
}
type fileReader struct {
*os.File
}
func NewFileReader(f *os.File) FileReader {
return &fileReader{f}
}
func (fr *fileReader) FileInfo() (fs.FileInfo, error) {
return fr.Stat()
}
// FileBufReader is a wrapper of bufio.Reader. It also provides file info.
type FileBufReader struct {
*bufio.Reader
fi fs.FileInfo
}
func NewFileBufReader(fr FileReader) *FileBufReader {
bufReader := bufio.NewReader(fr)
fi, err := fr.FileInfo()
if err != nil {
// This should never happen.
panic(err)
}
return &FileBufReader{bufReader, fi}
}
func (fbr *FileBufReader) FileInfo() fs.FileInfo {
return fbr.fi
}
================================================
FILE: client/pkg/fileutil/filereader_test.go
================================================
// Copyright 2022 The etcd Authors
//
// 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 fileutil
import (
"os"
"strings"
"testing"
"github.com/stretchr/testify/assert"
)
func TestFileBufReader(t *testing.T) {
f, err := os.CreateTemp(t.TempDir(), "wal")
if err != nil {
t.Errorf("Unexpected error: %v", err)
}
fi, err := f.Stat()
if err != nil {
t.Errorf("Unexpected error: %v", err)
}
fbr := NewFileBufReader(NewFileReader(f))
if !strings.HasPrefix(fbr.FileInfo().Name(), "wal") {
t.Errorf("Unexpected file name: %s", fbr.FileInfo().Name())
}
assert.Equal(t, fi.Size(), fbr.FileInfo().Size())
assert.Equal(t, fi.IsDir(), fbr.FileInfo().IsDir())
assert.Equal(t, fi.Mode(), fbr.FileInfo().Mode())
assert.Equal(t, fi.ModTime(), fbr.FileInfo().ModTime())
}
================================================
FILE: client/pkg/fileutil/fileutil.go
================================================
// Copyright 2015 The etcd Authors
//
// 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 fileutil
import (
"fmt"
"io"
"io/fs"
"os"
"path/filepath"
"go.uber.org/zap"
"go.etcd.io/etcd/client/pkg/v3/verify"
)
const (
// PrivateFileMode grants owner to read/write a file.
PrivateFileMode = 0o600
)
// IsDirWriteable checks if dir is writable by writing and removing a file
// to dir. It returns nil if dir is writable.
func IsDirWriteable(dir string) error {
f, err := filepath.Abs(filepath.Join(dir, ".touch"))
if err != nil {
return err
}
if err := os.WriteFile(f, []byte(""), PrivateFileMode); err != nil {
return err
}
return os.Remove(f)
}
// TouchDirAll is similar to os.MkdirAll. It creates directories with 0700 permission if any directory
// does not exists. TouchDirAll also ensures the given directory is writable.
func TouchDirAll(lg *zap.Logger, dir string) error {
verify.Assert(lg != nil, "nil log isn't allowed")
// If path is already a directory, MkdirAll does nothing and returns nil, so,
// first check if dir exists with an expected permission mode.
if Exist(dir) {
err := CheckDirPermission(dir, PrivateDirMode)
if err != nil {
lg.Warn("check file permission", zap.Error(err))
}
} else {
err := os.MkdirAll(dir, PrivateDirMode)
if err != nil {
// if mkdirAll("a/text") and "text" is not
// a directory, this will return syscall.ENOTDIR
return err
}
}
return IsDirWriteable(dir)
}
// CreateDirAll is similar to TouchDirAll but returns error
// if the deepest directory was not empty.
func CreateDirAll(lg *zap.Logger, dir string) error {
err := TouchDirAll(lg, dir)
if err == nil {
var ns []string
ns, err = ReadDir(dir)
if err != nil {
return err
}
if len(ns) != 0 {
err = fmt.Errorf("expected %q to be empty, got %q", dir, ns)
}
}
return err
}
// Exist returns true if a file or directory exists.
func Exist(name string) bool {
_, err := os.Stat(name)
return err == nil
}
// DirEmpty returns true if a directory empty and can access.
func DirEmpty(name string) bool {
ns, err := ReadDir(name)
return len(ns) == 0 && err == nil
}
// ZeroToEnd zeros a file starting from SEEK_CUR to its SEEK_END. May temporarily
// shorten the length of the file.
func ZeroToEnd(f *os.File) error {
// TODO: support FALLOC_FL_ZERO_RANGE
off, err := f.Seek(0, io.SeekCurrent)
if err != nil {
return err
}
lenf, lerr := f.Seek(0, io.SeekEnd)
if lerr != nil {
return lerr
}
if err = f.Truncate(off); err != nil {
return err
}
// make sure blocks remain allocated
if err = Preallocate(f, lenf, true); err != nil {
return err
}
_, err = f.Seek(off, io.SeekStart)
return err
}
// CheckDirPermission checks permission on an existing dir.
// Returns error if dir is empty or exist with a different permission than specified.
func CheckDirPermission(dir string, perm os.FileMode) error {
if !Exist(dir) {
return fmt.Errorf("directory %q empty, cannot check permission", dir)
}
// check the existing permission on the directory
dirInfo, err := os.Stat(dir)
if err != nil {
return err
}
dirMode := dirInfo.Mode().Perm()
if dirMode != perm {
err = fmt.Errorf("directory %q exist, but the permission is %q. The recommended permission is %q to prevent possible unprivileged access to the data", dir, dirInfo.Mode(), os.FileMode(PrivateDirMode))
return err
}
return nil
}
// RemoveMatchFile deletes file if matchFunc is true on an existing dir
// Returns error if the dir does not exist or remove file fail
func RemoveMatchFile(lg *zap.Logger, dir string, matchFunc func(fileName string) bool) error {
if lg == nil {
lg = zap.NewNop()
}
if !Exist(dir) {
return fmt.Errorf("directory %s does not exist", dir)
}
fileNames, err := ReadDir(dir)
if err != nil {
return err
}
var removeFailedFiles []string
for _, fileName := range fileNames {
if matchFunc(fileName) {
file := filepath.Join(dir, fileName)
if err = os.Remove(file); err != nil {
removeFailedFiles = append(removeFailedFiles, fileName)
lg.Error("remove file failed",
zap.String("file", file),
zap.Error(err))
}
}
}
if len(removeFailedFiles) != 0 {
return fmt.Errorf("remove file(s) %v error", removeFailedFiles)
}
return nil
}
// ListFiles lists files if matchFunc is true on an existing dir
// Returns error if the dir does not exist
func ListFiles(dir string, matchFunc func(fileName string) bool) ([]string, error) {
var files []string
err := filepath.Walk(dir, func(path string, info fs.FileInfo, err error) error {
if matchFunc(path) {
files = append(files, path)
}
return nil
})
return files, err
}
================================================
FILE: client/pkg/fileutil/fileutil_test.go
================================================
// Copyright 2015 The etcd Authors
//
// 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 fileutil
import (
"fmt"
"io"
"math/rand"
"os"
"os/user"
"path/filepath"
"runtime"
"strings"
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"go.uber.org/zap/zaptest"
)
func TestIsDirWriteable(t *testing.T) {
tmpdir := t.TempDir()
require.NoErrorf(t, IsDirWriteable(tmpdir), "unexpected IsDirWriteable error")
require.NoErrorf(t, os.Chmod(tmpdir, 0o444), "unexpected os.Chmod error")
me, err := user.Current()
if err != nil {
// err can be non-nil when cross compiled
// http://stackoverflow.com/questions/20609415/cross-compiling-user-current-not-implemented-on-linux-amd64
t.Skipf("failed to get current user: %v", err)
}
if me.Name == "root" || runtime.GOOS == "windows" {
// ideally we should check CAP_DAC_OVERRIDE.
// but it does not matter for tests.
// Chmod is not supported under windows.
t.Skipf("running as a superuser or in windows")
}
require.Errorf(t, IsDirWriteable(tmpdir), "expected IsDirWriteable to error")
}
func TestCreateDirAll(t *testing.T) {
tmpdir := t.TempDir()
tmpdir2 := filepath.Join(tmpdir, "testdir")
require.NoError(t, CreateDirAll(zaptest.NewLogger(t), tmpdir2))
require.NoError(t, os.WriteFile(filepath.Join(tmpdir2, "text.txt"), []byte("test text"), PrivateFileMode))
if err := CreateDirAll(zaptest.NewLogger(t), tmpdir2); err == nil || !strings.Contains(err.Error(), "to be empty, got") {
t.Fatalf("unexpected error %v", err)
}
}
func TestExist(t *testing.T) {
fdir := filepath.Join(os.TempDir(), fmt.Sprint(time.Now().UnixNano()+rand.Int63n(1000)))
os.RemoveAll(fdir)
if err := os.Mkdir(fdir, 0o666); err != nil {
t.Skip(err)
}
defer os.RemoveAll(fdir)
require.Truef(t, Exist(fdir), "expected Exist true, got %v", Exist(fdir))
f, err := os.CreateTemp(os.TempDir(), "fileutil")
require.NoError(t, err)
f.Close()
if g := Exist(f.Name()); !g {
t.Errorf("exist = %v, want true", g)
}
os.Remove(f.Name())
if g := Exist(f.Name()); g {
t.Errorf("exist = %v, want false", g)
}
}
func TestDirEmpty(t *testing.T) {
dir := t.TempDir()
require.Truef(t, DirEmpty(dir), "expected DirEmpty true, got %v", DirEmpty(dir))
file, err := os.CreateTemp(dir, "new_file")
require.NoError(t, err)
file.Close()
require.Falsef(t, DirEmpty(dir), "expected DirEmpty false, got %v", DirEmpty(dir))
require.Falsef(t, DirEmpty(file.Name()), "expected DirEmpty false, got %v", DirEmpty(file.Name()))
}
func TestZeroToEnd(t *testing.T) {
f, err := os.CreateTemp(os.TempDir(), "fileutil")
require.NoError(t, err)
defer os.Remove(f.Name())
defer f.Close()
// Ensure 0 size is a nop so zero-to-end on an empty file won't give EINVAL.
require.NoError(t, ZeroToEnd(f))
b := make([]byte, 1024)
for i := range b {
b[i] = 12
}
_, err = f.Write(b)
require.NoError(t, err)
_, err = f.Seek(512, io.SeekStart)
require.NoError(t, err)
require.NoError(t, ZeroToEnd(f))
off, serr := f.Seek(0, io.SeekCurrent)
require.NoError(t, serr)
require.Equalf(t, int64(512), off, "expected offset 512, got %d", off)
b = make([]byte, 512)
_, err = f.Read(b)
require.NoError(t, err)
for i := range b {
if b[i] != 0 {
t.Errorf("expected b[%d] = 0, got %d", i, b[i])
}
}
}
func TestDirPermission(t *testing.T) {
tmpdir := t.TempDir()
tmpdir2 := filepath.Join(tmpdir, "testpermission")
// create a new dir with 0700
require.NoError(t, CreateDirAll(zaptest.NewLogger(t), tmpdir2))
// check dir permission with mode different than created dir
if err := CheckDirPermission(tmpdir2, 0o600); err == nil {
t.Errorf("expected error, got nil")
}
}
func TestRemoveMatchFile(t *testing.T) {
tmpdir := t.TempDir()
f, err := os.CreateTemp(tmpdir, "tmp")
require.NoError(t, err)
f.Close()
f, err = os.CreateTemp(tmpdir, "foo.tmp")
require.NoError(t, err)
f.Close()
err = RemoveMatchFile(zaptest.NewLogger(t), tmpdir, func(fileName string) bool {
return strings.HasPrefix(fileName, "tmp")
})
if err != nil {
t.Errorf("expected nil, got error")
}
fnames, err := ReadDir(tmpdir)
require.NoError(t, err)
if len(fnames) != 1 {
t.Errorf("expected exist 1 files, got %d", len(fnames))
}
f, err = os.CreateTemp(tmpdir, "tmp")
require.NoError(t, err)
f.Close()
err = RemoveMatchFile(zaptest.NewLogger(t), tmpdir, func(fileName string) bool {
os.Remove(filepath.Join(tmpdir, fileName))
return strings.HasPrefix(fileName, "tmp")
})
if err == nil {
t.Errorf("expected error, got nil")
}
}
func TestTouchDirAll(t *testing.T) {
tmpdir := t.TempDir()
assert.Panicsf(t, func() {
TouchDirAll(nil, tmpdir)
}, "expected panic with nil log")
assert.NoError(t, TouchDirAll(zaptest.NewLogger(t), tmpdir))
}
================================================
FILE: client/pkg/fileutil/lock.go
================================================
// Copyright 2016 The etcd Authors
//
// 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 fileutil
import (
"errors"
"os"
)
var ErrLocked = errors.New("fileutil: file already locked")
type LockedFile struct{ *os.File }
================================================
FILE: client/pkg/fileutil/lock_flock.go
================================================
// Copyright 2016 The etcd Authors
//
// 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.
//go:build !windows && !plan9 && !solaris
package fileutil
import (
"errors"
"os"
"syscall"
)
func flockTryLockFile(path string, flag int, perm os.FileMode) (*LockedFile, error) {
f, err := os.OpenFile(path, flag, perm)
if err != nil {
return nil, err
}
if err = syscall.Flock(int(f.Fd()), syscall.LOCK_EX|syscall.LOCK_NB); err != nil {
f.Close()
if errors.Is(err, syscall.EWOULDBLOCK) {
err = ErrLocked
}
return nil, err
}
return &LockedFile{f}, nil
}
func flockLockFile(path string, flag int, perm os.FileMode) (*LockedFile, error) {
f, err := os.OpenFile(path, flag, perm)
if err != nil {
return nil, err
}
if err = syscall.Flock(int(f.Fd()), syscall.LOCK_EX); err != nil {
f.Close()
return nil, err
}
return &LockedFile{f}, err
}
================================================
FILE: client/pkg/fileutil/lock_linux.go
================================================
// Copyright 2016 The etcd Authors
//
// 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.
//go:build linux
package fileutil
import (
"errors"
"fmt"
"io"
"os"
"syscall"
"golang.org/x/sys/unix"
)
// This used to call syscall.Flock() but that call fails with EBADF on NFS.
// An alternative is lockf() which works on NFS but that call lets a process lock
// the same file twice. Instead, use Linux's non-standard open file descriptor
// locks which will block if the process already holds the file lock.
var (
wrlck = syscall.Flock_t{
Type: syscall.F_WRLCK,
Whence: int16(io.SeekStart),
Start: 0,
Len: 0,
}
linuxTryLockFile = flockTryLockFile
linuxLockFile = flockLockFile
)
func init() {
// use open file descriptor locks if the system supports it
getlk := syscall.Flock_t{Type: syscall.F_RDLCK}
if err := syscall.FcntlFlock(0, unix.F_OFD_GETLK, &getlk); err == nil {
linuxTryLockFile = ofdTryLockFile
linuxLockFile = ofdLockFile
}
}
func TryLockFile(path string, flag int, perm os.FileMode) (*LockedFile, error) {
return linuxTryLockFile(path, flag, perm)
}
func ofdTryLockFile(path string, flag int, perm os.FileMode) (*LockedFile, error) {
f, err := os.OpenFile(path, flag, perm)
if err != nil {
return nil, fmt.Errorf("ofdTryLockFile failed to open %q (%w)", path, err)
}
flock := wrlck
if err = syscall.FcntlFlock(f.Fd(), unix.F_OFD_SETLK, &flock); err != nil {
f.Close()
if errors.Is(err, syscall.EWOULDBLOCK) {
err = ErrLocked
}
return nil, err
}
return &LockedFile{f}, nil
}
func LockFile(path string, flag int, perm os.FileMode) (*LockedFile, error) {
return linuxLockFile(path, flag, perm)
}
func ofdLockFile(path string, flag int, perm os.FileMode) (*LockedFile, error) {
f, err := os.OpenFile(path, flag, perm)
if err != nil {
return nil, fmt.Errorf("ofdLockFile failed to open %q (%w)", path, err)
}
flock := wrlck
err = syscall.FcntlFlock(f.Fd(), unix.F_OFD_SETLKW, &flock)
if err != nil {
f.Close()
return nil, err
}
return &LockedFile{f}, nil
}
================================================
FILE: client/pkg/fileutil/lock_linux_test.go
================================================
// Copyright 2017 The etcd Authors
//
// 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.
//go:build linux
package fileutil
import "testing"
// TestLockAndUnlockSyscallFlock tests the fallback flock using the flock syscall.
func TestLockAndUnlockSyscallFlock(t *testing.T) {
oldTryLock, oldLock := linuxTryLockFile, linuxLockFile
defer func() {
linuxTryLockFile, linuxLockFile = oldTryLock, oldLock
}()
linuxTryLockFile, linuxLockFile = flockTryLockFile, flockLockFile
TestLockAndUnlock(t)
}
================================================
FILE: client/pkg/fileutil/lock_plan9.go
================================================
// Copyright 2015 The etcd Authors
//
// 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 fileutil
import (
"os"
"syscall"
"time"
)
func TryLockFile(path string, flag int, perm os.FileMode) (*LockedFile, error) {
if err := os.Chmod(path, syscall.DMEXCL|PrivateFileMode); err != nil {
return nil, err
}
f, err := os.Open(path, flag, perm)
if err != nil {
return nil, ErrLocked
}
return &LockedFile{f}, nil
}
func LockFile(path string, flag int, perm os.FileMode) (*LockedFile, error) {
if err := os.Chmod(path, syscall.DMEXCL|PrivateFileMode); err != nil {
return nil, err
}
for {
f, err := os.OpenFile(path, flag, perm)
if err == nil {
return &LockedFile{f}, nil
}
time.Sleep(10 * time.Millisecond)
}
}
================================================
FILE: client/pkg/fileutil/lock_solaris.go
================================================
// Copyright 2015 The etcd Authors
//
// 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.
//go:build solaris
package fileutil
import (
"os"
"syscall"
)
func TryLockFile(path string, flag int, perm os.FileMode) (*LockedFile, error) {
var lock syscall.Flock_t
lock.Start = 0
lock.Len = 0
lock.Pid = 0
lock.Type = syscall.F_WRLCK
lock.Whence = 0
lock.Pid = 0
f, err := os.OpenFile(path, flag, perm)
if err != nil {
return nil, err
}
if err := syscall.FcntlFlock(f.Fd(), syscall.F_SETLK, &lock); err != nil {
f.Close()
if err == syscall.EAGAIN {
err = ErrLocked
}
return nil, err
}
return &LockedFile{f}, nil
}
func LockFile(path string, flag int, perm os.FileMode) (*LockedFile, error) {
var lock syscall.Flock_t
lock.Start = 0
lock.Len = 0
lock.Pid = 0
lock.Type = syscall.F_WRLCK
lock.Whence = 0
f, err := os.OpenFile(path, flag, perm)
if err != nil {
return nil, err
}
if err = syscall.FcntlFlock(f.Fd(), syscall.F_SETLKW, &lock); err != nil {
f.Close()
return nil, err
}
return &LockedFile{f}, nil
}
================================================
FILE: client/pkg/fileutil/lock_test.go
================================================
// Copyright 2015 The etcd Authors
//
// 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 fileutil
import (
"os"
"testing"
"time"
"github.com/stretchr/testify/require"
)
func TestLockAndUnlock(t *testing.T) {
f, err := os.CreateTemp(t.TempDir(), "lock")
require.NoError(t, err)
f.Close()
defer func() {
require.NoError(t, os.Remove(f.Name()))
}()
// lock the file
l, err := LockFile(f.Name(), os.O_WRONLY, PrivateFileMode)
require.NoError(t, err)
// try lock a locked file
_, err = TryLockFile(f.Name(), os.O_WRONLY, PrivateFileMode)
require.ErrorIs(t, err, ErrLocked)
// unlock the file
require.NoError(t, l.Close())
// try lock the unlocked file
dupl, err := TryLockFile(f.Name(), os.O_WRONLY, PrivateFileMode)
if err != nil {
t.Errorf("err = %v, want %v", err, nil)
}
// blocking on locked file
locked := make(chan struct{}, 1)
go func() {
bl, blerr := LockFile(f.Name(), os.O_WRONLY, PrivateFileMode)
if blerr != nil {
t.Error(blerr)
}
locked <- struct{}{}
if blerr = bl.Close(); blerr != nil {
t.Error(blerr)
}
}()
select {
case <-locked:
t.Error("unexpected unblocking")
case <-time.After(100 * time.Millisecond):
}
// unlock
require.NoError(t, dupl.Close())
// the previously blocked routine should be unblocked
select {
case <-locked:
case <-time.After(1 * time.Second):
t.Error("unexpected blocking")
}
}
================================================
FILE: client/pkg/fileutil/lock_unix.go
================================================
// Copyright 2015 The etcd Authors
//
// 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.
//go:build !windows && !plan9 && !solaris && !linux
package fileutil
import (
"os"
)
func TryLockFile(path string, flag int, perm os.FileMode) (*LockedFile, error) {
return flockTryLockFile(path, flag, perm)
}
func LockFile(path string, flag int, perm os.FileMode) (*LockedFile, error) {
return flockLockFile(path, flag, perm)
}
================================================
FILE: client/pkg/fileutil/lock_windows.go
================================================
// Copyright 2015 The etcd Authors
//
// 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.
//go:build windows
package fileutil
import (
"errors"
"fmt"
"os"
"syscall"
"golang.org/x/sys/windows"
)
var errLocked = errors.New("the process cannot access the file because another process has locked a portion of the file")
func TryLockFile(path string, flag int, perm os.FileMode) (*LockedFile, error) {
f, err := open(path, flag, perm)
if err != nil {
return nil, err
}
if err := lockFile(windows.Handle(f.Fd()), windows.LOCKFILE_FAIL_IMMEDIATELY); err != nil {
f.Close()
return nil, err
}
return &LockedFile{f}, nil
}
func LockFile(path string, flag int, perm os.FileMode) (*LockedFile, error) {
f, err := open(path, flag, perm)
if err != nil {
return nil, err
}
if err := lockFile(windows.Handle(f.Fd()), 0); err != nil {
f.Close()
return nil, err
}
return &LockedFile{f}, nil
}
func open(path string, flag int, perm os.FileMode) (*os.File, error) {
if path == "" {
return nil, errors.New("cannot open empty filename")
}
var access uint32
switch flag {
case syscall.O_RDONLY:
access = syscall.GENERIC_READ
case syscall.O_WRONLY:
access = syscall.GENERIC_WRITE
case syscall.O_RDWR:
access = syscall.GENERIC_READ | syscall.GENERIC_WRITE
case syscall.O_WRONLY | syscall.O_CREAT:
access = syscall.GENERIC_ALL
default:
panic(fmt.Errorf("flag %v is not supported", flag))
}
fd, err := syscall.CreateFile(&(syscall.StringToUTF16(path)[0]),
access,
syscall.FILE_SHARE_READ|syscall.FILE_SHARE_WRITE|syscall.FILE_SHARE_DELETE,
nil,
syscall.OPEN_ALWAYS,
syscall.FILE_ATTRIBUTE_NORMAL,
0)
if err != nil {
return nil, err
}
return os.NewFile(uintptr(fd), path), nil
}
func lockFile(fd windows.Handle, flags uint32) error {
if fd == windows.InvalidHandle {
return nil
}
err := windows.LockFileEx(fd, flags|windows.LOCKFILE_EXCLUSIVE_LOCK, 0, 1, 0, &windows.Overlapped{})
if err == nil {
return nil
} else if err.Error() == errLocked.Error() {
return ErrLocked
} else if err != windows.ERROR_LOCK_VIOLATION {
return err
}
return nil
}
================================================
FILE: client/pkg/fileutil/preallocate.go
================================================
// Copyright 2015 The etcd Authors
//
// 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 fileutil
import (
"io"
"os"
)
// Preallocate tries to allocate the space for given file. This
// operation is only supported on darwin and linux by a few
// filesystems (APFS, btrfs, ext4, etc.).
// If the operation is unsupported, no error will be returned.
// Otherwise, the error encountered will be returned.
func Preallocate(f *os.File, sizeInBytes int64, extendFile bool) error {
if sizeInBytes == 0 {
// fallocate will return EINVAL if length is 0; skip
return nil
}
if extendFile {
return preallocExtend(f, sizeInBytes)
}
return preallocFixed(f, sizeInBytes)
}
func preallocExtendTrunc(f *os.File, sizeInBytes int64) error {
curOff, err := f.Seek(0, io.SeekCurrent)
if err != nil {
return err
}
size, err := f.Seek(sizeInBytes, io.SeekEnd)
if err != nil {
return err
}
if _, err = f.Seek(curOff, io.SeekStart); err != nil {
return err
}
if sizeInBytes > size {
return nil
}
return f.Truncate(sizeInBytes)
}
================================================
FILE: client/pkg/fileutil/preallocate_darwin.go
================================================
// Copyright 2016 The etcd Authors
//
// 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.
//go:build darwin
package fileutil
import (
"errors"
"os"
"syscall"
"golang.org/x/sys/unix"
)
func preallocExtend(f *os.File, sizeInBytes int64) error {
if err := preallocFixed(f, sizeInBytes); err != nil {
return err
}
return preallocExtendTrunc(f, sizeInBytes)
}
func preallocFixed(f *os.File, sizeInBytes int64) error {
// allocate all requested space or no space at all
// TODO: allocate contiguous space on disk with F_ALLOCATECONTIG flag
fstore := &unix.Fstore_t{
Flags: unix.F_ALLOCATEALL,
Posmode: unix.F_PEOFPOSMODE,
Length: sizeInBytes,
}
err := unix.FcntlFstore(f.Fd(), unix.F_PREALLOCATE, fstore)
if err == nil || errors.Is(err, unix.ENOTSUP) {
return nil
}
// wrong argument to fallocate syscall
if err == unix.EINVAL {
// filesystem "st_blocks" are allocated in the units of
// "Allocation Block Size" (run "diskutil info /" command)
var stat syscall.Stat_t
syscall.Fstat(int(f.Fd()), &stat)
// syscall.Statfs_t.Bsize is "optimal transfer block size"
// and contains matching 4096 value when latest OS X kernel
// supports 4,096 KB filesystem block size
var statfs syscall.Statfs_t
syscall.Fstatfs(int(f.Fd()), &statfs)
blockSize := int64(statfs.Bsize)
if stat.Blocks*blockSize >= sizeInBytes {
// enough blocks are already allocated
return nil
}
}
return err
}
================================================
FILE: client/pkg/fileutil/preallocate_test.go
================================================
// Copyright 2015 The etcd Authors
//
// 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 fileutil
import (
"os"
"testing"
"github.com/stretchr/testify/require"
)
func TestPreallocateExtend(t *testing.T) {
pf := func(f *os.File, sz int64) error { return Preallocate(f, sz, true) }
tf := func(t *testing.T, f *os.File) {
t.Helper()
testPreallocateExtend(t, f, pf)
}
runPreallocTest(t, tf)
}
func TestPreallocateExtendTrunc(t *testing.T) {
tf := func(t *testing.T, f *os.File) {
t.Helper()
testPreallocateExtend(t, f, preallocExtendTrunc)
}
runPreallocTest(t, tf)
}
func testPreallocateExtend(t *testing.T, f *os.File, pf func(*os.File, int64) error) {
t.Helper()
size := int64(64 * 1000)
require.NoError(t, pf(f, size))
stat, err := f.Stat()
require.NoError(t, err)
if stat.Size() != size {
t.Errorf("size = %d, want %d", stat.Size(), size)
}
}
func TestPreallocateFixed(t *testing.T) { runPreallocTest(t, testPreallocateFixed) }
func testPreallocateFixed(t *testing.T, f *os.File) {
t.Helper()
size := int64(64 * 1000)
require.NoError(t, Preallocate(f, size, false))
stat, err := f.Stat()
require.NoError(t, err)
if stat.Size() != 0 {
t.Errorf("size = %d, want %d", stat.Size(), 0)
}
}
func runPreallocTest(t *testing.T, test func(*testing.T, *os.File)) {
t.Helper()
p := t.TempDir()
f, err := os.CreateTemp(p, "")
require.NoError(t, err)
test(t, f)
}
================================================
FILE: client/pkg/fileutil/preallocate_unix.go
================================================
// Copyright 2016 The etcd Authors
//
// 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.
//go:build linux
package fileutil
import (
"errors"
"os"
"syscall"
)
func preallocExtend(f *os.File, sizeInBytes int64) error {
// use mode = 0 to change size
err := syscall.Fallocate(int(f.Fd()), 0, 0, sizeInBytes)
if err != nil {
var errno syscall.Errno
// not supported; fallback
// fallocate EINTRs frequently in some environments; fallback
if errors.As(err, &errno) && (errno == syscall.ENOTSUP || errno == syscall.EINTR) {
return preallocExtendTrunc(f, sizeInBytes)
}
}
return err
}
func preallocFixed(f *os.File, sizeInBytes int64) error {
// use mode = 1 to keep size; see FALLOC_FL_KEEP_SIZE
err := syscall.Fallocate(int(f.Fd()), 1, 0, sizeInBytes)
if err != nil {
var errno syscall.Errno
// treat not supported as nil error
if errors.As(err, &errno) && errno == syscall.ENOTSUP {
return nil
}
}
return err
}
================================================
FILE: client/pkg/fileutil/preallocate_unsupported.go
================================================
// Copyright 2015 The etcd Authors
//
// 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.
//go:build !linux && !darwin
package fileutil
import "os"
func preallocExtend(f *os.File, sizeInBytes int64) error {
return preallocExtendTrunc(f, sizeInBytes)
}
func preallocFixed(f *os.File, sizeInBytes int64) error { return nil }
================================================
FILE: client/pkg/fileutil/purge.go
================================================
// Copyright 2015 The etcd Authors
//
// 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 fileutil
import (
"os"
"path/filepath"
"strings"
"time"
"go.uber.org/zap"
)
func PurgeFile(lg *zap.Logger, dirname string, suffix string, max uint, interval time.Duration, stop <-chan struct{}) <-chan error {
return purgeFile(lg, dirname, suffix, max, interval, stop, nil, nil, true)
}
func PurgeFileWithDoneNotify(lg *zap.Logger, dirname string, suffix string, max uint, interval time.Duration, stop <-chan struct{}) (<-chan struct{}, <-chan error) {
doneC := make(chan struct{})
errC := purgeFile(lg, dirname, suffix, max, interval, stop, nil, doneC, true)
return doneC, errC
}
func PurgeFileWithoutFlock(lg *zap.Logger, dirname string, suffix string, max uint, interval time.Duration, stop <-chan struct{}) (<-chan struct{}, <-chan error) {
doneC := make(chan struct{})
errC := purgeFile(lg, dirname, suffix, max, interval, stop, nil, doneC, false)
return doneC, errC
}
// purgeFile is the internal implementation for PurgeFile which can post purged files to purgec if non-nil.
// if donec is non-nil, the function closes it to notify its exit.
func purgeFile(lg *zap.Logger, dirname string, suffix string, max uint, interval time.Duration, stop <-chan struct{}, purgec chan<- string, donec chan<- struct{}, flock bool) <-chan error {
if lg == nil {
lg = zap.NewNop()
}
errC := make(chan error, 1)
lg.Info("started to purge file",
zap.String("dir", dirname),
zap.String("suffix", suffix),
zap.Uint("max", max),
zap.Duration("interval", interval))
go func() {
if donec != nil {
defer close(donec)
}
for {
fnamesWithSuffix, err := readDirWithSuffix(dirname, suffix)
if err != nil {
errC <- err
return
}
nPurged := 0
for nPurged < len(fnamesWithSuffix)-int(max) {
f := filepath.Join(dirname, fnamesWithSuffix[nPurged])
var l *LockedFile
if flock {
l, err = TryLockFile(f, os.O_WRONLY, PrivateFileMode)
if err != nil {
lg.Warn("failed to lock file", zap.String("path", f), zap.Error(err))
break
}
}
if err = os.Remove(f); err != nil {
lg.Error("failed to remove file", zap.String("path", f), zap.Error(err))
errC <- err
return
}
if flock {
if err = l.Close(); err != nil {
lg.Error("failed to unlock/close", zap.String("path", l.Name()), zap.Error(err))
errC <- err
return
}
}
lg.Info("purged", zap.String("path", f))
nPurged++
}
if purgec != nil {
for i := 0; i < nPurged; i++ {
purgec <- fnamesWithSuffix[i]
}
}
select {
case <-time.After(interval):
case <-stop:
return
}
}
}()
return errC
}
func readDirWithSuffix(dirname string, suffix string) ([]string, error) {
fnames, err := ReadDir(dirname)
if err != nil {
return nil, err
}
// filter in place (ref. https://go.dev/wiki/SliceTricks#filtering-without-allocating)
fnamesWithSuffix := fnames[:0]
for _, fname := range fnames {
if strings.HasSuffix(fname, suffix) {
fnamesWithSuffix = append(fnamesWithSuffix, fname)
}
}
return fnamesWithSuffix, nil
}
================================================
FILE: client/pkg/fileutil/purge_test.go
================================================
// Copyright 2015 The etcd Authors
//
// 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 fileutil
import (
"fmt"
"os"
"path/filepath"
"reflect"
"testing"
"time"
"github.com/stretchr/testify/require"
"go.uber.org/zap/zaptest"
)
func TestPurgeFile(t *testing.T) {
dir := t.TempDir()
// minimal file set
for i := 0; i < 3; i++ {
f, ferr := os.Create(filepath.Join(dir, fmt.Sprintf("%d.test", i)))
require.NoError(t, ferr)
f.Close()
}
stop, purgec := make(chan struct{}), make(chan string, 10)
// keep 3 most recent files
errch := purgeFile(zaptest.NewLogger(t), dir, "test", 3, time.Millisecond, stop, purgec, nil, false)
select {
case f := <-purgec:
t.Errorf("unexpected purge on %q", f)
case <-time.After(10 * time.Millisecond):
}
// rest of the files
for i := 4; i < 10; i++ {
go func(n int) {
f, ferr := os.Create(filepath.Join(dir, fmt.Sprintf("%d.test", n)))
if ferr != nil {
t.Error(ferr)
}
f.Close()
}(i)
}
// watch files purge away
for i := 4; i < 10; i++ {
select {
case <-purgec:
case <-time.After(time.Second):
t.Errorf("purge took too long")
}
}
fnames, rerr := ReadDir(dir)
require.NoError(t, rerr)
wnames := []string{"7.test", "8.test", "9.test"}
if !reflect.DeepEqual(fnames, wnames) {
t.Errorf("filenames = %v, want %v", fnames, wnames)
}
// no error should be reported from purge routine
select {
case f := <-purgec:
t.Errorf("unexpected purge on %q", f)
case err := <-errch:
t.Errorf("unexpected purge error %v", err)
case <-time.After(10 * time.Millisecond):
}
close(stop)
}
func TestPurgeFileHoldingLockFile(t *testing.T) {
dir := t.TempDir()
for i := 0; i < 10; i++ {
var f *os.File
f, err := os.Create(filepath.Join(dir, fmt.Sprintf("%d.test", i)))
require.NoError(t, err)
f.Close()
}
// create a purge barrier at 5
p := filepath.Join(dir, fmt.Sprintf("%d.test", 5))
l, err := LockFile(p, os.O_WRONLY, PrivateFileMode)
require.NoError(t, err)
stop, purgec := make(chan struct{}), make(chan string, 10)
errch := purgeFile(zaptest.NewLogger(t), dir, "test", 3, time.Millisecond, stop, purgec, nil, true)
for i := 0; i < 5; i++ {
select {
case <-purgec:
case <-time.After(time.Second):
t.Fatalf("purge took too long")
}
}
fnames, rerr := ReadDir(dir)
require.NoError(t, rerr)
wnames := []string{"5.test", "6.test", "7.test", "8.test", "9.test"}
if !reflect.DeepEqual(fnames, wnames) {
t.Errorf("filenames = %v, want %v", fnames, wnames)
}
select {
case s := <-purgec:
t.Errorf("unexpected purge %q", s)
case err = <-errch:
t.Errorf("unexpected purge error %v", err)
case <-time.After(10 * time.Millisecond):
}
// remove the purge barrier
require.NoError(t, l.Close())
// wait for rest of purges (5, 6)
for i := 0; i < 2; i++ {
select {
case <-purgec:
case <-time.After(time.Second):
t.Fatalf("purge took too long")
}
}
fnames, rerr = ReadDir(dir)
require.NoError(t, rerr)
wnames = []string{"7.test", "8.test", "9.test"}
if !reflect.DeepEqual(fnames, wnames) {
t.Errorf("filenames = %v, want %v", fnames, wnames)
}
select {
case f := <-purgec:
t.Errorf("unexpected purge on %q", f)
case err := <-errch:
t.Errorf("unexpected purge error %v", err)
case <-time.After(10 * time.Millisecond):
}
close(stop)
}
================================================
FILE: client/pkg/fileutil/read_dir.go
================================================
// Copyright 2018 The etcd Authors
//
// 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 fileutil
import (
"os"
"path/filepath"
"sort"
)
// ReadDirOp represents an read-directory operation.
type ReadDirOp struct {
ext string
}
// ReadDirOption configures archiver operations.
type ReadDirOption func(*ReadDirOp)
// WithExt filters file names by their extensions.
// (e.g. WithExt(".wal") to list only WAL files)
func WithExt(ext string) ReadDirOption {
return func(op *ReadDirOp) { op.ext = ext }
}
func (op *ReadDirOp) applyOpts(opts []ReadDirOption) {
for _, opt := range opts {
opt(op)
}
}
// ReadDir returns the filenames in the given directory in sorted order.
func ReadDir(d string, opts ...ReadDirOption) ([]string, error) {
op := &ReadDirOp{}
op.applyOpts(opts)
dir, err := os.Open(d)
if err != nil {
return nil, err
}
defer dir.Close()
names, err := dir.Readdirnames(-1)
if err != nil {
return nil, err
}
sort.Strings(names)
if op.ext != "" {
tss := make([]string, 0)
for _, v := range names {
if filepath.Ext(v) == op.ext {
tss = append(tss, v)
}
}
names = tss
}
return names, nil
}
================================================
FILE: client/pkg/fileutil/read_dir_test.go
================================================
// Copyright 2018 The etcd Authors
//
// 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 fileutil
import (
"os"
"path/filepath"
"reflect"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestReadDir(t *testing.T) {
tmpdir := t.TempDir()
files := []string{"def", "abc", "xyz", "ghi"}
for _, f := range files {
writeFunc(t, filepath.Join(tmpdir, f))
}
fs, err := ReadDir(tmpdir)
require.NoErrorf(t, err, "error calling ReadDir")
wfs := []string{"abc", "def", "ghi", "xyz"}
require.Truef(t, reflect.DeepEqual(fs, wfs), "ReadDir: got %v, want %v", fs, wfs)
files = []string{"def.wal", "abc.wal", "xyz.wal", "ghi.wal"}
for _, f := range files {
writeFunc(t, filepath.Join(tmpdir, f))
}
fs, err = ReadDir(tmpdir, WithExt(".wal"))
require.NoErrorf(t, err, "error calling ReadDir")
wfs = []string{"abc.wal", "def.wal", "ghi.wal", "xyz.wal"}
require.Truef(t, reflect.DeepEqual(fs, wfs), "ReadDir: got %v, want %v", fs, wfs)
}
func writeFunc(t *testing.T, path string) {
t.Helper()
fh, err := os.Create(path)
require.NoErrorf(t, err, "error creating file")
assert.NoErrorf(t, fh.Close(), "error closing file")
}
================================================
FILE: client/pkg/fileutil/sync.go
================================================
// Copyright 2016 The etcd Authors
//
// 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.
//go:build !linux && !darwin
package fileutil
import "os"
// Fsync is a wrapper around file.Sync(). Special handling is needed on darwin platform.
func Fsync(f *os.File) error {
return f.Sync()
}
// Fdatasync is a wrapper around file.Sync(). Special handling is needed on linux platform.
func Fdatasync(f *os.File) error {
return f.Sync()
}
================================================
FILE: client/pkg/fileutil/sync_darwin.go
================================================
// Copyright 2016 The etcd Authors
//
// 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.
//go:build darwin
package fileutil
import (
"os"
"golang.org/x/sys/unix"
)
// Fsync on HFS/OSX flushes the data on to the physical drive but the drive
// may not write it to the persistent media for quite sometime and it may be
// written in out-of-order sequence. Using F_FULLFSYNC ensures that the
// physical drive's buffer will also get flushed to the media.
func Fsync(f *os.File) error {
_, err := unix.FcntlInt(f.Fd(), unix.F_FULLFSYNC, 0)
return err
}
// Fdatasync on darwin platform invokes fcntl(F_FULLFSYNC) for actual persistence
// on physical drive media.
func Fdatasync(f *os.File) error {
return Fsync(f)
}
================================================
FILE: client/pkg/fileutil/sync_linux.go
================================================
// Copyright 2016 The etcd Authors
//
// 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.
//go:build linux
package fileutil
import (
"os"
"syscall"
)
// Fsync is a wrapper around file.Sync(). Special handling is needed on darwin platform.
func Fsync(f *os.File) error {
return f.Sync()
}
// Fdatasync is similar to fsync(), but does not flush modified metadata
// unless that metadata is needed in order to allow a subsequent data retrieval
// to be correctly handled.
func Fdatasync(f *os.File) error {
return syscall.Fdatasync(int(f.Fd()))
}
================================================
FILE: client/pkg/go.mod
================================================
module go.etcd.io/etcd/client/pkg/v3
go 1.26
toolchain go1.26.1
require (
github.com/coreos/go-systemd/v22 v22.7.0
github.com/stretchr/testify v1.11.1
go.uber.org/zap v1.27.1
golang.org/x/sys v0.41.0
)
require (
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/kr/pretty v0.3.1 // indirect
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
github.com/rogpeppe/go-internal v1.14.1 // indirect
go.uber.org/multierr v1.11.0 // indirect
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
================================================
FILE: client/pkg/go.sum
================================================
github.com/coreos/go-systemd/v22 v22.7.0 h1:LAEzFkke61DFROc7zNLX/WA2i5J8gYqe0rSj9KI28KA=
github.com/coreos/go-systemd/v22 v22.7.0/go.mod h1:xNUYtjHu2EDXbsxz1i41wouACIwT7Ybq9o0BQhMwD0w=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
go.uber.org/zap v1.27.1 h1:08RqriUEv8+ArZRYSTXy1LeBScaMpVSTBhCeaZYfMYc=
go.uber.org/zap v1.27.1/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
golang.org/x/sys v0.41.0 h1:Ivj+2Cp/ylzLiEU89QhWblYnOE9zerudt9Ftecq2C6k=
golang.org/x/sys v0.41.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
================================================
FILE: client/pkg/logutil/doc.go
================================================
// Copyright 2018 The etcd Authors
//
// 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 logutil includes utilities to facilitate logging.
package logutil
================================================
FILE: client/pkg/logutil/log_format.go
================================================
// Copyright 2019 The etcd Authors
//
// 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 logutil
import "fmt"
const (
JSONLogFormat = "json"
ConsoleLogFormat = "console"
//revive:disable:var-naming
// Deprecated: Please use JSONLogFormat.
JsonLogFormat = JSONLogFormat
//revive:enable:var-naming
)
var DefaultLogFormat = JSONLogFormat
// ConvertToZapFormat converts and validated log format string.
func ConvertToZapFormat(format string) (string, error) {
switch format {
case ConsoleLogFormat:
return ConsoleLogFormat, nil
case JSONLogFormat:
return JSONLogFormat, nil
case "":
return DefaultLogFormat, nil
default:
return "", fmt.Errorf("unknown log format: %s, supported values json, console", format)
}
}
================================================
FILE: client/pkg/logutil/log_format_test.go
================================================
// Copyright 2019 The etcd Authors
//
// 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 logutil
import (
"testing"
)
func TestLogFormat(t *testing.T) {
tests := []struct {
given string
want string
errExpected bool
}{
{"json", JSONLogFormat, false},
{"console", ConsoleLogFormat, false},
{"", JSONLogFormat, false},
{"konsole", "", true},
}
for i, tt := range tests {
got, err := ConvertToZapFormat(tt.given)
if got != tt.want {
t.Errorf("#%d: ConvertToZapFormat failure: want=%v, got=%v", i, tt.want, got)
}
if err != nil {
if !tt.errExpected {
t.Errorf("#%d: ConvertToZapFormat unexpected error: %v", i, err)
}
}
}
}
================================================
FILE: client/pkg/logutil/log_level.go
================================================
// Copyright 2019 The etcd Authors
//
// 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 logutil
import (
"go.uber.org/zap/zapcore"
)
var DefaultLogLevel = "info"
// ConvertToZapLevel converts log level string to zapcore.Level.
func ConvertToZapLevel(lvl string) zapcore.Level {
var level zapcore.Level
if err := level.Set(lvl); err != nil {
panic(err)
}
return level
}
================================================
FILE: client/pkg/logutil/zap.go
================================================
// Copyright 2019 The etcd Authors
//
// 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 logutil
import (
"slices"
"time"
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
)
// CreateDefaultZapLogger creates a logger with default zap configuration
func CreateDefaultZapLogger(level zapcore.Level) (*zap.Logger, error) {
lcfg := DefaultZapLoggerConfig
lcfg.Level = zap.NewAtomicLevelAt(level)
c, err := lcfg.Build()
if err != nil {
return nil, err
}
return c, nil
}
// DefaultZapLoggerConfig defines default zap logger configuration.
var DefaultZapLoggerConfig = zap.Config{
Level: zap.NewAtomicLevelAt(ConvertToZapLevel(DefaultLogLevel)),
Development: false,
Sampling: &zap.SamplingConfig{
Initial: 100,
Thereafter: 100,
},
Encoding: DefaultLogFormat,
// copied from "zap.NewProductionEncoderConfig" with some updates
EncoderConfig: zapcore.EncoderConfig{
TimeKey: "ts",
LevelKey: "level",
NameKey: "logger",
CallerKey: "caller",
MessageKey: "msg",
StacktraceKey: "stacktrace",
LineEnding: zapcore.DefaultLineEnding,
EncodeLevel: zapcore.LowercaseLevelEncoder,
// Custom EncodeTime function to ensure we match format and precision of historic capnslog timestamps
EncodeTime: func(t time.Time, enc zapcore.PrimitiveArrayEncoder) {
enc.AppendString(t.Format("2006-01-02T15:04:05.000000Z0700"))
},
EncodeDuration: zapcore.StringDurationEncoder,
EncodeCaller: zapcore.ShortCallerEncoder,
},
// Use "/dev/null" to discard all
OutputPaths: []string{"stderr"},
ErrorOutputPaths: []string{"stderr"},
}
// MergeOutputPaths merges logging output paths, resolving conflicts.
func MergeOutputPaths(cfg zap.Config) zap.Config {
cfg.OutputPaths = mergePaths(cfg.OutputPaths)
cfg.ErrorOutputPaths = mergePaths(cfg.ErrorOutputPaths)
return cfg
}
func mergePaths(old []string) []string {
if len(old) == 0 {
// the original implementation ensures the result is non-nil
return []string{}
}
// use "/dev/null" to discard all
if slices.Contains(old, "/dev/null") {
return []string{"/dev/null"}
}
// clone a new one; don't modify the original, in case it matters.
dup := slices.Clone(old)
slices.Sort(dup)
return slices.Compact(dup)
}
================================================
FILE: client/pkg/logutil/zap_journal.go
================================================
// Copyright 2018 The etcd Authors
//
// 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.
//go:build !windows
package logutil
import (
"bytes"
"encoding/json"
"fmt"
"io"
"os"
"path/filepath"
"github.com/coreos/go-systemd/v22/journal"
"go.uber.org/zap/zapcore"
"go.etcd.io/etcd/client/pkg/v3/systemd"
)
// NewJournalWriter wraps "io.Writer" to redirect log output
// to the local systemd journal. If journald send fails, it fails
// back to writing to the original writer.
// The decode overhead is only <30µs per write.
// Reference: https://github.com/coreos/pkg/blob/master/capnslog/journald_formatter.go
func NewJournalWriter(wr io.Writer) (io.Writer, error) {
return &journalWriter{Writer: wr}, systemd.DialJournal()
}
type journalWriter struct {
io.Writer
}
// WARN: assume that etcd uses default field names in zap encoder config
// make sure to keep this up-to-date!
type logLine struct {
Level string `json:"level"`
Caller string `json:"caller"`
}
func (w *journalWriter) Write(p []byte) (int, error) {
line := &logLine{}
if err := json.NewDecoder(bytes.NewReader(p)).Decode(line); err != nil {
return 0, err
}
var pri journal.Priority
switch line.Level {
case zapcore.DebugLevel.String():
pri = journal.PriDebug
case zapcore.InfoLevel.String():
pri = journal.PriInfo
case zapcore.WarnLevel.String():
pri = journal.PriWarning
case zapcore.ErrorLevel.String():
pri = journal.PriErr
case zapcore.DPanicLevel.String():
pri = journal.PriCrit
case zapcore.PanicLevel.String():
pri = journal.PriCrit
case zapcore.FatalLevel.String():
pri = journal.PriCrit
default:
panic(fmt.Errorf("unknown log level: %q", line.Level))
}
err := journal.Send(string(p), pri, map[string]string{
"PACKAGE": filepath.Dir(line.Caller),
"SYSLOG_IDENTIFIER": filepath.Base(os.Args[0]),
})
if err != nil {
// "journal" also falls back to stderr
// "fmt.Fprintln(os.Stderr, s)"
return w.Writer.Write(p)
}
return 0, nil
}
================================================
FILE: client/pkg/logutil/zap_journal_test.go
================================================
// Copyright 2018 The etcd Authors
//
// 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.
//go:build !windows
package logutil
import (
"bytes"
"testing"
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
)
func TestNewJournalWriter(t *testing.T) {
buf := bytes.NewBuffer(nil)
jw, err := NewJournalWriter(buf)
if err != nil {
t.Skip(err)
}
syncer := zapcore.AddSync(jw)
cr := zapcore.NewCore(
zapcore.NewJSONEncoder(DefaultZapLoggerConfig.EncoderConfig),
syncer,
zap.NewAtomicLevelAt(zap.InfoLevel),
)
lg := zap.New(cr, zap.AddCaller(), zap.ErrorOutput(syncer))
defer lg.Sync()
lg.Info("TestNewJournalWriter")
if buf.String() == "" {
// check with "journalctl -f"
t.Log("sent logs successfully to journald")
}
}
================================================
FILE: client/pkg/logutil/zap_test.go
================================================
// Copyright 2024 The etcd Authors
//
// 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 logutil
import (
"bytes"
"encoding/json"
"regexp"
"slices"
"testing"
"github.com/stretchr/testify/require"
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
)
type commonLogFields struct {
Level string `json:"level"`
Timestamp string `json:"ts"`
Message string `json:"msg"`
}
const (
fractionSecondsPrecision = 6 // MicroSeconds
)
func TestEncodeTimePrecisionToMicroSeconds(t *testing.T) {
buf := bytes.NewBuffer(nil)
syncer := zapcore.AddSync(buf)
zc := zapcore.NewCore(
zapcore.NewJSONEncoder(DefaultZapLoggerConfig.EncoderConfig),
syncer,
zap.NewAtomicLevelAt(zap.InfoLevel),
)
lg := zap.New(zc)
lg.Info("TestZapLog")
fields := commonLogFields{}
require.NoError(t, json.Unmarshal(buf.Bytes(), &fields))
// example 1: 2024-06-06T23:37:21.948385Z
// example 2 with zone offset: 2024-06-06T16:16:44.176778-0700
regex := `\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.(\d+)(Z|[+-]\d{4})`
re := regexp.MustCompile(regex)
matches := re.FindStringSubmatch(fields.Timestamp)
require.Len(t, matches, 3)
require.Lenf(t, matches[1], fractionSecondsPrecision, "unexpected timestamp %s", fields.Timestamp)
}
func TestMergeOutputPaths(t *testing.T) {
tests := []struct {
name string
cfg zap.Config
want zap.Config
}{
{
name: "OutputPaths /dev/null",
cfg: zap.Config{
OutputPaths: []string{"c", "/dev/null"},
ErrorOutputPaths: []string{"c", "a", "a", "b"},
},
want: zap.Config{
OutputPaths: []string{"/dev/null"},
ErrorOutputPaths: []string{"a", "b", "c"},
},
},
{
name: "ErrorOutputPaths /dev/null",
cfg: zap.Config{
OutputPaths: []string{"c", "a", "a", "b"},
ErrorOutputPaths: []string{"/dev/null", "c"},
},
want: zap.Config{
OutputPaths: []string{"a", "b", "c"},
ErrorOutputPaths: []string{"/dev/null"},
},
},
{
name: "empty slice",
cfg: zap.Config{
OutputPaths: []string{},
ErrorOutputPaths: []string{"c", "a", "a", "b"},
},
want: zap.Config{
OutputPaths: []string{},
ErrorOutputPaths: []string{"a", "b", "c"},
},
},
{
name: "nil slice",
cfg: zap.Config{
OutputPaths: []string{"c", "a", "a", "b"},
ErrorOutputPaths: nil,
},
want: zap.Config{
OutputPaths: []string{"a", "b", "c"},
ErrorOutputPaths: []string{},
},
},
{
name: "normal",
cfg: zap.Config{
OutputPaths: []string{"c", "a", "a", "b"},
ErrorOutputPaths: []string{"c", "a", "a", "b"},
},
want: zap.Config{
OutputPaths: []string{"a", "b", "c"},
ErrorOutputPaths: []string{"a", "b", "c"},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
outputPaths := slices.Clone(tt.cfg.OutputPaths)
errorOutputPaths := slices.Clone(tt.cfg.ErrorOutputPaths)
require.Equal(t, tt.want, MergeOutputPaths(tt.cfg))
// ensure the OutputPaths and ErrorOutputPaths have not been modified
require.Equal(t, outputPaths, tt.cfg.OutputPaths)
require.Equal(t, errorOutputPaths, tt.cfg.ErrorOutputPaths)
})
}
}
================================================
FILE: client/pkg/pathutil/path.go
================================================
// Copyright 2025 The etcd Authors
//
// 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 pathutil implements utility functions for handling slash-separated
// paths.
package pathutil
import "path"
// CanonicalURLPath returns the canonical url path for p, which follows the rules:
// 1. the path always starts with "/"
// 2. replace multiple slashes with a single slash
// 3. replace each '.' '..' path name element with equivalent one
// 4. keep the trailing slash
// The function is borrowed from stdlib http.cleanPath in server.go.
func CanonicalURLPath(p string) string {
if p == "" {
return "/"
}
if p[0] != '/' {
p = "/" + p
}
np := path.Clean(p)
// path.Clean removes trailing slash except for root,
// put the trailing slash back if necessary.
if p[len(p)-1] == '/' && np != "/" {
np += "/"
}
return np
}
================================================
FILE: client/pkg/pathutil/path_test.go
================================================
// Copyright 2015 The etcd Authors
//
// 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 pathutil
import "testing"
func TestCanonicalURLPath(t *testing.T) {
tests := []struct {
p string
wp string
}{
{"/a", "/a"},
{"", "/"},
{"a", "/a"},
{"//a", "/a"},
{"/a/.", "/a"},
{"/a/..", "/"},
{"/a/", "/a/"},
{"/a//", "/a/"},
}
for i, tt := range tests {
if g := CanonicalURLPath(tt.p); g != tt.wp {
t.Errorf("#%d: canonical path = %s, want %s", i, g, tt.wp)
}
}
}
================================================
FILE: client/pkg/srv/srv.go
================================================
// Copyright 2015 The etcd Authors
//
// 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 srv looks up DNS SRV records.
package srv
import (
"fmt"
"net"
"net/url"
"strings"
"go.etcd.io/etcd/client/pkg/v3/types"
)
var (
// indirection for testing
lookupSRV = net.LookupSRV // net.DefaultResolver.LookupSRV when ctxs don't conflict
resolveTCPAddr = net.ResolveTCPAddr
)
// GetCluster gets the cluster information via DNS discovery.
// Also sees each entry as a separate instance.
func GetCluster(serviceScheme, service, name, dns string, apurls types.URLs) ([]string, error) {
tcp2ap := make(map[string]url.URL)
// First, resolve the apurls
for _, url := range apurls {
tcpAddr, err := resolveTCPAddr("tcp", url.Host)
if err != nil {
return nil, err
}
tcp2ap[tcpAddr.String()] = url
}
var (
tempName int
stringParts []string
)
updateNodeMap := func(service, scheme string) error {
_, addrs, err := lookupSRV(service, "tcp", dns)
if err != nil {
return err
}
for _, srv := range addrs {
port := fmt.Sprintf("%d", srv.Port)
host := net.JoinHostPort(srv.Target, port)
tcpAddr, terr := resolveTCPAddr("tcp", host)
if terr != nil {
err = terr
continue
}
n := ""
url, ok := tcp2ap[tcpAddr.String()]
if ok {
n = name
}
if n == "" {
n = fmt.Sprintf("%d", tempName)
tempName++
}
// SRV records have a trailing dot but URL shouldn't.
shortHost := strings.TrimSuffix(srv.Target, ".")
urlHost := net.JoinHostPort(shortHost, port)
if ok && url.Scheme != scheme {
err = fmt.Errorf("bootstrap at %s from DNS for %s has scheme mismatch with expected peer %s", scheme+"://"+urlHost, service, url.String())
} else {
stringParts = append(stringParts, fmt.Sprintf("%s=%s://%s", n, scheme, urlHost))
}
}
if len(stringParts) == 0 {
return err
}
return nil
}
err := updateNodeMap(service, serviceScheme)
if err != nil {
return nil, fmt.Errorf("error querying DNS SRV records for _%s %w", service, err)
}
return stringParts, nil
}
type SRVClients struct {
Endpoints []string
SRVs []*net.SRV
}
// GetClient looks up the client endpoints for a service and domain.
func GetClient(service, domain string, serviceName string) (*SRVClients, error) {
var (
urls []*url.URL
srvs []*net.SRV
)
updateURLs := func(service, scheme string) error {
_, addrs, err := lookupSRV(service, "tcp", domain)
if err != nil {
return err
}
for _, srv := range addrs {
urls = append(urls, &url.URL{
Scheme: scheme,
Host: net.JoinHostPort(srv.Target, fmt.Sprintf("%d", srv.Port)),
})
}
srvs = append(srvs, addrs...)
return nil
}
errHTTPS := updateURLs(GetSRVService(service, serviceName, "https"), "https")
errHTTP := updateURLs(GetSRVService(service, serviceName, "http"), "http")
if errHTTPS != nil && errHTTP != nil {
return nil, fmt.Errorf("dns lookup errors: %w and %w", errHTTPS, errHTTP)
}
endpoints := make([]string, len(urls))
for i := range urls {
endpoints[i] = urls[i].String()
}
return &SRVClients{Endpoints: endpoints, SRVs: srvs}, nil
}
// GetSRVService generates a SRV service including an optional suffix.
func GetSRVService(service, serviceName string, scheme string) (SRVService string) {
if scheme == "https" {
service = fmt.Sprintf("%s-ssl", service)
}
if serviceName != "" {
return fmt.Sprintf("%s-%s", service, serviceName)
}
return service
}
================================================
FILE: client/pkg/srv/srv_test.go
================================================
// Copyright 2015 The etcd Authors
//
// 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 srv
import (
"errors"
"fmt"
"net"
"reflect"
"strings"
"testing"
"github.com/stretchr/testify/require"
"go.etcd.io/etcd/client/pkg/v3/testutil"
)
func notFoundErr(service, proto, domain string) error {
name := fmt.Sprintf("_%s._%s.%s", service, proto, domain)
return &net.DNSError{Err: "no such host", Name: name, Server: "10.0.0.53:53", IsTimeout: false, IsTemporary: false, IsNotFound: true}
}
func TestSRVGetCluster(t *testing.T) {
defer func() {
lookupSRV = net.LookupSRV
resolveTCPAddr = net.ResolveTCPAddr
}()
hasErr := func(err error) bool {
return err != nil
}
name := "dnsClusterTest"
dns := map[string]string{
"1.example.com.:2480": "10.0.0.1:2480",
"2.example.com.:2480": "10.0.0.2:2480",
"3.example.com.:2480": "10.0.0.3:2480",
"4.example.com.:2380": "10.0.0.3:2380",
}
srvAll := []*net.SRV{
{Target: "1.example.com.", Port: 2480},
{Target: "2.example.com.", Port: 2480},
{Target: "3.example.com.", Port: 2480},
}
var srvNone []*net.SRV
tests := []struct {
service string
scheme string
withSSL []*net.SRV
withoutSSL []*net.SRV
urls []string
expected string
werr bool
}{
{
"etcd-server-ssl",
"https",
srvNone,
srvNone,
nil,
"",
true,
},
{
"etcd-server-ssl",
"https",
srvAll,
srvNone,
nil,
"0=https://1.example.com:2480,1=https://2.example.com:2480,2=https://3.example.com:2480",
false,
},
{
"etcd-server",
"http",
srvNone,
srvAll,
nil,
"0=http://1.example.com:2480,1=http://2.example.com:2480,2=http://3.example.com:2480",
false,
},
{
"etcd-server-ssl",
"https",
srvAll,
srvNone,
[]string{"https://10.0.0.1:2480"},
"dnsClusterTest=https://1.example.com:2480,0=https://2.example.com:2480,1=https://3.example.com:2480",
false,
},
// matching local member with resolved addr and return unresolved hostnames
{
"etcd-server-ssl",
"https",
srvAll,
srvNone,
[]string{"https://10.0.0.1:2480"},
"dnsClusterTest=https://1.example.com:2480,0=https://2.example.com:2480,1=https://3.example.com:2480",
false,
},
// reject if apurls are TLS but SRV is only http
{
"etcd-server",
"http",
srvNone,
srvAll,
[]string{"https://10.0.0.1:2480"},
"0=http://2.example.com:2480,1=http://3.example.com:2480",
false,
},
}
resolveTCPAddr = func(network, addr string) (*net.TCPAddr, error) {
if strings.Contains(addr, "10.0.0.") {
// accept IP addresses when resolving apurls
return net.ResolveTCPAddr(network, addr)
}
if dns[addr] == "" {
return nil, errors.New("missing dns record")
}
return net.ResolveTCPAddr(network, dns[addr])
}
for i, tt := range tests {
lookupSRV = func(service string, proto string, domain string) (string, []*net.SRV, error) {
if service == "etcd-server-ssl" {
if len(tt.withSSL) > 0 {
return "", tt.withSSL, nil
}
return "", nil, notFoundErr(service, proto, domain)
}
if service == "etcd-server" {
if len(tt.withoutSSL) > 0 {
return "", tt.withoutSSL, nil
}
return "", nil, notFoundErr(service, proto, domain)
}
return "", nil, errors.New("unknown service in mock")
}
urls := testutil.MustNewURLs(t, tt.urls)
str, err := GetCluster(tt.scheme, tt.service, name, "example.com", urls)
require.Equalf(t, hasErr(err), tt.werr, "%d: err = %#v, want = %#v", i, err, tt.werr)
require.Equalf(t, tt.expected, strings.Join(str, ","), "#%d: cluster = %s, want %s", i, str, tt.expected)
}
}
func TestSRVDiscover(t *testing.T) {
defer func() { lookupSRV = net.LookupSRV }()
hasErr := func(err error) bool {
return err != nil
}
tests := []struct {
withSSL []*net.SRV
withoutSSL []*net.SRV
expected []string
werr bool
}{
{
[]*net.SRV{},
[]*net.SRV{},
[]string{},
true,
},
{
[]*net.SRV{},
[]*net.SRV{
{Target: "10.0.0.1", Port: 2480},
{Target: "10.0.0.2", Port: 2480},
{Target: "10.0.0.3", Port: 2480},
},
[]string{"http://10.0.0.1:2480", "http://10.0.0.2:2480", "http://10.0.0.3:2480"},
false,
},
{
[]*net.SRV{
{Target: "10.0.0.1", Port: 2480},
{Target: "10.0.0.2", Port: 2480},
{Target: "10.0.0.3", Port: 2480},
},
[]*net.SRV{},
[]string{"https://10.0.0.1:2480", "https://10.0.0.2:2480", "https://10.0.0.3:2480"},
false,
},
{
[]*net.SRV{
{Target: "10.0.0.1", Port: 2480},
{Target: "10.0.0.2", Port: 2480},
{Target: "10.0.0.3", Port: 2480},
},
[]*net.SRV{
{Target: "10.0.0.1", Port: 7001},
},
[]string{"https://10.0.0.1:2480", "https://10.0.0.2:2480", "https://10.0.0.3:2480", "http://10.0.0.1:7001"},
false,
},
{
[]*net.SRV{
{Target: "10.0.0.1", Port: 2480},
{Target: "10.0.0.2", Port: 2480},
{Target: "10.0.0.3", Port: 2480},
},
[]*net.SRV{
{Target: "10.0.0.1", Port: 7001},
},
[]string{"https://10.0.0.1:2480", "https://10.0.0.2:2480", "https://10.0.0.3:2480", "http://10.0.0.1:7001"},
false,
},
{
[]*net.SRV{
{Target: "a.example.com", Port: 2480},
{Target: "b.example.com", Port: 2480},
{Target: "c.example.com.", Port: 2480},
},
[]*net.SRV{},
[]string{"https://a.example.com:2480", "https://b.example.com:2480", "https://c.example.com.:2480"},
false,
},
}
for i, tt := range tests {
lookupSRV = func(service string, proto string, domain string) (string, []*net.SRV, error) {
if service == "etcd-client-ssl" {
if len(tt.withSSL) > 0 {
return "", tt.withSSL, nil
}
return "", nil, notFoundErr(service, proto, domain)
}
if service == "etcd-client" {
if len(tt.withoutSSL) > 0 {
return "", tt.withoutSSL, nil
}
return "", nil, notFoundErr(service, proto, domain)
}
return "", nil, errors.New("unknown service in mock")
}
srvs, err := GetClient("etcd-client", "example.com", "")
require.Equalf(t, hasErr(err), tt.werr, "%d: err = %#v, want = %#v", i, err, tt.werr)
if srvs == nil {
if len(tt.expected) > 0 {
t.Errorf("#%d: srvs = nil, want non-nil", i)
}
} else {
if !reflect.DeepEqual(srvs.Endpoints, tt.expected) {
t.Errorf("#%d: endpoints = %v, want = %v", i, srvs.Endpoints, tt.expected)
}
}
}
}
func TestGetSRVService(t *testing.T) {
tests := []struct {
scheme string
serviceName string
expected string
}{
{
"https",
"",
"etcd-client-ssl",
},
{
"http",
"",
"etcd-client",
},
{
"https",
"foo",
"etcd-client-ssl-foo",
},
{
"http",
"bar",
"etcd-client-bar",
},
}
for i, tt := range tests {
service := GetSRVService("etcd-client", tt.serviceName, tt.scheme)
if strings.Compare(service, tt.expected) != 0 {
t.Errorf("#%d: service = %s, want %s", i, service, tt.expected)
}
}
}
================================================
FILE: client/pkg/systemd/doc.go
================================================
// Copyright 2018 The etcd Authors
//
// 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 systemd provides utility functions for systemd.
package systemd
================================================
FILE: client/pkg/systemd/journal.go
================================================
// Copyright 2018 The etcd Authors
//
// 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 systemd
import "net"
// DialJournal returns no error if the process can dial journal socket.
// Returns an error if dial failed, which indicates journald is not available
// (e.g. run embedded etcd as docker daemon).
// Reference: https://github.com/coreos/go-systemd/blob/master/journal/journal.go.
func DialJournal() error {
conn, err := net.Dial("unixgram", "/run/systemd/journal/socket")
if conn != nil {
defer conn.Close()
}
return err
}
================================================
FILE: client/pkg/testutil/assert.go
================================================
// Copyright 2017 The etcd Authors
//
// 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 testutil
import (
"testing"
"github.com/stretchr/testify/assert"
)
// AssertNil
// Deprecated: use github.com/stretchr/testify/assert.Nil instead.
func AssertNil(t *testing.T, v any) {
t.Helper()
assert.Nil(t, v)
}
// AssertNotNil
// Deprecated: use github.com/stretchr/testify/require.NotNil instead.
func AssertNotNil(t *testing.T, v any) {
t.Helper()
if v == nil {
t.Fatalf("expected non-nil, got %+v", v)
}
}
// AssertTrue
// Deprecated: use github.com/stretchr/testify/assert.True instead.
func AssertTrue(t *testing.T, v bool, msg ...string) {
t.Helper()
assert.True(t, v, msg) //nolint:testifylint
}
// AssertFalse
// Deprecated: use github.com/stretchr/testify/assert.False instead.
func AssertFalse(t *testing.T, v bool, msg ...string) {
t.Helper()
assert.False(t, v, msg) //nolint:testifylint
}
================================================
FILE: client/pkg/testutil/before.go
================================================
// Copyright 2022 The etcd Authors
//
// 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 testutil
import (
"log"
"os"
"testing"
"go.etcd.io/etcd/client/pkg/v3/verify"
)
func BeforeTest(tb testing.TB) {
tb.Helper()
RegisterLeakDetection(tb)
revertVerifyFunc := verify.EnableAllVerifications()
tempDir := tb.TempDir()
tb.Chdir(tempDir)
tb.Logf("Changing working directory to: %s", tempDir)
tb.Cleanup(func() {
revertVerifyFunc()
})
}
func BeforeIntegrationExamples(*testing.M) func() {
ExitInShortMode("Skipping: the tests require real cluster")
tempDir, err := os.MkdirTemp(os.TempDir(), "etcd-integration")
if err != nil {
log.Printf("Failed to obtain tempDir: %v", tempDir)
os.Exit(1)
}
err = os.Chdir(tempDir)
if err != nil {
log.Printf("Failed to change working dir to: %s: %v", tempDir, err)
os.Exit(1)
}
log.Printf("Running tests (examples) in dir(%v): ...", tempDir)
return func() { os.RemoveAll(tempDir) }
}
================================================
FILE: client/pkg/testutil/leak.go
================================================
// Copyright 2025 The etcd Authors
//
// 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 testutil
import (
"fmt"
"net/http"
"os"
"regexp"
"runtime"
"sort"
"strings"
"testing"
"time"
)
// TODO: Replace with https://github.com/uber-go/goleak.
/*
CheckLeakedGoroutine verifies tests do not leave any leaky
goroutines. It returns true when there are goroutines still
running(leaking) after all tests.
import "go.etcd.io/etcd/client/pkg/v3/testutil"
func TestMain(m *testing.M) {
testutil.MustTestMainWithLeakDetection(m)
}
func TestSample(t *testing.T) {
RegisterLeakDetection(t)
...
}
*/
var normalizedRegexp = regexp.MustCompile(`\(0[0-9a-fx, ]*\)`)
func CheckLeakedGoroutine() bool {
gs := interestingGoroutines()
if len(gs) == 0 {
return false
}
stackCount := make(map[string]int)
for _, g := range gs {
// strip out pointer arguments in first function of stack dump
normalized := string(normalizedRegexp.ReplaceAll([]byte(g), []byte("(...)")))
stackCount[normalized]++
}
fmt.Fprint(os.Stderr, "Unexpected goroutines running after all test(s).\n")
for stack, count := range stackCount {
fmt.Fprintf(os.Stderr, "%d instances of:\n%s\n", count, stack)
}
return true
}
// CheckAfterTest returns an error if AfterTest would fail with an error.
// Waits for go-routines shutdown for 'd'.
func CheckAfterTest(d time.Duration) error {
http.DefaultTransport.(*http.Transport).CloseIdleConnections()
var bad string
// Presence of these goroutines causes immediate test failure.
badSubstring := map[string]string{
").writeLoop(": "a Transport",
"created by net/http/httptest.(*Server).Start": "an httptest.Server",
"timeoutHandler": "a TimeoutHandler",
"net.(*netFD).connect(": "a timing out dial",
").noteClientGone(": "a closenotifier sender",
").readLoop(": "a Transport",
".grpc": "a gRPC resource",
").sendCloseSubstream(": "a stream closing routine",
}
var stacks string
begin := time.Now()
for time.Since(begin) < d {
bad = ""
goroutines := interestingGoroutines()
if len(goroutines) == 0 {
return nil
}
stacks = strings.Join(goroutines, "\n\n")
for substr, what := range badSubstring {
if strings.Contains(stacks, substr) {
bad = what
}
}
// Undesired goroutines found, but goroutines might just still be
// shutting down, so give it some time.
runtime.Gosched()
time.Sleep(50 * time.Millisecond)
}
return fmt.Errorf("appears to have leaked %s:\n%s", bad, stacks)
}
// RegisterLeakDetection is a convenient way to register before-and-after code to a test.
// If you execute RegisterLeakDetection, you don't need to explicitly register AfterTest.
func RegisterLeakDetection(t TB) {
if err := CheckAfterTest(10 * time.Millisecond); err != nil {
t.Skip("Found leaked goroutined BEFORE test", err)
return
}
t.Cleanup(func() {
afterTest(t)
})
}
// afterTest is meant to run in a defer that executes after a test completes.
// It will detect common goroutine leaks, retrying in case there are goroutines
// not synchronously torn down, and fail the test if any goroutines are stuck.
func afterTest(t TB) {
// If the test fails, the leaked goroutines list may hide the real
// source of problem.
if !t.Failed() {
if err := CheckAfterTest(1 * time.Second); err != nil {
t.Errorf("Test %v", err)
}
}
}
func interestingGoroutines() (gs []string) {
buf := make([]byte, 2<<20)
buf = buf[:runtime.Stack(buf, true)]
for _, g := range strings.Split(string(buf), "\n\n") {
sl := strings.SplitN(g, "\n", 2)
if len(sl) != 2 {
continue
}
stack := strings.TrimSpace(sl[1])
if stack == "" {
continue
}
shouldSkip := func() bool {
uninterestingMsgs := [...]string{
"sync.(*WaitGroup).Done",
"os.(*file).close",
"os.(*Process).Release",
"created by os/signal.init",
"runtime/panic.go",
"created by testing.RunTests",
"created by testing.runTests",
"created by testing.(*T).Run",
"testing.Main(",
"runtime.goexit",
"go.etcd.io/etcd/client/pkg/v3/testutil.interestingGoroutines",
"go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop",
"github.com/golang/glog.(*loggingT).flushDaemon",
"created by runtime.gc",
"created by text/template/parse.lex",
"runtime.MHeap_Scavenger",
"rcrypto/internal/boring.(*PublicKeyRSA).finalize",
"net.(*netFD).Close(",
"testing.(*T).Run",
"crypto/tls.(*certCache).evict",
}
for _, msg := range uninterestingMsgs {
if strings.Contains(stack, msg) {
return true
}
}
return false
}()
if shouldSkip {
continue
}
gs = append(gs, stack)
}
sort.Strings(gs)
return gs
}
func MustCheckLeakedGoroutine() {
http.DefaultTransport.(*http.Transport).CloseIdleConnections()
CheckAfterTest(5 * time.Second)
// Let the other goroutines finalize.
runtime.Gosched()
if CheckLeakedGoroutine() {
os.Exit(1)
}
}
// MustTestMainWithLeakDetection expands standard m.Run with leaked
// goroutines detection.
func MustTestMainWithLeakDetection(m *testing.M) {
v := m.Run()
if v == 0 {
MustCheckLeakedGoroutine()
}
os.Exit(v)
}
================================================
FILE: client/pkg/testutil/leak_test.go
================================================
// Copyright 2016 The etcd Authors
//
// 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 testutil
import (
"fmt"
"os"
"testing"
)
// so tests pass if given a -run that doesn't include TestSample
var ranSample = false
func TestMain(m *testing.M) {
m.Run()
isLeaked := CheckLeakedGoroutine()
if ranSample && !isLeaked {
fmt.Fprintln(os.Stderr, "expected leaky goroutines but none is detected")
os.Exit(1)
}
os.Exit(0)
}
func TestSample(t *testing.T) {
SkipTestIfShortMode(t, "Counting leaked routines is disabled in --short tests")
defer afterTest(t)
ranSample = true
for range make([]struct{}, 100) {
go func() {
select {}
}()
}
}
================================================
FILE: client/pkg/testutil/pauseable_handler.go
================================================
// Copyright 2015 The etcd Authors
//
// 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 testutil
import (
"net/http"
"sync"
)
type PauseableHandler struct {
Next http.Handler
mu sync.Mutex
paused bool
}
func (ph *PauseableHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
ph.mu.Lock()
paused := ph.paused
ph.mu.Unlock()
if !paused {
ph.Next.ServeHTTP(w, r)
} else {
hj, ok := w.(http.Hijacker)
if !ok {
panic("webserver doesn't support hijacking")
}
conn, _, err := hj.Hijack()
if err != nil {
panic(err.Error())
}
conn.Close()
}
}
func (ph *PauseableHandler) Pause() {
ph.mu.Lock()
defer ph.mu.Unlock()
ph.paused = true
}
func (ph *PauseableHandler) Resume() {
ph.mu.Lock()
defer ph.mu.Unlock()
ph.paused = false
}
================================================
FILE: client/pkg/testutil/recorder.go
================================================
// Copyright 2015 The etcd Authors
//
// 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 testutil
import (
"errors"
"fmt"
"sync"
"time"
)
type Action struct {
Name string
Params []any
}
type Recorder interface {
// Record publishes an Action (e.g., function call) which will
// be reflected by Wait() or Chan()
Record(a Action)
// Wait waits until at least n Actions are available or returns with error
Wait(n int) ([]Action, error)
// Action returns immediately available Actions
Action() []Action
// Chan returns the channel for actions published by Record
Chan() <-chan Action
}
// RecorderBuffered appends all Actions to a slice
type RecorderBuffered struct {
sync.Mutex
actions []Action
}
func (r *RecorderBuffered) Record(a Action) {
r.Lock()
r.actions = append(r.actions, a)
r.Unlock()
}
func (r *RecorderBuffered) Action() []Action {
r.Lock()
cpy := make([]Action, len(r.actions))
copy(cpy, r.actions)
r.Unlock()
return cpy
}
func (r *RecorderBuffered) Wait(n int) (acts []Action, err error) {
// legacy racey behavior
WaitSchedule()
acts = r.Action()
if len(acts) < n {
err = newLenErr(n, len(acts))
}
return acts, err
}
func (r *RecorderBuffered) Chan() <-chan Action {
ch := make(chan Action)
go func() {
acts := r.Action()
for i := range acts {
ch <- acts[i]
}
close(ch)
}()
return ch
}
// RecorderStream writes all Actions to an unbuffered channel
type recorderStream struct {
ch chan Action
waitTimeout time.Duration
}
func NewRecorderStream() Recorder {
return NewRecorderStreamWithWaitTimout(5 * time.Second)
}
func NewRecorderStreamWithWaitTimout(waitTimeout time.Duration) Recorder {
return &recorderStream{ch: make(chan Action), waitTimeout: waitTimeout}
}
func (r *recorderStream) Record(a Action) {
r.ch <- a
}
func (r *recorderStream) Action() (acts []Action) {
for {
select {
case act := <-r.ch:
acts = append(acts, act)
default:
return acts
}
}
}
func (r *recorderStream) Chan() <-chan Action {
return r.ch
}
func (r *recorderStream) Wait(n int) ([]Action, error) {
acts := make([]Action, n)
var timeoutC <-chan time.Time
if r.waitTimeout != 0 {
timeoutC = time.After(r.waitTimeout)
}
for i := 0; i < n; i++ {
select {
case acts[i] = <-r.ch:
case <-timeoutC:
acts = acts[:i]
return acts, newLenErr(n, i)
}
}
// extra wait to catch any Action spew
select {
case act := <-r.ch:
acts = append(acts, act)
case <-time.After(10 * time.Millisecond):
}
return acts, nil
}
func newLenErr(expected int, actual int) error {
s := fmt.Sprintf("len(actions) = %d, expected >= %d", actual, expected)
return errors.New(s)
}
================================================
FILE: client/pkg/testutil/testingtb.go
================================================
// Copyright 2021 The etcd Authors
//
// 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 testutil
import (
"log"
"os"
)
// TB is a subset of methods of testing.TB interface.
// We cannot implement testing.TB due to protection, so we expose this simplified interface.
type TB interface {
Cleanup(func())
Error(args ...any)
Errorf(format string, args ...any)
Fail()
FailNow()
Failed() bool
Fatal(args ...any)
Fatalf(format string, args ...any)
Logf(format string, args ...any)
Name() string
TempDir() string
Helper()
Skip(args ...any)
}
// NewTestingTBProthesis creates a fake variant of testing.TB implementation.
// It's supposed to be used in contexts were real testing.T is not provided,
// e.g. in 'examples'.
//
// The `closef` goroutine should get executed when tb will not be needed any longer.
//
// The provided implementation is NOT thread safe (Cleanup() method).
func NewTestingTBProthesis(name string) (tb TB, closef func()) {
testtb := &testingTBProthesis{name: name}
return testtb, testtb.close
}
type testingTBProthesis struct {
name string
failed bool
cleanups []func()
}
func (t *testingTBProthesis) Helper() {
// Ignored
}
func (t *testingTBProthesis) Skip(args ...any) {
t.Log(append([]any{"Skipping due to: "}, args...))
}
func (t *testingTBProthesis) Cleanup(f func()) {
t.cleanups = append(t.cleanups, f)
}
func (t *testingTBProthesis) Error(args ...any) {
log.Println(args...)
t.Fail()
}
func (t *testingTBProthesis) Errorf(format string, args ...any) {
log.Printf(format, args...)
t.Fail()
}
func (t *testingTBProthesis) Fail() {
t.failed = true
}
func (t *testingTBProthesis) FailNow() {
t.failed = true
panic("FailNow() called")
}
func (t *testingTBProthesis) Failed() bool {
return t.failed
}
func (t *testingTBProthesis) Fatal(args ...any) {
log.Fatalln(args...)
}
func (t *testingTBProthesis) Fatalf(format string, args ...any) {
log.Fatalf(format, args...)
}
func (t *testingTBProthesis) Logf(format string, args ...any) {
log.Printf(format, args...)
}
func (t *testingTBProthesis) Log(args ...any) {
log.Println(args...)
}
func (t *testingTBProthesis) Name() string {
return t.name
}
func (t *testingTBProthesis) TempDir() string {
dir, err := os.MkdirTemp("", t.name)
if err != nil {
t.Fatal(err)
}
t.cleanups = append([]func(){func() {
t.Logf("Cleaning UP: %v", dir)
os.RemoveAll(dir)
}}, t.cleanups...)
return dir
}
func (t *testingTBProthesis) close() {
for i := len(t.cleanups) - 1; i >= 0; i-- {
t.cleanups[i]()
}
}
================================================
FILE: client/pkg/testutil/testutil.go
================================================
// Copyright 2015 The etcd Authors
//
// 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 testutil provides test utility functions.
package testutil
import (
"flag"
"log"
"net/url"
"os"
"runtime"
"testing"
"time"
)
// WaitSchedule briefly sleeps in order to invoke the go scheduler.
// TODO: improve this when we are able to know the schedule or status of target go-routine.
func WaitSchedule() {
time.Sleep(10 * time.Millisecond)
}
func MustNewURLs(t *testing.T, urls []string) []url.URL {
t.Helper()
if urls == nil {
return nil
}
var us []url.URL
for _, url := range urls {
u := MustNewURL(t, url)
us = append(us, *u)
}
return us
}
func MustNewURL(t *testing.T, s string) *url.URL {
t.Helper()
u, err := url.Parse(s)
if err != nil {
t.Fatalf("parse %v error: %v", s, err)
}
return u
}
// FatalStack helps to fatal the test and print out the stacks of all running goroutines.
func FatalStack(t *testing.T, s string) {
t.Helper()
stackTrace := make([]byte, 1024*1024)
n := runtime.Stack(stackTrace, true)
t.Errorf("---> Test failed: %s", s)
t.Error(string(stackTrace[:n]))
t.Fatal(s)
}
// ConditionFunc returns true when a condition is met.
type ConditionFunc func() (bool, error)
// Poll calls a condition function repeatedly on a polling interval until it returns true, returns an error
// or the timeout is reached. If the condition function returns true or an error before the timeout, Poll
// immediately returns with the true value or the error. If the timeout is exceeded, Poll returns false.
func Poll(interval time.Duration, timeout time.Duration, condition ConditionFunc) (bool, error) {
timeoutCh := time.After(timeout)
ticker := time.NewTicker(interval)
defer ticker.Stop()
for {
select {
case <-timeoutCh:
return false, nil
case <-ticker.C:
success, err := condition()
if err != nil {
return false, err
}
if success {
return true, nil
}
}
}
}
func SkipTestIfShortMode(t TB, reason string) {
if t != nil {
t.Helper()
if testing.Short() {
t.Skip(reason)
}
}
}
// ExitInShortMode closes the current process (with 0) if the short test mode detected.
//
// To be used in Test-main, where test context (testing.TB) is not available.
func ExitInShortMode(reason string) {
// Calling testing.Short() requires flags to be parsed before.
if !flag.Parsed() {
flag.Parse()
}
if testing.Short() {
log.Println(reason)
os.Exit(0)
}
}
================================================
FILE: client/pkg/testutil/var.go
================================================
// Copyright 2018 The etcd Authors
//
// 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 testutil
import "time"
var (
ApplyTimeout = time.Second
RequestTimeout = 3 * time.Second
)
================================================
FILE: client/pkg/tlsutil/cipher_suites.go
================================================
// Copyright 2018 The etcd Authors
//
// 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 tlsutil
import (
"crypto/tls"
"fmt"
)
// GetCipherSuite returns the corresponding cipher suite,
// and boolean value if it is supported.
func GetCipherSuite(s string) (uint16, bool) {
for _, c := range tls.CipherSuites() {
if s == c.Name {
return c.ID, true
}
}
for _, c := range tls.InsecureCipherSuites() {
if s == c.Name {
return c.ID, true
}
}
switch s {
case "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305":
return tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, true
case "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305":
return tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, true
}
return 0, false
}
// GetCipherSuites returns list of corresponding cipher suite IDs.
func GetCipherSuites(ss []string) ([]uint16, error) {
cs := make([]uint16, len(ss))
for i, s := range ss {
var ok bool
cs[i], ok = GetCipherSuite(s)
if !ok {
return nil, fmt.Errorf("unexpected TLS cipher suite %q", s)
}
}
return cs, nil
}
================================================
FILE: client/pkg/tlsutil/cipher_suites_test.go
================================================
// Copyright 2018 The etcd Authors
//
// 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 tlsutil
import (
"crypto/tls"
"testing"
"github.com/stretchr/testify/require"
)
func TestGetCipherSuite_not_existing(t *testing.T) {
_, ok := GetCipherSuite("not_existing")
require.Falsef(t, ok, "Expected not ok")
}
func CipherSuiteExpectedToExist(tb testing.TB, cipher string, expectedID uint16) {
tb.Helper()
vid, ok := GetCipherSuite(cipher)
if !ok {
tb.Errorf("Expected %v cipher to exist", cipher)
}
if vid != expectedID {
tb.Errorf("For %v expected=%v found=%v", cipher, expectedID, vid)
}
}
func TestGetCipherSuite_success(t *testing.T) {
CipherSuiteExpectedToExist(t, "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA", tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA)
CipherSuiteExpectedToExist(t, "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256", tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256)
// Explicit test for legacy names
CipherSuiteExpectedToExist(t, "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305", tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256)
CipherSuiteExpectedToExist(t, "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305", tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256)
}
func TestGetCipherSuite_insecure(t *testing.T) {
CipherSuiteExpectedToExist(t, "TLS_ECDHE_RSA_WITH_RC4_128_SHA", tls.TLS_ECDHE_RSA_WITH_RC4_128_SHA)
}
================================================
FILE: client/pkg/tlsutil/doc.go
================================================
// Copyright 2016 The etcd Authors
//
// 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 tlsutil provides utility functions for handling TLS.
package tlsutil
================================================
FILE: client/pkg/tlsutil/tlsutil.go
================================================
// Copyright 2016 The etcd Authors
//
// 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 tlsutil
import (
"crypto/tls"
"crypto/x509"
"encoding/pem"
"os"
)
// NewCertPool creates x509 certPool with provided CA files.
func NewCertPool(CAFiles []string) (*x509.CertPool, error) {
certPool := x509.NewCertPool()
for _, CAFile := range CAFiles {
pemByte, err := os.ReadFile(CAFile)
if err != nil {
return nil, err
}
for {
var block *pem.Block
block, pemByte = pem.Decode(pemByte)
if block == nil {
break
}
cert, err := x509.ParseCertificate(block.Bytes)
if err != nil {
return nil, err
}
certPool.AddCert(cert)
}
}
return certPool, nil
}
// NewCert generates TLS cert by using the given cert,key and parse function.
func NewCert(certfile, keyfile string, parseFunc func([]byte, []byte) (tls.Certificate, error)) (*tls.Certificate, error) {
cert, err := os.ReadFile(certfile)
if err != nil {
return nil, err
}
key, err := os.ReadFile(keyfile)
if err != nil {
return nil, err
}
if parseFunc == nil {
parseFunc = tls.X509KeyPair
}
tlsCert, err := parseFunc(cert, key)
if err != nil {
return nil, err
}
return &tlsCert, nil
}
================================================
FILE: client/pkg/tlsutil/versions.go
================================================
// Copyright 2023 The etcd Authors
//
// 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 tlsutil
import (
"crypto/tls"
"fmt"
)
type TLSVersion string
// Constants for TLS versions.
const (
TLSVersionDefault TLSVersion = ""
TLSVersion12 TLSVersion = "TLS1.2"
TLSVersion13 TLSVersion = "TLS1.3"
)
// GetTLSVersion returns the corresponding tls.Version or error.
func GetTLSVersion(version string) (uint16, error) {
var v uint16
switch version {
case string(TLSVersionDefault):
v = 0 // 0 means let Go decide.
case string(TLSVersion12):
v = tls.VersionTLS12
case string(TLSVersion13):
v = tls.VersionTLS13
default:
return 0, fmt.Errorf("unexpected TLS version %q (must be one of: TLS1.2, TLS1.3)", version)
}
return v, nil
}
================================================
FILE: client/pkg/tlsutil/versions_test.go
================================================
// Copyright 2023 The etcd Authors
//
// 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 tlsutil
import (
"crypto/tls"
"testing"
"github.com/stretchr/testify/assert"
)
func TestGetVersion(t *testing.T) {
tests := []struct {
name string
version string
want uint16
expectError bool
}{
{
name: "TLS1.2",
version: "TLS1.2",
want: tls.VersionTLS12,
},
{
name: "TLS1.3",
version: "TLS1.3",
want: tls.VersionTLS13,
},
{
name: "Empty version",
version: "",
want: 0,
},
{
name: "Converting invalid version string to TLS version",
version: "not_existing",
expectError: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := GetTLSVersion(tt.version)
if err != nil {
assert.Truef(t, tt.expectError, "GetTLSVersion() returned error while expecting success: %v", err)
return
}
assert.Equal(t, tt.want, got)
})
}
}
================================================
FILE: client/pkg/transport/doc.go
================================================
// Copyright 2015 The etcd Authors
//
// 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 transport implements various HTTP transport utilities based on Go
// net package.
package transport
================================================
FILE: client/pkg/transport/keepalive_listener.go
================================================
// Copyright 2015 The etcd Authors
//
// 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 transport
import (
"crypto/tls"
"errors"
"fmt"
"net"
"time"
)
// NewKeepAliveListener returns a listener that listens on the given address.
// Be careful when wrap around KeepAliveListener with another Listener if TLSInfo is not nil.
// Some pkgs (like go/http) might expect Listener to return TLSConn type to start TLS handshake.
// http://tldp.org/HOWTO/TCP-Keepalive-HOWTO/overview.html
//
// Note(ahrtr):
// only `net.TCPConn` supports `SetKeepAlive` and `SetKeepAlivePeriod`
// by default, so if you want to wrap multiple layers of net.Listener,
// the `keepaliveListener` should be the one which is closest to the
// original `net.Listener` implementation, namely `TCPListener`.
func NewKeepAliveListener(l net.Listener, scheme string, tlscfg *tls.Config) (net.Listener, error) {
kal := &keepaliveListener{
Listener: l,
}
if scheme == "https" {
if tlscfg == nil {
return nil, errors.New("cannot listen on TLS for given listener: KeyFile and CertFile are not presented")
}
return newTLSKeepaliveListener(kal, tlscfg), nil
}
return kal, nil
}
type keepaliveListener struct{ net.Listener }
func (kln *keepaliveListener) Accept() (net.Conn, error) {
c, err := kln.Listener.Accept()
if err != nil {
return nil, err
}
kac, err := createKeepaliveConn(c)
if err != nil {
return nil, fmt.Errorf("create keepalive connection failed, %w", err)
}
// detection time: tcp_keepalive_time + tcp_keepalive_probes + tcp_keepalive_intvl
// default on linux: 30 + 8 * 30
// default on osx: 30 + 8 * 75
if err := kac.SetKeepAlive(true); err != nil {
return nil, fmt.Errorf("SetKeepAlive failed, %w", err)
}
if err := kac.SetKeepAlivePeriod(30 * time.Second); err != nil {
return nil, fmt.Errorf("SetKeepAlivePeriod failed, %w", err)
}
return kac, nil
}
func createKeepaliveConn(c net.Conn) (*keepAliveConn, error) {
tcpc, ok := c.(*net.TCPConn)
if !ok {
return nil, ErrNotTCP
}
return &keepAliveConn{tcpc}, nil
}
type keepAliveConn struct {
*net.TCPConn
}
// SetKeepAlive sets keepalive
func (l *keepAliveConn) SetKeepAlive(doKeepAlive bool) error {
return l.TCPConn.SetKeepAlive(doKeepAlive)
}
// A tlsKeepaliveListener implements a network listener (net.Listener) for TLS connections.
type tlsKeepaliveListener struct {
net.Listener
config *tls.Config
}
// Accept waits for and returns the next incoming TLS connection.
// The returned connection c is a *tls.Conn.
func (l *tlsKeepaliveListener) Accept() (net.Conn, error) {
c, err := l.Listener.Accept()
if err != nil {
return nil, err
}
c = tls.Server(c, l.config)
return c, nil
}
// newTLSKeepaliveListener creates a Listener which accepts connections from an inner
// Listener and wraps each connection with Server.
// The configuration config must be non-nil and must have
// at least one certificate.
func newTLSKeepaliveListener(inner net.Listener, config *tls.Config) net.Listener {
l := &tlsKeepaliveListener{}
l.Listener = inner
l.config = config
return l
}
================================================
FILE: client/pkg/transport/keepalive_listener_openbsd.go
================================================
// Copyright 2023 The etcd Authors
//
// 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.
//go:build openbsd
package transport
import "time"
// SetKeepAlivePeriod sets keepalive period
func (l *keepAliveConn) SetKeepAlivePeriod(d time.Duration) error {
// OpenBSD has no user-settable per-socket TCP keepalive options.
// Refer to https://github.com/etcd-io/etcd/issues/15811.
return nil
}
================================================
FILE: client/pkg/transport/keepalive_listener_test.go
================================================
// Copyright 2015 The etcd Authors
//
// 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 transport
import (
"crypto/tls"
"net"
"net/http"
"testing"
"github.com/stretchr/testify/require"
)
// TestNewKeepAliveListener tests NewKeepAliveListener returns a listener
// that accepts connections.
// TODO: verify the keepalive option is set correctly
func TestNewKeepAliveListener(t *testing.T) {
ln, err := net.Listen("tcp", "127.0.0.1:0")
require.NoErrorf(t, err, "unexpected listen error")
ln, err = NewKeepAliveListener(ln, "http", nil)
require.NoErrorf(t, err, "unexpected NewKeepAliveListener error")
go http.Get("http://" + ln.Addr().String())
conn, err := ln.Accept()
require.NoErrorf(t, err, "unexpected Accept error")
_, ok := conn.(*keepAliveConn)
require.Truef(t, ok, "Unexpected conn type: %T, wanted *keepAliveConn", conn)
conn.Close()
ln.Close()
ln, err = net.Listen("tcp", "127.0.0.1:0")
require.NoErrorf(t, err, "unexpected Listen error")
// tls
tlsinfo, err := createSelfCert(t)
require.NoErrorf(t, err, "unable to create tmpfile")
tlsInfo := TLSInfo{CertFile: tlsinfo.CertFile, KeyFile: tlsinfo.KeyFile}
tlsInfo.parseFunc = fakeCertificateParserFunc(nil)
tlscfg, err := tlsInfo.ServerConfig()
require.NoErrorf(t, err, "unexpected serverConfig error")
tlsln, err := NewKeepAliveListener(ln, "https", tlscfg)
require.NoErrorf(t, err, "unexpected NewKeepAliveListener error")
go http.Get("https://" + tlsln.Addr().String())
conn, err = tlsln.Accept()
require.NoErrorf(t, err, "unexpected Accept error")
if _, ok := conn.(*tls.Conn); !ok {
t.Errorf("failed to accept *tls.Conn")
}
conn.Close()
tlsln.Close()
}
func TestNewKeepAliveListenerTLSEmptyConfig(t *testing.T) {
ln, err := net.Listen("tcp", "127.0.0.1:0")
require.NoErrorf(t, err, "unexpected listen error")
_, err = NewKeepAliveListener(ln, "https", nil)
if err == nil {
t.Errorf("err = nil, want not presented error")
}
}
================================================
FILE: client/pkg/transport/keepalive_listener_unix.go
================================================
// Copyright 2023 The etcd Authors
//
// 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.
//go:build !openbsd
package transport
import "time"
// SetKeepAlivePeriod sets keepalive period
func (l *keepAliveConn) SetKeepAlivePeriod(d time.Duration) error {
return l.TCPConn.SetKeepAlivePeriod(d)
}
================================================
FILE: client/pkg/transport/limit_listen.go
================================================
// Copyright 2013 The etcd Authors
//
// 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 transport provides network utility functions, complementing the more
// common ones in the net package.
package transport
import (
"errors"
"net"
"sync"
"time"
)
var ErrNotTCP = errors.New("only tcp connections have keepalive")
// LimitListener returns a Listener that accepts at most n simultaneous
// connections from the provided Listener.
func LimitListener(l net.Listener, n int) net.Listener {
return &limitListener{l, make(chan struct{}, n)}
}
type limitListener struct {
net.Listener
sem chan struct{}
}
func (l *limitListener) acquire() { l.sem <- struct{}{} }
func (l *limitListener) release() { <-l.sem }
func (l *limitListener) Accept() (net.Conn, error) {
l.acquire()
c, err := l.Listener.Accept()
if err != nil {
l.release()
return nil, err
}
return &limitListenerConn{Conn: c, release: l.release}, nil
}
type limitListenerConn struct {
net.Conn
releaseOnce sync.Once
release func()
}
func (l *limitListenerConn) Close() error {
err := l.Conn.Close()
l.releaseOnce.Do(l.release)
return err
}
// SetKeepAlive sets keepalive
//
// Deprecated: use (*keepAliveConn) SetKeepAlive instead.
func (l *limitListenerConn) SetKeepAlive(doKeepAlive bool) error {
tcpc, ok := l.Conn.(*net.TCPConn)
if !ok {
return ErrNotTCP
}
return tcpc.SetKeepAlive(doKeepAlive)
}
// SetKeepAlivePeriod sets keepalive period
//
// Deprecated: use (*keepAliveConn) SetKeepAlivePeriod instead.
func (l *limitListenerConn) SetKeepAlivePeriod(d time.Duration) error {
tcpc, ok := l.Conn.(*net.TCPConn)
if !ok {
return ErrNotTCP
}
return tcpc.SetKeepAlivePeriod(d)
}
================================================
FILE: client/pkg/transport/listener.go
================================================
// Copyright 2015 The etcd Authors
//
// 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 transport
import (
"context"
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"crypto/tls"
"crypto/x509"
"crypto/x509/pkix"
"encoding/pem"
"errors"
"fmt"
"math/big"
"net"
"os"
"path/filepath"
"strings"
"time"
"go.uber.org/zap"
"go.etcd.io/etcd/client/pkg/v3/fileutil"
"go.etcd.io/etcd/client/pkg/v3/tlsutil"
"go.etcd.io/etcd/client/pkg/v3/verify"
)
// NewListener creates a new listner.
func NewListener(addr, scheme string, tlsinfo *TLSInfo) (l net.Listener, err error) {
return newListener(addr, scheme, WithTLSInfo(tlsinfo))
}
// NewListenerWithOpts creates a new listener which accepts listener options.
func NewListenerWithOpts(addr, scheme string, opts ...ListenerOption) (net.Listener, error) {
return newListener(addr, scheme, opts...)
}
func newListener(addr, scheme string, opts ...ListenerOption) (net.Listener, error) {
if scheme == "unix" || scheme == "unixs" {
// unix sockets via unix://laddr
return NewUnixListener(addr)
}
lnOpts := newListenOpts(opts...)
switch {
case lnOpts.IsSocketOpts():
// new ListenConfig with socket options.
lnOpts.ListenConfig = newListenConfig(lnOpts.socketOpts)
// check for timeout
fallthrough
case lnOpts.IsTimeout(), lnOpts.IsSocketOpts():
// timeout listener with socket options.
ln, err := newKeepAliveListener(&lnOpts.ListenConfig, addr)
if err != nil {
return nil, err
}
lnOpts.Listener = &rwTimeoutListener{
Listener: ln,
readTimeout: lnOpts.readTimeout,
writeTimeout: lnOpts.writeTimeout,
}
case lnOpts.IsTimeout():
ln, err := newKeepAliveListener(nil, addr)
if err != nil {
return nil, err
}
lnOpts.Listener = &rwTimeoutListener{
Listener: ln,
readTimeout: lnOpts.readTimeout,
writeTimeout: lnOpts.writeTimeout,
}
default:
ln, err := newKeepAliveListener(nil, addr)
if err != nil {
return nil, err
}
lnOpts.Listener = ln
}
// only skip if not passing TLSInfo
if lnOpts.skipTLSInfoCheck && !lnOpts.IsTLS() {
return lnOpts.Listener, nil
}
return wrapTLS(scheme, lnOpts.tlsInfo, lnOpts.Listener)
}
func newKeepAliveListener(cfg *net.ListenConfig, addr string) (net.Listener, error) {
var ln net.Listener
var err error
if cfg != nil {
ln, err = cfg.Listen(context.TODO(), "tcp", addr)
} else {
ln, err = net.Listen("tcp", addr)
}
if err != nil {
return nil, err
}
return NewKeepAliveListener(ln, "tcp", nil)
}
func wrapTLS(scheme string, tlsinfo *TLSInfo, l net.Listener) (net.Listener, error) {
if scheme != "https" && scheme != "unixs" {
return l, nil
}
if tlsinfo != nil && tlsinfo.SkipClientSANVerify {
return NewTLSListener(l, tlsinfo)
}
return newTLSListener(l, tlsinfo, checkSAN)
}
func newListenConfig(sopts *SocketOpts) net.ListenConfig {
lc := net.ListenConfig{}
if sopts != nil {
ctls := getControls(sopts)
if len(ctls) > 0 {
lc.Control = ctls.Control
}
}
return lc
}
type TLSInfo struct {
// CertFile is the _server_ cert, it will also be used as a _client_ certificate if ClientCertFile is empty
CertFile string
// KeyFile is the key for the CertFile
KeyFile string
// ClientCertFile is a _client_ cert for initiating connections when ClientCertAuth is defined. If ClientCertAuth
// is true but this value is empty, the CertFile will be used instead.
ClientCertFile string
// ClientKeyFile is the key for the ClientCertFile
ClientKeyFile string
TrustedCAFile string
ClientCertAuth bool
CRLFile string
InsecureSkipVerify bool
SkipClientSANVerify bool
// ServerName ensures the cert matches the given host in case of discovery / virtual hosting
ServerName string
// HandshakeFailure is optionally called when a connection fails to handshake. The
// connection will be closed immediately afterwards.
HandshakeFailure func(*tls.Conn, error)
// CipherSuites is a list of supported cipher suites.
// If empty, Go auto-populates it by default.
// Note that cipher suites are prioritized in the given order.
CipherSuites []uint16
// MinVersion is the minimum TLS version that is acceptable.
// If not set, the minimum version is TLS 1.2.
MinVersion uint16
// MaxVersion is the maximum TLS version that is acceptable.
// If not set, the default used by Go is selected (see tls.Config.MaxVersion).
MaxVersion uint16
selfCert bool
// parseFunc exists to simplify testing. Typically, parseFunc
// should be left nil. In that case, tls.X509KeyPair will be used.
parseFunc func([]byte, []byte) (tls.Certificate, error)
// AllowedCN is a CN which must be provided by a client.
//
// Deprecated: use AllowedCNs instead.
AllowedCN string
// AllowedHostname is an IP address or hostname that must match the TLS
// certificate provided by a client.
//
// Deprecated: use AllowedHostnames instead.
AllowedHostname string
// AllowedCNs is a list of acceptable CNs which must be provided by a client.
AllowedCNs []string
// AllowedHostnames is a list of acceptable IP addresses or hostnames that must match the
// TLS certificate provided by a client.
AllowedHostnames []string
// Logger logs TLS errors.
// If nil, all logs are discarded.
Logger *zap.Logger
// EmptyCN indicates that the cert must have empty CN.
// If true, ClientConfig() will return an error for a cert with non empty CN.
EmptyCN bool
// LocalAddr is the local IP address to use when communicating with a peer.
LocalAddr string
}
func (info TLSInfo) String() string {
return fmt.Sprintf("cert = %s, key = %s, client-cert=%s, client-key=%s, trusted-ca = %s, client-cert-auth = %v, crl-file = %s", info.CertFile, info.KeyFile, info.ClientCertFile, info.ClientKeyFile, info.TrustedCAFile, info.ClientCertAuth, info.CRLFile)
}
func (info TLSInfo) Empty() bool {
return info.CertFile == "" && info.KeyFile == ""
}
func SelfCert(lg *zap.Logger, dirpath string, hosts []string, selfSignedCertValidity uint, additionalUsages ...x509.ExtKeyUsage) (TLSInfo, error) {
verify.Assert(lg != nil, "nil log isn't allowed")
var err error
info := TLSInfo{Logger: lg}
if selfSignedCertValidity == 0 {
err = errors.New("selfSignedCertValidity is invalid,it should be greater than 0")
info.Logger.Warn(
"cannot generate cert",
zap.Error(err),
)
return info, err
}
err = fileutil.TouchDirAll(lg, dirpath)
if err != nil {
info.Logger.Warn(
"cannot create cert directory",
zap.Error(err),
)
return info, err
}
certPath, err := filepath.Abs(filepath.Join(dirpath, "cert.pem"))
if err != nil {
return info, err
}
keyPath, err := filepath.Abs(filepath.Join(dirpath, "key.pem"))
if err != nil {
return info, err
}
_, errcert := os.Stat(certPath)
_, errkey := os.Stat(keyPath)
if errcert == nil && errkey == nil {
info.CertFile = certPath
info.KeyFile = keyPath
info.ClientCertFile = certPath
info.ClientKeyFile = keyPath
info.selfCert = true
return info, err
}
serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128)
serialNumber, err := rand.Int(rand.Reader, serialNumberLimit)
if err != nil {
info.Logger.Warn(
"cannot generate random number",
zap.Error(err),
)
return info, err
}
tmpl := x509.Certificate{
SerialNumber: serialNumber,
Subject: pkix.Name{Organization: []string{"etcd"}},
NotBefore: time.Now(),
NotAfter: time.Now().Add(time.Duration(selfSignedCertValidity) * 365 * (24 * time.Hour)),
KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature | x509.KeyUsageCRLSign,
ExtKeyUsage: append([]x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}, additionalUsages...),
BasicConstraintsValid: true,
IsCA: true,
}
info.Logger.Warn(
"automatically generate certificates",
zap.Time("certificate-validity-bound-not-after", tmpl.NotAfter),
)
for _, host := range hosts {
h, _, _ := net.SplitHostPort(host)
if ip := net.ParseIP(h); ip != nil {
tmpl.IPAddresses = append(tmpl.IPAddresses, ip)
} else {
tmpl.DNSNames = append(tmpl.DNSNames, h)
}
}
priv, err := ecdsa.GenerateKey(elliptic.P521(), rand.Reader)
if err != nil {
info.Logger.Warn(
"cannot generate ECDSA key",
zap.Error(err),
)
return info, err
}
derBytes, err := x509.CreateCertificate(rand.Reader, &tmpl, &tmpl, &priv.PublicKey, priv)
if err != nil {
info.Logger.Warn(
"cannot generate x509 certificate",
zap.Error(err),
)
return info, err
}
certOut, err := os.Create(certPath)
if err != nil {
info.Logger.Warn(
"cannot cert file",
zap.String("path", certPath),
zap.Error(err),
)
return info, err
}
pem.Encode(certOut, &pem.Block{Type: "CERTIFICATE", Bytes: derBytes})
certOut.Close()
info.Logger.Info("created cert file", zap.String("path", certPath))
b, err := x509.MarshalECPrivateKey(priv)
if err != nil {
return info, err
}
keyOut, err := os.OpenFile(keyPath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0o600)
if err != nil {
info.Logger.Warn(
"cannot key file",
zap.String("path", keyPath),
zap.Error(err),
)
return info, err
}
pem.Encode(keyOut, &pem.Block{Type: "EC PRIVATE KEY", Bytes: b})
keyOut.Close()
info.Logger.Info("created key file", zap.String("path", keyPath))
return SelfCert(lg, dirpath, hosts, selfSignedCertValidity)
}
// baseConfig is called on initial TLS handshake start.
//
// Previously,
// 1. Server has non-empty (*tls.Config).Certificates on client hello
// 2. Server calls (*tls.Config).GetCertificate iff:
// - Server's (*tls.Config).Certificates is not empty, or
// - Client supplies SNI; non-empty (*tls.ClientHelloInfo).ServerName
//
// When (*tls.Config).Certificates is always populated on initial handshake,
// client is expected to provide a valid matching SNI to pass the TLS
// verification, thus trigger server (*tls.Config).GetCertificate to reload
// TLS assets. However, a cert whose SAN field does not include domain names
// but only IP addresses, has empty (*tls.ClientHelloInfo).ServerName, thus
// it was never able to trigger TLS reload on initial handshake; first
// ceritifcate object was being used, never being updated.
//
// Now, (*tls.Config).Certificates is created empty on initial TLS client
// handshake, in order to trigger (*tls.Config).GetCertificate and populate
// rest of the certificates on every new TLS connection, even when client
// SNI is empty (e.g. cert only includes IPs).
func (info TLSInfo) baseConfig() (*tls.Config, error) {
if info.KeyFile == "" || info.CertFile == "" {
return nil, fmt.Errorf("KeyFile and CertFile must both be present[key: %v, cert: %v]", info.KeyFile, info.CertFile)
}
if info.Logger == nil {
info.Logger = zap.NewNop()
}
_, err := tlsutil.NewCert(info.CertFile, info.KeyFile, info.parseFunc)
if err != nil {
return nil, err
}
// Perform prevalidation of client cert and key if either are provided. This makes sure we crash before accepting any connections.
if (info.ClientKeyFile == "") != (info.ClientCertFile == "") {
return nil, fmt.Errorf("ClientKeyFile and ClientCertFile must both be present or both absent: key: %v, cert: %v]", info.ClientKeyFile, info.ClientCertFile)
}
if info.ClientCertFile != "" {
_, err := tlsutil.NewCert(info.ClientCertFile, info.ClientKeyFile, info.parseFunc)
if err != nil {
return nil, err
}
}
var minVersion uint16
if info.MinVersion != 0 {
minVersion = info.MinVersion
} else {
// Default minimum version is TLS 1.2, previous versions are insecure and deprecated.
minVersion = tls.VersionTLS12
}
cfg := &tls.Config{
MinVersion: minVersion,
MaxVersion: info.MaxVersion,
ServerName: info.ServerName,
}
if len(info.CipherSuites) > 0 {
cfg.CipherSuites = info.CipherSuites
}
// Client certificates may be verified by either an exact match on the CN,
// or a more general check of the CN and SANs.
var verifyCertificate func(*x509.Certificate) bool
if info.AllowedCN != "" && len(info.AllowedCNs) > 0 {
return nil, fmt.Errorf("AllowedCN and AllowedCNs are mutually exclusive (cn=%q, cns=%q)", info.AllowedCN, info.AllowedCNs)
}
if info.AllowedHostname != "" && len(info.AllowedHostnames) > 0 {
return nil, fmt.Errorf("AllowedHostname and AllowedHostnames are mutually exclusive (hostname=%q, hostnames=%q)", info.AllowedHostname, info.AllowedHostnames)
}
if info.AllowedCN != "" && info.AllowedHostname != "" {
return nil, fmt.Errorf("AllowedCN and AllowedHostname are mutually exclusive (cn=%q, hostname=%q)", info.AllowedCN, info.AllowedHostname)
}
if len(info.AllowedCNs) > 0 && len(info.AllowedHostnames) > 0 {
return nil, fmt.Errorf("AllowedCNs and AllowedHostnames are mutually exclusive (cns=%q, hostnames=%q)", info.AllowedCNs, info.AllowedHostnames)
}
if info.AllowedCN != "" {
info.Logger.Warn("AllowedCN is deprecated, use AllowedCNs instead")
verifyCertificate = func(cert *x509.Certificate) bool {
return info.AllowedCN == cert.Subject.CommonName
}
}
if info.AllowedHostname != "" {
info.Logger.Warn("AllowedHostname is deprecated, use AllowedHostnames instead")
verifyCertificate = func(cert *x509.Certificate) bool {
return cert.VerifyHostname(info.AllowedHostname) == nil
}
}
if len(info.AllowedCNs) > 0 {
verifyCertificate = func(cert *x509.Certificate) bool {
for _, allowedCN := range info.AllowedCNs {
if allowedCN == cert.Subject.CommonName {
return true
}
}
return false
}
}
if len(info.AllowedHostnames) > 0 {
verifyCertificate = func(cert *x509.Certificate) bool {
for _, allowedHostname := range info.AllowedHostnames {
if cert.VerifyHostname(allowedHostname) == nil {
return true
}
}
return false
}
}
if verifyCertificate != nil {
cfg.VerifyPeerCertificate = func(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error {
for _, chains := range verifiedChains {
if len(chains) != 0 {
if verifyCertificate(chains[0]) {
return nil
}
}
}
return errors.New("client certificate authentication failed")
}
}
// this only reloads certs when there's a client request
// TODO: support server-side refresh (e.g. inotify, SIGHUP), caching
cfg.GetCertificate = func(clientHello *tls.ClientHelloInfo) (cert *tls.Certificate, err error) {
cert, err = tlsutil.NewCert(info.CertFile, info.KeyFile, info.parseFunc)
if os.IsNotExist(err) {
info.Logger.Warn(
"failed to find peer cert files",
zap.String("cert-file", info.CertFile),
zap.String("key-file", info.KeyFile),
zap.Error(err),
)
} else if err != nil {
info.Logger.Warn(
"failed to create peer certificate",
zap.String("cert-file", info.CertFile),
zap.String("key-file", info.KeyFile),
zap.Error(err),
)
}
return cert, err
}
cfg.GetClientCertificate = func(unused *tls.CertificateRequestInfo) (cert *tls.Certificate, err error) {
certfile, keyfile := info.CertFile, info.KeyFile
if info.ClientCertFile != "" {
certfile, keyfile = info.ClientCertFile, info.ClientKeyFile
}
cert, err = tlsutil.NewCert(certfile, keyfile, info.parseFunc)
if os.IsNotExist(err) {
info.Logger.Warn(
"failed to find client cert files",
zap.String("cert-file", certfile),
zap.String("key-file", keyfile),
zap.Error(err),
)
} else if err != nil {
info.Logger.Warn(
"failed to create client certificate",
zap.String("cert-file", certfile),
zap.String("key-file", keyfile),
zap.Error(err),
)
}
return cert, err
}
return cfg, nil
}
// cafiles returns a list of CA file paths.
func (info TLSInfo) cafiles() []string {
cs := make([]string, 0)
if info.TrustedCAFile != "" {
cs = append(cs, info.TrustedCAFile)
}
return cs
}
// ServerConfig generates a tls.Config object for use by an HTTP server.
func (info TLSInfo) ServerConfig() (*tls.Config, error) {
cfg, err := info.baseConfig()
if err != nil {
return nil, err
}
if info.Logger == nil {
info.Logger = zap.NewNop()
}
cfg.ClientAuth = tls.NoClientCert
if info.TrustedCAFile != "" || info.ClientCertAuth {
cfg.ClientAuth = tls.RequireAndVerifyClientCert
}
cs := info.cafiles()
if len(cs) > 0 {
info.Logger.Info("Loading cert pool", zap.Strings("cs", cs),
zap.Any("tlsinfo", info))
cp, err := tlsutil.NewCertPool(cs)
if err != nil {
return nil, err
}
cfg.ClientCAs = cp
}
// "h2" NextProtos is necessary for enabling HTTP2 for go's HTTP server
cfg.NextProtos = []string{"h2"}
return cfg, nil
}
// ClientConfig generates a tls.Config object for use by an HTTP client.
func (info TLSInfo) ClientConfig() (*tls.Config, error) {
var cfg *tls.Config
var err error
if !info.Empty() {
cfg, err = info.baseConfig()
if err != nil {
return nil, err
}
} else {
cfg = &tls.Config{ServerName: info.ServerName}
}
cfg.InsecureSkipVerify = info.InsecureSkipVerify
cs := info.cafiles()
if len(cs) > 0 {
cfg.RootCAs, err = tlsutil.NewCertPool(cs)
if err != nil {
return nil, err
}
}
if info.selfCert {
cfg.InsecureSkipVerify = true
}
if info.EmptyCN {
hasNonEmptyCN := false
cn := ""
_, err := tlsutil.NewCert(info.CertFile, info.KeyFile, func(certPEMBlock []byte, keyPEMBlock []byte) (tls.Certificate, error) {
var block *pem.Block
block, _ = pem.Decode(certPEMBlock)
cert, err := x509.ParseCertificate(block.Bytes)
if err != nil {
return tls.Certificate{}, err
}
if len(cert.Subject.CommonName) != 0 {
hasNonEmptyCN = true
cn = cert.Subject.CommonName
}
return tls.X509KeyPair(certPEMBlock, keyPEMBlock)
})
if err != nil {
return nil, err
}
if hasNonEmptyCN {
return nil, fmt.Errorf("cert has non empty Common Name (%s): %s", cn, info.CertFile)
}
}
return cfg, nil
}
// IsClosedConnError returns true if the error is from closing listener, cmux.
// copied from golang.org/x/net/http2/http2.go
func IsClosedConnError(err error) bool {
// 'use of closed network connection' (Go <=1.8)
// 'use of closed file or network connection' (Go >1.8, internal/poll.ErrClosing)
// 'mux: listener closed' (cmux.ErrListenerClosed)
return err != nil && strings.Contains(err.Error(), "closed")
}
================================================
FILE: client/pkg/transport/listener_opts.go
================================================
// Copyright 2021 The etcd Authors
//
// 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 transport
import (
"net"
"time"
)
type ListenerOptions struct {
Listener net.Listener
ListenConfig net.ListenConfig
socketOpts *SocketOpts
tlsInfo *TLSInfo
skipTLSInfoCheck bool
writeTimeout time.Duration
readTimeout time.Duration
}
func newListenOpts(opts ...ListenerOption) *ListenerOptions {
lnOpts := &ListenerOptions{}
lnOpts.applyOpts(opts)
return lnOpts
}
func (lo *ListenerOptions) applyOpts(opts []ListenerOption) {
for _, opt := range opts {
opt(lo)
}
}
// IsTimeout returns true if the listener has a read/write timeout defined.
func (lo *ListenerOptions) IsTimeout() bool { return lo.readTimeout != 0 || lo.writeTimeout != 0 }
// IsSocketOpts returns true if the listener options includes socket options.
func (lo *ListenerOptions) IsSocketOpts() bool {
if lo.socketOpts == nil {
return false
}
return lo.socketOpts.ReusePort || lo.socketOpts.ReuseAddress
}
// IsTLS returns true if listner options includes TLSInfo.
func (lo *ListenerOptions) IsTLS() bool {
if lo.tlsInfo == nil {
return false
}
return !lo.tlsInfo.Empty()
}
// ListenerOption are options which can be applied to the listener.
type ListenerOption func(*ListenerOptions)
// WithTimeout allows for a read or write timeout to be applied to the listener.
func WithTimeout(read, write time.Duration) ListenerOption {
return func(lo *ListenerOptions) {
lo.writeTimeout = write
lo.readTimeout = read
}
}
// WithSocketOpts defines socket options that will be applied to the listener.
func WithSocketOpts(s *SocketOpts) ListenerOption {
return func(lo *ListenerOptions) { lo.socketOpts = s }
}
// WithTLSInfo adds TLS credentials to the listener.
func WithTLSInfo(t *TLSInfo) ListenerOption {
return func(lo *ListenerOptions) { lo.tlsInfo = t }
}
// WithSkipTLSInfoCheck when true a transport can be created with an https scheme
// without passing TLSInfo, circumventing not presented error. Skipping this check
// also requires that TLSInfo is not passed.
func WithSkipTLSInfoCheck(skip bool) ListenerOption {
return func(lo *ListenerOptions) { lo.skipTLSInfoCheck = skip }
}
================================================
FILE: client/pkg/transport/listener_test.go
================================================
// Copyright 2015 The etcd Authors
//
// 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 transport
import (
"crypto/rand"
"crypto/tls"
"crypto/x509"
"encoding/pem"
"errors"
"math/big"
"net"
"net/http"
"os"
"path/filepath"
"sync"
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"go.uber.org/zap/zaptest"
)
func createSelfCert(t *testing.T) (*TLSInfo, error) {
t.Helper()
return createSelfCertEx(t, "127.0.0.1")
}
func createSelfCertEx(t *testing.T, host string, additionalUsages ...x509.ExtKeyUsage) (*TLSInfo, error) {
t.Helper()
d := t.TempDir()
info, err := SelfCert(zaptest.NewLogger(t), d, []string{host + ":0"}, 1, additionalUsages...)
if err != nil {
return nil, err
}
return &info, nil
}
func fakeCertificateParserFunc(err error) func(certPEMBlock, keyPEMBlock []byte) (tls.Certificate, error) {
return func(certPEMBlock, keyPEMBlock []byte) (tls.Certificate, error) {
return tls.Certificate{}, err
}
}
// TestNewListenerTLSInfo tests that NewListener with valid TLSInfo returns
// a TLS listener that accepts TLS connections.
func TestNewListenerTLSInfo(t *testing.T) {
tlsInfo, err := createSelfCert(t)
require.NoErrorf(t, err, "unable to create cert")
testNewListenerTLSInfoAccept(t, *tlsInfo)
}
func TestNewListenerWithOpts(t *testing.T) {
tlsInfo, err := createSelfCert(t)
require.NoErrorf(t, err, "unable to create cert")
tests := map[string]struct {
opts []ListenerOption
scheme string
expectedErr bool
}{
"https scheme no TLSInfo": {
opts: []ListenerOption{},
expectedErr: true,
scheme: "https",
},
"https scheme no TLSInfo with skip check": {
opts: []ListenerOption{WithSkipTLSInfoCheck(true)},
expectedErr: false,
scheme: "https",
},
"https scheme empty TLSInfo with skip check": {
opts: []ListenerOption{
WithSkipTLSInfoCheck(true),
WithTLSInfo(&TLSInfo{}),
},
expectedErr: false,
scheme: "https",
},
"https scheme empty TLSInfo no skip check": {
opts: []ListenerOption{
WithTLSInfo(&TLSInfo{}),
},
expectedErr: true,
scheme: "https",
},
"https scheme with TLSInfo and skip check": {
opts: []ListenerOption{
WithSkipTLSInfoCheck(true),
WithTLSInfo(tlsInfo),
},
expectedErr: false,
scheme: "https",
},
}
for testName, test := range tests {
t.Run(testName, func(t *testing.T) {
ln, err := NewListenerWithOpts("127.0.0.1:0", test.scheme, test.opts...)
if ln != nil {
defer ln.Close()
}
require.Falsef(t, test.expectedErr && err == nil, "expected error")
if !test.expectedErr {
require.NoErrorf(t, err, "unexpected error: %v", err)
}
})
}
}
func TestNewListenerWithSocketOpts(t *testing.T) {
tlsInfo, err := createSelfCert(t)
require.NoErrorf(t, err, "unable to create cert")
tests := map[string]struct {
opts []ListenerOption
scheme string
expectedErr bool
}{
"nil socketopts": {
opts: []ListenerOption{WithSocketOpts(nil)},
expectedErr: true,
scheme: "http",
},
"empty socketopts": {
opts: []ListenerOption{WithSocketOpts(&SocketOpts{})},
expectedErr: true,
scheme: "http",
},
"reuse address": {
opts: []ListenerOption{WithSocketOpts(&SocketOpts{ReuseAddress: true})},
scheme: "http",
expectedErr: true,
},
"reuse address with TLS": {
opts: []ListenerOption{
WithSocketOpts(&SocketOpts{ReuseAddress: true}),
WithTLSInfo(tlsInfo),
},
scheme: "https",
expectedErr: true,
},
"reuse address and port": {
opts: []ListenerOption{WithSocketOpts(&SocketOpts{ReuseAddress: true, ReusePort: true})},
scheme: "http",
expectedErr: false,
},
"reuse address and port with TLS": {
opts: []ListenerOption{
WithSocketOpts(&SocketOpts{ReuseAddress: true, ReusePort: true}),
WithTLSInfo(tlsInfo),
},
scheme: "https",
expectedErr: false,
},
"reuse port with TLS and timeout": {
opts: []ListenerOption{
WithSocketOpts(&SocketOpts{ReusePort: true}),
WithTLSInfo(tlsInfo),
WithTimeout(5*time.Second, 5*time.Second),
},
scheme: "https",
expectedErr: false,
},
"reuse port with https scheme and no TLSInfo skip check": {
opts: []ListenerOption{
WithSocketOpts(&SocketOpts{ReusePort: true}),
WithSkipTLSInfoCheck(true),
},
scheme: "https",
expectedErr: false,
},
"reuse port": {
opts: []ListenerOption{WithSocketOpts(&SocketOpts{ReusePort: true})},
scheme: "http",
expectedErr: false,
},
}
for testName, test := range tests {
t.Run(testName, func(t *testing.T) {
ln, err := NewListenerWithOpts("127.0.0.1:0", test.scheme, test.opts...)
require.NoErrorf(t, err, "unexpected NewListenerWithSocketOpts error")
defer ln.Close()
ln2, err := NewListenerWithOpts(ln.Addr().String(), test.scheme, test.opts...)
if ln2 != nil {
ln2.Close()
}
if test.expectedErr {
require.Errorf(t, err, "expected error")
}
if !test.expectedErr {
require.NoErrorf(t, err, "unexpected error: %v", err)
}
if test.scheme == "http" {
lnOpts := newListenOpts(test.opts...)
if !lnOpts.IsSocketOpts() && !lnOpts.IsTimeout() {
_, ok := ln.(*keepaliveListener)
require.Truef(t, ok, "ln: unexpected listener type: %T, wanted *keepaliveListener", ln)
}
}
})
}
}
func testNewListenerTLSInfoAccept(t *testing.T, tlsInfo TLSInfo) {
t.Helper()
ln, err := NewListener("127.0.0.1:0", "https", &tlsInfo)
require.NoErrorf(t, err, "unexpected NewListener error")
defer ln.Close()
tr := &http.Transport{TLSClientConfig: &tls.Config{InsecureSkipVerify: true}}
cli := &http.Client{Transport: tr}
go cli.Get("https://" + ln.Addr().String())
conn, err := ln.Accept()
require.NoErrorf(t, err, "unexpected Accept error")
defer conn.Close()
if _, ok := conn.(*tls.Conn); !ok {
t.Error("failed to accept *tls.Conn")
}
}
// TestNewListenerTLSInfoSkipClientSANVerify tests that if client IP address mismatches
// with specified address in its certificate the connection is still accepted
// if the flag SkipClientSANVerify is set (i.e. checkSAN() is disabled for the client side)
func TestNewListenerTLSInfoSkipClientSANVerify(t *testing.T) {
tests := []struct {
skipClientSANVerify bool
goodClientHost bool
acceptExpected bool
}{
{false, true, true},
{false, false, false},
{true, true, true},
{true, false, true},
}
for _, test := range tests {
testNewListenerTLSInfoClientCheck(t, test.skipClientSANVerify, test.goodClientHost, test.acceptExpected)
}
}
func testNewListenerTLSInfoClientCheck(t *testing.T, skipClientSANVerify, goodClientHost, acceptExpected bool) {
t.Helper()
tlsInfo, err := createSelfCert(t)
require.NoErrorf(t, err, "unable to create cert")
host := "127.0.0.222"
if goodClientHost {
host = "127.0.0.1"
}
clientTLSInfo, err := createSelfCertEx(t, host, x509.ExtKeyUsageClientAuth)
require.NoErrorf(t, err, "unable to create cert")
tlsInfo.SkipClientSANVerify = skipClientSANVerify
tlsInfo.TrustedCAFile = clientTLSInfo.CertFile
rootCAs := x509.NewCertPool()
loaded, err := os.ReadFile(tlsInfo.CertFile)
require.NoErrorf(t, err, "unexpected missing certfile")
rootCAs.AppendCertsFromPEM(loaded)
clientCert, err := tls.LoadX509KeyPair(clientTLSInfo.CertFile, clientTLSInfo.KeyFile)
require.NoErrorf(t, err, "unable to create peer cert")
tlsConfig := &tls.Config{}
tlsConfig.InsecureSkipVerify = false
tlsConfig.Certificates = []tls.Certificate{clientCert}
tlsConfig.RootCAs = rootCAs
ln, err := NewListener("127.0.0.1:0", "https", tlsInfo)
require.NoErrorf(t, err, "unexpected NewListener error")
defer ln.Close()
tr := &http.Transport{TLSClientConfig: tlsConfig}
cli := &http.Client{Transport: tr}
chClientErr := make(chan error, 1)
go func() {
_, err := cli.Get("https://" + ln.Addr().String())
chClientErr <- err
}()
chAcceptErr := make(chan error, 1)
chAcceptConn := make(chan net.Conn, 1)
go func() {
conn, err := ln.Accept()
if err != nil {
chAcceptErr <- err
} else {
chAcceptConn <- conn
}
}()
select {
case <-chClientErr:
if acceptExpected {
t.Errorf("accepted for good client address: skipClientSANVerify=%t, goodClientHost=%t", skipClientSANVerify, goodClientHost)
}
case acceptErr := <-chAcceptErr:
t.Fatalf("unexpected Accept error: %v", acceptErr)
case conn := <-chAcceptConn:
defer conn.Close()
if _, ok := conn.(*tls.Conn); !ok {
t.Errorf("failed to accept *tls.Conn")
}
if !acceptExpected {
t.Errorf("accepted for bad client address: skipClientSANVerify=%t, goodClientHost=%t", skipClientSANVerify, goodClientHost)
}
}
}
func TestNewListenerTLSEmptyInfo(t *testing.T) {
_, err := NewListener("127.0.0.1:0", "https", nil)
if err == nil {
t.Errorf("err = nil, want not presented error")
}
}
func TestNewTransportTLSInfo(t *testing.T) {
tlsinfo, err := createSelfCert(t)
require.NoErrorf(t, err, "unable to create cert")
tests := []TLSInfo{
{},
{
CertFile: tlsinfo.CertFile,
KeyFile: tlsinfo.KeyFile,
},
{
CertFile: tlsinfo.CertFile,
KeyFile: tlsinfo.KeyFile,
TrustedCAFile: tlsinfo.TrustedCAFile,
},
{
TrustedCAFile: tlsinfo.TrustedCAFile,
},
}
for i, tt := range tests {
tt.parseFunc = fakeCertificateParserFunc(nil)
trans, err := NewTransport(tt, time.Second)
require.NoErrorf(t, err, "Received unexpected error from NewTransport")
require.NotNilf(t, trans.TLSClientConfig, "#%d: want non-nil TLSClientConfig", i)
}
}
func TestTLSInfoNonexist(t *testing.T) {
tlsInfo := TLSInfo{CertFile: "@badname", KeyFile: "@badname"}
_, err := tlsInfo.ServerConfig()
werr := &os.PathError{
Op: "open",
Path: "@badname",
Err: errors.New("no such file or directory"),
}
if err.Error() != werr.Error() {
t.Errorf("err = %v, want %v", err, werr)
}
}
func TestTLSInfoEmpty(t *testing.T) {
tests := []struct {
info TLSInfo
want bool
}{
{TLSInfo{}, true},
{TLSInfo{TrustedCAFile: "baz"}, true},
{TLSInfo{CertFile: "foo"}, false},
{TLSInfo{KeyFile: "bar"}, false},
{TLSInfo{CertFile: "foo", KeyFile: "bar"}, false},
{TLSInfo{CertFile: "foo", TrustedCAFile: "baz"}, false},
{TLSInfo{KeyFile: "bar", TrustedCAFile: "baz"}, false},
{TLSInfo{CertFile: "foo", KeyFile: "bar", TrustedCAFile: "baz"}, false},
}
for i, tt := range tests {
got := tt.info.Empty()
if tt.want != got {
t.Errorf("#%d: result of Empty() incorrect: want=%t got=%t", i, tt.want, got)
}
}
}
func TestTLSInfoMissingFields(t *testing.T) {
tlsinfo, err := createSelfCert(t)
require.NoErrorf(t, err, "unable to create cert")
tests := []TLSInfo{
{CertFile: tlsinfo.CertFile},
{KeyFile: tlsinfo.KeyFile},
{CertFile: tlsinfo.CertFile, TrustedCAFile: tlsinfo.TrustedCAFile},
{KeyFile: tlsinfo.KeyFile, TrustedCAFile: tlsinfo.TrustedCAFile},
}
for i, info := range tests {
if _, err = info.ServerConfig(); err == nil {
t.Errorf("#%d: expected non-nil error from ServerConfig()", i)
}
if _, err = info.ClientConfig(); err == nil {
t.Errorf("#%d: expected non-nil error from ClientConfig()", i)
}
}
}
func TestTLSInfoParseFuncError(t *testing.T) {
tlsinfo, err := createSelfCert(t)
require.NoErrorf(t, err, "unable to create cert")
tests := []struct {
info TLSInfo
}{
{
info: *tlsinfo,
},
{
info: TLSInfo{CertFile: "", KeyFile: "", TrustedCAFile: tlsinfo.CertFile, EmptyCN: true},
},
}
for i, tt := range tests {
tt.info.parseFunc = fakeCertificateParserFunc(errors.New("fake"))
if _, err = tt.info.ServerConfig(); err == nil {
t.Errorf("#%d: expected non-nil error from ServerConfig()", i)
}
if _, err = tt.info.ClientConfig(); err == nil {
t.Errorf("#%d: expected non-nil error from ClientConfig()", i)
}
}
}
func TestTLSInfoConfigFuncs(t *testing.T) {
ln := zaptest.NewLogger(t)
tlsinfo, err := createSelfCert(t)
require.NoErrorf(t, err, "unable to create cert")
tests := []struct {
info TLSInfo
clientAuth tls.ClientAuthType
wantCAs bool
}{
{
info: TLSInfo{CertFile: tlsinfo.CertFile, KeyFile: tlsinfo.KeyFile, Logger: ln},
clientAuth: tls.NoClientCert,
wantCAs: false,
},
{
info: TLSInfo{CertFile: tlsinfo.CertFile, KeyFile: tlsinfo.KeyFile, TrustedCAFile: tlsinfo.CertFile, Logger: ln},
clientAuth: tls.RequireAndVerifyClientCert,
wantCAs: true,
},
}
for i, tt := range tests {
tt.info.parseFunc = fakeCertificateParserFunc(nil)
sCfg, err := tt.info.ServerConfig()
if err != nil {
t.Errorf("#%d: expected nil error from ServerConfig(), got non-nil: %v", i, err)
}
if tt.wantCAs != (sCfg.ClientCAs != nil) {
t.Errorf("#%d: wantCAs=%t but ClientCAs=%v", i, tt.wantCAs, sCfg.ClientCAs)
}
cCfg, err := tt.info.ClientConfig()
if err != nil {
t.Errorf("#%d: expected nil error from ClientConfig(), got non-nil: %v", i, err)
}
if tt.wantCAs != (cCfg.RootCAs != nil) {
t.Errorf("#%d: wantCAs=%t but RootCAs=%v", i, tt.wantCAs, sCfg.RootCAs)
}
}
}
func TestNewListenerUnixSocket(t *testing.T) {
l, err := NewListener("testsocket", "unix", nil)
if err != nil {
t.Errorf("error listening on unix socket (%v)", err)
}
l.Close()
}
// TestNewListenerTLSInfoSelfCert tests that a new certificate accepts connections.
func TestNewListenerTLSInfoSelfCert(t *testing.T) {
tmpdir := t.TempDir()
tlsinfo, err := SelfCert(zaptest.NewLogger(t), tmpdir, []string{"127.0.0.1"}, 1)
require.NoError(t, err)
require.Falsef(t, tlsinfo.Empty(), "tlsinfo should have certs (%+v)", tlsinfo)
testNewListenerTLSInfoAccept(t, tlsinfo)
assert.Panicsf(t, func() {
SelfCert(nil, tmpdir, []string{"127.0.0.1"}, 1)
}, "expected panic with nil log")
}
func TestIsClosedConnError(t *testing.T) {
l, err := NewListener("testsocket", "unix", nil)
if err != nil {
t.Errorf("error listening on unix socket (%v)", err)
}
l.Close()
_, err = l.Accept()
require.Truef(t, IsClosedConnError(err), "expect true, got false (%v)", err)
}
func TestSocktOptsEmpty(t *testing.T) {
tests := []struct {
sopts SocketOpts
want bool
}{
{SocketOpts{}, true},
{SocketOpts{ReuseAddress: true, ReusePort: false}, false},
{SocketOpts{ReusePort: true}, false},
}
for i, tt := range tests {
got := tt.sopts.Empty()
if tt.want != got {
t.Errorf("#%d: result of Empty() incorrect: want=%t got=%t", i, tt.want, got)
}
}
}
// TestNewListenerWithACRLFile tests when a revocation list is present.
func TestNewListenerWithACRLFile(t *testing.T) {
clientTLSInfo, err := createSelfCertEx(t, "127.0.0.1", x509.ExtKeyUsageClientAuth)
require.NoErrorf(t, err, "unable to create client cert")
loadFileAsPEM := func(fileName string) []byte {
loaded, readErr := os.ReadFile(fileName)
require.NoErrorf(t, readErr, "unable to read file %q", fileName)
block, _ := pem.Decode(loaded)
return block.Bytes
}
clientCert, err := x509.ParseCertificate(loadFileAsPEM(clientTLSInfo.CertFile))
require.NoErrorf(t, err, "unable to parse client cert")
tests := map[string]struct {
expectHandshakeError bool
revokedCertificateEntries []x509.RevocationListEntry
revocationListContents []byte
}{
"empty revocation list": {
expectHandshakeError: false,
},
"client cert is revoked": {
expectHandshakeError: true,
revokedCertificateEntries: []x509.RevocationListEntry{
{
SerialNumber: clientCert.SerialNumber,
RevocationTime: time.Now(),
},
},
},
"invalid CRL file content": {
expectHandshakeError: true,
revocationListContents: []byte("@invalidcontent"),
},
}
for testName, test := range tests {
t.Run(testName, func(t *testing.T) {
tmpdir := t.TempDir()
tlsInfo, err := createSelfCert(t)
require.NoErrorf(t, err, "unable to create server cert")
tlsInfo.TrustedCAFile = clientTLSInfo.CertFile
tlsInfo.CRLFile = filepath.Join(tmpdir, "revoked.r0")
cert, err := x509.ParseCertificate(loadFileAsPEM(tlsInfo.CertFile))
require.NoErrorf(t, err, "unable to decode server cert")
key, err := x509.ParseECPrivateKey(loadFileAsPEM(tlsInfo.KeyFile))
require.NoErrorf(t, err, "unable to parse server key")
revocationListContents := test.revocationListContents
if len(revocationListContents) == 0 {
tmpl := &x509.RevocationList{
RevokedCertificateEntries: test.revokedCertificateEntries,
ThisUpdate: time.Now(),
NextUpdate: time.Now().Add(time.Hour),
Number: big.NewInt(1),
}
revocationListContents, err = x509.CreateRevocationList(rand.Reader, tmpl, cert, key)
require.NoErrorf(t, err, "unable to create revocation list")
}
err = os.WriteFile(tlsInfo.CRLFile, revocationListContents, 0o600)
require.NoErrorf(t, err, "unable to write revocation list")
chHandshakeFailure := make(chan error, 1)
tlsInfo.HandshakeFailure = func(_ *tls.Conn, err error) {
if err != nil {
chHandshakeFailure <- err
}
}
rootCAs := x509.NewCertPool()
rootCAs.AddCert(cert)
clientCert, err := tls.LoadX509KeyPair(clientTLSInfo.CertFile, clientTLSInfo.KeyFile)
require.NoErrorf(t, err, "unable to create peer cert")
ln, err := NewListener("127.0.0.1:0", "https", tlsInfo)
require.NoErrorf(t, err, "unable to start listener")
tlsConfig := &tls.Config{}
tlsConfig.InsecureSkipVerify = false
tlsConfig.Certificates = []tls.Certificate{clientCert}
tlsConfig.RootCAs = rootCAs
tr := &http.Transport{TLSClientConfig: tlsConfig}
cli := &http.Client{Transport: tr, Timeout: 5 * time.Second}
var wg sync.WaitGroup
wg.Add(2)
go func() {
defer wg.Done()
if _, gerr := cli.Get("https://" + ln.Addr().String()); gerr != nil {
t.Logf("http GET failed: %v", gerr)
}
}()
chAcceptConn := make(chan net.Conn, 1)
go func() {
defer wg.Done()
conn, err := ln.Accept()
if err == nil {
chAcceptConn <- conn
}
}()
timer := time.NewTimer(5 * time.Second)
defer func() {
if !timer.Stop() {
<-timer.C
}
}()
select {
case err := <-chHandshakeFailure:
if !test.expectHandshakeError {
t.Errorf("expecting no handshake error, got: %v", err)
}
case conn := <-chAcceptConn:
if test.expectHandshakeError {
t.Errorf("expecting handshake error, got nothing")
}
conn.Close()
case <-timer.C:
t.Error("timed out waiting for closed connection or handshake error")
}
ln.Close()
wg.Wait()
})
}
}
================================================
FILE: client/pkg/transport/listener_tls.go
================================================
// Copyright 2017 The etcd Authors
//
// 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 transport
import (
"context"
"crypto/tls"
"crypto/x509"
"fmt"
"net"
"os"
"strings"
"sync"
)
// tlsListener overrides a TLS listener so it will reject client
// certificates with insufficient SAN credentials or CRL revoked
// certificates.
type tlsListener struct {
net.Listener
connc chan net.Conn
donec chan struct{}
err error
handshakeFailure func(*tls.Conn, error)
check tlsCheckFunc
}
type tlsCheckFunc func(context.Context, *tls.Conn) error
// NewTLSListener handshakes TLS connections and performs optional CRL checking.
func NewTLSListener(l net.Listener, tlsinfo *TLSInfo) (net.Listener, error) {
check := func(context.Context, *tls.Conn) error { return nil }
return newTLSListener(l, tlsinfo, check)
}
func newTLSListener(l net.Listener, tlsinfo *TLSInfo, check tlsCheckFunc) (net.Listener, error) {
if tlsinfo == nil || tlsinfo.Empty() {
l.Close()
return nil, fmt.Errorf("cannot listen on TLS for %s: KeyFile and CertFile are not presented", l.Addr().String())
}
tlscfg, err := tlsinfo.ServerConfig()
if err != nil {
return nil, err
}
hf := tlsinfo.HandshakeFailure
if hf == nil {
hf = func(*tls.Conn, error) {}
}
if len(tlsinfo.CRLFile) > 0 {
prevCheck := check
check = func(ctx context.Context, tlsConn *tls.Conn) error {
if err := prevCheck(ctx, tlsConn); err != nil {
return err
}
st := tlsConn.ConnectionState()
if certs := st.PeerCertificates; len(certs) > 0 {
return checkCRL(tlsinfo.CRLFile, certs)
}
return nil
}
}
tlsl := &tlsListener{
Listener: tls.NewListener(l, tlscfg),
connc: make(chan net.Conn),
donec: make(chan struct{}),
handshakeFailure: hf,
check: check,
}
go tlsl.acceptLoop()
return tlsl, nil
}
func (l *tlsListener) Accept() (net.Conn, error) {
select {
case conn := <-l.connc:
return conn, nil
case <-l.donec:
return nil, l.err
}
}
func checkSAN(ctx context.Context, tlsConn *tls.Conn) error {
st := tlsConn.ConnectionState()
if certs := st.PeerCertificates; len(certs) > 0 {
addr := tlsConn.RemoteAddr().String()
return checkCertSAN(ctx, certs[0], addr)
}
return nil
}
// acceptLoop launches each TLS handshake in a separate goroutine
// to prevent a hanging TLS connection from blocking other connections.
func (l *tlsListener) acceptLoop() {
var wg sync.WaitGroup
var pendingMu sync.Mutex
pending := make(map[net.Conn]struct{})
ctx, cancel := context.WithCancel(context.Background())
defer func() {
cancel()
pendingMu.Lock()
for c := range pending {
c.Close()
}
pendingMu.Unlock()
wg.Wait()
close(l.donec)
}()
for {
conn, err := l.Listener.Accept()
if err != nil {
l.err = err
return
}
pendingMu.Lock()
pending[conn] = struct{}{}
pendingMu.Unlock()
wg.Add(1)
go func() {
defer func() {
if conn != nil {
conn.Close()
}
wg.Done()
}()
tlsConn := conn.(*tls.Conn)
herr := tlsConn.Handshake()
pendingMu.Lock()
delete(pending, conn)
pendingMu.Unlock()
if herr != nil {
l.handshakeFailure(tlsConn, herr)
return
}
if err := l.check(ctx, tlsConn); err != nil {
l.handshakeFailure(tlsConn, err)
return
}
select {
case l.connc <- tlsConn:
conn = nil
case <-ctx.Done():
}
}()
}
}
func checkCRL(crlPath string, cert []*x509.Certificate) error {
// TODO: cache
crlBytes, err := os.ReadFile(crlPath)
if err != nil {
return err
}
certList, err := x509.ParseRevocationList(crlBytes)
if err != nil {
return err
}
revokedSerials := make(map[string]struct{})
for _, rc := range certList.RevokedCertificateEntries {
revokedSerials[string(rc.SerialNumber.Bytes())] = struct{}{}
}
for _, c := range cert {
serial := string(c.SerialNumber.Bytes())
if _, ok := revokedSerials[serial]; ok {
return fmt.Errorf("transport: certificate serial %x revoked", serial)
}
}
return nil
}
func checkCertSAN(ctx context.Context, cert *x509.Certificate, remoteAddr string) error {
if len(cert.IPAddresses) == 0 && len(cert.DNSNames) == 0 {
return nil
}
h, _, herr := net.SplitHostPort(remoteAddr)
if herr != nil {
return herr
}
if len(cert.IPAddresses) > 0 {
cerr := cert.VerifyHostname(h)
if cerr == nil {
return nil
}
if len(cert.DNSNames) == 0 {
return cerr
}
}
if len(cert.DNSNames) > 0 {
ok, err := isHostInDNS(ctx, h, cert.DNSNames)
if ok {
return nil
}
errStr := ""
if err != nil {
errStr = " (" + err.Error() + ")"
}
return fmt.Errorf("tls: %q does not match any of DNSNames %q"+errStr, h, cert.DNSNames)
}
return nil
}
func isHostInDNS(ctx context.Context, host string, dnsNames []string) (ok bool, err error) {
// reverse lookup
var names []string
var wildcards []string
for _, dns := range dnsNames {
if strings.HasPrefix(dns, "*.") {
wildcards = append(wildcards, dns[1:])
} else {
names = append(names, dns)
}
}
lnames, lerr := net.DefaultResolver.LookupAddr(ctx, host)
for _, name := range lnames {
// strip trailing '.' from PTR record
if name[len(name)-1] == '.' {
name = name[:len(name)-1]
}
for _, wc := range wildcards {
if strings.HasSuffix(name, wc) {
return true, nil
}
}
for _, n := range names {
if n == name {
return true, nil
}
}
}
err = lerr
// forward lookup
for _, dns := range names {
addrs, lerr := net.DefaultResolver.LookupHost(ctx, dns)
if lerr != nil {
err = lerr
continue
}
for _, addr := range addrs {
if addr == host {
return true, nil
}
}
}
return false, err
}
func (l *tlsListener) Close() error {
err := l.Listener.Close()
<-l.donec
return err
}
================================================
FILE: client/pkg/transport/sockopt.go
================================================
// Copyright 2021 The etcd Authors
//
// 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 transport
import (
"syscall"
)
type Controls []func(network, addr string, conn syscall.RawConn) error
func (ctls Controls) Control(network, addr string, conn syscall.RawConn) error {
for _, s := range ctls {
if err := s(network, addr, conn); err != nil {
return err
}
}
return nil
}
type SocketOpts struct {
// ReusePort enables socket option SO_REUSEPORT [1] which allows rebind of
// a port already in use. User should keep in mind that flock can fail
// in which case lock on data file could result in unexpected
// condition. User should take caution to protect against lock race.
// [1] https://man7.org/linux/man-pages/man7/socket.7.html
ReusePort bool `json:"reuse-port"`
// ReuseAddress enables a socket option SO_REUSEADDR which allows
// binding to an address in `TIME_WAIT` state. Useful to improve MTTR
// in cases where etcd slow to restart due to excessive `TIME_WAIT`.
// [1] https://man7.org/linux/man-pages/man7/socket.7.html
ReuseAddress bool `json:"reuse-address"`
}
func getControls(sopts *SocketOpts) Controls {
ctls := Controls{}
if sopts.ReuseAddress {
ctls = append(ctls, setReuseAddress)
}
if sopts.ReusePort {
ctls = append(ctls, setReusePort)
}
return ctls
}
func (sopts *SocketOpts) Empty() bool {
return !sopts.ReuseAddress && !sopts.ReusePort
}
================================================
FILE: client/pkg/transport/sockopt_solaris.go
================================================
// Copyright 2021 The etcd Authors
//
// 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.
//go:build solaris
package transport
import (
"errors"
"syscall"
"golang.org/x/sys/unix"
)
func setReusePort(network, address string, c syscall.RawConn) error {
return errors.New("port reuse is not supported on Solaris")
}
func setReuseAddress(network, address string, conn syscall.RawConn) error {
return conn.Control(func(fd uintptr) {
syscall.SetsockoptInt(int(fd), syscall.SOL_SOCKET, unix.SO_REUSEADDR, 1)
})
}
================================================
FILE: client/pkg/transport/sockopt_unix.go
================================================
// Copyright 2021 The etcd Authors
//
// 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.
//go:build !windows && !solaris && !wasm && !js
package transport
import (
"syscall"
"golang.org/x/sys/unix"
)
func setReusePort(network, address string, conn syscall.RawConn) error {
return conn.Control(func(fd uintptr) {
syscall.SetsockoptInt(int(fd), syscall.SOL_SOCKET, unix.SO_REUSEPORT, 1)
})
}
func setReuseAddress(network, address string, conn syscall.RawConn) error {
return conn.Control(func(fd uintptr) {
syscall.SetsockoptInt(int(fd), syscall.SOL_SOCKET, unix.SO_REUSEADDR, 1)
})
}
================================================
FILE: client/pkg/transport/sockopt_wasm.go
================================================
// Copyright 2023 The etcd Authors
//
// 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.
//go:build wasm || js
package transport
import (
"errors"
"syscall"
)
func setReusePort(network, address string, c syscall.RawConn) error {
return errors.New("port reuse is not supported on WASM")
}
func setReuseAddress(network, addr string, conn syscall.RawConn) error {
return errors.New("address reuse is not supported on WASM")
}
================================================
FILE: client/pkg/transport/sockopt_windows.go
================================================
// Copyright 2021 The etcd Authors
//
// 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.
//go:build windows
package transport
import (
"errors"
"syscall"
)
func setReusePort(network, address string, c syscall.RawConn) error {
return errors.New("port reuse is not supported on Windows")
}
// Windows supports SO_REUSEADDR, but it may cause undefined behavior, as
// there is no protection against port hijacking.
func setReuseAddress(network, addr string, conn syscall.RawConn) error {
return errors.New("address reuse is not supported on Windows")
}
================================================
FILE: client/pkg/transport/timeout_conn.go
================================================
// Copyright 2015 The etcd Authors
//
// 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 transport
import (
"net"
"time"
)
type timeoutConn struct {
net.Conn
writeTimeout time.Duration
readTimeout time.Duration
}
func (c timeoutConn) Write(b []byte) (n int, err error) {
if c.writeTimeout > 0 {
if err := c.SetWriteDeadline(time.Now().Add(c.writeTimeout)); err != nil {
return 0, err
}
}
return c.Conn.Write(b)
}
func (c timeoutConn) Read(b []byte) (n int, err error) {
if c.readTimeout > 0 {
if err := c.SetReadDeadline(time.Now().Add(c.readTimeout)); err != nil {
return 0, err
}
}
return c.Conn.Read(b)
}
================================================
FILE: client/pkg/transport/timeout_dialer.go
================================================
// Copyright 2015 The etcd Authors
//
// 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 transport
import (
"net"
"time"
)
type rwTimeoutDialer struct {
wtimeoutd time.Duration
rdtimeoutd time.Duration
net.Dialer
}
func (d *rwTimeoutDialer) Dial(network, address string) (net.Conn, error) {
conn, err := d.Dialer.Dial(network, address)
tconn := &timeoutConn{
readTimeout: d.rdtimeoutd,
writeTimeout: d.wtimeoutd,
Conn: conn,
}
return tconn, err
}
================================================
FILE: client/pkg/transport/timeout_dialer_test.go
================================================
// Copyright 2015 The etcd Authors
//
// 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 transport
import (
"errors"
"net"
"testing"
"time"
"github.com/stretchr/testify/require"
)
func TestReadWriteTimeoutDialer(t *testing.T) {
stop := make(chan struct{})
ln, err := net.Listen("tcp", "127.0.0.1:0")
require.NoErrorf(t, err, "unexpected listen error")
defer func() {
stop <- struct{}{}
}()
ts := testBlockingServer{ln, 2, stop}
go ts.Start(t)
d := rwTimeoutDialer{
wtimeoutd: 10 * time.Millisecond,
rdtimeoutd: 10 * time.Millisecond,
}
conn, err := d.Dial("tcp", ln.Addr().String())
require.NoErrorf(t, err, "unexpected dial error")
defer conn.Close()
// fill the socket buffer
data := make([]byte, 5*1024*1024)
done := make(chan struct{}, 1)
go func() {
_, err = conn.Write(data)
done <- struct{}{}
}()
select {
case <-done:
// Wait 5s more than timeout to avoid delay in low-end systems;
// the slack was 1s extra, but that wasn't enough for CI.
case <-time.After(d.wtimeoutd*10 + 5*time.Second):
t.Fatal("wait timeout")
}
var operr *net.OpError
if !errors.As(err, &operr) || operr.Op != "write" || !operr.Timeout() {
t.Errorf("err = %v, want write i/o timeout error", err)
}
conn, err = d.Dial("tcp", ln.Addr().String())
require.NoErrorf(t, err, "unexpected dial error")
defer conn.Close()
buf := make([]byte, 10)
go func() {
_, err = conn.Read(buf)
done <- struct{}{}
}()
select {
case <-done:
case <-time.After(d.rdtimeoutd * 10):
t.Fatal("wait timeout")
}
if !errors.As(err, &operr) || operr.Op != "read" || !operr.Timeout() {
t.Errorf("err = %v, want read i/o timeout error", err)
}
}
type testBlockingServer struct {
ln net.Listener
n int
stop chan struct{}
}
func (ts *testBlockingServer) Start(t *testing.T) {
t.Helper()
for i := 0; i < ts.n; i++ {
conn, err := ts.ln.Accept()
if err != nil {
t.Error(err)
}
defer conn.Close()
}
<-ts.stop
}
================================================
FILE: client/pkg/transport/timeout_listener.go
================================================
// Copyright 2015 The etcd Authors
//
// 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 transport
import (
"net"
"time"
)
// NewTimeoutListener returns a listener that listens on the given address.
// If read/write on the accepted connection blocks longer than its time limit,
// it will return timeout error.
func NewTimeoutListener(addr string, scheme string, tlsinfo *TLSInfo, readTimeout, writeTimeout time.Duration) (net.Listener, error) {
return newListener(addr, scheme, WithTimeout(readTimeout, writeTimeout), WithTLSInfo(tlsinfo))
}
type rwTimeoutListener struct {
net.Listener
writeTimeout time.Duration
readTimeout time.Duration
}
func (rwln *rwTimeoutListener) Accept() (net.Conn, error) {
c, err := rwln.Listener.Accept()
if err != nil {
return nil, err
}
return timeoutConn{
Conn: c,
writeTimeout: rwln.writeTimeout,
readTimeout: rwln.readTimeout,
}, nil
}
================================================
FILE: client/pkg/transport/timeout_listener_test.go
================================================
// Copyright 2015 The etcd Authors
//
// 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 transport
import (
"errors"
"net"
"testing"
"time"
"github.com/stretchr/testify/require"
)
// TestNewTimeoutListener tests that NewTimeoutListener returns a
// rwTimeoutListener struct with timeouts set.
func TestNewTimeoutListener(t *testing.T) {
l, err := NewTimeoutListener("127.0.0.1:0", "http", nil, time.Hour, time.Hour)
require.NoErrorf(t, err, "unexpected NewTimeoutListener error")
defer l.Close()
tln := l.(*rwTimeoutListener)
if tln.readTimeout != time.Hour {
t.Errorf("read timeout = %s, want %s", tln.readTimeout, time.Hour)
}
if tln.writeTimeout != time.Hour {
t.Errorf("write timeout = %s, want %s", tln.writeTimeout, time.Hour)
}
}
func TestWriteReadTimeoutListener(t *testing.T) {
ln, err := net.Listen("tcp", "127.0.0.1:0")
require.NoErrorf(t, err, "unexpected listen error")
wln := rwTimeoutListener{
Listener: ln,
writeTimeout: 10 * time.Millisecond,
readTimeout: 10 * time.Millisecond,
}
blocker := func(stopCh <-chan struct{}) {
conn, derr := net.Dial("tcp", ln.Addr().String())
if derr != nil {
t.Errorf("unexpected dail error: %v", derr)
}
defer conn.Close()
// block the receiver until the writer timeout
<-stopCh
}
writerStopCh := make(chan struct{}, 1)
go blocker(writerStopCh)
conn, err := wln.Accept()
if err != nil {
writerStopCh <- struct{}{}
t.Fatalf("unexpected accept error: %v", err)
}
defer conn.Close()
// fill the socket buffer
data := make([]byte, 5*1024*1024)
done := make(chan struct{}, 1)
go func() {
_, err = conn.Write(data)
done <- struct{}{}
}()
select {
case <-done:
// It waits 1s more to avoid delay in low-end system.
case <-time.After(wln.writeTimeout*10 + time.Second):
writerStopCh <- struct{}{}
t.Fatal("wait timeout")
}
var operr *net.OpError
if !errors.As(err, &operr) || operr.Op != "write" || !operr.Timeout() {
t.Errorf("err = %v, want write i/o timeout error", err)
}
writerStopCh <- struct{}{}
readerStopCh := make(chan struct{}, 1)
go blocker(readerStopCh)
conn, err = wln.Accept()
if err != nil {
readerStopCh <- struct{}{}
t.Fatalf("unexpected accept error: %v", err)
}
buf := make([]byte, 10)
go func() {
_, err = conn.Read(buf)
done <- struct{}{}
}()
select {
case <-done:
case <-time.After(wln.readTimeout * 10):
readerStopCh <- struct{}{}
t.Fatal("wait timeout")
}
if !errors.As(err, &operr) || operr.Op != "read" || !operr.Timeout() {
t.Errorf("err = %v, want read i/o timeout error", err)
}
readerStopCh <- struct{}{}
}
================================================
FILE: client/pkg/transport/timeout_transport.go
================================================
// Copyright 2015 The etcd Authors
//
// 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 transport
import (
"net"
"net/http"
"time"
)
// NewTimeoutTransport returns a transport created using the given TLS info.
// If read/write on the created connection blocks longer than its time limit,
// it will return timeout error.
// If read/write timeout is set, transport will not be able to reuse connection.
func NewTimeoutTransport(info TLSInfo, dialtimeoutd, rdtimeoutd, wtimeoutd time.Duration) (*http.Transport, error) {
tr, err := NewTransport(info, dialtimeoutd)
if err != nil {
return nil, err
}
if rdtimeoutd != 0 || wtimeoutd != 0 {
// the timed out connection will timeout soon after it is idle.
// it should not be put back to http transport as an idle connection for future usage.
tr.MaxIdleConnsPerHost = -1
} else {
// allow more idle connections between peers to avoid unnecessary port allocation.
tr.MaxIdleConnsPerHost = 1024
}
tr.Dial = (&rwTimeoutDialer{ //nolint:staticcheck // TODO: remove for a supported version
Dialer: net.Dialer{
Timeout: dialtimeoutd,
KeepAlive: 30 * time.Second,
},
rdtimeoutd: rdtimeoutd,
wtimeoutd: wtimeoutd,
}).Dial
return tr, nil
}
================================================
FILE: client/pkg/transport/timeout_transport_test.go
================================================
// Copyright 2015 The etcd Authors
//
// 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 transport
import (
"bytes"
"io"
"net/http"
"net/http/httptest"
"testing"
"time"
"github.com/stretchr/testify/require"
)
// TestNewTimeoutTransport tests that NewTimeoutTransport returns a transport
// that can dial out timeout connections.
func TestNewTimeoutTransport(t *testing.T) {
tr, err := NewTimeoutTransport(TLSInfo{}, time.Hour, time.Hour, time.Hour)
require.NoError(t, err)
remoteAddr := func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte(r.RemoteAddr))
}
srv := httptest.NewServer(http.HandlerFunc(remoteAddr))
defer srv.Close()
conn, err := tr.Dial("tcp", srv.Listener.Addr().String()) //nolint:staticcheck // TODO: remove for a supported version
require.NoError(t, err)
defer conn.Close()
tconn, ok := conn.(*timeoutConn)
require.Truef(t, ok, "failed to dial out *timeoutConn")
if tconn.readTimeout != time.Hour {
t.Errorf("read timeout = %s, want %s", tconn.readTimeout, time.Hour)
}
if tconn.writeTimeout != time.Hour {
t.Errorf("write timeout = %s, want %s", tconn.writeTimeout, time.Hour)
}
// ensure not reuse timeout connection
req, err := http.NewRequest(http.MethodGet, srv.URL, nil)
require.NoError(t, err)
resp, err := tr.RoundTrip(req)
require.NoError(t, err)
addr0, err := io.ReadAll(resp.Body)
resp.Body.Close()
require.NoError(t, err)
resp, err = tr.RoundTrip(req)
require.NoError(t, err)
addr1, err := io.ReadAll(resp.Body)
resp.Body.Close()
require.NoError(t, err)
if bytes.Equal(addr0, addr1) {
t.Errorf("addr0 = %s addr1= %s, want not equal", addr0, addr1)
}
}
================================================
FILE: client/pkg/transport/tls.go
================================================
// Copyright 2016 The etcd Authors
//
// 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 transport
import (
"context"
"errors"
"fmt"
"strings"
"time"
)
// ValidateSecureEndpoints scans the given endpoints against tls info, returning only those
// endpoints that could be validated as secure.
func ValidateSecureEndpoints(tlsInfo TLSInfo, eps []string) ([]string, error) {
t, err := NewTransport(tlsInfo, 5*time.Second)
if err != nil {
return nil, err
}
defer t.CloseIdleConnections()
var errs []string
var endpoints []string
for _, ep := range eps {
if !strings.HasPrefix(ep, "https://") {
errs = append(errs, fmt.Sprintf("%q is insecure", ep))
continue
}
conn, cerr := t.DialContext(context.Background(), "tcp", ep[len("https://"):])
if cerr != nil {
errs = append(errs, fmt.Sprintf("%q failed to dial (%v)", ep, cerr))
continue
}
conn.Close()
endpoints = append(endpoints, ep)
}
if len(errs) != 0 {
err = errors.New(strings.Join(errs, ","))
}
return endpoints, err
}
================================================
FILE: client/pkg/transport/tls_test.go
================================================
// Copyright 2022 The etcd Authors
//
// 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 transport
import (
"net/http"
"net/http/httptest"
"reflect"
"testing"
"github.com/stretchr/testify/require"
)
func TestValidateSecureEndpoints(t *testing.T) {
tlsInfo, err := createSelfCert(t)
require.NoErrorf(t, err, "unable to create cert")
remoteAddr := func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte(r.RemoteAddr))
}
srv := httptest.NewServer(http.HandlerFunc(remoteAddr))
defer srv.Close()
tests := map[string]struct {
endPoints []string
expectedEndpoints []string
expectedErr bool
}{
"invalidEndPoints": {
endPoints: []string{
"invalid endpoint",
},
expectedEndpoints: nil,
expectedErr: true,
},
"insecureEndpoints": {
endPoints: []string{
"http://127.0.0.1:8000",
"http://" + srv.Listener.Addr().String(),
},
expectedEndpoints: nil,
expectedErr: true,
},
"secureEndPoints": {
endPoints: []string{
"https://" + srv.Listener.Addr().String(),
},
expectedEndpoints: []string{
"https://" + srv.Listener.Addr().String(),
},
expectedErr: false,
},
"mixEndPoints": {
endPoints: []string{
"https://" + srv.Listener.Addr().String(),
"http://" + srv.Listener.Addr().String(),
"invalid end points",
},
expectedEndpoints: []string{
"https://" + srv.Listener.Addr().String(),
},
expectedErr: true,
},
}
for name, test := range tests {
t.Run(name, func(t *testing.T) {
secureEps, err := ValidateSecureEndpoints(*tlsInfo, test.endPoints)
if test.expectedErr != (err != nil) {
t.Errorf("Unexpected error, got: %v, want: %v", err, test.expectedErr)
}
if !reflect.DeepEqual(test.expectedEndpoints, secureEps) {
t.Errorf("expected endpoints %v, got %v", test.expectedEndpoints, secureEps)
}
})
}
}
================================================
FILE: client/pkg/transport/transport.go
================================================
// Copyright 2016 The etcd Authors
//
// 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 transport
import (
"context"
"net"
"net/http"
"strings"
"time"
)
type unixTransport struct{ *http.Transport }
func NewTransport(info TLSInfo, dialtimeoutd time.Duration) (*http.Transport, error) {
cfg, err := info.ClientConfig()
if err != nil {
return nil, err
}
var ipAddr net.Addr
if info.LocalAddr != "" {
ipAddr, err = net.ResolveTCPAddr("tcp", info.LocalAddr+":0")
if err != nil {
return nil, err
}
}
t := &http.Transport{
Proxy: http.ProxyFromEnvironment,
DialContext: (&net.Dialer{
Timeout: dialtimeoutd,
LocalAddr: ipAddr,
// value taken from http.DefaultTransport
KeepAlive: 30 * time.Second,
}).DialContext,
// value taken from http.DefaultTransport
TLSHandshakeTimeout: 10 * time.Second,
TLSClientConfig: cfg,
}
dialer := &net.Dialer{
Timeout: dialtimeoutd,
KeepAlive: 30 * time.Second,
}
dialContext := func(ctx context.Context, net, addr string) (net.Conn, error) {
return dialer.DialContext(ctx, "unix", addr)
}
tu := &http.Transport{
Proxy: http.ProxyFromEnvironment,
DialContext: dialContext,
TLSHandshakeTimeout: 10 * time.Second,
TLSClientConfig: cfg,
// Cost of reopening connection on sockets is low, and they are mostly used in testing.
// Long living unix-transport connections were leading to 'leak' test flakes.
// Alternatively the returned Transport (t) should override CloseIdleConnections to
// forward it to 'tu' as well.
IdleConnTimeout: time.Microsecond,
}
ut := &unixTransport{tu}
t.RegisterProtocol("unix", ut)
t.RegisterProtocol("unixs", ut)
return t, nil
}
func (urt *unixTransport) RoundTrip(req *http.Request) (*http.Response, error) {
url := *req.URL
req.URL = &url
req.URL.Scheme = strings.Replace(req.URL.Scheme, "unix", "http", 1)
return urt.Transport.RoundTrip(req)
}
================================================
FILE: client/pkg/transport/transport_test.go
================================================
// Copyright 2018 The etcd Authors
//
// 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 transport
import (
"crypto/tls"
"net/http"
"strings"
"testing"
"time"
"github.com/stretchr/testify/require"
)
// TestNewTransportTLSInvalidCipherSuitesTLS12 expects a client with invalid
// cipher suites fail to handshake with the server.
func TestNewTransportTLSInvalidCipherSuitesTLS12(t *testing.T) {
tlsInfo, err := createSelfCert(t)
require.NoErrorf(t, err, "unable to create cert")
cipherSuites := []uint16{
tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,
tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305,
}
// make server and client have unmatched cipher suites
srvTLS, cliTLS := *tlsInfo, *tlsInfo
srvTLS.CipherSuites, cliTLS.CipherSuites = cipherSuites[:2], cipherSuites[2:]
ln, err := NewListener("127.0.0.1:0", "https", &srvTLS)
require.NoError(t, err)
defer ln.Close()
donec := make(chan struct{})
go func() {
ln.Accept()
donec <- struct{}{}
}()
go func() {
tr, err := NewTransport(cliTLS, 3*time.Second)
tr.TLSClientConfig.MaxVersion = tls.VersionTLS12
if err != nil {
t.Errorf("unexpected NewTransport error: %v", err)
}
cli := &http.Client{Transport: tr}
_, gerr := cli.Get("https://" + ln.Addr().String())
if gerr == nil || !strings.Contains(gerr.Error(), "tls: handshake failure") {
t.Error("expected client TLS handshake error")
}
ln.Close()
donec <- struct{}{}
}()
<-donec
<-donec
}
================================================
FILE: client/pkg/transport/unix_listener.go
================================================
// Copyright 2016 The etcd Authors
//
// 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 transport
import (
"net"
"os"
)
type unixListener struct{ net.Listener }
func NewUnixListener(addr string) (net.Listener, error) {
if err := os.Remove(addr); err != nil && !os.IsNotExist(err) {
return nil, err
}
l, err := net.Listen("unix", addr)
if err != nil {
return nil, err
}
return &unixListener{l}, nil
}
func (ul *unixListener) Close() error {
if err := os.Remove(ul.Addr().String()); err != nil && !os.IsNotExist(err) {
return err
}
return ul.Listener.Close()
}
================================================
FILE: client/pkg/types/doc.go
================================================
// Copyright 2015 The etcd Authors
//
// 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 types declares various data types and implements type-checking
// functions.
package types
================================================
FILE: client/pkg/types/id.go
================================================
// Copyright 2015 The etcd Authors
//
// 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 types
import (
"strconv"
"strings"
)
// ID represents a generic identifier which is canonically
// stored as a uint64 but is typically represented as a
// base-16 string for input/output
type ID uint64
func (i ID) String() string {
return strconv.FormatUint(uint64(i), 16)
}
// IDFromString attempts to create an ID from a base-16 string.
func IDFromString(s string) (ID, error) {
i, err := strconv.ParseUint(s, 16, 64)
return ID(i), err
}
// IDSlice implements the sort interface
type IDSlice []ID
func (p IDSlice) Len() int { return len(p) }
func (p IDSlice) Less(i, j int) bool { return uint64(p[i]) < uint64(p[j]) }
func (p IDSlice) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
func (p IDSlice) String() string {
var b strings.Builder
if p.Len() > 0 {
b.WriteString(p[0].String())
}
for i := 1; i < p.Len(); i++ {
b.WriteString(",")
b.WriteString(p[i].String())
}
return b.String()
}
================================================
FILE: client/pkg/types/id_test.go
================================================
// Copyright 2015 The etcd Authors
//
// 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 types
import (
"reflect"
"sort"
"testing"
"github.com/stretchr/testify/require"
)
func TestIDString(t *testing.T) {
tests := []struct {
input ID
want string
}{
{
input: 12,
want: "c",
},
{
input: 4918257920282737594,
want: "444129853c343bba",
},
}
for i, tt := range tests {
got := tt.input.String()
if tt.want != got {
t.Errorf("#%d: ID.String failure: want=%v, got=%v", i, tt.want, got)
}
}
}
func TestIDFromString(t *testing.T) {
tests := []struct {
input string
want ID
}{
{
input: "17",
want: 23,
},
{
input: "612840dae127353",
want: 437557308098245459,
},
}
for i, tt := range tests {
got, err := IDFromString(tt.input)
if err != nil {
t.Errorf("#%d: IDFromString failure: err=%v", i, err)
continue
}
if tt.want != got {
t.Errorf("#%d: IDFromString failure: want=%v, got=%v", i, tt.want, got)
}
}
}
func TestIDFromStringFail(t *testing.T) {
tests := []string{
"",
"XXX",
"612840dae127353612840dae127353",
}
for i, tt := range tests {
_, err := IDFromString(tt)
require.Errorf(t, err, "#%d: IDFromString expected error", i)
}
}
func TestIDSlice(t *testing.T) {
g := []ID{10, 500, 5, 1, 100, 25}
w := []ID{1, 5, 10, 25, 100, 500}
sort.Sort(IDSlice(g))
if !reflect.DeepEqual(g, w) {
t.Errorf("slice after sort = %#v, want %#v", g, w)
}
}
================================================
FILE: client/pkg/types/set.go
================================================
// Copyright 2015 The etcd Authors
//
// 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 types
import (
"reflect"
"sort"
"sync"
)
type Set interface {
Add(string)
Remove(string)
Contains(val ...string) bool
Equals(Set) bool
Length() int
Values() []string
Copy() Set
Sub(Set) Set
// ContainsAll returns whether the set contains all given values
// Deprecated: Use Contains instead.
ContainsAll(values []string) bool
}
type ThreadsafeSet interface {
Set
}
func NewUnsafeSet(values ...string) Set {
set := &unsafeSet{make(map[string]struct{})}
for _, v := range values {
set.Add(v)
}
return set
}
func NewThreadsafeSet(values ...string) ThreadsafeSet {
us := NewUnsafeSet(values...)
return &tsafeSet{us, sync.RWMutex{}}
}
var _ Set = (*unsafeSet)(nil)
type unsafeSet struct {
d map[string]struct{}
}
// Add adds a new value to the set (no-op if the value is already present)
func (us *unsafeSet) Add(value string) {
us.d[value] = struct{}{}
}
// Remove removes the given value from the set
func (us *unsafeSet) Remove(value string) {
delete(us.d, value)
}
// Contains returns whether the set contains the given value
func (us *unsafeSet) Contains(values ...string) (exists bool) {
for _, value := range values {
if _, exists := us.d[value]; !exists {
return false
}
}
return true
}
// ContainsAll returns whether the set contains all given values
// Deprecated: Use Contains instead.
func (us *unsafeSet) ContainsAll(values []string) bool {
for _, s := range values {
if !us.Contains(s) {
return false
}
}
return true
}
// Equals returns whether the contents of two sets are identical
func (us *unsafeSet) Equals(other Set) bool {
v1 := sort.StringSlice(us.Values())
v2 := sort.StringSlice(other.Values())
v1.Sort()
v2.Sort()
return reflect.DeepEqual(v1, v2)
}
// Length returns the number of elements in the set
func (us *unsafeSet) Length() int {
return len(us.d)
}
// Values returns the values of the Set in an unspecified order.
func (us *unsafeSet) Values() (values []string) {
values = make([]string, 0, len(us.d))
for val := range us.d {
values = append(values, val)
}
return values
}
// Copy creates a new Set containing the values of the first
func (us *unsafeSet) Copy() Set {
cp := NewUnsafeSet()
for val := range us.d {
cp.Add(val)
}
return cp
}
// Sub removes all elements in other from the set
func (us *unsafeSet) Sub(other Set) Set {
oValues := other.Values()
result := us.Copy().(*unsafeSet)
for _, val := range oValues {
if _, ok := result.d[val]; !ok {
continue
}
delete(result.d, val)
}
return result
}
var _ ThreadsafeSet = (*tsafeSet)(nil)
type tsafeSet struct {
us Set
m sync.RWMutex
}
func (ts *tsafeSet) Add(value string) {
ts.m.Lock()
defer ts.m.Unlock()
ts.us.Add(value)
}
func (ts *tsafeSet) Remove(value string) {
ts.m.Lock()
defer ts.m.Unlock()
ts.us.Remove(value)
}
func (ts *tsafeSet) Contains(values ...string) (exists bool) {
ts.m.RLock()
defer ts.m.RUnlock()
return ts.us.Contains(values...)
}
// ContainsAll returns whether the set contains all given values
// Deprecated: Use Contains instead.
func (ts *tsafeSet) ContainsAll(values []string) bool {
ts.m.RLock()
defer ts.m.RUnlock()
return ts.us.ContainsAll(values)
}
func (ts *tsafeSet) Equals(other Set) bool {
ts.m.RLock()
defer ts.m.RUnlock()
// If ts and other represent the same variable, avoid calling
// ts.us.Equals(other), to avoid double RLock bug
if _other, ok := other.(*tsafeSet); ok {
if _other == ts {
return true
}
}
return ts.us.Equals(other)
}
func (ts *tsafeSet) Length() int {
ts.m.RLock()
defer ts.m.RUnlock()
return ts.us.Length()
}
func (ts *tsafeSet) Values() (values []string) {
ts.m.RLock()
defer ts.m.RUnlock()
return ts.us.Values()
}
func (ts *tsafeSet) Copy() Set {
ts.m.RLock()
defer ts.m.RUnlock()
usResult := ts.us.Copy().(*unsafeSet)
return &tsafeSet{usResult, sync.RWMutex{}}
}
func (ts *tsafeSet) Sub(other Set) Set {
ts.m.RLock()
defer ts.m.RUnlock()
// If ts and other represent the same variable, avoid calling
// ts.us.Sub(other), to avoid double RLock bug
if _other, ok := other.(*tsafeSet); ok {
if _other == ts {
usResult := NewUnsafeSet()
return &tsafeSet{usResult, sync.RWMutex{}}
}
}
usResult := ts.us.Sub(other).(*unsafeSet)
return &tsafeSet{usResult, sync.RWMutex{}}
}
================================================
FILE: client/pkg/types/set_test.go
================================================
// Copyright 2015 The etcd Authors
//
// 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 types
import (
"reflect"
"sort"
"testing"
"github.com/stretchr/testify/require"
)
func TestUnsafeSet(t *testing.T) {
driveSetTests(t, NewUnsafeSet())
}
func TestThreadsafeSet(t *testing.T) {
driveSetTests(t, NewThreadsafeSet())
}
// Check that two slices contents are equal; order is irrelevant
func equal(a, b []string) bool {
as := sort.StringSlice(a)
bs := sort.StringSlice(b)
as.Sort()
bs.Sort()
return reflect.DeepEqual(as, bs)
}
func driveSetTests(t *testing.T, s Set) {
t.Helper()
// Verify operations on an empty set
values := s.Values()
require.Emptyf(t, values, "Expect values=%v got %v", []string{}, values)
l := s.Length()
require.Equalf(t, 0, l, "Expected length=0, got %d", l)
for _, v := range []string{"foo", "bar", "baz"} {
require.Falsef(t, s.Contains(v), "Expect s.Contains(%q) to be fale, got true", v)
}
// Add three items, ensure they show up
s.Add("foo")
s.Add("bar")
s.Add("baz")
eValues := []string{"foo", "bar", "baz"}
values = s.Values()
require.Truef(t, equal(values, eValues), "Expect values=%v got %v", eValues, values)
for _, v := range eValues {
require.Truef(t, s.Contains(v), "Expect s.Contains(%q) to be true, got false", v)
}
l = s.Length()
require.Equalf(t, 3, l, "Expected length=3, got %d", l)
// Add the same item a second time, ensuring it is not duplicated
s.Add("foo")
values = s.Values()
require.Truef(t, equal(values, eValues), "Expect values=%v got %v", eValues, values)
l = s.Length()
require.Equalf(t, 3, l, "Expected length=3, got %d", l)
// Remove all items, ensure they are gone
s.Remove("foo")
s.Remove("bar")
s.Remove("baz")
eValues = []string{}
values = s.Values()
require.Truef(t, equal(values, eValues), "Expect values=%v got %v", eValues, values)
l = s.Length()
require.Equalf(t, 0, l, "Expected length=0, got %d", l)
// Create new copies of the set, and ensure they are unlinked to the
// original Set by making modifications
s.Add("foo")
s.Add("bar")
cp1 := s.Copy()
cp2 := s.Copy()
s.Remove("foo")
cp3 := s.Copy()
cp1.Add("baz")
for i, tt := range []struct {
want []string
got []string
}{
{[]string{"bar"}, s.Values()},
{[]string{"foo", "bar", "baz"}, cp1.Values()},
{[]string{"foo", "bar"}, cp2.Values()},
{[]string{"bar"}, cp3.Values()},
} {
require.Truef(t, equal(tt.want, tt.got), "case %d: expect values=%v got %v", i, tt.want, tt.got)
}
for i, tt := range []struct {
want bool
got bool
}{
{true, s.Equals(cp3)},
{true, cp3.Equals(s)},
{false, s.Equals(cp2)},
{false, s.Equals(cp1)},
{false, cp1.Equals(s)},
{false, cp2.Equals(s)},
{false, cp2.Equals(cp1)},
} {
require.Equalf(t, tt.want, tt.got, "case %d: want %t, got %t", i, tt.want, tt.got)
}
// Subtract values from a Set, ensuring a new Set is created and
// the original Sets are unmodified
sub1 := cp1.Sub(s)
sub2 := cp2.Sub(cp1)
for i, tt := range []struct {
want []string
got []string
}{
{[]string{"foo", "bar", "baz"}, cp1.Values()},
{[]string{"foo", "bar"}, cp2.Values()},
{[]string{"bar"}, s.Values()},
{[]string{"foo", "baz"}, sub1.Values()},
{[]string{}, sub2.Values()},
} {
require.Truef(t, equal(tt.want, tt.got), "case %d: expect values=%v got %v", i, tt.want, tt.got)
}
}
func TestUnsafeSetContainsAll(t *testing.T) {
vals := []string{"foo", "bar", "baz"}
s := NewUnsafeSet(vals...)
tests := []struct {
strs []string
wcontain bool
}{
{[]string{}, true},
{vals[:1], true},
{vals[:2], true},
{vals, true},
{[]string{"cuz"}, false},
{[]string{vals[0], "cuz"}, false},
}
for i, tt := range tests {
if g := s.ContainsAll(tt.strs); g != tt.wcontain {
t.Errorf("#%d: ok = %v, want %v", i, g, tt.wcontain)
}
}
}
================================================
FILE: client/pkg/types/slice.go
================================================
// Copyright 2015 The etcd Authors
//
// 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 types
// Uint64Slice implements sort interface
type Uint64Slice []uint64
func (p Uint64Slice) Len() int { return len(p) }
func (p Uint64Slice) Less(i, j int) bool { return p[i] < p[j] }
func (p Uint64Slice) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
================================================
FILE: client/pkg/types/slice_test.go
================================================
// Copyright 2015 The etcd Authors
//
// 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 types
import (
"reflect"
"sort"
"testing"
)
func TestUint64Slice(t *testing.T) {
g := Uint64Slice{10, 500, 5, 1, 100, 25}
w := Uint64Slice{1, 5, 10, 25, 100, 500}
sort.Sort(g)
if !reflect.DeepEqual(g, w) {
t.Errorf("slice after sort = %#v, want %#v", g, w)
}
}
================================================
FILE: client/pkg/types/urls.go
================================================
// Copyright 2015 The etcd Authors
//
// 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 types
import (
"errors"
"fmt"
"net"
"net/url"
"sort"
"strings"
)
type URLs []url.URL
func NewURLs(strs []string) (URLs, error) {
all := make([]url.URL, len(strs))
if len(all) == 0 {
return nil, errors.New("no valid URLs given")
}
for i, in := range strs {
in = strings.TrimSpace(in)
u, err := url.Parse(in)
if err != nil {
return nil, err
}
switch u.Scheme {
case "http", "https":
if _, _, err := net.SplitHostPort(u.Host); err != nil {
return nil, fmt.Errorf(`URL address does not have the form "host:port": %s`, in)
}
if u.Path != "" {
return nil, fmt.Errorf("URL must not contain a path: %s", in)
}
case "unix", "unixs":
default:
return nil, fmt.Errorf("URL scheme must be http, https, unix, or unixs: %s", in)
}
all[i] = *u
}
us := URLs(all)
us.Sort()
return us, nil
}
func MustNewURLs(strs []string) URLs {
urls, err := NewURLs(strs)
if err != nil {
panic(err)
}
return urls
}
func (us URLs) String() string {
return strings.Join(us.StringSlice(), ",")
}
func (us *URLs) Sort() {
sort.Sort(us)
}
func (us URLs) Len() int { return len(us) }
func (us URLs) Less(i, j int) bool { return us[i].String() < us[j].String() }
func (us URLs) Swap(i, j int) { us[i], us[j] = us[j], us[i] }
func (us URLs) StringSlice() []string {
out := make([]string, len(us))
for i := range us {
out[i] = us[i].String()
}
return out
}
================================================
FILE: client/pkg/types/urls_test.go
================================================
// Copyright 2015 The etcd Authors
//
// 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 types
import (
"reflect"
"testing"
"go.etcd.io/etcd/client/pkg/v3/testutil"
)
func TestNewURLs(t *testing.T) {
tests := []struct {
strs []string
wurls URLs
}{
{
[]string{"http://127.0.0.1:2379"},
testutil.MustNewURLs(t, []string{"http://127.0.0.1:2379"}),
},
// it can trim space
{
[]string{" http://127.0.0.1:2379 "},
testutil.MustNewURLs(t, []string{"http://127.0.0.1:2379"}),
},
// it does sort
{
[]string{
"http://127.0.0.2:2379",
"http://127.0.0.1:2379",
},
testutil.MustNewURLs(t, []string{
"http://127.0.0.1:2379",
"http://127.0.0.2:2379",
}),
},
}
for i, tt := range tests {
urls, _ := NewURLs(tt.strs)
if !reflect.DeepEqual(urls, tt.wurls) {
t.Errorf("#%d: urls = %+v, want %+v", i, urls, tt.wurls)
}
}
}
func TestURLsString(t *testing.T) {
tests := []struct {
us URLs
wstr string
}{
{
URLs{},
"",
},
{
testutil.MustNewURLs(t, []string{"http://127.0.0.1:2379"}),
"http://127.0.0.1:2379",
},
{
testutil.MustNewURLs(t, []string{
"http://127.0.0.1:2379",
"http://127.0.0.2:2379",
}),
"http://127.0.0.1:2379,http://127.0.0.2:2379",
},
{
testutil.MustNewURLs(t, []string{
"http://127.0.0.2:2379",
"http://127.0.0.1:2379",
}),
"http://127.0.0.2:2379,http://127.0.0.1:2379",
},
}
for i, tt := range tests {
g := tt.us.String()
if g != tt.wstr {
t.Errorf("#%d: string = %s, want %s", i, g, tt.wstr)
}
}
}
func TestURLsSort(t *testing.T) {
g := testutil.MustNewURLs(t, []string{
"http://127.0.0.4:2379",
"http://127.0.0.2:2379",
"http://127.0.0.1:2379",
"http://127.0.0.3:2379",
})
w := testutil.MustNewURLs(t, []string{
"http://127.0.0.1:2379",
"http://127.0.0.2:2379",
"http://127.0.0.3:2379",
"http://127.0.0.4:2379",
})
gurls := URLs(g)
gurls.Sort()
if !reflect.DeepEqual(g, w) {
t.Errorf("URLs after sort = %#v, want %#v", g, w)
}
}
func TestURLsStringSlice(t *testing.T) {
tests := []struct {
us URLs
wstr []string
}{
{
URLs{},
[]string{},
},
{
testutil.MustNewURLs(t, []string{"http://127.0.0.1:2379"}),
[]string{"http://127.0.0.1:2379"},
},
{
testutil.MustNewURLs(t, []string{
"http://127.0.0.1:2379",
"http://127.0.0.2:2379",
}),
[]string{"http://127.0.0.1:2379", "http://127.0.0.2:2379"},
},
{
testutil.MustNewURLs(t, []string{
"http://127.0.0.2:2379",
"http://127.0.0.1:2379",
}),
[]string{"http://127.0.0.2:2379", "http://127.0.0.1:2379"},
},
}
for i, tt := range tests {
g := tt.us.StringSlice()
if !reflect.DeepEqual(g, tt.wstr) {
t.Errorf("#%d: string slice = %+v, want %+v", i, g, tt.wstr)
}
}
}
func TestNewURLsFail(t *testing.T) {
tests := [][]string{
// no urls given
{},
// missing protocol scheme
{"://127.0.0.1:2379"},
// unsupported scheme
{"mailto://127.0.0.1:2379"},
// not conform to host:port
{"http://127.0.0.1"},
// contain a path
{"http://127.0.0.1:2379/path"},
}
for i, tt := range tests {
_, err := NewURLs(tt)
if err == nil {
t.Errorf("#%d: err = nil, but error", i)
}
}
}
================================================
FILE: client/pkg/types/urlsmap.go
================================================
// Copyright 2015 The etcd Authors
//
// 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 types
import (
"fmt"
"sort"
"strings"
)
// URLsMap is a map from a name to its URLs.
type URLsMap map[string]URLs
// NewURLsMap returns a URLsMap instantiated from the given string,
// which consists of discovery-formatted names-to-URLs, like:
// mach0=http://1.1.1.1:2380,mach0=http://2.2.2.2::2380,mach1=http://3.3.3.3:2380,mach2=http://4.4.4.4:2380
func NewURLsMap(s string) (URLsMap, error) {
m := parse(s)
cl := URLsMap{}
for name, urls := range m {
us, err := NewURLs(urls)
if err != nil {
return nil, err
}
cl[name] = us
}
return cl, nil
}
// NewURLsMapFromStringMap takes a map of strings and returns a URLsMap. The
// string values in the map can be multiple values separated by the sep string.
func NewURLsMapFromStringMap(m map[string]string, sep string) (URLsMap, error) {
var err error
um := URLsMap{}
for k, v := range m {
um[k], err = NewURLs(strings.Split(v, sep))
if err != nil {
return nil, err
}
}
return um, nil
}
// String turns URLsMap into discovery-formatted name-to-URLs sorted by name.
func (c URLsMap) String() string {
var pairs []string
for name, urls := range c {
for _, url := range urls {
pairs = append(pairs, fmt.Sprintf("%s=%s", name, url.String()))
}
}
sort.Strings(pairs)
return strings.Join(pairs, ",")
}
// URLs returns a list of all URLs.
// The returned list is sorted in ascending lexicographical order.
func (c URLsMap) URLs() []string {
var urls []string
for _, us := range c {
for _, u := range us {
urls = append(urls, u.String())
}
}
sort.Strings(urls)
return urls
}
// Len returns the size of URLsMap.
func (c URLsMap) Len() int {
return len(c)
}
// parse parses the given string and returns a map listing the values specified for each key.
func parse(s string) map[string][]string {
m := make(map[string][]string)
for s != "" {
key := s
if i := strings.IndexAny(key, ","); i >= 0 {
key, s = key[:i], key[i+1:]
} else {
s = ""
}
if key == "" {
continue
}
value := ""
if i := strings.Index(key, "="); i >= 0 {
key, value = key[:i], key[i+1:]
}
m[key] = append(m[key], value)
}
return m
}
================================================
FILE: client/pkg/types/urlsmap_test.go
================================================
// Copyright 2015 The etcd Authors
//
// 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 types
import (
"reflect"
"testing"
"github.com/stretchr/testify/require"
"go.etcd.io/etcd/client/pkg/v3/testutil"
)
func TestParseInitialCluster(t *testing.T) {
c, err := NewURLsMap("mem1=http://10.0.0.1:2379,mem1=http://128.193.4.20:2379,mem2=http://10.0.0.2:2379,default=http://127.0.0.1:2379")
require.NoError(t, err)
wc := URLsMap(map[string]URLs{
"mem1": testutil.MustNewURLs(t, []string{"http://10.0.0.1:2379", "http://128.193.4.20:2379"}),
"mem2": testutil.MustNewURLs(t, []string{"http://10.0.0.2:2379"}),
"default": testutil.MustNewURLs(t, []string{"http://127.0.0.1:2379"}),
})
if !reflect.DeepEqual(c, wc) {
t.Errorf("cluster = %+v, want %+v", c, wc)
}
}
func TestParseInitialClusterBad(t *testing.T) {
tests := []string{
// invalid URL
"%^",
// no URL defined for member
"mem1=,mem2=http://128.193.4.20:2379,mem3=http://10.0.0.2:2379",
"mem1,mem2=http://128.193.4.20:2379,mem3=http://10.0.0.2:2379",
// bad URL for member
"default=http://localhost/",
}
for i, tt := range tests {
if _, err := NewURLsMap(tt); err == nil {
t.Errorf("#%d: unexpected successful parse, want err", i)
}
}
}
func TestNameURLPairsString(t *testing.T) {
cls := URLsMap(map[string]URLs{
"abc": testutil.MustNewURLs(t, []string{"http://1.1.1.1:1111", "http://0.0.0.0:0000"}),
"def": testutil.MustNewURLs(t, []string{"http://2.2.2.2:2222"}),
"ghi": testutil.MustNewURLs(t, []string{"http://3.3.3.3:1234", "http://127.0.0.1:2380"}),
// no PeerURLs = not included
"four": testutil.MustNewURLs(t, []string{}),
"five": testutil.MustNewURLs(t, nil),
})
w := "abc=http://0.0.0.0:0000,abc=http://1.1.1.1:1111,def=http://2.2.2.2:2222,ghi=http://127.0.0.1:2380,ghi=http://3.3.3.3:1234"
g := cls.String()
require.Equalf(t, g, w, "NameURLPairs.String():\ngot %#v\nwant %#v", g, w)
}
func TestParse(t *testing.T) {
tests := []struct {
s string
wm map[string][]string
}{
{
"",
map[string][]string{},
},
{
"a=b",
map[string][]string{"a": {"b"}},
},
{
"a=b,a=c",
map[string][]string{"a": {"b", "c"}},
},
{
"a=b,a1=c",
map[string][]string{"a": {"b"}, "a1": {"c"}},
},
}
for i, tt := range tests {
m := parse(tt.s)
if !reflect.DeepEqual(m, tt.wm) {
t.Errorf("#%d: m = %+v, want %+v", i, m, tt.wm)
}
}
}
// TestNewURLsMapIPV6 is only tested in Go1.5+ because Go1.4 doesn't support literal IPv6 address with zone in
// URI (https://github.com/golang/go/issues/6530).
func TestNewURLsMapIPV6(t *testing.T) {
c, err := NewURLsMap("mem1=http://[2001:db8::1]:2380,mem1=http://[fe80::6e40:8ff:feb1:58e4%25en0]:2380,mem2=http://[fe80::92e2:baff:fe7c:3224%25ext0]:2380")
require.NoError(t, err)
wc := URLsMap(map[string]URLs{
"mem1": testutil.MustNewURLs(t, []string{"http://[2001:db8::1]:2380", "http://[fe80::6e40:8ff:feb1:58e4%25en0]:2380"}),
"mem2": testutil.MustNewURLs(t, []string{"http://[fe80::92e2:baff:fe7c:3224%25ext0]:2380"}),
})
if !reflect.DeepEqual(c, wc) {
t.Errorf("cluster = %#v, want %#v", c, wc)
}
}
func TestNewURLsMapFromStringMapEmpty(t *testing.T) {
mss := make(map[string]string)
urlsMap, err := NewURLsMapFromStringMap(mss, ",")
if err != nil {
t.Errorf("Unexpected error: %v", err)
}
s := ""
um, err := NewURLsMap(s)
if err != nil {
t.Errorf("Unexpected error: %v", err)
}
if um.String() != urlsMap.String() {
t.Errorf("Expected:\n%+v\ngot:\n%+v", um, urlsMap)
}
}
func TestNewURLsMapFromStringMapNormal(t *testing.T) {
mss := make(map[string]string)
mss["host0"] = "http://127.0.0.1:2379,http://127.0.0.1:2380"
mss["host1"] = "http://127.0.0.1:2381,http://127.0.0.1:2382"
mss["host2"] = "http://127.0.0.1:2383,http://127.0.0.1:2384"
mss["host3"] = "http://127.0.0.1:2385,http://127.0.0.1:2386"
urlsMap, err := NewURLsMapFromStringMap(mss, ",")
if err != nil {
t.Errorf("Unexpected error: %v", err)
}
s := "host0=http://127.0.0.1:2379,host0=http://127.0.0.1:2380," +
"host1=http://127.0.0.1:2381,host1=http://127.0.0.1:2382," +
"host2=http://127.0.0.1:2383,host2=http://127.0.0.1:2384," +
"host3=http://127.0.0.1:2385,host3=http://127.0.0.1:2386"
um, err := NewURLsMap(s)
if err != nil {
t.Errorf("Unexpected error: %v", err)
}
if um.String() != urlsMap.String() {
t.Errorf("Expected:\n%+v\ngot:\n%+v", um, urlsMap)
}
}
================================================
FILE: client/pkg/verify/verify.go
================================================
// Copyright 2022 The etcd Authors
//
// 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 verify
import (
"fmt"
"os"
"strings"
)
const envVerify = "ETCD_VERIFY"
type VerificationType string
const (
envVerifyValueAll VerificationType = "all"
envVerifyValueAssert VerificationType = "assert"
)
func getEnvVerify() string {
return strings.ToLower(os.Getenv(envVerify))
}
func IsVerificationEnabled(verification VerificationType) bool {
env := getEnvVerify()
return env == string(envVerifyValueAll) || env == strings.ToLower(string(verification))
}
// EnableVerifications sets `envVerify` and returns a function that
// can be used to bring the original settings.
func EnableVerifications(verification VerificationType) func() {
previousEnv := getEnvVerify()
os.Setenv(envVerify, string(verification))
return func() {
os.Setenv(envVerify, previousEnv)
}
}
// EnableAllVerifications enables verification and returns a function
// that can be used to bring the original settings.
func EnableAllVerifications() func() {
return EnableVerifications(envVerifyValueAll)
}
// DisableVerifications unsets `envVerify` and returns a function that
// can be used to bring the original settings.
func DisableVerifications() func() {
previousEnv := getEnvVerify()
os.Unsetenv(envVerify)
return func() {
os.Setenv(envVerify, previousEnv)
}
}
// Verify performs verification if the assertions are enabled.
// In the default setup running in tests and skipped in the production code.
func Verify(msg string, f VerifyFunc) {
if IsVerificationEnabled(envVerifyValueAssert) {
ok, details := f()
verifier(ok, msg, details)
}
}
type VerifyFunc func() (condition bool, details map[string]any)
func verifier(condition bool, msg string, details map[string]any) {
if !condition {
panic(fmt.Sprintf("%s. details: %v.", msg, details))
}
}
// Assert will panic with a given formatted message if the given condition is false.
func Assert(condition bool, msg string, v ...any) {
if !condition {
panic(fmt.Sprintf("assertion failed: "+msg, v...))
}
}
================================================
FILE: client/v3/.gomodguard.yaml
================================================
---
blocked:
modules:
- go.etcd.io/etcd:
reason: "Forbidden dependency"
- go.etcd.io/etcd/pkg/v3:
reason: "Forbidden dependency"
- go.etcd.io/etcd/server/v3:
reason: "Forbidden dependency"
- go.etcd.io/etcd/tests/v3:
reason: "Forbidden dependency"
- go.etcd.io/etcd/v3:
reason: "Forbidden dependency"
================================================
FILE: client/v3/LICENSE
================================================
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright 2020 The etcd Authors
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.
================================================
FILE: client/v3/OWNERS
================================================
# See the OWNERS docs at https://go.k8s.io/owners
labels:
- area/clientv3
================================================
FILE: client/v3/README.md
================================================
# etcd/client/v3
[](https://etcd.io/docs)
[](https://godoc.org/go.etcd.io/etcd/client/v3)
`etcd/clientv3` is the official Go etcd client for v3.
## Install
```bash
go get go.etcd.io/etcd/client/v3
```
## Get started
Create client using `clientv3.New`:
```go
import clientv3 "go.etcd.io/etcd/client/v3"
func main() {
cli, err := clientv3.New(clientv3.Config{
Endpoints: []string{"localhost:2379", "localhost:22379", "localhost:32379"},
DialTimeout: 5 * time.Second,
})
if err != nil {
// handle error!
}
defer cli.Close()
}
```
etcd v3 uses [`gRPC`](https://www.grpc.io) for remote procedure calls. And `clientv3` uses
[`grpc-go`](https://github.com/grpc/grpc-go) to connect to etcd. Make sure to close the client after using it.
If the client is not closed, the connection will have leaky goroutines. To specify client request timeout,
pass `context.WithTimeout` to APIs:
```go
ctx, cancel := context.WithTimeout(context.Background(), timeout)
resp, err := cli.Put(ctx, "sample_key", "sample_value")
cancel()
if err != nil {
// handle error!
}
// use the response
```
For full compatibility, it is recommended to install released versions of clients using go modules.
## Error Handling
etcd client returns 2 types of errors:
1. context error: canceled or deadline exceeded.
2. gRPC error: see [api/v3rpc/rpctypes](https://godoc.org/go.etcd.io/etcd/api/v3rpc/rpctypes).
Here is the example code to handle client errors:
```go
resp, err := cli.Put(ctx, "", "")
if err != nil {
switch err {
case context.Canceled:
log.Fatalf("ctx is canceled by another routine: %v", err)
case context.DeadlineExceeded:
log.Fatalf("ctx is attached with a deadline is exceeded: %v", err)
case rpctypes.ErrEmptyKey:
log.Fatalf("client-side error: %v", err)
default:
log.Fatalf("bad cluster endpoints, which are not etcd servers: %v", err)
}
}
```
## Metrics
The etcd client optionally exposes RPC metrics through [go-grpc-prometheus](https://github.com/grpc-ecosystem/go-grpc-prometheus). See the [examples](https://github.com/etcd-io/etcd/blob/main/tests/integration/clientv3/examples/example_metrics_test.go).
## Namespacing
The [namespace](https://godoc.org/go.etcd.io/etcd/client/v3/namespace) package provides `clientv3` interface wrappers to transparently isolate client requests to a user-defined prefix.
## Request size limit
Client request size limit is configurable via `clientv3.Config.MaxCallSendMsgSize` and `MaxCallRecvMsgSize` in bytes. If none given, client request send limit defaults to 2 MiB including gRPC overhead bytes. And receive limit defaults to `math.MaxInt32`.
## Examples
More code [examples](https://github.com/etcd-io/etcd/tree/main/tests/integration/clientv3/examples) can be found at [GoDoc](https://pkg.go.dev/go.etcd.io/etcd/client/v3).
================================================
FILE: client/v3/auth.go
================================================
// Copyright 2016 The etcd Authors
//
// 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 clientv3
import (
"context"
"fmt"
"strings"
"google.golang.org/grpc"
"go.etcd.io/etcd/api/v3/authpb"
pb "go.etcd.io/etcd/api/v3/etcdserverpb"
)
type (
AuthEnableResponse pb.AuthEnableResponse
AuthDisableResponse pb.AuthDisableResponse
AuthStatusResponse pb.AuthStatusResponse
AuthenticateResponse pb.AuthenticateResponse
AuthUserAddResponse pb.AuthUserAddResponse
AuthUserDeleteResponse pb.AuthUserDeleteResponse
AuthUserChangePasswordResponse pb.AuthUserChangePasswordResponse
AuthUserGrantRoleResponse pb.AuthUserGrantRoleResponse
AuthUserGetResponse pb.AuthUserGetResponse
AuthUserRevokeRoleResponse pb.AuthUserRevokeRoleResponse
AuthRoleAddResponse pb.AuthRoleAddResponse
AuthRoleGrantPermissionResponse pb.AuthRoleGrantPermissionResponse
AuthRoleGetResponse pb.AuthRoleGetResponse
AuthRoleRevokePermissionResponse pb.AuthRoleRevokePermissionResponse
AuthRoleDeleteResponse pb.AuthRoleDeleteResponse
AuthUserListResponse pb.AuthUserListResponse
AuthRoleListResponse pb.AuthRoleListResponse
PermissionType authpb.Permission_Type
Permission authpb.Permission
)
const (
PermRead = authpb.Permission_READ
PermWrite = authpb.Permission_WRITE
PermReadWrite = authpb.Permission_READWRITE
)
type UserAddOptions authpb.UserAddOptions
type Auth interface {
// Authenticate login and get token
Authenticate(ctx context.Context, name string, password string) (*AuthenticateResponse, error)
// AuthEnable enables auth of an etcd cluster.
AuthEnable(ctx context.Context) (*AuthEnableResponse, error)
// AuthDisable disables auth of an etcd cluster.
AuthDisable(ctx context.Context) (*AuthDisableResponse, error)
// AuthStatus returns the status of auth of an etcd cluster.
AuthStatus(ctx context.Context) (*AuthStatusResponse, error)
// UserAdd adds a new user to an etcd cluster.
UserAdd(ctx context.Context, name string, password string) (*AuthUserAddResponse, error)
// UserAddWithOptions adds a new user to an etcd cluster with some options.
UserAddWithOptions(ctx context.Context, name string, password string, opt *UserAddOptions) (*AuthUserAddResponse, error)
// UserDelete deletes a user from an etcd cluster.
UserDelete(ctx context.Context, name string) (*AuthUserDeleteResponse, error)
// UserChangePassword changes a password of a user.
UserChangePassword(ctx context.Context, name string, password string) (*AuthUserChangePasswordResponse, error)
// UserGrantRole grants a role to a user.
UserGrantRole(ctx context.Context, user string, role string) (*AuthUserGrantRoleResponse, error)
// UserGet gets a detailed information of a user.
UserGet(ctx context.Context, name string) (*AuthUserGetResponse, error)
// UserList gets a list of all users.
UserList(ctx context.Context) (*AuthUserListResponse, error)
// UserRevokeRole revokes a role of a user.
UserRevokeRole(ctx context.Context, name string, role string) (*AuthUserRevokeRoleResponse, error)
// RoleAdd adds a new role to an etcd cluster.
RoleAdd(ctx context.Context, name string) (*AuthRoleAddResponse, error)
// RoleGrantPermission grants a permission to a role.
RoleGrantPermission(ctx context.Context, name string, key, rangeEnd string, permType PermissionType) (*AuthRoleGrantPermissionResponse, error)
// RoleGet gets a detailed information of a role.
RoleGet(ctx context.Context, role string) (*AuthRoleGetResponse, error)
// RoleList gets a list of all roles.
RoleList(ctx context.Context) (*AuthRoleListResponse, error)
// RoleRevokePermission revokes a permission from a role.
RoleRevokePermission(ctx context.Context, role string, key, rangeEnd string) (*AuthRoleRevokePermissionResponse, error)
// RoleDelete deletes a role.
RoleDelete(ctx context.Context, role string) (*AuthRoleDeleteResponse, error)
}
type authClient struct {
remote pb.AuthClient
callOpts []grpc.CallOption
}
func NewAuth(c *Client) Auth {
api := &authClient{remote: RetryAuthClient(c)}
if c != nil {
api.callOpts = c.callOpts
}
return api
}
func NewAuthFromAuthClient(remote pb.AuthClient, c *Client) Auth {
api := &authClient{remote: remote}
if c != nil {
api.callOpts = c.callOpts
}
return api
}
func (auth *authClient) Authenticate(ctx context.Context, name string, password string) (*AuthenticateResponse, error) {
resp, err := auth.remote.Authenticate(ctx, &pb.AuthenticateRequest{Name: name, Password: password}, auth.callOpts...)
return (*AuthenticateResponse)(resp), ContextError(ctx, err)
}
func (auth *authClient) AuthEnable(ctx context.Context) (*AuthEnableResponse, error) {
resp, err := auth.remote.AuthEnable(ctx, &pb.AuthEnableRequest{}, auth.callOpts...)
return (*AuthEnableResponse)(resp), ContextError(ctx, err)
}
func (auth *authClient) AuthDisable(ctx context.Context) (*AuthDisableResponse, error) {
resp, err := auth.remote.AuthDisable(ctx, &pb.AuthDisableRequest{}, auth.callOpts...)
return (*AuthDisableResponse)(resp), ContextError(ctx, err)
}
func (auth *authClient) AuthStatus(ctx context.Context) (*AuthStatusResponse, error) {
resp, err := auth.remote.AuthStatus(ctx, &pb.AuthStatusRequest{}, auth.callOpts...)
return (*AuthStatusResponse)(resp), ContextError(ctx, err)
}
func (auth *authClient) UserAdd(ctx context.Context, name string, password string) (*AuthUserAddResponse, error) {
resp, err := auth.remote.UserAdd(ctx, &pb.AuthUserAddRequest{Name: name, Password: password, Options: &authpb.UserAddOptions{NoPassword: false}}, auth.callOpts...)
return (*AuthUserAddResponse)(resp), ContextError(ctx, err)
}
func (auth *authClient) UserAddWithOptions(ctx context.Context, name string, password string, options *UserAddOptions) (*AuthUserAddResponse, error) {
resp, err := auth.remote.UserAdd(ctx, &pb.AuthUserAddRequest{Name: name, Password: password, Options: (*authpb.UserAddOptions)(options)}, auth.callOpts...)
return (*AuthUserAddResponse)(resp), ContextError(ctx, err)
}
func (auth *authClient) UserDelete(ctx context.Context, name string) (*AuthUserDeleteResponse, error) {
resp, err := auth.remote.UserDelete(ctx, &pb.AuthUserDeleteRequest{Name: name}, auth.callOpts...)
return (*AuthUserDeleteResponse)(resp), ContextError(ctx, err)
}
func (auth *authClient) UserChangePassword(ctx context.Context, name string, password string) (*AuthUserChangePasswordResponse, error) {
resp, err := auth.remote.UserChangePassword(ctx, &pb.AuthUserChangePasswordRequest{Name: name, Password: password}, auth.callOpts...)
return (*AuthUserChangePasswordResponse)(resp), ContextError(ctx, err)
}
func (auth *authClient) UserGrantRole(ctx context.Context, user string, role string) (*AuthUserGrantRoleResponse, error) {
resp, err := auth.remote.UserGrantRole(ctx, &pb.AuthUserGrantRoleRequest{User: user, Role: role}, auth.callOpts...)
return (*AuthUserGrantRoleResponse)(resp), ContextError(ctx, err)
}
func (auth *authClient) UserGet(ctx context.Context, name string) (*AuthUserGetResponse, error) {
resp, err := auth.remote.UserGet(ctx, &pb.AuthUserGetRequest{Name: name}, auth.callOpts...)
return (*AuthUserGetResponse)(resp), ContextError(ctx, err)
}
func (auth *authClient) UserList(ctx context.Context) (*AuthUserListResponse, error) {
resp, err := auth.remote.UserList(ctx, &pb.AuthUserListRequest{}, auth.callOpts...)
return (*AuthUserListResponse)(resp), ContextError(ctx, err)
}
func (auth *authClient) UserRevokeRole(ctx context.Context, name string, role string) (*AuthUserRevokeRoleResponse, error) {
resp, err := auth.remote.UserRevokeRole(ctx, &pb.AuthUserRevokeRoleRequest{Name: name, Role: role}, auth.callOpts...)
return (*AuthUserRevokeRoleResponse)(resp), ContextError(ctx, err)
}
func (auth *authClient) RoleAdd(ctx context.Context, name string) (*AuthRoleAddResponse, error) {
resp, err := auth.remote.RoleAdd(ctx, &pb.AuthRoleAddRequest{Name: name}, auth.callOpts...)
return (*AuthRoleAddResponse)(resp), ContextError(ctx, err)
}
func (auth *authClient) RoleGrantPermission(ctx context.Context, name string, key, rangeEnd string, permType PermissionType) (*AuthRoleGrantPermissionResponse, error) {
perm := &authpb.Permission{
Key: []byte(key),
RangeEnd: []byte(rangeEnd),
PermType: authpb.Permission_Type(permType),
}
resp, err := auth.remote.RoleGrantPermission(ctx, &pb.AuthRoleGrantPermissionRequest{Name: name, Perm: perm}, auth.callOpts...)
return (*AuthRoleGrantPermissionResponse)(resp), ContextError(ctx, err)
}
func (auth *authClient) RoleGet(ctx context.Context, role string) (*AuthRoleGetResponse, error) {
resp, err := auth.remote.RoleGet(ctx, &pb.AuthRoleGetRequest{Role: role}, auth.callOpts...)
return (*AuthRoleGetResponse)(resp), ContextError(ctx, err)
}
func (auth *authClient) RoleList(ctx context.Context) (*AuthRoleListResponse, error) {
resp, err := auth.remote.RoleList(ctx, &pb.AuthRoleListRequest{}, auth.callOpts...)
return (*AuthRoleListResponse)(resp), ContextError(ctx, err)
}
func (auth *authClient) RoleRevokePermission(ctx context.Context, role string, key, rangeEnd string) (*AuthRoleRevokePermissionResponse, error) {
resp, err := auth.remote.RoleRevokePermission(ctx, &pb.AuthRoleRevokePermissionRequest{Role: role, Key: []byte(key), RangeEnd: []byte(rangeEnd)}, auth.callOpts...)
return (*AuthRoleRevokePermissionResponse)(resp), ContextError(ctx, err)
}
func (auth *authClient) RoleDelete(ctx context.Context, role string) (*AuthRoleDeleteResponse, error) {
resp, err := auth.remote.RoleDelete(ctx, &pb.AuthRoleDeleteRequest{Role: role}, auth.callOpts...)
return (*AuthRoleDeleteResponse)(resp), ContextError(ctx, err)
}
func StrToPermissionType(s string) (PermissionType, error) {
val, ok := authpb.Permission_Type_value[strings.ToUpper(s)]
if ok {
return PermissionType(val), nil
}
return PermissionType(-1), fmt.Errorf("invalid permission type: %s", s)
}
================================================
FILE: client/v3/client.go
================================================
// Copyright 2016 The etcd Authors
//
// 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 clientv3
import (
"context"
"errors"
"fmt"
"strings"
"sync"
"sync/atomic"
"time"
"github.com/coreos/go-semver/semver"
"go.uber.org/zap"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
grpccredentials "google.golang.org/grpc/credentials"
"google.golang.org/grpc/credentials/insecure"
healthpb "google.golang.org/grpc/health/grpc_health_v1"
"google.golang.org/grpc/keepalive"
"google.golang.org/grpc/status"
"go.etcd.io/etcd/api/v3/v3rpc/rpctypes"
"go.etcd.io/etcd/api/v3/version"
"go.etcd.io/etcd/client/pkg/v3/logutil"
"go.etcd.io/etcd/client/pkg/v3/verify"
"go.etcd.io/etcd/client/v3/credentials"
"go.etcd.io/etcd/client/v3/internal/endpoint"
"go.etcd.io/etcd/client/v3/internal/resolver"
)
var (
ErrNoAvailableEndpoints = errors.New("etcdclient: no available endpoints")
ErrOldCluster = errors.New("etcdclient: old cluster version")
ErrMutuallyExclusiveCfg = errors.New("Username/Password and Token configurations are mutually exclusive")
)
// Client provides and manages an etcd v3 client session.
type Client struct {
Cluster
KV
Lease
Watcher
Auth
Maintenance
conn *grpc.ClientConn
cfg Config
creds grpccredentials.TransportCredentials
resolver *resolver.EtcdManualResolver
epMu *sync.RWMutex
endpoints []string
ctx context.Context
cancel context.CancelFunc
// Username is a user name for authentication.
Username string
// Password is a password for authentication.
Password string
// Token is a JWT used for authentication instead of a password.
Token string
authTokenBundle credentials.PerRPCCredentialsBundle
callOpts []grpc.CallOption
lg atomic.Pointer[zap.Logger]
}
// New creates a new etcdv3 client from a given configuration.
func New(cfg Config) (*Client, error) {
if len(cfg.Endpoints) == 0 {
return nil, ErrNoAvailableEndpoints
}
return newClient(&cfg)
}
// NewCtxClient creates a client with a context but no underlying grpc
// connection. This is useful for embedded cases that override the
// service interface implementations and do not need connection management.
func NewCtxClient(ctx context.Context, opts ...Option) *Client {
cctx, cancel := context.WithCancel(ctx)
c := &Client{ctx: cctx, cancel: cancel, epMu: new(sync.RWMutex)}
for _, opt := range opts {
opt(c)
}
if c.lg.Load() == nil {
c.lg.Store(zap.NewNop())
}
return c
}
// Option is a function type that can be passed as argument to NewCtxClient to configure client
type Option func(*Client)
// NewFromURL creates a new etcdv3 client from a URL.
func NewFromURL(url string) (*Client, error) {
return New(Config{Endpoints: []string{url}})
}
// NewFromURLs creates a new etcdv3 client from URLs.
func NewFromURLs(urls []string) (*Client, error) {
return New(Config{Endpoints: urls})
}
// WithZapLogger is a NewCtxClient option that overrides the logger
func WithZapLogger(lg *zap.Logger) Option {
return func(c *Client) {
c.lg.Store(lg)
}
}
// WithLogger overrides the logger.
//
// Deprecated: Please use WithZapLogger or Logger field in clientv3.Config
//
// Does not changes grpcLogger, that can be explicitly configured
// using grpc_zap.ReplaceGrpcLoggerV2(..) method.
func (c *Client) WithLogger(lg *zap.Logger) *Client {
c.lg.Store(lg)
return c
}
// GetLogger gets the logger.
// NOTE: This method is for internal use of etcd-client library and should not be used as general-purpose logger.
func (c *Client) GetLogger() *zap.Logger {
return c.lg.Load()
}
// Close shuts down the client's etcd connections.
func (c *Client) Close() error {
c.cancel()
if c.Watcher != nil {
c.Watcher.Close()
}
if c.Lease != nil {
c.Lease.Close()
}
if c.conn != nil {
return ContextError(c.ctx, c.conn.Close())
}
return c.ctx.Err()
}
// Ctx is a context for "out of band" messages (e.g., for sending
// "clean up" message when another context is canceled). It is
// canceled on client Close().
func (c *Client) Ctx() context.Context { return c.ctx }
// Endpoints lists the registered endpoints for the client.
func (c *Client) Endpoints() []string {
// copy the slice; protect original endpoints from being changed
c.epMu.RLock()
defer c.epMu.RUnlock()
eps := make([]string, len(c.endpoints))
copy(eps, c.endpoints)
return eps
}
// SetEndpoints updates client's endpoints.
func (c *Client) SetEndpoints(eps ...string) {
c.epMu.Lock()
defer c.epMu.Unlock()
c.endpoints = eps
c.resolver.SetEndpoints(eps)
}
// Sync synchronizes client's endpoints with the known endpoints from the etcd membership.
func (c *Client) Sync(ctx context.Context) error {
mresp, err := c.MemberList(ctx)
if err != nil {
return err
}
var eps []string
for _, m := range mresp.Members {
if len(m.Name) != 0 && !m.IsLearner {
eps = append(eps, m.ClientURLs...)
}
}
// The linearizable `MemberList` returned successfully, so the
// endpoints shouldn't be empty.
verify.Verify("empty endpoints returned from etcd cluster", func() (bool, map[string]any) {
return len(eps) > 0, nil
})
c.SetEndpoints(eps...)
c.GetLogger().Debug("set etcd endpoints by autoSync", zap.Strings("endpoints", eps))
return nil
}
func (c *Client) autoSync() {
if c.cfg.AutoSyncInterval == time.Duration(0) {
return
}
for {
select {
case <-c.ctx.Done():
return
case <-time.After(c.cfg.AutoSyncInterval):
ctx, cancel := context.WithTimeout(c.ctx, 5*time.Second)
err := c.Sync(ctx)
cancel()
if err != nil && !errors.Is(err, c.ctx.Err()) {
c.GetLogger().Info("Auto sync endpoints failed.", zap.Error(err))
}
}
}
}
// dialSetupOpts gives the dial opts prior to any authentication.
func (c *Client) dialSetupOpts(creds grpccredentials.TransportCredentials, dopts ...grpc.DialOption) []grpc.DialOption {
var opts []grpc.DialOption
if c.cfg.DialKeepAliveTime > 0 {
params := keepalive.ClientParameters{
Time: c.cfg.DialKeepAliveTime,
Timeout: c.cfg.DialKeepAliveTimeout,
PermitWithoutStream: c.cfg.PermitWithoutStream,
}
opts = append(opts, grpc.WithKeepaliveParams(params))
}
opts = append(opts, dopts...)
if creds != nil {
opts = append(opts, grpc.WithTransportCredentials(creds))
} else {
opts = append(opts, grpc.WithTransportCredentials(insecure.NewCredentials()))
}
unaryMaxRetries := defaultUnaryMaxRetries
if c.cfg.MaxUnaryRetries > 0 {
unaryMaxRetries = c.cfg.MaxUnaryRetries
}
backoffWaitBetween := defaultBackoffWaitBetween
if c.cfg.BackoffWaitBetween > 0 {
backoffWaitBetween = c.cfg.BackoffWaitBetween
}
backoffJitterFraction := defaultBackoffJitterFraction
if c.cfg.BackoffJitterFraction > 0 {
backoffJitterFraction = c.cfg.BackoffJitterFraction
}
// Interceptor retry and backoff.
// TODO: Replace all of clientv3/retry.go with RetryPolicy:
// https://github.com/grpc/grpc-proto/blob/cdd9ed5c3d3f87aef62f373b93361cf7bddc620d/grpc/service_config/service_config.proto#L130
rrBackoff := withBackoff(c.roundRobinQuorumBackoff(backoffWaitBetween, backoffJitterFraction))
opts = append(opts,
// Disable stream retry by default since go-grpc-middleware/retry does not support client streams.
// Streams that are safe to retry are enabled individually.
grpc.WithStreamInterceptor(c.streamClientInterceptor(withMax(0), rrBackoff)),
grpc.WithUnaryInterceptor(c.unaryClientInterceptor(withMax(unaryMaxRetries), rrBackoff)),
)
return opts
}
// Dial connects to a single endpoint using the client's config.
func (c *Client) Dial(ep string) (*grpc.ClientConn, error) {
creds := c.credentialsForEndpoint(ep)
// Using ad-hoc created resolver, to guarantee only explicitly given
// endpoint is used.
return c.dial(creds, grpc.WithResolvers(resolver.New(ep)))
}
func (c *Client) getToken(ctx context.Context) error {
var err error // return last error in a case of fail
if c.Token != "" {
c.authTokenBundle.UpdateAuthToken(c.Token)
return nil
}
if c.Username == "" || c.Password == "" {
return nil
}
resp, err := c.Auth.Authenticate(ctx, c.Username, c.Password)
if err != nil {
if errors.Is(err, rpctypes.ErrAuthNotEnabled) {
c.authTokenBundle.UpdateAuthToken("")
return nil
}
return err
}
c.authTokenBundle.UpdateAuthToken(resp.Token)
return nil
}
// dialWithBalancer dials the client's current load balanced resolver group. The scheme of the host
// of the provided endpoint determines the scheme used for all endpoints of the client connection.
func (c *Client) dialWithBalancer(dopts ...grpc.DialOption) (*grpc.ClientConn, error) {
creds := c.credentialsForEndpoint(c.Endpoints()[0])
opts := append(dopts, grpc.WithResolvers(c.resolver))
return c.dial(creds, opts...)
}
// dial configures and dials any grpc balancer target.
func (c *Client) dial(creds grpccredentials.TransportCredentials, dopts ...grpc.DialOption) (*grpc.ClientConn, error) {
opts := c.dialSetupOpts(creds, dopts...)
if c.authTokenBundle != nil {
opts = append(opts, grpc.WithPerRPCCredentials(c.authTokenBundle.PerRPCCredentials()))
}
opts = append(opts, c.cfg.DialOptions...)
target := fmt.Sprintf("%s://%p/%s", resolver.Schema, c, authority(c.endpoints[0]))
conn, err := grpc.NewClient(target, opts...)
if err != nil {
return nil, err
}
if dialTimeout := c.cfg.DialTimeout; dialTimeout > 0 {
dctx, cancel := context.WithTimeout(c.ctx, dialTimeout)
defer cancel()
if err := waitForConnection(dctx, conn); err != nil {
conn.Close()
return nil, err
}
}
return conn, nil
}
func waitForConnection(ctx context.Context, conn *grpc.ClientConn) error {
cli := healthpb.NewHealthClient(conn)
// Use WaitForReady to wait until the connection is ready. The health check
// may return Unimplemented if the server does not expose the health endpoint,
// or FailedPrecondition if the leader has not yet applied the configuration
// change that enables it. In both cases, we can still treat the connection as
// healthy enough to proceed.
//
// Use withMax to disable retrying on Unimplemented, so that we can
// return the original error immediately.
_, err := cli.Check(ctx, &healthpb.HealthCheckRequest{}, grpc.WaitForReady(true), withMax(0))
if err == nil {
return nil
}
if cerr := ctx.Err(); cerr != nil {
if serr, ok := status.FromError(err); ok && serr.Message() != "" {
return fmt.Errorf("etcdclient: failed to connect to the etcd server: %s: %w", serr.Message(), cerr)
}
return fmt.Errorf("etcdclient: failed to connect to the etcd server: %w", cerr)
}
serr, ok := status.FromError(err)
if ok {
switch serr.Code() {
case codes.Unimplemented, codes.FailedPrecondition:
return nil
}
}
return fmt.Errorf("etcdclient: failed to dial by invoking health endpoint: %w", err)
}
func authority(endpoint string) string {
spl := strings.SplitN(endpoint, "://", 2)
if len(spl) < 2 {
if strings.HasPrefix(endpoint, "unix:") {
return endpoint[len("unix:"):]
}
if strings.HasPrefix(endpoint, "unixs:") {
return endpoint[len("unixs:"):]
}
return endpoint
}
return spl[1]
}
func (c *Client) credentialsForEndpoint(ep string) grpccredentials.TransportCredentials {
r := endpoint.RequiresCredentials(ep)
switch r {
case endpoint.CredsDrop:
return nil
case endpoint.CredsOptional:
return c.creds
case endpoint.CredsRequire:
if c.creds != nil {
return c.creds
}
return credentials.NewTransportCredential(nil)
default:
panic(fmt.Errorf("unsupported CredsRequirement: %v", r))
}
}
func newClient(cfg *Config) (*Client, error) {
if cfg == nil {
cfg = &Config{}
}
var creds grpccredentials.TransportCredentials
if cfg.TLS != nil {
creds = credentials.NewTransportCredential(cfg.TLS)
}
if cfg.Token != "" && (cfg.Username != "" || cfg.Password != "") {
return nil, ErrMutuallyExclusiveCfg
}
// use a temporary skeleton client to bootstrap first connection
baseCtx := context.TODO()
if cfg.Context != nil {
baseCtx = cfg.Context
}
ctx, cancel := context.WithCancel(baseCtx)
client := &Client{
conn: nil,
cfg: *cfg,
creds: creds,
ctx: ctx,
cancel: cancel,
epMu: new(sync.RWMutex),
callOpts: defaultCallOpts,
}
var err error
var lg *zap.Logger
if cfg.Logger != nil {
lg = cfg.Logger
} else if cfg.LogConfig != nil {
lg, err = cfg.LogConfig.Build()
} else {
lg, err = logutil.CreateDefaultZapLogger(ClientLogLevel())
if lg != nil {
lg = lg.Named("etcd-client")
}
}
if err != nil {
return nil, err
}
client.lg.Store(lg)
if cfg.Username != "" && cfg.Password != "" {
client.Username = cfg.Username
client.Password = cfg.Password
client.authTokenBundle = credentials.NewPerRPCCredentialBundle()
}
if cfg.Token != "" {
client.Token = cfg.Token
client.authTokenBundle = credentials.NewPerRPCCredentialBundle()
}
if cfg.MaxCallSendMsgSize > 0 || cfg.MaxCallRecvMsgSize > 0 {
if cfg.MaxCallRecvMsgSize > 0 && cfg.MaxCallSendMsgSize > cfg.MaxCallRecvMsgSize {
return nil, fmt.Errorf("gRPC message recv limit (%d bytes) must be greater than send limit (%d bytes)", cfg.MaxCallRecvMsgSize, cfg.MaxCallSendMsgSize)
}
callOpts := []grpc.CallOption{
defaultWaitForReady,
defaultMaxCallSendMsgSize,
defaultMaxCallRecvMsgSize,
}
if cfg.MaxCallSendMsgSize > 0 {
callOpts[1] = grpc.MaxCallSendMsgSize(cfg.MaxCallSendMsgSize)
}
if cfg.MaxCallRecvMsgSize > 0 {
callOpts[2] = grpc.MaxCallRecvMsgSize(cfg.MaxCallRecvMsgSize)
}
client.callOpts = callOpts
}
client.resolver = resolver.New(cfg.Endpoints...)
if len(cfg.Endpoints) < 1 {
client.cancel()
return nil, errors.New("at least one Endpoint is required in client config")
}
client.SetEndpoints(cfg.Endpoints...)
// Use a provided endpoint target so that for https:// without any tls config given, then
// grpc will assume the certificate server name is the endpoint host.
conn, err := client.dialWithBalancer()
if err != nil {
client.cancel()
client.resolver.Close()
// TODO: Error like `fmt.Errorf(dialing [%s] failed: %v, strings.Join(cfg.Endpoints, ";"), err)` would help with debugging a lot.
return nil, err
}
client.conn = conn
client.Cluster = NewCluster(client)
client.KV = NewKV(client)
client.Lease = NewLease(client)
client.Watcher = NewWatcher(client)
client.Auth = NewAuth(client)
client.Maintenance = NewMaintenance(client)
// get token with established connection
ctx, cancel = client.ctx, func() {}
if client.cfg.DialTimeout > 0 {
ctx, cancel = context.WithTimeout(ctx, client.cfg.DialTimeout)
}
err = client.getToken(ctx)
if err != nil {
client.Close()
cancel()
// TODO: Consider fmt.Errorf("communicating with [%s] failed: %v", strings.Join(cfg.Endpoints, ";"), err)
return nil, err
}
cancel()
if cfg.RejectOldCluster {
if err := client.checkVersion(); err != nil {
client.Close()
return nil, err
}
}
go client.autoSync()
return client, nil
}
// roundRobinQuorumBackoff retries against quorum between each backoff.
// This is intended for use with a round robin load balancer.
func (c *Client) roundRobinQuorumBackoff(waitBetween time.Duration, jitterFraction float64) backoffFunc {
return func(attempt uint) time.Duration {
// after each round robin across quorum, backoff for our wait between duration
n := uint(len(c.Endpoints()))
quorum := (n/2 + 1)
if attempt%quorum == 0 {
c.GetLogger().Debug("backoff", zap.Uint("attempt", attempt), zap.Uint("quorum", quorum), zap.Duration("waitBetween", waitBetween), zap.Float64("jitterFraction", jitterFraction))
return jitterUp(waitBetween, jitterFraction)
}
c.GetLogger().Debug("backoff skipped", zap.Uint("attempt", attempt), zap.Uint("quorum", quorum))
return 0
}
}
// minSupportedVersion returns the minimum version supported, which is the previous minor release.
func minSupportedVersion() *semver.Version {
ver := semver.Must(semver.NewVersion(version.Version))
// consider only major and minor version
ver = &semver.Version{Major: ver.Major, Minor: ver.Minor}
for i := range version.AllVersions {
if version.AllVersions[i].Equal(*ver) {
if i == 0 {
return ver
}
return &version.AllVersions[i-1]
}
}
panic("current version is not in the version list")
}
func (c *Client) checkVersion() (err error) {
var wg sync.WaitGroup
eps := c.Endpoints()
errc := make(chan error, len(eps))
ctx, cancel := context.WithCancel(c.ctx)
if c.cfg.DialTimeout > 0 {
cancel()
ctx, cancel = context.WithTimeout(c.ctx, c.cfg.DialTimeout)
}
wg.Add(len(eps))
for _, ep := range eps {
// if cluster is current, any endpoint gives a recent version
go func(e string) {
defer wg.Done()
resp, rerr := c.Status(ctx, e)
if rerr != nil {
errc <- rerr
return
}
vs, serr := semver.NewVersion(resp.Version)
if serr != nil {
errc <- serr
return
}
if vs.LessThan(*minSupportedVersion()) {
rerr = ErrOldCluster
}
errc <- rerr
}(ep)
}
// wait for success
for range eps {
if err = <-errc; err != nil {
break
}
}
cancel()
wg.Wait()
return err
}
// ActiveConnection returns the current in-use connection
func (c *Client) ActiveConnection() *grpc.ClientConn { return c.conn }
// isHaltErr returns true if the given error and context indicate no forward
// progress can be made, even after reconnecting.
func isHaltErr(ctx context.Context, err error) bool {
if ctx != nil && ctx.Err() != nil {
return true
}
if err == nil {
return false
}
ev, _ := status.FromError(err)
// Unavailable codes mean the system will be right back.
// (e.g., can't connect, lost leader)
// Treat Internal codes as if something failed, leaving the
// system in an inconsistent state, but retrying could make progress.
// (e.g., failed in middle of send, corrupted frame)
// TODO: are permanent Internal errors possible from grpc?
return ev.Code() != codes.Unavailable && ev.Code() != codes.Internal
}
// isUnavailableErr returns true if the given error is an unavailable error
func isUnavailableErr(ctx context.Context, err error) bool {
if ctx != nil && ctx.Err() != nil {
return false
}
if err == nil {
return false
}
ev, ok := status.FromError(err)
if ok {
// Unavailable codes mean the system will be right back.
// (e.g., can't connect, lost leader)
return ev.Code() == codes.Unavailable
}
return false
}
// ContextError converts the error into an EtcdError if the error message matches one of
// the defined messages; otherwise, it tries to retrieve the context error.
func ContextError(ctx context.Context, err error) error {
if err == nil {
return nil
}
err = rpctypes.Error(err)
var serverErr rpctypes.EtcdError
if errors.As(err, &serverErr) {
return err
}
if ev, ok := status.FromError(err); ok {
code := ev.Code()
switch code {
case codes.DeadlineExceeded:
fallthrough
case codes.Canceled:
if ctx.Err() != nil {
err = ctx.Err()
}
}
}
return err
}
func canceledByCaller(stopCtx context.Context, err error) bool {
if stopCtx.Err() == nil || err == nil {
return false
}
return errors.Is(err, context.Canceled) || errors.Is(err, context.DeadlineExceeded)
}
// IsConnCanceled returns true, if error is from a closed gRPC connection.
// ref. https://github.com/grpc/grpc-go/pull/1854
func IsConnCanceled(err error) bool {
if err == nil {
return false
}
// >= gRPC v1.23.x
s, ok := status.FromError(err)
if ok {
// connection is canceled or server has already closed the connection
return s.Code() == codes.Canceled || s.Message() == "transport is closing"
}
// >= gRPC v1.10.x
if errors.Is(err, context.Canceled) {
return true
}
// <= gRPC v1.7.x returns 'errors.New("grpc: the client connection is closing")'
return strings.Contains(err.Error(), "grpc: the client connection is closing")
}
================================================
FILE: client/v3/client_test.go
================================================
// Copyright 2016 The etcd Authors
//
// 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 clientv3
import (
"context"
"errors"
"io"
"net"
"sync"
"testing"
"time"
"github.com/coreos/go-semver/semver"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"go.uber.org/zap"
"go.uber.org/zap/zaptest"
"google.golang.org/grpc"
"google.golang.org/grpc/health"
healthpb "google.golang.org/grpc/health/grpc_health_v1"
"go.etcd.io/etcd/api/v3/etcdserverpb"
"go.etcd.io/etcd/api/v3/v3rpc/rpctypes"
"go.etcd.io/etcd/api/v3/version"
"go.etcd.io/etcd/client/pkg/v3/testutil"
)
func NewClient(t *testing.T, cfg Config) (*Client, error) {
t.Helper()
if cfg.Logger == nil {
cfg.Logger = zaptest.NewLogger(t).Named("client")
}
return New(cfg)
}
func TestDialNotImplemented(t *testing.T) {
testutil.RegisterLeakDetection(t)
ln, err := net.Listen("tcp", "127.0.0.1:0")
require.NoError(t, err)
defer ln.Close()
srv := grpc.NewServer()
serveDone := make(chan error)
go func() {
defer close(serveDone)
srv.Serve(ln)
}()
defer func() {
srv.Stop()
<-serveDone
}()
ep := ln.Addr().String()
cfg := Config{
Endpoints: []string{ep},
DialTimeout: 10 * time.Second,
}
c, err := NewClient(t, cfg)
require.NoError(t, err)
defer c.Close()
_, err = c.Get(t.Context(), "foo")
require.ErrorContains(t, err, "code = Unimplemented desc = unknown service etcdserverpb.KV")
}
func TestDialCancel(t *testing.T) {
testutil.RegisterLeakDetection(t)
// Start a real gRPC endpoint with health service so initial dial readiness
// check succeeds before switching endpoints below.
ln, err := net.Listen("tcp", "127.0.0.1:0")
require.NoError(t, err)
defer ln.Close()
srv := grpc.NewServer()
healthpb.RegisterHealthServer(srv, health.NewServer())
serveDone := make(chan error)
go func() {
defer close(serveDone)
srv.Serve(ln)
}()
defer func() {
srv.Stop()
<-serveDone
}()
ep := ln.Addr().String()
cfg := Config{
Endpoints: []string{ep},
DialTimeout: 30 * time.Second,
}
c, err := NewClient(t, cfg)
require.NoError(t, err)
// connect to ipv4 black hole so dial blocks
c.SetEndpoints("http://254.0.0.1:12345")
// issue Get to force redial attempts
getc := make(chan struct{})
go func() {
defer close(getc)
// Get may hang forever on grpc's Stream.Header() if its
// context is never canceled.
c.Get(c.Ctx(), "abc")
}()
// wait a little bit so client close is after dial starts
time.Sleep(100 * time.Millisecond)
donec := make(chan struct{})
go func() {
defer close(donec)
c.Close()
}()
select {
case <-time.After(5 * time.Second):
t.Fatalf("failed to close")
case <-donec:
}
select {
case <-time.After(5 * time.Second):
t.Fatalf("get failed to exit")
case <-getc:
}
}
func TestDialTimeout(t *testing.T) {
testutil.RegisterLeakDetection(t)
wantError := context.DeadlineExceeded
testCfgs := []Config{
{
Endpoints: []string{"http://254.0.0.1:12345"},
DialTimeout: 2 * time.Second,
},
{
Endpoints: []string{"http://254.0.0.1:12345"},
DialTimeout: time.Second,
Username: "abc",
Password: "def",
},
}
for i, cfg := range testCfgs {
donec := make(chan error, 1)
go func(cfg Config, i int) {
// without timeout, dial continues forever on ipv4 black hole
c, err := NewClient(t, cfg)
if c != nil || err == nil {
t.Errorf("#%d: new client should fail", i)
}
donec <- err
}(cfg, i)
time.Sleep(10 * time.Millisecond)
select {
case err := <-donec:
t.Errorf("#%d: dial didn't wait (%v)", i, err)
default:
}
select {
case <-time.After(5 * time.Second):
t.Errorf("#%d: failed to timeout dial on time", i)
case err := <-donec:
if !errors.Is(err, wantError) {
t.Errorf("#%d: unexpected error '%v', want '%v'", i, err, wantError)
}
}
}
}
func TestDialNoTimeout(t *testing.T) {
cfg := Config{Endpoints: []string{"127.0.0.1:12345"}}
c, err := NewClient(t, cfg)
require.NotNilf(t, c, "new client with DialNoWait should succeed, got %v", err)
require.NoErrorf(t, err, "new client with DialNoWait should succeed")
c.Close()
}
func TestMaxUnaryRetries(t *testing.T) {
maxUnaryRetries := uint(10)
cfg := Config{
Endpoints: []string{"127.0.0.1:12345"},
MaxUnaryRetries: maxUnaryRetries,
}
c, err := NewClient(t, cfg)
require.NoError(t, err)
require.NotNil(t, c)
defer c.Close()
require.Equal(t, maxUnaryRetries, c.cfg.MaxUnaryRetries)
}
func TestBackoff(t *testing.T) {
backoffWaitBetween := 100 * time.Millisecond
cfg := Config{
Endpoints: []string{"127.0.0.1:12345"},
BackoffWaitBetween: backoffWaitBetween,
}
c, err := NewClient(t, cfg)
require.NoError(t, err)
require.NotNil(t, c)
defer c.Close()
require.Equal(t, backoffWaitBetween, c.cfg.BackoffWaitBetween)
}
func TestBackoffJitterFraction(t *testing.T) {
backoffJitterFraction := float64(0.9)
cfg := Config{
Endpoints: []string{"127.0.0.1:12345"},
BackoffJitterFraction: backoffJitterFraction,
}
c, err := NewClient(t, cfg)
require.NoError(t, err)
require.NotNil(t, c)
defer c.Close()
require.InDelta(t, backoffJitterFraction, c.cfg.BackoffJitterFraction, 0.01)
}
func TestIsHaltErr(t *testing.T) {
assert.Truef(t,
isHaltErr(t.Context(), errors.New("etcdserver: some etcdserver error")),
"error created by errors.New should be unavailable error",
)
assert.Falsef(t,
isHaltErr(t.Context(), rpctypes.ErrGRPCStopped),
`error "%v" should not be halt error`, rpctypes.ErrGRPCStopped,
)
assert.Falsef(t,
isHaltErr(t.Context(), rpctypes.ErrGRPCNoLeader),
`error "%v" should not be halt error`, rpctypes.ErrGRPCNoLeader,
)
ctx, cancel := context.WithCancel(t.Context())
assert.Falsef(t,
isHaltErr(ctx, nil),
"no error and active context should be halt error",
)
cancel()
assert.Truef(t,
isHaltErr(ctx, nil),
"cancel on context should be halt error",
)
}
func TestIsUnavailableErr(t *testing.T) {
assert.Falsef(t,
isUnavailableErr(t.Context(), errors.New("etcdserver: some etcdserver error")),
"error created by errors.New should not be unavailable error",
)
assert.Truef(t,
isUnavailableErr(t.Context(), rpctypes.ErrGRPCStopped),
`error "%v" should be unavailable error`, rpctypes.ErrGRPCStopped,
)
assert.Falsef(t,
isUnavailableErr(t.Context(), rpctypes.ErrGRPCNotCapable),
"error %v should not be unavailable error", rpctypes.ErrGRPCNotCapable,
)
ctx, cancel := context.WithCancel(t.Context())
assert.Falsef(t,
isUnavailableErr(ctx, nil),
"no error and active context should not be unavailable error",
)
cancel()
assert.Falsef(t,
isUnavailableErr(ctx, nil),
"cancel on context should not be unavailable error",
)
}
func TestCloseCtxClient(t *testing.T) {
ctx := t.Context()
c := NewCtxClient(ctx)
err := c.Close()
// Close returns ctx.toErr, a nil error means an open Done channel
if err == nil {
t.Errorf("failed to Close the client. %v", err)
}
}
func TestWithLogger(t *testing.T) {
ctx := t.Context()
c := NewCtxClient(ctx)
if c.lg.Load() == nil {
t.Errorf("unexpected nil in *zap.Logger")
}
c.WithLogger(nil)
if c.GetLogger() != nil {
t.Errorf("WithLogger should modify *zap.Logger")
}
}
func TestZapWithLogger(t *testing.T) {
ctx := t.Context()
lg := zap.NewNop()
c := NewCtxClient(ctx, WithZapLogger(lg))
if c.GetLogger() != lg {
t.Errorf("WithZapLogger should modify *zap.Logger")
}
}
func TestAuthTokenBundleNoOverwrite(t *testing.T) {
// This call in particular changes working directory to the tmp dir of
// the test. The `etcd-auth-test:0` can be created in local directory,
// not exceeding the longest allowed path on OsX.
testutil.BeforeTest(t)
// Create a mock AuthServer to handle Authenticate RPCs.
lis, err := net.Listen("unix", "etcd-auth-test:0")
require.NoError(t, err)
defer lis.Close()
addr := "unix://" + lis.Addr().String()
srv := grpc.NewServer()
etcdserverpb.RegisterAuthServer(srv, mockAuthServer{})
go srv.Serve(lis)
defer srv.Stop()
// Create a client, which should call Authenticate on the mock server to
// exchange username/password for an auth token.
c, err := NewClient(t, Config{
DialTimeout: 5 * time.Second,
Endpoints: []string{addr},
Username: "foo",
Password: "bar",
})
require.NoError(t, err)
defer c.Close()
oldTokenBundle := c.authTokenBundle
// Call the public Dial again, which should preserve the original
// authTokenBundle.
gc, err := c.Dial(addr)
require.NoError(t, err)
defer gc.Close()
newTokenBundle := c.authTokenBundle
if oldTokenBundle != newTokenBundle {
t.Error("Client.authTokenBundle has been overwritten during Client.Dial")
}
}
func TestNewWithOnlyJWT(t *testing.T) {
// This call in particular changes working directory to the tmp dir of
// the test. The `etcd-auth-test:1` can be created in local directory,
// not exceeding the longest allowed path on OsX.
testutil.BeforeTest(t)
// Create a mock AuthServer to handle Authenticate RPCs.
lis, err := net.Listen("unix", "etcd-auth-test:1")
if err != nil {
t.Fatal(err)
}
defer lis.Close()
addr := "unix://" + lis.Addr().String()
srv := grpc.NewServer()
// Having a token removes the need to ever call Authenticate on the
// server. If that happens then this will cause a connection failure.
etcdserverpb.RegisterAuthServer(srv, mockFailingAuthServer{})
go srv.Serve(lis)
defer srv.Stop()
c, err := NewClient(t, Config{
DialTimeout: 5 * time.Second,
Endpoints: []string{addr},
Token: "foo",
})
if err != nil {
t.Fatal(err)
}
defer c.Close()
meta, err := c.authTokenBundle.PerRPCCredentials().GetRequestMetadata(t.Context(), "")
if err != nil {
t.Errorf("Error building request metadata: %s", err)
}
if tok, ok := meta[rpctypes.TokenFieldNameGRPC]; !ok {
t.Error("Token was not successfully set in the auth bundle")
} else if tok != "foo" {
t.Errorf("Incorrect token set in auth bundle, got '%s', expected 'foo'", tok)
}
}
func TestNewOnlyJWTExclusivity(t *testing.T) {
testutil.BeforeTest(t)
// Create a mock AuthServer to handle Authenticate RPCs.
lis, err := net.Listen("unix", "etcd-auth-test:1")
if err != nil {
t.Fatal(err)
}
defer lis.Close()
addr := "unix://" + lis.Addr().String()
srv := grpc.NewServer()
// Having a token removes the need to ever call Authenticate on the
// server. If that happens then this will cause a connection failure.
etcdserverpb.RegisterAuthServer(srv, mockFailingAuthServer{})
go srv.Serve(lis)
defer srv.Stop()
_, err = NewClient(t, Config{
DialTimeout: 5 * time.Second,
Endpoints: []string{addr},
Token: "foo",
Username: "user",
Password: "pass",
})
require.ErrorIs(t, ErrMutuallyExclusiveCfg, err)
}
func TestSyncFiltersMembers(t *testing.T) {
c, _ := NewClient(t, Config{Endpoints: []string{"http://254.0.0.1:12345"}})
defer c.Close()
c.Cluster = &mockCluster{
[]*etcdserverpb.Member{
{ID: 0, Name: "", ClientURLs: []string{"http://254.0.0.1:12345"}, IsLearner: false},
{ID: 1, Name: "isStarted", ClientURLs: []string{"http://254.0.0.2:12345"}, IsLearner: true},
{ID: 2, Name: "isStartedAndNotLearner", ClientURLs: []string{"http://254.0.0.3:12345"}, IsLearner: false},
},
}
c.Sync(t.Context())
endpoints := c.Endpoints()
if len(endpoints) != 1 || endpoints[0] != "http://254.0.0.3:12345" {
t.Error("Client.Sync uses learner and/or non-started member client URLs")
}
}
func TestMinSupportedVersion(t *testing.T) {
testutil.BeforeTest(t)
tests := []struct {
name string
currentVersion semver.Version
minSupportedVersion semver.Version
}{
{
name: "v3.6 client should accept v3.5",
currentVersion: version.V3_6,
minSupportedVersion: version.V3_5,
},
{
name: "v3.7 client should accept v3.6",
currentVersion: version.V3_7,
minSupportedVersion: version.V3_6,
},
{
name: "first minor version should accept its previous version",
currentVersion: version.V4_0,
minSupportedVersion: version.V3_7,
},
{
name: "first version in list should not accept previous versions",
currentVersion: version.V3_0,
minSupportedVersion: version.V3_0,
},
}
versionBackup := version.Version
t.Cleanup(func() {
version.Version = versionBackup
})
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
version.Version = tt.currentVersion.String()
require.True(t, minSupportedVersion().Equal(tt.minSupportedVersion))
})
}
}
func TestClientRejectOldCluster(t *testing.T) {
testutil.BeforeTest(t)
tests := []struct {
name string
endpoints []string
versions []string
expectedError error
}{
{
name: "all new versions with the same value",
endpoints: []string{"192.168.3.41:22379", "192.168.3.41:22479", "192.168.3.41:22579"},
versions: []string{version.Version, version.Version, version.Version},
expectedError: nil,
},
{
name: "all new versions with different values",
endpoints: []string{"192.168.3.41:22379", "192.168.3.41:22479", "192.168.3.41:22579"},
versions: []string{version.Version, minSupportedVersion().String(), minSupportedVersion().String()},
expectedError: nil,
},
{
name: "all old versions with different values",
endpoints: []string{"192.168.3.41:22379", "192.168.3.41:22479", "192.168.3.41:22579"},
versions: []string{"3.3.0", "3.3.0", "3.4.0"},
expectedError: ErrOldCluster,
},
{
name: "all old versions with the same value",
endpoints: []string{"192.168.3.41:22379", "192.168.3.41:22479", "192.168.3.41:22579"},
versions: []string{"3.3.0", "3.3.0", "3.3.0"},
expectedError: ErrOldCluster,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if len(tt.endpoints) != len(tt.versions) || len(tt.endpoints) == 0 {
t.Errorf("Unexpected endpoints and versions length, len(endpoints):%d, len(versions):%d", len(tt.endpoints), len(tt.versions))
return
}
endpointToVersion := make(map[string]string)
for j := range tt.endpoints {
endpointToVersion[tt.endpoints[j]] = tt.versions[j]
}
c := &Client{
ctx: t.Context(),
endpoints: tt.endpoints,
epMu: new(sync.RWMutex),
Maintenance: &mockMaintenance{
Version: endpointToVersion,
},
}
if err := c.checkVersion(); !errors.Is(err, tt.expectedError) {
t.Errorf("checkVersion err:%v", err)
}
})
}
}
type mockMaintenance struct {
Version map[string]string
}
func (mm mockMaintenance) Status(ctx context.Context, endpoint string) (*StatusResponse, error) {
return &StatusResponse{Version: mm.Version[endpoint]}, nil
}
func (mm mockMaintenance) AlarmList(ctx context.Context) (*AlarmResponse, error) {
return nil, nil
}
func (mm mockMaintenance) AlarmDisarm(ctx context.Context, m *AlarmMember) (*AlarmResponse, error) {
return nil, nil
}
func (mm mockMaintenance) Defragment(ctx context.Context, endpoint string) (*DefragmentResponse, error) {
return nil, nil
}
func (mm mockMaintenance) HashKV(ctx context.Context, endpoint string, rev int64) (*HashKVResponse, error) {
return nil, nil
}
func (mm mockMaintenance) SnapshotWithVersion(ctx context.Context) (*SnapshotResponse, error) {
return nil, nil
}
func (mm mockMaintenance) Snapshot(ctx context.Context) (io.ReadCloser, error) {
return nil, nil
}
func (mm mockMaintenance) MoveLeader(ctx context.Context, transfereeID uint64) (*MoveLeaderResponse, error) {
return nil, nil
}
func (mm mockMaintenance) Downgrade(ctx context.Context, action DowngradeAction, version string) (*DowngradeResponse, error) {
return nil, nil
}
type mockFailingAuthServer struct {
etcdserverpb.UnimplementedAuthServer
}
func (mockFailingAuthServer) Authenticate(context.Context, *etcdserverpb.AuthenticateRequest) (*etcdserverpb.AuthenticateResponse, error) {
return nil, errors.New("this auth server always fails")
}
type mockAuthServer struct {
etcdserverpb.UnimplementedAuthServer
}
func (mockAuthServer) Authenticate(context.Context, *etcdserverpb.AuthenticateRequest) (*etcdserverpb.AuthenticateResponse, error) {
return &etcdserverpb.AuthenticateResponse{Token: "mock-token"}, nil
}
type mockCluster struct {
members []*etcdserverpb.Member
}
func (mc *mockCluster) MemberList(ctx context.Context, opts ...OpOption) (*MemberListResponse, error) {
return &MemberListResponse{Members: mc.members}, nil
}
func (mc *mockCluster) MemberAdd(ctx context.Context, peerAddrs []string) (*MemberAddResponse, error) {
return nil, nil
}
func (mc *mockCluster) MemberAddAsLearner(ctx context.Context, peerAddrs []string) (*MemberAddResponse, error) {
return nil, nil
}
func (mc *mockCluster) MemberRemove(ctx context.Context, id uint64) (*MemberRemoveResponse, error) {
return nil, nil
}
func (mc *mockCluster) MemberUpdate(ctx context.Context, id uint64, peerAddrs []string) (*MemberUpdateResponse, error) {
return nil, nil
}
func (mc *mockCluster) MemberPromote(ctx context.Context, id uint64) (*MemberPromoteResponse, error) {
return nil, nil
}
================================================
FILE: client/v3/clientv3util/example_key_test.go
================================================
// Copyright 2017 The etcd Authors
//
// 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 clientv3util_test
import (
"context"
"log"
clientv3 "go.etcd.io/etcd/client/v3"
"go.etcd.io/etcd/client/v3/clientv3util"
)
func ExampleKeyMissing() {
cli, err := clientv3.New(clientv3.Config{
Endpoints: []string{"127.0.0.1:2379"},
})
if err != nil {
log.Fatal(err)
}
defer cli.Close()
kvc := clientv3.NewKV(cli)
// perform a put only if key is missing
// It is useful to do the check atomically to avoid overwriting
// the existing key which would generate potentially unwanted events,
// unless of course you wanted to do an overwrite no matter what.
_, err = kvc.Txn(context.Background()).
If(clientv3util.KeyMissing("purpleidea")).
Then(clientv3.OpPut("purpleidea", "hello world")).
Commit()
if err != nil {
log.Fatal(err)
}
}
func ExampleKeyExists() {
cli, err := clientv3.New(clientv3.Config{
Endpoints: []string{"127.0.0.1:2379"},
})
if err != nil {
log.Fatal(err)
}
defer cli.Close()
kvc := clientv3.NewKV(cli)
// perform a delete only if key already exists
_, err = kvc.Txn(context.Background()).
If(clientv3util.KeyExists("purpleidea")).
Then(clientv3.OpDelete("purpleidea")).
Commit()
if err != nil {
log.Fatal(err)
}
}
================================================
FILE: client/v3/clientv3util/util.go
================================================
// Copyright 2017 The etcd Authors
//
// 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 clientv3util contains utility functions derived from clientv3.
package clientv3util
import (
clientv3 "go.etcd.io/etcd/client/v3"
)
// KeyExists returns a comparison operation that evaluates to true iff the given
// key exists. It does this by checking if the key `Version` is greater than 0.
// It is a useful guard in transaction delete operations.
func KeyExists(key string) clientv3.Cmp {
return clientv3.Compare(clientv3.Version(key), ">", 0)
}
// KeyMissing returns a comparison operation that evaluates to true iff the
// given key does not exist.
func KeyMissing(key string) clientv3.Cmp {
return clientv3.Compare(clientv3.Version(key), "=", 0)
}
================================================
FILE: client/v3/cluster.go
================================================
// Copyright 2016 The etcd Authors
//
// 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 clientv3
import (
"context"
"google.golang.org/grpc"
pb "go.etcd.io/etcd/api/v3/etcdserverpb"
"go.etcd.io/etcd/client/pkg/v3/types"
)
type (
Member pb.Member
MemberListResponse pb.MemberListResponse
MemberAddResponse pb.MemberAddResponse
MemberRemoveResponse pb.MemberRemoveResponse
MemberUpdateResponse pb.MemberUpdateResponse
MemberPromoteResponse pb.MemberPromoteResponse
)
type Cluster interface {
// MemberList lists the current cluster membership.
MemberList(ctx context.Context, opts ...OpOption) (*MemberListResponse, error)
// MemberAdd adds a new member into the cluster.
MemberAdd(ctx context.Context, peerAddrs []string) (*MemberAddResponse, error)
// MemberAddAsLearner adds a new learner member into the cluster.
MemberAddAsLearner(ctx context.Context, peerAddrs []string) (*MemberAddResponse, error)
// MemberRemove removes an existing member from the cluster.
MemberRemove(ctx context.Context, id uint64) (*MemberRemoveResponse, error)
// MemberUpdate updates the peer addresses of the member.
MemberUpdate(ctx context.Context, id uint64, peerAddrs []string) (*MemberUpdateResponse, error)
// MemberPromote promotes a member from raft learner (non-voting) to raft voting member.
MemberPromote(ctx context.Context, id uint64) (*MemberPromoteResponse, error)
}
type cluster struct {
remote pb.ClusterClient
callOpts []grpc.CallOption
}
func NewCluster(c *Client) Cluster {
api := &cluster{remote: RetryClusterClient(c)}
if c != nil {
api.callOpts = c.callOpts
}
return api
}
func NewClusterFromClusterClient(remote pb.ClusterClient, c *Client) Cluster {
api := &cluster{remote: remote}
if c != nil {
api.callOpts = c.callOpts
}
return api
}
func (c *cluster) MemberAdd(ctx context.Context, peerAddrs []string) (*MemberAddResponse, error) {
return c.memberAdd(ctx, peerAddrs, false)
}
func (c *cluster) MemberAddAsLearner(ctx context.Context, peerAddrs []string) (*MemberAddResponse, error) {
return c.memberAdd(ctx, peerAddrs, true)
}
func (c *cluster) memberAdd(ctx context.Context, peerAddrs []string, isLearner bool) (*MemberAddResponse, error) {
// fail-fast before panic in rafthttp
if _, err := types.NewURLs(peerAddrs); err != nil {
return nil, err
}
r := &pb.MemberAddRequest{
PeerURLs: peerAddrs,
IsLearner: isLearner,
}
resp, err := c.remote.MemberAdd(ctx, r, c.callOpts...)
if err != nil {
return nil, ContextError(ctx, err)
}
return (*MemberAddResponse)(resp), nil
}
func (c *cluster) MemberRemove(ctx context.Context, id uint64) (*MemberRemoveResponse, error) {
r := &pb.MemberRemoveRequest{ID: id}
resp, err := c.remote.MemberRemove(ctx, r, c.callOpts...)
if err != nil {
return nil, ContextError(ctx, err)
}
return (*MemberRemoveResponse)(resp), nil
}
func (c *cluster) MemberUpdate(ctx context.Context, id uint64, peerAddrs []string) (*MemberUpdateResponse, error) {
// fail-fast before panic in rafthttp
if _, err := types.NewURLs(peerAddrs); err != nil {
return nil, err
}
// it is safe to retry on update.
r := &pb.MemberUpdateRequest{ID: id, PeerURLs: peerAddrs}
resp, err := c.remote.MemberUpdate(ctx, r, c.callOpts...)
if err == nil {
return (*MemberUpdateResponse)(resp), nil
}
return nil, ContextError(ctx, err)
}
func (c *cluster) MemberList(ctx context.Context, opts ...OpOption) (*MemberListResponse, error) {
opt := OpGet("", opts...)
resp, err := c.remote.MemberList(ctx, &pb.MemberListRequest{Linearizable: !opt.serializable}, c.callOpts...)
if err == nil {
return (*MemberListResponse)(resp), nil
}
return nil, ContextError(ctx, err)
}
func (c *cluster) MemberPromote(ctx context.Context, id uint64) (*MemberPromoteResponse, error) {
r := &pb.MemberPromoteRequest{ID: id}
resp, err := c.remote.MemberPromote(ctx, r, c.callOpts...)
if err != nil {
return nil, ContextError(ctx, err)
}
return (*MemberPromoteResponse)(resp), nil
}
================================================
FILE: client/v3/compact_op.go
================================================
// Copyright 2016 The etcd Authors
//
// 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 clientv3
import (
pb "go.etcd.io/etcd/api/v3/etcdserverpb"
)
// CompactOp represents a compact operation.
type CompactOp struct {
revision int64
physical bool
}
// CompactOption configures compact operation.
type CompactOption func(*CompactOp)
func (op *CompactOp) applyCompactOpts(opts []CompactOption) {
for _, opt := range opts {
opt(op)
}
}
// OpCompact wraps slice CompactOption to create a CompactOp.
func OpCompact(rev int64, opts ...CompactOption) CompactOp {
ret := CompactOp{revision: rev}
ret.applyCompactOpts(opts)
return ret
}
func (op CompactOp) toRequest() *pb.CompactionRequest {
return &pb.CompactionRequest{Revision: op.revision, Physical: op.physical}
}
// WithCompactPhysical makes Compact wait until all compacted entries are
// removed from the etcd server's storage.
func WithCompactPhysical() CompactOption {
return func(op *CompactOp) { op.physical = true }
}
================================================
FILE: client/v3/compact_op_test.go
================================================
// Copyright 2016 The etcd Authors
//
// 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 clientv3
import (
"reflect"
"testing"
"github.com/stretchr/testify/require"
"go.etcd.io/etcd/api/v3/etcdserverpb"
)
func TestCompactOp(t *testing.T) {
req1 := OpCompact(100, WithCompactPhysical()).toRequest()
req2 := &etcdserverpb.CompactionRequest{Revision: 100, Physical: true}
require.Truef(t, reflect.DeepEqual(req1, req2), "expected %+v, got %+v", req2, req1)
}
================================================
FILE: client/v3/compare.go
================================================
// Copyright 2016 The etcd Authors
//
// 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 clientv3
import (
pb "go.etcd.io/etcd/api/v3/etcdserverpb"
)
type (
CompareTarget int
CompareResult int
)
const (
CompareVersion CompareTarget = iota
CompareCreated
CompareModified
CompareValue
)
type Cmp pb.Compare
func Compare(cmp Cmp, result string, v any) Cmp {
var r pb.Compare_CompareResult
switch result {
case "=":
r = pb.Compare_EQUAL
case "!=":
r = pb.Compare_NOT_EQUAL
case ">":
r = pb.Compare_GREATER
case "<":
r = pb.Compare_LESS
default:
panic("Unknown result op")
}
cmp.Result = r
switch cmp.Target {
case pb.Compare_VALUE:
val, ok := v.(string)
if !ok {
panic("bad compare value")
}
cmp.TargetUnion = &pb.Compare_Value{Value: []byte(val)}
case pb.Compare_VERSION:
cmp.TargetUnion = &pb.Compare_Version{Version: mustInt64(v)}
case pb.Compare_CREATE:
cmp.TargetUnion = &pb.Compare_CreateRevision{CreateRevision: mustInt64(v)}
case pb.Compare_MOD:
cmp.TargetUnion = &pb.Compare_ModRevision{ModRevision: mustInt64(v)}
case pb.Compare_LEASE:
cmp.TargetUnion = &pb.Compare_Lease{Lease: mustInt64orLeaseID(v)}
default:
panic("Unknown compare type")
}
return cmp
}
func Value(key string) Cmp {
return Cmp{Key: []byte(key), Target: pb.Compare_VALUE}
}
func Version(key string) Cmp {
return Cmp{Key: []byte(key), Target: pb.Compare_VERSION}
}
func CreateRevision(key string) Cmp {
return Cmp{Key: []byte(key), Target: pb.Compare_CREATE}
}
func ModRevision(key string) Cmp {
return Cmp{Key: []byte(key), Target: pb.Compare_MOD}
}
// LeaseValue compares a key's LeaseID to a value of your choosing. The empty
// LeaseID is 0, otherwise known as `NoLease`.
func LeaseValue(key string) Cmp {
return Cmp{Key: []byte(key), Target: pb.Compare_LEASE}
}
// KeyBytes returns the byte slice holding with the comparison key.
func (cmp *Cmp) KeyBytes() []byte { return cmp.Key }
// WithKeyBytes sets the byte slice for the comparison key.
func (cmp *Cmp) WithKeyBytes(key []byte) { cmp.Key = key }
// ValueBytes returns the byte slice holding the comparison value, if any.
func (cmp *Cmp) ValueBytes() []byte {
if tu, ok := cmp.TargetUnion.(*pb.Compare_Value); ok {
return tu.Value
}
return nil
}
// WithValueBytes sets the byte slice for the comparison's value.
func (cmp *Cmp) WithValueBytes(v []byte) { cmp.TargetUnion.(*pb.Compare_Value).Value = v }
// WithRange sets the comparison to scan the range [key, end).
func (cmp Cmp) WithRange(end string) Cmp {
cmp.RangeEnd = []byte(end)
return cmp
}
// WithPrefix sets the comparison to scan all keys prefixed by the key.
func (cmp Cmp) WithPrefix() Cmp {
cmp.RangeEnd = getPrefix(cmp.Key)
return cmp
}
// mustInt64 panics if val isn't an int or int64. It returns an int64 otherwise.
func mustInt64(val any) int64 {
if v, ok := val.(int64); ok {
return v
}
if v, ok := val.(int); ok {
return int64(v)
}
panic("bad value")
}
// mustInt64orLeaseID panics if val isn't a LeaseID, int or int64. It returns an
// int64 otherwise.
func mustInt64orLeaseID(val any) int64 {
if v, ok := val.(LeaseID); ok {
return int64(v)
}
return mustInt64(val)
}
================================================
FILE: client/v3/concurrency/doc.go
================================================
// Copyright 2016 The etcd Authors
//
// 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 concurrency implements concurrency operations on top of
// etcd such as distributed locks, barriers, and elections.
package concurrency
================================================
FILE: client/v3/concurrency/election.go
================================================
// Copyright 2016 The etcd Authors
//
// 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 concurrency
import (
"context"
"errors"
"fmt"
pb "go.etcd.io/etcd/api/v3/etcdserverpb"
"go.etcd.io/etcd/api/v3/mvccpb"
v3 "go.etcd.io/etcd/client/v3"
)
var (
ErrElectionNotLeader = errors.New("election: not leader")
ErrElectionNoLeader = errors.New("election: no leader")
)
type Election struct {
session *Session
keyPrefix string
leaderKey string
leaderRev int64
leaderSession *Session
hdr *pb.ResponseHeader
}
// NewElection returns a new election on a given key prefix.
func NewElection(s *Session, pfx string) *Election {
return &Election{session: s, keyPrefix: pfx + "/"}
}
// ResumeElection initializes an election with a known leader.
func ResumeElection(s *Session, pfx string, leaderKey string, leaderRev int64) *Election {
return &Election{
keyPrefix: pfx,
session: s,
leaderKey: leaderKey,
leaderRev: leaderRev,
leaderSession: s,
}
}
// Campaign puts a value as eligible for the election on the prefix
// key.
// Multiple sessions can participate in the election for the
// same prefix, but only one can be the leader at a time.
//
// If the context is 'context.TODO()/context.Background()', the Campaign
// will continue to be blocked for other keys to be deleted, unless server
// returns a non-recoverable error (e.g. ErrCompacted).
// Otherwise, until the context is not cancelled or timed-out, Campaign will
// continue to be blocked until it becomes the leader.
func (e *Election) Campaign(ctx context.Context, val string) error {
s := e.session
client := e.session.Client()
k := fmt.Sprintf("%s%x", e.keyPrefix, s.Lease())
txn := client.Txn(ctx).If(v3.Compare(v3.CreateRevision(k), "=", 0))
txn = txn.Then(v3.OpPut(k, val, v3.WithLease(s.Lease())))
txn = txn.Else(v3.OpGet(k))
resp, err := txn.Commit()
if err != nil {
return err
}
e.leaderKey, e.leaderRev, e.leaderSession = k, resp.Header.Revision, s
if !resp.Succeeded {
kv := resp.Responses[0].GetResponseRange().Kvs[0]
e.leaderRev = kv.CreateRevision
if string(kv.Value) != val {
if err = e.Proclaim(ctx, val); err != nil {
e.Resign(ctx)
return err
}
}
}
err = waitDeletes(ctx, client, e.keyPrefix, e.leaderRev-1)
if err != nil {
// clean up in case of context cancel
select {
case <-ctx.Done():
e.Resign(client.Ctx())
default:
e.leaderSession = nil
}
return err
}
e.hdr = resp.Header
return nil
}
// Proclaim lets the leader announce a new value without another election.
func (e *Election) Proclaim(ctx context.Context, val string) error {
if e.leaderSession == nil {
return ErrElectionNotLeader
}
client := e.session.Client()
cmp := v3.Compare(v3.CreateRevision(e.leaderKey), "=", e.leaderRev)
txn := client.Txn(ctx).If(cmp)
txn = txn.Then(v3.OpPut(e.leaderKey, val, v3.WithLease(e.leaderSession.Lease())))
tresp, terr := txn.Commit()
if terr != nil {
return terr
}
if !tresp.Succeeded {
e.leaderKey = ""
return ErrElectionNotLeader
}
e.hdr = tresp.Header
return nil
}
// Resign lets a leader start a new election.
func (e *Election) Resign(ctx context.Context) (err error) {
if e.leaderSession == nil {
return nil
}
client := e.session.Client()
cmp := v3.Compare(v3.CreateRevision(e.leaderKey), "=", e.leaderRev)
resp, err := client.Txn(ctx).If(cmp).Then(v3.OpDelete(e.leaderKey)).Commit()
if err == nil {
e.hdr = resp.Header
}
e.leaderKey = ""
e.leaderSession = nil
return err
}
// Leader returns the leader value for the current election.
func (e *Election) Leader(ctx context.Context) (*v3.GetResponse, error) {
client := e.session.Client()
resp, err := client.Get(ctx, e.keyPrefix, v3.WithFirstCreate()...)
if err != nil {
return nil, err
} else if len(resp.Kvs) == 0 {
// no leader currently elected
return nil, ErrElectionNoLeader
}
return resp, nil
}
// Observe returns a channel that reliably observes ordered leader proposals
// as GetResponse values on every current elected leader key. It will not
// necessarily fetch all historical leader updates, but will always post the
// most recent leader value.
//
// The channel closes when the context is canceled or the underlying watcher
// is otherwise disrupted.
func (e *Election) Observe(ctx context.Context) <-chan v3.GetResponse {
retc := make(chan v3.GetResponse)
go e.observe(ctx, retc)
return retc
}
func (e *Election) observe(ctx context.Context, ch chan<- v3.GetResponse) {
client := e.session.Client()
defer close(ch)
for {
resp, err := client.Get(ctx, e.keyPrefix, v3.WithFirstCreate()...)
if err != nil {
return
}
var kv *mvccpb.KeyValue
var hdr *pb.ResponseHeader
if len(resp.Kvs) == 0 {
cctx, cancel := context.WithCancel(ctx)
// wait for first key put on prefix
opts := []v3.OpOption{v3.WithRev(resp.Header.Revision), v3.WithPrefix()}
wch := client.Watch(cctx, e.keyPrefix, opts...)
for kv == nil {
wr, ok := <-wch
if !ok || wr.Err() != nil {
cancel()
return
}
// only accept puts; a delete will make observe() spin
for _, ev := range wr.Events {
if ev.Type == mvccpb.Event_PUT {
hdr, kv = &wr.Header, ev.Kv
// may have multiple revs; hdr.rev = the last rev
// set to kv's rev in case batch has multiple Puts
hdr.Revision = kv.ModRevision
break
}
}
}
cancel()
} else {
hdr, kv = resp.Header, resp.Kvs[0]
}
select {
case ch <- v3.GetResponse{Header: hdr, Kvs: []*mvccpb.KeyValue{kv}}:
case <-ctx.Done():
return
}
cctx, cancel := context.WithCancel(ctx)
wch := client.Watch(cctx, string(kv.Key), v3.WithRev(hdr.Revision+1))
keyDeleted := false
for !keyDeleted {
wr, ok := <-wch
if !ok {
cancel()
return
}
for _, ev := range wr.Events {
if ev.Type == mvccpb.Event_DELETE {
keyDeleted = true
break
}
resp.Header = &wr.Header
resp.Kvs = []*mvccpb.KeyValue{ev.Kv}
select {
case ch <- *resp:
case <-cctx.Done():
cancel()
return
}
}
}
cancel()
}
}
// Key returns the leader key if elected, empty string otherwise.
func (e *Election) Key() string { return e.leaderKey }
// Rev returns the leader key's creation revision, if elected.
func (e *Election) Rev() int64 { return e.leaderRev }
// Header is the response header from the last successful election proposal.
func (e *Election) Header() *pb.ResponseHeader { return e.hdr }
================================================
FILE: client/v3/concurrency/key.go
================================================
// Copyright 2016 The etcd Authors
//
// 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 concurrency
import (
"context"
"errors"
"go.etcd.io/etcd/api/v3/mvccpb"
v3 "go.etcd.io/etcd/client/v3"
)
func waitDelete(ctx context.Context, client *v3.Client, key string, rev int64) error {
cctx, cancel := context.WithCancel(ctx)
defer cancel()
var wr v3.WatchResponse
wch := client.Watch(cctx, key, v3.WithRev(rev))
for wr = range wch {
for _, ev := range wr.Events {
if ev.Type == mvccpb.Event_DELETE {
return nil
}
}
}
if err := wr.Err(); err != nil {
return err
}
if err := ctx.Err(); err != nil {
return err
}
return errors.New("lost watcher waiting for delete")
}
// waitDeletes efficiently waits until all keys matching the prefix and no greater
// than the create revision are deleted.
func waitDeletes(ctx context.Context, client *v3.Client, pfx string, maxCreateRev int64) error {
getOpts := append(v3.WithLastCreate(), v3.WithMaxCreateRev(maxCreateRev))
for {
resp, err := client.Get(ctx, pfx, getOpts...)
if err != nil {
return err
}
if len(resp.Kvs) == 0 {
return nil
}
lastKey := string(resp.Kvs[0].Key)
if err = waitDelete(ctx, client, lastKey, resp.Header.Revision); err != nil {
return err
}
}
}
================================================
FILE: client/v3/concurrency/main_test.go
================================================
// Copyright 2017 The etcd Authors
//
// 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 concurrency_test
import (
"testing"
"go.etcd.io/etcd/client/pkg/v3/testutil"
)
func exampleEndpoints() []string { return nil }
func forUnitTestsRunInMockedContext(mocking func(), _example func()) {
mocking()
// TODO: Call 'example' when mocking() provides realistic mocking of transport.
// The real testing logic of examples gets executed
// as part of ./tests/integration/clientv3/concurrency/...
}
func TestMain(m *testing.M) {
testutil.MustTestMainWithLeakDetection(m)
}
================================================
FILE: client/v3/concurrency/mutex.go
================================================
// Copyright 2016 The etcd Authors
//
// 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 concurrency
import (
"context"
"errors"
"fmt"
"strings"
"sync"
pb "go.etcd.io/etcd/api/v3/etcdserverpb"
v3 "go.etcd.io/etcd/client/v3"
)
// ErrLocked is returned by TryLock when Mutex is already locked by another session.
var (
ErrLocked = errors.New("mutex: Locked by another session")
ErrSessionExpired = errors.New("mutex: session is expired")
ErrLockReleased = errors.New("mutex: lock has already been released")
)
// Mutex implements the sync Locker interface with etcd
type Mutex struct {
s *Session
pfx string
myKey string
myRev int64
hdr *pb.ResponseHeader
}
func NewMutex(s *Session, pfx string) *Mutex {
return &Mutex{s, pfx + "/", "", -1, nil}
}
// TryLock locks the mutex if not already locked by another session.
// If lock is held by another session, return immediately after attempting necessary cleanup
// The ctx argument is used for the sending/receiving Txn RPC.
func (m *Mutex) TryLock(ctx context.Context) error {
resp, err := m.tryAcquire(ctx)
if err != nil {
return err
}
// if no key on prefix / the minimum rev is key, already hold the lock
ownerKey := resp.Responses[1].GetResponseRange().Kvs
if len(ownerKey) == 0 || ownerKey[0].CreateRevision == m.myRev {
m.hdr = resp.Header
return nil
}
client := m.s.Client()
// Cannot lock, so delete the key
if _, err := client.Delete(ctx, m.myKey); err != nil {
return err
}
m.myKey = "\x00"
m.myRev = -1
return ErrLocked
}
// Lock locks the mutex with a cancelable context. If the context is canceled
// while trying to acquire the lock, the mutex tries to clean its stale lock entry.
func (m *Mutex) Lock(ctx context.Context) error {
resp, err := m.tryAcquire(ctx)
if err != nil {
return err
}
// if no key on prefix / the minimum rev is key, already hold the lock
ownerKey := resp.Responses[1].GetResponseRange().Kvs
if len(ownerKey) == 0 || ownerKey[0].CreateRevision == m.myRev {
m.hdr = resp.Header
return nil
}
client := m.s.Client()
// wait for deletion revisions prior to myKey
// TODO: early termination if the session key is deleted before other session keys with smaller revisions.
werr := waitDeletes(ctx, client, m.pfx, m.myRev-1)
// release lock key if wait failed
if werr != nil {
m.Unlock(client.Ctx())
return werr
}
// make sure the session is not expired, and the owner key still exists.
gresp, werr := client.Get(ctx, m.myKey)
if werr != nil {
m.Unlock(client.Ctx())
return werr
}
if len(gresp.Kvs) == 0 { // is the session key lost?
return ErrSessionExpired
}
m.hdr = gresp.Header
return nil
}
func (m *Mutex) tryAcquire(ctx context.Context) (*v3.TxnResponse, error) {
s := m.s
client := m.s.Client()
m.myKey = fmt.Sprintf("%s%x", m.pfx, s.Lease())
cmp := v3.Compare(v3.CreateRevision(m.myKey), "=", 0)
// put self in lock waiters via myKey; oldest waiter holds lock
put := v3.OpPut(m.myKey, "", v3.WithLease(s.Lease()))
// reuse key in case this session already holds the lock
get := v3.OpGet(m.myKey)
// fetch current holder to complete uncontended path with only one RPC
getOwner := v3.OpGet(m.pfx, v3.WithFirstCreate()...)
resp, err := client.Txn(ctx).If(cmp).Then(put, getOwner).Else(get, getOwner).Commit()
if err != nil {
return nil, err
}
m.myRev = resp.Header.Revision
if !resp.Succeeded {
m.myRev = resp.Responses[0].GetResponseRange().Kvs[0].CreateRevision
}
return resp, nil
}
func (m *Mutex) Unlock(ctx context.Context) error {
if m.myKey == "" || m.myRev <= 0 || m.myKey == "\x00" {
return ErrLockReleased
}
if !strings.HasPrefix(m.myKey, m.pfx) {
return fmt.Errorf("invalid key %q, it should have prefix %q", m.myKey, m.pfx)
}
client := m.s.Client()
if _, err := client.Delete(ctx, m.myKey); err != nil {
return err
}
m.myKey = "\x00"
m.myRev = -1
return nil
}
func (m *Mutex) IsOwner() v3.Cmp {
return v3.Compare(v3.CreateRevision(m.myKey), "=", m.myRev)
}
func (m *Mutex) Key() string { return m.myKey }
// Header is the response header received from etcd on acquiring the lock.
func (m *Mutex) Header() *pb.ResponseHeader { return m.hdr }
type lockerMutex struct{ *Mutex }
func (lm *lockerMutex) Lock() {
client := lm.s.Client()
if err := lm.Mutex.Lock(client.Ctx()); err != nil {
panic(err)
}
}
func (lm *lockerMutex) Unlock() {
client := lm.s.Client()
if err := lm.Mutex.Unlock(client.Ctx()); err != nil {
panic(err)
}
}
// NewLocker creates a sync.Locker backed by an etcd mutex.
func NewLocker(s *Session, pfx string) sync.Locker {
return &lockerMutex{NewMutex(s, pfx)}
}
================================================
FILE: client/v3/concurrency/session.go
================================================
// Copyright 2016 The etcd Authors
//
// 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 concurrency
import (
"context"
"time"
"go.uber.org/zap"
v3 "go.etcd.io/etcd/client/v3"
)
const defaultSessionTTL = 60
// Session represents a lease kept alive for the lifetime of a client.
// Fault-tolerant applications may use sessions to reason about liveness.
type Session struct {
client *v3.Client
opts *sessionOptions
id v3.LeaseID
ctx context.Context
cancel context.CancelFunc
donec <-chan struct{}
}
// NewSession gets the leased session for a client.
func NewSession(client *v3.Client, opts ...SessionOption) (*Session, error) {
lg := client.GetLogger()
ops := &sessionOptions{ttl: defaultSessionTTL, ctx: client.Ctx()}
for _, opt := range opts {
opt(ops, lg)
}
id := ops.leaseID
if id == v3.NoLease {
resp, err := client.Grant(ops.ctx, int64(ops.ttl))
if err != nil {
return nil, err
}
id = resp.ID
}
ctx, cancel := context.WithCancel(ops.ctx)
keepAlive, err := client.KeepAlive(ctx, id)
if err != nil || keepAlive == nil {
cancel()
return nil, err
}
donec := make(chan struct{})
s := &Session{client: client, opts: ops, id: id, ctx: ctx, cancel: cancel, donec: donec}
// keep the lease alive until client error or cancelled context
go func() {
defer func() {
close(donec)
cancel()
}()
for range keepAlive {
// eat messages until keep alive channel closes
}
}()
return s, nil
}
// Client is the etcd client that is attached to the session.
func (s *Session) Client() *v3.Client {
return s.client
}
// Lease is the lease ID for keys bound to the session.
func (s *Session) Lease() v3.LeaseID { return s.id }
// Ctx is the context attached to the session, it is canceled when the lease is orphaned, expires, or
// is otherwise no longer being refreshed.
func (s *Session) Ctx() context.Context {
return s.ctx
}
// Done returns a channel that closes when the lease is orphaned, expires, or
// is otherwise no longer being refreshed.
func (s *Session) Done() <-chan struct{} { return s.donec }
// Orphan ends the refresh for the session lease. This is useful
// in case the state of the client connection is indeterminate (revoke
// would fail) or when transferring lease ownership.
func (s *Session) Orphan() {
s.cancel()
<-s.donec
}
// Close orphans the session and revokes the session lease.
func (s *Session) Close() error {
s.Orphan()
// if revoke takes longer than the ttl, lease is expired anyway
ctx, cancel := context.WithTimeout(s.opts.ctx, time.Duration(s.opts.ttl)*time.Second)
_, err := s.client.Revoke(ctx, s.id)
cancel()
return err
}
type sessionOptions struct {
ttl int
leaseID v3.LeaseID
ctx context.Context
}
// SessionOption configures Session.
type SessionOption func(*sessionOptions, *zap.Logger)
// WithTTL configures the session's TTL in seconds.
// If TTL is <= 0, the default 60 seconds TTL will be used.
func WithTTL(ttl int) SessionOption {
return func(so *sessionOptions, lg *zap.Logger) {
if ttl > 0 {
so.ttl = ttl
} else {
lg.Warn("WithTTL(): TTL should be > 0, preserving current TTL", zap.Int64("current-session-ttl", int64(so.ttl)))
}
}
}
// WithLease specifies the existing leaseID to be used for the session.
// This is useful in process restart scenario, for example, to reclaim
// leadership from an election prior to restart.
func WithLease(leaseID v3.LeaseID) SessionOption {
return func(so *sessionOptions, _ *zap.Logger) {
so.leaseID = leaseID
}
}
// WithContext assigns a context to the session instead of defaulting to
// using the client context. This is useful for canceling NewSession and
// Close operations immediately without having to close the client. If the
// context is canceled before Close() completes, the session's lease will be
// abandoned and left to expire instead of being revoked.
func WithContext(ctx context.Context) SessionOption {
return func(so *sessionOptions, _ *zap.Logger) {
so.ctx = ctx
}
}
================================================
FILE: client/v3/concurrency/stm.go
================================================
// Copyright 2016 The etcd Authors
//
// 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 concurrency
import (
"context"
"math"
v3 "go.etcd.io/etcd/client/v3"
)
// STM is an interface for software transactional memory.
type STM interface {
// Get returns the value for a key and inserts the key in the txn's read set.
// If Get fails, it aborts the transaction with an error, never returning.
Get(key ...string) string
// Put adds a value for a key to the write set.
Put(key, val string, opts ...v3.OpOption)
// Rev returns the revision of a key in the read set.
Rev(key string) int64
// Del deletes a key.
Del(key string)
// commit attempts to apply the txn's changes to the server.
commit() *v3.TxnResponse
reset()
}
// Isolation is an enumeration of transactional isolation levels which
// describes how transactions should interfere and conflict.
type Isolation int
const (
// SerializableSnapshot provides serializable isolation and also checks
// for write conflicts.
SerializableSnapshot Isolation = iota
// Serializable reads within the same transaction attempt return data
// from the revision of the first read.
Serializable
// RepeatableReads reads within the same transaction attempt always
// return the same data.
RepeatableReads
// ReadCommitted reads keys from any committed revision.
ReadCommitted
)
// stmError safely passes STM errors through panic to the STM error channel.
type stmError struct{ err error }
type stmOptions struct {
iso Isolation
ctx context.Context
prefetch []string
}
type stmOption func(*stmOptions)
// WithIsolation specifies the transaction isolation level.
func WithIsolation(lvl Isolation) stmOption {
return func(so *stmOptions) { so.iso = lvl }
}
// WithAbortContext specifies the context for permanently aborting the transaction.
func WithAbortContext(ctx context.Context) stmOption {
return func(so *stmOptions) { so.ctx = ctx }
}
// WithPrefetch is a hint to prefetch a list of keys before trying to apply.
// If an STM transaction will unconditionally fetch a set of keys, prefetching
// those keys will save the round-trip cost from requesting each key one by one
// with Get().
func WithPrefetch(keys ...string) stmOption {
return func(so *stmOptions) { so.prefetch = append(so.prefetch, keys...) }
}
// NewSTM initiates a new STM instance, using serializable snapshot isolation by default.
func NewSTM(c *v3.Client, apply func(STM) error, so ...stmOption) (*v3.TxnResponse, error) {
opts := &stmOptions{ctx: c.Ctx()}
for _, f := range so {
f(opts)
}
if len(opts.prefetch) != 0 {
f := apply
apply = func(s STM) error {
s.Get(opts.prefetch...)
return f(s)
}
}
return runSTM(mkSTM(c, opts), apply)
}
func mkSTM(c *v3.Client, opts *stmOptions) STM {
switch opts.iso {
case SerializableSnapshot:
s := &stmSerializable{
stm: stm{client: c, ctx: opts.ctx},
prefetch: make(map[string]*v3.GetResponse),
}
s.conflicts = func() []v3.Cmp {
return append(s.rset.cmps(), s.wset.cmps(s.rset.first()+1)...)
}
return s
case Serializable:
s := &stmSerializable{
stm: stm{client: c, ctx: opts.ctx},
prefetch: make(map[string]*v3.GetResponse),
}
s.conflicts = func() []v3.Cmp { return s.rset.cmps() }
return s
case RepeatableReads:
s := &stm{client: c, ctx: opts.ctx, getOpts: []v3.OpOption{v3.WithSerializable()}}
s.conflicts = func() []v3.Cmp { return s.rset.cmps() }
return s
case ReadCommitted:
s := &stm{client: c, ctx: opts.ctx, getOpts: []v3.OpOption{v3.WithSerializable()}}
s.conflicts = func() []v3.Cmp { return nil }
return s
default:
panic("unsupported stm")
}
}
type stmResponse struct {
resp *v3.TxnResponse
err error
}
func runSTM(s STM, apply func(STM) error) (*v3.TxnResponse, error) {
outc := make(chan stmResponse, 1)
go func() {
defer func() {
if r := recover(); r != nil {
e, ok := r.(stmError)
if !ok {
// client apply panicked
panic(r)
}
outc <- stmResponse{nil, e.err}
}
}()
var out stmResponse
for {
s.reset()
if out.err = apply(s); out.err != nil {
break
}
if out.resp = s.commit(); out.resp != nil {
break
}
}
outc <- out
}()
r := <-outc
return r.resp, r.err
}
// stm implements repeatable-read software transactional memory over etcd
type stm struct {
client *v3.Client
ctx context.Context
// rset holds read key values and revisions
rset readSet
// wset holds overwritten keys and their values
wset writeSet
// getOpts are the opts used for gets
getOpts []v3.OpOption
// conflicts computes the current conflicts on the txn
conflicts func() []v3.Cmp
}
type stmPut struct {
val string
op v3.Op
}
type readSet map[string]*v3.GetResponse
func (rs readSet) add(keys []string, txnresp *v3.TxnResponse) {
for i, resp := range txnresp.Responses {
rs[keys[i]] = (*v3.GetResponse)(resp.GetResponseRange())
}
}
// first returns the store revision from the first fetch
func (rs readSet) first() int64 {
ret := int64(math.MaxInt64 - 1)
for _, resp := range rs {
if rev := resp.Header.Revision; rev < ret {
ret = rev
}
}
return ret
}
// cmps guards the txn from updates to read set
func (rs readSet) cmps() []v3.Cmp {
cmps := make([]v3.Cmp, 0, len(rs))
for k, rk := range rs {
cmps = append(cmps, isKeyCurrent(k, rk))
}
return cmps
}
type writeSet map[string]stmPut
func (ws writeSet) get(keys ...string) *stmPut {
for _, key := range keys {
if wv, ok := ws[key]; ok {
return &wv
}
}
return nil
}
// cmps returns a cmp list testing no writes have happened past rev
func (ws writeSet) cmps(rev int64) []v3.Cmp {
cmps := make([]v3.Cmp, 0, len(ws))
for key := range ws {
cmps = append(cmps, v3.Compare(v3.ModRevision(key), "<", rev))
}
return cmps
}
// puts is the list of ops for all pending writes
func (ws writeSet) puts() []v3.Op {
puts := make([]v3.Op, 0, len(ws))
for _, v := range ws {
puts = append(puts, v.op)
}
return puts
}
func (s *stm) Get(keys ...string) string {
if wv := s.wset.get(keys...); wv != nil {
return wv.val
}
return respToValue(s.fetch(keys...))
}
func (s *stm) Put(key, val string, opts ...v3.OpOption) {
s.wset[key] = stmPut{val, v3.OpPut(key, val, opts...)}
}
func (s *stm) Del(key string) { s.wset[key] = stmPut{"", v3.OpDelete(key)} }
func (s *stm) Rev(key string) int64 {
if resp := s.fetch(key); resp != nil && len(resp.Kvs) != 0 {
return resp.Kvs[0].ModRevision
}
return 0
}
func (s *stm) commit() *v3.TxnResponse {
txnresp, err := s.client.Txn(s.ctx).If(s.conflicts()...).Then(s.wset.puts()...).Commit()
if err != nil {
panic(stmError{err})
}
if txnresp.Succeeded {
return txnresp
}
return nil
}
func (s *stm) fetch(keys ...string) *v3.GetResponse {
if len(keys) == 0 {
return nil
}
ops := make([]v3.Op, len(keys))
for i, key := range keys {
if resp, ok := s.rset[key]; ok {
return resp
}
ops[i] = v3.OpGet(key, s.getOpts...)
}
txnresp, err := s.client.Txn(s.ctx).Then(ops...).Commit()
if err != nil {
panic(stmError{err})
}
s.rset.add(keys, txnresp)
return (*v3.GetResponse)(txnresp.Responses[0].GetResponseRange())
}
func (s *stm) reset() {
s.rset = make(map[string]*v3.GetResponse)
s.wset = make(map[string]stmPut)
}
type stmSerializable struct {
stm
prefetch map[string]*v3.GetResponse
}
func (s *stmSerializable) Get(keys ...string) string {
if len(keys) == 0 {
return ""
}
if wv := s.wset.get(keys...); wv != nil {
return wv.val
}
firstRead := len(s.rset) == 0
for _, key := range keys {
if resp, ok := s.prefetch[key]; ok {
delete(s.prefetch, key)
s.rset[key] = resp
}
}
resp := s.stm.fetch(keys...)
if firstRead {
// txn's base revision is defined by the first read
s.getOpts = []v3.OpOption{
v3.WithRev(resp.Header.Revision),
v3.WithSerializable(),
}
}
return respToValue(resp)
}
func (s *stmSerializable) Rev(key string) int64 {
s.Get(key)
return s.stm.Rev(key)
}
func (s *stmSerializable) gets() ([]string, []v3.Op) {
keys := make([]string, 0, len(s.rset))
ops := make([]v3.Op, 0, len(s.rset))
for k := range s.rset {
keys = append(keys, k)
ops = append(ops, v3.OpGet(k))
}
return keys, ops
}
func (s *stmSerializable) commit() *v3.TxnResponse {
keys, getops := s.gets()
txn := s.client.Txn(s.ctx).If(s.conflicts()...).Then(s.wset.puts()...)
// use Else to prefetch keys in case of conflict to save a round trip
txnresp, err := txn.Else(getops...).Commit()
if err != nil {
panic(stmError{err})
}
if txnresp.Succeeded {
return txnresp
}
// load prefetch with Else data
s.rset.add(keys, txnresp)
s.prefetch = s.rset
s.getOpts = nil
return nil
}
func isKeyCurrent(k string, r *v3.GetResponse) v3.Cmp {
if len(r.Kvs) != 0 {
return v3.Compare(v3.ModRevision(k), "=", r.Kvs[0].ModRevision)
}
return v3.Compare(v3.ModRevision(k), "=", 0)
}
func respToValue(resp *v3.GetResponse) string {
if resp == nil || len(resp.Kvs) == 0 {
return ""
}
return string(resp.Kvs[0].Value)
}
// NewSTMRepeatable is deprecated.
func NewSTMRepeatable(ctx context.Context, c *v3.Client, apply func(STM) error) (*v3.TxnResponse, error) {
return NewSTM(c, apply, WithAbortContext(ctx), WithIsolation(RepeatableReads))
}
// NewSTMSerializable is deprecated.
func NewSTMSerializable(ctx context.Context, c *v3.Client, apply func(STM) error) (*v3.TxnResponse, error) {
return NewSTM(c, apply, WithAbortContext(ctx), WithIsolation(Serializable))
}
// NewSTMReadCommitted is deprecated.
func NewSTMReadCommitted(ctx context.Context, c *v3.Client, apply func(STM) error) (*v3.TxnResponse, error) {
return NewSTM(c, apply, WithAbortContext(ctx), WithIsolation(ReadCommitted))
}
================================================
FILE: client/v3/concurrency/stm_test.go
================================================
// Copyright 2023 The etcd Authors
//
// 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 concurrency
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestGet(t *testing.T) {
tests := []struct {
name string
stm *stmSerializable
in []string
resp string
}{
{
name: "Empty keys returns empty string",
stm: &stmSerializable{},
in: []string{},
resp: "",
},
{
name: "Nil keys returns empty string",
stm: &stmSerializable{},
in: nil,
resp: "",
},
}
for _, test := range tests {
test := test
t.Run(test.name, func(t *testing.T) {
resp := test.stm.Get(test.in...)
assert.Equal(t, test.resp, resp)
})
}
}
================================================
FILE: client/v3/config.go
================================================
// Copyright 2016 The etcd Authors
//
// 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 clientv3
import (
"context"
"crypto/tls"
"time"
"go.uber.org/zap"
"google.golang.org/grpc"
"go.etcd.io/etcd/client/pkg/v3/transport"
)
type Config struct {
// Endpoints is a list of URLs.
Endpoints []string `json:"endpoints"`
// AutoSyncInterval is the interval to update endpoints with its latest members.
// 0 disables auto-sync. By default auto-sync is disabled.
AutoSyncInterval time.Duration `json:"auto-sync-interval"`
// DialTimeout is the timeout for failing to establish a connection.
DialTimeout time.Duration `json:"dial-timeout"`
// DialKeepAliveTime is the time after which client pings the server to see if
// transport is alive.
DialKeepAliveTime time.Duration `json:"dial-keep-alive-time"`
// DialKeepAliveTimeout is the time that the client waits for a response for the
// keep-alive probe. If the response is not received in this time, the connection is closed.
DialKeepAliveTimeout time.Duration `json:"dial-keep-alive-timeout"`
// MaxCallSendMsgSize is the client-side request send limit in bytes.
// If 0, it defaults to 2.0 MiB (2 * 1024 * 1024).
// Make sure that "MaxCallSendMsgSize" < server-side default send/recv limit.
// ("--max-request-bytes" flag to etcd or "embed.Config.MaxRequestBytes").
MaxCallSendMsgSize int
// MaxCallRecvMsgSize is the client-side response receive limit.
// If 0, it defaults to "math.MaxInt32", because range response can
// easily exceed request send limits.
// Make sure that "MaxCallRecvMsgSize" >= server-side default send/recv limit.
// ("--max-recv-bytes" flag to etcd).
MaxCallRecvMsgSize int
// TLS holds the client secure credentials, if any.
TLS *tls.Config
// Username is a user name for authentication.
Username string `json:"username"`
// Password is a password for authentication.
Password string `json:"password"`
// Token is a JWT used for authentication instead of a password.
Token string `json:"token"`
// RejectOldCluster when set will refuse to create a client against an outdated cluster.
RejectOldCluster bool `json:"reject-old-cluster"`
// DialOptions is a list of dial options for the grpc client (e.g., for interceptors).
// Note that grpc.NewClient ignores options that are specific to grpc.Dial such as
// "grpc.WithBlock()". Use DialTimeout to bound client initialization time.
DialOptions []grpc.DialOption
// Context is the default client context; it can be used to cancel grpc dial out and
// other operations that do not have an explicit context.
Context context.Context
// Logger sets client-side logger.
// If nil, fallback to building LogConfig.
Logger *zap.Logger
// LogConfig configures client-side logger.
// If nil, use the default logger.
// TODO: configure gRPC logger
LogConfig *zap.Config
// PermitWithoutStream when set will allow client to send keepalive pings to server without any active streams(RPCs).
PermitWithoutStream bool `json:"permit-without-stream"`
// MaxUnaryRetries is the maximum number of retries for unary RPCs.
MaxUnaryRetries uint `json:"max-unary-retries"`
// BackoffWaitBetween is the wait time before retrying an RPC.
BackoffWaitBetween time.Duration `json:"backoff-wait-between"`
// BackoffJitterFraction is the jitter fraction to randomize backoff wait time.
BackoffJitterFraction float64 `json:"backoff-jitter-fraction"`
// TODO: support custom balancer picker
}
// ConfigSpec is the configuration from users, which comes from command-line flags,
// environment variables or config file. It is a fully declarative configuration,
// and can be serialized & deserialized to/from JSON.
type ConfigSpec struct {
Endpoints []string `json:"endpoints"`
RequestTimeout time.Duration `json:"request-timeout"`
DialTimeout time.Duration `json:"dial-timeout"`
KeepAliveTime time.Duration `json:"keepalive-time"`
KeepAliveTimeout time.Duration `json:"keepalive-timeout"`
MaxCallSendMsgSize int `json:"max-request-bytes"`
MaxCallRecvMsgSize int `json:"max-recv-bytes"`
Secure *SecureConfig `json:"secure"`
Auth *AuthConfig `json:"auth"`
}
type SecureConfig struct {
Cert string `json:"cert"`
Key string `json:"key"`
Cacert string `json:"cacert"`
ServerName string `json:"server-name"`
InsecureTransport bool `json:"insecure-transport"`
InsecureSkipVerify bool `json:"insecure-skip-tls-verify"`
}
type AuthConfig struct {
Username string `json:"username"`
Password string `json:"password"`
Token string `json:"token"`
}
func (cs *ConfigSpec) Clone() *ConfigSpec {
if cs == nil {
return nil
}
clone := *cs
if len(cs.Endpoints) > 0 {
clone.Endpoints = make([]string, len(cs.Endpoints))
copy(clone.Endpoints, cs.Endpoints)
}
if cs.Secure != nil {
clone.Secure = &SecureConfig{}
*clone.Secure = *cs.Secure
}
if cs.Auth != nil {
clone.Auth = &AuthConfig{}
*clone.Auth = *cs.Auth
}
return &clone
}
func (cfg AuthConfig) Empty() bool {
return cfg.Username == "" && cfg.Password == "" && cfg.Token == ""
}
// NewClientConfig creates a Config based on the provided ConfigSpec.
func NewClientConfig(confSpec *ConfigSpec, lg *zap.Logger) (*Config, error) {
tlsCfg, err := newTLSConfig(confSpec.Secure, lg)
if err != nil {
return nil, err
}
cfg := &Config{
Endpoints: confSpec.Endpoints,
DialTimeout: confSpec.DialTimeout,
DialKeepAliveTime: confSpec.KeepAliveTime,
DialKeepAliveTimeout: confSpec.KeepAliveTimeout,
MaxCallSendMsgSize: confSpec.MaxCallSendMsgSize,
MaxCallRecvMsgSize: confSpec.MaxCallRecvMsgSize,
TLS: tlsCfg,
}
if confSpec.Auth != nil {
cfg.Username = confSpec.Auth.Username
cfg.Password = confSpec.Auth.Password
cfg.Token = confSpec.Auth.Token
}
return cfg, nil
}
func newTLSConfig(scfg *SecureConfig, lg *zap.Logger) (*tls.Config, error) {
var (
tlsCfg *tls.Config
err error
)
if scfg == nil {
return nil, nil
}
if scfg.Cert != "" || scfg.Key != "" || scfg.Cacert != "" || scfg.ServerName != "" {
cfgtls := &transport.TLSInfo{
CertFile: scfg.Cert,
KeyFile: scfg.Key,
TrustedCAFile: scfg.Cacert,
ServerName: scfg.ServerName,
Logger: lg,
}
if tlsCfg, err = cfgtls.ClientConfig(); err != nil {
return nil, err
}
}
// If key/cert is not given but user wants secure connection, we
// should still setup an empty tls configuration for gRPC to setup
// secure connection.
if tlsCfg == nil && !scfg.InsecureTransport {
tlsCfg = &tls.Config{}
}
// If the user wants to skip TLS verification then we should set
// the InsecureSkipVerify flag in tls configuration.
if scfg.InsecureSkipVerify {
if tlsCfg == nil {
tlsCfg = &tls.Config{}
}
tlsCfg.InsecureSkipVerify = scfg.InsecureSkipVerify
}
return tlsCfg, nil
}
================================================
FILE: client/v3/config_test.go
================================================
// Copyright 2022 The etcd Authors
//
// 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 clientv3
import (
"crypto/tls"
"encoding/json"
"reflect"
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"go.uber.org/zap"
"go.etcd.io/etcd/client/pkg/v3/logutil"
"go.etcd.io/etcd/client/pkg/v3/transport"
)
func TestNewClientConfig(t *testing.T) {
cases := []struct {
name string
spec ConfigSpec
expectedConf Config
}{
{
name: "only has basic info",
spec: ConfigSpec{
Endpoints: []string{"http://192.168.0.10:2379"},
DialTimeout: 2 * time.Second,
KeepAliveTime: 3 * time.Second,
KeepAliveTimeout: 5 * time.Second,
},
expectedConf: Config{
Endpoints: []string{"http://192.168.0.10:2379"},
DialTimeout: 2 * time.Second,
DialKeepAliveTime: 3 * time.Second,
DialKeepAliveTimeout: 5 * time.Second,
},
},
{
name: "auth enabled",
spec: ConfigSpec{
Endpoints: []string{"http://192.168.0.12:2379"},
DialTimeout: 1 * time.Second,
KeepAliveTime: 4 * time.Second,
KeepAliveTimeout: 6 * time.Second,
Auth: &AuthConfig{
Username: "test",
Password: "changeme",
},
},
expectedConf: Config{
Endpoints: []string{"http://192.168.0.12:2379"},
DialTimeout: 1 * time.Second,
DialKeepAliveTime: 4 * time.Second,
DialKeepAliveTimeout: 6 * time.Second,
Username: "test",
Password: "changeme",
},
},
{
name: "JWT specified",
spec: ConfigSpec{
Endpoints: []string{"http://192.168.0.12:2379"},
DialTimeout: 1 * time.Second,
KeepAliveTime: 4 * time.Second,
KeepAliveTimeout: 6 * time.Second,
Auth: &AuthConfig{
Token: "test",
},
},
expectedConf: Config{
Endpoints: []string{"http://192.168.0.12:2379"},
DialTimeout: 1 * time.Second,
DialKeepAliveTime: 4 * time.Second,
DialKeepAliveTimeout: 6 * time.Second,
Token: "test",
},
},
{
name: "default secure transport",
spec: ConfigSpec{
Endpoints: []string{"http://192.168.0.10:2379"},
DialTimeout: 2 * time.Second,
KeepAliveTime: 3 * time.Second,
KeepAliveTimeout: 5 * time.Second,
Secure: &SecureConfig{
InsecureTransport: false,
},
},
expectedConf: Config{
Endpoints: []string{"http://192.168.0.10:2379"},
DialTimeout: 2 * time.Second,
DialKeepAliveTime: 3 * time.Second,
DialKeepAliveTimeout: 5 * time.Second,
TLS: &tls.Config{},
},
},
{
name: "default secure transport and skip TLS verification",
spec: ConfigSpec{
Endpoints: []string{"http://192.168.0.13:2379"},
DialTimeout: 1 * time.Second,
KeepAliveTime: 3 * time.Second,
KeepAliveTimeout: 5 * time.Second,
Secure: &SecureConfig{
InsecureTransport: false,
InsecureSkipVerify: true,
},
},
expectedConf: Config{
Endpoints: []string{"http://192.168.0.13:2379"},
DialTimeout: 1 * time.Second,
DialKeepAliveTime: 3 * time.Second,
DialKeepAliveTimeout: 5 * time.Second,
TLS: &tls.Config{
InsecureSkipVerify: true,
},
},
},
{
name: "insecure transport and skip TLS verification",
spec: ConfigSpec{
Endpoints: []string{"http://192.168.0.13:2379"},
DialTimeout: 1 * time.Second,
KeepAliveTime: 3 * time.Second,
KeepAliveTimeout: 5 * time.Second,
Secure: &SecureConfig{
InsecureTransport: true,
InsecureSkipVerify: true,
},
},
expectedConf: Config{
Endpoints: []string{"http://192.168.0.13:2379"},
DialTimeout: 1 * time.Second,
DialKeepAliveTime: 3 * time.Second,
DialKeepAliveTimeout: 5 * time.Second,
TLS: &tls.Config{
InsecureSkipVerify: true,
},
},
},
}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
lg, _ := logutil.CreateDefaultZapLogger(zap.InfoLevel)
cfg, err := NewClientConfig(&tc.spec, lg)
require.NoError(t, err)
assert.Equal(t, tc.expectedConf, *cfg)
})
}
}
func TestNewClientConfigWithSecureCfg(t *testing.T) {
tls, err := transport.SelfCert(zap.NewNop(), t.TempDir(), []string{"localhost"}, 1)
require.NoError(t, err)
scfg := &SecureConfig{
Cert: tls.CertFile,
Key: tls.KeyFile,
Cacert: tls.TrustedCAFile,
}
cfg, err := NewClientConfig(&ConfigSpec{
Endpoints: []string{"http://192.168.0.13:2379"},
DialTimeout: 2 * time.Second,
KeepAliveTime: 3 * time.Second,
KeepAliveTimeout: 5 * time.Second,
Secure: scfg,
}, nil)
require.NoErrorf(t, err, "Unexpected result client config")
if cfg == nil || cfg.TLS == nil {
t.Fatalf("Unexpected result client config: %v", err)
}
}
func TestConfigSpecClone(t *testing.T) {
cfgSpec := &ConfigSpec{
Endpoints: []string{"ep1", "ep2", "ep3"},
RequestTimeout: 10 * time.Second,
DialTimeout: 2 * time.Second,
KeepAliveTime: 5 * time.Second,
KeepAliveTimeout: 2 * time.Second,
Secure: &SecureConfig{
Cert: "path/2/cert",
Key: "path/2/key",
Cacert: "path/2/cacert",
InsecureTransport: true,
InsecureSkipVerify: false,
},
Auth: &AuthConfig{
Username: "foo",
Password: "changeme",
},
}
testCases := []struct {
name string
cs *ConfigSpec
newEp []string
newSecure *SecureConfig
newAuth *AuthConfig
expectedEqual bool
}{
{
name: "normal case",
cs: cfgSpec,
expectedEqual: true,
},
{
name: "point to a new slice of endpoint, but with the same data",
cs: cfgSpec,
newEp: []string{"ep1", "ep2", "ep3"},
expectedEqual: true,
},
{
name: "update endpoint",
cs: cfgSpec,
newEp: []string{"ep1", "newep2", "ep3"},
expectedEqual: false,
},
{
name: "point to a new secureConfig, but with the same data",
cs: cfgSpec,
newSecure: &SecureConfig{
Cert: "path/2/cert",
Key: "path/2/key",
Cacert: "path/2/cacert",
InsecureTransport: true,
InsecureSkipVerify: false,
},
expectedEqual: true,
},
{
name: "update key in secureConfig",
cs: cfgSpec,
newSecure: &SecureConfig{
Cert: "path/2/cert",
Key: "newPath/2/key",
Cacert: "path/2/cacert",
InsecureTransport: true,
InsecureSkipVerify: false,
},
expectedEqual: false,
},
{
name: "update bool values in secureConfig",
cs: cfgSpec,
newSecure: &SecureConfig{
Cert: "path/2/cert",
Key: "path/2/key",
Cacert: "path/2/cacert",
InsecureTransport: false,
InsecureSkipVerify: true,
},
expectedEqual: false,
},
{
name: "point to a new authConfig, but with the same data",
cs: cfgSpec,
newAuth: &AuthConfig{
Username: "foo",
Password: "changeme",
},
expectedEqual: true,
},
{
name: "update authConfig",
cs: cfgSpec,
newAuth: &AuthConfig{
Username: "newUser",
Password: "newPassword",
},
expectedEqual: false,
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
dataBeforeTest, err := json.Marshal(tc.cs)
require.NoError(t, err)
clonedCfgSpec := tc.cs.Clone()
if len(tc.newEp) > 0 {
clonedCfgSpec.Endpoints = tc.newEp
}
if tc.newSecure != nil {
clonedCfgSpec.Secure = tc.newSecure
}
if tc.newAuth != nil {
clonedCfgSpec.Auth = tc.newAuth
}
actualEqual := reflect.DeepEqual(tc.cs, clonedCfgSpec)
require.Equal(t, tc.expectedEqual, actualEqual)
// double-check the original ConfigSpec isn't updated
dataAfterTest, err := json.Marshal(tc.cs)
require.NoError(t, err)
require.True(t, reflect.DeepEqual(dataBeforeTest, dataAfterTest))
})
}
}
================================================
FILE: client/v3/credentials/credentials.go
================================================
// Copyright 2019 The etcd Authors
//
// 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 credentials implements gRPC credential interface with etcd specific logic.
// e.g., client handshake with custom authority parameter
package credentials
import (
"context"
"crypto/tls"
"sync"
grpccredentials "google.golang.org/grpc/credentials"
"go.etcd.io/etcd/api/v3/v3rpc/rpctypes"
)
func NewTransportCredential(cfg *tls.Config) grpccredentials.TransportCredentials {
return grpccredentials.NewTLS(cfg)
}
// PerRPCCredentialsBundle defines gRPC credential interface.
type PerRPCCredentialsBundle interface {
UpdateAuthToken(token string)
PerRPCCredentials() grpccredentials.PerRPCCredentials
}
func NewPerRPCCredentialBundle() PerRPCCredentialsBundle {
return &perRPCCredentialBundle{
rc: &perRPCCredential{},
}
}
// perRPCCredentialBundle implements `PerRPCCredentialsBundle` interface.
type perRPCCredentialBundle struct {
rc *perRPCCredential
}
func (b *perRPCCredentialBundle) UpdateAuthToken(token string) {
if b.rc == nil {
return
}
b.rc.UpdateAuthToken(token)
}
func (b *perRPCCredentialBundle) PerRPCCredentials() grpccredentials.PerRPCCredentials {
return b.rc
}
// perRPCCredential implements `grpccredentials.PerRPCCredentials` interface.
type perRPCCredential struct {
authToken string
authTokenMu sync.RWMutex
}
func (rc *perRPCCredential) RequireTransportSecurity() bool { return false }
func (rc *perRPCCredential) GetRequestMetadata(ctx context.Context, s ...string) (map[string]string, error) {
rc.authTokenMu.RLock()
authToken := rc.authToken
rc.authTokenMu.RUnlock()
if authToken == "" {
return nil, nil
}
return map[string]string{rpctypes.TokenFieldNameGRPC: authToken}, nil
}
func (rc *perRPCCredential) UpdateAuthToken(token string) {
rc.authTokenMu.Lock()
rc.authToken = token
rc.authTokenMu.Unlock()
}
================================================
FILE: client/v3/credentials/credentials_test.go
================================================
// Copyright 2022 The etcd Authors
//
// 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 credentials
import (
"testing"
"github.com/stretchr/testify/assert"
"go.etcd.io/etcd/api/v3/v3rpc/rpctypes"
)
func TestUpdateAuthToken(t *testing.T) {
bundle := NewPerRPCCredentialBundle()
ctx := t.Context()
metadataBeforeUpdate, _ := bundle.PerRPCCredentials().GetRequestMetadata(ctx)
assert.Empty(t, metadataBeforeUpdate)
bundle.UpdateAuthToken("abcdefg")
metadataAfterUpdate, _ := bundle.PerRPCCredentials().GetRequestMetadata(ctx)
assert.Equal(t, "abcdefg", metadataAfterUpdate[rpctypes.TokenFieldNameGRPC])
}
================================================
FILE: client/v3/ctx.go
================================================
// Copyright 2020 The etcd Authors
//
// 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 clientv3
import (
"context"
"google.golang.org/grpc/metadata"
"go.etcd.io/etcd/api/v3/v3rpc/rpctypes"
"go.etcd.io/etcd/api/v3/version"
)
// WithRequireLeader requires client requests to only succeed
// when the cluster has a leader.
func WithRequireLeader(ctx context.Context) context.Context {
md, ok := metadata.FromOutgoingContext(ctx)
if !ok { // no outgoing metadata ctx key, create one
md = metadata.Pairs(rpctypes.MetadataRequireLeaderKey, rpctypes.MetadataHasLeader)
return metadata.NewOutgoingContext(ctx, md)
}
copied := md.Copy() // avoid racey updates
// overwrite/add 'hasleader' key/value
copied.Set(rpctypes.MetadataRequireLeaderKey, rpctypes.MetadataHasLeader)
return metadata.NewOutgoingContext(ctx, copied)
}
// embeds client version
func withVersion(ctx context.Context) context.Context {
md, ok := metadata.FromOutgoingContext(ctx)
if !ok { // no outgoing metadata ctx key, create one
md = metadata.Pairs(rpctypes.MetadataClientAPIVersionKey, version.APIVersion)
return metadata.NewOutgoingContext(ctx, md)
}
copied := md.Copy() // avoid racey updates
// overwrite/add version key/value
copied.Set(rpctypes.MetadataClientAPIVersionKey, version.APIVersion)
return metadata.NewOutgoingContext(ctx, copied)
}
================================================
FILE: client/v3/ctx_test.go
================================================
// Copyright 2020 The etcd Authors
//
// 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 clientv3
import (
"reflect"
"testing"
"github.com/stretchr/testify/require"
"google.golang.org/grpc/metadata"
"go.etcd.io/etcd/api/v3/v3rpc/rpctypes"
"go.etcd.io/etcd/api/v3/version"
)
func TestMetadataWithRequireLeader(t *testing.T) {
ctx := t.Context()
_, ok := metadata.FromOutgoingContext(ctx)
require.Falsef(t, ok, "expected no outgoing metadata ctx key")
// add a conflicting key with some other value
md := metadata.Pairs(rpctypes.MetadataRequireLeaderKey, "invalid")
// add a key, and expect not be overwritten
md.Set("hello", "1", "2")
ctx = metadata.NewOutgoingContext(ctx, md)
// expect overwrites but still keep other keys
ctx = WithRequireLeader(ctx)
md, ok = metadata.FromOutgoingContext(ctx)
require.Truef(t, ok, "expected outgoing metadata ctx key")
ss := md.Get(rpctypes.MetadataRequireLeaderKey)
require.Truef(t, reflect.DeepEqual(ss, []string{rpctypes.MetadataHasLeader}), "unexpected metadata for %q %v", rpctypes.MetadataRequireLeaderKey, ss)
ss = md.Get("hello")
require.Truef(t, reflect.DeepEqual(ss, []string{"1", "2"}), "unexpected metadata for 'hello' %v", ss)
}
func TestMetadataWithClientAPIVersion(t *testing.T) {
ctx := withVersion(WithRequireLeader(t.Context()))
md, ok := metadata.FromOutgoingContext(ctx)
require.Truef(t, ok, "expected outgoing metadata ctx key")
ss := md.Get(rpctypes.MetadataRequireLeaderKey)
require.Truef(t, reflect.DeepEqual(ss, []string{rpctypes.MetadataHasLeader}), "unexpected metadata for %q %v", rpctypes.MetadataRequireLeaderKey, ss)
ss = md.Get(rpctypes.MetadataClientAPIVersionKey)
require.Truef(t, reflect.DeepEqual(ss, []string{version.APIVersion}), "unexpected metadata for %q %v", rpctypes.MetadataClientAPIVersionKey, ss)
}
================================================
FILE: client/v3/doc.go
================================================
// Copyright 2016 The etcd Authors
//
// 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 clientv3 implements the official Go etcd client for v3.
//
// Create client using `clientv3.New`:
//
// // expect dial time-out on ipv4 blackhole
// _, err := clientv3.New(clientv3.Config{
// Endpoints: []string{"http://254.0.0.1:12345"},
// DialTimeout: 2 * time.Second,
// })
//
// // etcd clientv3 >= v3.2.10, grpc/grpc-go >= v1.7.3
// if err == context.DeadlineExceeded {
// // handle errors
// }
//
// // etcd clientv3 <= v3.2.9, grpc/grpc-go <= v1.2.1
// if err == grpc.ErrClientConnTimeout {
// // handle errors
// }
//
// cli, err := clientv3.New(clientv3.Config{
// Endpoints: []string{"localhost:2379", "localhost:22379", "localhost:32379"},
// DialTimeout: 5 * time.Second,
// })
// if err != nil {
// // handle error!
// }
// defer cli.Close()
//
// Make sure to close the client after using it. If the client is not closed, the
// connection will have leaky goroutines.
//
// To specify a client request timeout, wrap the context with context.WithTimeout:
//
// ctx, cancel := context.WithTimeout(context.Background(), timeout)
// defer cancel()
// resp, err := kvc.Put(ctx, "sample_key", "sample_value")
// if err != nil {
// // handle error!
// }
// // use the response
//
// The Client has internal state (watchers and leases), so Clients should be reused instead of created as needed.
// Clients are safe for concurrent use by multiple goroutines.
//
// etcd client returns 2 types of errors:
//
// 1. context error: canceled or deadline exceeded.
// 2. gRPC error: e.g. when clock drifts in server-side before client's context deadline exceeded.
// See https://github.com/etcd-io/etcd/blob/main/api/v3rpc/rpctypes/error.go
//
// Here is the example code to handle client errors:
//
// resp, err := kvc.Put(ctx, "", "")
// if err != nil {
// if err == context.Canceled {
// // ctx is canceled by another routine
// } else if err == context.DeadlineExceeded {
// // ctx is attached with a deadline and it exceeded
// } else if err == rpctypes.ErrEmptyKey {
// // client-side error: key is not provided
// } else if ev, ok := status.FromError(err); ok {
// code := ev.Code()
// if code == codes.DeadlineExceeded {
// // server-side context might have timed-out first (due to clock skew)
// // while original client-side context is not timed-out yet
// }
// } else {
// // bad cluster endpoints, which are not etcd servers
// }
// }
//
// go func() { cli.Close() }()
// _, err := kvc.Get(ctx, "a")
// if err != nil {
// // with etcd clientv3 <= v3.3
// if err == context.Canceled {
// // grpc balancer calls 'Get' with an inflight client.Close
// } else if err == grpc.ErrClientConnClosing { // <= gRCP v1.7.x
// // grpc balancer calls 'Get' after client.Close.
// }
// // with etcd clientv3 >= v3.4
// if clientv3.IsConnCanceled(err) {
// // gRPC client connection is closed
// }
// }
//
// The grpc load balancer is registered statically and is shared across etcd clients.
// To enable detailed load balancer logging, set the ETCD_CLIENT_DEBUG environment
// variable. E.g. "ETCD_CLIENT_DEBUG=1".
package clientv3
================================================
FILE: client/v3/experimental/recipes/barrier.go
================================================
// Copyright 2016 The etcd Authors
//
// 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 recipe
import (
"context"
"go.etcd.io/etcd/api/v3/mvccpb"
v3 "go.etcd.io/etcd/client/v3"
)
// Barrier creates a key in etcd to block processes, then deletes the key to
// release all blocked processes.
type Barrier struct {
client *v3.Client
ctx context.Context
key string
}
func NewBarrier(client *v3.Client, key string) *Barrier {
return &Barrier{client, context.TODO(), key}
}
// Hold creates the barrier key causing processes to block on Wait.
func (b *Barrier) Hold() error {
_, err := newKey(b.client, b.key, v3.NoLease)
return err
}
// Release deletes the barrier key to unblock all waiting processes.
func (b *Barrier) Release() error {
_, err := b.client.Delete(b.ctx, b.key)
return err
}
// Wait blocks on the barrier key until it is deleted. If there is no key, Wait
// assumes Release has already been called and returns immediately.
func (b *Barrier) Wait() error {
resp, err := b.client.Get(b.ctx, b.key)
if err != nil {
return err
}
if len(resp.Kvs) == 0 {
// key already removed
return nil
}
_, err = WaitEvents(
b.client,
b.key,
resp.Header.Revision+1,
[]mvccpb.Event_EventType{mvccpb.Event_DELETE})
return err
}
================================================
FILE: client/v3/experimental/recipes/client.go
================================================
// Copyright 2016 The etcd Authors
//
// 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 recipe
import (
"context"
"errors"
spb "go.etcd.io/etcd/api/v3/mvccpb"
v3 "go.etcd.io/etcd/client/v3"
)
var (
ErrKeyExists = errors.New("key already exists")
ErrWaitMismatch = errors.New("unexpected wait result")
ErrTooManyClients = errors.New("too many clients")
ErrNoWatcher = errors.New("no watcher channel")
)
// deleteRevKey deletes a key by revision, returning false if key is missing
func deleteRevKey(kv v3.KV, key string, rev int64) (bool, error) {
cmp := v3.Compare(v3.ModRevision(key), "=", rev)
req := v3.OpDelete(key)
txnresp, err := kv.Txn(context.TODO()).If(cmp).Then(req).Commit()
if err != nil {
return false, err
} else if !txnresp.Succeeded {
return false, nil
}
return true, nil
}
func claimFirstKey(kv v3.KV, kvs []*spb.KeyValue) (*spb.KeyValue, error) {
for _, k := range kvs {
ok, err := deleteRevKey(kv, string(k.Key), k.ModRevision)
if err != nil {
return nil, err
} else if ok {
return k, nil
}
}
return nil, nil
}
================================================
FILE: client/v3/experimental/recipes/doc.go
================================================
// Copyright 2017 The etcd Authors
//
// 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 recipe contains experimental client-side distributed
// synchronization primitives.
package recipe
================================================
FILE: client/v3/experimental/recipes/double_barrier.go
================================================
// Copyright 2016 The etcd Authors
//
// 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 recipe
import (
"context"
"go.etcd.io/etcd/api/v3/mvccpb"
clientv3 "go.etcd.io/etcd/client/v3"
"go.etcd.io/etcd/client/v3/concurrency"
)
// DoubleBarrier blocks processes on Enter until an expected count enters, then
// blocks again on Leave until all processes have left.
type DoubleBarrier struct {
s *concurrency.Session
ctx context.Context
key string // key for the collective barrier
count int
myKey *EphemeralKV // current key for this process on the barrier
}
func NewDoubleBarrier(s *concurrency.Session, key string, count int) *DoubleBarrier {
return &DoubleBarrier{
s: s,
ctx: context.TODO(),
key: key,
count: count,
}
}
// Enter waits for "count" processes to enter the barrier then returns
func (b *DoubleBarrier) Enter() error {
client := b.s.Client()
// Check the entered clients before creating the UniqueEphemeralKey,
// fail the request if there are already too many clients.
if resp1, err := b.enteredClients(client); err != nil {
return err
} else if len(resp1.Kvs) >= b.count {
return ErrTooManyClients
}
ek, err := newUniqueEphemeralKey(b.s, b.key+"/waiters")
if err != nil {
return err
}
b.myKey = ek
// Check the entered clients after creating the UniqueEphemeralKey
resp2, err := b.enteredClients(client)
if err != nil {
return err
}
if len(resp2.Kvs) >= b.count {
lastWaiter := resp2.Kvs[b.count-1]
if ek.rev > lastWaiter.CreateRevision {
// delete itself now, otherwise other processes may need to wait
// until these keys are automatically deleted when the related
// lease expires.
//nolint:staticcheck // SA9003 disable empty branch checker to keep the comment for why we ignore error
if err = b.myKey.Delete(); err != nil {
// Nothing to do here. We have to wait for the key to be
// deleted when the lease expires.
}
return ErrTooManyClients
}
if ek.rev == lastWaiter.CreateRevision {
// TODO(ahrtr): we might need to compare ek.key and
// string(lastWaiter.Key), they should be equal.
// unblock all other waiters
_, err = client.Put(b.ctx, b.key+"/ready", "")
return err
}
}
_, err = WaitEvents(
client,
b.key+"/ready",
ek.Revision(),
[]mvccpb.Event_EventType{mvccpb.Event_PUT})
return err
}
// enteredClients gets all the entered clients, which are ordered by the
// createRevision in ascending order.
func (b *DoubleBarrier) enteredClients(cli *clientv3.Client) (*clientv3.GetResponse, error) {
resp, err := cli.Get(b.ctx, b.key+"/waiters", clientv3.WithPrefix(),
clientv3.WithSort(clientv3.SortByCreateRevision, clientv3.SortAscend))
if err != nil {
return nil, err
}
return resp, nil
}
// Leave waits for "count" processes to leave the barrier then returns
func (b *DoubleBarrier) Leave() error {
client := b.s.Client()
resp, err := client.Get(b.ctx, b.key+"/waiters", clientv3.WithPrefix())
if err != nil {
return err
}
if len(resp.Kvs) == 0 {
return nil
}
lowest, highest := resp.Kvs[0], resp.Kvs[0]
for _, k := range resp.Kvs {
if k.ModRevision < lowest.ModRevision {
lowest = k
}
if k.ModRevision > highest.ModRevision {
highest = k
}
}
isLowest := string(lowest.Key) == b.myKey.Key()
if len(resp.Kvs) == 1 && isLowest {
// this is the only node in the barrier; finish up
if _, err = client.Delete(b.ctx, b.key+"/ready"); err != nil {
return err
}
return b.myKey.Delete()
}
// this ensures that if a process fails, the ephemeral lease will be
// revoked, its barrier key is removed, and the barrier can resume
// lowest process in node => wait on highest process
if isLowest {
_, err = WaitEvents(
client,
string(highest.Key),
highest.ModRevision,
[]mvccpb.Event_EventType{mvccpb.Event_DELETE})
if err != nil {
return err
}
return b.Leave()
}
// delete self and wait on lowest process
if err = b.myKey.Delete(); err != nil {
return err
}
key := string(lowest.Key)
_, err = WaitEvents(
client,
key,
lowest.ModRevision,
[]mvccpb.Event_EventType{mvccpb.Event_DELETE})
if err != nil {
return err
}
return b.Leave()
}
================================================
FILE: client/v3/experimental/recipes/grpc_gateway/user_add.sh
================================================
#!/bin/bash
# Copyright 2018 The etcd Authors
#
# 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.
usage () {
echo 'Username required: ./user_add.sh $username'
exit
}
if [ "$1" == "" ]; then
usage
fi
newuser=$1
read -r -s -p "Enter password for $newuser" newpass
user=root
pass=toor
host=127.0.0.1
port=2379
api=v3
cacert="path/to/ca.pem"
key="path/to/client-key.pem"
cert="path/to/client.pem"
tokengen() {
json=$(printf '{"name": "%s", "password": "%s"}' \
"$(escape "$1")" \
"$(escape "$2")"
)
curl -s --cacert $cacert \
--key $key \
--cert $cert \
-X POST \
-d "$json" \
https://${host}:${port}/${api}/auth/authenticate \
| jq -r '.token'
}
add_user() {
json=$(printf '{"name": "%s", "password": "%s"}' \
"$(escape "$1")" \
"$(escape "$2")"
)
curl -s --cacert $cacert \
--key $key \
--cert $cert \
-H "Authorization: $3" \
-X POST \
-d "$json" \
https://${host}:${port}/${api}/auth/user/add
}
escape() {
echo "${1//\"/\\\"}"
}
token=$(tokengen $user $pass)
response=$(add_user $newuser $newpass $token)
echo -e "\\n$response"
================================================
FILE: client/v3/experimental/recipes/key.go
================================================
// Copyright 2016 The etcd Authors
//
// 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 recipe
import (
"context"
"errors"
"fmt"
"strings"
"time"
v3 "go.etcd.io/etcd/client/v3"
"go.etcd.io/etcd/client/v3/concurrency"
)
// RemoteKV is a key/revision pair created by the client and stored on etcd
type RemoteKV struct {
kv v3.KV
key string
rev int64
val string
}
func newKey(kv v3.KV, key string, leaseID v3.LeaseID) (*RemoteKV, error) {
return newKV(kv, key, "", leaseID)
}
func newKV(kv v3.KV, key, val string, leaseID v3.LeaseID) (*RemoteKV, error) {
rev, err := putNewKV(kv, key, val, leaseID)
if err != nil {
return nil, err
}
return &RemoteKV{kv, key, rev, val}, nil
}
func newUniqueKV(kv v3.KV, prefix string, val string) (*RemoteKV, error) {
for {
newKey := fmt.Sprintf("%s/%v", prefix, time.Now().UnixNano())
rev, err := putNewKV(kv, newKey, val, v3.NoLease)
if err == nil {
return &RemoteKV{kv, newKey, rev, val}, nil
}
if !errors.Is(err, ErrKeyExists) {
return nil, err
}
}
}
// putNewKV attempts to create the given key, only succeeding if the key did
// not yet exist.
func putNewKV(kv v3.KV, key, val string, leaseID v3.LeaseID) (int64, error) {
cmp := v3.Compare(v3.Version(key), "=", 0)
req := v3.OpPut(key, val, v3.WithLease(leaseID))
txnresp, err := kv.Txn(context.TODO()).If(cmp).Then(req).Commit()
if err != nil {
return 0, err
}
if !txnresp.Succeeded {
return 0, ErrKeyExists
}
return txnresp.Header.Revision, nil
}
// newSequentialKV allocates a new sequential key /nnnnn with a given
// prefix and value. Note: a bookkeeping node __ is also allocated.
func newSequentialKV(kv v3.KV, prefix, val string) (*RemoteKV, error) {
resp, err := kv.Get(context.TODO(), prefix, v3.WithLastKey()...)
if err != nil {
return nil, err
}
// add 1 to last key, if any
newSeqNum := 0
if len(resp.Kvs) != 0 {
fields := strings.Split(string(resp.Kvs[0].Key), "/")
_, serr := fmt.Sscanf(fields[len(fields)-1], "%d", &newSeqNum)
if serr != nil {
return nil, serr
}
newSeqNum++
}
newKey := fmt.Sprintf("%s/%016d", prefix, newSeqNum)
// base prefix key must be current (i.e., <=) with the server update;
// the base key is important to avoid the following:
// N1: LastKey() == 1, start txn.
// N2: new Key 2, new Key 3, Delete Key 2
// N1: txn succeeds allocating key 2 when it shouldn't
baseKey := "__" + prefix
// current revision might contain modification so +1
cmp := v3.Compare(v3.ModRevision(baseKey), "<", resp.Header.Revision+1)
reqPrefix := v3.OpPut(baseKey, "")
reqnewKey := v3.OpPut(newKey, val)
txn := kv.Txn(context.TODO())
txnresp, err := txn.If(cmp).Then(reqPrefix, reqnewKey).Commit()
if err != nil {
return nil, err
}
if !txnresp.Succeeded {
return newSequentialKV(kv, prefix, val)
}
return &RemoteKV{kv, newKey, txnresp.Header.Revision, val}, nil
}
func (rk *RemoteKV) Key() string { return rk.key }
func (rk *RemoteKV) Revision() int64 { return rk.rev }
func (rk *RemoteKV) Value() string { return rk.val }
func (rk *RemoteKV) Delete() error {
if rk.kv == nil {
return nil
}
_, err := rk.kv.Delete(context.TODO(), rk.key)
rk.kv = nil
return err
}
func (rk *RemoteKV) Put(val string) error {
_, err := rk.kv.Put(context.TODO(), rk.key, val)
return err
}
// EphemeralKV is a new key associated with a session lease
type EphemeralKV struct{ RemoteKV }
// newEphemeralKV creates a new key/value pair associated with a session lease
func newEphemeralKV(s *concurrency.Session, key, val string) (*EphemeralKV, error) {
k, err := newKV(s.Client(), key, val, s.Lease())
if err != nil {
return nil, err
}
return &EphemeralKV{*k}, nil
}
// newUniqueEphemeralKey creates a new unique valueless key associated with a session lease
func newUniqueEphemeralKey(s *concurrency.Session, prefix string) (*EphemeralKV, error) {
return newUniqueEphemeralKV(s, prefix, "")
}
// newUniqueEphemeralKV creates a new unique key/value pair associated with a session lease
func newUniqueEphemeralKV(s *concurrency.Session, prefix, val string) (ek *EphemeralKV, err error) {
for {
newKey := fmt.Sprintf("%s/%v", prefix, time.Now().UnixNano())
ek, err = newEphemeralKV(s, newKey, val)
if err == nil || !errors.Is(err, ErrKeyExists) {
break
}
}
return ek, err
}
================================================
FILE: client/v3/experimental/recipes/priority_queue.go
================================================
// Copyright 2016 The etcd Authors
//
// 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 recipe
import (
"context"
"fmt"
"go.etcd.io/etcd/api/v3/mvccpb"
v3 "go.etcd.io/etcd/client/v3"
)
// PriorityQueue implements a multi-reader, multi-writer distributed queue.
type PriorityQueue struct {
client *v3.Client
ctx context.Context
key string
}
// NewPriorityQueue creates an etcd priority queue.
func NewPriorityQueue(client *v3.Client, key string) *PriorityQueue {
return &PriorityQueue{client, context.TODO(), key + "/"}
}
// Enqueue puts a value into a queue with a given priority.
func (q *PriorityQueue) Enqueue(val string, pr uint16) error {
prefix := fmt.Sprintf("%s%05d", q.key, pr)
_, err := newSequentialKV(q.client, prefix, val)
return err
}
// Dequeue returns Enqueue()'d items in FIFO order. If the
// queue is empty, Dequeue blocks until items are available.
func (q *PriorityQueue) Dequeue() (string, error) {
// TODO: fewer round trips by fetching more than one key
resp, err := q.client.Get(q.ctx, q.key, v3.WithFirstKey()...)
if err != nil {
return "", err
}
kv, err := claimFirstKey(q.client, resp.Kvs)
if err != nil {
return "", err
} else if kv != nil {
return string(kv.Value), nil
} else if resp.More {
// missed some items, retry to read in more
return q.Dequeue()
}
// nothing to dequeue; wait on items
ev, err := WaitPrefixEvents(
q.client,
q.key,
resp.Header.Revision,
[]mvccpb.Event_EventType{mvccpb.Event_PUT})
if err != nil {
return "", err
}
ok, err := deleteRevKey(q.client, string(ev.Kv.Key), ev.Kv.ModRevision)
if err != nil {
return "", err
} else if !ok {
return q.Dequeue()
}
return string(ev.Kv.Value), err
}
================================================
FILE: client/v3/experimental/recipes/queue.go
================================================
// Copyright 2016 The etcd Authors
//
// 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 recipe
import (
"context"
"go.etcd.io/etcd/api/v3/mvccpb"
v3 "go.etcd.io/etcd/client/v3"
)
// Queue implements a multi-reader, multi-writer distributed queue.
type Queue struct {
client *v3.Client
ctx context.Context
keyPrefix string
}
func NewQueue(client *v3.Client, keyPrefix string) *Queue {
return &Queue{client, context.TODO(), keyPrefix}
}
func (q *Queue) Enqueue(val string) error {
_, err := newUniqueKV(q.client, q.keyPrefix, val)
return err
}
// Dequeue returns Enqueue()'d elements in FIFO order. If the
// queue is empty, Dequeue blocks until elements are available.
func (q *Queue) Dequeue() (string, error) {
// TODO: fewer round trips by fetching more than one key
resp, err := q.client.Get(q.ctx, q.keyPrefix, v3.WithFirstRev()...)
if err != nil {
return "", err
}
kv, err := claimFirstKey(q.client, resp.Kvs)
if err != nil {
return "", err
} else if kv != nil {
return string(kv.Value), nil
} else if resp.More {
// missed some items, retry to read in more
return q.Dequeue()
}
// nothing yet; wait on elements
ev, err := WaitPrefixEvents(
q.client,
q.keyPrefix,
resp.Header.Revision,
[]mvccpb.Event_EventType{mvccpb.Event_PUT})
if err != nil {
return "", err
}
ok, err := deleteRevKey(q.client, string(ev.Kv.Key), ev.Kv.ModRevision)
if err != nil {
return "", err
} else if !ok {
return q.Dequeue()
}
return string(ev.Kv.Value), err
}
================================================
FILE: client/v3/experimental/recipes/rwmutex.go
================================================
// Copyright 2016 The etcd Authors
//
// 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 recipe
import (
"context"
"go.etcd.io/etcd/api/v3/mvccpb"
v3 "go.etcd.io/etcd/client/v3"
"go.etcd.io/etcd/client/v3/concurrency"
)
type RWMutex struct {
s *concurrency.Session
ctx context.Context
pfx string
myKey *EphemeralKV
}
func NewRWMutex(s *concurrency.Session, prefix string) *RWMutex {
return &RWMutex{s, context.TODO(), prefix + "/", nil}
}
func (rwm *RWMutex) RLock() error {
rk, err := newUniqueEphemeralKey(rwm.s, rwm.pfx+"read")
if err != nil {
return err
}
rwm.myKey = rk
// wait until nodes with "write-" and a lower revision number than myKey are gone
for {
if done, werr := rwm.waitOnLastRev(rwm.pfx + "write"); done || werr != nil {
return werr
}
}
}
func (rwm *RWMutex) Lock() error {
rk, err := newUniqueEphemeralKey(rwm.s, rwm.pfx+"write")
if err != nil {
return err
}
rwm.myKey = rk
// wait until all keys of lower revision than myKey are gone
for {
if done, werr := rwm.waitOnLastRev(rwm.pfx); done || werr != nil {
return werr
}
// get the new lowest key until this is the only one left
}
}
// waitOnLastRev will wait on the last key with a revision < rwm.myKey.Revision with a
// given prefix. If there are no keys left to wait on, return true.
func (rwm *RWMutex) waitOnLastRev(pfx string) (bool, error) {
client := rwm.s.Client()
// get key that's blocking myKey
opts := append(v3.WithLastRev(), v3.WithMaxModRev(rwm.myKey.Revision()-1))
lastKey, err := client.Get(rwm.ctx, pfx, opts...)
if err != nil {
return false, err
}
if len(lastKey.Kvs) == 0 {
return true, nil
}
// wait for release on blocking key
_, err = WaitEvents(
client,
string(lastKey.Kvs[0].Key),
rwm.myKey.Revision(),
[]mvccpb.Event_EventType{mvccpb.Event_DELETE})
return false, err
}
func (rwm *RWMutex) RUnlock() error { return rwm.myKey.Delete() }
func (rwm *RWMutex) Unlock() error { return rwm.myKey.Delete() }
================================================
FILE: client/v3/experimental/recipes/watch.go
================================================
// Copyright 2016 The etcd Authors
//
// 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 recipe
import (
"context"
"go.etcd.io/etcd/api/v3/mvccpb"
clientv3 "go.etcd.io/etcd/client/v3"
)
// WaitEvents waits on a key until it observes the given events and returns the final one.
func WaitEvents(c *clientv3.Client, key string, rev int64, evs []mvccpb.Event_EventType) (*clientv3.Event, error) {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
wc := c.Watch(ctx, key, clientv3.WithRev(rev))
if wc == nil {
return nil, ErrNoWatcher
}
return waitEvents(wc, evs), nil
}
func WaitPrefixEvents(c *clientv3.Client, prefix string, rev int64, evs []mvccpb.Event_EventType) (*clientv3.Event, error) {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
wc := c.Watch(ctx, prefix, clientv3.WithPrefix(), clientv3.WithRev(rev))
if wc == nil {
return nil, ErrNoWatcher
}
return waitEvents(wc, evs), nil
}
func waitEvents(wc clientv3.WatchChan, evs []mvccpb.Event_EventType) *clientv3.Event {
i := 0
for wresp := range wc {
for _, ev := range wresp.Events {
if ev.Type == evs[i] {
i++
if i == len(evs) {
return ev
}
}
}
}
return nil
}
================================================
FILE: client/v3/go.mod
================================================
module go.etcd.io/etcd/client/v3
go 1.26
toolchain go1.26.1
require (
github.com/coreos/go-semver v0.3.1
github.com/dustin/go-humanize v1.0.1
github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.1.0
github.com/prometheus/client_golang v1.23.2
github.com/stretchr/testify v1.11.1
go.etcd.io/etcd/api/v3 v3.6.0-alpha.0
go.etcd.io/etcd/client/pkg/v3 v3.6.0-alpha.0
go.uber.org/zap v1.27.1
google.golang.org/grpc v1.79.2
sigs.k8s.io/yaml v1.6.0
)
require (
github.com/beorn7/perks v1.0.1 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/coreos/go-systemd/v22 v22.7.0 // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/golang/protobuf v1.5.4 // indirect
github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.3 // indirect
github.com/grpc-ecosystem/grpc-gateway/v2 v2.28.0 // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
github.com/prometheus/client_model v0.6.2 // indirect
github.com/prometheus/common v0.67.5 // indirect
github.com/prometheus/procfs v0.16.1 // indirect
go.opentelemetry.io/otel/metric v1.42.0 // indirect
go.opentelemetry.io/otel/sdk v1.42.0 // indirect
go.opentelemetry.io/otel/trace v1.42.0 // indirect
go.uber.org/multierr v1.11.0 // indirect
go.yaml.in/yaml/v2 v2.4.3 // indirect
golang.org/x/net v0.51.0 // indirect
golang.org/x/sys v0.41.0 // indirect
golang.org/x/text v0.34.0 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20260209200024-4cfbd4190f57 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20260209200024-4cfbd4190f57 // indirect
google.golang.org/protobuf v1.36.11 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
replace (
go.etcd.io/etcd/api/v3 => ../../api
go.etcd.io/etcd/client/pkg/v3 => ../pkg
)
================================================
FILE: client/v3/go.sum
================================================
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/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/coreos/go-semver v0.3.1 h1:yi21YpKnrx1gt5R+la8n5WgS0kCrsPp33dmEyHReZr4=
github.com/coreos/go-semver v0.3.1/go.mod h1:irMmmIw/7yzSRPWryHsK7EYSg09caPQL03VsM8rvUec=
github.com/coreos/go-systemd/v22 v22.7.0 h1:LAEzFkke61DFROc7zNLX/WA2i5J8gYqe0rSj9KI28KA=
github.com/coreos/go-systemd/v22 v22.7.0/go.mod h1:xNUYtjHu2EDXbsxz1i41wouACIwT7Ybq9o0BQhMwD0w=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.1.0 h1:QGLs/O40yoNK9vmy4rhUGBVyMf1lISBGtXRpsu/Qu/o=
github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.1.0/go.mod h1:hM2alZsMUni80N33RBe6J0e423LB+odMj7d3EMP9l20=
github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.3 h1:B+8ClL/kCQkRiU82d9xajRPKYMrB7E0MbtzWVi1K4ns=
github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.3/go.mod h1:NbCUVmiS4foBGBHOYlCT25+YmGpJ32dZPi75pGEUpj4=
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/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
github.com/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/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.23.2 h1:Je96obch5RDVy3FDMndoUsjAhG5Edi49h0RJWRi/o0o=
github.com/prometheus/client_golang v1.23.2/go.mod h1:Tb1a6LWHB3/SPIzCoaDXI4I8UHKeFTEQ1YCr+0Gyqmg=
github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk=
github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE=
github.com/prometheus/common v0.67.5 h1:pIgK94WWlQt1WLwAC5j2ynLaBRDiinoAb86HZHTUGI4=
github.com/prometheus/common v0.67.5/go.mod h1:SjE/0MzDEEAyrdr5Gqc6G+sXI67maCxzaT3A2+HqjUw=
github.com/prometheus/procfs v0.16.1 h1:hZ15bTNuirocR6u0JZ6BAHHmwS1p8B4P6MRqxtzMyRg=
github.com/prometheus/procfs v0.16.1/go.mod h1:teAbpZRB1iIAJYREa1LsoWUXykVXA1KlTmWl8x/U+Is=
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/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
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/otel v1.42.0 h1:lSQGzTgVR3+sgJDAU/7/ZMjN9Z+vUip7leaqBKy4sho=
go.opentelemetry.io/otel v1.42.0/go.mod h1:lJNsdRMxCUIWuMlVJWzecSMuNjE7dOYyWlqOXWkdqCc=
go.opentelemetry.io/otel/metric v1.42.0 h1:2jXG+3oZLNXEPfNmnpxKDeZsFI5o4J+nz6xUlaFdF/4=
go.opentelemetry.io/otel/metric v1.42.0/go.mod h1:RlUN/7vTU7Ao/diDkEpQpnz3/92J9ko05BIwxYa2SSI=
go.opentelemetry.io/otel/sdk v1.42.0 h1:LyC8+jqk6UJwdrI/8VydAq/hvkFKNHZVIWuslJXYsDo=
go.opentelemetry.io/otel/sdk v1.42.0/go.mod h1:rGHCAxd9DAph0joO4W6OPwxjNTYWghRWmkHuGbayMts=
go.opentelemetry.io/otel/sdk/metric v1.42.0 h1:D/1QR46Clz6ajyZ3G8SgNlTJKBdGp84q9RKCAZ3YGuA=
go.opentelemetry.io/otel/sdk/metric v1.42.0/go.mod h1:Ua6AAlDKdZ7tdvaQKfSmnFTdHx37+J4ba8MwVCYM5hc=
go.opentelemetry.io/otel/trace v1.42.0 h1:OUCgIPt+mzOnaUTpOQcBiM/PLQ/Op7oq6g4LenLmOYY=
go.opentelemetry.io/otel/trace v1.42.0/go.mod h1:f3K9S+IFqnumBkKhRJMeaZeNk9epyhnCmQh/EysQCdc=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
go.uber.org/zap v1.27.1 h1:08RqriUEv8+ArZRYSTXy1LeBScaMpVSTBhCeaZYfMYc=
go.uber.org/zap v1.27.1/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
go.yaml.in/yaml/v2 v2.4.3 h1:6gvOSjQoTB3vt1l+CU+tSyi/HOjfOjRLJ4YwYZGwRO0=
go.yaml.in/yaml/v2 v2.4.3/go.mod h1:zSxWcmIDjOzPXpjlTTbAsKokqkDNAVtZO0WOMiT90s8=
go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
golang.org/x/net v0.51.0 h1:94R/GTO7mt3/4wIKpcR5gkGmRLOuE/2hNGeWq/GBIFo=
golang.org/x/net v0.51.0/go.mod h1:aamm+2QF5ogm02fjy5Bb7CQ0WMt1/WVM7FtyaTLlA9Y=
golang.org/x/sys v0.41.0 h1:Ivj+2Cp/ylzLiEU89QhWblYnOE9zerudt9Ftecq2C6k=
golang.org/x/sys v0.41.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
golang.org/x/text v0.34.0 h1:oL/Qq0Kdaqxa1KbNeMKwQq0reLCCaFtqu2eNuSeNHbk=
golang.org/x/text v0.34.0/go.mod h1:homfLqTYRFyVYemLBFl5GgL/DWEiH5wcsQ5gSh1yziA=
gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk=
gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=
google.golang.org/genproto/googleapis/api v0.0.0-20260209200024-4cfbd4190f57 h1:JLQynH/LBHfCTSbDWl+py8C+Rg/k1OVH3xfcaiANuF0=
google.golang.org/genproto/googleapis/api v0.0.0-20260209200024-4cfbd4190f57/go.mod h1:kSJwQxqmFXeo79zOmbrALdflXQeAYcUbgS7PbpMknCY=
google.golang.org/genproto/googleapis/rpc v0.0.0-20260209200024-4cfbd4190f57 h1:mWPCjDEyshlQYzBpMNHaEof6UX1PmHcaUODUywQ0uac=
google.golang.org/genproto/googleapis/rpc v0.0.0-20260209200024-4cfbd4190f57/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ=
google.golang.org/grpc v1.79.2 h1:fRMD94s2tITpyJGtBBn7MkMseNpOZU8ZxgC3MMBaXRU=
google.golang.org/grpc v1.79.2/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ=
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
sigs.k8s.io/yaml v1.6.0 h1:G8fkbMSAFqgEFgh4b1wmtzDnioxFCUgTZhlbj5P9QYs=
sigs.k8s.io/yaml v1.6.0/go.mod h1:796bPqUfzR/0jLAl6XjHl3Ck7MiyVv8dbTdyT3/pMf4=
================================================
FILE: client/v3/internal/endpoint/endpoint.go
================================================
// Copyright 2021 The etcd Authors
//
// 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 endpoint
import (
"fmt"
"net"
"net/url"
"path"
"strings"
)
type CredsRequirement int
const (
// CredsRequire - Credentials/certificate required for thi type of connection.
CredsRequire CredsRequirement = iota
// CredsDrop - Credentials/certificate not needed and should get ignored.
CredsDrop
// CredsOptional - Credentials/certificate might be used if supplied
CredsOptional
)
func extractHostFromHostPort(ep string) string {
host, _, err := net.SplitHostPort(ep)
if err != nil {
return ep
}
return host
}
// mustSplit2 returns the values from strings.SplitN(s, sep, 2).
// If sep is not found, it returns ("", "", false) instead.
func mustSplit2(s, sep string) (string, string) {
spl := strings.SplitN(s, sep, 2)
if len(spl) < 2 {
panic(fmt.Errorf("token '%v' expected to have separator sep: `%v`", s, sep))
}
return spl[0], spl[1]
}
func schemeToCredsRequirement(schema string) CredsRequirement {
switch schema {
case "https", "unixs":
return CredsRequire
case "http":
return CredsDrop
case "unix":
// Preserving previous behavior from:
// https://github.com/etcd-io/etcd/blob/dae29bb719dd69dc119146fc297a0628fcc1ccf8/client/v3/client.go#L212
// that likely was a bug due to missing 'fallthrough'.
// At the same time it seems legit to let the users decide whether they
// want credential control or not (and 'unixs' schema is not a standard thing).
return CredsOptional
case "":
return CredsOptional
default:
return CredsOptional
}
}
// This function translates endpoints names supported by etcd server into
// endpoints as supported by grpc with additional information
// (server_name for cert validation, requireCreds - whether certs are needed).
// The main differences:
// - etcd supports unixs & https names as opposed to unix & http to
// distinguish need to configure certificates.
// - etcd support http(s) names as opposed to tcp supported by grpc/dial method.
// - etcd supports unix(s)://local-file naming schema
// (as opposed to unix:local-file canonical name used by grpc for current dir files).
// - Within the unix(s) schemas, the last segment (filename) without 'port' (content after colon)
// is considered serverName - to allow local testing of cert-protected communication.
//
// See more:
// - https://github.com/grpc/grpc-go/blob/26c143bd5f59344a4b8a1e491e0f5e18aa97abc7/internal/grpcutil/target.go#L47
// - https://golang.org/pkg/net/#Dial
// - https://github.com/grpc/grpc/blob/master/doc/naming.md
func translateEndpoint(ep string) (addr string, serverName string, requireCreds CredsRequirement) {
if strings.HasPrefix(ep, "unix:") || strings.HasPrefix(ep, "unixs:") {
if strings.HasPrefix(ep, "unix:///") || strings.HasPrefix(ep, "unixs:///") {
// absolute path case
schema, absolutePath := mustSplit2(ep, "://")
return "unix://" + absolutePath, path.Base(absolutePath), schemeToCredsRequirement(schema)
}
if strings.HasPrefix(ep, "unix://") || strings.HasPrefix(ep, "unixs://") {
// legacy etcd local path
schema, localPath := mustSplit2(ep, "://")
return "unix:" + localPath, path.Base(localPath), schemeToCredsRequirement(schema)
}
schema, localPath := mustSplit2(ep, ":")
return "unix:" + localPath, path.Base(localPath), schemeToCredsRequirement(schema)
}
if strings.Contains(ep, "://") {
url, err := url.Parse(ep)
if err != nil {
return ep, ep, CredsOptional
}
if url.Scheme == "http" || url.Scheme == "https" {
return url.Host, url.Host, schemeToCredsRequirement(url.Scheme)
}
return ep, url.Host, schemeToCredsRequirement(url.Scheme)
}
// Handles plain addresses like 10.0.0.44:437.
return ep, ep, CredsOptional
}
// RequiresCredentials returns whether given endpoint requires
// credentials/certificates for connection.
func RequiresCredentials(ep string) CredsRequirement {
_, _, requireCreds := translateEndpoint(ep)
return requireCreds
}
// Interpret endpoint parses an endpoint of the form
// (http|https)://*|(unix|unixs)://)
// and returns low-level address (supported by 'net') to connect to,
// and a server name used for x509 certificate matching.
func Interpret(ep string) (address string, serverName string) {
addr, serverName, _ := translateEndpoint(ep)
return addr, serverName
}
================================================
FILE: client/v3/internal/endpoint/endpoint_test.go
================================================
// Copyright 2021 The etcd Authors
//
// 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 endpoint
import (
"testing"
)
func Test_interpret(t *testing.T) {
tests := []struct {
endpoint string
wantAddress string
wantServerName string
wantRequiresCreds CredsRequirement
}{
{"127.0.0.1", "127.0.0.1", "127.0.0.1", CredsOptional},
{"localhost", "localhost", "localhost", CredsOptional},
{"localhost:8080", "localhost:8080", "localhost:8080", CredsOptional},
{"unix:127.0.0.1", "unix:127.0.0.1", "127.0.0.1", CredsOptional},
{"unix:127.0.0.1:8080", "unix:127.0.0.1:8080", "127.0.0.1:8080", CredsOptional},
{"unix://127.0.0.1", "unix:127.0.0.1", "127.0.0.1", CredsOptional},
{"unix://127.0.0.1:8080", "unix:127.0.0.1:8080", "127.0.0.1:8080", CredsOptional},
{"unixs:127.0.0.1", "unix:127.0.0.1", "127.0.0.1", CredsRequire},
{"unixs:127.0.0.1:8080", "unix:127.0.0.1:8080", "127.0.0.1:8080", CredsRequire},
{"unixs://127.0.0.1", "unix:127.0.0.1", "127.0.0.1", CredsRequire},
{"unixs://127.0.0.1:8080", "unix:127.0.0.1:8080", "127.0.0.1:8080", CredsRequire},
{"http://127.0.0.1", "127.0.0.1", "127.0.0.1", CredsDrop},
{"http://127.0.0.1:8080", "127.0.0.1:8080", "127.0.0.1:8080", CredsDrop},
{"https://127.0.0.1", "127.0.0.1", "127.0.0.1", CredsRequire},
{"https://127.0.0.1:8080", "127.0.0.1:8080", "127.0.0.1:8080", CredsRequire},
{"https://localhost:20000", "localhost:20000", "localhost:20000", CredsRequire},
{"unix:///tmp/abc", "unix:///tmp/abc", "abc", CredsOptional},
{"unixs:///tmp/abc", "unix:///tmp/abc", "abc", CredsRequire},
{"unix:///tmp/abc:1234", "unix:///tmp/abc:1234", "abc:1234", CredsOptional},
{"unixs:///tmp/abc:1234", "unix:///tmp/abc:1234", "abc:1234", CredsRequire},
{"etcd.io", "etcd.io", "etcd.io", CredsOptional},
{"http://etcd.io/abc", "etcd.io", "etcd.io", CredsDrop},
{"dns://something-other", "dns://something-other", "something-other", CredsOptional},
{"http://[2001:db8:1f70::999:de8:7648:6e8]:100/", "[2001:db8:1f70::999:de8:7648:6e8]:100", "[2001:db8:1f70::999:de8:7648:6e8]:100", CredsDrop},
{"[2001:db8:1f70::999:de8:7648:6e8]:100", "[2001:db8:1f70::999:de8:7648:6e8]:100", "[2001:db8:1f70::999:de8:7648:6e8]:100", CredsOptional},
{"unix:unexpected-file_name#123$456", "unix:unexpected-file_name#123$456", "unexpected-file_name#123$456", CredsOptional},
}
for _, tt := range tests {
t.Run("Interpret_"+tt.endpoint, func(t *testing.T) {
gotAddress, gotServerName := Interpret(tt.endpoint)
if gotAddress != tt.wantAddress {
t.Errorf("Interpret() gotAddress = %v, want %v", gotAddress, tt.wantAddress)
}
if gotServerName != tt.wantServerName {
t.Errorf("Interpret() gotServerName = %v, want %v", gotServerName, tt.wantServerName)
}
})
t.Run("RequiresCredentials_"+tt.endpoint, func(t *testing.T) {
requiresCreds := RequiresCredentials(tt.endpoint)
if requiresCreds != tt.wantRequiresCreds {
t.Errorf("RequiresCredentials() got = %v, want %v", requiresCreds, tt.wantRequiresCreds)
}
})
}
}
func Test_extractHostFromHostPort(t *testing.T) {
tests := []struct {
ep string
want string
}{
{ep: "localhost", want: "localhost"},
{ep: "localhost:8080", want: "localhost"},
{ep: "192.158.7.14:8080", want: "192.158.7.14"},
{ep: "192.158.7.14:8080", want: "192.158.7.14"},
{ep: "[2001:db8:1f70::999:de8:7648:6e8]", want: "[2001:db8:1f70::999:de8:7648:6e8]"},
{ep: "[2001:db8:1f70::999:de8:7648:6e8]:100", want: "2001:db8:1f70::999:de8:7648:6e8"},
}
for _, tt := range tests {
t.Run(tt.ep, func(t *testing.T) {
if got := extractHostFromHostPort(tt.ep); got != tt.want {
t.Errorf("extractHostFromHostPort() = %v, want %v", got, tt.want)
}
})
}
}
================================================
FILE: client/v3/internal/resolver/resolver.go
================================================
// Copyright 2021 The etcd Authors
//
// 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 resolver
import (
"google.golang.org/grpc/resolver"
"google.golang.org/grpc/resolver/manual"
"google.golang.org/grpc/serviceconfig"
"go.etcd.io/etcd/client/v3/internal/endpoint"
)
const (
Schema = "etcd-endpoints"
)
// EtcdManualResolver is a Resolver (and resolver.Builder) that can be updated
// using SetEndpoints.
type EtcdManualResolver struct {
*manual.Resolver
endpoints []string
serviceConfig *serviceconfig.ParseResult
}
func New(endpoints ...string) *EtcdManualResolver {
r := manual.NewBuilderWithScheme(Schema)
return &EtcdManualResolver{Resolver: r, endpoints: endpoints, serviceConfig: nil}
}
// Build returns itself for Resolver, because it's both a builder and a resolver.
func (r *EtcdManualResolver) Build(target resolver.Target, cc resolver.ClientConn, opts resolver.BuildOptions) (resolver.Resolver, error) {
r.serviceConfig = cc.ParseServiceConfig(`{"loadBalancingPolicy": "round_robin"}`)
if r.serviceConfig.Err != nil {
return nil, r.serviceConfig.Err
}
res, err := r.Resolver.Build(target, cc, opts)
if err != nil {
return nil, err
}
// Populates endpoints stored in r into ClientConn (cc).
r.updateState()
return res, nil
}
func (r *EtcdManualResolver) SetEndpoints(endpoints []string) {
r.endpoints = endpoints
r.updateState()
}
func (r EtcdManualResolver) updateState() {
if getCC(r) != nil {
eps := make([]resolver.Endpoint, len(r.endpoints))
for i, ep := range r.endpoints {
addr, serverName := endpoint.Interpret(ep)
eps[i] = resolver.Endpoint{Addresses: []resolver.Address{
{Addr: addr, ServerName: serverName},
}}
}
state := resolver.State{
Endpoints: eps,
ServiceConfig: r.serviceConfig,
}
r.UpdateState(state)
}
}
func getCC(r EtcdManualResolver) (cc resolver.ClientConn) {
defer func() {
if rec := recover(); rec != nil {
cc = nil
}
}()
return r.CC()
}
================================================
FILE: client/v3/kubernetes/client.go
================================================
// Copyright 2024 The etcd Authors
//
// 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 kubernetes
import (
"context"
"fmt"
pb "go.etcd.io/etcd/api/v3/etcdserverpb"
"go.etcd.io/etcd/api/v3/mvccpb"
clientv3 "go.etcd.io/etcd/client/v3"
)
// New creates Client from config.
// Caller is responsible to call Close() to clean up client.
func New(cfg clientv3.Config) (*Client, error) {
c, err := clientv3.New(cfg)
if err != nil {
return nil, err
}
kc := &Client{
Client: c,
}
kc.Kubernetes = kc
return kc, nil
}
type Client struct {
*clientv3.Client
Kubernetes Interface
}
var _ Interface = (*Client)(nil)
func (k Client) Get(ctx context.Context, key string, opts GetOptions) (resp GetResponse, err error) {
rangeResp, err := k.KV.Get(ctx, key, clientv3.WithRev(opts.Revision), clientv3.WithLimit(1))
if err != nil {
return resp, err
}
resp.Revision = rangeResp.Header.Revision
if len(rangeResp.Kvs) == 1 {
resp.KV = rangeResp.Kvs[0]
}
return resp, nil
}
func (k Client) List(ctx context.Context, prefix string, opts ListOptions) (resp ListResponse, err error) {
rangeStart := prefix
if opts.Continue != "" {
rangeStart = opts.Continue
}
rangeEnd := clientv3.GetPrefixRangeEnd(prefix)
rangeResp, err := k.KV.Get(ctx, rangeStart, clientv3.WithRange(rangeEnd), clientv3.WithLimit(opts.Limit), clientv3.WithRev(opts.Revision))
if err != nil {
return resp, err
}
resp.Kvs = rangeResp.Kvs
resp.Count = rangeResp.Count
resp.Revision = rangeResp.Header.Revision
return resp, nil
}
func (k Client) Count(ctx context.Context, prefix string, _ CountOptions) (int64, error) {
resp, err := k.KV.Get(ctx, prefix, clientv3.WithPrefix(), clientv3.WithCountOnly())
if err != nil {
return 0, err
}
return resp.Count, nil
}
func (k Client) OptimisticPut(ctx context.Context, key string, value []byte, expectedRevision int64, opts PutOptions) (resp PutResponse, err error) {
txn := k.KV.Txn(ctx).If(
clientv3.Compare(clientv3.ModRevision(key), "=", expectedRevision),
).Then(
clientv3.OpPut(key, string(value), clientv3.WithLease(opts.LeaseID)),
)
if opts.GetOnFailure {
txn = txn.Else(clientv3.OpGet(key))
}
txnResp, err := txn.Commit()
if err != nil {
return resp, err
}
resp.Succeeded = txnResp.Succeeded
resp.Revision = txnResp.Header.Revision
if opts.GetOnFailure && !txnResp.Succeeded {
if len(txnResp.Responses) == 0 {
return resp, fmt.Errorf("invalid OptimisticPut response: %v", txnResp.Responses)
}
resp.KV = kvFromTxnResponse(txnResp.Responses[0])
}
return resp, nil
}
func (k Client) OptimisticDelete(ctx context.Context, key string, expectedRevision int64, opts DeleteOptions) (resp DeleteResponse, err error) {
txn := k.KV.Txn(ctx).If(
clientv3.Compare(clientv3.ModRevision(key), "=", expectedRevision),
).Then(
clientv3.OpDelete(key),
)
if opts.GetOnFailure {
txn = txn.Else(clientv3.OpGet(key))
}
txnResp, err := txn.Commit()
if err != nil {
return resp, err
}
resp.Succeeded = txnResp.Succeeded
resp.Revision = txnResp.Header.Revision
if opts.GetOnFailure && !txnResp.Succeeded {
resp.KV = kvFromTxnResponse(txnResp.Responses[0])
}
return resp, nil
}
func kvFromTxnResponse(resp *pb.ResponseOp) *mvccpb.KeyValue {
getResponse := resp.GetResponseRange()
if len(getResponse.Kvs) == 1 {
return getResponse.Kvs[0]
}
return nil
}
================================================
FILE: client/v3/kubernetes/interface.go
================================================
// Copyright 2024 The etcd Authors
//
// 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 kubernetes
import (
"context"
"go.etcd.io/etcd/api/v3/mvccpb"
clientv3 "go.etcd.io/etcd/client/v3"
)
// Interface defines the minimal client-side interface that Kubernetes requires
// to interact with etcd. Methods below are standard etcd operations with
// semantics adjusted to better suit Kubernetes' needs.
type Interface interface {
// Get retrieves a single key-value pair from etcd.
//
// If opts.Revision is set to a non-zero value, the key-value pair is retrieved at the specified revision.
// If the required revision has been compacted, the request will fail with ErrCompacted.
Get(ctx context.Context, key string, opts GetOptions) (GetResponse, error)
// List retrieves key-value pairs with the specified prefix, ordered lexicographically by key.
//
// If opts.Revision is non-zero, the key-value pairs are retrieved at the specified revision.
// If the required revision has been compacted, the request will fail with ErrCompacted.
// If opts.Limit is greater than zero, the number of returned key-value pairs is bounded by the limit.
// If opts.Continue is not empty, the listing will start from the key
// specified by it. When paginating, the Continue value should be set
// to the last observed key with "\x00" appended to it.
List(ctx context.Context, prefix string, opts ListOptions) (ListResponse, error)
// Count returns the number of keys with the specified prefix.
//
// Currently, there are no options for the Count operation. However, a placeholder options struct (CountOptions)
// is provided for future extensibility in case options become necessary.
Count(ctx context.Context, prefix string, opts CountOptions) (int64, error)
// OptimisticPut creates or updates a key-value pair if the key has not been modified or created
// since the revision specified in expectedRevision.
//
// An OptimisticPut fails if the key has been modified since expectedRevision.
OptimisticPut(ctx context.Context, key string, value []byte, expectedRevision int64, opts PutOptions) (PutResponse, error)
// OptimisticDelete deletes the key-value pair if it hasn't been modified since the revision
// specified in expectedRevision.
//
// An OptimisticDelete fails if the key has been modified since expectedRevision.
OptimisticDelete(ctx context.Context, key string, expectedRevision int64, opts DeleteOptions) (DeleteResponse, error)
}
type GetOptions struct {
// Revision is the point-in-time of the etcd key-value store to use for the Get operation.
// If Revision is 0, it gets the latest value.
Revision int64
}
type ListOptions struct {
// Revision is the point-in-time of the etcd key-value store to use for the List operation.
// If Revision is 0, it gets the latest values.
Revision int64
// Limit is the maximum number of keys to return for a List operation.
// 0 means no limitation.
Limit int64
// Continue is a key from which to resume the List operation.
// It should be set to the last key from a previous ListResponse
// with "\x00" appended to it when paginating.
Continue string
}
// CountOptions is a placeholder for potential future options for the Count operation.
type CountOptions struct{}
type PutOptions struct {
// GetOnFailure specifies whether to return the modified key-value pair if the Put operation fails due to a revision mismatch.
GetOnFailure bool
// LeaseID is the ID of a lease to associate with the key allowing for automatic deletion after lease expires after it's TTL (time to live).
// Deprecated: Should be replaced with TTL when Interface starts using one lease per object.
LeaseID clientv3.LeaseID
}
type DeleteOptions struct {
// GetOnFailure specifies whether to return the modified key-value pair if the Delete operation fails due to a revision mismatch.
GetOnFailure bool
}
type GetResponse struct {
// KV is the key-value pair retrieved from etcd.
KV *mvccpb.KeyValue
// Revision is the revision of the key-value store at the time of the Get operation.
Revision int64
}
type ListResponse struct {
// Kvs is the list of key-value pairs retrieved from etcd, ordered lexicographically by key.
Kvs []*mvccpb.KeyValue
// Count is the total number of keys with the specified prefix, even if not all were returned due to a limit.
Count int64
// Revision is the revision of the key-value store at the time of the List operation.
Revision int64
}
type PutResponse struct {
// KV is the created or updated key-value pair. If the Put operation failed and GetOnFailure was true, this
// will be the modified key-value pair that caused the failure.
KV *mvccpb.KeyValue
// Succeeded indicates whether the Put operation was successful.
Succeeded bool
// Revision is the revision of the key-value store after the Put operation.
Revision int64
}
type DeleteResponse struct {
// KV is the deleted key-value pair. If the Delete operation failed and GetOnFailure was true, this
// will be the modified key-value pair that caused the failure.
KV *mvccpb.KeyValue
// Succeeded indicates whether the Delete operation was successful.
Succeeded bool
// Revision is the revision of the key-value store after the Delete operation.
Revision int64
}
================================================
FILE: client/v3/kv.go
================================================
// Copyright 2015 The etcd Authors
//
// 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 clientv3
import (
"context"
"google.golang.org/grpc"
pb "go.etcd.io/etcd/api/v3/etcdserverpb"
"go.etcd.io/etcd/api/v3/v3rpc/rpctypes"
)
type (
CompactResponse pb.CompactionResponse
PutResponse pb.PutResponse
GetResponse pb.RangeResponse
DeleteResponse pb.DeleteRangeResponse
TxnResponse pb.TxnResponse
)
type KV interface {
// Put puts a key-value pair into etcd.
// Note that key,value can be plain bytes array and string is
// an immutable representation of that bytes array.
// To get a string of bytes, do string([]byte{0x10, 0x20}).
Put(ctx context.Context, key, val string, opts ...OpOption) (*PutResponse, error)
// Get retrieves keys.
// By default, Get will return the value for "key", if any.
// When passed WithRange(end), Get will return the keys in the range [key, end).
// When passed WithFromKey(), Get returns keys greater than or equal to key.
// When passed WithRev(rev) with rev > 0, Get retrieves keys at the given revision;
// if the required revision is compacted, the request will fail with ErrCompacted .
// When passed WithLimit(limit), the number of returned keys is bounded by limit.
// When passed WithSort(), the keys will be sorted.
Get(ctx context.Context, key string, opts ...OpOption) (*GetResponse, error)
// Delete deletes a key, or optionally using WithRange(end), [key, end).
Delete(ctx context.Context, key string, opts ...OpOption) (*DeleteResponse, error)
// Compact compacts etcd KV history before the given rev.
Compact(ctx context.Context, rev int64, opts ...CompactOption) (*CompactResponse, error)
// Do applies a single Op on KV without a transaction.
// Do is useful when creating arbitrary operations to be issued at a
// later time; the user can range over the operations, calling Do to
// execute them. Get/Put/Delete, on the other hand, are best suited
// for when the operation should be issued at the time of declaration.
Do(ctx context.Context, op Op) (OpResponse, error)
// Txn creates a transaction.
Txn(ctx context.Context) Txn
}
type OpResponse struct {
put *PutResponse
get *GetResponse
del *DeleteResponse
txn *TxnResponse
}
func (op OpResponse) Put() *PutResponse { return op.put }
func (op OpResponse) Get() *GetResponse { return op.get }
func (op OpResponse) Del() *DeleteResponse { return op.del }
func (op OpResponse) Txn() *TxnResponse { return op.txn }
func (resp *PutResponse) OpResponse() OpResponse {
return OpResponse{put: resp}
}
func (resp *GetResponse) OpResponse() OpResponse {
return OpResponse{get: resp}
}
func (resp *DeleteResponse) OpResponse() OpResponse {
return OpResponse{del: resp}
}
func (resp *TxnResponse) OpResponse() OpResponse {
return OpResponse{txn: resp}
}
type kv struct {
remote pb.KVClient
callOpts []grpc.CallOption
}
func NewKV(c *Client) KV {
api := &kv{remote: RetryKVClient(c)}
if c != nil {
api.callOpts = c.callOpts
}
return api
}
func NewKVFromKVClient(remote pb.KVClient, c *Client) KV {
api := &kv{remote: remote}
if c != nil {
api.callOpts = c.callOpts
}
return api
}
func (kv *kv) Put(ctx context.Context, key, val string, opts ...OpOption) (*PutResponse, error) {
r, err := kv.Do(ctx, OpPut(key, val, opts...))
return r.put, ContextError(ctx, err)
}
func (kv *kv) Get(ctx context.Context, key string, opts ...OpOption) (*GetResponse, error) {
r, err := kv.Do(ctx, OpGet(key, opts...))
return r.get, ContextError(ctx, err)
}
func (kv *kv) Delete(ctx context.Context, key string, opts ...OpOption) (*DeleteResponse, error) {
r, err := kv.Do(ctx, OpDelete(key, opts...))
return r.del, ContextError(ctx, err)
}
func (kv *kv) Compact(ctx context.Context, rev int64, opts ...CompactOption) (*CompactResponse, error) {
resp, err := kv.remote.Compact(ctx, OpCompact(rev, opts...).toRequest(), kv.callOpts...)
if err != nil {
return nil, ContextError(ctx, err)
}
return (*CompactResponse)(resp), nil
}
func (kv *kv) Txn(ctx context.Context) Txn {
return &txn{
kv: kv,
ctx: ctx,
callOpts: kv.callOpts,
}
}
func (kv *kv) Do(ctx context.Context, op Op) (OpResponse, error) {
var err error
switch op.t {
case tRange:
if op.IsSortOptionValid() {
var resp *pb.RangeResponse
resp, err = kv.remote.Range(ctx, op.toRangeRequest(), kv.callOpts...)
if err == nil {
return OpResponse{get: (*GetResponse)(resp)}, nil
}
} else {
err = rpctypes.ErrInvalidSortOption
}
case tPut:
var resp *pb.PutResponse
r := &pb.PutRequest{Key: op.key, Value: op.val, Lease: int64(op.leaseID), PrevKv: op.prevKV, IgnoreValue: op.ignoreValue, IgnoreLease: op.ignoreLease}
resp, err = kv.remote.Put(ctx, r, kv.callOpts...)
if err == nil {
return OpResponse{put: (*PutResponse)(resp)}, nil
}
case tDeleteRange:
var resp *pb.DeleteRangeResponse
r := &pb.DeleteRangeRequest{Key: op.key, RangeEnd: op.end, PrevKv: op.prevKV}
resp, err = kv.remote.DeleteRange(ctx, r, kv.callOpts...)
if err == nil {
return OpResponse{del: (*DeleteResponse)(resp)}, nil
}
case tTxn:
var resp *pb.TxnResponse
resp, err = kv.remote.Txn(ctx, op.toTxnRequest(), kv.callOpts...)
if err == nil {
return OpResponse{txn: (*TxnResponse)(resp)}, nil
}
default:
panic("Unknown op")
}
return OpResponse{}, ContextError(ctx, err)
}
================================================
FILE: client/v3/lease.go
================================================
// Copyright 2016 The etcd Authors
//
// 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 clientv3
import (
"context"
"errors"
"sync"
"time"
"go.uber.org/zap"
"google.golang.org/grpc"
"google.golang.org/grpc/metadata"
pb "go.etcd.io/etcd/api/v3/etcdserverpb"
"go.etcd.io/etcd/api/v3/v3rpc/rpctypes"
)
type (
LeaseRevokeResponse pb.LeaseRevokeResponse
LeaseID int64
)
// LeaseGrantResponse wraps the protobuf message LeaseGrantResponse.
type LeaseGrantResponse struct {
*pb.ResponseHeader
ID LeaseID
TTL int64
Error string
}
// LeaseKeepAliveResponse wraps the protobuf message LeaseKeepAliveResponse.
type LeaseKeepAliveResponse struct {
*pb.ResponseHeader
ID LeaseID
TTL int64
}
// LeaseTimeToLiveResponse wraps the protobuf message LeaseTimeToLiveResponse.
type LeaseTimeToLiveResponse struct {
*pb.ResponseHeader
ID LeaseID `json:"id"`
// TTL is the remaining TTL in seconds for the lease; the lease will expire in under TTL+1 seconds. Expired lease will return -1.
TTL int64 `json:"ttl"`
// GrantedTTL is the initial granted time in seconds upon lease creation/renewal.
GrantedTTL int64 `json:"granted-ttl"`
// Keys is the list of keys attached to this lease.
Keys [][]byte `json:"keys"`
}
// LeaseStatus represents a lease status.
type LeaseStatus struct {
ID LeaseID `json:"id"`
// TODO: TTL int64
}
// LeaseLeasesResponse wraps the protobuf message LeaseLeasesResponse.
type LeaseLeasesResponse struct {
*pb.ResponseHeader
Leases []LeaseStatus `json:"leases"`
}
const (
// defaultTTL is the assumed lease TTL used for the first keepalive
// deadline before the actual TTL is known to the client.
defaultTTL = 5 * time.Second
// NoLease is a lease ID for the absence of a lease.
NoLease LeaseID = 0
// retryConnWait is how long to wait before retrying request due to an error
retryConnWait = 500 * time.Millisecond
)
// LeaseResponseChSize is the size of buffer to store unsent lease responses.
// WARNING: DO NOT UPDATE.
// Only for testing purposes.
var LeaseResponseChSize = 16
// ErrKeepAliveHalted is returned if client keep alive loop halts with an unexpected error.
//
// This usually means that automatic lease renewal via KeepAlive is broken, but KeepAliveOnce will still work as expected.
type ErrKeepAliveHalted struct {
Reason error
}
func (e ErrKeepAliveHalted) Error() string {
s := "etcdclient: leases keep alive halted"
if e.Reason != nil {
s += ": " + e.Reason.Error()
}
return s
}
type Lease interface {
// Grant creates a new lease.
Grant(ctx context.Context, ttl int64) (*LeaseGrantResponse, error)
// Revoke revokes the given lease.
Revoke(ctx context.Context, id LeaseID) (*LeaseRevokeResponse, error)
// TimeToLive retrieves the lease information of the given lease ID.
TimeToLive(ctx context.Context, id LeaseID, opts ...LeaseOption) (*LeaseTimeToLiveResponse, error)
// Leases retrieves all leases.
Leases(ctx context.Context) (*LeaseLeasesResponse, error)
// KeepAlive attempts to keep the given lease alive forever. If the keepalive responses posted
// to the channel are not consumed promptly the channel may become full. When full, the lease
// client will continue sending keep alive requests to the etcd server, but will drop responses
// until there is capacity on the channel to send more responses.
//
// If client keep alive loop halts with an unexpected error (e.g. "etcdserver: no leader") or
// canceled by the caller (e.g. context.Canceled), KeepAlive returns a ErrKeepAliveHalted error
// containing the error reason.
//
// The returned "LeaseKeepAliveResponse" channel closes if underlying keep
// alive stream is interrupted in some way the client cannot handle itself;
// given context "ctx" is canceled or timed out.
//
// TODO(v4.0): post errors to last keep alive message before closing
// (see https://github.com/etcd-io/etcd/pull/7866)
KeepAlive(ctx context.Context, id LeaseID) (<-chan *LeaseKeepAliveResponse, error)
// KeepAliveOnce renews the lease once. The response corresponds to the
// first message from calling KeepAlive. If the response has a recoverable
// error, KeepAliveOnce will retry the RPC with a new keep alive message.
//
// In most of the cases, Keepalive should be used instead of KeepAliveOnce.
KeepAliveOnce(ctx context.Context, id LeaseID) (*LeaseKeepAliveResponse, error)
// Close releases all resources Lease keeps for efficient communication
// with the etcd server.
Close() error
}
type lessor struct {
mu sync.Mutex // guards all fields
// donec is closed and loopErr is set when recvKeepAliveLoop stops
donec chan struct{}
loopErr error
remote pb.LeaseClient
stream pb.Lease_LeaseKeepAliveClient
streamCancel context.CancelFunc
stopCtx context.Context
stopCancel context.CancelFunc
keepAlives map[LeaseID]*keepAlive
// firstKeepAliveTimeout is the timeout for the first keepalive request
// before the actual TTL is known to the lease client
firstKeepAliveTimeout time.Duration
// firstKeepAliveOnce ensures stream starts after first KeepAlive call.
firstKeepAliveOnce sync.Once
callOpts []grpc.CallOption
lg *zap.Logger
}
// keepAlive multiplexes a keepalive for a lease over multiple channels
type keepAlive struct {
chs []chan<- *LeaseKeepAliveResponse
ctxs []context.Context
// deadline is the time the keep alive channels close if no response
deadline time.Time
// nextKeepAlive is when to send the next keep alive message
nextKeepAlive time.Time
// donec is closed on lease revoke, expiration, or cancel.
donec chan struct{}
}
func NewLease(c *Client) Lease {
return NewLeaseFromLeaseClient(RetryLeaseClient(c), c, c.cfg.DialTimeout+time.Second)
}
func NewLeaseFromLeaseClient(remote pb.LeaseClient, c *Client, keepAliveTimeout time.Duration) Lease {
l := &lessor{
donec: make(chan struct{}),
keepAlives: make(map[LeaseID]*keepAlive),
remote: remote,
firstKeepAliveTimeout: keepAliveTimeout,
}
if l.firstKeepAliveTimeout == time.Second {
l.firstKeepAliveTimeout = defaultTTL
}
if c != nil {
l.lg = c.GetLogger()
l.callOpts = c.callOpts
}
reqLeaderCtx := WithRequireLeader(context.Background())
l.stopCtx, l.stopCancel = context.WithCancel(reqLeaderCtx)
return l
}
func (l *lessor) Grant(ctx context.Context, ttl int64) (*LeaseGrantResponse, error) {
r := &pb.LeaseGrantRequest{TTL: ttl}
resp, err := l.remote.LeaseGrant(ctx, r, l.callOpts...)
if err == nil {
gresp := &LeaseGrantResponse{
ResponseHeader: resp.GetHeader(),
ID: LeaseID(resp.ID),
TTL: resp.TTL,
Error: resp.Error,
}
return gresp, nil
}
return nil, ContextError(ctx, err)
}
func (l *lessor) Revoke(ctx context.Context, id LeaseID) (*LeaseRevokeResponse, error) {
r := &pb.LeaseRevokeRequest{ID: int64(id)}
resp, err := l.remote.LeaseRevoke(ctx, r, l.callOpts...)
if err == nil {
return (*LeaseRevokeResponse)(resp), nil
}
return nil, ContextError(ctx, err)
}
func (l *lessor) TimeToLive(ctx context.Context, id LeaseID, opts ...LeaseOption) (*LeaseTimeToLiveResponse, error) {
r := toLeaseTimeToLiveRequest(id, opts...)
resp, err := l.remote.LeaseTimeToLive(ctx, r, l.callOpts...)
if err != nil {
return nil, ContextError(ctx, err)
}
gresp := &LeaseTimeToLiveResponse{
ResponseHeader: resp.GetHeader(),
ID: LeaseID(resp.ID),
TTL: resp.TTL,
GrantedTTL: resp.GrantedTTL,
Keys: resp.Keys,
}
return gresp, nil
}
func (l *lessor) Leases(ctx context.Context) (*LeaseLeasesResponse, error) {
resp, err := l.remote.LeaseLeases(ctx, &pb.LeaseLeasesRequest{}, l.callOpts...)
if err == nil {
leases := make([]LeaseStatus, len(resp.Leases))
for i := range resp.Leases {
leases[i] = LeaseStatus{ID: LeaseID(resp.Leases[i].ID)}
}
return &LeaseLeasesResponse{ResponseHeader: resp.GetHeader(), Leases: leases}, nil
}
return nil, ContextError(ctx, err)
}
// To identify the context passed to `KeepAlive`, a key/value pair is
// attached to the context. The key is a `keepAliveCtxKey` object, and
// the value is the pointer to the context object itself, ensuring
// uniqueness as each context has a unique memory address.
type keepAliveCtxKey struct{}
func (l *lessor) KeepAlive(ctx context.Context, id LeaseID) (<-chan *LeaseKeepAliveResponse, error) {
ch := make(chan *LeaseKeepAliveResponse, LeaseResponseChSize)
l.mu.Lock()
// ensure that recvKeepAliveLoop is still running
select {
case <-l.donec:
err := l.loopErr
l.mu.Unlock()
close(ch)
return ch, ErrKeepAliveHalted{Reason: err}
default:
}
ka, ok := l.keepAlives[id]
if ctx.Done() != nil {
ctx = context.WithValue(ctx, keepAliveCtxKey{}, &ctx)
}
if !ok {
// create fresh keep alive
ka = &keepAlive{
chs: []chan<- *LeaseKeepAliveResponse{ch},
ctxs: []context.Context{ctx},
deadline: time.Now().Add(l.firstKeepAliveTimeout),
nextKeepAlive: time.Now(),
donec: make(chan struct{}),
}
l.keepAlives[id] = ka
} else {
// add channel and context to existing keep alive
ka.ctxs = append(ka.ctxs, ctx)
ka.chs = append(ka.chs, ch)
}
l.mu.Unlock()
if ctx.Done() != nil {
go l.keepAliveCtxCloser(ctx, id, ka.donec)
}
l.firstKeepAliveOnce.Do(func() {
go l.recvKeepAliveLoop()
go l.deadlineLoop()
})
return ch, nil
}
func (l *lessor) KeepAliveOnce(ctx context.Context, id LeaseID) (*LeaseKeepAliveResponse, error) {
for {
resp, err := l.keepAliveOnce(ctx, id)
if err == nil {
if resp.TTL <= 0 {
err = rpctypes.ErrLeaseNotFound
}
return resp, err
}
if isHaltErr(ctx, err) {
return nil, ContextError(ctx, err)
}
}
}
func (l *lessor) Close() error {
l.stopCancel()
// close for synchronous teardown if stream goroutines never launched
l.firstKeepAliveOnce.Do(func() { close(l.donec) })
<-l.donec
return nil
}
func (l *lessor) keepAliveCtxCloser(ctx context.Context, id LeaseID, donec <-chan struct{}) {
select {
case <-donec:
return
case <-l.donec:
return
case <-ctx.Done():
}
l.mu.Lock()
defer l.mu.Unlock()
ka, ok := l.keepAlives[id]
if !ok {
return
}
// close channel and remove context if still associated with keep alive
for i, c := range ka.ctxs {
if c.Value(keepAliveCtxKey{}) == ctx.Value(keepAliveCtxKey{}) {
close(ka.chs[i])
ka.ctxs = append(ka.ctxs[:i], ka.ctxs[i+1:]...)
ka.chs = append(ka.chs[:i], ka.chs[i+1:]...)
break
}
}
// remove if no one more listeners
if len(ka.chs) == 0 {
delete(l.keepAlives, id)
}
}
// closeRequireLeader scans keepAlives for ctxs that have require leader
// and closes the associated channels.
func (l *lessor) closeRequireLeader() {
l.mu.Lock()
defer l.mu.Unlock()
for _, ka := range l.keepAlives {
reqIdxs := 0
// find all required leader channels, close, mark as nil
for i, ctx := range ka.ctxs {
md, ok := metadata.FromOutgoingContext(ctx)
if !ok {
continue
}
ks := md[rpctypes.MetadataRequireLeaderKey]
if len(ks) < 1 || ks[0] != rpctypes.MetadataHasLeader {
continue
}
close(ka.chs[i])
ka.chs[i] = nil
reqIdxs++
}
if reqIdxs == 0 {
continue
}
// remove all channels that required a leader from keepalive
newChs := make([]chan<- *LeaseKeepAliveResponse, len(ka.chs)-reqIdxs)
newCtxs := make([]context.Context, len(newChs))
newIdx := 0
for i := range ka.chs {
if ka.chs[i] == nil {
continue
}
newChs[newIdx], newCtxs[newIdx] = ka.chs[i], ka.ctxs[newIdx]
newIdx++
}
ka.chs, ka.ctxs = newChs, newCtxs
}
}
func (l *lessor) keepAliveOnce(ctx context.Context, id LeaseID) (karesp *LeaseKeepAliveResponse, ferr error) {
cctx, cancel := context.WithCancel(ctx)
defer cancel()
stream, err := l.remote.LeaseKeepAlive(cctx, l.callOpts...)
if err != nil {
return nil, ContextError(ctx, err)
}
defer func() {
if cerr := stream.CloseSend(); cerr != nil {
if ferr == nil {
ferr = ContextError(ctx, cerr)
}
return
}
}()
err = stream.Send(&pb.LeaseKeepAliveRequest{ID: int64(id)})
if err != nil {
return nil, ContextError(ctx, err)
}
resp, rerr := stream.Recv()
if rerr != nil {
return nil, ContextError(ctx, rerr)
}
karesp = &LeaseKeepAliveResponse{
ResponseHeader: resp.GetHeader(),
ID: LeaseID(resp.ID),
TTL: resp.TTL,
}
return karesp, nil
}
func (l *lessor) recvKeepAliveLoop() (gerr error) {
defer func() {
l.mu.Lock()
close(l.donec)
l.loopErr = gerr
for _, ka := range l.keepAlives {
ka.close()
}
l.keepAlives = make(map[LeaseID]*keepAlive)
l.mu.Unlock()
}()
for {
stream, err := l.resetRecv()
if err != nil {
l.lg.Warn("error occurred during lease keep alive loop",
zap.Error(err),
)
if canceledByCaller(l.stopCtx, err) {
return err
}
} else {
for {
resp, err := stream.Recv()
if err != nil {
if canceledByCaller(l.stopCtx, err) {
return err
}
if errors.Is(ContextError(l.stopCtx, err), rpctypes.ErrNoLeader) {
l.closeRequireLeader()
}
break
}
l.recvKeepAlive(resp)
}
}
select {
case <-time.After(retryConnWait):
case <-l.stopCtx.Done():
return l.stopCtx.Err()
}
}
}
// resetRecv opens a new lease stream and starts sending keep alive requests.
func (l *lessor) resetRecv() (pb.Lease_LeaseKeepAliveClient, error) {
sctx, cancel := context.WithCancel(l.stopCtx)
stream, err := l.remote.LeaseKeepAlive(sctx, append(l.callOpts, withMax(0))...)
if err != nil {
cancel()
return nil, err
}
l.mu.Lock()
defer l.mu.Unlock()
if l.stream != nil && l.streamCancel != nil {
l.streamCancel()
}
l.streamCancel = cancel
l.stream = stream
go l.sendKeepAliveLoop(stream)
return stream, nil
}
// recvKeepAlive updates a lease based on its LeaseKeepAliveResponse
func (l *lessor) recvKeepAlive(resp *pb.LeaseKeepAliveResponse) {
karesp := &LeaseKeepAliveResponse{
ResponseHeader: resp.GetHeader(),
ID: LeaseID(resp.ID),
TTL: resp.TTL,
}
l.mu.Lock()
defer l.mu.Unlock()
ka, ok := l.keepAlives[karesp.ID]
if !ok {
return
}
if karesp.TTL <= 0 {
// lease expired; close all keep alive channels
delete(l.keepAlives, karesp.ID)
ka.close()
return
}
// send update to all channels
nextKeepAlive := time.Now().Add((time.Duration(karesp.TTL) * time.Second) / 3.0)
ka.deadline = time.Now().Add(time.Duration(karesp.TTL) * time.Second)
for _, ch := range ka.chs {
select {
case ch <- karesp:
default:
if l.lg != nil {
l.lg.Warn("lease keepalive response queue is full; dropping response send",
zap.Int("queue-size", len(ch)),
zap.Int("queue-capacity", cap(ch)),
)
}
}
// still advance in order to rate-limit keep-alive sends
ka.nextKeepAlive = nextKeepAlive
}
}
// deadlineLoop reaps any keep alive channels that have not received a response
// within the lease TTL
func (l *lessor) deadlineLoop() {
timer := time.NewTimer(time.Second)
defer timer.Stop()
for {
timer.Reset(time.Second)
select {
case <-timer.C:
case <-l.donec:
return
}
now := time.Now()
l.mu.Lock()
for id, ka := range l.keepAlives {
if ka.deadline.Before(now) {
// waited too long for response; lease may be expired
ka.close()
delete(l.keepAlives, id)
}
}
l.mu.Unlock()
}
}
// sendKeepAliveLoop sends keep alive requests for the lifetime of the given stream.
func (l *lessor) sendKeepAliveLoop(stream pb.Lease_LeaseKeepAliveClient) {
for {
var tosend []LeaseID
now := time.Now()
l.mu.Lock()
for id, ka := range l.keepAlives {
if ka.nextKeepAlive.Before(now) {
tosend = append(tosend, id)
}
}
l.mu.Unlock()
for _, id := range tosend {
r := &pb.LeaseKeepAliveRequest{ID: int64(id)}
if err := stream.Send(r); err != nil {
l.lg.Warn("error occurred during lease keep alive request sending",
zap.Error(err),
)
return
}
}
select {
case <-time.After(retryConnWait):
case <-stream.Context().Done():
return
case <-l.donec:
return
case <-l.stopCtx.Done():
return
}
}
}
func (ka *keepAlive) close() {
close(ka.donec)
for _, ch := range ka.chs {
close(ch)
}
}
================================================
FILE: client/v3/leasing/cache.go
================================================
// Copyright 2017 The etcd Authors
//
// 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 leasing
import (
"context"
"strings"
"sync"
"time"
v3pb "go.etcd.io/etcd/api/v3/etcdserverpb"
"go.etcd.io/etcd/api/v3/mvccpb"
v3 "go.etcd.io/etcd/client/v3"
)
const revokeBackoff = 2 * time.Second
type leaseCache struct {
mu sync.RWMutex
entries map[string]*leaseKey
revokes map[string]time.Time
header *v3pb.ResponseHeader
}
type leaseKey struct {
response *v3.GetResponse
// rev is the leasing key revision.
rev int64
waitc chan struct{}
}
func (lc *leaseCache) Rev(key string) int64 {
lc.mu.RLock()
defer lc.mu.RUnlock()
if li := lc.entries[key]; li != nil {
return li.rev
}
return 0
}
func (lc *leaseCache) Lock(key string) (chan<- struct{}, int64) {
lc.mu.Lock()
defer lc.mu.Unlock()
if li := lc.entries[key]; li != nil {
li.waitc = make(chan struct{})
return li.waitc, li.rev
}
return nil, 0
}
func (lc *leaseCache) LockRange(begin, end string) (ret []chan<- struct{}) {
lc.mu.Lock()
defer lc.mu.Unlock()
for k, li := range lc.entries {
if inRange(k, begin, end) {
li.waitc = make(chan struct{})
ret = append(ret, li.waitc)
}
}
return ret
}
func inRange(k, begin, end string) bool {
if strings.Compare(k, begin) < 0 {
return false
}
if end != "\x00" && strings.Compare(k, end) >= 0 {
return false
}
return true
}
func (lc *leaseCache) LockWriteOps(ops []v3.Op) (ret []chan<- struct{}) {
for _, op := range ops {
if op.IsGet() {
continue
}
key := string(op.KeyBytes())
if end := string(op.RangeBytes()); end == "" {
if wc, _ := lc.Lock(key); wc != nil {
ret = append(ret, wc)
}
} else {
for k := range lc.entries {
if !inRange(k, key, end) {
continue
}
if wc, _ := lc.Lock(k); wc != nil {
ret = append(ret, wc)
}
}
}
}
return ret
}
func (lc *leaseCache) NotifyOps(ops []v3.Op) (wcs []<-chan struct{}) {
for _, op := range ops {
if op.IsGet() {
if _, wc := lc.notify(string(op.KeyBytes())); wc != nil {
wcs = append(wcs, wc)
}
}
}
return wcs
}
func (lc *leaseCache) MayAcquire(key string) bool {
lc.mu.RLock()
lr, ok := lc.revokes[key]
lc.mu.RUnlock()
return !ok || time.Since(lr) > revokeBackoff
}
func (lc *leaseCache) Add(key string, resp *v3.GetResponse, op v3.Op) *v3.GetResponse {
lk := &leaseKey{resp, resp.Header.Revision, closedCh}
lc.mu.Lock()
if lc.header == nil || lc.header.Revision < resp.Header.Revision {
lc.header = resp.Header
}
lc.entries[key] = lk
ret := lk.get(op)
lc.mu.Unlock()
return ret
}
func (lc *leaseCache) Update(key, val []byte, respHeader *v3pb.ResponseHeader) {
li := lc.entries[string(key)]
if li == nil {
return
}
cacheResp := li.response
if len(cacheResp.Kvs) == 0 {
kv := &mvccpb.KeyValue{
Key: key,
CreateRevision: respHeader.Revision,
}
cacheResp.Kvs = append(cacheResp.Kvs, kv)
cacheResp.Count = 1
}
cacheResp.Kvs[0].Version++
if cacheResp.Kvs[0].ModRevision < respHeader.Revision {
cacheResp.Header = respHeader
cacheResp.Kvs[0].ModRevision = respHeader.Revision
cacheResp.Kvs[0].Value = val
}
}
func (lc *leaseCache) Delete(key string, hdr *v3pb.ResponseHeader) {
lc.mu.Lock()
defer lc.mu.Unlock()
lc.delete(key, hdr)
}
func (lc *leaseCache) delete(key string, hdr *v3pb.ResponseHeader) {
if li := lc.entries[key]; li != nil && hdr.Revision >= li.response.Header.Revision {
li.response.Kvs = nil
li.response.Header = copyHeader(hdr)
}
}
func (lc *leaseCache) Evict(key string) (rev int64) {
lc.mu.Lock()
defer lc.mu.Unlock()
if li := lc.entries[key]; li != nil {
rev = li.rev
delete(lc.entries, key)
lc.revokes[key] = time.Now()
}
return rev
}
func (lc *leaseCache) EvictRange(key, end string) {
lc.mu.Lock()
defer lc.mu.Unlock()
for k := range lc.entries {
if inRange(k, key, end) {
delete(lc.entries, key)
lc.revokes[key] = time.Now()
}
}
}
func isBadOp(op v3.Op) bool { return op.Rev() > 0 || len(op.RangeBytes()) > 0 }
func (lc *leaseCache) Get(ctx context.Context, op v3.Op) (*v3.GetResponse, bool) {
if isBadOp(op) {
return nil, false
}
key := string(op.KeyBytes())
li, wc := lc.notify(key)
if li == nil {
return nil, true
}
select {
case <-wc:
case <-ctx.Done():
return nil, true
}
lc.mu.RLock()
lk := *li
ret := lk.get(op)
lc.mu.RUnlock()
return ret, true
}
func (lk *leaseKey) get(op v3.Op) *v3.GetResponse {
ret := *lk.response
ret.Header = copyHeader(ret.Header)
empty := len(ret.Kvs) == 0 || op.IsCountOnly()
empty = empty || (op.MinModRev() > ret.Kvs[0].ModRevision)
empty = empty || (op.MaxModRev() != 0 && op.MaxModRev() < ret.Kvs[0].ModRevision)
empty = empty || (op.MinCreateRev() > ret.Kvs[0].CreateRevision)
empty = empty || (op.MaxCreateRev() != 0 && op.MaxCreateRev() < ret.Kvs[0].CreateRevision)
if empty {
ret.Kvs = nil
} else {
kv := *ret.Kvs[0]
kv.Key = make([]byte, len(kv.Key))
copy(kv.Key, ret.Kvs[0].Key)
if !op.IsKeysOnly() {
kv.Value = make([]byte, len(kv.Value))
copy(kv.Value, ret.Kvs[0].Value)
}
ret.Kvs = []*mvccpb.KeyValue{&kv}
}
return &ret
}
func (lc *leaseCache) notify(key string) (*leaseKey, <-chan struct{}) {
lc.mu.RLock()
defer lc.mu.RUnlock()
if li := lc.entries[key]; li != nil {
return li, li.waitc
}
return nil, nil
}
func (lc *leaseCache) clearOldRevokes(ctx context.Context) {
for {
select {
case <-ctx.Done():
return
case <-time.After(time.Second):
lc.mu.Lock()
for k, lr := range lc.revokes {
if time.Since(lr.Add(revokeBackoff)) > 0 {
delete(lc.revokes, k)
}
}
lc.mu.Unlock()
}
}
}
func (lc *leaseCache) evalCmp(cmps []v3.Cmp) (cmpVal bool, ok bool) {
for _, cmp := range cmps {
if len(cmp.RangeEnd) > 0 {
return false, false
}
lk := lc.entries[string(cmp.Key)]
if lk == nil {
return false, false
}
if !evalCmp(lk.response, cmp) {
return false, true
}
}
return true, true
}
func (lc *leaseCache) evalOps(ops []v3.Op) ([]*v3pb.ResponseOp, bool) {
resps := make([]*v3pb.ResponseOp, len(ops))
for i, op := range ops {
if !op.IsGet() || isBadOp(op) {
// TODO: support read-only Txn
return nil, false
}
lk := lc.entries[string(op.KeyBytes())]
if lk == nil {
return nil, false
}
resp := lk.get(op)
if resp == nil {
return nil, false
}
resps[i] = &v3pb.ResponseOp{
Response: &v3pb.ResponseOp_ResponseRange{
ResponseRange: (*v3pb.RangeResponse)(resp),
},
}
}
return resps, true
}
================================================
FILE: client/v3/leasing/doc.go
================================================
// Copyright 2017 The etcd Authors
//
// 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 leasing serves linearizable reads from a local cache by acquiring
// exclusive write access to keys through a client-side leasing protocol. This
// leasing layer can either directly wrap the etcd client or it can be exposed
// through the etcd grpc proxy server, granting multiple clients write access.
//
// First, create a leasing KV from a clientv3.Client 'cli':
//
// lkv, err := leasing.NewKV(cli, "leasing-prefix")
// if err != nil {
// // handle error
// }
//
// A range request for a key "abc" tries to acquire a leasing key so it can cache the range's
// key locally. On the server, the leasing key is stored to "leasing-prefix/abc":
//
// resp, err := lkv.Get(context.TODO(), "abc")
//
// Future linearized read requests using 'lkv' will be served locally for the lease's lifetime:
//
// resp, err = lkv.Get(context.TODO(), "abc")
//
// If another leasing client writes to a leased key, then the owner relinquishes its exclusive
// access, permitting the writer to modify the key:
//
// lkv2, err := leasing.NewKV(cli, "leasing-prefix")
// if err != nil {
// // handle error
// }
// lkv2.Put(context.TODO(), "abc", "456")
// resp, err = lkv.Get("abc")
package leasing
================================================
FILE: client/v3/leasing/kv.go
================================================
// Copyright 2017 The etcd Authors
//
// 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 leasing
import (
"context"
"errors"
"strings"
"sync"
"time"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
pb "go.etcd.io/etcd/api/v3/etcdserverpb"
"go.etcd.io/etcd/api/v3/mvccpb"
"go.etcd.io/etcd/api/v3/v3rpc/rpctypes"
v3 "go.etcd.io/etcd/client/v3"
"go.etcd.io/etcd/client/v3/concurrency"
)
type leasingKV struct {
cl *v3.Client
kv v3.KV
pfx string
leases leaseCache
ctx context.Context
cancel context.CancelFunc
wg sync.WaitGroup
sessionOpts []concurrency.SessionOption
session *concurrency.Session
sessionc chan struct{}
}
var closedCh chan struct{}
func init() {
closedCh = make(chan struct{})
close(closedCh)
}
// NewKV wraps a KV instance so that all requests are wired through a leasing protocol.
func NewKV(cl *v3.Client, pfx string, opts ...concurrency.SessionOption) (v3.KV, func(), error) {
cctx, cancel := context.WithCancel(cl.Ctx())
lkv := &leasingKV{
cl: cl,
kv: cl.KV,
pfx: pfx,
leases: leaseCache{revokes: make(map[string]time.Time)},
ctx: cctx,
cancel: cancel,
sessionOpts: opts,
sessionc: make(chan struct{}),
}
lkv.wg.Add(2)
go func() {
defer lkv.wg.Done()
lkv.monitorSession()
}()
go func() {
defer lkv.wg.Done()
lkv.leases.clearOldRevokes(cctx)
}()
return lkv, lkv.Close, lkv.waitSession(cctx)
}
func (lkv *leasingKV) Close() {
lkv.cancel()
lkv.wg.Wait()
}
func (lkv *leasingKV) Get(ctx context.Context, key string, opts ...v3.OpOption) (*v3.GetResponse, error) {
return lkv.get(ctx, v3.OpGet(key, opts...))
}
func (lkv *leasingKV) Put(ctx context.Context, key, val string, opts ...v3.OpOption) (*v3.PutResponse, error) {
return lkv.put(ctx, v3.OpPut(key, val, opts...))
}
func (lkv *leasingKV) Delete(ctx context.Context, key string, opts ...v3.OpOption) (*v3.DeleteResponse, error) {
return lkv.delete(ctx, v3.OpDelete(key, opts...))
}
func (lkv *leasingKV) Do(ctx context.Context, op v3.Op) (v3.OpResponse, error) {
switch {
case op.IsGet():
resp, err := lkv.get(ctx, op)
return resp.OpResponse(), err
case op.IsPut():
resp, err := lkv.put(ctx, op)
return resp.OpResponse(), err
case op.IsDelete():
resp, err := lkv.delete(ctx, op)
return resp.OpResponse(), err
case op.IsTxn():
cmps, thenOps, elseOps := op.Txn()
resp, err := lkv.Txn(ctx).If(cmps...).Then(thenOps...).Else(elseOps...).Commit()
return resp.OpResponse(), err
}
return v3.OpResponse{}, nil
}
func (lkv *leasingKV) Compact(ctx context.Context, rev int64, opts ...v3.CompactOption) (*v3.CompactResponse, error) {
return lkv.kv.Compact(ctx, rev, opts...)
}
func (lkv *leasingKV) Txn(ctx context.Context) v3.Txn {
return &txnLeasing{Txn: lkv.kv.Txn(ctx), lkv: lkv, ctx: ctx}
}
func (lkv *leasingKV) monitorSession() {
for lkv.ctx.Err() == nil {
if lkv.session != nil {
select {
case <-lkv.session.Done():
case <-lkv.ctx.Done():
return
}
}
lkv.leases.mu.Lock()
select {
case <-lkv.sessionc:
lkv.sessionc = make(chan struct{})
default:
}
lkv.leases.entries = make(map[string]*leaseKey)
lkv.leases.mu.Unlock()
s, err := concurrency.NewSession(lkv.cl, lkv.sessionOpts...)
if err != nil {
continue
}
lkv.leases.mu.Lock()
lkv.session = s
close(lkv.sessionc)
lkv.leases.mu.Unlock()
}
}
func (lkv *leasingKV) monitorLease(ctx context.Context, key string, rev int64) {
cctx, cancel := context.WithCancel(lkv.ctx)
defer cancel()
for cctx.Err() == nil {
if rev == 0 {
resp, err := lkv.kv.Get(ctx, lkv.pfx+key)
if err != nil {
continue
}
rev = resp.Header.Revision
if len(resp.Kvs) == 0 || string(resp.Kvs[0].Value) == "REVOKE" {
lkv.rescind(cctx, key, rev)
return
}
}
wch := lkv.cl.Watch(cctx, lkv.pfx+key, v3.WithRev(rev+1))
for resp := range wch {
for _, ev := range resp.Events {
if string(ev.Kv.Value) != "REVOKE" {
continue
}
if v3.LeaseID(ev.Kv.Lease) == lkv.leaseID() {
lkv.rescind(cctx, key, ev.Kv.ModRevision)
}
return
}
}
rev = 0
}
}
// rescind releases a lease from this client.
func (lkv *leasingKV) rescind(ctx context.Context, key string, rev int64) {
if lkv.leases.Evict(key) > rev {
return
}
cmp := v3.Compare(v3.CreateRevision(lkv.pfx+key), "<", rev)
op := v3.OpDelete(lkv.pfx + key)
for ctx.Err() == nil {
if _, err := lkv.kv.Txn(ctx).If(cmp).Then(op).Commit(); err == nil {
return
}
}
}
func (lkv *leasingKV) waitRescind(ctx context.Context, key string, rev int64) error {
cctx, cancel := context.WithCancel(ctx)
defer cancel()
wch := lkv.cl.Watch(cctx, lkv.pfx+key, v3.WithRev(rev+1))
for resp := range wch {
for _, ev := range resp.Events {
if ev.Type == v3.EventTypeDelete {
return ctx.Err()
}
}
}
return ctx.Err()
}
func (lkv *leasingKV) tryModifyOp(ctx context.Context, op v3.Op) (*v3.TxnResponse, chan<- struct{}, error) {
key := string(op.KeyBytes())
wc, rev := lkv.leases.Lock(key)
cmp := v3.Compare(v3.CreateRevision(lkv.pfx+key), "<", rev+1)
resp, err := lkv.kv.Txn(ctx).If(cmp).Then(op).Commit()
switch {
case err != nil:
lkv.leases.Evict(key)
fallthrough
case !resp.Succeeded:
if wc != nil {
close(wc)
}
return nil, nil, err
}
return resp, wc, nil
}
func (lkv *leasingKV) put(ctx context.Context, op v3.Op) (pr *v3.PutResponse, err error) {
if err := lkv.waitSession(ctx); err != nil {
return nil, err
}
for ctx.Err() == nil {
resp, wc, err := lkv.tryModifyOp(ctx, op)
if err != nil || wc == nil {
resp, err = lkv.revoke(ctx, string(op.KeyBytes()), op)
}
if err != nil {
return nil, err
}
if resp.Succeeded {
lkv.leases.mu.Lock()
lkv.leases.Update(op.KeyBytes(), op.ValueBytes(), resp.Header)
lkv.leases.mu.Unlock()
pr = (*v3.PutResponse)(resp.Responses[0].GetResponsePut())
pr.Header = resp.Header
}
if wc != nil {
close(wc)
}
if resp.Succeeded {
return pr, nil
}
}
return nil, ctx.Err()
}
func (lkv *leasingKV) acquire(ctx context.Context, key string, op v3.Op) (*v3.TxnResponse, error) {
for ctx.Err() == nil {
if err := lkv.waitSession(ctx); err != nil {
return nil, err
}
lcmp := v3.Cmp{Key: []byte(key), Target: pb.Compare_LEASE}
resp, err := lkv.kv.Txn(ctx).If(
v3.Compare(v3.CreateRevision(lkv.pfx+key), "=", 0),
v3.Compare(lcmp, "=", 0)).
Then(
op,
v3.OpPut(lkv.pfx+key, "", v3.WithLease(lkv.leaseID()))).
Else(
op,
v3.OpGet(lkv.pfx+key),
).Commit()
if err == nil {
if !resp.Succeeded {
kvs := resp.Responses[1].GetResponseRange().Kvs
// if txn failed since already owner, lease is acquired
resp.Succeeded = len(kvs) > 0 && v3.LeaseID(kvs[0].Lease) == lkv.leaseID()
}
return resp, nil
}
// retry if transient error
var serverErr rpctypes.EtcdError
if errors.As(err, &serverErr) {
return nil, err
}
if ev, ok := status.FromError(err); ok && ev.Code() != codes.Unavailable {
return nil, err
}
}
return nil, ctx.Err()
}
func (lkv *leasingKV) get(ctx context.Context, op v3.Op) (*v3.GetResponse, error) {
do := func() (*v3.GetResponse, error) {
r, err := lkv.kv.Do(ctx, op)
return r.Get(), err
}
if !lkv.readySession() {
return do()
}
if resp, ok := lkv.leases.Get(ctx, op); resp != nil {
return resp, nil
} else if !ok || op.IsSerializable() {
// must be handled by server or can skip linearization
return do()
}
key := string(op.KeyBytes())
if !lkv.leases.MayAcquire(key) {
resp, err := lkv.kv.Do(ctx, op)
return resp.Get(), err
}
resp, err := lkv.acquire(ctx, key, v3.OpGet(key))
if err != nil {
return nil, err
}
getResp := (*v3.GetResponse)(resp.Responses[0].GetResponseRange())
getResp.Header = resp.Header
if resp.Succeeded {
getResp = lkv.leases.Add(key, getResp, op)
lkv.wg.Add(1)
go func() {
defer lkv.wg.Done()
lkv.monitorLease(ctx, key, resp.Header.Revision)
}()
}
return getResp, nil
}
func (lkv *leasingKV) deleteRangeRPC(ctx context.Context, maxLeaseRev int64, key, end string) (*v3.DeleteResponse, error) {
lkey, lend := lkv.pfx+key, lkv.pfx+end
resp, err := lkv.kv.Txn(ctx).If(
v3.Compare(v3.CreateRevision(lkey).WithRange(lend), "<", maxLeaseRev+1),
).Then(
v3.OpGet(key, v3.WithRange(end), v3.WithKeysOnly()),
v3.OpDelete(key, v3.WithRange(end)),
).Commit()
if err != nil {
lkv.leases.EvictRange(key, end)
return nil, err
}
if !resp.Succeeded {
return nil, nil
}
for _, kv := range resp.Responses[0].GetResponseRange().Kvs {
lkv.leases.Delete(string(kv.Key), resp.Header)
}
delResp := (*v3.DeleteResponse)(resp.Responses[1].GetResponseDeleteRange())
delResp.Header = resp.Header
return delResp, nil
}
func (lkv *leasingKV) deleteRange(ctx context.Context, op v3.Op) (*v3.DeleteResponse, error) {
key, end := string(op.KeyBytes()), string(op.RangeBytes())
for ctx.Err() == nil {
maxLeaseRev, err := lkv.revokeRange(ctx, key, end)
if err != nil {
return nil, err
}
wcs := lkv.leases.LockRange(key, end)
delResp, err := lkv.deleteRangeRPC(ctx, maxLeaseRev, key, end)
closeAll(wcs)
if err != nil || delResp != nil {
return delResp, err
}
}
return nil, ctx.Err()
}
func (lkv *leasingKV) delete(ctx context.Context, op v3.Op) (dr *v3.DeleteResponse, err error) {
if err := lkv.waitSession(ctx); err != nil {
return nil, err
}
if len(op.RangeBytes()) > 0 {
return lkv.deleteRange(ctx, op)
}
key := string(op.KeyBytes())
for ctx.Err() == nil {
resp, wc, err := lkv.tryModifyOp(ctx, op)
if err != nil || wc == nil {
resp, err = lkv.revoke(ctx, key, op)
}
if err != nil {
// don't know if delete was processed
lkv.leases.Evict(key)
return nil, err
}
if resp.Succeeded {
dr = (*v3.DeleteResponse)(resp.Responses[0].GetResponseDeleteRange())
dr.Header = resp.Header
lkv.leases.Delete(key, dr.Header)
}
if wc != nil {
close(wc)
}
if resp.Succeeded {
return dr, nil
}
}
return nil, ctx.Err()
}
func (lkv *leasingKV) revoke(ctx context.Context, key string, op v3.Op) (*v3.TxnResponse, error) {
rev := lkv.leases.Rev(key)
txn := lkv.kv.Txn(ctx).If(v3.Compare(v3.CreateRevision(lkv.pfx+key), "<", rev+1)).Then(op)
resp, err := txn.Else(v3.OpPut(lkv.pfx+key, "REVOKE", v3.WithIgnoreLease())).Commit()
if err != nil || resp.Succeeded {
return resp, err
}
return resp, lkv.waitRescind(ctx, key, resp.Header.Revision)
}
func (lkv *leasingKV) revokeRange(ctx context.Context, begin, end string) (int64, error) {
lkey, lend := lkv.pfx+begin, ""
if len(end) > 0 {
lend = lkv.pfx + end
}
leaseKeys, err := lkv.kv.Get(ctx, lkey, v3.WithRange(lend))
if err != nil {
return 0, err
}
return lkv.revokeLeaseKvs(ctx, leaseKeys.Kvs)
}
func (lkv *leasingKV) revokeLeaseKvs(ctx context.Context, kvs []*mvccpb.KeyValue) (int64, error) {
maxLeaseRev := int64(0)
for _, kv := range kvs {
if rev := kv.CreateRevision; rev > maxLeaseRev {
maxLeaseRev = rev
}
if v3.LeaseID(kv.Lease) == lkv.leaseID() {
// don't revoke own keys
continue
}
key := strings.TrimPrefix(string(kv.Key), lkv.pfx)
if _, err := lkv.revoke(ctx, key, v3.OpGet(key)); err != nil {
return 0, err
}
}
return maxLeaseRev, nil
}
func (lkv *leasingKV) waitSession(ctx context.Context) error {
lkv.leases.mu.RLock()
sessionc := lkv.sessionc
lkv.leases.mu.RUnlock()
select {
case <-sessionc:
return nil
case <-lkv.ctx.Done():
return lkv.ctx.Err()
case <-ctx.Done():
return ctx.Err()
}
}
func (lkv *leasingKV) readySession() bool {
lkv.leases.mu.RLock()
defer lkv.leases.mu.RUnlock()
if lkv.session == nil {
return false
}
select {
case <-lkv.session.Done():
default:
return true
}
return false
}
func (lkv *leasingKV) leaseID() v3.LeaseID {
lkv.leases.mu.RLock()
defer lkv.leases.mu.RUnlock()
return lkv.session.Lease()
}
================================================
FILE: client/v3/leasing/txn.go
================================================
// Copyright 2017 The etcd Authors
//
// 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 leasing
import (
"context"
"strings"
v3pb "go.etcd.io/etcd/api/v3/etcdserverpb"
v3 "go.etcd.io/etcd/client/v3"
)
type txnLeasing struct {
v3.Txn
lkv *leasingKV
ctx context.Context
cs []v3.Cmp
opst []v3.Op
opse []v3.Op
}
func (txn *txnLeasing) If(cs ...v3.Cmp) v3.Txn {
txn.cs = append(txn.cs, cs...)
txn.Txn = txn.Txn.If(cs...)
return txn
}
func (txn *txnLeasing) Then(ops ...v3.Op) v3.Txn {
txn.opst = append(txn.opst, ops...)
txn.Txn = txn.Txn.Then(ops...)
return txn
}
func (txn *txnLeasing) Else(ops ...v3.Op) v3.Txn {
txn.opse = append(txn.opse, ops...)
txn.Txn = txn.Txn.Else(ops...)
return txn
}
func (txn *txnLeasing) Commit() (*v3.TxnResponse, error) {
if resp, err := txn.eval(); resp != nil || err != nil {
return resp, err
}
return txn.serverTxn()
}
func (txn *txnLeasing) eval() (*v3.TxnResponse, error) {
// TODO: wait on keys in comparisons
thenOps, elseOps := gatherOps(txn.opst), gatherOps(txn.opse)
ops := make([]v3.Op, 0, len(thenOps)+len(elseOps))
ops = append(ops, thenOps...)
ops = append(ops, elseOps...)
for _, ch := range txn.lkv.leases.NotifyOps(ops) {
select {
case <-ch:
case <-txn.ctx.Done():
return nil, txn.ctx.Err()
}
}
txn.lkv.leases.mu.RLock()
defer txn.lkv.leases.mu.RUnlock()
succeeded, ok := txn.lkv.leases.evalCmp(txn.cs)
if !ok || txn.lkv.leases.header == nil {
return nil, nil
}
if ops = txn.opst; !succeeded {
ops = txn.opse
}
resps, ok := txn.lkv.leases.evalOps(ops)
if !ok {
return nil, nil
}
return &v3.TxnResponse{Header: copyHeader(txn.lkv.leases.header), Succeeded: succeeded, Responses: resps}, nil
}
// fallback computes the ops to fetch all possible conflicting
// leasing keys for a list of ops.
func (txn *txnLeasing) fallback(ops []v3.Op) (fbOps []v3.Op) {
for _, op := range ops {
if op.IsGet() {
continue
}
lkey, lend := txn.lkv.pfx+string(op.KeyBytes()), ""
if len(op.RangeBytes()) > 0 {
lend = txn.lkv.pfx + string(op.RangeBytes())
}
fbOps = append(fbOps, v3.OpGet(lkey, v3.WithRange(lend)))
}
return fbOps
}
func (txn *txnLeasing) guardKeys(ops []v3.Op) (cmps []v3.Cmp) {
seen := make(map[string]bool)
for _, op := range ops {
key := string(op.KeyBytes())
if op.IsGet() || len(op.RangeBytes()) != 0 || seen[key] {
continue
}
rev := txn.lkv.leases.Rev(key)
cmps = append(cmps, v3.Compare(v3.CreateRevision(txn.lkv.pfx+key), "<", rev+1))
seen[key] = true
}
return cmps
}
func (txn *txnLeasing) guardRanges(ops []v3.Op) (cmps []v3.Cmp, err error) {
for _, op := range ops {
if op.IsGet() || len(op.RangeBytes()) == 0 {
continue
}
key, end := string(op.KeyBytes()), string(op.RangeBytes())
maxRevLK, err := txn.lkv.revokeRange(txn.ctx, key, end)
if err != nil {
return nil, err
}
opts := append(v3.WithLastRev(), v3.WithRange(end))
getResp, err := txn.lkv.kv.Get(txn.ctx, key, opts...)
if err != nil {
return nil, err
}
maxModRev := int64(0)
if len(getResp.Kvs) > 0 {
maxModRev = getResp.Kvs[0].ModRevision
}
noKeyUpdate := v3.Compare(v3.ModRevision(key).WithRange(end), "<", maxModRev+1)
noLeaseUpdate := v3.Compare(
v3.CreateRevision(txn.lkv.pfx+key).WithRange(txn.lkv.pfx+end),
"<",
maxRevLK+1)
cmps = append(cmps, noKeyUpdate, noLeaseUpdate)
}
return cmps, nil
}
func (txn *txnLeasing) guard(ops []v3.Op) ([]v3.Cmp, error) {
cmps := txn.guardKeys(ops)
rangeCmps, err := txn.guardRanges(ops)
return append(cmps, rangeCmps...), err
}
func (txn *txnLeasing) commitToCache(txnResp *v3pb.TxnResponse, userTxn v3.Op) {
ops := gatherResponseOps(txnResp.Responses, []v3.Op{userTxn})
txn.lkv.leases.mu.Lock()
for _, op := range ops {
key := string(op.KeyBytes())
if op.IsDelete() && len(op.RangeBytes()) > 0 {
end := string(op.RangeBytes())
for k := range txn.lkv.leases.entries {
if inRange(k, key, end) {
txn.lkv.leases.delete(k, txnResp.Header)
}
}
} else if op.IsDelete() {
txn.lkv.leases.delete(key, txnResp.Header)
}
if op.IsPut() {
txn.lkv.leases.Update(op.KeyBytes(), op.ValueBytes(), txnResp.Header)
}
}
txn.lkv.leases.mu.Unlock()
}
func (txn *txnLeasing) revokeFallback(fbResps []*v3pb.ResponseOp) error {
for _, resp := range fbResps {
_, err := txn.lkv.revokeLeaseKvs(txn.ctx, resp.GetResponseRange().Kvs)
if err != nil {
return err
}
}
return nil
}
func (txn *txnLeasing) serverTxn() (*v3.TxnResponse, error) {
if err := txn.lkv.waitSession(txn.ctx); err != nil {
return nil, err
}
userOps := gatherOps(append(txn.opst, txn.opse...))
userTxn := v3.OpTxn(txn.cs, txn.opst, txn.opse)
fbOps := txn.fallback(userOps)
defer closeAll(txn.lkv.leases.LockWriteOps(userOps))
for {
cmps, err := txn.guard(userOps)
if err != nil {
return nil, err
}
resp, err := txn.lkv.kv.Txn(txn.ctx).If(cmps...).Then(userTxn).Else(fbOps...).Commit()
if err != nil {
for _, cmp := range cmps {
txn.lkv.leases.Evict(strings.TrimPrefix(string(cmp.Key), txn.lkv.pfx))
}
return nil, err
}
if resp.Succeeded {
txn.commitToCache((*v3pb.TxnResponse)(resp), userTxn)
userResp := resp.Responses[0].GetResponseTxn()
userResp.Header = resp.Header
return (*v3.TxnResponse)(userResp), nil
}
if err := txn.revokeFallback(resp.Responses); err != nil {
return nil, err
}
}
}
================================================
FILE: client/v3/leasing/util.go
================================================
// Copyright 2017 The etcd Authors
//
// 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 leasing
import (
"bytes"
v3pb "go.etcd.io/etcd/api/v3/etcdserverpb"
v3 "go.etcd.io/etcd/client/v3"
)
func compareInt64(a, b int64) int {
switch {
case a < b:
return -1
case a > b:
return 1
default:
return 0
}
}
func evalCmp(resp *v3.GetResponse, tcmp v3.Cmp) bool {
var result int
if len(resp.Kvs) != 0 {
kv := resp.Kvs[0]
switch tcmp.Target {
case v3pb.Compare_VALUE:
if tv, _ := tcmp.TargetUnion.(*v3pb.Compare_Value); tv != nil {
result = bytes.Compare(kv.Value, tv.Value)
}
case v3pb.Compare_CREATE:
if tv, _ := tcmp.TargetUnion.(*v3pb.Compare_CreateRevision); tv != nil {
result = compareInt64(kv.CreateRevision, tv.CreateRevision)
}
case v3pb.Compare_MOD:
if tv, _ := tcmp.TargetUnion.(*v3pb.Compare_ModRevision); tv != nil {
result = compareInt64(kv.ModRevision, tv.ModRevision)
}
case v3pb.Compare_VERSION:
if tv, _ := tcmp.TargetUnion.(*v3pb.Compare_Version); tv != nil {
result = compareInt64(kv.Version, tv.Version)
}
}
}
switch tcmp.Result {
case v3pb.Compare_EQUAL:
return result == 0
case v3pb.Compare_NOT_EQUAL:
return result != 0
case v3pb.Compare_GREATER:
return result > 0
case v3pb.Compare_LESS:
return result < 0
}
return true
}
func gatherOps(ops []v3.Op) (ret []v3.Op) {
for _, op := range ops {
if !op.IsTxn() {
ret = append(ret, op)
continue
}
_, thenOps, elseOps := op.Txn()
ret = append(ret, gatherOps(append(thenOps, elseOps...))...)
}
return ret
}
func gatherResponseOps(resp []*v3pb.ResponseOp, ops []v3.Op) (ret []v3.Op) {
for i, op := range ops {
if !op.IsTxn() {
ret = append(ret, op)
continue
}
_, thenOps, elseOps := op.Txn()
if txnResp := resp[i].GetResponseTxn(); txnResp.Succeeded {
ret = append(ret, gatherResponseOps(txnResp.Responses, thenOps)...)
} else {
ret = append(ret, gatherResponseOps(txnResp.Responses, elseOps)...)
}
}
return ret
}
func copyHeader(hdr *v3pb.ResponseHeader) *v3pb.ResponseHeader {
h := *hdr
return &h
}
func closeAll(chs []chan<- struct{}) {
for _, ch := range chs {
close(ch)
}
}
================================================
FILE: client/v3/logger.go
================================================
// Copyright 2016 The etcd Authors
//
// 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 clientv3
import (
"log"
"os"
"go.uber.org/zap/zapcore"
"go.uber.org/zap/zapgrpc"
"google.golang.org/grpc/grpclog"
"go.etcd.io/etcd/client/pkg/v3/logutil"
)
func init() {
// We override grpc logger only when the environment variable is set
// in order to not interfere by default with user's code or other libraries.
if os.Getenv("ETCD_CLIENT_DEBUG") != "" {
lg, err := logutil.CreateDefaultZapLogger(ClientLogLevel())
if err != nil {
panic(err)
}
lg = lg.Named("etcd-client")
grpclog.SetLoggerV2(zapgrpc.NewLogger(lg))
}
}
// SetLogger sets grpc logger.
//
// Deprecated: use grpclog.SetLoggerV2 directly or grpc_zap.ReplaceGrpcLoggerV2.
func SetLogger(l grpclog.LoggerV2) {
grpclog.SetLoggerV2(l)
}
// ClientLogLevel translates ETCD_CLIENT_DEBUG into zap log level.
func ClientLogLevel() zapcore.Level {
envLevel := os.Getenv("ETCD_CLIENT_DEBUG")
if envLevel == "" || envLevel == "true" {
return zapcore.InfoLevel
}
var l zapcore.Level
if err := l.Set(envLevel); err != nil {
log.Print("Invalid value for environment variable 'ETCD_CLIENT_DEBUG'. Using default level: 'info'")
return zapcore.InfoLevel
}
return l
}
================================================
FILE: client/v3/main_test.go
================================================
// Copyright 2017 The etcd Authors
//
// 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 clientv3_test
import (
"testing"
"time"
"go.etcd.io/etcd/client/pkg/v3/testutil"
)
const (
dialTimeout = 5 * time.Second
requestTimeout = 10 * time.Second
)
func exampleEndpoints() []string { return nil }
func forUnitTestsRunInMockedContext(mocking func(), _example func()) {
mocking()
// TODO: Call 'example' when mocking() provides realistic mocking of transport.
// The real testing logic of examples gets executed
// as part of ./tests/integration/clientv3/integration/...
}
func TestMain(m *testing.M) {
testutil.MustTestMainWithLeakDetection(m)
}
================================================
FILE: client/v3/maintenance.go
================================================
// Copyright 2016 The etcd Authors
//
// 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 clientv3
import (
"context"
"errors"
"fmt"
"io"
"go.uber.org/zap"
"google.golang.org/grpc"
pb "go.etcd.io/etcd/api/v3/etcdserverpb"
)
type (
DefragmentResponse pb.DefragmentResponse
AlarmResponse pb.AlarmResponse
AlarmMember pb.AlarmMember
StatusResponse pb.StatusResponse
HashKVResponse pb.HashKVResponse
MoveLeaderResponse pb.MoveLeaderResponse
DowngradeResponse pb.DowngradeResponse
DowngradeAction pb.DowngradeRequest_DowngradeAction
)
const (
DowngradeValidate = DowngradeAction(pb.DowngradeRequest_VALIDATE)
DowngradeEnable = DowngradeAction(pb.DowngradeRequest_ENABLE)
DowngradeCancel = DowngradeAction(pb.DowngradeRequest_CANCEL)
)
type Maintenance interface {
// AlarmList gets all active alarms.
AlarmList(ctx context.Context) (*AlarmResponse, error)
// AlarmDisarm disarms a given alarm.
AlarmDisarm(ctx context.Context, m *AlarmMember) (*AlarmResponse, error)
// Defragment releases wasted space from internal fragmentation on a given etcd member.
// Defragment is only needed when deleting a large number of keys and want to reclaim
// the resources.
// Defragment is an expensive operation. User should avoid defragmenting multiple members
// at the same time.
// To defragment multiple members in the cluster, user need to call defragment multiple
// times with different endpoints.
Defragment(ctx context.Context, endpoint string) (*DefragmentResponse, error)
// Status gets the status of the endpoint.
Status(ctx context.Context, endpoint string) (*StatusResponse, error)
// HashKV returns a hash of the KV state at the time of the RPC.
// If revision is zero, the hash is computed on all keys. If the revision
// is non-zero, the hash is computed on all keys at or below the given revision.
HashKV(ctx context.Context, endpoint string, rev int64) (*HashKVResponse, error)
// SnapshotWithVersion returns a reader for a point-in-time snapshot and version of etcd that created it.
// If the context "ctx" is canceled or timed out, reading from returned
// "io.ReadCloser" would error out (e.g. context.Canceled, context.DeadlineExceeded).
SnapshotWithVersion(ctx context.Context) (*SnapshotResponse, error)
// Snapshot provides a reader for a point-in-time snapshot of etcd.
// If the context "ctx" is canceled or timed out, reading from returned
// "io.ReadCloser" would error out (e.g. context.Canceled, context.DeadlineExceeded).
// Deprecated: use SnapshotWithVersion instead.
Snapshot(ctx context.Context) (io.ReadCloser, error)
// MoveLeader requests current leader to transfer its leadership to the transferee.
// Request must be made to the leader.
MoveLeader(ctx context.Context, transfereeID uint64) (*MoveLeaderResponse, error)
// Downgrade requests downgrades, verifies feasibility or cancels downgrade
// on the cluster version.
// Supported since etcd 3.5.
Downgrade(ctx context.Context, action DowngradeAction, version string) (*DowngradeResponse, error)
}
// SnapshotResponse is aggregated response from the snapshot stream.
// Consumer is responsible for closing steam by calling .Snapshot.Close()
type SnapshotResponse struct {
// Header is the first header in the snapshot stream, has the current key-value store information
// and indicates the point in time of the snapshot.
Header *pb.ResponseHeader
// Snapshot exposes ReaderCloser interface for data stored in the Blob field in the snapshot stream.
Snapshot io.ReadCloser
// Version is the local version of server that created the snapshot.
// In cluster with binaries with different version, each cluster can return different result.
// Informs which etcd server version should be used when restoring the snapshot.
// Supported on etcd >= v3.6.
Version string
}
type maintenance struct {
lg *zap.Logger
dial func(endpoint string) (pb.MaintenanceClient, func(), error)
remote pb.MaintenanceClient
callOpts []grpc.CallOption
}
func NewMaintenance(c *Client) Maintenance {
api := &maintenance{
lg: c.GetLogger(),
dial: func(endpoint string) (pb.MaintenanceClient, func(), error) {
conn, err := c.Dial(endpoint)
if err != nil {
return nil, nil, fmt.Errorf("failed to dial endpoint %s with maintenance client: %w", endpoint, err)
}
cancel := func() { conn.Close() }
return RetryMaintenanceClient(c, conn), cancel, nil
},
remote: RetryMaintenanceClient(c, c.conn),
}
if c != nil {
api.callOpts = c.callOpts
}
return api
}
func NewMaintenanceFromMaintenanceClient(remote pb.MaintenanceClient, c *Client) Maintenance {
api := &maintenance{
dial: func(string) (pb.MaintenanceClient, func(), error) {
return remote, func() {}, nil
},
remote: remote,
}
if c != nil {
api.callOpts = c.callOpts
api.lg = c.GetLogger()
}
return api
}
func (m *maintenance) AlarmList(ctx context.Context) (*AlarmResponse, error) {
req := &pb.AlarmRequest{
Action: pb.AlarmRequest_GET,
MemberID: 0, // all
Alarm: pb.AlarmType_NONE, // all
}
resp, err := m.remote.Alarm(ctx, req, m.callOpts...)
if err == nil {
return (*AlarmResponse)(resp), nil
}
return nil, ContextError(ctx, err)
}
func (m *maintenance) AlarmDisarm(ctx context.Context, am *AlarmMember) (*AlarmResponse, error) {
req := &pb.AlarmRequest{
Action: pb.AlarmRequest_DEACTIVATE,
MemberID: am.MemberID,
Alarm: am.Alarm,
}
if req.MemberID == 0 && req.Alarm == pb.AlarmType_NONE {
ar, err := m.AlarmList(ctx)
if err != nil {
return nil, ContextError(ctx, err)
}
ret := AlarmResponse{}
for _, am := range ar.Alarms {
dresp, derr := m.AlarmDisarm(ctx, (*AlarmMember)(am))
if derr != nil {
return nil, ContextError(ctx, derr)
}
ret.Alarms = append(ret.Alarms, dresp.Alarms...)
}
return &ret, nil
}
resp, err := m.remote.Alarm(ctx, req, m.callOpts...)
if err == nil {
return (*AlarmResponse)(resp), nil
}
return nil, ContextError(ctx, err)
}
func (m *maintenance) Defragment(ctx context.Context, endpoint string) (*DefragmentResponse, error) {
remote, cancel, err := m.dial(endpoint)
if err != nil {
return nil, ContextError(ctx, err)
}
defer cancel()
resp, err := remote.Defragment(ctx, &pb.DefragmentRequest{}, m.callOpts...)
if err != nil {
return nil, ContextError(ctx, err)
}
return (*DefragmentResponse)(resp), nil
}
func (m *maintenance) Status(ctx context.Context, endpoint string) (*StatusResponse, error) {
remote, cancel, err := m.dial(endpoint)
if err != nil {
return nil, ContextError(ctx, err)
}
defer cancel()
resp, err := remote.Status(ctx, &pb.StatusRequest{}, m.callOpts...)
if err != nil {
return nil, ContextError(ctx, err)
}
return (*StatusResponse)(resp), nil
}
func (m *maintenance) HashKV(ctx context.Context, endpoint string, rev int64) (*HashKVResponse, error) {
remote, cancel, err := m.dial(endpoint)
if err != nil {
return nil, ContextError(ctx, err)
}
defer cancel()
resp, err := remote.HashKV(ctx, &pb.HashKVRequest{Revision: rev}, m.callOpts...)
if err != nil {
return nil, ContextError(ctx, err)
}
return (*HashKVResponse)(resp), nil
}
func (m *maintenance) SnapshotWithVersion(ctx context.Context) (*SnapshotResponse, error) {
ss, err := m.remote.Snapshot(ctx, &pb.SnapshotRequest{}, append(m.callOpts, withMax(defaultStreamMaxRetries))...)
if err != nil {
return nil, ContextError(ctx, err)
}
m.lg.Info("opened snapshot stream; downloading")
pr, pw := io.Pipe()
resp, err := ss.Recv()
if err != nil {
m.logAndCloseWithError(err, pw)
return nil, err
}
go func() {
// Saving response is blocking
err := m.save(resp, pw)
if err != nil {
m.logAndCloseWithError(err, pw)
return
}
for {
sresp, err := ss.Recv()
if err != nil {
m.logAndCloseWithError(err, pw)
return
}
err = m.save(sresp, pw)
if err != nil {
m.logAndCloseWithError(err, pw)
return
}
}
}()
return &SnapshotResponse{
Header: resp.GetHeader(),
Snapshot: &snapshotReadCloser{ctx: ctx, ReadCloser: pr},
Version: resp.GetVersion(),
}, nil
}
func (m *maintenance) Snapshot(ctx context.Context) (io.ReadCloser, error) {
ss, err := m.remote.Snapshot(ctx, &pb.SnapshotRequest{}, append(m.callOpts, withMax(defaultStreamMaxRetries))...)
if err != nil {
return nil, ContextError(ctx, err)
}
m.lg.Info("opened snapshot stream; downloading")
pr, pw := io.Pipe()
go func() {
for {
resp, err := ss.Recv()
if err != nil {
m.logAndCloseWithError(err, pw)
return
}
err = m.save(resp, pw)
if err != nil {
m.logAndCloseWithError(err, pw)
return
}
}
}()
return &snapshotReadCloser{ctx: ctx, ReadCloser: pr}, nil
}
func (m *maintenance) logAndCloseWithError(err error, pw *io.PipeWriter) {
switch {
case errors.Is(err, io.EOF):
m.lg.Info("completed snapshot read; closing")
default:
m.lg.Warn("failed to receive from snapshot stream; closing", zap.Error(err))
}
pw.CloseWithError(err)
}
func (m *maintenance) save(resp *pb.SnapshotResponse, pw *io.PipeWriter) error {
// can "resp == nil && err == nil"
// before we receive snapshot SHA digest?
// No, server sends EOF with an empty response
// after it sends SHA digest at the end
if _, werr := pw.Write(resp.Blob); werr != nil {
return werr
}
return nil
}
type snapshotReadCloser struct {
ctx context.Context
io.ReadCloser
}
func (rc *snapshotReadCloser) Read(p []byte) (n int, err error) {
n, err = rc.ReadCloser.Read(p)
return n, ContextError(rc.ctx, err)
}
func (m *maintenance) MoveLeader(ctx context.Context, transfereeID uint64) (*MoveLeaderResponse, error) {
resp, err := m.remote.MoveLeader(ctx, &pb.MoveLeaderRequest{TargetID: transfereeID}, m.callOpts...)
return (*MoveLeaderResponse)(resp), ContextError(ctx, err)
}
func (m *maintenance) Downgrade(ctx context.Context, action DowngradeAction, version string) (*DowngradeResponse, error) {
var actionType pb.DowngradeRequest_DowngradeAction
switch action {
case DowngradeValidate:
actionType = pb.DowngradeRequest_VALIDATE
case DowngradeEnable:
actionType = pb.DowngradeRequest_ENABLE
case DowngradeCancel:
actionType = pb.DowngradeRequest_CANCEL
default:
return nil, errors.New("etcdclient: unknown downgrade action")
}
resp, err := m.remote.Downgrade(ctx, &pb.DowngradeRequest{Action: actionType, Version: version}, m.callOpts...)
return (*DowngradeResponse)(resp), ContextError(ctx, err)
}
================================================
FILE: client/v3/mirror/syncer.go
================================================
// Copyright 2016 The etcd Authors
//
// 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 mirror implements etcd mirroring operations.
package mirror
import (
"context"
clientv3 "go.etcd.io/etcd/client/v3"
)
const (
batchLimit = 1000
)
// Syncer syncs with the key-value state of an etcd cluster.
type Syncer interface {
// SyncBase syncs the base state of the key-value state.
// The key-value state are sent through the returned chan.
SyncBase(ctx context.Context) (<-chan clientv3.GetResponse, chan error)
// SyncUpdates syncs the updates of the key-value state.
// The update events are sent through the returned chan.
SyncUpdates(ctx context.Context) clientv3.WatchChan
}
// NewSyncer creates a Syncer.
func NewSyncer(c *clientv3.Client, prefix string, rev int64) Syncer {
return &syncer{c: c, prefix: prefix, rev: rev}
}
type syncer struct {
c *clientv3.Client
rev int64
prefix string
}
func (s *syncer) SyncBase(ctx context.Context) (<-chan clientv3.GetResponse, chan error) {
respchan := make(chan clientv3.GetResponse, 1024)
errchan := make(chan error, 1)
// if rev is not specified, we will choose the most recent revision.
if s.rev == 0 {
// If len(s.prefix) == 0, we will check a random key to fetch the most recent
// revision (foo), otherwise we use the provided prefix.
checkPath := "foo"
if len(s.prefix) != 0 {
checkPath = s.prefix
}
resp, err := s.c.Get(ctx, checkPath)
if err != nil {
errchan <- err
close(respchan)
close(errchan)
return respchan, errchan
}
s.rev = resp.Header.Revision
}
go func() {
defer close(respchan)
defer close(errchan)
var key string
opts := []clientv3.OpOption{
clientv3.WithLimit(batchLimit), clientv3.WithRev(s.rev),
clientv3.WithSort(clientv3.SortByKey, clientv3.SortAscend),
}
if len(s.prefix) == 0 {
// If len(s.prefix) == 0, we will sync the entire key-value space.
// We then range from the smallest key (0x00) to the end.
opts = append(opts, clientv3.WithFromKey())
key = "\x00"
} else {
// If len(s.prefix) != 0, we will sync key-value space with given prefix.
// We then range from the prefix to the next prefix if exists. Or we will
// range from the prefix to the end if the next prefix does not exists.
opts = append(opts, clientv3.WithRange(clientv3.GetPrefixRangeEnd(s.prefix)))
key = s.prefix
}
for {
resp, err := s.c.Get(ctx, key, opts...)
if err != nil {
errchan <- err
return
}
respchan <- *resp
if !resp.More {
return
}
// move to next key
key = string(append(resp.Kvs[len(resp.Kvs)-1].Key, 0))
}
}()
return respchan, errchan
}
func (s *syncer) SyncUpdates(ctx context.Context) clientv3.WatchChan {
if s.rev == 0 {
panic("unexpected revision = 0. Calling SyncUpdates before SyncBase finishes?")
}
return s.c.Watch(ctx, s.prefix, clientv3.WithPrefix(), clientv3.WithRev(s.rev+1))
}
================================================
FILE: client/v3/mock/mockserver/doc.go
================================================
// Copyright 2018 The etcd Authors
//
// 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 mockserver provides mock implementations for etcdserver's server interface.
package mockserver
================================================
FILE: client/v3/mock/mockserver/mockserver.go
================================================
// Copyright 2018 The etcd Authors
//
// 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 mockserver
import (
"context"
"fmt"
"net"
"os"
"sync"
"google.golang.org/grpc"
"google.golang.org/grpc/resolver"
pb "go.etcd.io/etcd/api/v3/etcdserverpb"
)
// MockServer provides a mocked out grpc server of the etcdserver interface.
type MockServer struct {
ln net.Listener
Network string
Address string
GRPCServer *grpc.Server
}
func (ms *MockServer) ResolverAddress() resolver.Address {
switch ms.Network {
case "unix":
return resolver.Address{Addr: fmt.Sprintf("unix://%s", ms.Address)}
case "tcp":
return resolver.Address{Addr: ms.Address}
default:
panic("illegal network type: " + ms.Network)
}
}
// MockServers provides a cluster of mocket out gprc servers of the etcdserver interface.
type MockServers struct {
mu sync.RWMutex
Servers []*MockServer
wg sync.WaitGroup
}
// StartMockServers creates the desired count of mock servers
// and starts them.
func StartMockServers(count int) (ms *MockServers, err error) {
return StartMockServersOnNetwork(count, "tcp")
}
// StartMockServersOnNetwork creates mock servers on either 'tcp' or 'unix' sockets.
func StartMockServersOnNetwork(count int, network string) (ms *MockServers, err error) {
switch network {
case "tcp":
return startMockServersTCP(count)
case "unix":
return startMockServersUnix(count)
default:
return nil, fmt.Errorf("unsupported network type: %s", network)
}
}
func startMockServersTCP(count int) (ms *MockServers, err error) {
addrs := make([]string, 0, count)
for i := 0; i < count; i++ {
addrs = append(addrs, "localhost:0")
}
return startMockServers("tcp", addrs)
}
func startMockServersUnix(count int) (ms *MockServers, err error) {
dir := os.TempDir()
addrs := make([]string, 0, count)
for i := 0; i < count; i++ {
f, err := os.CreateTemp(dir, "etcd-unix-so-")
if err != nil {
return nil, fmt.Errorf("failed to allocate temp file for unix socket: %w", err)
}
fn := f.Name()
err = os.Remove(fn)
if err != nil {
return nil, fmt.Errorf("failed to remove temp file before creating unix socket: %w", err)
}
addrs = append(addrs, fn)
}
return startMockServers("unix", addrs)
}
func startMockServers(network string, addrs []string) (ms *MockServers, err error) {
ms = &MockServers{
Servers: make([]*MockServer, len(addrs)),
wg: sync.WaitGroup{},
}
defer func() {
if err != nil {
ms.Stop()
}
}()
for idx, addr := range addrs {
ln, err := net.Listen(network, addr)
if err != nil {
return nil, fmt.Errorf("failed to listen %w", err)
}
ms.Servers[idx] = &MockServer{ln: ln, Network: network, Address: ln.Addr().String()}
ms.StartAt(idx)
}
return ms, nil
}
// StartAt restarts mock server at given index.
func (ms *MockServers) StartAt(idx int) (err error) {
ms.mu.Lock()
defer ms.mu.Unlock()
if ms.Servers[idx].ln == nil {
ms.Servers[idx].ln, err = net.Listen(ms.Servers[idx].Network, ms.Servers[idx].Address)
if err != nil {
return fmt.Errorf("failed to listen %w", err)
}
}
svr := grpc.NewServer()
pb.RegisterKVServer(svr, &mockKVServer{})
pb.RegisterLeaseServer(svr, &mockLeaseServer{})
ms.Servers[idx].GRPCServer = svr
ms.wg.Add(1)
go func(svr *grpc.Server, l net.Listener) {
svr.Serve(l)
}(ms.Servers[idx].GRPCServer, ms.Servers[idx].ln)
return nil
}
// StopAt stops mock server at given index.
func (ms *MockServers) StopAt(idx int) {
ms.mu.Lock()
defer ms.mu.Unlock()
if ms.Servers[idx].ln == nil {
return
}
ms.Servers[idx].GRPCServer.Stop()
ms.Servers[idx].GRPCServer = nil
ms.Servers[idx].ln = nil
ms.wg.Done()
}
// Stop stops the mock server, immediately closing all open connections and listeners.
func (ms *MockServers) Stop() {
for idx := range ms.Servers {
ms.StopAt(idx)
}
ms.wg.Wait()
}
type mockKVServer struct {
// we want compile errors if new methods are added
pb.UnsafeKVServer
}
func (m *mockKVServer) Range(context.Context, *pb.RangeRequest) (*pb.RangeResponse, error) {
return &pb.RangeResponse{}, nil
}
func (m *mockKVServer) Put(context.Context, *pb.PutRequest) (*pb.PutResponse, error) {
return &pb.PutResponse{}, nil
}
func (m *mockKVServer) DeleteRange(context.Context, *pb.DeleteRangeRequest) (*pb.DeleteRangeResponse, error) {
return &pb.DeleteRangeResponse{}, nil
}
func (m *mockKVServer) Txn(context.Context, *pb.TxnRequest) (*pb.TxnResponse, error) {
return &pb.TxnResponse{}, nil
}
func (m *mockKVServer) Compact(context.Context, *pb.CompactionRequest) (*pb.CompactionResponse, error) {
return &pb.CompactionResponse{}, nil
}
func (m *mockKVServer) Lease(context.Context, *pb.LeaseGrantRequest) (*pb.LeaseGrantResponse, error) {
return &pb.LeaseGrantResponse{}, nil
}
type mockLeaseServer struct {
// we want compile errors if new methods are added
pb.UnsafeLeaseServer
}
func (s mockLeaseServer) LeaseGrant(context.Context, *pb.LeaseGrantRequest) (*pb.LeaseGrantResponse, error) {
return &pb.LeaseGrantResponse{}, nil
}
func (s *mockLeaseServer) LeaseRevoke(context.Context, *pb.LeaseRevokeRequest) (*pb.LeaseRevokeResponse, error) {
return &pb.LeaseRevokeResponse{}, nil
}
func (s *mockLeaseServer) LeaseKeepAlive(pb.Lease_LeaseKeepAliveServer) error {
return nil
}
func (s *mockLeaseServer) LeaseTimeToLive(context.Context, *pb.LeaseTimeToLiveRequest) (*pb.LeaseTimeToLiveResponse, error) {
return &pb.LeaseTimeToLiveResponse{}, nil
}
func (s *mockLeaseServer) LeaseLeases(context.Context, *pb.LeaseLeasesRequest) (*pb.LeaseLeasesResponse, error) {
return &pb.LeaseLeasesResponse{}, nil
}
================================================
FILE: client/v3/namespace/doc.go
================================================
// Copyright 2017 The etcd Authors
//
// 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 namespace is a clientv3 wrapper that translates all keys to begin
// with a given prefix.
//
// First, create a client:
//
// cli, err := clientv3.New(clientv3.Config{Endpoints: []string{"localhost:2379"}})
// if err != nil {
// // handle error!
// }
//
// Next, override the client interfaces:
//
// unprefixedKV := cli.KV
// cli.KV = namespace.NewKV(cli.KV, "my-prefix/")
// cli.Watcher = namespace.NewWatcher(cli.Watcher, "my-prefix/")
// cli.Lease = namespace.NewLease(cli.Lease, "my-prefix/")
//
// Now calls using 'cli' will namespace / prefix all keys with "my-prefix/":
//
// cli.Put(context.TODO(), "abc", "123")
// resp, _ := unprefixedKV.Get(context.TODO(), "my-prefix/abc")
// fmt.Printf("%s\n", resp.Kvs[0].Value)
// // Output: 123
// unprefixedKV.Put(context.TODO(), "my-prefix/abc", "456")
// resp, _ = cli.Get(context.TODO(), "abc")
// fmt.Printf("%s\n", resp.Kvs[0].Value)
// // Output: 456
package namespace
================================================
FILE: client/v3/namespace/kv.go
================================================
// Copyright 2017 The etcd Authors
//
// 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 namespace
import (
"context"
pb "go.etcd.io/etcd/api/v3/etcdserverpb"
"go.etcd.io/etcd/api/v3/v3rpc/rpctypes"
clientv3 "go.etcd.io/etcd/client/v3"
)
type kvPrefix struct {
clientv3.KV
pfx string
}
// NewKV wraps a KV instance so that all requests
// are prefixed with a given string.
func NewKV(kv clientv3.KV, prefix string) clientv3.KV {
return &kvPrefix{kv, prefix}
}
func (kv *kvPrefix) Put(ctx context.Context, key, val string, opts ...clientv3.OpOption) (*clientv3.PutResponse, error) {
if len(key) == 0 {
return nil, rpctypes.ErrEmptyKey
}
op := kv.prefixOp(clientv3.OpPut(key, val, opts...))
r, err := kv.KV.Do(ctx, op)
if err != nil {
return nil, err
}
put := r.Put()
kv.unprefixPutResponse(put)
return put, nil
}
func (kv *kvPrefix) Get(ctx context.Context, key string, opts ...clientv3.OpOption) (*clientv3.GetResponse, error) {
if len(key) == 0 && !(clientv3.IsOptsWithFromKey(opts) || clientv3.IsOptsWithPrefix(opts)) {
return nil, rpctypes.ErrEmptyKey
}
getOp := clientv3.OpGet(key, opts...)
if !getOp.IsSortOptionValid() {
return nil, rpctypes.ErrInvalidSortOption
}
r, err := kv.KV.Do(ctx, kv.prefixOp(getOp))
if err != nil {
return nil, err
}
get := r.Get()
kv.unprefixGetResponse(get)
return get, nil
}
func (kv *kvPrefix) Delete(ctx context.Context, key string, opts ...clientv3.OpOption) (*clientv3.DeleteResponse, error) {
if len(key) == 0 && !(clientv3.IsOptsWithFromKey(opts) || clientv3.IsOptsWithPrefix(opts)) {
return nil, rpctypes.ErrEmptyKey
}
r, err := kv.KV.Do(ctx, kv.prefixOp(clientv3.OpDelete(key, opts...)))
if err != nil {
return nil, err
}
del := r.Del()
kv.unprefixDeleteResponse(del)
return del, nil
}
func (kv *kvPrefix) Do(ctx context.Context, op clientv3.Op) (clientv3.OpResponse, error) {
if len(op.KeyBytes()) == 0 && !op.IsTxn() {
return clientv3.OpResponse{}, rpctypes.ErrEmptyKey
}
r, err := kv.KV.Do(ctx, kv.prefixOp(op))
if err != nil {
return r, err
}
switch {
case r.Get() != nil:
kv.unprefixGetResponse(r.Get())
case r.Put() != nil:
kv.unprefixPutResponse(r.Put())
case r.Del() != nil:
kv.unprefixDeleteResponse(r.Del())
case r.Txn() != nil:
kv.unprefixTxnResponse(r.Txn())
}
return r, nil
}
type txnPrefix struct {
clientv3.Txn
kv *kvPrefix
}
func (kv *kvPrefix) Txn(ctx context.Context) clientv3.Txn {
return &txnPrefix{kv.KV.Txn(ctx), kv}
}
func (txn *txnPrefix) If(cs ...clientv3.Cmp) clientv3.Txn {
txn.Txn = txn.Txn.If(txn.kv.prefixCmps(cs)...)
return txn
}
func (txn *txnPrefix) Then(ops ...clientv3.Op) clientv3.Txn {
txn.Txn = txn.Txn.Then(txn.kv.prefixOps(ops)...)
return txn
}
func (txn *txnPrefix) Else(ops ...clientv3.Op) clientv3.Txn {
txn.Txn = txn.Txn.Else(txn.kv.prefixOps(ops)...)
return txn
}
func (txn *txnPrefix) Commit() (*clientv3.TxnResponse, error) {
resp, err := txn.Txn.Commit()
if err != nil {
return nil, err
}
txn.kv.unprefixTxnResponse(resp)
return resp, nil
}
func (kv *kvPrefix) prefixOp(op clientv3.Op) clientv3.Op {
if !op.IsTxn() {
begin, end := kv.prefixInterval(op.KeyBytes(), op.RangeBytes())
op.WithKeyBytes(begin)
op.WithRangeBytes(end)
return op
}
cmps, thenOps, elseOps := op.Txn()
return clientv3.OpTxn(kv.prefixCmps(cmps), kv.prefixOps(thenOps), kv.prefixOps(elseOps))
}
func (kv *kvPrefix) unprefixGetResponse(resp *clientv3.GetResponse) {
for i := range resp.Kvs {
resp.Kvs[i].Key = resp.Kvs[i].Key[len(kv.pfx):]
}
}
func (kv *kvPrefix) unprefixPutResponse(resp *clientv3.PutResponse) {
if resp.PrevKv != nil {
resp.PrevKv.Key = resp.PrevKv.Key[len(kv.pfx):]
}
}
func (kv *kvPrefix) unprefixDeleteResponse(resp *clientv3.DeleteResponse) {
for i := range resp.PrevKvs {
resp.PrevKvs[i].Key = resp.PrevKvs[i].Key[len(kv.pfx):]
}
}
func (kv *kvPrefix) unprefixTxnResponse(resp *clientv3.TxnResponse) {
for _, r := range resp.Responses {
switch tv := r.Response.(type) {
case *pb.ResponseOp_ResponseRange:
if tv.ResponseRange != nil {
kv.unprefixGetResponse((*clientv3.GetResponse)(tv.ResponseRange))
}
case *pb.ResponseOp_ResponsePut:
if tv.ResponsePut != nil {
kv.unprefixPutResponse((*clientv3.PutResponse)(tv.ResponsePut))
}
case *pb.ResponseOp_ResponseDeleteRange:
if tv.ResponseDeleteRange != nil {
kv.unprefixDeleteResponse((*clientv3.DeleteResponse)(tv.ResponseDeleteRange))
}
case *pb.ResponseOp_ResponseTxn:
if tv.ResponseTxn != nil {
kv.unprefixTxnResponse((*clientv3.TxnResponse)(tv.ResponseTxn))
}
default:
}
}
}
func (kv *kvPrefix) prefixInterval(key, end []byte) (pfxKey []byte, pfxEnd []byte) {
return prefixInterval(kv.pfx, key, end)
}
func (kv *kvPrefix) prefixCmps(cs []clientv3.Cmp) []clientv3.Cmp {
newCmps := make([]clientv3.Cmp, len(cs))
for i := range cs {
newCmps[i] = cs[i]
pfxKey, endKey := kv.prefixInterval(cs[i].KeyBytes(), cs[i].RangeEnd)
newCmps[i].WithKeyBytes(pfxKey)
if len(cs[i].RangeEnd) != 0 {
newCmps[i].RangeEnd = endKey
}
}
return newCmps
}
func (kv *kvPrefix) prefixOps(ops []clientv3.Op) []clientv3.Op {
newOps := make([]clientv3.Op, len(ops))
for i := range ops {
newOps[i] = kv.prefixOp(ops[i])
}
return newOps
}
================================================
FILE: client/v3/namespace/lease.go
================================================
// Copyright 2017 The etcd Authors
//
// 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 namespace
import (
"bytes"
"context"
clientv3 "go.etcd.io/etcd/client/v3"
)
type leasePrefix struct {
clientv3.Lease
pfx []byte
}
// NewLease wraps a Lease interface to filter for only keys with a prefix
// and remove that prefix when fetching attached keys through TimeToLive.
func NewLease(l clientv3.Lease, prefix string) clientv3.Lease {
return &leasePrefix{l, []byte(prefix)}
}
func (l *leasePrefix) TimeToLive(ctx context.Context, id clientv3.LeaseID, opts ...clientv3.LeaseOption) (*clientv3.LeaseTimeToLiveResponse, error) {
resp, err := l.Lease.TimeToLive(ctx, id, opts...)
if err != nil {
return nil, err
}
if len(resp.Keys) > 0 {
var outKeys [][]byte
for i := range resp.Keys {
if len(resp.Keys[i]) < len(l.pfx) {
// too short
continue
}
if !bytes.Equal(resp.Keys[i][:len(l.pfx)], l.pfx) {
// doesn't match prefix
continue
}
// strip prefix
outKeys = append(outKeys, resp.Keys[i][len(l.pfx):])
}
resp.Keys = outKeys
}
return resp, nil
}
================================================
FILE: client/v3/namespace/util.go
================================================
// Copyright 2017 The etcd Authors
//
// 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 namespace
func prefixInterval(pfx string, key, end []byte) (pfxKey []byte, pfxEnd []byte) {
pfxKey = make([]byte, len(pfx)+len(key))
copy(pfxKey[copy(pfxKey, pfx):], key)
if len(end) == 1 && end[0] == 0 {
// the edge of the keyspace
pfxEnd = make([]byte, len(pfx))
copy(pfxEnd, pfx)
ok := false
for i := len(pfxEnd) - 1; i >= 0; i-- {
if pfxEnd[i]++; pfxEnd[i] != 0 {
ok = true
break
}
}
if !ok {
// 0xff..ff => 0x00
pfxEnd = []byte{0}
}
} else if len(end) >= 1 {
pfxEnd = make([]byte, len(pfx)+len(end))
copy(pfxEnd[copy(pfxEnd, pfx):], end)
}
return pfxKey, pfxEnd
}
================================================
FILE: client/v3/namespace/util_test.go
================================================
// Copyright 2017 The etcd Authors
//
// 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 namespace
import (
"bytes"
"testing"
)
func TestPrefixInterval(t *testing.T) {
tests := []struct {
pfx string
key []byte
end []byte
wKey []byte
wEnd []byte
}{
// single key
{
pfx: "pfx/",
key: []byte("a"),
wKey: []byte("pfx/a"),
},
// range
{
pfx: "pfx/",
key: []byte("abc"),
end: []byte("def"),
wKey: []byte("pfx/abc"),
wEnd: []byte("pfx/def"),
},
// one-sided range
{
pfx: "pfx/",
key: []byte("abc"),
end: []byte{0},
wKey: []byte("pfx/abc"),
wEnd: []byte("pfx0"),
},
// one-sided range, end of keyspace
{
pfx: "\xff\xff",
key: []byte("abc"),
end: []byte{0},
wKey: []byte("\xff\xffabc"),
wEnd: []byte{0},
},
}
for i, tt := range tests {
pfxKey, pfxEnd := prefixInterval(tt.pfx, tt.key, tt.end)
if !bytes.Equal(pfxKey, tt.wKey) {
t.Errorf("#%d: expected key=%q, got key=%q", i, tt.wKey, pfxKey)
}
if !bytes.Equal(pfxEnd, tt.wEnd) {
t.Errorf("#%d: expected end=%q, got end=%q", i, tt.wEnd, pfxEnd)
}
}
}
================================================
FILE: client/v3/namespace/watch.go
================================================
// Copyright 2017 The etcd Authors
//
// 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 namespace
import (
"context"
"sync"
clientv3 "go.etcd.io/etcd/client/v3"
)
type watcherPrefix struct {
clientv3.Watcher
pfx string
wg sync.WaitGroup
stopc chan struct{}
stopOnce sync.Once
}
// NewWatcher wraps a Watcher instance so that all Watch requests
// are prefixed with a given string and all Watch responses have
// the prefix removed.
func NewWatcher(w clientv3.Watcher, prefix string) clientv3.Watcher {
return &watcherPrefix{Watcher: w, pfx: prefix, stopc: make(chan struct{})}
}
func (w *watcherPrefix) Watch(ctx context.Context, key string, opts ...clientv3.OpOption) clientv3.WatchChan {
// since OpOption is opaque, determine range for prefixing through an OpGet
op := clientv3.OpGet(key, opts...)
end := op.RangeBytes()
pfxBegin, pfxEnd := prefixInterval(w.pfx, []byte(key), end)
if pfxEnd != nil {
opts = append(opts, clientv3.WithRange(string(pfxEnd)))
}
wch := w.Watcher.Watch(ctx, string(pfxBegin), opts...)
// translate watch events from prefixed to unprefixed
pfxWch := make(chan clientv3.WatchResponse)
w.wg.Add(1)
go func() {
defer func() {
close(pfxWch)
w.wg.Done()
}()
for wr := range wch {
for i := range wr.Events {
wr.Events[i].Kv.Key = wr.Events[i].Kv.Key[len(w.pfx):]
if wr.Events[i].PrevKv != nil {
wr.Events[i].PrevKv.Key = wr.Events[i].Kv.Key
}
}
select {
case pfxWch <- wr:
case <-ctx.Done():
return
case <-w.stopc:
return
}
}
}()
return pfxWch
}
func (w *watcherPrefix) Close() error {
err := w.Watcher.Close()
w.stopOnce.Do(func() { close(w.stopc) })
w.wg.Wait()
return err
}
================================================
FILE: client/v3/naming/doc.go
================================================
// Copyright 2017 The etcd Authors
//
// 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 naming provides:
// - subpackage endpoints: an abstraction layer to store and read endpoints
// information from etcd.
// - subpackage resolver: an etcd-backed gRPC resolver for discovering gRPC
// services based on the endpoints configuration
//
// To use, first import the packages:
//
// import (
// "go.etcd.io/etcd/client/v3"
// "go.etcd.io/etcd/client/v3/naming/endpoints"
// "go.etcd.io/etcd/client/v3/naming/resolver"
// "google.golang.org/grpc"
// )
//
// First, register new endpoint addresses for a service:
//
// func etcdAdd(c *clientv3.Client, service, addr string) error {
// em := endpoints.NewManager(c, service)
// return em.AddEndpoint(c.Ctx(), service+"/"+addr, endpoints.Endpoint{Addr:addr});
// }
//
// Dial an RPC service using the etcd gRPC resolver and a gRPC Balancer:
//
// func etcdDial(c *clientv3.Client, service string) (*grpc.ClientConn, error) {
// etcdResolver, err := resolver.NewBuilder(c);
// if err { return nil, err }
// conn, err := grpc.NewClient("etcd:///"+service, grpc.WithResolvers(etcdResolver))
// if err != nil { return nil, err }
// return conn, nil
// }
//
// Optionally, force delete an endpoint:
//
// func etcdDelete(c *clientv3, service, addr string) error {
// em := endpoints.NewManager(c, service)
// return em.DeleteEndpoint(c.Ctx(), service+"/"+addr)
// }
//
// Or register an expiring endpoint with a lease:
//
// func etcdAdd(c *clientv3.Client, lid clientv3.LeaseID, service, addr string) error {
// em := endpoints.NewManager(c, service)
// return em.AddEndpoint(c.Ctx(), service+"/"+addr, endpoints.Endpoint{Addr:addr}, clientv3.WithLease(lid));
// }
package naming
================================================
FILE: client/v3/naming/endpoints/endpoints.go
================================================
// Copyright 2021 The etcd Authors
//
// 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 endpoints
import (
"context"
clientv3 "go.etcd.io/etcd/client/v3"
)
// Endpoint represents a single address the connection can be established with.
//
// Inspired by: https://pkg.go.dev/google.golang.org/grpc/resolver#Address.
// Please document etcd version since which version each field is supported.
type Endpoint struct {
// Addr is the server address on which a connection will be established.
// Since etcd 3.1
Addr string
// Metadata is the information associated with Addr.
// Since etcd 3.1
Metadata any
}
type Operation uint8
const (
// Add indicates an Endpoint is added.
Add Operation = iota
// Delete indicates an existing address is deleted.
Delete
)
// Update describes a single edit action of an Endpoint.
type Update struct {
// Op - action Add or Delete.
Op Operation
Key string
Endpoint Endpoint
}
// WatchChannel is used to deliver notifications about endpoints updates.
type WatchChannel <-chan []*Update
// Key2EndpointMap maps etcd key into struct describing the endpoint.
type Key2EndpointMap map[string]Endpoint
// UpdateWithOpts describes endpoint update (add or delete) together
// with etcd options (e.g. to attach an endpoint to a lease).
type UpdateWithOpts struct {
Update
Opts []clientv3.OpOption
}
// NewAddUpdateOpts constructs UpdateWithOpts for endpoint registration.
func NewAddUpdateOpts(key string, endpoint Endpoint, opts ...clientv3.OpOption) *UpdateWithOpts {
return &UpdateWithOpts{Update: Update{Op: Add, Key: key, Endpoint: endpoint}, Opts: opts}
}
// NewDeleteUpdateOpts constructs UpdateWithOpts for endpoint deletion.
func NewDeleteUpdateOpts(key string, opts ...clientv3.OpOption) *UpdateWithOpts {
return &UpdateWithOpts{Update: Update{Op: Delete, Key: key}, Opts: opts}
}
// Manager can be used to add/remove & inspect endpoints stored in etcd for
// a particular target.
type Manager interface {
// Update allows to atomically add/remove a few endpoints from etcd.
Update(ctx context.Context, updates []*UpdateWithOpts) error
// AddEndpoint registers a single endpoint in etcd.
// For more advanced use-cases use the Update method.
AddEndpoint(ctx context.Context, key string, endpoint Endpoint, opts ...clientv3.OpOption) error
// DeleteEndpoint deletes a single endpoint stored in etcd.
// For more advanced use-cases use the Update method.
DeleteEndpoint(ctx context.Context, key string, opts ...clientv3.OpOption) error
// List returns all the endpoints for the current target as a map.
List(ctx context.Context) (Key2EndpointMap, error)
// NewWatchChannel creates a channel that populates or endpoint updates.
// Cancel the 'ctx' to close the watcher.
NewWatchChannel(ctx context.Context) (WatchChannel, error)
}
================================================
FILE: client/v3/naming/endpoints/endpoints_impl.go
================================================
// Copyright 2021 The etcd Authors
//
// 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 endpoints
// TODO: The API is not yet implemented.
import (
"context"
"encoding/json"
"errors"
"strings"
"go.uber.org/zap"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
clientv3 "go.etcd.io/etcd/client/v3"
"go.etcd.io/etcd/client/v3/naming/endpoints/internal"
)
type endpointManager struct {
// Client is an initialized etcd client.
client *clientv3.Client
target string
}
// NewManager creates an endpoint manager which implements the interface of 'Manager'.
func NewManager(client *clientv3.Client, target string) (Manager, error) {
if client == nil {
return nil, errors.New("invalid etcd client")
}
if target == "" {
return nil, errors.New("invalid target")
}
em := &endpointManager{
client: client,
target: target,
}
return em, nil
}
func (m *endpointManager) Update(ctx context.Context, updates []*UpdateWithOpts) (err error) {
ops := make([]clientv3.Op, 0, len(updates))
for _, update := range updates {
if !strings.HasPrefix(update.Key, m.target+"/") {
return status.Errorf(codes.InvalidArgument, "endpoints: endpoint key should be prefixed with '%s/' got: '%s'", m.target, update.Key)
}
switch update.Op {
case Add:
internalUpdate := &internal.Update{
Op: internal.Add,
Addr: update.Endpoint.Addr,
Metadata: update.Endpoint.Metadata,
}
var v []byte
if v, err = json.Marshal(internalUpdate); err != nil {
return status.Error(codes.InvalidArgument, err.Error())
}
ops = append(ops, clientv3.OpPut(update.Key, string(v), update.Opts...))
case Delete:
ops = append(ops, clientv3.OpDelete(update.Key, update.Opts...))
default:
return status.Error(codes.InvalidArgument, "endpoints: bad update op")
}
}
_, err = m.client.KV.Txn(ctx).Then(ops...).Commit()
return err
}
func (m *endpointManager) AddEndpoint(ctx context.Context, key string, endpoint Endpoint, opts ...clientv3.OpOption) error {
return m.Update(ctx, []*UpdateWithOpts{NewAddUpdateOpts(key, endpoint, opts...)})
}
func (m *endpointManager) DeleteEndpoint(ctx context.Context, key string, opts ...clientv3.OpOption) error {
return m.Update(ctx, []*UpdateWithOpts{NewDeleteUpdateOpts(key, opts...)})
}
func (m *endpointManager) NewWatchChannel(ctx context.Context) (WatchChannel, error) {
key := m.target + "/"
resp, err := m.client.Get(ctx, key, clientv3.WithPrefix(), clientv3.WithSerializable())
if err != nil {
return nil, err
}
lg := m.client.GetLogger()
initUpdates := make([]*Update, 0, len(resp.Kvs))
for _, kv := range resp.Kvs {
var iup internal.Update
if err := json.Unmarshal(kv.Value, &iup); err != nil {
lg.Warn("unmarshal endpoint update failed", zap.String("key", string(kv.Key)), zap.Error(err))
continue
}
up := &Update{
Op: Add,
Key: string(kv.Key),
Endpoint: Endpoint{Addr: iup.Addr, Metadata: iup.Metadata},
}
initUpdates = append(initUpdates, up)
}
upch := make(chan []*Update, 1)
if len(initUpdates) > 0 {
upch <- initUpdates
}
go m.watch(ctx, resp.Header.Revision+1, upch)
return upch, nil
}
func (m *endpointManager) watch(ctx context.Context, rev int64, upch chan []*Update) {
defer close(upch)
lg := m.client.GetLogger()
opts := []clientv3.OpOption{clientv3.WithRev(rev), clientv3.WithPrefix()}
key := m.target + "/"
wch := m.client.Watch(ctx, key, opts...)
for {
select {
case <-ctx.Done():
return
case wresp, ok := <-wch:
if !ok {
lg.Warn("watch closed", zap.String("target", m.target))
return
}
if wresp.Err() != nil {
lg.Warn("watch failed", zap.String("target", m.target), zap.Error(wresp.Err()))
return
}
deltaUps := make([]*Update, 0, len(wresp.Events))
for _, e := range wresp.Events {
var iup internal.Update
var err error
var op Operation
switch e.Type {
case clientv3.EventTypePut:
err = json.Unmarshal(e.Kv.Value, &iup)
op = Add
if err != nil {
lg.Warn("unmarshal endpoint update failed", zap.String("key", string(e.Kv.Key)), zap.Error(err))
continue
}
case clientv3.EventTypeDelete:
iup = internal.Update{Op: internal.Delete}
op = Delete
default:
continue
}
up := &Update{Op: op, Key: string(e.Kv.Key), Endpoint: Endpoint{Addr: iup.Addr, Metadata: iup.Metadata}}
deltaUps = append(deltaUps, up)
}
if len(deltaUps) > 0 {
upch <- deltaUps
}
}
}
}
func (m *endpointManager) List(ctx context.Context) (Key2EndpointMap, error) {
key := m.target + "/"
resp, err := m.client.Get(ctx, key, clientv3.WithPrefix(), clientv3.WithSerializable())
if err != nil {
return nil, err
}
eps := make(Key2EndpointMap)
for _, kv := range resp.Kvs {
var iup internal.Update
if err := json.Unmarshal(kv.Value, &iup); err != nil {
continue
}
eps[string(kv.Key)] = Endpoint{Addr: iup.Addr, Metadata: iup.Metadata}
}
return eps, nil
}
================================================
FILE: client/v3/naming/endpoints/internal/update.go
================================================
// Copyright 2021 The etcd Authors
//
// 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 internal
// Operation describes action performed on endpoint (addition vs deletion).
// Must stay JSON-format compatible with:
// https://pkg.go.dev/google.golang.org/grpc@v1.29.1/naming#Operation
type Operation uint8
const (
// Add indicates a new address is added.
Add Operation = iota
// Delete indicates an existing address is deleted.
Delete
)
// Update defines a persistent (JSON marshalled) format representing
// endpoint within the etcd storage.
//
// As the format can be persisted by one version of etcd client library and
// read by other the format must be kept backward compatible and
// in particular must be superset of the grpc(<=1.29.1) naming.Update structure:
// https://pkg.go.dev/google.golang.org/grpc@v1.29.1/naming#Update
//
// Please document since which version of etcd-client given property is supported.
// Please keep the naming consistent with e.g. https://pkg.go.dev/google.golang.org/grpc/resolver#Address.
//
// Notice that it is not valid having both empty string Addr and nil Metadata in an Update.
type Update struct {
// Op indicates the operation of the update.
// Since etcd 3.1.
Op Operation
// Addr is the updated address. It is empty string if there is no address update.
// Since etcd 3.1.
Addr string
// Metadata is the updated metadata. It is nil if there is no metadata update.
// Metadata is not required for a custom naming implementation.
// Since etcd 3.1.
Metadata any
}
================================================
FILE: client/v3/naming/resolver/resolver.go
================================================
// Copyright 2021 The etcd Authors
//
// 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 resolver
import (
"context"
"strings"
"sync"
"google.golang.org/grpc/codes"
gresolver "google.golang.org/grpc/resolver"
"google.golang.org/grpc/status"
clientv3 "go.etcd.io/etcd/client/v3"
"go.etcd.io/etcd/client/v3/naming/endpoints"
)
type builder struct {
c *clientv3.Client
}
func (b builder) Build(target gresolver.Target, cc gresolver.ClientConn, opts gresolver.BuildOptions) (gresolver.Resolver, error) {
// Refer to https://github.com/grpc/grpc-go/blob/16d3df80f029f57cff5458f1d6da6aedbc23545d/clientconn.go#L1587-L1611
endpoint := target.URL.Path
if endpoint == "" {
endpoint = target.URL.Opaque
}
endpoint = strings.TrimPrefix(endpoint, "/")
r := &resolver{
c: b.c,
target: endpoint,
cc: cc,
}
r.ctx, r.cancel = context.WithCancel(context.Background())
em, err := endpoints.NewManager(r.c, r.target)
if err != nil {
return nil, status.Errorf(codes.InvalidArgument, "resolver: failed to new endpoint manager: %s", err)
}
r.wch, err = em.NewWatchChannel(r.ctx)
if err != nil {
return nil, status.Errorf(codes.Internal, "resolver: failed to new watch channer: %s", err)
}
r.wg.Add(1)
go r.watch()
return r, nil
}
func (b builder) Scheme() string {
return "etcd"
}
// NewBuilder creates a resolver builder.
func NewBuilder(client *clientv3.Client) (gresolver.Builder, error) {
return builder{c: client}, nil
}
type resolver struct {
c *clientv3.Client
target string
cc gresolver.ClientConn
wch endpoints.WatchChannel
ctx context.Context
cancel context.CancelFunc
wg sync.WaitGroup
}
func (r *resolver) watch() {
defer r.wg.Done()
allUps := make(map[string]*endpoints.Update)
for {
select {
case <-r.ctx.Done():
return
case ups, ok := <-r.wch:
if !ok {
return
}
for _, up := range ups {
switch up.Op {
case endpoints.Add:
allUps[up.Key] = up
case endpoints.Delete:
delete(allUps, up.Key)
}
}
eps := convertToGRPCEndpoint(allUps)
r.cc.UpdateState(gresolver.State{Endpoints: eps})
}
}
}
func convertToGRPCEndpoint(ups map[string]*endpoints.Update) []gresolver.Endpoint {
var eps []gresolver.Endpoint
for _, up := range ups {
ep := gresolver.Endpoint{
Addresses: []gresolver.Address{
{
Addr: up.Endpoint.Addr,
},
},
}
eps = append(eps, ep)
}
return eps
}
// ResolveNow is a no-op here.
// It's just a hint, resolver can ignore this if it's not necessary.
func (r *resolver) ResolveNow(gresolver.ResolveNowOptions) {}
func (r *resolver) Close() {
r.cancel()
r.wg.Wait()
}
================================================
FILE: client/v3/op.go
================================================
// Copyright 2016 The etcd Authors
//
// 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 clientv3
import pb "go.etcd.io/etcd/api/v3/etcdserverpb"
type opType int
const (
// A default Op has opType 0, which is invalid.
tRange opType = iota + 1
tPut
tDeleteRange
tTxn
)
var noPrefixEnd = []byte{0}
// Op represents an Operation that kv can execute.
type Op struct {
t opType
key []byte
end []byte
// for range
limit int64
sort *SortOption
serializable bool
keysOnly bool
countOnly bool
minModRev int64
maxModRev int64
minCreateRev int64
maxCreateRev int64
// for range, watch
rev int64
// for watch, put, delete
prevKV bool
// for watch
// fragmentation should be disabled by default
// if true, split watch events when total exceeds
// "--max-request-bytes" flag value + 512-byte
fragment bool
// for put
ignoreValue bool
ignoreLease bool
// progressNotify is for progress updates.
progressNotify bool
// createdNotify is for created event
createdNotify bool
// filters for watchers
filterPut bool
filterDelete bool
// for put
val []byte
leaseID LeaseID
// txn
cmps []Cmp
thenOps []Op
elseOps []Op
isOptsWithFromKey bool
isOptsWithPrefix bool
}
// accessors / mutators
// IsTxn returns true if the "Op" type is transaction.
func (op Op) IsTxn() bool {
return op.t == tTxn
}
// Txn returns the comparison(if) operations, "then" operations, and "else" operations.
func (op Op) Txn() ([]Cmp, []Op, []Op) {
return op.cmps, op.thenOps, op.elseOps
}
// KeyBytes returns the byte slice holding the Op's key.
func (op Op) KeyBytes() []byte { return op.key }
// WithKeyBytes sets the byte slice for the Op's key.
func (op *Op) WithKeyBytes(key []byte) { op.key = key }
// RangeBytes returns the byte slice holding with the Op's range end, if any.
func (op Op) RangeBytes() []byte { return op.end }
// Rev returns the requested revision, if any.
func (op Op) Rev() int64 { return op.rev }
// Limit returns limit of the result, if any.
func (op Op) Limit() int64 { return op.limit }
// IsPut returns true iff the operation is a Put.
func (op Op) IsPut() bool { return op.t == tPut }
// IsGet returns true iff the operation is a Get.
func (op Op) IsGet() bool { return op.t == tRange }
// IsDelete returns true iff the operation is a Delete.
func (op Op) IsDelete() bool { return op.t == tDeleteRange }
// IsSerializable returns true if the serializable field is true.
func (op Op) IsSerializable() bool { return op.serializable }
// IsKeysOnly returns whether keysOnly is set.
func (op Op) IsKeysOnly() bool { return op.keysOnly }
// IsCountOnly returns whether countOnly is set.
func (op Op) IsCountOnly() bool { return op.countOnly }
// IsSortSet returns true if WithSort is set.
func (op Op) IsSortSet() bool { return op.sort != nil }
func (op Op) IsOptsWithFromKey() bool { return op.isOptsWithFromKey }
func (op Op) IsOptsWithPrefix() bool { return op.isOptsWithPrefix }
// IsPrevKV returns whether WithPrevKV() is set.
func (op Op) IsPrevKV() bool { return op.prevKV }
// IsFragment returns whether WithFragment() is set.
func (op Op) IsFragment() bool { return op.fragment }
// IsProgressNotify returns whether WithProgressNotify() is set.
func (op Op) IsProgressNotify() bool { return op.progressNotify }
// IsCreatedNotify returns whether WithCreatedNotify() is set.
func (op Op) IsCreatedNotify() bool { return op.createdNotify }
// IsFilterPut returns whether WithFilterPut() is set.
func (op Op) IsFilterPut() bool { return op.filterPut }
// IsFilterDelete returns whether WithFilterDelete() is set.
func (op Op) IsFilterDelete() bool { return op.filterDelete }
// MinModRev returns the operation's minimum modify revision.
func (op Op) MinModRev() int64 { return op.minModRev }
// MaxModRev returns the operation's maximum modify revision.
func (op Op) MaxModRev() int64 { return op.maxModRev }
// MinCreateRev returns the operation's minimum create revision.
func (op Op) MinCreateRev() int64 { return op.minCreateRev }
// MaxCreateRev returns the operation's maximum create revision.
func (op Op) MaxCreateRev() int64 { return op.maxCreateRev }
// WithRangeBytes sets the byte slice for the Op's range end.
func (op *Op) WithRangeBytes(end []byte) { op.end = end }
// ValueBytes returns the byte slice holding the Op's value, if any.
func (op Op) ValueBytes() []byte { return op.val }
// WithValueBytes sets the byte slice for the Op's value.
func (op *Op) WithValueBytes(v []byte) { op.val = v }
func (op Op) toRangeRequest() *pb.RangeRequest {
if op.t != tRange {
panic("op.t != tRange")
}
r := &pb.RangeRequest{
Key: op.key,
RangeEnd: op.end,
Limit: op.limit,
Revision: op.rev,
Serializable: op.serializable,
KeysOnly: op.keysOnly,
CountOnly: op.countOnly,
MinModRevision: op.minModRev,
MaxModRevision: op.maxModRev,
MinCreateRevision: op.minCreateRev,
MaxCreateRevision: op.maxCreateRev,
}
if op.sort != nil {
r.SortOrder = pb.RangeRequest_SortOrder(op.sort.Order)
r.SortTarget = pb.RangeRequest_SortTarget(op.sort.Target)
}
return r
}
func (op Op) toTxnRequest() *pb.TxnRequest {
thenOps := make([]*pb.RequestOp, len(op.thenOps))
for i, tOp := range op.thenOps {
thenOps[i] = tOp.toRequestOp()
}
elseOps := make([]*pb.RequestOp, len(op.elseOps))
for i, eOp := range op.elseOps {
elseOps[i] = eOp.toRequestOp()
}
cmps := make([]*pb.Compare, len(op.cmps))
for i := range op.cmps {
cmps[i] = (*pb.Compare)(&op.cmps[i])
}
return &pb.TxnRequest{Compare: cmps, Success: thenOps, Failure: elseOps}
}
func (op Op) toRequestOp() *pb.RequestOp {
switch op.t {
case tRange:
return &pb.RequestOp{Request: &pb.RequestOp_RequestRange{RequestRange: op.toRangeRequest()}}
case tPut:
r := &pb.PutRequest{Key: op.key, Value: op.val, Lease: int64(op.leaseID), PrevKv: op.prevKV, IgnoreValue: op.ignoreValue, IgnoreLease: op.ignoreLease}
return &pb.RequestOp{Request: &pb.RequestOp_RequestPut{RequestPut: r}}
case tDeleteRange:
r := &pb.DeleteRangeRequest{Key: op.key, RangeEnd: op.end, PrevKv: op.prevKV}
return &pb.RequestOp{Request: &pb.RequestOp_RequestDeleteRange{RequestDeleteRange: r}}
case tTxn:
return &pb.RequestOp{Request: &pb.RequestOp_RequestTxn{RequestTxn: op.toTxnRequest()}}
default:
panic("Unknown Op")
}
}
func (op Op) isWrite() bool {
if op.t == tTxn {
for _, tOp := range op.thenOps {
if tOp.isWrite() {
return true
}
}
for _, tOp := range op.elseOps {
if tOp.isWrite() {
return true
}
}
return false
}
return op.t != tRange
}
func NewOp() *Op {
return &Op{key: []byte("")}
}
// OpGet returns "get" operation based on given key and operation options.
func OpGet(key string, opts ...OpOption) Op {
// WithPrefix and WithFromKey are not supported together
if IsOptsWithPrefix(opts) && IsOptsWithFromKey(opts) {
panic("`WithPrefix` and `WithFromKey` cannot be set at the same time, choose one")
}
ret := Op{t: tRange, key: []byte(key)}
ret.applyOpts(opts)
return ret
}
// OpDelete returns "delete" operation based on given key and operation options.
func OpDelete(key string, opts ...OpOption) Op {
// WithPrefix and WithFromKey are not supported together
if IsOptsWithPrefix(opts) && IsOptsWithFromKey(opts) {
panic("`WithPrefix` and `WithFromKey` cannot be set at the same time, choose one")
}
ret := Op{t: tDeleteRange, key: []byte(key)}
ret.applyOpts(opts)
switch {
case ret.leaseID != 0:
panic("unexpected lease in delete")
case ret.limit != 0:
panic("unexpected limit in delete")
case ret.rev != 0:
panic("unexpected revision in delete")
case ret.sort != nil:
panic("unexpected sort in delete")
case ret.serializable:
panic("unexpected serializable in delete")
case ret.countOnly:
panic("unexpected countOnly in delete")
case ret.minModRev != 0, ret.maxModRev != 0:
panic("unexpected mod revision filter in delete")
case ret.minCreateRev != 0, ret.maxCreateRev != 0:
panic("unexpected create revision filter in delete")
case ret.filterDelete, ret.filterPut:
panic("unexpected filter in delete")
case ret.createdNotify:
panic("unexpected createdNotify in delete")
}
return ret
}
// OpPut returns "put" operation based on given key-value and operation options.
func OpPut(key, val string, opts ...OpOption) Op {
ret := Op{t: tPut, key: []byte(key), val: []byte(val)}
ret.applyOpts(opts)
switch {
case ret.end != nil:
panic("unexpected range in put")
case ret.limit != 0:
panic("unexpected limit in put")
case ret.rev != 0:
panic("unexpected revision in put")
case ret.sort != nil:
panic("unexpected sort in put")
case ret.serializable:
panic("unexpected serializable in put")
case ret.countOnly:
panic("unexpected countOnly in put")
case ret.minModRev != 0, ret.maxModRev != 0:
panic("unexpected mod revision filter in put")
case ret.minCreateRev != 0, ret.maxCreateRev != 0:
panic("unexpected create revision filter in put")
case ret.filterDelete, ret.filterPut:
panic("unexpected filter in put")
case ret.createdNotify:
panic("unexpected createdNotify in put")
}
return ret
}
// OpTxn returns "txn" operation based on given transaction conditions.
func OpTxn(cmps []Cmp, thenOps []Op, elseOps []Op) Op {
return Op{t: tTxn, cmps: cmps, thenOps: thenOps, elseOps: elseOps}
}
func OpWatch(key string, opts ...OpOption) Op {
ret := Op{t: tRange, key: []byte(key)}
ret.applyOpts(opts)
switch {
case ret.leaseID != 0:
panic("unexpected lease in watch")
case ret.limit != 0:
panic("unexpected limit in watch")
case ret.sort != nil:
panic("unexpected sort in watch")
case ret.serializable:
panic("unexpected serializable in watch")
case ret.countOnly:
panic("unexpected countOnly in watch")
case ret.minModRev != 0, ret.maxModRev != 0:
panic("unexpected mod revision filter in watch")
case ret.minCreateRev != 0, ret.maxCreateRev != 0:
panic("unexpected create revision filter in watch")
}
return ret
}
func (op *Op) applyOpts(opts []OpOption) {
for _, opt := range opts {
opt(op)
}
}
// OpOption configures Operations like Get, Put, Delete.
type OpOption func(*Op)
// WithLease attaches a lease ID to a key in 'Put' request.
func WithLease(leaseID LeaseID) OpOption {
return func(op *Op) { op.leaseID = leaseID }
}
// WithLimit limits the number of results to return from 'Get' request.
// If WithLimit is given a 0 limit, it is treated as no limit.
func WithLimit(n int64) OpOption { return func(op *Op) { op.limit = n } }
// WithRev specifies the store revision for 'Get' request.
// Or the start revision of 'Watch' request.
func WithRev(rev int64) OpOption { return func(op *Op) { op.rev = rev } }
// WithSort specifies the ordering in 'Get' request. It requires
// 'WithRange' and/or 'WithPrefix' to be specified too.
// 'target' specifies the target to sort by: key, version, revisions, value.
// 'order' can be either 'SortNone', 'SortAscend', 'SortDescend'.
func WithSort(target SortTarget, order SortOrder) OpOption {
return func(op *Op) {
if target == SortByKey && order == SortAscend {
// If order != SortNone, server fetches the entire key-space,
// and then applies the sort and limit, if provided.
// Since by default the server returns results sorted by keys
// in lexicographically ascending order, the client should ignore
// SortOrder if the target is SortByKey.
order = SortNone
}
op.sort = &SortOption{target, order}
}
}
// GetPrefixRangeEnd gets the range end of the prefix.
// 'Get(foo, WithPrefix())' is equal to 'Get(foo, WithRange(GetPrefixRangeEnd(foo))'.
func GetPrefixRangeEnd(prefix string) string {
return string(getPrefix([]byte(prefix)))
}
func getPrefix(key []byte) []byte {
end := make([]byte, len(key))
copy(end, key)
for i := len(end) - 1; i >= 0; i-- {
if end[i] < 0xff {
end[i] = end[i] + 1
end = end[:i+1]
return end
}
}
// next prefix does not exist (e.g., 0xffff);
// default to WithFromKey policy
return noPrefixEnd
}
// WithPrefix enables 'Get', 'Delete', or 'Watch' requests to operate
// on the keys with matching prefix. For example, 'Get(foo, WithPrefix())'
// can return 'foo1', 'foo2', and so on.
func WithPrefix() OpOption {
return func(op *Op) {
op.isOptsWithPrefix = true
if len(op.key) == 0 {
op.key, op.end = []byte{0}, []byte{0}
return
}
op.end = getPrefix(op.key)
}
}
// WithRange specifies the range of 'Get', 'Delete', 'Watch' requests.
// For example, 'Get' requests with 'WithRange(end)' returns
// the keys in the range [key, end).
// endKey must be lexicographically greater than start key.
func WithRange(endKey string) OpOption {
return func(op *Op) { op.end = []byte(endKey) }
}
// WithFromKey specifies the range of 'Get', 'Delete', 'Watch' requests
// to be equal or greater than the key in the argument.
func WithFromKey() OpOption {
return func(op *Op) {
if len(op.key) == 0 {
op.key = []byte{0}
}
op.end = []byte("\x00")
op.isOptsWithFromKey = true
}
}
// WithSerializable makes `Get` and `MemberList` requests serializable.
// By default, they are linearizable. Serializable requests are better
// for lower latency requirement, but users should be aware that they
// could get stale data with serializable requests.
//
// In some situations users may want to use serializable requests. For
// example, when adding a new member to a one-node cluster, it's reasonable
// and safe to use serializable request before the new added member gets
// started.
func WithSerializable() OpOption {
return func(op *Op) { op.serializable = true }
}
// WithKeysOnly makes the 'Get' request return only the keys and the corresponding
// values will be omitted.
func WithKeysOnly() OpOption {
return func(op *Op) { op.keysOnly = true }
}
// WithCountOnly makes the 'Get' request return only the count of keys.
func WithCountOnly() OpOption {
return func(op *Op) { op.countOnly = true }
}
// WithMinModRev filters out keys for Get with modification revisions less than the given revision.
func WithMinModRev(rev int64) OpOption { return func(op *Op) { op.minModRev = rev } }
// WithMaxModRev filters out keys for Get with modification revisions greater than the given revision.
func WithMaxModRev(rev int64) OpOption { return func(op *Op) { op.maxModRev = rev } }
// WithMinCreateRev filters out keys for Get with creation revisions less than the given revision.
func WithMinCreateRev(rev int64) OpOption { return func(op *Op) { op.minCreateRev = rev } }
// WithMaxCreateRev filters out keys for Get with creation revisions greater than the given revision.
func WithMaxCreateRev(rev int64) OpOption { return func(op *Op) { op.maxCreateRev = rev } }
// WithFirstCreate gets the key with the oldest creation revision in the request range.
func WithFirstCreate() []OpOption { return withTop(SortByCreateRevision, SortAscend) }
// WithLastCreate gets the key with the latest creation revision in the request range.
func WithLastCreate() []OpOption { return withTop(SortByCreateRevision, SortDescend) }
// WithFirstKey gets the lexically first key in the request range.
func WithFirstKey() []OpOption { return withTop(SortByKey, SortAscend) }
// WithLastKey gets the lexically last key in the request range.
func WithLastKey() []OpOption { return withTop(SortByKey, SortDescend) }
// WithFirstRev gets the key with the oldest modification revision in the request range.
func WithFirstRev() []OpOption { return withTop(SortByModRevision, SortAscend) }
// WithLastRev gets the key with the latest modification revision in the request range.
func WithLastRev() []OpOption { return withTop(SortByModRevision, SortDescend) }
// withTop gets the first key over the get's prefix given a sort order
func withTop(target SortTarget, order SortOrder) []OpOption {
return []OpOption{WithPrefix(), WithSort(target, order), WithLimit(1)}
}
// WithProgressNotify makes watch server send periodic progress updates
// every 10 minutes when there is no incoming events.
// Progress updates have zero events in WatchResponse.
func WithProgressNotify() OpOption {
return func(op *Op) {
op.progressNotify = true
}
}
// WithCreatedNotify makes watch server sends the created event.
func WithCreatedNotify() OpOption {
return func(op *Op) {
op.createdNotify = true
}
}
// WithFilterPut discards PUT events from the watcher.
func WithFilterPut() OpOption {
return func(op *Op) { op.filterPut = true }
}
// WithFilterDelete discards DELETE events from the watcher.
func WithFilterDelete() OpOption {
return func(op *Op) { op.filterDelete = true }
}
// WithPrevKV gets the previous key-value pair before the event happens. If the previous KV is already compacted,
// nothing will be returned.
func WithPrevKV() OpOption {
return func(op *Op) {
op.prevKV = true
}
}
// WithFragment to receive raw watch response with fragmentation.
// Fragmentation is disabled by default. If fragmentation is enabled,
// etcd watch server will split watch response before sending to clients
// when the total size of watch events exceed server-side request limit.
// The default server-side request limit is 1.5 MiB, which can be configured
// as "--max-request-bytes" flag value + gRPC-overhead 512 bytes.
// See "etcdserver/api/v3rpc/watch.go" for more details.
func WithFragment() OpOption {
return func(op *Op) { op.fragment = true }
}
// WithIgnoreValue updates the key using its current value.
// This option can not be combined with non-empty values.
// Returns an error if the key does not exist.
func WithIgnoreValue() OpOption {
return func(op *Op) {
op.ignoreValue = true
}
}
// WithIgnoreLease updates the key using its current lease.
// This option can not be combined with WithLease.
// Returns an error if the key does not exist.
func WithIgnoreLease() OpOption {
return func(op *Op) {
op.ignoreLease = true
}
}
// LeaseOp represents an Operation that lease can execute.
type LeaseOp struct {
id LeaseID
// for TimeToLive
attachedKeys bool
}
// LeaseOption configures lease operations.
type LeaseOption func(*LeaseOp)
func (op *LeaseOp) applyOpts(opts []LeaseOption) {
for _, opt := range opts {
opt(op)
}
}
// WithAttachedKeys makes TimeToLive list the keys attached to the given lease ID.
func WithAttachedKeys() LeaseOption {
return func(op *LeaseOp) { op.attachedKeys = true }
}
func toLeaseTimeToLiveRequest(id LeaseID, opts ...LeaseOption) *pb.LeaseTimeToLiveRequest {
ret := &LeaseOp{id: id}
ret.applyOpts(opts)
return &pb.LeaseTimeToLiveRequest{ID: int64(id), Keys: ret.attachedKeys}
}
// IsOptsWithPrefix returns true if WithPrefix option is called in the given opts.
func IsOptsWithPrefix(opts []OpOption) bool {
ret := NewOp()
for _, opt := range opts {
opt(ret)
}
return ret.isOptsWithPrefix
}
// IsOptsWithFromKey returns true if WithFromKey option is called in the given opts.
func IsOptsWithFromKey(opts []OpOption) bool {
ret := NewOp()
for _, opt := range opts {
opt(ret)
}
return ret.isOptsWithFromKey
}
func (op Op) IsSortOptionValid() bool {
if op.sort != nil {
sortOrder := int32(op.sort.Order)
sortTarget := int32(op.sort.Target)
if _, ok := pb.RangeRequest_SortOrder_name[sortOrder]; !ok {
return false
}
if _, ok := pb.RangeRequest_SortTarget_name[sortTarget]; !ok {
return false
}
}
return true
}
================================================
FILE: client/v3/op_test.go
================================================
// Copyright 2016 The etcd Authors
//
// 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 clientv3
import (
"reflect"
"testing"
pb "go.etcd.io/etcd/api/v3/etcdserverpb"
)
// TestOpWithSort tests if WithSort(ASCEND, KEY) and WithLimit are specified,
// RangeRequest ignores the SortOption to avoid unnecessarily fetching
// the entire key-space.
func TestOpWithSort(t *testing.T) {
opReq := OpGet("foo", WithSort(SortByKey, SortAscend), WithLimit(10)).toRequestOp().Request
q, ok := opReq.(*pb.RequestOp_RequestRange)
if !ok {
t.Fatalf("expected range request, got %v", reflect.TypeOf(opReq))
}
req := q.RequestRange
wreq := &pb.RangeRequest{Key: []byte("foo"), SortOrder: pb.RangeRequest_NONE, Limit: 10}
if !reflect.DeepEqual(req, wreq) {
t.Fatalf("expected %+v, got %+v", wreq, req)
}
}
func TestIsSortOptionValid(t *testing.T) {
rangeReqs := []struct {
sortOrder pb.RangeRequest_SortOrder
sortTarget pb.RangeRequest_SortTarget
expectedValid bool
}{
{
sortOrder: pb.RangeRequest_ASCEND,
sortTarget: pb.RangeRequest_CREATE,
expectedValid: true,
},
{
sortOrder: pb.RangeRequest_ASCEND,
sortTarget: 100,
expectedValid: false,
},
{
sortOrder: 200,
sortTarget: pb.RangeRequest_MOD,
expectedValid: false,
},
}
for _, req := range rangeReqs {
getOp := Op{
sort: &SortOption{
Order: SortOrder(req.sortOrder),
Target: SortTarget(req.sortTarget),
},
}
actualRet := getOp.IsSortOptionValid()
if actualRet != req.expectedValid {
t.Errorf("expected sortOrder (%d) and sortTarget (%d) to be %t, but got %t",
req.sortOrder, req.sortTarget, req.expectedValid, actualRet)
}
}
}
func TestIsOptsWithPrefix(t *testing.T) {
optswithprefix := []OpOption{WithPrefix()}
op := OpGet("key", optswithprefix...)
if !IsOptsWithPrefix(optswithprefix) || !op.IsOptsWithPrefix() {
t.Errorf("IsOptsWithPrefix = false, expected true")
}
optswithfromkey := []OpOption{WithFromKey()}
op = OpGet("key", optswithfromkey...)
if IsOptsWithPrefix(optswithfromkey) || op.IsOptsWithPrefix() {
t.Errorf("IsOptsWithPrefix = true, expected false")
}
}
func TestIsOptsWithFromKey(t *testing.T) {
optswithfromkey := []OpOption{WithFromKey()}
op := OpGet("key", optswithfromkey...)
if !IsOptsWithFromKey(optswithfromkey) || !op.IsOptsWithFromKey() {
t.Errorf("IsOptsWithFromKey = false, expected true")
}
optswithprefix := []OpOption{WithPrefix()}
op = OpGet("key", optswithprefix...)
if IsOptsWithFromKey(optswithprefix) || op.IsOptsWithFromKey() {
t.Errorf("IsOptsWithFromKey = true, expected false")
}
}
================================================
FILE: client/v3/options.go
================================================
// Copyright 2017 The etcd Authors
//
// 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 clientv3
import (
"math"
"time"
"google.golang.org/grpc"
)
var (
// client-side handling retrying of request failures where data was not written to the wire or
// where server indicates it did not process the data. gRPC default is "WaitForReady(false)"
// but for etcd we default to "WaitForReady(true)" to minimize client request error responses due to
// transient failures.
defaultWaitForReady = grpc.WaitForReady(true)
// client-side request send limit, gRPC default is math.MaxInt32
// Make sure that "client-side send limit < server-side default send/recv limit"
// Same value as "embed.DefaultMaxRequestBytes" plus gRPC overhead bytes
defaultMaxCallSendMsgSize = grpc.MaxCallSendMsgSize(2 * 1024 * 1024)
// client-side response receive limit, gRPC default is 4MB
// Make sure that "client-side receive limit >= server-side default send/recv limit"
// because range response can easily exceed request send limits
// Default to math.MaxInt32; writes exceeding server-side send limit fails anyway
defaultMaxCallRecvMsgSize = grpc.MaxCallRecvMsgSize(math.MaxInt32)
// client-side non-streaming retry limit, only applied to requests where server responds with
// a error code clearly indicating it was unable to process the request such as codes.Unavailable.
// If set to 0, retry is disabled.
defaultUnaryMaxRetries uint = 100
// client-side streaming retry limit, only applied to requests where server responds with
// a error code clearly indicating it was unable to process the request such as codes.Unavailable.
// If set to 0, retry is disabled.
defaultStreamMaxRetries = ^uint(0) // max uint
// client-side retry backoff wait between requests.
defaultBackoffWaitBetween = 25 * time.Millisecond
// client-side retry backoff default jitter fraction.
defaultBackoffJitterFraction = 0.10
)
// defaultCallOpts defines a list of default "gRPC.CallOption".
// Some options are exposed to "clientv3.Config".
// Defaults will be overridden by the settings in "clientv3.Config".
var defaultCallOpts = []grpc.CallOption{
defaultWaitForReady,
defaultMaxCallSendMsgSize,
defaultMaxCallRecvMsgSize,
}
// MaxLeaseTTL is the maximum lease TTL value
const MaxLeaseTTL = 9000000000
================================================
FILE: client/v3/ordering/doc.go
================================================
// Copyright 2017 The etcd Authors
//
// 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 ordering is a clientv3 wrapper that caches response header revisions
// to detect ordering violations from stale responses. Users may define a
// policy on how to handle the ordering violation, but typically the client
// should connect to another endpoint and reissue the request.
//
// The most common situation where an ordering violation happens is a client
// reconnects to a partitioned member and issues a serializable read. Since the
// partitioned member is likely behind the last member, it may return a Get
// response based on a store revision older than the store revision used to
// service a prior Get on the former endpoint.
//
// First, create a client:
//
// cli, err := clientv3.New(clientv3.Config{Endpoints: []string{"localhost:2379"}})
// if err != nil {
// // handle error!
// }
//
// Next, override the client interface with the ordering wrapper:
//
// vf := func(op clientv3.Op, resp clientv3.OpResponse, prevRev int64) error {
// return fmt.Errorf("ordering: issued %+v, got %+v, expected rev=%v", op, resp, prevRev)
// }
// cli.KV = ordering.NewKV(cli.KV, vf)
//
// Now calls using 'cli' will reject order violations with an error.
package ordering
================================================
FILE: client/v3/ordering/kv.go
================================================
// Copyright 2017 The etcd Authors
//
// 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 ordering
import (
"context"
"sync"
clientv3 "go.etcd.io/etcd/client/v3"
)
// kvOrdering ensures that serialized requests do not return
// get with revisions less than the previous
// returned revision.
type kvOrdering struct {
clientv3.KV
orderViolationFunc OrderViolationFunc
prevRev int64
revMu sync.RWMutex
}
func NewKV(kv clientv3.KV, orderViolationFunc OrderViolationFunc) *kvOrdering {
return &kvOrdering{kv, orderViolationFunc, 0, sync.RWMutex{}}
}
func (kv *kvOrdering) getPrevRev() int64 {
kv.revMu.RLock()
defer kv.revMu.RUnlock()
return kv.prevRev
}
func (kv *kvOrdering) setPrevRev(currRev int64) {
kv.revMu.Lock()
defer kv.revMu.Unlock()
if currRev > kv.prevRev {
kv.prevRev = currRev
}
}
func (kv *kvOrdering) Get(ctx context.Context, key string, opts ...clientv3.OpOption) (*clientv3.GetResponse, error) {
// prevRev is stored in a local variable in order to record the prevRev
// at the beginning of the Get operation, because concurrent
// access to kvOrdering could change the prevRev field in the
// middle of the Get operation.
prevRev := kv.getPrevRev()
op := clientv3.OpGet(key, opts...)
for {
r, err := kv.KV.Do(ctx, op)
if err != nil {
return nil, err
}
resp := r.Get()
if resp.Header.Revision == prevRev {
return resp, nil
} else if resp.Header.Revision > prevRev {
kv.setPrevRev(resp.Header.Revision)
return resp, nil
}
err = kv.orderViolationFunc(op, r, prevRev)
if err != nil {
return nil, err
}
}
}
func (kv *kvOrdering) Txn(ctx context.Context) clientv3.Txn {
return &txnOrdering{
kv.KV.Txn(ctx),
kv,
ctx,
sync.Mutex{},
[]clientv3.Cmp{},
[]clientv3.Op{},
[]clientv3.Op{},
}
}
// txnOrdering ensures that serialized requests do not return
// txn responses with revisions less than the previous
// returned revision.
type txnOrdering struct {
clientv3.Txn
*kvOrdering
ctx context.Context
mu sync.Mutex
cmps []clientv3.Cmp
thenOps []clientv3.Op
elseOps []clientv3.Op
}
func (txn *txnOrdering) If(cs ...clientv3.Cmp) clientv3.Txn {
txn.mu.Lock()
defer txn.mu.Unlock()
txn.cmps = cs
txn.Txn.If(cs...)
return txn
}
func (txn *txnOrdering) Then(ops ...clientv3.Op) clientv3.Txn {
txn.mu.Lock()
defer txn.mu.Unlock()
txn.thenOps = ops
txn.Txn.Then(ops...)
return txn
}
func (txn *txnOrdering) Else(ops ...clientv3.Op) clientv3.Txn {
txn.mu.Lock()
defer txn.mu.Unlock()
txn.elseOps = ops
txn.Txn.Else(ops...)
return txn
}
func (txn *txnOrdering) Commit() (*clientv3.TxnResponse, error) {
// prevRev is stored in a local variable in order to record the prevRev
// at the beginning of the Commit operation, because concurrent
// access to txnOrdering could change the prevRev field in the
// middle of the Commit operation.
prevRev := txn.getPrevRev()
opTxn := clientv3.OpTxn(txn.cmps, txn.thenOps, txn.elseOps)
for {
opResp, err := txn.KV.Do(txn.ctx, opTxn)
if err != nil {
return nil, err
}
txnResp := opResp.Txn()
if txnResp.Header.Revision >= prevRev {
txn.setPrevRev(txnResp.Header.Revision)
return txnResp, nil
}
err = txn.orderViolationFunc(opTxn, opResp, prevRev)
if err != nil {
return nil, err
}
}
}
================================================
FILE: client/v3/ordering/kv_test.go
================================================
// Copyright 2017 The etcd Authors
//
// 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 ordering
import (
"context"
"sync"
"testing"
pb "go.etcd.io/etcd/api/v3/etcdserverpb"
clientv3 "go.etcd.io/etcd/client/v3"
)
type mockKV struct {
clientv3.KV
response clientv3.OpResponse
}
func (kv *mockKV) Do(ctx context.Context, op clientv3.Op) (clientv3.OpResponse, error) {
return kv.response, nil
}
var rangeTests = []struct {
prevRev int64
response *clientv3.GetResponse
}{
{
5,
&clientv3.GetResponse{
Header: &pb.ResponseHeader{
Revision: 5,
},
},
},
{
5,
&clientv3.GetResponse{
Header: &pb.ResponseHeader{
Revision: 4,
},
},
},
{
5,
&clientv3.GetResponse{
Header: &pb.ResponseHeader{
Revision: 6,
},
},
},
}
func TestKvOrdering(t *testing.T) {
for i, tt := range rangeTests {
mKV := &mockKV{clientv3.NewKVFromKVClient(nil, nil), tt.response.OpResponse()}
kv := &kvOrdering{
mKV,
func(r *clientv3.GetResponse) OrderViolationFunc {
return func(op clientv3.Op, resp clientv3.OpResponse, prevRev int64) error {
r.Header.Revision++
return nil
}
}(tt.response),
tt.prevRev,
sync.RWMutex{},
}
res, err := kv.Get(t.Context(), "mockKey")
if err != nil {
t.Errorf("#%d: expected response %+v, got error %+v", i, tt.response, err)
}
if rev := res.Header.Revision; rev < tt.prevRev {
t.Errorf("#%d: expected revision %d, got %d", i, tt.prevRev, rev)
}
}
}
var txnTests = []struct {
prevRev int64
response *clientv3.TxnResponse
}{
{
5,
&clientv3.TxnResponse{
Header: &pb.ResponseHeader{
Revision: 5,
},
},
},
{
5,
&clientv3.TxnResponse{
Header: &pb.ResponseHeader{
Revision: 8,
},
},
},
{
5,
&clientv3.TxnResponse{
Header: &pb.ResponseHeader{
Revision: 4,
},
},
},
}
func TestTxnOrdering(t *testing.T) {
for i, tt := range txnTests {
mKV := &mockKV{clientv3.NewKVFromKVClient(nil, nil), tt.response.OpResponse()}
kv := &kvOrdering{
mKV,
func(r *clientv3.TxnResponse) OrderViolationFunc {
return func(op clientv3.Op, resp clientv3.OpResponse, prevRev int64) error {
r.Header.Revision++
return nil
}
}(tt.response),
tt.prevRev,
sync.RWMutex{},
}
txn := &txnOrdering{
kv.Txn(t.Context()),
kv,
t.Context(),
sync.Mutex{},
[]clientv3.Cmp{},
[]clientv3.Op{},
[]clientv3.Op{},
}
res, err := txn.Commit()
if err != nil {
t.Errorf("#%d: expected response %+v, got error %+v", i, tt.response, err)
}
if rev := res.Header.Revision; rev < tt.prevRev {
t.Errorf("#%d: expected revision %d, got %d", i, tt.prevRev, rev)
}
}
}
================================================
FILE: client/v3/ordering/util.go
================================================
// Copyright 2017 The etcd Authors
//
// 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 ordering
import (
"errors"
"sync/atomic"
clientv3 "go.etcd.io/etcd/client/v3"
)
type OrderViolationFunc func(op clientv3.Op, resp clientv3.OpResponse, prevRev int64) error
var ErrNoGreaterRev = errors.New("etcdclient: no cluster members have a revision higher than the previously received revision")
func NewOrderViolationSwitchEndpointClosure(c *clientv3.Client) OrderViolationFunc {
violationCount := int32(0)
return func(_ clientv3.Op, _ clientv3.OpResponse, _ int64) error {
// Each request is assigned by round-robin load-balancer's picker to a different
// endpoint. If we cycled them 5 times (even with some level of concurrency),
// with high probability no endpoint points on a member with fresh data.
// TODO: Ideally we should track members (resp.opp.Header) that returned
// stale result and explicitly temporarily disable them in 'picker'.
if atomic.LoadInt32(&violationCount) > int32(5*len(c.Endpoints())) {
return ErrNoGreaterRev
}
atomic.AddInt32(&violationCount, 1)
return nil
}
}
================================================
FILE: client/v3/retry.go
================================================
// Copyright 2016 The etcd Authors
//
// 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 clientv3
import (
"context"
"errors"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
pb "go.etcd.io/etcd/api/v3/etcdserverpb"
"go.etcd.io/etcd/api/v3/v3rpc/rpctypes"
)
type retryPolicy uint8
const (
repeatable retryPolicy = iota
nonRepeatable
)
func (rp retryPolicy) String() string {
switch rp {
case repeatable:
return "repeatable"
case nonRepeatable:
return "nonRepeatable"
default:
return "UNKNOWN"
}
}
// isSafeRetryImmutableRPC returns "true" when an immutable request is safe for retry.
//
// immutable requests (e.g. Get) should be retried unless it's
// an obvious server-side error (e.g. rpctypes.ErrRequestTooLarge).
//
// Returning "false" means retry should stop, since client cannot
// handle itself even with retries.
func isSafeRetryImmutableRPC(err error) bool {
eErr := rpctypes.Error(err)
var serverErr rpctypes.EtcdError
if errors.As(eErr, &serverErr) && serverErr.Code() != codes.Unavailable {
// interrupted by non-transient server-side or gRPC-side error
// client cannot handle itself (e.g. rpctypes.ErrCompacted)
return false
}
// only retry if unavailable
ev, ok := status.FromError(err)
if !ok {
// all errors from RPC is typed "grpc/status.(*statusError)"
// (ref. https://github.com/grpc/grpc-go/pull/1782)
//
// if the error type is not "grpc/status.(*statusError)",
// it could be from "Dial"
// TODO: do not retry for now
// ref. https://github.com/grpc/grpc-go/issues/1581
return false
}
return ev.Code() == codes.Unavailable
}
// isSafeRetryMutableRPC returns "true" when a mutable request is safe for retry.
//
// mutable requests (e.g. Put, Delete, Txn) should only be retried
// when the status code is codes.Unavailable when initial connection
// has not been established (no endpoint is up).
//
// Returning "false" means retry should stop, otherwise it violates
// write-at-most-once semantics.
func isSafeRetryMutableRPC(err error) bool {
if ev, ok := status.FromError(err); ok && ev.Code() != codes.Unavailable {
// not safe for mutable RPCs
// e.g. interrupted by non-transient error that client cannot handle itself,
// or transient error while the connection has already been established
return false
}
desc := rpctypes.ErrorDesc(err)
return desc == "there is no address available" || desc == "there is no connection available"
}
type retryKVClient struct {
kc pb.KVClient
}
// RetryKVClient implements a KVClient.
func RetryKVClient(c *Client) pb.KVClient {
return &retryKVClient{
kc: pb.NewKVClient(c.conn),
}
}
func (rkv *retryKVClient) Range(ctx context.Context, in *pb.RangeRequest, opts ...grpc.CallOption) (resp *pb.RangeResponse, err error) {
return rkv.kc.Range(ctx, in, append(opts, withRepeatablePolicy())...)
}
func (rkv *retryKVClient) Put(ctx context.Context, in *pb.PutRequest, opts ...grpc.CallOption) (resp *pb.PutResponse, err error) {
return rkv.kc.Put(ctx, in, opts...)
}
func (rkv *retryKVClient) DeleteRange(ctx context.Context, in *pb.DeleteRangeRequest, opts ...grpc.CallOption) (resp *pb.DeleteRangeResponse, err error) {
return rkv.kc.DeleteRange(ctx, in, opts...)
}
func (rkv *retryKVClient) Txn(ctx context.Context, in *pb.TxnRequest, opts ...grpc.CallOption) (resp *pb.TxnResponse, err error) {
return rkv.kc.Txn(ctx, in, opts...)
}
func (rkv *retryKVClient) Compact(ctx context.Context, in *pb.CompactionRequest, opts ...grpc.CallOption) (resp *pb.CompactionResponse, err error) {
return rkv.kc.Compact(ctx, in, opts...)
}
type retryLeaseClient struct {
lc pb.LeaseClient
}
// RetryLeaseClient implements a LeaseClient.
func RetryLeaseClient(c *Client) pb.LeaseClient {
return &retryLeaseClient{
lc: pb.NewLeaseClient(c.conn),
}
}
func (rlc *retryLeaseClient) LeaseTimeToLive(ctx context.Context, in *pb.LeaseTimeToLiveRequest, opts ...grpc.CallOption) (resp *pb.LeaseTimeToLiveResponse, err error) {
return rlc.lc.LeaseTimeToLive(ctx, in, append(opts, withRepeatablePolicy())...)
}
func (rlc *retryLeaseClient) LeaseLeases(ctx context.Context, in *pb.LeaseLeasesRequest, opts ...grpc.CallOption) (resp *pb.LeaseLeasesResponse, err error) {
return rlc.lc.LeaseLeases(ctx, in, append(opts, withRepeatablePolicy())...)
}
func (rlc *retryLeaseClient) LeaseGrant(ctx context.Context, in *pb.LeaseGrantRequest, opts ...grpc.CallOption) (resp *pb.LeaseGrantResponse, err error) {
return rlc.lc.LeaseGrant(ctx, in, append(opts, withRepeatablePolicy())...)
}
func (rlc *retryLeaseClient) LeaseRevoke(ctx context.Context, in *pb.LeaseRevokeRequest, opts ...grpc.CallOption) (resp *pb.LeaseRevokeResponse, err error) {
return rlc.lc.LeaseRevoke(ctx, in, append(opts, withRepeatablePolicy())...)
}
func (rlc *retryLeaseClient) LeaseKeepAlive(ctx context.Context, opts ...grpc.CallOption) (stream pb.Lease_LeaseKeepAliveClient, err error) {
return rlc.lc.LeaseKeepAlive(ctx, append(opts, withRepeatablePolicy())...)
}
type retryClusterClient struct {
cc pb.ClusterClient
}
// RetryClusterClient implements a ClusterClient.
func RetryClusterClient(c *Client) pb.ClusterClient {
return &retryClusterClient{
cc: pb.NewClusterClient(c.conn),
}
}
func (rcc *retryClusterClient) MemberList(ctx context.Context, in *pb.MemberListRequest, opts ...grpc.CallOption) (resp *pb.MemberListResponse, err error) {
return rcc.cc.MemberList(ctx, in, append(opts, withRepeatablePolicy())...)
}
func (rcc *retryClusterClient) MemberAdd(ctx context.Context, in *pb.MemberAddRequest, opts ...grpc.CallOption) (resp *pb.MemberAddResponse, err error) {
return rcc.cc.MemberAdd(ctx, in, opts...)
}
func (rcc *retryClusterClient) MemberRemove(ctx context.Context, in *pb.MemberRemoveRequest, opts ...grpc.CallOption) (resp *pb.MemberRemoveResponse, err error) {
return rcc.cc.MemberRemove(ctx, in, opts...)
}
func (rcc *retryClusterClient) MemberUpdate(ctx context.Context, in *pb.MemberUpdateRequest, opts ...grpc.CallOption) (resp *pb.MemberUpdateResponse, err error) {
return rcc.cc.MemberUpdate(ctx, in, opts...)
}
func (rcc *retryClusterClient) MemberPromote(ctx context.Context, in *pb.MemberPromoteRequest, opts ...grpc.CallOption) (resp *pb.MemberPromoteResponse, err error) {
return rcc.cc.MemberPromote(ctx, in, opts...)
}
type retryMaintenanceClient struct {
mc pb.MaintenanceClient
}
// RetryMaintenanceClient implements a Maintenance.
func RetryMaintenanceClient(c *Client, conn *grpc.ClientConn) pb.MaintenanceClient {
return &retryMaintenanceClient{
mc: pb.NewMaintenanceClient(conn),
}
}
func (rmc *retryMaintenanceClient) Alarm(ctx context.Context, in *pb.AlarmRequest, opts ...grpc.CallOption) (resp *pb.AlarmResponse, err error) {
return rmc.mc.Alarm(ctx, in, append(opts, withRepeatablePolicy())...)
}
func (rmc *retryMaintenanceClient) Status(ctx context.Context, in *pb.StatusRequest, opts ...grpc.CallOption) (resp *pb.StatusResponse, err error) {
return rmc.mc.Status(ctx, in, append(opts, withRepeatablePolicy())...)
}
func (rmc *retryMaintenanceClient) Hash(ctx context.Context, in *pb.HashRequest, opts ...grpc.CallOption) (resp *pb.HashResponse, err error) {
return rmc.mc.Hash(ctx, in, append(opts, withRepeatablePolicy())...)
}
func (rmc *retryMaintenanceClient) HashKV(ctx context.Context, in *pb.HashKVRequest, opts ...grpc.CallOption) (resp *pb.HashKVResponse, err error) {
return rmc.mc.HashKV(ctx, in, append(opts, withRepeatablePolicy())...)
}
func (rmc *retryMaintenanceClient) Snapshot(ctx context.Context, in *pb.SnapshotRequest, opts ...grpc.CallOption) (stream pb.Maintenance_SnapshotClient, err error) {
return rmc.mc.Snapshot(ctx, in, append(opts, withRepeatablePolicy())...)
}
func (rmc *retryMaintenanceClient) MoveLeader(ctx context.Context, in *pb.MoveLeaderRequest, opts ...grpc.CallOption) (resp *pb.MoveLeaderResponse, err error) {
return rmc.mc.MoveLeader(ctx, in, append(opts, withRepeatablePolicy())...)
}
func (rmc *retryMaintenanceClient) Defragment(ctx context.Context, in *pb.DefragmentRequest, opts ...grpc.CallOption) (resp *pb.DefragmentResponse, err error) {
return rmc.mc.Defragment(ctx, in, opts...)
}
func (rmc *retryMaintenanceClient) Downgrade(ctx context.Context, in *pb.DowngradeRequest, opts ...grpc.CallOption) (resp *pb.DowngradeResponse, err error) {
return rmc.mc.Downgrade(ctx, in, opts...)
}
type retryAuthClient struct {
ac pb.AuthClient
}
// RetryAuthClient implements a AuthClient.
func RetryAuthClient(c *Client) pb.AuthClient {
return &retryAuthClient{
ac: pb.NewAuthClient(c.conn),
}
}
func (rac *retryAuthClient) UserList(ctx context.Context, in *pb.AuthUserListRequest, opts ...grpc.CallOption) (resp *pb.AuthUserListResponse, err error) {
return rac.ac.UserList(ctx, in, append(opts, withRepeatablePolicy())...)
}
func (rac *retryAuthClient) UserGet(ctx context.Context, in *pb.AuthUserGetRequest, opts ...grpc.CallOption) (resp *pb.AuthUserGetResponse, err error) {
return rac.ac.UserGet(ctx, in, append(opts, withRepeatablePolicy())...)
}
func (rac *retryAuthClient) RoleGet(ctx context.Context, in *pb.AuthRoleGetRequest, opts ...grpc.CallOption) (resp *pb.AuthRoleGetResponse, err error) {
return rac.ac.RoleGet(ctx, in, append(opts, withRepeatablePolicy())...)
}
func (rac *retryAuthClient) RoleList(ctx context.Context, in *pb.AuthRoleListRequest, opts ...grpc.CallOption) (resp *pb.AuthRoleListResponse, err error) {
return rac.ac.RoleList(ctx, in, append(opts, withRepeatablePolicy())...)
}
func (rac *retryAuthClient) AuthEnable(ctx context.Context, in *pb.AuthEnableRequest, opts ...grpc.CallOption) (resp *pb.AuthEnableResponse, err error) {
return rac.ac.AuthEnable(ctx, in, opts...)
}
func (rac *retryAuthClient) AuthDisable(ctx context.Context, in *pb.AuthDisableRequest, opts ...grpc.CallOption) (resp *pb.AuthDisableResponse, err error) {
return rac.ac.AuthDisable(ctx, in, opts...)
}
func (rac *retryAuthClient) AuthStatus(ctx context.Context, in *pb.AuthStatusRequest, opts ...grpc.CallOption) (resp *pb.AuthStatusResponse, err error) {
return rac.ac.AuthStatus(ctx, in, opts...)
}
func (rac *retryAuthClient) UserAdd(ctx context.Context, in *pb.AuthUserAddRequest, opts ...grpc.CallOption) (resp *pb.AuthUserAddResponse, err error) {
return rac.ac.UserAdd(ctx, in, opts...)
}
func (rac *retryAuthClient) UserDelete(ctx context.Context, in *pb.AuthUserDeleteRequest, opts ...grpc.CallOption) (resp *pb.AuthUserDeleteResponse, err error) {
return rac.ac.UserDelete(ctx, in, opts...)
}
func (rac *retryAuthClient) UserChangePassword(ctx context.Context, in *pb.AuthUserChangePasswordRequest, opts ...grpc.CallOption) (resp *pb.AuthUserChangePasswordResponse, err error) {
return rac.ac.UserChangePassword(ctx, in, opts...)
}
func (rac *retryAuthClient) UserGrantRole(ctx context.Context, in *pb.AuthUserGrantRoleRequest, opts ...grpc.CallOption) (resp *pb.AuthUserGrantRoleResponse, err error) {
return rac.ac.UserGrantRole(ctx, in, opts...)
}
func (rac *retryAuthClient) UserRevokeRole(ctx context.Context, in *pb.AuthUserRevokeRoleRequest, opts ...grpc.CallOption) (resp *pb.AuthUserRevokeRoleResponse, err error) {
return rac.ac.UserRevokeRole(ctx, in, opts...)
}
func (rac *retryAuthClient) RoleAdd(ctx context.Context, in *pb.AuthRoleAddRequest, opts ...grpc.CallOption) (resp *pb.AuthRoleAddResponse, err error) {
return rac.ac.RoleAdd(ctx, in, opts...)
}
func (rac *retryAuthClient) RoleDelete(ctx context.Context, in *pb.AuthRoleDeleteRequest, opts ...grpc.CallOption) (resp *pb.AuthRoleDeleteResponse, err error) {
return rac.ac.RoleDelete(ctx, in, opts...)
}
func (rac *retryAuthClient) RoleGrantPermission(ctx context.Context, in *pb.AuthRoleGrantPermissionRequest, opts ...grpc.CallOption) (resp *pb.AuthRoleGrantPermissionResponse, err error) {
return rac.ac.RoleGrantPermission(ctx, in, opts...)
}
func (rac *retryAuthClient) RoleRevokePermission(ctx context.Context, in *pb.AuthRoleRevokePermissionRequest, opts ...grpc.CallOption) (resp *pb.AuthRoleRevokePermissionResponse, err error) {
return rac.ac.RoleRevokePermission(ctx, in, opts...)
}
func (rac *retryAuthClient) Authenticate(ctx context.Context, in *pb.AuthenticateRequest, opts ...grpc.CallOption) (resp *pb.AuthenticateResponse, err error) {
return rac.ac.Authenticate(ctx, in, opts...)
}
================================================
FILE: client/v3/retry_interceptor.go
================================================
// Copyright 2016 The etcd Authors
//
// 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.
// Based on github.com/grpc-ecosystem/go-grpc-middleware/retry, but modified to support the more
// fine grained error checking required by write-at-most-once retry semantics of etcd.
package clientv3
import (
"context"
"errors"
"io"
"sync"
"time"
"go.uber.org/zap"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/metadata"
"google.golang.org/grpc/peer"
"google.golang.org/grpc/status"
"go.etcd.io/etcd/api/v3/v3rpc/rpctypes"
)
// unaryClientInterceptor returns a new retrying unary client interceptor.
//
// The default configuration of the interceptor is to not retry *at all*. This behaviour can be
// changed through options (e.g. WithMax) on creation of the interceptor or on call (through grpc.CallOptions).
func (c *Client) unaryClientInterceptor(optFuncs ...retryOption) grpc.UnaryClientInterceptor {
intOpts := reuseOrNewWithCallOptions(defaultOptions, optFuncs)
return func(ctx context.Context, method string, req, reply any, cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error {
ctx = withVersion(ctx)
grpcOpts, retryOpts := filterCallOptions(opts)
var p peer.Peer
grpcOpts = append(grpcOpts, grpc.Peer(&p))
callOpts := reuseOrNewWithCallOptions(intOpts, retryOpts)
// short circuit for simplicity, and avoiding allocations.
if callOpts.max == 0 {
return invoker(ctx, method, req, reply, cc, grpcOpts...)
}
var lastErr error
for attempt := uint(0); attempt < callOpts.max; attempt++ {
if err := waitRetryBackoff(ctx, attempt, callOpts); err != nil {
return err
}
c.GetLogger().Debug(
"retrying of unary invoker",
zap.String("target", cc.Target()),
zap.String("method", method),
zap.Uint("attempt", attempt),
)
lastErr = invoker(ctx, method, req, reply, cc, grpcOpts...)
if lastErr == nil {
return nil
}
c.GetLogger().Warn(
"retrying of unary invoker failed",
zap.String("target", cc.Target()),
zap.String("peer", p.String()),
zap.String("method", method),
zap.Uint("attempt", attempt),
zap.Error(lastErr),
)
if isContextError(lastErr) {
if ctx.Err() != nil {
// its the context deadline or cancellation.
return lastErr
}
// its the callCtx deadline or cancellation, in which case try again.
continue
}
if c.shouldRefreshToken(lastErr, callOpts) {
gtErr := c.refreshToken(ctx)
if gtErr != nil {
c.GetLogger().Warn(
"retrying of unary invoker failed to fetch new auth token",
zap.String("target", cc.Target()),
zap.Error(gtErr),
)
return gtErr // lastErr must be invalid auth token
}
continue
}
if !isSafeRetry(c, lastErr, callOpts) {
return lastErr
}
}
return lastErr
}
}
// streamClientInterceptor returns a new retrying stream client interceptor for server side streaming calls.
//
// The default configuration of the interceptor is to not retry *at all*. This behaviour can be
// changed through options (e.g. WithMax) on creation of the interceptor or on call (through grpc.CallOptions).
//
// Retry logic is available *only for ServerStreams*, i.e. 1:n streams, as the internal logic needs
// to buffer the messages sent by the client. If retry is enabled on any other streams (ClientStreams,
// BidiStreams), the retry interceptor will fail the call.
func (c *Client) streamClientInterceptor(optFuncs ...retryOption) grpc.StreamClientInterceptor {
intOpts := reuseOrNewWithCallOptions(defaultOptions, optFuncs)
return func(ctx context.Context, desc *grpc.StreamDesc, cc *grpc.ClientConn, method string, streamer grpc.Streamer, opts ...grpc.CallOption) (grpc.ClientStream, error) {
ctx = withVersion(ctx)
// getToken automatically. Otherwise, auth token may be invalid after watch reconnection because the token has expired
// (see https://github.com/etcd-io/etcd/issues/11954 for more).
err := c.getToken(ctx)
if err != nil {
c.GetLogger().Error("clientv3/retry_interceptor: getToken failed", zap.Error(err))
return nil, err
}
grpcOpts, retryOpts := filterCallOptions(opts)
callOpts := reuseOrNewWithCallOptions(intOpts, retryOpts)
// short circuit for simplicity, and avoiding allocations.
if callOpts.max == 0 {
return streamer(ctx, desc, cc, method, grpcOpts...)
}
if desc.ClientStreams {
return nil, status.Errorf(codes.Unimplemented, "clientv3/retry_interceptor: cannot retry on ClientStreams, set Disable()")
}
newStreamer, err := streamer(ctx, desc, cc, method, grpcOpts...)
if err != nil {
c.GetLogger().Error("streamer failed to create ClientStream", zap.Error(err))
return nil, err // TODO(mwitkow): Maybe dial and transport errors should be retriable?
}
retryingStreamer := &serverStreamingRetryingStream{
client: c,
ClientStream: newStreamer,
callOpts: callOpts,
ctx: ctx,
streamerCall: func(ctx context.Context) (grpc.ClientStream, error) {
return streamer(ctx, desc, cc, method, grpcOpts...)
},
}
return retryingStreamer, nil
}
}
// shouldRefreshToken checks whether there's a need to refresh the token based on the error and callOptions,
// and returns a boolean value.
func (c *Client) shouldRefreshToken(err error, callOpts *options) bool {
if c.Token != "" {
// do not try to refresh the token as it is set by user
return false
}
if errors.Is(rpctypes.Error(err), rpctypes.ErrUserEmpty) {
// refresh the token when username, password is present but the server returns ErrUserEmpty
// which is possible when the client token is cleared somehow
return c.authTokenBundle != nil // equal to c.Username != "" && c.Password != ""
}
return callOpts.retryAuth &&
(errors.Is(rpctypes.Error(err), rpctypes.ErrInvalidAuthToken) || errors.Is(rpctypes.Error(err), rpctypes.ErrAuthOldRevision))
}
func (c *Client) refreshToken(ctx context.Context) error {
if c.authTokenBundle == nil {
// c.authTokenBundle will be initialized only when
// c.Username != "" && c.Password != "".
//
// When users use the TLS CommonName based authentication, the
// authTokenBundle is always nil. But it's possible for the clients
// to get `rpctypes.ErrAuthOldRevision` response when the clients
// concurrently modify auth data (e.g, addUser, deleteUser etc.).
// In this case, there is no need to refresh the token; instead the
// clients just need to retry the operations (e.g. Put, Delete etc).
return nil
}
return c.getToken(ctx)
}
// type serverStreamingRetryingStream is the implementation of grpc.ClientStream that acts as a
// proxy to the underlying call. If any of the RecvMsg() calls fail, it will try to reestablish
// a new ClientStream according to the retry policy.
type serverStreamingRetryingStream struct {
grpc.ClientStream
client *Client
bufferedSends []any // single message that the client can sen
receivedGood bool // indicates whether any prior receives were successful
wasClosedSend bool // indicates that CloseSend was closed
ctx context.Context
callOpts *options
streamerCall func(ctx context.Context) (grpc.ClientStream, error)
mu sync.RWMutex
}
func (s *serverStreamingRetryingStream) setStream(clientStream grpc.ClientStream) {
s.mu.Lock()
s.ClientStream = clientStream
s.mu.Unlock()
}
func (s *serverStreamingRetryingStream) getStream() grpc.ClientStream {
s.mu.RLock()
defer s.mu.RUnlock()
return s.ClientStream
}
func (s *serverStreamingRetryingStream) SendMsg(m any) error {
s.mu.Lock()
s.bufferedSends = append(s.bufferedSends, m)
s.mu.Unlock()
return s.getStream().SendMsg(m)
}
func (s *serverStreamingRetryingStream) CloseSend() error {
s.mu.Lock()
s.wasClosedSend = true
s.mu.Unlock()
return s.getStream().CloseSend()
}
func (s *serverStreamingRetryingStream) Header() (metadata.MD, error) {
return s.getStream().Header()
}
func (s *serverStreamingRetryingStream) Trailer() metadata.MD {
return s.getStream().Trailer()
}
func (s *serverStreamingRetryingStream) RecvMsg(m any) error {
attemptRetry, lastErr := s.receiveMsgAndIndicateRetry(m)
if !attemptRetry {
return lastErr // success or hard failure
}
// We start off from attempt 1, because zeroth was already made on normal SendMsg().
for attempt := uint(1); attempt < s.callOpts.max; attempt++ {
if err := waitRetryBackoff(s.ctx, attempt, s.callOpts); err != nil {
return err
}
newStream, err := s.reestablishStreamAndResendBuffer(s.ctx)
if err != nil {
s.client.GetLogger().Error("failed reestablishStreamAndResendBuffer", zap.Error(err))
return err // TODO(mwitkow): Maybe dial and transport errors should be retriable?
}
s.setStream(newStream)
s.client.GetLogger().Warn("retrying RecvMsg", zap.Error(lastErr))
attemptRetry, lastErr = s.receiveMsgAndIndicateRetry(m)
if !attemptRetry {
return lastErr
}
}
return lastErr
}
func (s *serverStreamingRetryingStream) receiveMsgAndIndicateRetry(m any) (bool, error) {
s.mu.RLock()
wasGood := s.receivedGood
s.mu.RUnlock()
err := s.getStream().RecvMsg(m)
if err == nil || errors.Is(err, io.EOF) {
s.mu.Lock()
s.receivedGood = true
s.mu.Unlock()
return false, err
} else if wasGood {
// previous RecvMsg in the stream succeeded, no retry logic should interfere
return false, err
}
if isContextError(err) {
if s.ctx.Err() != nil {
return false, err
}
// its the callCtx deadline or cancellation, in which case try again.
return true, err
}
if s.client.shouldRefreshToken(err, s.callOpts) {
gtErr := s.client.refreshToken(s.ctx)
if gtErr != nil {
s.client.GetLogger().Warn("retry failed to fetch new auth token", zap.Error(gtErr))
return false, err // return the original error for simplicity
}
return true, err
}
return isSafeRetry(s.client, err, s.callOpts), err
}
func (s *serverStreamingRetryingStream) reestablishStreamAndResendBuffer(callCtx context.Context) (grpc.ClientStream, error) {
s.mu.RLock()
bufferedSends := s.bufferedSends
s.mu.RUnlock()
newStream, err := s.streamerCall(callCtx)
if err != nil {
return nil, err
}
for _, msg := range bufferedSends {
if err := newStream.SendMsg(msg); err != nil {
return nil, err
}
}
if err := newStream.CloseSend(); err != nil {
return nil, err
}
return newStream, nil
}
func waitRetryBackoff(ctx context.Context, attempt uint, callOpts *options) error {
waitTime := time.Duration(0)
if attempt > 0 {
waitTime = callOpts.backoffFunc(attempt)
}
if waitTime > 0 {
timer := time.NewTimer(waitTime)
select {
case <-ctx.Done():
timer.Stop()
return contextErrToGRPCErr(ctx.Err())
case <-timer.C:
}
}
return nil
}
// isSafeRetry returns "true", if request is safe for retry with the given error.
func isSafeRetry(c *Client, err error, callOpts *options) bool {
if isContextError(err) {
return false
}
// Situation when learner refuses RPC it is supposed to not serve is from the server
// perspective not retryable.
// But for backward-compatibility reasons we need to support situation that
// customer provides mix of learners (not yet voters) and voters with an
// expectation to pick voter in the next attempt.
// TODO: Ideally client should be 'aware' which endpoint represents: leader/voter/learner with high probability.
if errors.Is(err, rpctypes.ErrGRPCNotSupportedForLearner) && len(c.Endpoints()) > 1 {
return true
}
switch callOpts.retryPolicy {
case repeatable:
return isSafeRetryImmutableRPC(err)
case nonRepeatable:
return isSafeRetryMutableRPC(err)
default:
c.GetLogger().Warn("unrecognized retry policy", zap.String("retryPolicy", callOpts.retryPolicy.String()))
return false
}
}
func isContextError(err error) bool {
return status.Code(err) == codes.DeadlineExceeded || status.Code(err) == codes.Canceled
}
func contextErrToGRPCErr(err error) error {
switch {
case errors.Is(err, context.DeadlineExceeded):
return status.Error(codes.DeadlineExceeded, err.Error())
case errors.Is(err, context.Canceled):
return status.Error(codes.Canceled, err.Error())
default:
return status.Error(codes.Unknown, err.Error())
}
}
var defaultOptions = &options{
retryPolicy: nonRepeatable,
max: 0, // disable
backoffFunc: backoffLinearWithJitter(50*time.Millisecond /*jitter*/, 0.10),
retryAuth: true,
}
// backoffFunc denotes a family of functions that control the backoff duration between call retries.
//
// They are called with an identifier of the attempt, and should return a time the system client should
// hold off for. If the time returned is longer than the `context.Context.Deadline` of the request
// the deadline of the request takes precedence and the wait will be interrupted before proceeding
// with the next iteration.
type backoffFunc func(attempt uint) time.Duration
// withRepeatablePolicy sets the repeatable policy of this call.
func withRepeatablePolicy() retryOption {
return retryOption{applyFunc: func(o *options) {
o.retryPolicy = repeatable
}}
}
// withMax sets the maximum number of retries on this call, or this interceptor.
func withMax(maxRetries uint) retryOption {
return retryOption{applyFunc: func(o *options) {
o.max = maxRetries
}}
}
// WithBackoff sets the `BackoffFunc` used to control time between retries.
func withBackoff(bf backoffFunc) retryOption {
return retryOption{applyFunc: func(o *options) {
o.backoffFunc = bf
}}
}
type options struct {
retryPolicy retryPolicy
max uint
backoffFunc backoffFunc
retryAuth bool
}
// retryOption is a grpc.CallOption that is local to clientv3's retry interceptor.
type retryOption struct {
grpc.EmptyCallOption // make sure we implement private after() and before() fields so we don't panic.
applyFunc func(opt *options)
}
func reuseOrNewWithCallOptions(opt *options, retryOptions []retryOption) *options {
if len(retryOptions) == 0 {
return opt
}
optCopy := &options{}
*optCopy = *opt
for _, f := range retryOptions {
f.applyFunc(optCopy)
}
return optCopy
}
func filterCallOptions(callOptions []grpc.CallOption) (grpcOptions []grpc.CallOption, retryOptions []retryOption) {
for _, opt := range callOptions {
if co, ok := opt.(retryOption); ok {
retryOptions = append(retryOptions, co)
} else {
grpcOptions = append(grpcOptions, opt)
}
}
return grpcOptions, retryOptions
}
// BackoffLinearWithJitter waits a set period of time, allowing for jitter (fractional adjustment).
//
// For example waitBetween=1s and jitter=0.10 can generate waits between 900ms and 1100ms.
func backoffLinearWithJitter(waitBetween time.Duration, jitterFraction float64) backoffFunc {
return func(attempt uint) time.Duration {
return jitterUp(waitBetween, jitterFraction)
}
}
================================================
FILE: client/v3/retry_interceptor_test.go
================================================
// Copyright 2022 The etcd Authors
//
// 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 clientv3
import (
"testing"
grpccredentials "google.golang.org/grpc/credentials"
"go.etcd.io/etcd/api/v3/v3rpc/rpctypes"
"go.etcd.io/etcd/client/v3/credentials"
)
type dummyAuthTokenBundle struct{}
func (d dummyAuthTokenBundle) PerRPCCredentials() grpccredentials.PerRPCCredentials {
return nil
}
func (d dummyAuthTokenBundle) UpdateAuthToken(token string) {
}
func TestClientShouldRefreshToken(t *testing.T) {
type fields struct {
authTokenBundle credentials.PerRPCCredentialsBundle
token string
}
type args struct {
err error
callOpts *options
}
optsWithTrue := &options{
retryAuth: true,
}
optsWithFalse := &options{
retryAuth: false,
}
tests := []struct {
name string
fields fields
args args
want bool
}{
{
name: "ErrUserEmpty and non nil authTokenBundle",
fields: fields{
authTokenBundle: &dummyAuthTokenBundle{},
},
args: args{rpctypes.ErrGRPCUserEmpty, optsWithTrue},
want: true,
},
{
name: "ErrUserEmpty and nil authTokenBundle",
fields: fields{
authTokenBundle: nil,
},
args: args{rpctypes.ErrGRPCUserEmpty, optsWithTrue},
want: false,
},
{
name: "ErrGRPCInvalidAuthToken and retryAuth",
fields: fields{
authTokenBundle: nil,
},
args: args{rpctypes.ErrGRPCInvalidAuthToken, optsWithTrue},
want: true,
},
{
name: "ErrGRPCInvalidAuthToken and !retryAuth",
fields: fields{
authTokenBundle: nil,
},
args: args{rpctypes.ErrGRPCInvalidAuthToken, optsWithFalse},
want: false,
},
{
name: "ErrGRPCAuthOldRevision and retryAuth",
fields: fields{
authTokenBundle: nil,
},
args: args{rpctypes.ErrGRPCAuthOldRevision, optsWithTrue},
want: true,
},
{
name: "ErrGRPCAuthOldRevision and !retryAuth",
fields: fields{
authTokenBundle: nil,
},
args: args{rpctypes.ErrGRPCAuthOldRevision, optsWithFalse},
want: false,
},
{
name: "Other error and retryAuth",
fields: fields{
authTokenBundle: nil,
},
args: args{rpctypes.ErrGRPCAuthFailed, optsWithTrue},
want: false,
},
{
name: "Other error and !retryAuth",
fields: fields{
authTokenBundle: nil,
},
args: args{rpctypes.ErrGRPCAuthFailed, optsWithFalse},
want: false,
},
{
name: "User provided token, ErrGRPCInvalidAuthToken",
fields: fields{
authTokenBundle: nil,
token: "user-supplied-token",
},
args: args{rpctypes.ErrGRPCInvalidAuthToken, optsWithTrue},
want: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
c := &Client{
authTokenBundle: tt.fields.authTokenBundle,
Token: tt.fields.token,
}
if got := c.shouldRefreshToken(tt.args.err, tt.args.callOpts); got != tt.want {
t.Errorf("shouldRefreshToken() = %v, want %v", got, tt.want)
}
})
}
}
================================================
FILE: client/v3/snapshot/doc.go
================================================
// Copyright 2018 The etcd Authors
//
// 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 snapshot implements utilities around etcd snapshot.
package snapshot
================================================
FILE: client/v3/snapshot/v3_snapshot.go
================================================
// Copyright 2018 The etcd Authors
//
// 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 snapshot
import (
"context"
"crypto/sha256"
"errors"
"fmt"
"io"
"os"
"time"
"github.com/dustin/go-humanize"
"go.uber.org/zap"
"go.etcd.io/etcd/client/pkg/v3/fileutil"
clientv3 "go.etcd.io/etcd/client/v3"
)
// hasChecksum returns "true" if the file size "n"
// has appended sha256 hash digest.
func hasChecksum(n int64) bool {
// 512 is chosen because it's a minimum disk sector size
// smaller than (and multiplies to) OS page size in most systems
return (n % 512) == sha256.Size
}
// SaveWithVersion fetches snapshot from remote etcd server, saves data
// to target path and returns server version. If the context "ctx" is canceled or timed out,
// snapshot save stream will error out (e.g. context.Canceled,
// context.DeadlineExceeded). Make sure to specify only one endpoint
// in client configuration. Snapshot API must be requested to a
// selected node, and saved snapshot is the point-in-time state of
// the selected node.
// Etcd ", v1),
// Compare(Version(k1), "=", 2)
// ).Then(
// OpPut(k2,v2), OpPut(k3,v3)
// ).Else(
// OpPut(k4,v4), OpPut(k5,v5)
// ).Commit()
type Txn interface {
// If takes a list of comparison. If all comparisons passed in succeed,
// the operations passed into Then() will be executed. Or the operations
// passed into Else() will be executed.
If(cs ...Cmp) Txn
// Then takes a list of operations. The Ops list will be executed, if the
// comparisons passed in If() succeed.
Then(ops ...Op) Txn
// Else takes a list of operations. The Ops list will be executed, if the
// comparisons passed in If() fail.
Else(ops ...Op) Txn
// Commit tries to commit the transaction.
Commit() (*TxnResponse, error)
}
type txn struct {
kv *kv
ctx context.Context
mu sync.Mutex
cif bool
cthen bool
celse bool
isWrite bool
cmps []*pb.Compare
sus []*pb.RequestOp
fas []*pb.RequestOp
callOpts []grpc.CallOption
}
func (txn *txn) If(cs ...Cmp) Txn {
txn.mu.Lock()
defer txn.mu.Unlock()
if txn.cif {
panic("cannot call If twice!")
}
if txn.cthen {
panic("cannot call If after Then!")
}
if txn.celse {
panic("cannot call If after Else!")
}
txn.cif = true
for i := range cs {
txn.cmps = append(txn.cmps, (*pb.Compare)(&cs[i]))
}
return txn
}
func (txn *txn) Then(ops ...Op) Txn {
txn.mu.Lock()
defer txn.mu.Unlock()
if txn.cthen {
panic("cannot call Then twice!")
}
if txn.celse {
panic("cannot call Then after Else!")
}
txn.cthen = true
for _, op := range ops {
txn.isWrite = txn.isWrite || op.isWrite()
txn.sus = append(txn.sus, op.toRequestOp())
}
return txn
}
func (txn *txn) Else(ops ...Op) Txn {
txn.mu.Lock()
defer txn.mu.Unlock()
if txn.celse {
panic("cannot call Else twice!")
}
txn.celse = true
for _, op := range ops {
txn.isWrite = txn.isWrite || op.isWrite()
txn.fas = append(txn.fas, op.toRequestOp())
}
return txn
}
func (txn *txn) Commit() (*TxnResponse, error) {
txn.mu.Lock()
defer txn.mu.Unlock()
r := &pb.TxnRequest{Compare: txn.cmps, Success: txn.sus, Failure: txn.fas}
var resp *pb.TxnResponse
var err error
resp, err = txn.kv.remote.Txn(txn.ctx, r, txn.callOpts...)
if err != nil {
return nil, ContextError(txn.ctx, err)
}
return (*TxnResponse)(resp), nil
}
================================================
FILE: client/v3/txn_test.go
================================================
// Copyright 2016 The etcd Authors
//
// 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 clientv3
import (
"testing"
"time"
"go.etcd.io/etcd/client/pkg/v3/testutil"
)
func TestTxnPanics(t *testing.T) {
testutil.RegisterLeakDetection(t)
kv := &kv{}
df := func(errc chan string) {
if s := recover(); s != nil {
errc <- s.(string)
}
}
cmp := Compare(CreateRevision("foo"), "=", 0)
op := OpPut("foo", "bar")
tests := []struct {
f func(chan string)
err string
}{
{
f: func(errc chan string) {
defer df(errc)
kv.Txn(t.Context()).If(cmp).If(cmp)
},
err: "cannot call If twice!",
},
{
f: func(errc chan string) {
defer df(errc)
kv.Txn(t.Context()).Then(op).If(cmp)
},
err: "cannot call If after Then!",
},
{
f: func(errc chan string) {
defer df(errc)
kv.Txn(t.Context()).Else(op).If(cmp)
},
err: "cannot call If after Else!",
},
{
f: func(errc chan string) {
defer df(errc)
kv.Txn(t.Context()).Then(op).Then(op)
},
err: "cannot call Then twice!",
},
{
f: func(errc chan string) {
defer df(errc)
kv.Txn(t.Context()).Else(op).Then(op)
},
err: "cannot call Then after Else!",
},
{
f: func(errc chan string) {
defer df(errc)
kv.Txn(t.Context()).Else(op).Else(op)
},
err: "cannot call Else twice!",
},
}
for i, tt := range tests {
errc := make(chan string, 1)
go tt.f(errc)
select {
case err := <-errc:
if err != tt.err {
t.Errorf("#%d: got %s, wanted %s", i, err, tt.err)
}
case <-time.After(time.Second):
t.Errorf("#%d: did not panic, wanted panic %s", i, tt.err)
}
}
}
================================================
FILE: client/v3/utils.go
================================================
// Copyright 2018 The etcd Authors
//
// 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 clientv3
import (
"math/rand"
"time"
)
// jitterUp adds random jitter to the duration.
//
// This adds or subtracts time from the duration within a given jitter fraction.
// For example for 10s and jitter 0.1, it will return a time within [9s, 11s])
//
// Reference: https://godoc.org/github.com/grpc-ecosystem/go-grpc-middleware/util/backoffutils
func jitterUp(duration time.Duration, jitter float64) time.Duration {
multiplier := jitter * (rand.Float64()*2 - 1)
return time.Duration(float64(duration) * (1 + multiplier))
}
================================================
FILE: client/v3/watch.go
================================================
// Copyright 2016 The etcd Authors
//
// 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 clientv3
import (
"context"
"errors"
"fmt"
"sync"
"time"
"go.uber.org/zap"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/metadata"
"google.golang.org/grpc/status"
pb "go.etcd.io/etcd/api/v3/etcdserverpb"
"go.etcd.io/etcd/api/v3/mvccpb"
v3rpc "go.etcd.io/etcd/api/v3/v3rpc/rpctypes"
)
const (
EventTypeDelete = mvccpb.Event_DELETE
EventTypePut = mvccpb.Event_PUT
closeSendErrTimeout = 250 * time.Millisecond
// AutoWatchID is the watcher ID passed in WatchStream.Watch when no
// user-provided ID is available. If pass, an ID will automatically be assigned.
AutoWatchID = 0
// InvalidWatchID represents an invalid watch ID and prevents duplication with an existing watch.
InvalidWatchID = -1
)
type Event mvccpb.Event
type WatchChan <-chan WatchResponse
type Watcher interface {
// Watch watches on a key or prefix. The watched events will be returned
// through the returned channel. If revisions waiting to be sent over the
// watch are compacted, then the watch will be canceled by the server, the
// client will post a compacted error watch response, and the channel will close.
// If the requested revision is 0 or unspecified, the returned channel will
// return watch events that happen after the server receives the watch request.
// If the context "ctx" is canceled or timed out, returned "WatchChan" is closed,
// and "WatchResponse" from this closed channel has zero events and nil "Err()".
// The context "ctx" MUST be canceled, as soon as watcher is no longer being used,
// to release the associated resources.
//
// If the context is "context.Background/TODO", returned "WatchChan" will
// not be closed and block until event is triggered, except when server
// returns a non-recoverable error (e.g. ErrCompacted).
// For example, when context passed with "WithRequireLeader" and the
// connected server has no leader (e.g. due to network partition),
// error "etcdserver: no leader" (ErrNoLeader) will be returned,
// and then "WatchChan" is closed with non-nil "Err()".
// In order to prevent a watch stream being stuck in a partitioned node,
// make sure to wrap context with "WithRequireLeader".
//
// Otherwise, as long as the context has not been canceled or timed out,
// watch will retry on other recoverable errors forever until reconnected.
//
// TODO: explicitly set context error in the last "WatchResponse" message and close channel?
// Currently, client contexts are overwritten with "valCtx" that never closes.
// TODO(v3.4): configure watch retry policy, limit maximum retry number
// (see https://github.com/etcd-io/etcd/issues/8980)
Watch(ctx context.Context, key string, opts ...OpOption) WatchChan
// RequestProgress requests a progress notify response be sent in all watch channels.
RequestProgress(ctx context.Context) error
// Close closes the watcher and cancels all watch requests.
Close() error
}
type WatchResponse struct {
Header pb.ResponseHeader
Events []*Event
// CompactRevision is the minimum revision the watcher may receive.
CompactRevision int64
// Canceled is used to indicate watch failure.
// If the watch failed and the stream was about to close, before the channel is closed,
// the channel sends a final response that has Canceled set to true with a non-nil Err().
Canceled bool
// Created is used to indicate the creation of the watcher.
Created bool
closeErr error
// CancelReason is a reason of canceling watch
CancelReason string
}
// IsCreate returns true if the event tells that the key is newly created.
func (e *Event) IsCreate() bool {
return e.Type == EventTypePut && e.Kv.CreateRevision == e.Kv.ModRevision
}
// IsModify returns true if the event tells that a new value is put on existing key.
func (e *Event) IsModify() bool {
return e.Type == EventTypePut && e.Kv.CreateRevision != e.Kv.ModRevision
}
// Err is the error value if this WatchResponse holds an error.
func (wr *WatchResponse) Err() error {
switch {
case wr.closeErr != nil:
return v3rpc.Error(wr.closeErr)
case wr.CompactRevision != 0:
return v3rpc.ErrCompacted
case wr.Canceled:
if len(wr.CancelReason) != 0 {
return v3rpc.Error(status.Error(codes.FailedPrecondition, wr.CancelReason))
}
return v3rpc.ErrFutureRev
}
return nil
}
// IsProgressNotify returns true if the WatchResponse is progress notification.
func (wr *WatchResponse) IsProgressNotify() bool {
return len(wr.Events) == 0 && !wr.Canceled && !wr.Created && wr.CompactRevision == 0 && wr.Header.Revision != 0
}
// watcher implements the Watcher interface
type watcher struct {
remote pb.WatchClient
callOpts []grpc.CallOption
// mu protects the grpc streams map
mu sync.Mutex
// streams holds all the active grpc streams keyed by ctx value.
streams map[string]*watchGRPCStream
lg *zap.Logger
}
// watchGRPCStream tracks all watch resources attached to a single grpc stream.
type watchGRPCStream struct {
owner *watcher
remote pb.WatchClient
callOpts []grpc.CallOption
// ctx controls internal remote.Watch requests
ctx context.Context
// ctxKey is the key used when looking up this stream's context
ctxKey string
cancel context.CancelFunc
// substreams holds all active watchers on this grpc stream
substreams map[int64]*watcherStream
// resuming holds all resuming watchers on this grpc stream
resuming []*watcherStream
// reqc sends a watch request from Watch() to the main goroutine
reqc chan watchStreamRequest
// respc receives data from the watch client
respc chan *pb.WatchResponse
// donec closes to broadcast shutdown
donec chan struct{}
// errc transmits errors from grpc Recv to the watch stream reconnect logic
errc chan error
// closingc gets the watcherStream of closing watchers
closingc chan *watcherStream
// wg is Done when all substream goroutines have exited
wg sync.WaitGroup
// resumec closes to signal that all substreams should begin resuming
resumec chan struct{}
// closeErr is the error that closed the watch stream
closeErr error
lg *zap.Logger
}
// watchStreamRequest is a union of the supported watch request operation types
type watchStreamRequest interface {
toPB() *pb.WatchRequest
}
// watchRequest is issued by the subscriber to start a new watcher
type watchRequest struct {
ctx context.Context
key string
end string
rev int64
// send created notification event if this field is true
createdNotify bool
// progressNotify is for progress updates
progressNotify bool
// fragmentation should be disabled by default
// if true, split watch events when total exceeds
// "--max-request-bytes" flag value + 512-byte
fragment bool
// filters is the list of events to filter out
filters []pb.WatchCreateRequest_FilterType
// get the previous key-value pair before the event happens
prevKV bool
// retc receives a chan WatchResponse once the watcher is established
retc chan chan WatchResponse
}
// progressRequest is issued by the subscriber to request watch progress
type progressRequest struct{}
// watcherStream represents a registered watcher
type watcherStream struct {
// initReq is the request that initiated this request
initReq watchRequest
// outc publishes watch responses to subscriber
outc chan WatchResponse
// recvc buffers watch responses before publishing
recvc chan *WatchResponse
// donec closes when the watcherStream goroutine stops.
donec chan struct{}
// closing is set to true when stream should be scheduled to shutdown.
closing bool
// id is the registered watch id on the grpc stream
id int64
// buf holds all events received from etcd but not yet consumed by the client
buf []*WatchResponse
}
func NewWatcher(c *Client) Watcher {
return NewWatchFromWatchClient(pb.NewWatchClient(c.conn), c)
}
func NewWatchFromWatchClient(wc pb.WatchClient, c *Client) Watcher {
w := &watcher{
remote: wc,
streams: make(map[string]*watchGRPCStream),
}
if c != nil {
w.callOpts = c.callOpts
w.lg = c.GetLogger()
}
return w
}
// never closes
var (
valCtxCh = make(chan struct{})
zeroTime = time.Unix(0, 0)
)
// ctx with only the values; never Done
type valCtx struct{ context.Context }
func (vc *valCtx) Deadline() (time.Time, bool) { return zeroTime, false }
func (vc *valCtx) Done() <-chan struct{} { return valCtxCh }
func (vc *valCtx) Err() error { return nil }
func (w *watcher) newWatcherGRPCStream(inctx context.Context) *watchGRPCStream {
ctx, cancel := context.WithCancel(&valCtx{inctx})
wgs := &watchGRPCStream{
owner: w,
remote: w.remote,
callOpts: w.callOpts,
ctx: ctx,
ctxKey: streamKeyFromCtx(inctx),
cancel: cancel,
substreams: make(map[int64]*watcherStream),
respc: make(chan *pb.WatchResponse),
reqc: make(chan watchStreamRequest),
donec: make(chan struct{}),
errc: make(chan error, 1),
closingc: make(chan *watcherStream),
resumec: make(chan struct{}),
lg: w.lg,
}
go wgs.run()
return wgs
}
// Watch posts a watch request to run() and waits for a new watcher channel
func (w *watcher) Watch(ctx context.Context, key string, opts ...OpOption) WatchChan {
ow := OpWatch(key, opts...)
var filters []pb.WatchCreateRequest_FilterType
if ow.filterPut {
filters = append(filters, pb.WatchCreateRequest_NOPUT)
}
if ow.filterDelete {
filters = append(filters, pb.WatchCreateRequest_NODELETE)
}
wr := &watchRequest{
ctx: ctx,
createdNotify: ow.createdNotify,
key: string(ow.key),
end: string(ow.end),
rev: ow.rev,
progressNotify: ow.progressNotify,
fragment: ow.fragment,
filters: filters,
prevKV: ow.prevKV,
retc: make(chan chan WatchResponse, 1),
}
ok := false
ctxKey := streamKeyFromCtx(ctx)
var closeCh chan WatchResponse
for {
// find or allocate appropriate grpc watch stream
w.mu.Lock()
if w.streams == nil {
// closed
w.mu.Unlock()
ch := make(chan WatchResponse)
close(ch)
return ch
}
wgs := w.streams[ctxKey]
if wgs == nil {
wgs = w.newWatcherGRPCStream(ctx)
w.streams[ctxKey] = wgs
}
donec := wgs.donec
reqc := wgs.reqc
w.mu.Unlock()
// couldn't create channel; return closed channel
if closeCh == nil {
closeCh = make(chan WatchResponse, 1)
}
// submit request
select {
case reqc <- wr:
ok = true
case <-wr.ctx.Done():
ok = false
case <-donec:
ok = false
if wgs.closeErr != nil {
closeCh <- WatchResponse{Canceled: true, closeErr: wgs.closeErr}
break
}
// retry; may have dropped stream from no ctxs
continue
}
// receive channel
if ok {
select {
case ret := <-wr.retc:
return ret
case <-ctx.Done():
case <-donec:
if wgs.closeErr != nil {
closeCh <- WatchResponse{Canceled: true, closeErr: wgs.closeErr}
break
}
// retry; may have dropped stream from no ctxs
continue
}
}
break
}
close(closeCh)
return closeCh
}
func (w *watcher) Close() (err error) {
w.mu.Lock()
streams := w.streams
w.streams = nil
w.mu.Unlock()
for _, wgs := range streams {
if werr := wgs.close(); werr != nil {
err = werr
}
}
// Consider context.Canceled as a successful close
if errors.Is(err, context.Canceled) {
err = nil
}
return err
}
// RequestProgress requests a progress notify response be sent in all watch channels.
func (w *watcher) RequestProgress(ctx context.Context) (err error) {
ctxKey := streamKeyFromCtx(ctx)
w.mu.Lock()
if w.streams == nil {
w.mu.Unlock()
return errors.New("no stream found for context")
}
wgs := w.streams[ctxKey]
if wgs == nil {
wgs = w.newWatcherGRPCStream(ctx)
w.streams[ctxKey] = wgs
}
donec := wgs.donec
reqc := wgs.reqc
w.mu.Unlock()
pr := &progressRequest{}
select {
case reqc <- pr:
return nil
case <-ctx.Done():
return ctx.Err()
case <-donec:
if wgs.closeErr != nil {
return wgs.closeErr
}
// retry; may have dropped stream from no ctxs
return w.RequestProgress(ctx)
}
}
func (w *watchGRPCStream) close() (err error) {
w.cancel()
<-w.donec
select {
case err = <-w.errc:
default:
}
return ContextError(w.ctx, err)
}
func (w *watcher) closeStream(wgs *watchGRPCStream) {
w.mu.Lock()
close(wgs.donec)
wgs.cancel()
if w.streams != nil {
delete(w.streams, wgs.ctxKey)
}
w.mu.Unlock()
}
func (w *watchGRPCStream) addSubstream(resp *pb.WatchResponse, ws *watcherStream) {
// check watch ID for backward compatibility (<= v3.3)
if resp.WatchId == InvalidWatchID || (resp.Canceled && resp.CancelReason != "") {
w.closeErr = v3rpc.Error(errors.New(resp.CancelReason))
// failed; no channel
close(ws.recvc)
return
}
ws.id = resp.WatchId
w.substreams[ws.id] = ws
}
func (w *watchGRPCStream) sendCloseSubstream(ws *watcherStream, resp *WatchResponse) {
select {
case ws.outc <- *resp:
case <-ws.initReq.ctx.Done():
case <-time.After(closeSendErrTimeout):
}
close(ws.outc)
}
func (w *watchGRPCStream) closeSubstream(ws *watcherStream) {
// send channel response in case stream was never established
select {
case ws.initReq.retc <- ws.outc:
default:
}
// close subscriber's channel
if closeErr := w.closeErr; closeErr != nil && ws.initReq.ctx.Err() == nil {
go w.sendCloseSubstream(ws, &WatchResponse{Canceled: true, closeErr: w.closeErr})
} else if ws.outc != nil {
close(ws.outc)
}
if ws.id != InvalidWatchID {
delete(w.substreams, ws.id)
return
}
for i := range w.resuming {
if w.resuming[i] == ws {
w.resuming[i] = nil
return
}
}
}
// run is the root of the goroutines for managing a watcher client
func (w *watchGRPCStream) run() {
var wc pb.Watch_WatchClient
var closeErr error
// substreams marked to close but goroutine still running; needed for
// avoiding double-closing recvc on grpc stream teardown
closing := make(map[*watcherStream]struct{})
defer func() {
w.closeErr = closeErr
// shutdown substreams and resuming substreams
for _, ws := range w.substreams {
if _, ok := closing[ws]; !ok {
close(ws.recvc)
closing[ws] = struct{}{}
}
}
for _, ws := range w.resuming {
if _, ok := closing[ws]; ws != nil && !ok {
close(ws.recvc)
closing[ws] = struct{}{}
}
}
w.joinSubstreams()
for range closing {
w.closeSubstream(<-w.closingc)
}
w.wg.Wait()
w.owner.closeStream(w)
}()
// start a stream with the etcd grpc server
if wc, closeErr = w.newWatchClient(); closeErr != nil {
return
}
cancelSet := make(map[int64]struct{})
var cur *pb.WatchResponse
backoff := time.Millisecond
for {
select {
// Watch() requested
case req := <-w.reqc:
switch wreq := req.(type) {
case *watchRequest:
outc := make(chan WatchResponse, 1)
// TODO: pass custom watch ID?
ws := &watcherStream{
initReq: *wreq,
id: InvalidWatchID,
outc: outc,
// unbuffered so resumes won't cause repeat events
recvc: make(chan *WatchResponse),
}
ws.donec = make(chan struct{})
w.wg.Add(1)
go w.serveSubstream(ws, w.resumec)
// queue up for watcher creation/resume
w.resuming = append(w.resuming, ws)
if len(w.resuming) == 1 {
// head of resume queue, can register a new watcher
if err := wc.Send(ws.initReq.toPB()); err != nil {
w.lg.Debug("error when sending request", zap.Error(err))
}
}
case *progressRequest:
if err := wc.Send(wreq.toPB()); err != nil {
w.lg.Debug("error when sending request", zap.Error(err))
}
}
// new events from the watch client
case pbresp := <-w.respc:
if cur == nil || pbresp.Created || pbresp.Canceled {
cur = pbresp
} else if cur.WatchId == pbresp.WatchId {
// merge new events
cur.Events = append(cur.Events, pbresp.Events...)
// update "Fragment" field; last response with "Fragment" == false
cur.Fragment = pbresp.Fragment
}
switch {
case pbresp.Created:
// response to head of queue creation
if len(w.resuming) != 0 {
if ws := w.resuming[0]; ws != nil {
w.addSubstream(pbresp, ws)
w.dispatchEvent(pbresp)
w.resuming[0] = nil
}
}
if ws := w.nextResume(); ws != nil {
if err := wc.Send(ws.initReq.toPB()); err != nil {
w.lg.Debug("error when sending request", zap.Error(err))
}
}
// reset for next iteration
cur = nil
case pbresp.Canceled && pbresp.CompactRevision == 0:
delete(cancelSet, pbresp.WatchId)
if ws, ok := w.substreams[pbresp.WatchId]; ok {
// signal to stream goroutine to update closingc
close(ws.recvc)
closing[ws] = struct{}{}
}
// reset for next iteration
cur = nil
case cur.Fragment:
// watch response events are still fragmented
// continue to fetch next fragmented event arrival
continue
default:
// dispatch to appropriate watch stream
ok := w.dispatchEvent(cur)
// reset for next iteration
cur = nil
if ok {
break
}
// watch response on unexpected watch id; cancel id
if _, ok := cancelSet[pbresp.WatchId]; ok {
break
}
cancelSet[pbresp.WatchId] = struct{}{}
cr := &pb.WatchRequest_CancelRequest{
CancelRequest: &pb.WatchCancelRequest{
WatchId: pbresp.WatchId,
},
}
req := &pb.WatchRequest{RequestUnion: cr}
w.lg.Debug("sending watch cancel request for failed dispatch", zap.Int64("watch-id", pbresp.WatchId))
if err := wc.Send(req); err != nil {
w.lg.Debug("failed to send watch cancel request", zap.Int64("watch-id", pbresp.WatchId), zap.Error(err))
}
}
// watch client failed on Recv; spawn another if possible
case err := <-w.errc:
if isHaltErr(w.ctx, err) || errors.Is(ContextError(w.ctx, err), v3rpc.ErrNoLeader) {
closeErr = err
return
}
backoff = w.backoffIfUnavailable(backoff, err)
if wc, closeErr = w.newWatchClient(); closeErr != nil {
return
}
if ws := w.nextResume(); ws != nil {
if err := wc.Send(ws.initReq.toPB()); err != nil {
w.lg.Debug("error when sending request", zap.Error(err))
}
}
cancelSet = make(map[int64]struct{})
case <-w.ctx.Done():
return
case ws := <-w.closingc:
w.closeSubstream(ws)
delete(closing, ws)
// no more watchers on this stream, shutdown, skip cancellation
if len(w.substreams)+len(w.resuming) == 0 {
return
}
if ws.id != InvalidWatchID {
// client is closing an established watch; close it on the server proactively instead of waiting
// to close when the next message arrives
cancelSet[ws.id] = struct{}{}
cr := &pb.WatchRequest_CancelRequest{
CancelRequest: &pb.WatchCancelRequest{
WatchId: ws.id,
},
}
req := &pb.WatchRequest{RequestUnion: cr}
w.lg.Debug("sending watch cancel request for closed watcher", zap.Int64("watch-id", ws.id))
if err := wc.Send(req); err != nil {
w.lg.Debug("failed to send watch cancel request", zap.Int64("watch-id", ws.id), zap.Error(err))
}
}
}
}
}
// nextResume chooses the next resuming to register with the grpc stream. Abandoned
// streams are marked as nil in the queue since the head must wait for its inflight registration.
func (w *watchGRPCStream) nextResume() *watcherStream {
for len(w.resuming) != 0 {
if w.resuming[0] != nil {
return w.resuming[0]
}
w.resuming = w.resuming[1:len(w.resuming)]
}
return nil
}
// dispatchEvent sends a WatchResponse to the appropriate watcher stream
func (w *watchGRPCStream) dispatchEvent(pbresp *pb.WatchResponse) bool {
events := make([]*Event, len(pbresp.Events))
for i, ev := range pbresp.Events {
events[i] = (*Event)(ev)
}
// TODO: return watch ID?
wr := &WatchResponse{
Header: *pbresp.Header,
Events: events,
CompactRevision: pbresp.CompactRevision,
Created: pbresp.Created,
Canceled: pbresp.Canceled,
CancelReason: pbresp.CancelReason,
}
// watch IDs are zero indexed, so request notify watch responses are assigned a watch ID of InvalidWatchID to
// indicate they should be broadcast.
if wr.IsProgressNotify() && pbresp.WatchId == InvalidWatchID {
return w.broadcastResponse(wr)
}
return w.unicastResponse(wr, pbresp.WatchId)
}
// broadcastResponse send a watch response to all watch substreams.
func (w *watchGRPCStream) broadcastResponse(wr *WatchResponse) bool {
for _, ws := range w.substreams {
select {
case ws.recvc <- wr:
case <-ws.donec:
}
}
return true
}
// unicastResponse sends a watch response to a specific watch substream.
func (w *watchGRPCStream) unicastResponse(wr *WatchResponse, watchID int64) bool {
ws, ok := w.substreams[watchID]
if !ok {
return false
}
select {
case ws.recvc <- wr:
case <-ws.donec:
return false
}
return true
}
// serveWatchClient forwards messages from the grpc stream to run()
func (w *watchGRPCStream) serveWatchClient(wc pb.Watch_WatchClient) {
for {
resp, err := wc.Recv()
if err != nil {
select {
case w.errc <- err:
case <-w.donec:
}
return
}
select {
case w.respc <- resp:
case <-w.donec:
return
}
}
}
// serveSubstream forwards watch responses from run() to the subscriber
func (w *watchGRPCStream) serveSubstream(ws *watcherStream, resumec chan struct{}) {
if ws.closing {
panic("created substream goroutine but substream is closing")
}
// nextRev is the minimum expected next revision
nextRev := ws.initReq.rev
resuming := false
defer func() {
if !resuming {
ws.closing = true
}
close(ws.donec)
if !resuming {
w.closingc <- ws
}
w.wg.Done()
}()
emptyWr := &WatchResponse{}
for {
curWr := emptyWr
outc := ws.outc
if len(ws.buf) > 0 {
curWr = ws.buf[0]
} else {
outc = nil
}
select {
case outc <- *curWr:
if ws.buf[0].Err() != nil {
return
}
ws.buf[0] = nil
ws.buf = ws.buf[1:]
case wr, ok := <-ws.recvc:
if !ok {
// shutdown from closeSubstream
return
}
if wr.Created {
if ws.initReq.retc != nil {
ws.initReq.retc <- ws.outc
// to prevent next write from taking the slot in buffered channel
// and posting duplicate create events
ws.initReq.retc = nil
// send first creation event only if requested
if ws.initReq.createdNotify {
ws.outc <- *wr
}
// once the watch channel is returned, a current revision
// watch must resume at the store revision. This is necessary
// for the following case to work as expected:
// wch := m1.Watch("a")
// m2.Put("a", "b")
// <-wch
// If the revision is only bound on the first observed event,
// if wch is disconnected before the Put is issued, then reconnects
// after it is committed, it'll miss the Put.
if ws.initReq.rev == 0 {
nextRev = wr.Header.Revision
}
}
} else {
// current progress of watch; <= store revision
nextRev = wr.Header.Revision + 1
}
if len(wr.Events) > 0 {
nextRev = wr.Events[len(wr.Events)-1].Kv.ModRevision + 1
}
ws.initReq.rev = nextRev
// created event is already sent above,
// watcher should not post duplicate events
if wr.Created {
continue
}
// TODO pause channel if buffer gets too large
ws.buf = append(ws.buf, wr)
case <-w.ctx.Done():
return
case <-ws.initReq.ctx.Done():
return
case <-resumec:
resuming = true
return
}
}
// lazily send cancel message if events on missing id
}
func (w *watchGRPCStream) newWatchClient() (pb.Watch_WatchClient, error) {
// mark all substreams as resuming
close(w.resumec)
w.resumec = make(chan struct{})
w.joinSubstreams()
for _, ws := range w.substreams {
ws.id = InvalidWatchID
w.resuming = append(w.resuming, ws)
}
// strip out nils, if any
var resuming []*watcherStream
for _, ws := range w.resuming {
if ws != nil {
resuming = append(resuming, ws)
}
}
w.resuming = resuming
w.substreams = make(map[int64]*watcherStream)
// connect to grpc stream while accepting watcher cancellation
stopc := make(chan struct{})
donec := w.waitCancelSubstreams(stopc)
wc, err := w.openWatchClient()
close(stopc)
<-donec
// serve all non-closing streams, even if there's a client error
// so that the teardown path can shutdown the streams as expected.
for _, ws := range w.resuming {
if ws.closing {
continue
}
ws.donec = make(chan struct{})
w.wg.Add(1)
go w.serveSubstream(ws, w.resumec)
}
if err != nil {
return nil, v3rpc.Error(err)
}
// receive data from new grpc stream
go w.serveWatchClient(wc)
return wc, nil
}
func (w *watchGRPCStream) waitCancelSubstreams(stopc <-chan struct{}) <-chan struct{} {
var wg sync.WaitGroup
wg.Add(len(w.resuming))
donec := make(chan struct{})
for i := range w.resuming {
go func(ws *watcherStream) {
defer wg.Done()
if ws.closing {
if ws.initReq.ctx.Err() != nil && ws.outc != nil {
close(ws.outc)
ws.outc = nil
}
return
}
select {
case <-ws.initReq.ctx.Done():
// closed ws will be removed from resuming
ws.closing = true
close(ws.outc)
ws.outc = nil
w.wg.Add(1)
go func() {
defer w.wg.Done()
w.closingc <- ws
}()
case <-stopc:
}
}(w.resuming[i])
}
go func() {
defer close(donec)
wg.Wait()
}()
return donec
}
// joinSubstreams waits for all substream goroutines to complete.
func (w *watchGRPCStream) joinSubstreams() {
for _, ws := range w.substreams {
<-ws.donec
}
for _, ws := range w.resuming {
if ws != nil {
<-ws.donec
}
}
}
var maxBackoff = 100 * time.Millisecond
func (w *watchGRPCStream) backoffIfUnavailable(backoff time.Duration, err error) time.Duration {
if isUnavailableErr(w.ctx, err) {
// retry, but backoff
if backoff < maxBackoff {
// 25% backoff factor
backoff = backoff + backoff/4
if backoff > maxBackoff {
backoff = maxBackoff
}
}
time.Sleep(backoff)
}
return backoff
}
// openWatchClient retries opening a watch client until success or halt.
// manually retry in case "ws==nil && err==nil"
// TODO: remove FailFast=false
func (w *watchGRPCStream) openWatchClient() (ws pb.Watch_WatchClient, err error) {
backoff := time.Millisecond
for {
select {
case <-w.ctx.Done():
if err == nil {
return nil, w.ctx.Err()
}
return nil, err
default:
}
if ws, err = w.remote.Watch(w.ctx, w.callOpts...); ws != nil && err == nil {
break
}
if isHaltErr(w.ctx, err) {
return nil, v3rpc.Error(err)
}
backoff = w.backoffIfUnavailable(backoff, err)
}
return ws, nil
}
// toPB converts an internal watch request structure to its protobuf WatchRequest structure.
func (wr *watchRequest) toPB() *pb.WatchRequest {
req := &pb.WatchCreateRequest{
StartRevision: wr.rev,
Key: []byte(wr.key),
RangeEnd: []byte(wr.end),
ProgressNotify: wr.progressNotify,
Filters: wr.filters,
PrevKv: wr.prevKV,
Fragment: wr.fragment,
}
cr := &pb.WatchRequest_CreateRequest{CreateRequest: req}
return &pb.WatchRequest{RequestUnion: cr}
}
// toPB converts an internal progress request structure to its protobuf WatchRequest structure.
func (pr *progressRequest) toPB() *pb.WatchRequest {
req := &pb.WatchProgressRequest{}
cr := &pb.WatchRequest_ProgressRequest{ProgressRequest: req}
return &pb.WatchRequest{RequestUnion: cr}
}
func streamKeyFromCtx(ctx context.Context) string {
if md, ok := metadata.FromOutgoingContext(ctx); ok {
return fmt.Sprintf("%+v", map[string][]string(md))
}
return ""
}
================================================
FILE: client/v3/watch_test.go
================================================
// Copyright 2016 The etcd Authors
//
// 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 clientv3
import (
"context"
"testing"
"google.golang.org/grpc/metadata"
"go.etcd.io/etcd/api/v3/mvccpb"
)
func TestEvent(t *testing.T) {
tests := []struct {
ev *Event
isCreate bool
isModify bool
}{{
ev: &Event{
Type: EventTypePut,
Kv: &mvccpb.KeyValue{
CreateRevision: 3,
ModRevision: 3,
},
},
isCreate: true,
}, {
ev: &Event{
Type: EventTypePut,
Kv: &mvccpb.KeyValue{
CreateRevision: 3,
ModRevision: 4,
},
},
isModify: true,
}}
for i, tt := range tests {
if tt.isCreate && !tt.ev.IsCreate() {
t.Errorf("#%d: event should be Create event", i)
}
if tt.isModify && !tt.ev.IsModify() {
t.Errorf("#%d: event should be Modify event", i)
}
}
}
// TestStreamKeyFromCtx tests the streamKeyFromCtx function to ensure it correctly
// formats metadata as a map[string][]string when extracting metadata from the context.
//
// The fmt package in Go guarantees that maps are printed in a consistent order,
// sorted by the keys. This test verifies that the streamKeyFromCtx function
// produces the expected formatted string representation of metadata maps when called with
// various context scenarios.
func TestStreamKeyFromCtx(t *testing.T) {
tests := []struct {
name string
ctx context.Context
expected string
}{
{
name: "multiple keys",
ctx: metadata.NewOutgoingContext(t.Context(), metadata.MD{
"key1": []string{"value1"},
"key2": []string{"value2a", "value2b"},
}),
expected: "map[key1:[value1] key2:[value2a value2b]]",
},
{
name: "no keys",
ctx: metadata.NewOutgoingContext(t.Context(), metadata.MD{}),
expected: "map[]",
},
{
name: "only one key",
ctx: metadata.NewOutgoingContext(t.Context(), metadata.MD{
"key1": []string{"value1", "value1a"},
}),
expected: "map[key1:[value1 value1a]]",
},
{
name: "no metadata",
ctx: t.Context(),
expected: "",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
actual := streamKeyFromCtx(tt.ctx)
if actual != tt.expected {
t.Errorf("streamKeyFromCtx() = %v, expected %v", actual, tt.expected)
}
})
}
}
================================================
FILE: client/v3/yaml/config.go
================================================
// Copyright 2017 The etcd Authors
//
// 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 yaml handles yaml-formatted clientv3 configuration data.
package yaml
import (
"crypto/tls"
"crypto/x509"
"os"
"sigs.k8s.io/yaml"
"go.etcd.io/etcd/client/pkg/v3/tlsutil"
clientv3 "go.etcd.io/etcd/client/v3"
)
type yamlConfig struct {
clientv3.Config
InsecureTransport bool `json:"insecure-transport"`
InsecureSkipTLSVerify bool `json:"insecure-skip-tls-verify"`
Certfile string `json:"cert-file"`
Keyfile string `json:"key-file"`
TrustedCAfile string `json:"trusted-ca-file"`
// CAfile is being deprecated. Use 'TrustedCAfile' instead.
// TODO: deprecate this in v4
CAfile string `json:"ca-file"`
}
// NewConfig creates a new clientv3.Config from a yaml file.
func NewConfig(fpath string) (*clientv3.Config, error) {
b, err := os.ReadFile(fpath)
if err != nil {
return nil, err
}
yc := &yamlConfig{}
err = yaml.Unmarshal(b, yc)
if err != nil {
return nil, err
}
if yc.InsecureTransport {
return &yc.Config, nil
}
var (
cert *tls.Certificate
cp *x509.CertPool
)
if yc.Certfile != "" && yc.Keyfile != "" {
cert, err = tlsutil.NewCert(yc.Certfile, yc.Keyfile, nil)
if err != nil {
return nil, err
}
}
if yc.TrustedCAfile != "" {
cp, err = tlsutil.NewCertPool([]string{yc.TrustedCAfile})
if err != nil {
return nil, err
}
}
tlscfg := &tls.Config{
MinVersion: tls.VersionTLS12,
InsecureSkipVerify: yc.InsecureSkipTLSVerify,
RootCAs: cp,
}
if cert != nil {
tlscfg.Certificates = []tls.Certificate{*cert}
}
yc.Config.TLS = tlscfg
return &yc.Config, nil
}
================================================
FILE: client/v3/yaml/config_test.go
================================================
// Copyright 2016 The etcd Authors
//
// 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 yaml
import (
"log"
"os"
"reflect"
"testing"
"github.com/stretchr/testify/require"
"sigs.k8s.io/yaml"
)
var (
certPath = "../../../tests/fixtures/server.crt"
privateKeyPath = "../../../tests/fixtures/server.key.insecure"
caPath = "../../../tests/fixtures/ca.crt"
)
func TestConfigFromFile(t *testing.T) {
tests := []struct {
ym *yamlConfig
werr bool
}{
{
&yamlConfig{},
false,
},
{
&yamlConfig{
InsecureTransport: true,
},
false,
},
{
&yamlConfig{
Keyfile: privateKeyPath,
Certfile: certPath,
TrustedCAfile: caPath,
InsecureSkipTLSVerify: true,
},
false,
},
{
&yamlConfig{
Keyfile: "bad",
Certfile: "bad",
},
true,
},
{
&yamlConfig{
Keyfile: privateKeyPath,
Certfile: certPath,
TrustedCAfile: "bad",
},
true,
},
}
for i, tt := range tests {
tmpfile, err := os.CreateTemp(t.TempDir(), "clientcfg")
if err != nil {
log.Fatal(err)
}
b, err := yaml.Marshal(tt.ym)
require.NoError(t, err)
_, err = tmpfile.Write(b)
require.NoError(t, err)
require.NoError(t, tmpfile.Close())
cfg, cerr := NewConfig(tmpfile.Name())
if cerr != nil && !tt.werr {
t.Errorf("#%d: err = %v, want %v", i, cerr, tt.werr)
continue
}
if cerr != nil {
os.Remove(tmpfile.Name())
continue
}
if !reflect.DeepEqual(cfg.Endpoints, tt.ym.Endpoints) {
t.Errorf("#%d: endpoint = %v, want %v", i, cfg.Endpoints, tt.ym.Endpoints)
}
if tt.ym.InsecureTransport != (cfg.TLS == nil) {
t.Errorf("#%d: insecureTransport = %v, want %v", i, cfg.TLS == nil, tt.ym.InsecureTransport)
}
if !tt.ym.InsecureTransport {
if tt.ym.Certfile != "" && len(cfg.TLS.Certificates) == 0 {
t.Errorf("#%d: failed to load in cert", i)
}
if tt.ym.TrustedCAfile != "" && cfg.TLS.RootCAs == nil {
t.Errorf("#%d: failed to load in ca cert", i)
}
if cfg.TLS.InsecureSkipVerify != tt.ym.InsecureSkipTLSVerify {
t.Errorf("#%d: skipTLSVeify = %v, want %v", i, cfg.TLS.InsecureSkipVerify, tt.ym.InsecureSkipTLSVerify)
}
}
os.Remove(tmpfile.Name())
}
}
================================================
FILE: code-of-conduct.md
================================================
## etcd Community Code of Conduct
etcd follows the [CNCF Code of Conduct](https://github.com/cncf/foundation/blob/master/code-of-conduct.md).
================================================
FILE: codecov.yml
================================================
---
# https://docs.codecov.com/docs/codecovyml-reference
codecov:
token: 6040de41-c073-4d6f-bbf8-d89256ef31e1
disable_default_path_fixes: true
require_ci_to_pass: false
notify:
wait_for_ci: false
fixes:
- go.etcd.io/etcd/api/v3/::api/
- go.etcd.io/etcd/client/v3/::client/v3/
- go.etcd.io/etcd/etcdctl/v3/::etcdctl/
- go.etcd.io/etcd/etcdutl/v3/::etcdutl/
- go.etcd.io/etcd/pkg/v3/::pkg/
- go.etcd.io/etcd/server/v3/::server/
ignore:
- '**/*.pb.go'
- '**/*.pb.gw.go'
- tests/**/*
- go.etcd.io/etcd/tests/**/*
coverage:
range: 60..80
round: down
precision: 2
status:
project:
default:
target: auto
# allow some coverage reductions within a threshold
# this allows a 1% drop from the previous base commit coverage
threshold: 1%
patch:
default:
target: auto
threshold: 80%
comment:
layout: "header, files, diff, footer"
behavior: default # default: update, if exists. Otherwise post new; new: delete old and post new
require_changes: false # if true: only post the comment if coverage changes
require_base: false # [true :: must have a base report to post]
require_head: true # [true :: must have a head report to post]
hide_project_coverage: false # [true :: only show coverage on the git diff]
================================================
FILE: contrib/OWNERS
================================================
# See the OWNERS docs at https://go.k8s.io/owners
labels:
- area/contrib
================================================
FILE: contrib/README.md
================================================
## Contrib
Scripts and files which may be useful but aren't part of the core etcd project.
* [lock](lock) - example addressing the expired lease problem of distributed locking with etcd
* [mixin](mixin) - customisable set of Grafana dashboard and Prometheus alerts for etcd
* [raftexample](raftexample) - an example distributed key-value store using raft
* [systemd](systemd) - an example unit file for deploying etcd on systemd-based distributions
* [systemd/etcd3-multinode](systemd/etcd3-multinode) - multi-node cluster setup with systemd
================================================
FILE: contrib/lock/README.md
================================================
# What is this?
This directory provides an executable example of the scenarios described in [the article by Martin Kleppmann][fencing].
Generally speaking, a lease-based lock service cannot provide mutual exclusion to processes. This is because such a lease mechanism depends on the physical clock of both the lock service and client processes. Many factors (e.g. stop-the-world GC pause of a language runtime) can cause false expiration of a granted lease as depicted in the below figure: ![unsafe lock][unsafe-lock]
As discussed in [notes on the usage of lock and lease][why], such a problem can be solved with a technique called version number validation or fencing tokens. With this technique a shared resource (storage in the figures) needs to validate requests from clients based on their tokens like this: ![fencing tokens][fencing-tokens]
This directory contains two programs: `client` and `storage`. With `etcd`, you can reproduce the expired lease problem of distributed locking and a simple example solution of the validation technique which can avoid incorrect access from a client with an expired lease.
`storage` works as a very simple key value in-memory store which is accessible through HTTP and a custom JSON protocol. `client` works as client processes which tries to write a key/value to `storage` with coordination of etcd locking.
## How to build
For building `client` and `storage`, just execute `go build` in each directory.
## How to try
At first, you need to start an etcd cluster, which works as lock service in the figures. On top of the etcd source directory, execute commands like below:
```
$ make # build etcd
$ bin/etcd # start etcd
```
Then run `storage` command in `storage` directory:
```
$ ./storage
```
Now client processes ("Client 1" and "Client 2" in the figures) can be started. At first, execute below command for starting a client process which corresponds to "Client 1":
```
$ ./client 1
```
It will show an output like this:
```
client 1 starts
created etcd client and session
acquired lock, version: 694d82254d5fa305
please manually revoke the lease using 'etcdctl lease revoke 694d82254d5fa305' or wait for it to expire, then start executing client 2 and hit any key...
```
Verify the lease was created using:
```
$ bin/etcdctl lease list
found 1 leases
694d82254d5fa305
```
Then proceed to manually revoke the lease using:
```
$ bin/etcdctl lease revoke 694d82254d5fa305
lease 694d82254d5fa305 revoked
```
Now another client process can be started like this:
```
$ ./client 2
client 2 starts
created etcd client and session
acquired lock, version: 694d82254e18770a
this is client 2, continuing
```
If things go well the second client process invoked as `./client 2` finishes soon. It successfully writes a key to `storage` process.
After checking this, please hit any key for `./client 1` and resume the process. It will show an output like below:
```
resuming client 1
expected fail to write to storage with old lease version: error: given version (694d82254d5fa305) is different from the existing version (694d82254e18770a)
```
[fencing]: https://martin.kleppmann.com/2016/02/08/how-to-do-distributed-locking.html
[fencing-tokens]: https://martin.kleppmann.com/2016/02/fencing-tokens.png
[unsafe-lock]: https://martin.kleppmann.com/2016/02/unsafe-lock.png
[why]: https://etcd.io/docs/next/learning/why/#notes-on-the-usage-of-lock-and-lease
================================================
FILE: contrib/mixin/.gitignore
================================================
vendor
================================================
FILE: contrib/mixin/.lint
================================================
---
exclusions:
template-instance-rule:
reason: The mixin only uses `instance` for alerts, and `cluster` for dashboard queries
template-job-rule:
reason: The dashboards use 'cluster' label as selector, rather than 'job'
target-job-rule:
reason: The mixin uses 'cluster' instead of 'job'
target-instance-rule:
reason: The mixin only uses `instance` for alerts, and `cluster` for dashboard queries
alert-name-camelcase:
reason: etcd is spelled all lowercase, meaning all alert name start with a lowercase
alert-summary-style:
reason: etcd is spelled all lowercase, meaning summaries starting with 'etcd' are still valid
panel-units-rule:
reason: Stat panels have no unit, and some panels use custom unit or text
panel-title-description-rule:
reason: Suppress noisy linting rule until we can address minor tech debt like this
================================================
FILE: contrib/mixin/Makefile
================================================
.PHONY: tools manifests test clean jb_install
OS := linux
ARCH ?= amd64
PROMETHEUS_VERSION := 2.33.1
tools:
go install github.com/google/go-jsonnet/cmd/jsonnet@latest
go install github.com/brancz/gojsontoyaml@latest
go install github.com/jsonnet-bundler/jsonnet-bundler/cmd/jb@latest
wget -qO- "https://github.com/prometheus/prometheus/releases/download/v${PROMETHEUS_VERSION}/prometheus-${PROMETHEUS_VERSION}.${OS}-${ARCH}.tar.gz" |\
tar xvz --strip-components=1 -C "$$(go env GOPATH)/bin" prometheus-${PROMETHEUS_VERSION}.${OS}-${ARCH}/promtool
manifests: manifests/etcd-prometheusRules.yaml
manifests/etcd-prometheusRules.yaml:
mkdir -p manifests
jsonnet -e '(import "mixin.libsonnet").prometheusAlerts' | gojsontoyaml > manifests/etcd-prometheusRules.yaml
test: manifests/etcd-prometheusRules.yaml
promtool test rules test.yaml
jb_install:
jb install
clean:
rm -rf manifests/*.yaml
================================================
FILE: contrib/mixin/OWNERS
================================================
# See the OWNERS docs at https://go.k8s.io/owners
labels:
- area/observability
================================================
FILE: contrib/mixin/README.md
================================================
# Prometheus Monitoring Mixin for etcd
> NOTE: This project is *alpha* stage. Flags, configuration, behaviour and design may change significantly in following releases.
A customisable set of Grafana dashboard and Prometheus alerts for etcd.
Instructions for use are the same as the [kubernetes-mixin](https://github.com/kubernetes-monitoring/kubernetes-mixin).
## Grafana 7.x support
By default, this mixin generates the dashboard compatible with Grafana 8.x or newer.
To generate dashboard for Grafana 7.x, set in the config.libsonnet:
```
// set to true if dashboards should be compatible with Grafana 7x or earlier
grafana7x: true,
```
## Background
* For more information about monitoring mixins, see this [design doc](https://docs.google.com/document/d/1A9xvzwqnFVSOZ5fD3blKODXfsat5fg6ZhnKu9LK3lB4/edit#).
## Testing alerts
Make sure to have [jsonnet](https://jsonnet.org/) and [gojsontoyaml](https://github.com/brancz/gojsontoyaml) installed. You can fetch it via
```
make tools
```
First compile the mixin to a YAML file, which the promtool will read:
```
make manifests
```
Then run the unit test:
```
promtool test rules test.yaml
```
================================================
FILE: contrib/mixin/alerts/alerts.libsonnet
================================================
{
prometheusAlerts+:: {
groups+: [
{
name: 'etcd',
rules: [
{
alert: 'etcdMembersDown',
expr: |||
max without (endpoint) (
sum without (%(etcd_instance_labels)s) (up{%(etcd_selector)s} == bool 0)
or
count without (To) (
sum without (%(etcd_instance_labels)s) (rate(etcd_network_peer_sent_failures_total{%(etcd_selector)s}[%(network_failure_range)ss])) > 0.01
)
)
> 0
||| % { etcd_instance_labels: $._config.etcd_instance_labels, etcd_selector: $._config.etcd_selector, network_failure_range: $._config.scrape_interval_seconds * 4 },
'for': '20m',
labels: {
severity: 'warning',
},
annotations: {
description: 'etcd cluster "{{ $labels.%s }}": members are down ({{ $value }}).' % $._config.clusterLabel,
summary: 'etcd cluster members are down.',
},
},
{
alert: 'etcdInsufficientMembers',
expr: |||
sum(up{%(etcd_selector)s} == bool 1) without (%(etcd_instance_labels)s) < ((count(up{%(etcd_selector)s}) without (%(etcd_instance_labels)s) + 1) / 2)
||| % $._config,
'for': '3m',
labels: {
severity: 'critical',
},
annotations: {
description: 'etcd cluster "{{ $labels.%s }}": insufficient members ({{ $value }}).' % $._config.clusterLabel,
summary: 'etcd cluster has insufficient number of members.',
},
},
{
alert: 'etcdNoLeader',
expr: |||
etcd_server_has_leader{%(etcd_selector)s} == 0
||| % $._config,
'for': '1m',
labels: {
severity: 'critical',
},
annotations: {
description: 'etcd cluster "{{ $labels.%s }}": member {{ $labels.instance }} has no leader.' % $._config.clusterLabel,
summary: 'etcd cluster has no leader.',
},
},
{
alert: 'etcdHighNumberOfLeaderChanges',
expr: |||
increase((max without (%(etcd_instance_labels)s) (etcd_server_leader_changes_seen_total{%(etcd_selector)s}) or 0*absent(etcd_server_leader_changes_seen_total{%(etcd_selector)s}))[15m:1m]) >= 4
||| % $._config,
'for': '5m',
labels: {
severity: 'warning',
},
annotations: {
description: 'etcd cluster "{{ $labels.%s }}": {{ $value }} leader changes within the last 15 minutes. Frequent elections may be a sign of insufficient resources, high network latency, or disruptions by other components and should be investigated.' % $._config.clusterLabel,
summary: 'etcd cluster has high number of leader changes.',
},
},
{
alert: 'etcdHighNumberOfFailedGRPCRequests',
expr: |||
100 * sum(rate(grpc_server_handled_total{%(etcd_selector)s, grpc_code=~"Unknown|FailedPrecondition|ResourceExhausted|Internal|Unavailable|DataLoss|DeadlineExceeded"}[5m])) without (grpc_type, grpc_code)
/
sum(rate(grpc_server_handled_total{%(etcd_selector)s}[5m])) without (grpc_type, grpc_code)
> 1
||| % $._config,
'for': '10m',
labels: {
severity: 'warning',
},
annotations: {
description: 'etcd cluster "{{ $labels.%s }}": {{ $value }}%% of requests for {{ $labels.grpc_method }} failed on etcd instance {{ $labels.instance }}.' % $._config.clusterLabel,
summary: 'etcd cluster has high number of failed grpc requests.',
},
},
{
alert: 'etcdHighNumberOfFailedGRPCRequests',
expr: |||
100 * sum(rate(grpc_server_handled_total{%(etcd_selector)s, grpc_code=~"Unknown|FailedPrecondition|ResourceExhausted|Internal|Unavailable|DataLoss|DeadlineExceeded"}[5m])) without (grpc_type, grpc_code)
/
sum(rate(grpc_server_handled_total{%(etcd_selector)s}[5m])) without (grpc_type, grpc_code)
> 5
||| % $._config,
'for': '5m',
labels: {
severity: 'critical',
},
annotations: {
description: 'etcd cluster "{{ $labels.%s }}": {{ $value }}%% of requests for {{ $labels.grpc_method }} failed on etcd instance {{ $labels.instance }}.' % $._config.clusterLabel,
summary: 'etcd cluster has high number of failed grpc requests.',
},
},
{
alert: 'etcdGRPCRequestsSlow',
expr: |||
histogram_quantile(0.99, sum(rate(grpc_server_handling_seconds_bucket{%(etcd_selector)s, grpc_method!="Defragment", grpc_type="unary"}[5m])) without(grpc_type))
> 0.15
||| % $._config,
'for': '10m',
labels: {
severity: 'critical',
},
annotations: {
description: 'etcd cluster "{{ $labels.%s }}": 99th percentile of gRPC requests is {{ $value }}s on etcd instance {{ $labels.instance }} for {{ $labels.grpc_method }} method.' % $._config.clusterLabel,
summary: 'etcd grpc requests are slow',
},
},
{
alert: 'etcdMemberCommunicationSlow',
expr: |||
histogram_quantile(0.99, rate(etcd_network_peer_round_trip_time_seconds_bucket{%(etcd_selector)s}[5m]))
> 0.15
||| % $._config,
'for': '10m',
labels: {
severity: 'warning',
},
annotations: {
description: 'etcd cluster "{{ $labels.%s }}": member communication with {{ $labels.To }} is taking {{ $value }}s on etcd instance {{ $labels.instance }}.' % $._config.clusterLabel,
summary: 'etcd cluster member communication is slow.',
},
},
{
alert: 'etcdHighNumberOfFailedProposals',
expr: |||
rate(etcd_server_proposals_failed_total{%(etcd_selector)s}[15m]) > 5
||| % $._config,
'for': '15m',
labels: {
severity: 'warning',
},
annotations: {
description: 'etcd cluster "{{ $labels.%s }}": {{ $value }} proposal failures within the last 30 minutes on etcd instance {{ $labels.instance }}.' % $._config.clusterLabel,
summary: 'etcd cluster has high number of proposal failures.',
},
},
{
alert: 'etcdHighFsyncDurations',
expr: |||
histogram_quantile(0.99, rate(etcd_disk_wal_fsync_duration_seconds_bucket{%(etcd_selector)s}[5m]))
> 0.5
||| % $._config,
'for': '10m',
labels: {
severity: 'warning',
},
annotations: {
description: 'etcd cluster "{{ $labels.%s }}": 99th percentile fsync durations are {{ $value }}s on etcd instance {{ $labels.instance }}.' % $._config.clusterLabel,
summary: 'etcd cluster 99th percentile fsync durations are too high.',
},
},
{
alert: 'etcdHighFsyncDurations',
expr: |||
histogram_quantile(0.99, rate(etcd_disk_wal_fsync_duration_seconds_bucket{%(etcd_selector)s}[5m]))
> 1
||| % $._config,
'for': '10m',
labels: {
severity: 'critical',
},
annotations: {
description: 'etcd cluster "{{ $labels.%s }}": 99th percentile fsync durations are {{ $value }}s on etcd instance {{ $labels.instance }}.' % $._config.clusterLabel,
summary: 'etcd cluster 99th percentile fsync durations are too high.',
},
},
{
alert: 'etcdHighCommitDurations',
expr: |||
histogram_quantile(0.99, rate(etcd_disk_backend_commit_duration_seconds_bucket{%(etcd_selector)s}[5m]))
> 0.25
||| % $._config,
'for': '10m',
labels: {
severity: 'warning',
},
annotations: {
description: 'etcd cluster "{{ $labels.%s }}": 99th percentile commit durations {{ $value }}s on etcd instance {{ $labels.instance }}.' % $._config.clusterLabel,
summary: 'etcd cluster 99th percentile commit durations are too high.',
},
},
{
alert: 'etcdDatabaseQuotaLowSpace',
expr: |||
(last_over_time(etcd_mvcc_db_total_size_in_bytes{%(etcd_selector)s}[5m]) / last_over_time(etcd_server_quota_backend_bytes{%(etcd_selector)s}[5m]))*100 > 95
||| % $._config,
'for': '10m',
labels: {
severity: 'critical',
},
annotations: {
description: 'etcd cluster "{{ $labels.%s }}": database size exceeds the defined quota on etcd instance {{ $labels.instance }}, please defrag or increase the quota as the writes to etcd will be disabled when it is full.' % $._config.clusterLabel,
summary: 'etcd cluster database is running full.',
},
},
{
alert: 'etcdExcessiveDatabaseGrowth',
expr: |||
predict_linear(etcd_mvcc_db_total_size_in_bytes{%(etcd_selector)s}[4h], 4*60*60) > etcd_server_quota_backend_bytes{%(etcd_selector)s}
||| % $._config,
'for': '10m',
labels: {
severity: 'warning',
},
annotations: {
description: 'etcd cluster "{{ $labels.%s }}": Predicting running out of disk space in the next four hours, based on write observations within the past four hours on etcd instance {{ $labels.instance }}, please check as it might be disruptive.' % $._config.clusterLabel,
summary: 'etcd cluster database growing very fast.',
},
},
{
alert: 'etcdDatabaseHighFragmentationRatio',
expr: |||
(last_over_time(etcd_mvcc_db_total_size_in_use_in_bytes{%(etcd_selector)s}[5m]) / last_over_time(etcd_mvcc_db_total_size_in_bytes{%(etcd_selector)s}[5m])) < 0.5 and etcd_mvcc_db_total_size_in_use_in_bytes{%(etcd_selector)s} > 104857600
||| % $._config,
'for': '10m',
labels: {
severity: 'warning',
},
annotations: {
description: 'etcd cluster "{{ $labels.%s }}": database size in use on instance {{ $labels.instance }} is {{ $value | humanizePercentage }} of the actual allocated disk space, please run defragmentation (e.g. etcdctl defrag) to retrieve the unused fragmented disk space.' % $._config.clusterLabel,
summary: 'etcd database size in use is less than 50% of the actual allocated storage.',
runbook_url: 'https://etcd.io/docs/v3.5/op-guide/maintenance/#defragmentation',
},
},
],
},
],
},
}
================================================
FILE: contrib/mixin/config.libsonnet
================================================
{
_config+:: {
// set to true if dashboards should be compatible with Grafana 7x or earlier
grafana7x: false,
etcd_selector: 'job=~".*etcd.*"',
// etcd_instance_labels are the label names that are uniquely
// identifying an instance and need to be aggreated away for alerts
// that are about an etcd cluster as a whole. For example, if etcd
// instances are deployed on K8s, you will likely want to change
// this to 'instance, pod'.
etcd_instance_labels: 'instance',
// scrape_interval_seconds is the global scrape interval which can be
// used to dynamically adjust rate windows as a function of the interval.
scrape_interval_seconds: 30,
// Dashboard variable refresh option on Grafana (https://grafana.com/docs/grafana/latest/datasources/prometheus/).
// 0 : Never (Will never refresh the Dashboard variables values)
// 1 : On Dashboard Load (Will refresh Dashboards variables when dashboard are loaded)
// 2 : On Time Range Change (Will refresh Dashboards variables when time range will be changed)
dashboard_var_refresh: 2,
// clusterLabel is used to identify a cluster.
clusterLabel: 'job',
},
}
================================================
FILE: contrib/mixin/dashboards/dashboards.libsonnet
================================================
(import "etcd.libsonnet") +
(import "etcd-grafana7x.libsonnet")
================================================
FILE: contrib/mixin/dashboards/etcd-grafana7x.libsonnet
================================================
{
grafanaDashboards+:: if $._config.grafana7x then {
'etcd.json': {
uid: std.md5('etcd.json'),
title: 'etcd',
description: 'etcd sample Grafana dashboard with Prometheus',
tags: ['etcd-mixin'],
style: 'dark',
timezone: 'browser',
editable: true,
hideControls: false,
sharedCrosshair: false,
rows: [
{
collapse: false,
editable: true,
height: '250px',
panels: [
{
cacheTimeout: null,
colorBackground: false,
colorValue: false,
colors: [
'rgba(245, 54, 54, 0.9)',
'rgba(237, 129, 40, 0.89)',
'rgba(50, 172, 45, 0.97)',
],
datasource: '$datasource',
editable: true,
'error': false,
format: 'none',
gauge: {
maxValue: 100,
minValue: 0,
show: false,
thresholdLabels: false,
thresholdMarkers: true,
},
id: 28,
interval: null,
isNew: true,
links: [],
mappingType: 1,
mappingTypes: [
{
name: 'value to text',
value: 1,
},
{
name: 'range to text',
value: 2,
},
],
maxDataPoints: 100,
nullPointMode: 'connected',
nullText: null,
postfix: '',
postfixFontSize: '50%',
prefix: '',
prefixFontSize: '50%',
rangeMaps: [{
from: 'null',
text: 'N/A',
to: 'null',
}],
span: 3,
sparkline: {
fillColor: 'rgba(31, 118, 189, 0.18)',
full: false,
lineColor: 'rgb(31, 120, 193)',
show: false,
},
targets: [{
expr: 'sum(etcd_server_has_leader{%s, %s="$cluster"})' % [$._config.etcd_selector, $._config.clusterLabel],
intervalFactor: 2,
legendFormat: '',
metric: 'etcd_server_has_leader',
refId: 'A',
step: 20,
}],
thresholds: '',
title: 'Up',
type: 'singlestat',
valueFontSize: '200%',
valueMaps: [{
op: '=',
text: 'N/A',
value: 'null',
}],
valueName: 'avg',
},
{
aliasColors: {},
bars: false,
datasource: '$datasource',
editable: true,
'error': false,
fill: 0,
id: 23,
isNew: true,
legend: {
avg: false,
current: false,
max: false,
min: false,
show: false,
total: false,
values: false,
},
lines: true,
linewidth: 2,
links: [],
nullPointMode: 'connected',
percentage: false,
pointradius: 5,
points: false,
renderer: 'flot',
seriesOverrides: [],
span: 5,
stack: false,
steppedLine: false,
targets: [
{
expr: 'sum(rate(grpc_server_started_total{%s, %s="$cluster",grpc_type="unary"}[$__rate_interval]))' % [$._config.etcd_selector, $._config.clusterLabel],
format: 'time_series',
intervalFactor: 2,
legendFormat: 'RPC Rate',
metric: 'grpc_server_started_total',
refId: 'A',
step: 2,
},
{
expr: 'sum(rate(grpc_server_handled_total{%s, %s="$cluster",grpc_type="unary",grpc_code=~"Unknown|FailedPrecondition|ResourceExhausted|Internal|Unavailable|DataLoss|DeadlineExceeded"}[$__rate_interval]))' % [$._config.etcd_selector, $._config.clusterLabel],
format: 'time_series',
intervalFactor: 2,
legendFormat: 'RPC Failed Rate',
metric: 'grpc_server_handled_total',
refId: 'B',
step: 2,
},
],
thresholds: [],
timeFrom: null,
timeShift: null,
title: 'RPC Rate',
tooltip: {
msResolution: false,
shared: true,
sort: 0,
value_type: 'individual',
},
type: 'graph',
xaxis: {
mode: 'time',
name: null,
show: true,
values: [],
},
yaxes: [
{
format: 'ops',
label: null,
logBase: 1,
max: null,
min: null,
show: true,
},
{
format: 'short',
label: null,
logBase: 1,
max: null,
min: null,
show: true,
},
],
},
{
aliasColors: {},
bars: false,
datasource: '$datasource',
editable: true,
'error': false,
fill: 0,
id: 41,
isNew: true,
legend: {
avg: false,
current: false,
max: false,
min: false,
show: false,
total: false,
values: false,
},
lines: true,
linewidth: 2,
links: [],
nullPointMode: 'connected',
percentage: false,
pointradius: 5,
points: false,
renderer: 'flot',
seriesOverrides: [],
span: 4,
stack: true,
steppedLine: false,
targets: [
{
expr: 'sum(grpc_server_started_total{%(etcd_selector)s,%(clusterLabel)s="$cluster",grpc_service="etcdserverpb.Watch",grpc_type="bidi_stream"}) - sum(grpc_server_handled_total{%(clusterLabel)s="$cluster",grpc_service="etcdserverpb.Watch",grpc_type="bidi_stream"})' % $._config,
intervalFactor: 2,
legendFormat: 'Watch Streams',
metric: 'grpc_server_handled_total',
refId: 'A',
step: 4,
},
{
expr: 'sum(grpc_server_started_total{%(etcd_selector)s,%(clusterLabel)s="$cluster",grpc_service="etcdserverpb.Lease",grpc_type="bidi_stream"}) - sum(grpc_server_handled_total{%(clusterLabel)s="$cluster",grpc_service="etcdserverpb.Lease",grpc_type="bidi_stream"})' % $._config,
intervalFactor: 2,
legendFormat: 'Lease Streams',
metric: 'grpc_server_handled_total',
refId: 'B',
step: 4,
},
],
thresholds: [],
timeFrom: null,
timeShift: null,
title: 'Active Streams',
tooltip: {
msResolution: false,
shared: true,
sort: 0,
value_type: 'individual',
},
type: 'graph',
xaxis: {
mode: 'time',
name: null,
show: true,
values: [],
},
yaxes: [
{
format: 'short',
label: '',
logBase: 1,
max: null,
min: null,
show: true,
},
{
format: 'short',
label: null,
logBase: 1,
max: null,
min: null,
show: true,
},
],
},
],
showTitle: false,
title: 'Row',
},
{
collapse: false,
editable: true,
height: '250px',
panels: [
{
aliasColors: {},
bars: false,
datasource: '$datasource',
decimals: null,
editable: true,
'error': false,
fill: 0,
grid: {},
id: 1,
legend: {
avg: false,
current: false,
max: false,
min: false,
show: false,
total: false,
values: false,
},
lines: true,
linewidth: 2,
links: [],
nullPointMode: 'connected',
percentage: false,
pointradius: 5,
points: false,
renderer: 'flot',
seriesOverrides: [],
span: 4,
stack: false,
steppedLine: false,
targets: [{
expr: 'etcd_mvcc_db_total_size_in_bytes{%s, %s="$cluster"}' % [$._config.etcd_selector, $._config.clusterLabel],
hide: false,
interval: '',
intervalFactor: 2,
legendFormat: '{{instance}} DB Size',
metric: '',
refId: 'A',
step: 4,
}],
thresholds: [],
timeFrom: null,
timeShift: null,
title: 'DB Size',
tooltip: {
msResolution: false,
shared: true,
sort: 0,
value_type: 'cumulative',
},
type: 'graph',
xaxis: {
mode: 'time',
name: null,
show: true,
values: [],
},
yaxes: [
{
format: 'bytes',
logBase: 1,
max: null,
min: null,
show: true,
},
{
format: 'short',
logBase: 1,
max: null,
min: null,
show: false,
},
],
},
{
aliasColors: {},
bars: false,
datasource: '$datasource',
editable: true,
'error': false,
fill: 0,
grid: {},
id: 3,
legend: {
avg: false,
current: false,
max: false,
min: false,
show: false,
total: false,
values: false,
},
lines: true,
linewidth: 2,
links: [],
nullPointMode: 'connected',
percentage: false,
pointradius: 1,
points: false,
renderer: 'flot',
seriesOverrides: [],
span: 4,
stack: false,
steppedLine: true,
targets: [
{
expr: 'histogram_quantile(0.99, sum(rate(etcd_disk_wal_fsync_duration_seconds_bucket{%s, %s="$cluster"}[$__rate_interval])) by (instance, le))' % [$._config.etcd_selector, $._config.clusterLabel],
hide: false,
intervalFactor: 2,
legendFormat: '{{instance}} WAL fsync',
metric: 'etcd_disk_wal_fsync_duration_seconds_bucket',
refId: 'A',
step: 4,
},
{
expr: 'histogram_quantile(0.99, sum(rate(etcd_disk_backend_commit_duration_seconds_bucket{%s, %s="$cluster"}[$__rate_interval])) by (instance, le))' % [$._config.etcd_selector, $._config.clusterLabel],
intervalFactor: 2,
legendFormat: '{{instance}} DB fsync',
metric: 'etcd_disk_backend_commit_duration_seconds_bucket',
refId: 'B',
step: 4,
},
],
thresholds: [],
timeFrom: null,
timeShift: null,
title: 'Disk Sync Duration',
tooltip: {
msResolution: false,
shared: true,
sort: 0,
value_type: 'cumulative',
},
type: 'graph',
xaxis: {
mode: 'time',
name: null,
show: true,
values: [],
},
yaxes: [
{
format: 's',
logBase: 1,
max: null,
min: null,
show: true,
},
{
format: 'short',
logBase: 1,
max: null,
min: null,
show: false,
},
],
},
{
aliasColors: {},
bars: false,
datasource: '$datasource',
editable: true,
'error': false,
fill: 0,
id: 29,
isNew: true,
legend: {
avg: false,
current: false,
max: false,
min: false,
show: false,
total: false,
values: false,
},
lines: true,
linewidth: 2,
links: [],
nullPointMode: 'connected',
percentage: false,
pointradius: 5,
points: false,
renderer: 'flot',
seriesOverrides: [],
span: 4,
stack: false,
steppedLine: false,
targets: [{
expr: 'process_resident_memory_bytes{%s, %s="$cluster"}' % [$._config.etcd_selector, $._config.clusterLabel],
intervalFactor: 2,
legendFormat: '{{instance}} Resident Memory',
metric: 'process_resident_memory_bytes',
refId: 'A',
step: 4,
}],
thresholds: [],
timeFrom: null,
timeShift: null,
title: 'Memory',
tooltip: {
msResolution: false,
shared: true,
sort: 0,
value_type: 'individual',
},
type: 'graph',
xaxis: {
mode: 'time',
name: null,
show: true,
values: [],
},
yaxes: [
{
format: 'bytes',
label: null,
logBase: 1,
max: null,
min: null,
show: true,
},
{
format: 'short',
label: null,
logBase: 1,
max: null,
min: null,
show: true,
},
],
},
],
title: 'New row',
},
{
collapse: false,
editable: true,
height: '250px',
panels: [
{
aliasColors: {},
bars: false,
datasource: '$datasource',
editable: true,
'error': false,
fill: 5,
id: 22,
isNew: true,
legend: {
avg: false,
current: false,
max: false,
min: false,
show: false,
total: false,
values: false,
},
lines: true,
linewidth: 2,
links: [],
nullPointMode: 'connected',
percentage: false,
pointradius: 5,
points: false,
renderer: 'flot',
seriesOverrides: [],
span: 3,
stack: true,
steppedLine: false,
targets: [{
expr: 'rate(etcd_network_client_grpc_received_bytes_total{%s, %s="$cluster"}[$__rate_interval])' % [$._config.etcd_selector, $._config.clusterLabel],
intervalFactor: 2,
legendFormat: '{{instance}} Client Traffic In',
metric: 'etcd_network_client_grpc_received_bytes_total',
refId: 'A',
step: 4,
}],
thresholds: [],
timeFrom: null,
timeShift: null,
title: 'Client Traffic In',
tooltip: {
msResolution: false,
shared: true,
sort: 0,
value_type: 'individual',
},
type: 'graph',
xaxis: {
mode: 'time',
name: null,
show: true,
values: [],
},
yaxes: [
{
format: 'Bps',
label: null,
logBase: 1,
max: null,
min: null,
show: true,
},
{
format: 'short',
label: null,
logBase: 1,
max: null,
min: null,
show: true,
},
],
},
{
aliasColors: {},
bars: false,
datasource: '$datasource',
editable: true,
'error': false,
fill: 5,
id: 21,
isNew: true,
legend: {
avg: false,
current: false,
max: false,
min: false,
show: false,
total: false,
values: false,
},
lines: true,
linewidth: 2,
links: [],
nullPointMode: 'connected',
percentage: false,
pointradius: 5,
points: false,
renderer: 'flot',
seriesOverrides: [],
span: 3,
stack: true,
steppedLine: false,
targets: [{
expr: 'rate(etcd_network_client_grpc_sent_bytes_total{%s, %s="$cluster"}[$__rate_interval])' % [$._config.etcd_selector, $._config.clusterLabel],
intervalFactor: 2,
legendFormat: '{{instance}} Client Traffic Out',
metric: 'etcd_network_client_grpc_sent_bytes_total',
refId: 'A',
step: 4,
}],
thresholds: [],
timeFrom: null,
timeShift: null,
title: 'Client Traffic Out',
tooltip: {
msResolution: false,
shared: true,
sort: 0,
value_type: 'individual',
},
type: 'graph',
xaxis: {
mode: 'time',
name: null,
show: true,
values: [],
},
yaxes: [
{
format: 'Bps',
label: null,
logBase: 1,
max: null,
min: null,
show: true,
},
{
format: 'short',
label: null,
logBase: 1,
max: null,
min: null,
show: true,
},
],
},
{
aliasColors: {},
bars: false,
datasource: '$datasource',
editable: true,
'error': false,
fill: 0,
id: 20,
isNew: true,
legend: {
avg: false,
current: false,
max: false,
min: false,
show: false,
total: false,
values: false,
},
lines: true,
linewidth: 2,
links: [],
nullPointMode: 'connected',
percentage: false,
pointradius: 5,
points: false,
renderer: 'flot',
seriesOverrides: [],
span: 3,
stack: false,
steppedLine: false,
targets: [{
expr: 'sum(rate(etcd_network_peer_received_bytes_total{%s, %s="$cluster"}[$__rate_interval])) by (instance)' % [$._config.etcd_selector, $._config.clusterLabel],
intervalFactor: 2,
legendFormat: '{{instance}} Peer Traffic In',
metric: 'etcd_network_peer_received_bytes_total',
refId: 'A',
step: 4,
}],
thresholds: [],
timeFrom: null,
timeShift: null,
title: 'Peer Traffic In',
tooltip: {
msResolution: false,
shared: true,
sort: 0,
value_type: 'individual',
},
type: 'graph',
xaxis: {
mode: 'time',
name: null,
show: true,
values: [],
},
yaxes: [
{
format: 'Bps',
label: null,
logBase: 1,
max: null,
min: null,
show: true,
},
{
format: 'short',
label: null,
logBase: 1,
max: null,
min: null,
show: true,
},
],
},
{
aliasColors: {},
bars: false,
datasource: '$datasource',
decimals: null,
editable: true,
'error': false,
fill: 0,
grid: {},
id: 16,
legend: {
avg: false,
current: false,
max: false,
min: false,
show: false,
total: false,
values: false,
},
lines: true,
linewidth: 2,
links: [],
nullPointMode: 'connected',
percentage: false,
pointradius: 5,
points: false,
renderer: 'flot',
seriesOverrides: [],
span: 3,
stack: false,
steppedLine: false,
targets: [{
expr: 'sum(rate(etcd_network_peer_sent_bytes_total{%s, %s="$cluster"}[$__rate_interval])) by (instance)' % [$._config.etcd_selector, $._config.clusterLabel],
hide: false,
interval: '',
intervalFactor: 2,
legendFormat: '{{instance}} Peer Traffic Out',
metric: 'etcd_network_peer_sent_bytes_total',
refId: 'A',
step: 4,
}],
thresholds: [],
timeFrom: null,
timeShift: null,
title: 'Peer Traffic Out',
tooltip: {
msResolution: false,
shared: true,
sort: 0,
value_type: 'cumulative',
},
type: 'graph',
xaxis: {
mode: 'time',
name: null,
show: true,
values: [],
},
yaxes: [
{
format: 'Bps',
logBase: 1,
max: null,
min: null,
show: true,
},
{
format: 'short',
logBase: 1,
max: null,
min: null,
show: true,
},
],
},
],
title: 'New row',
},
{
collapse: false,
editable: true,
height: '250px',
panels: [
{
aliasColors: {},
bars: false,
datasource: '$datasource',
editable: true,
'error': false,
fill: 0,
id: 40,
isNew: true,
legend: {
avg: false,
current: false,
max: false,
min: false,
show: false,
total: false,
values: false,
},
lines: true,
linewidth: 2,
links: [],
nullPointMode: 'connected',
percentage: false,
pointradius: 5,
points: false,
renderer: 'flot',
seriesOverrides: [],
span: 6,
stack: false,
steppedLine: false,
targets: [
{
expr: 'sum(rate(etcd_server_proposals_failed_total{%s, %s="$cluster"}[$__rate_interval]))' % [$._config.etcd_selector, $._config.clusterLabel],
intervalFactor: 2,
legendFormat: 'Proposal Failure Rate',
metric: 'etcd_server_proposals_failed_total',
refId: 'A',
step: 2,
},
{
expr: 'sum(etcd_server_proposals_pending{%s, %s="$cluster"})' % [$._config.etcd_selector, $._config.clusterLabel],
intervalFactor: 2,
legendFormat: 'Proposal Pending Total',
metric: 'etcd_server_proposals_pending',
refId: 'B',
step: 2,
},
{
expr: 'sum(rate(etcd_server_proposals_committed_total{%s, %s="$cluster"}[$__rate_interval]))' % [$._config.etcd_selector, $._config.clusterLabel],
intervalFactor: 2,
legendFormat: 'Proposal Commit Rate',
metric: 'etcd_server_proposals_committed_total',
refId: 'C',
step: 2,
},
{
expr: 'sum(rate(etcd_server_proposals_applied_total{%s, %s="$cluster"}[$__rate_interval]))' % [$._config.etcd_selector, $._config.clusterLabel],
intervalFactor: 2,
legendFormat: 'Proposal Apply Rate',
refId: 'D',
step: 2,
},
],
thresholds: [],
timeFrom: null,
timeShift: null,
title: 'Raft Proposals',
tooltip: {
msResolution: false,
shared: true,
sort: 0,
value_type: 'individual',
},
type: 'graph',
xaxis: {
mode: 'time',
name: null,
show: true,
values: [],
},
yaxes: [
{
format: 'short',
label: '',
logBase: 1,
max: null,
min: null,
show: true,
},
{
format: 'short',
label: null,
logBase: 1,
max: null,
min: null,
show: true,
},
],
},
{
aliasColors: {},
bars: false,
datasource: '$datasource',
decimals: 0,
editable: true,
'error': false,
fill: 0,
id: 19,
isNew: true,
legend: {
alignAsTable: false,
avg: false,
current: false,
max: false,
min: false,
rightSide: false,
show: false,
total: false,
values: false,
},
lines: true,
linewidth: 2,
links: [],
nullPointMode: 'connected',
percentage: false,
pointradius: 5,
points: false,
renderer: 'flot',
seriesOverrides: [],
span: 6,
stack: false,
steppedLine: false,
targets: [{
expr: 'changes(etcd_server_leader_changes_seen_total{%s, %s="$cluster"}[1d])' % [$._config.etcd_selector, $._config.clusterLabel],
intervalFactor: 2,
legendFormat: '{{instance}} Total Leader Elections Per Day',
metric: 'etcd_server_leader_changes_seen_total',
refId: 'A',
step: 2,
}],
thresholds: [],
timeFrom: null,
timeShift: null,
title: 'Total Leader Elections Per Day',
tooltip: {
msResolution: false,
shared: true,
sort: 0,
value_type: 'individual',
},
type: 'graph',
xaxis: {
mode: 'time',
name: null,
show: true,
values: [],
},
yaxes: [
{
format: 'short',
label: null,
logBase: 1,
max: null,
min: null,
show: true,
},
{
format: 'short',
label: null,
logBase: 1,
max: null,
min: null,
show: true,
},
],
},
{
aliasColors: {},
bars: false,
dashLength: 10,
dashes: false,
datasource: '$datasource',
decimals: 0,
editable: true,
'error': false,
fieldConfig: {
defaults: {
custom: {},
},
overrides: [],
},
fill: 0,
fillGradient: 0,
gridPos: {
h: 7,
w: 12,
x: 0,
y: 28,
},
hiddenSeries: false,
id: 42,
isNew: true,
legend: {
alignAsTable: false,
avg: false,
current: false,
max: false,
min: false,
rightSide: false,
show: false,
total: false,
values: false,
},
lines: true,
linewidth: 2,
links: [],
nullPointMode: 'connected',
options: {
alertThreshold: true,
},
percentage: false,
pluginVersion: '7.4.3',
pointradius: 5,
points: false,
renderer: 'flot',
seriesOverrides: [],
spaceLength: 10,
stack: false,
steppedLine: false,
targets: [
{
expr: 'histogram_quantile(0.99, sum by (instance, le) (rate(etcd_network_peer_round_trip_time_seconds_bucket{%s, %s="$cluster"}[$__rate_interval])))' % [$._config.etcd_selector, $._config.clusterLabel],
interval: '',
intervalFactor: 2,
legendFormat: '{{instance}} Peer round trip time',
metric: 'etcd_network_peer_round_trip_time_seconds_bucket',
refId: 'A',
step: 2,
},
],
thresholds: [],
timeFrom: null,
timeRegions: [],
timeShift: null,
title: 'Peer round trip time',
tooltip: {
msResolution: false,
shared: true,
sort: 0,
value_type: 'individual',
},
type: 'graph',
xaxis: {
buckets: null,
mode: 'time',
name: null,
show: true,
values: [],
},
yaxes: [
{
'$$hashKey': 'object:925',
decimals: null,
format: 's',
label: null,
logBase: 1,
max: null,
min: null,
show: true,
},
{
'$$hashKey': 'object:926',
format: 'short',
label: null,
logBase: 1,
max: null,
min: null,
show: true,
},
],
yaxis: {
align: false,
alignLevel: null,
},
},
],
title: 'New row',
},
],
time: {
from: 'now-15m',
to: 'now',
},
timepicker: {
now: true,
refresh_intervals: [
'5s',
'10s',
'30s',
'1m',
'5m',
'15m',
'30m',
'1h',
'2h',
'1d',
],
time_options: [
'5m',
'15m',
'1h',
'6h',
'12h',
'24h',
'2d',
'7d',
'30d',
],
},
templating: {
list: [
{
current: {
text: 'Prometheus',
value: 'Prometheus',
},
hide: 0,
label: 'Data Source',
name: 'datasource',
options: [],
query: 'prometheus',
refresh: 1,
regex: '',
type: 'datasource',
},
{
allValue: null,
current: {
text: 'prod',
value: 'prod',
},
datasource: '$datasource',
hide: 0,
includeAll: false,
label: 'cluster',
multi: false,
name: 'cluster',
options: [],
query: 'label_values(etcd_server_has_leader{%s}, %s)' % [$._config.etcd_selector, $._config.clusterLabel],
refresh: $._config.dashboard_var_refresh,
regex: '',
sort: 2,
tagValuesQuery: '',
tags: [],
tagsQuery: '',
type: 'query',
useTags: false,
},
],
},
annotations: {
list: [],
},
refresh: '10s',
schemaVersion: 13,
version: 215,
links: [],
gnetId: null,
},
} else {},
}
================================================
FILE: contrib/mixin/dashboards/etcd.libsonnet
================================================
{
grafanaDashboards+:: if !$._config.grafana7x then {
local g = import './g.libsonnet',
local panels = import './panels.libsonnet',
local variables = import './variables.libsonnet',
local targets = import './targets.libsonnet',
local v = variables($._config),
local t = targets(v, $._config),
'etcd.json':
g.dashboard.new('etcd')
+ g.dashboard.withUid(std.md5('etcd.json'))
+ g.dashboard.withRefresh('10s')
+ g.dashboard.time.withFrom('now-15m')
+ g.dashboard.time.withTo('now')
+ g.dashboard.withDescription('etcd sample Grafana dashboard with Prometheus')
+ g.dashboard.withTags(['etcd-mixin'])
+ g.dashboard.withVariables([
v.datasource,
v.cluster,
])
+ g.dashboard.withPanels(
[
panels.stat.up('Up', t.up) { gridPos: { x: 0, h: 7, w: 6, y: 0 } },
panels.timeSeries.rpcRate('RPC rate', [t.rpcRate, t.rpcFailedRate]) { gridPos: { x: 6, h: 7, w: 10, y: 0 } },
panels.timeSeries.activeStreams('Active streams', [t.watchStreams, t.leaseStreams]) { gridPos: { x: 16, h: 7, w: 8, y: 0 } },
panels.timeSeries.dbSize('DB size', [t.dbSize]) { gridPos: { x: 0, h: 7, w: 8, y: 25 } },
panels.timeSeries.diskSync('Disk sync duration', [t.walFsync, t.dbFsync]) { gridPos: { x: 8, h: 7, w: 8, y: 25 } },
panels.timeSeries.memory('Memory', [t.memory]) { gridPos: { x: 16, h: 7, w: 8, y: 25 } },
panels.timeSeries.traffic('Client traffic in', [t.clientTrafficIn]) { gridPos: { x: 0, h: 7, w: 6, y: 50 } },
panels.timeSeries.traffic('Client traffic out', [t.clientTrafficOut]) { gridPos: { x: 6, h: 7, w: 6, y: 50 } },
panels.timeSeries.traffic('Peer traffic in', [t.peerTrafficIn]) { gridPos: { x: 12, h: 7, w: 6, y: 50 } },
panels.timeSeries.traffic('Peer traffic out', [t.peerTrafficOut]) { gridPos: { x: 18, h: 7, w: 6, y: 50 } },
panels.timeSeries.raftProposals('Raft proposals', [t.raftProposals]) { gridPos: { x: 0, h: 7, w: 8, y: 75 } },
panels.timeSeries.leaderElections('Total leader elections per day', [t.leaderElections]) { gridPos: { x: 8, h: 7, w: 8, y: 75 } },
panels.timeSeries.peerRtt('Peer round trip time', [t.peerRtt]) { gridPos: { x: 16, h: 7, w: 8, y: 75 } },
]
),
} else {},
}
================================================
FILE: contrib/mixin/dashboards/g.libsonnet
================================================
import 'github.com/grafana/grafonnet/gen/grafonnet-v10.0.0/main.libsonnet'
================================================
FILE: contrib/mixin/dashboards/panels.libsonnet
================================================
local g = import 'g.libsonnet';
{
stat: {
local stat = g.panel.stat,
base(title, targets):
stat.new(title)
+ stat.queryOptions.withTargets(targets)
+ stat.queryOptions.withInterval('1m'),
up(title, targets):
self.base(title, targets)
+ stat.options.withColorMode('none')
+ stat.options.withGraphMode('none')
+ stat.options.reduceOptions.withCalcs([
'lastNotNull',
]),
},
timeSeries: {
local timeSeries = g.panel.timeSeries,
local fieldOverride = g.panel.timeSeries.fieldOverride,
local custom = timeSeries.fieldConfig.defaults.custom,
local defaults = timeSeries.fieldConfig.defaults,
local options = timeSeries.options,
base(title, targets):
timeSeries.new(title)
+ timeSeries.queryOptions.withTargets(targets)
+ timeSeries.queryOptions.withInterval('1m')
+ custom.withLineWidth(2)
+ custom.withFillOpacity(0)
+ custom.withShowPoints('never'),
rpcRate(title, targets):
self.base(title, targets)
+ timeSeries.standardOptions.withUnit('ops'),
activeStreams(title, targets):
self.base(title, targets),
dbSize(title, targets):
self.base(title, targets)
+ timeSeries.standardOptions.withUnit('bytes'),
diskSync(title, targets):
self.base(title, targets)
+ timeSeries.standardOptions.withUnit('s'),
memory(title, targets):
self.base(title, targets)
+ timeSeries.standardOptions.withUnit('bytes'),
traffic(title, targets):
self.base(title, targets)
+ timeSeries.standardOptions.withUnit('Bps'),
raftProposals(title, targets):
self.base(title, targets),
leaderElections(title, targets):
self.base(title, targets),
peerRtt(title, targets):
self.base(title, targets)
+ timeSeries.standardOptions.withUnit('s'),
},
}
================================================
FILE: contrib/mixin/dashboards/targets.libsonnet
================================================
local g = import './g.libsonnet';
local prometheusQuery = g.query.prometheus;
function(variables, config) {
up:
prometheusQuery.new(
'$' + variables.datasource.name,
'sum(etcd_server_has_leader{%s, %s="$cluster"})' % [config.etcd_selector, config.clusterLabel]
)
+ prometheusQuery.withLegendFormat(|||
{{cluster}} - {{namespace}}
|||),
rpcRate:
prometheusQuery.new(
'$' + variables.datasource.name,
'sum(rate(grpc_server_started_total{%s, %s="$cluster",grpc_type="unary"}[$__rate_interval]))' % [config.etcd_selector, config.clusterLabel]
)
+ prometheusQuery.withLegendFormat('RPC rate'),
rpcFailedRate:
prometheusQuery.new(
'$' + variables.datasource.name,
'sum(rate(grpc_server_handled_total{%s, %s="$cluster",grpc_type="unary",grpc_code=~"Unknown|FailedPrecondition|ResourceExhausted|Internal|Unavailable|DataLoss|DeadlineExceeded"}[$__rate_interval]))' % [config.etcd_selector, config.clusterLabel]
)
+ prometheusQuery.withLegendFormat('RPC failed rate'),
watchStreams:
prometheusQuery.new(
'$' + variables.datasource.name,
'sum(grpc_server_started_total{%(etcd_selector)s,%(clusterLabel)s="$cluster",grpc_service="etcdserverpb.Watch",grpc_type="bidi_stream"}) - sum(grpc_server_handled_total{%(clusterLabel)s="$cluster",grpc_service="etcdserverpb.Watch",grpc_type="bidi_stream"})' % config
)
+ prometheusQuery.withLegendFormat('Watch streams'),
leaseStreams:
prometheusQuery.new(
'$' + variables.datasource.name,
'sum(grpc_server_started_total{%(etcd_selector)s,%(clusterLabel)s="$cluster",grpc_service="etcdserverpb.Lease",grpc_type="bidi_stream"}) - sum(grpc_server_handled_total{%(clusterLabel)s="$cluster",grpc_service="etcdserverpb.Lease",grpc_type="bidi_stream"})' % config
)
+ prometheusQuery.withLegendFormat('Lease streams'),
dbSize:
prometheusQuery.new(
'$' + variables.datasource.name,
'etcd_mvcc_db_total_size_in_bytes{%s, %s="$cluster"}' % [config.etcd_selector, config.clusterLabel],
)
+ prometheusQuery.withLegendFormat('{{instance}} DB size'),
walFsync:
prometheusQuery.new(
'$' + variables.datasource.name,
'histogram_quantile(0.99, sum(rate(etcd_disk_wal_fsync_duration_seconds_bucket{%s, %s="$cluster"}[$__rate_interval])) by (instance, le))' % [config.etcd_selector, config.clusterLabel],
)
+ prometheusQuery.withLegendFormat('{{instance}} WAL fsync'),
dbFsync:
prometheusQuery.new(
'$' + variables.datasource.name,
'histogram_quantile(0.99, sum(rate(etcd_disk_backend_commit_duration_seconds_bucket{%s, %s="$cluster"}[$__rate_interval])) by (instance, le))' % [config.etcd_selector, config.clusterLabel],
)
+ prometheusQuery.withLegendFormat('{{instance}} DB fsync'),
memory:
prometheusQuery.new(
'$' + variables.datasource.name,
'process_resident_memory_bytes{%s, %s="$cluster"}' % [config.etcd_selector, config.clusterLabel],
)
+ prometheusQuery.withLegendFormat('{{instance}} resident memory'),
clientTrafficIn:
prometheusQuery.new(
'$' + variables.datasource.name,
'rate(etcd_network_client_grpc_received_bytes_total{%s, %s="$cluster"}[$__rate_interval])' % [config.etcd_selector, config.clusterLabel],
)
+ prometheusQuery.withLegendFormat('{{instance}} client traffic in'),
clientTrafficOut:
prometheusQuery.new(
'$' + variables.datasource.name,
'rate(etcd_network_client_grpc_sent_bytes_total{%s, %s="$cluster"}[$__rate_interval])' % [config.etcd_selector, config.clusterLabel],
)
+ prometheusQuery.withLegendFormat('{{instance}} client traffic out'),
peerTrafficIn:
prometheusQuery.new(
'$' + variables.datasource.name,
'sum(rate(etcd_network_peer_received_bytes_total{%s, %s="$cluster"}[$__rate_interval])) by (instance)' % [config.etcd_selector, config.clusterLabel],
)
+ prometheusQuery.withLegendFormat('{{instance}} peer traffic in'),
peerTrafficOut:
prometheusQuery.new(
'$' + variables.datasource.name,
'sum(rate(etcd_network_peer_sent_bytes_total{%s, %s="$cluster"}[$__rate_interval])) by (instance)' % [config.etcd_selector, config.clusterLabel],
)
+ prometheusQuery.withLegendFormat('{{instance}} peer traffic out'),
raftProposals:
prometheusQuery.new(
'$' + variables.datasource.name,
'changes(etcd_server_leader_changes_seen_total{%s, %s="$cluster"}[1d])' % [config.etcd_selector, config.clusterLabel],
)
+ prometheusQuery.withLegendFormat('{{instance}} total leader elections per day'),
leaderElections:
prometheusQuery.new(
'$' + variables.datasource.name,
'changes(etcd_server_leader_changes_seen_total{%s, %s="$cluster"}[1d])' % [config.etcd_selector, config.clusterLabel],
)
+ prometheusQuery.withLegendFormat('{{instance}} total leader elections per day'),
peerRtt:
prometheusQuery.new(
'$' + variables.datasource.name,
'histogram_quantile(0.99, sum by (instance, le) (rate(etcd_network_peer_round_trip_time_seconds_bucket{%s, %s="$cluster"}[$__rate_interval])))' % [config.etcd_selector, config.clusterLabel],
)
+ prometheusQuery.withLegendFormat('{{instance}} peer round trip time'),
}
================================================
FILE: contrib/mixin/dashboards/variables.libsonnet
================================================
// variables.libsonnet
local g = import './g.libsonnet';
local var = g.dashboard.variable;
function(config) {
datasource:
var.datasource.new('datasource', 'prometheus')
+ var.datasource.generalOptions.withLabel('Data Source'),
cluster:
var.query.new('cluster')
+ var.query.generalOptions.withLabel('cluster')
+ var.query.withDatasourceFromVariable(self.datasource)
+ { refresh: config.dashboard_var_refresh }
+ var.query.queryTypes.withLabelValues(
config.clusterLabel,
'etcd_server_has_leader{%s}' % [config.etcd_selector]
),
}
================================================
FILE: contrib/mixin/jsonnetfile.json
================================================
{
"version": 1,
"dependencies": [
{
"source": {
"git": {
"remote": "https://github.com/grafana/grafonnet.git",
"subdir": "gen/grafonnet-v10.0.0"
}
},
"version": "main"
}
],
"legacyImports": true
}
================================================
FILE: contrib/mixin/jsonnetfile.lock.json
================================================
{
"version": 1,
"dependencies": [
{
"source": {
"git": {
"remote": "https://github.com/grafana/grafonnet.git",
"subdir": "gen/grafonnet-v10.0.0"
}
},
"version": "e85299323fd8808187d30865cc5c7a38a347399a",
"sum": "uJCTMGtY/7c5HSLQ7UQD38TOPmuSYrIKLIKmdSF/Htk="
},
{
"source": {
"git": {
"remote": "https://github.com/jsonnet-libs/docsonnet.git",
"subdir": "doc-util"
}
},
"version": "fd8de9039b3c06da77d635a3a8289809a5bfb542",
"sum": "mFebrE9fhyAKW4zbnidcjVFupziN5LPA/Z7ii94uCzs="
},
{
"source": {
"git": {
"remote": "https://github.com/jsonnet-libs/xtd.git",
"subdir": ""
}
},
"version": "0256a910ac71f0f842696d7bca0bf01ea77eb654",
"sum": "zBOpb1oTNvXdq9RF6yzTHill5r1YTJLBBoqyx4JYtAg="
}
],
"legacyImports": false
}
================================================
FILE: contrib/mixin/mixin.libsonnet
================================================
(import './config.libsonnet') +
(import './dashboards/dashboards.libsonnet') +
(import './alerts/alerts.libsonnet')
================================================
FILE: contrib/mixin/test.yaml
================================================
---
rule_files: [manifests/etcd-prometheusRules.yaml]
evaluation_interval: 1m
tests:
- interval: 1m
input_series:
- series: up{job="etcd",instance="10.10.10.0"}
values: 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0
- series: up{job="etcd",instance="10.10.10.1"}
values: 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
- series: up{job="etcd",instance="10.10.10.2"}
values: 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
alert_rule_test:
- eval_time: 3m
alertname: etcdInsufficientMembers
- eval_time: 5m
alertname: etcdInsufficientMembers
- eval_time: 22m
alertname: etcdMembersDown
- eval_time: 24m
alertname: etcdMembersDown
exp_alerts:
- exp_labels:
job: etcd
severity: warning
exp_annotations:
description: 'etcd cluster "etcd": members are down (3).'
summary: etcd cluster members are down.
- eval_time: 7m
alertname: etcdInsufficientMembers
- eval_time: 11m
alertname: etcdInsufficientMembers
exp_alerts:
- exp_labels:
job: etcd
severity: critical
exp_annotations:
description: 'etcd cluster "etcd": insufficient members (1).'
summary: etcd cluster has insufficient number of members.
- eval_time: 15m
alertname: etcdInsufficientMembers
exp_alerts:
- exp_labels:
job: etcd
severity: critical
exp_annotations:
description: 'etcd cluster "etcd": insufficient members (0).'
summary: etcd cluster has insufficient number of members.
- interval: 1m
input_series:
- series: up{job="etcd",instance="10.10.10.0"}
values: 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0
- series: up{job="etcd",instance="10.10.10.1"}
values: 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0
- series: up{job="etcd",instance="10.10.10.2"}
values: 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
alert_rule_test:
- eval_time: 24m
alertname: etcdMembersDown
exp_alerts:
- exp_labels:
job: etcd
severity: warning
exp_annotations:
description: 'etcd cluster "etcd": members are down (3).'
summary: etcd cluster members are down.
- interval: 1m
input_series:
- series: up{job="etcd",instance="10.10.10.0"}
values: 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0
- series: up{job="etcd",instance="10.10.10.1"}
values: 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0
- series: etcd_network_peer_sent_failures_total{To="member-1",job="etcd",endpoint="test"}
values: 0 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
alert_rule_test:
- eval_time: 23m
alertname: etcdMembersDown
exp_alerts:
- exp_labels:
job: etcd
severity: warning
exp_annotations:
description: 'etcd cluster "etcd": members are down (1).'
summary: etcd cluster members are down.
- interval: 1m
input_series:
- series: etcd_server_leader_changes_seen_total{job="etcd",instance="10.10.10.0"}
values: 0 0 2 0 0 1 0 0 0 0 0 0 0 0 0 0
- series: etcd_server_leader_changes_seen_total{job="etcd",instance="10.10.10.1"}
values: 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0
- series: etcd_server_leader_changes_seen_total{job="etcd",instance="10.10.10.2"}
values: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
alert_rule_test:
- eval_time: 10m
alertname: etcdHighNumberOfLeaderChanges
exp_alerts:
- exp_labels:
job: etcd
severity: warning
exp_annotations:
description: 'etcd cluster "etcd": 4 leader changes within the last 15 minutes. Frequent elections may be a sign of insufficient resources, high network latency, or disruptions by other components and should be investigated.'
summary: etcd cluster has high number of leader changes.
- interval: 1m
input_series:
- series: etcd_server_leader_changes_seen_total{job="etcd",instance="10.10.10.0"}
values: 0 0 2 0 0 0 0 0 0 0 0 0 0 0 0 0
- series: etcd_server_leader_changes_seen_total{job="etcd",instance="10.10.10.1"}
values: 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0
- series: etcd_server_leader_changes_seen_total{job="etcd",instance="10.10.10.2"}
values: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
alert_rule_test:
- eval_time: 10m
alertname: etcdHighNumberOfLeaderChanges
exp_alerts:
- interval: 1m
input_series:
- series: etcd_mvcc_db_total_size_in_bytes{job="etcd",instance="10.10.10.0"}
values: 0+8192x240
- series: etcd_server_quota_backend_bytes{job="etcd",instance="10.10.10.0"}
values: 524288+0x240
- series: etcd_mvcc_db_total_size_in_bytes{job="etcd",instance="10.10.10.1"}
values: 0+1024x240
- series: etcd_server_quota_backend_bytes{job="etcd",instance="10.10.10.1"}
values: 524288+0x240
alert_rule_test:
- eval_time: 11m
alertname: etcdExcessiveDatabaseGrowth
exp_alerts:
- exp_labels:
instance: 10.10.10.0
job: etcd
severity: warning
exp_annotations:
description: 'etcd cluster "etcd": Predicting running out of disk space in the next four hours, based on write observations within the past four hours on etcd instance 10.10.10.0, please check as it might be disruptive.'
summary: etcd cluster database growing very fast.
- interval: 1m
input_series:
- series: etcd_mvcc_db_total_size_in_use_in_bytes{job="etcd",instance="10.10.10.0"}
values: 300000000+0x10
- series: etcd_mvcc_db_total_size_in_bytes{job="etcd",instance="10.10.10.0"}
values: 1000000000+0x10
- series: etcd_mvcc_db_total_size_in_use_in_bytes{job="etcd",instance="10.10.10.1"}
values: 700000000+0x10
- series: etcd_mvcc_db_total_size_in_bytes{job="etcd",instance="10.10.10.1"}
values: 1000000000+0x10
alert_rule_test:
- eval_time: 11m
alertname: etcdDatabaseHighFragmentationRatio
exp_alerts:
- exp_labels:
instance: 10.10.10.0
job: etcd
severity: warning
exp_annotations:
description: 'etcd cluster "etcd": database size in use on instance 10.10.10.0 is 30% of the actual allocated disk space, please run defragmentation (e.g. etcdctl defrag) to retrieve the unused fragmented disk space.'
runbook_url: https://etcd.io/docs/v3.5/op-guide/maintenance/#defragmentation
summary: etcd database size in use is less than 50% of the actual allocated storage.
================================================
FILE: contrib/raftexample/Procfile
================================================
# Use goreman to run `go install github.com/mattn/goreman@latest`
raftexample1: ./raftexample --id 1 --cluster http://127.0.0.1:12379,http://127.0.0.1:22379,http://127.0.0.1:32379 --port 12380
raftexample2: ./raftexample --id 2 --cluster http://127.0.0.1:12379,http://127.0.0.1:22379,http://127.0.0.1:32379 --port 22380
raftexample3: ./raftexample --id 3 --cluster http://127.0.0.1:12379,http://127.0.0.1:22379,http://127.0.0.1:32379 --port 32380
================================================
FILE: contrib/raftexample/README.md
================================================
# raftexample
raftexample is an example usage of etcd's [raft library](https://github.com/etcd-io/raft). It provides a simple REST API for a key-value store cluster backed by the [Raft][raft] consensus algorithm.
[raft]: http://raftconsensus.github.io/
## Getting Started
### Building raftexample
Clone `etcd` to `/src/go.etcd.io/etcd`
```sh
export GOPATH=
cd /src/go.etcd.io/etcd/contrib/raftexample
go build -o raftexample
```
### Running single node raftexample
First start a single-member cluster of raftexample:
```sh
raftexample --id 1 --cluster http://127.0.0.1:12379 --port 12380
```
Each raftexample process maintains a single raft instance and a key-value server.
The process's list of comma separated peers (--cluster), its raft ID index into the peer list (--id), and http key-value server port (--port) are passed through the command line.
Next, store a value ("hello") to a key ("my-key"):
```
curl -L http://127.0.0.1:12380/my-key -XPUT -d hello
```
Finally, retrieve the stored key:
```
curl -L http://127.0.0.1:12380/my-key
```
### Running a local cluster
First install [goreman](https://github.com/mattn/goreman), which manages Procfile-based applications.
The [Procfile script](./Procfile) will set up a local example cluster. Start it with:
```sh
goreman start
```
This will bring up three raftexample instances.
Now it's possible to write a key-value pair to any member of the cluster and likewise retrieve it from any member.
### Fault Tolerance
To test cluster recovery, first start a cluster and write a value "foo":
```sh
goreman start
curl -L http://127.0.0.1:12380/my-key -XPUT -d foo
```
Next, remove a node and replace the value with "bar" to check cluster availability:
```sh
goreman run stop raftexample2
curl -L http://127.0.0.1:12380/my-key -XPUT -d bar
curl -L http://127.0.0.1:32380/my-key
```
Finally, bring the node back up and verify it recovers with the updated value "bar":
```sh
goreman run start raftexample2
curl -L http://127.0.0.1:22380/my-key
```
### Dynamic cluster reconfiguration
Nodes can be added to or removed from a running cluster using requests to the REST API.
For example, suppose we have a 3-node cluster that was started with the commands:
```sh
raftexample --id 1 --cluster http://127.0.0.1:12379,http://127.0.0.1:22379,http://127.0.0.1:32379 --port 12380
raftexample --id 2 --cluster http://127.0.0.1:12379,http://127.0.0.1:22379,http://127.0.0.1:32379 --port 22380
raftexample --id 3 --cluster http://127.0.0.1:12379,http://127.0.0.1:22379,http://127.0.0.1:32379 --port 32380
```
A fourth node with ID 4 can be added by issuing a POST:
```sh
curl -L http://127.0.0.1:12380/4 -XPOST -d http://127.0.0.1:42379
```
Then the new node can be started as the others were, using the --join option:
```sh
raftexample --id 4 --cluster http://127.0.0.1:12379,http://127.0.0.1:22379,http://127.0.0.1:32379,http://127.0.0.1:42379 --port 42380 --join
```
The new node should join the cluster and be able to service key/value requests.
We can remove a node using a DELETE request:
```sh
curl -L http://127.0.0.1:12380/3 -XDELETE
```
Node 3 should shut itself down once the cluster has processed this request.
## Design
The raftexample consists of three components: a raft-backed key-value store, a REST API server, and a raft consensus server based on etcd's raft implementation.
The raft-backed key-value store is a key-value map that holds all committed key-values.
The store bridges communication between the raft server and the REST server.
Key-value updates are issued through the store to the raft server.
The store updates its map once raft reports the updates are committed.
The REST server exposes the current raft consensus by accessing the raft-backed key-value store.
A GET command looks up a key in the store and returns the value, if any.
A key-value PUT command issues an update proposal to the store.
The raft server participates in consensus with its cluster peers.
When the REST server submits a proposal, the raft server transmits the proposal to its peers.
When raft reaches a consensus, the server publishes all committed updates over a commit channel.
For raftexample, this commit channel is consumed by the key-value store.
================================================
FILE: contrib/raftexample/doc.go
================================================
// Copyright 2016 The etcd Authors
//
// 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.
// raftexample is a simple KV store using the raft and rafthttp libraries.
package main
================================================
FILE: contrib/raftexample/httpapi.go
================================================
// Copyright 2015 The etcd Authors
//
// 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 main
import (
"io"
"log"
"net/http"
"strconv"
"go.etcd.io/raft/v3/raftpb"
)
// Handler for a http based key-value store backed by raft
type httpKVAPI struct {
store *kvstore
confChangeC chan<- raftpb.ConfChange
}
func (h *httpKVAPI) ServeHTTP(w http.ResponseWriter, r *http.Request) {
key := r.RequestURI
defer r.Body.Close()
switch r.Method {
case http.MethodPut:
v, err := io.ReadAll(r.Body)
if err != nil {
log.Printf("Failed to read on PUT (%v)\n", err)
http.Error(w, "Failed on PUT", http.StatusBadRequest)
return
}
h.store.Propose(key, string(v))
// Optimistic-- no waiting for ack from raft. Value is not yet
// committed so a subsequent GET on the key may return old value
w.WriteHeader(http.StatusNoContent)
case http.MethodGet:
if v, ok := h.store.Lookup(key); ok {
w.Write([]byte(v))
} else {
http.Error(w, "Failed to GET", http.StatusNotFound)
}
case http.MethodPost:
url, err := io.ReadAll(r.Body)
if err != nil {
log.Printf("Failed to read on POST (%v)\n", err)
http.Error(w, "Failed on POST", http.StatusBadRequest)
return
}
nodeID, err := strconv.ParseUint(key[1:], 0, 64)
if err != nil {
log.Printf("Failed to convert ID for conf change (%v)\n", err)
http.Error(w, "Failed on POST", http.StatusBadRequest)
return
}
cc := raftpb.ConfChange{
Type: raftpb.ConfChangeAddNode,
NodeID: nodeID,
Context: url,
}
h.confChangeC <- cc
// As above, optimistic that raft will apply the conf change
w.WriteHeader(http.StatusNoContent)
case http.MethodDelete:
nodeID, err := strconv.ParseUint(key[1:], 0, 64)
if err != nil {
log.Printf("Failed to convert ID for conf change (%v)\n", err)
http.Error(w, "Failed on DELETE", http.StatusBadRequest)
return
}
cc := raftpb.ConfChange{
Type: raftpb.ConfChangeRemoveNode,
NodeID: nodeID,
}
h.confChangeC <- cc
// As above, optimistic that raft will apply the conf change
w.WriteHeader(http.StatusNoContent)
default:
w.Header().Set("Allow", http.MethodPut)
w.Header().Add("Allow", http.MethodGet)
w.Header().Add("Allow", http.MethodPost)
w.Header().Add("Allow", http.MethodDelete)
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
}
}
// serveHTTPKVAPI starts a key-value server with a GET/PUT API and listens.
func serveHTTPKVAPI(kv *kvstore, port int, confChangeC chan<- raftpb.ConfChange, errorC <-chan error) {
srv := http.Server{
Addr: ":" + strconv.Itoa(port),
Handler: &httpKVAPI{
store: kv,
confChangeC: confChangeC,
},
}
go func() {
if err := srv.ListenAndServe(); err != nil {
log.Fatal(err)
}
}()
// exit when raft goes down
if err, ok := <-errorC; ok {
log.Fatal(err)
}
}
================================================
FILE: contrib/raftexample/kvstore.go
================================================
// Copyright 2015 The etcd Authors
//
// 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 main
import (
"bytes"
"encoding/gob"
"encoding/json"
"errors"
"log"
"strings"
"sync"
"go.etcd.io/etcd/server/v3/etcdserver/api/snap"
"go.etcd.io/raft/v3/raftpb"
)
// a key-value store backed by raft
type kvstore struct {
proposeC chan<- string // channel for proposing updates
mu sync.RWMutex
kvStore map[string]string // current committed key-value pairs
snapshotter *snap.Snapshotter
}
type kv struct {
Key string
Val string
}
func newKVStore(snapshotter *snap.Snapshotter, proposeC chan<- string, commitC <-chan *commit, errorC <-chan error) *kvstore {
s := &kvstore{proposeC: proposeC, kvStore: make(map[string]string), snapshotter: snapshotter}
snapshot, err := s.loadSnapshot()
if err != nil {
log.Panic(err)
}
if snapshot != nil {
log.Printf("loading snapshot at term %d and index %d", snapshot.Metadata.Term, snapshot.Metadata.Index)
if err := s.recoverFromSnapshot(snapshot.Data); err != nil {
log.Panic(err)
}
}
// read commits from raft into kvStore map until error
go s.readCommits(commitC, errorC)
return s
}
func (s *kvstore) Lookup(key string) (string, bool) {
s.mu.RLock()
defer s.mu.RUnlock()
v, ok := s.kvStore[key]
return v, ok
}
func (s *kvstore) Propose(k string, v string) {
var buf strings.Builder
if err := gob.NewEncoder(&buf).Encode(kv{k, v}); err != nil {
log.Fatal(err)
}
s.proposeC <- buf.String()
}
func (s *kvstore) readCommits(commitC <-chan *commit, errorC <-chan error) {
for commit := range commitC {
if commit == nil {
// signaled to load snapshot
snapshot, err := s.loadSnapshot()
if err != nil {
log.Panic(err)
}
if snapshot != nil {
log.Printf("loading snapshot at term %d and index %d", snapshot.Metadata.Term, snapshot.Metadata.Index)
if err := s.recoverFromSnapshot(snapshot.Data); err != nil {
log.Panic(err)
}
}
continue
}
for _, data := range commit.data {
var dataKv kv
dec := gob.NewDecoder(bytes.NewBufferString(data))
if err := dec.Decode(&dataKv); err != nil {
log.Fatalf("raftexample: could not decode message (%v)", err)
}
s.mu.Lock()
s.kvStore[dataKv.Key] = dataKv.Val
s.mu.Unlock()
}
close(commit.applyDoneC)
}
if err, ok := <-errorC; ok {
log.Fatal(err)
}
}
func (s *kvstore) getSnapshot() ([]byte, error) {
s.mu.RLock()
defer s.mu.RUnlock()
return json.Marshal(s.kvStore)
}
func (s *kvstore) loadSnapshot() (*raftpb.Snapshot, error) {
snapshot, err := s.snapshotter.Load()
if errors.Is(err, snap.ErrNoSnapshot) {
return nil, nil
}
if err != nil {
return nil, err
}
return snapshot, nil
}
func (s *kvstore) recoverFromSnapshot(snapshot []byte) error {
var store map[string]string
if err := json.Unmarshal(snapshot, &store); err != nil {
return err
}
s.mu.Lock()
defer s.mu.Unlock()
s.kvStore = store
return nil
}
================================================
FILE: contrib/raftexample/kvstore_test.go
================================================
// Copyright 2016 The etcd Authors
//
// 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 main
import (
"reflect"
"testing"
"github.com/stretchr/testify/require"
)
func Test_kvstore_snapshot(t *testing.T) {
tm := map[string]string{"foo": "bar"}
s := &kvstore{kvStore: tm}
v, _ := s.Lookup("foo")
require.Equalf(t, "bar", v, "foo has unexpected value, got %s", v)
data, err := s.getSnapshot()
require.NoError(t, err)
s.kvStore = nil
err = s.recoverFromSnapshot(data)
require.NoError(t, err)
v, _ = s.Lookup("foo")
require.Equalf(t, "bar", v, "foo has unexpected value, got %s", v)
require.Truef(t, reflect.DeepEqual(s.kvStore, tm), "store expected %+v, got %+v", tm, s.kvStore)
}
================================================
FILE: contrib/raftexample/listener.go
================================================
// Copyright 2015 The etcd Authors
//
// 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 main
import (
"errors"
"net"
"time"
)
// stoppableListener sets TCP keep-alive timeouts on accepted
// connections and waits on stopc message
type stoppableListener struct {
*net.TCPListener
stopc <-chan struct{}
}
func newStoppableListener(addr string, stopc <-chan struct{}) (*stoppableListener, error) {
ln, err := net.Listen("tcp", addr)
if err != nil {
return nil, err
}
return &stoppableListener{ln.(*net.TCPListener), stopc}, nil
}
func (ln stoppableListener) Accept() (c net.Conn, err error) {
connc := make(chan *net.TCPConn, 1)
errc := make(chan error, 1)
go func() {
tc, err := ln.AcceptTCP()
if err != nil {
errc <- err
return
}
connc <- tc
}()
select {
case <-ln.stopc:
return nil, errors.New("server stopped")
case err := <-errc:
return nil, err
case tc := <-connc:
tc.SetKeepAlive(true)
tc.SetKeepAlivePeriod(3 * time.Minute)
return tc, nil
}
}
================================================
FILE: contrib/raftexample/main.go
================================================
// Copyright 2015 The etcd Authors
//
// 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 main
import (
"flag"
"strings"
"go.etcd.io/raft/v3/raftpb"
)
func main() {
cluster := flag.String("cluster", "http://127.0.0.1:9021", "comma separated cluster peers")
id := flag.Int("id", 1, "node ID")
kvport := flag.Int("port", 9121, "key-value server port")
join := flag.Bool("join", false, "join an existing cluster")
flag.Parse()
proposeC := make(chan string)
defer close(proposeC)
confChangeC := make(chan raftpb.ConfChange)
defer close(confChangeC)
// raft provides a commit stream for the proposals from the http api
var kvs *kvstore
getSnapshot := func() ([]byte, error) { return kvs.getSnapshot() }
commitC, errorC, snapshotterReady := newRaftNode(*id, strings.Split(*cluster, ","), *join, getSnapshot, proposeC, confChangeC)
kvs = newKVStore(<-snapshotterReady, proposeC, commitC, errorC)
// the key-value http handler will propose updates to raft
serveHTTPKVAPI(kvs, *kvport, confChangeC, errorC)
}
================================================
FILE: contrib/raftexample/raft.go
================================================
// Copyright 2015 The etcd Authors
//
// 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 main
import (
"context"
"errors"
"fmt"
"log"
"net/http"
"net/url"
"os"
"strconv"
"time"
"go.uber.org/zap"
"go.etcd.io/etcd/client/pkg/v3/fileutil"
"go.etcd.io/etcd/client/pkg/v3/types"
"go.etcd.io/etcd/server/v3/etcdserver/api/rafthttp"
"go.etcd.io/etcd/server/v3/etcdserver/api/snap"
stats "go.etcd.io/etcd/server/v3/etcdserver/api/v2stats"
"go.etcd.io/etcd/server/v3/storage/wal"
"go.etcd.io/etcd/server/v3/storage/wal/walpb"
"go.etcd.io/raft/v3"
"go.etcd.io/raft/v3/raftpb"
)
type commit struct {
data []string
applyDoneC chan<- struct{}
}
// A key-value stream backed by raft
type raftNode struct {
proposeC <-chan string // proposed messages (k,v)
confChangeC <-chan raftpb.ConfChange // proposed cluster config changes
commitC chan<- *commit // entries committed to log (k,v)
errorC chan<- error // errors from raft session
id int // client ID for raft session
peers []string // raft peer URLs
join bool // node is joining an existing cluster
waldir string // path to WAL directory
snapdir string // path to snapshot directory
getSnapshot func() ([]byte, error)
confState raftpb.ConfState
snapshotIndex uint64
appliedIndex uint64
// raft backing for the commit/error channel
node raft.Node
raftStorage *raft.MemoryStorage
wal *wal.WAL
snapshotter *snap.Snapshotter
snapshotterReady chan *snap.Snapshotter // signals when snapshotter is ready
snapCount uint64
transport *rafthttp.Transport
stopc chan struct{} // signals proposal channel closed
httpstopc chan struct{} // signals http server to shutdown
httpdonec chan struct{} // signals http server shutdown complete
logger *zap.Logger
}
var defaultSnapshotCount uint64 = 10000
// newRaftNode initiates a raft instance and returns a committed log entry
// channel and error channel. Proposals for log updates are sent over the
// provided the proposal channel. All log entries are replayed over the
// commit channel, followed by a nil message (to indicate the channel is
// current), then new log entries. To shutdown, close proposeC and read errorC.
func newRaftNode(id int, peers []string, join bool, getSnapshot func() ([]byte, error), proposeC <-chan string,
confChangeC <-chan raftpb.ConfChange,
) (<-chan *commit, <-chan error, <-chan *snap.Snapshotter) {
commitC := make(chan *commit)
errorC := make(chan error)
rc := &raftNode{
proposeC: proposeC,
confChangeC: confChangeC,
commitC: commitC,
errorC: errorC,
id: id,
peers: peers,
join: join,
waldir: fmt.Sprintf("raftexample-%d", id),
snapdir: fmt.Sprintf("raftexample-%d-snap", id),
getSnapshot: getSnapshot,
snapCount: defaultSnapshotCount,
stopc: make(chan struct{}),
httpstopc: make(chan struct{}),
httpdonec: make(chan struct{}),
logger: zap.NewExample(),
snapshotterReady: make(chan *snap.Snapshotter, 1),
// rest of structure populated after WAL replay
}
go rc.startRaft()
return commitC, errorC, rc.snapshotterReady
}
func (rc *raftNode) saveSnap(snap raftpb.Snapshot) error {
walSnap := walpb.Snapshot{
Index: new(snap.Metadata.Index),
Term: new(snap.Metadata.Term),
ConfState: &snap.Metadata.ConfState,
}
// save the snapshot file before writing the snapshot to the wal.
// This makes it possible for the snapshot file to become orphaned, but prevents
// a WAL snapshot entry from having no corresponding snapshot file.
if err := rc.snapshotter.SaveSnap(snap); err != nil {
return err
}
if err := rc.wal.SaveSnapshot(walSnap); err != nil {
return err
}
return rc.wal.ReleaseLockTo(snap.Metadata.Index)
}
func (rc *raftNode) entriesToApply(ents []raftpb.Entry) (nents []raftpb.Entry) {
if len(ents) == 0 {
return ents
}
firstIdx := ents[0].Index
if firstIdx > rc.appliedIndex+1 {
log.Fatalf("first index of committed entry[%d] should <= progress.appliedIndex[%d]+1", firstIdx, rc.appliedIndex)
}
if rc.appliedIndex-firstIdx+1 < uint64(len(ents)) {
nents = ents[rc.appliedIndex-firstIdx+1:]
}
return nents
}
// publishEntries writes committed log entries to commit channel and returns
// whether all entries could be published.
func (rc *raftNode) publishEntries(ents []raftpb.Entry) (<-chan struct{}, bool) {
if len(ents) == 0 {
return nil, true
}
data := make([]string, 0, len(ents))
for i := range ents {
switch ents[i].Type {
case raftpb.EntryNormal:
if len(ents[i].Data) == 0 {
// ignore empty messages
break
}
s := string(ents[i].Data)
data = append(data, s)
case raftpb.EntryConfChange:
var cc raftpb.ConfChange
cc.Unmarshal(ents[i].Data)
rc.confState = *rc.node.ApplyConfChange(cc)
switch cc.Type {
case raftpb.ConfChangeAddNode:
if len(cc.Context) > 0 {
rc.transport.AddPeer(types.ID(cc.NodeID), []string{string(cc.Context)})
}
case raftpb.ConfChangeRemoveNode:
if cc.NodeID == uint64(rc.id) {
log.Println("I've been removed from the cluster! Shutting down.")
return nil, false
}
rc.transport.RemovePeer(types.ID(cc.NodeID))
}
}
}
var applyDoneC chan struct{}
if len(data) > 0 {
applyDoneC = make(chan struct{}, 1)
select {
case rc.commitC <- &commit{data, applyDoneC}:
case <-rc.stopc:
return nil, false
}
}
// after commit, update appliedIndex
rc.appliedIndex = ents[len(ents)-1].Index
return applyDoneC, true
}
func (rc *raftNode) loadSnapshot() *raftpb.Snapshot {
if wal.Exist(rc.waldir) {
walSnaps, err := wal.ValidSnapshotEntries(rc.logger, rc.waldir)
if err != nil {
log.Fatalf("raftexample: error listing snapshots (%v)", err)
}
snapshot, err := rc.snapshotter.LoadNewestAvailable(walSnaps)
if err != nil && !errors.Is(err, snap.ErrNoSnapshot) {
log.Fatalf("raftexample: error loading snapshot (%v)", err)
}
return snapshot
}
return &raftpb.Snapshot{}
}
// openWAL returns a WAL ready for reading.
func (rc *raftNode) openWAL(snapshot *raftpb.Snapshot) *wal.WAL {
if !wal.Exist(rc.waldir) {
if err := os.Mkdir(rc.waldir, 0o750); err != nil {
log.Fatalf("raftexample: cannot create dir for wal (%v)", err)
}
w, err := wal.Create(zap.NewExample(), rc.waldir, nil)
if err != nil {
log.Fatalf("raftexample: create wal error (%v)", err)
}
w.Close()
}
walsnap := walpb.Snapshot{}
if snapshot != nil {
walsnap.Index, walsnap.Term = new(snapshot.Metadata.Index), new(snapshot.Metadata.Term)
}
log.Printf("loading WAL at term %d and index %d", walsnap.GetTerm(), walsnap.GetIndex())
w, err := wal.Open(zap.NewExample(), rc.waldir, walsnap)
if err != nil {
log.Fatalf("raftexample: error loading wal (%v)", err)
}
return w
}
// replayWAL replays WAL entries into the raft instance.
func (rc *raftNode) replayWAL() *wal.WAL {
log.Printf("replaying WAL of member %d", rc.id)
snapshot := rc.loadSnapshot()
w := rc.openWAL(snapshot)
_, st, ents, err := w.ReadAll()
if err != nil {
log.Fatalf("raftexample: failed to read WAL (%v)", err)
}
rc.raftStorage = raft.NewMemoryStorage()
if snapshot != nil {
rc.raftStorage.ApplySnapshot(*snapshot)
}
rc.raftStorage.SetHardState(st)
// append to storage so raft starts at the right place in log
rc.raftStorage.Append(ents)
return w
}
func (rc *raftNode) writeError(err error) {
rc.stopHTTP()
close(rc.commitC)
rc.errorC <- err
close(rc.errorC)
rc.node.Stop()
}
func (rc *raftNode) startRaft() {
if !fileutil.Exist(rc.snapdir) {
if err := os.Mkdir(rc.snapdir, 0o750); err != nil {
log.Fatalf("raftexample: cannot create dir for snapshot (%v)", err)
}
}
rc.snapshotter = snap.New(zap.NewExample(), rc.snapdir)
oldwal := wal.Exist(rc.waldir)
rc.wal = rc.replayWAL()
// signal replay has finished
rc.snapshotterReady <- rc.snapshotter
rpeers := make([]raft.Peer, len(rc.peers))
for i := range rpeers {
rpeers[i] = raft.Peer{ID: uint64(i + 1)}
}
c := &raft.Config{
ID: uint64(rc.id),
ElectionTick: 10,
HeartbeatTick: 1,
Storage: rc.raftStorage,
MaxSizePerMsg: 1024 * 1024,
MaxInflightMsgs: 256,
MaxUncommittedEntriesSize: 1 << 30,
}
if oldwal || rc.join {
rc.node = raft.RestartNode(c)
} else {
rc.node = raft.StartNode(c, rpeers)
}
rc.transport = &rafthttp.Transport{
Logger: rc.logger,
ID: types.ID(rc.id),
ClusterID: 0x1000,
Raft: rc,
ServerStats: stats.NewServerStats("", ""),
LeaderStats: stats.NewLeaderStats(zap.NewExample(), strconv.Itoa(rc.id)),
ErrorC: make(chan error),
}
rc.transport.Start()
for i := range rc.peers {
if i+1 != rc.id {
rc.transport.AddPeer(types.ID(i+1), []string{rc.peers[i]})
}
}
go rc.serveRaft()
go rc.serveChannels()
}
// stop closes http, closes all channels, and stops raft.
func (rc *raftNode) stop() {
rc.stopHTTP()
close(rc.commitC)
close(rc.errorC)
rc.node.Stop()
}
func (rc *raftNode) stopHTTP() {
rc.transport.Stop()
close(rc.httpstopc)
<-rc.httpdonec
}
func (rc *raftNode) publishSnapshot(snapshotToSave raftpb.Snapshot) {
if raft.IsEmptySnap(snapshotToSave) {
return
}
log.Printf("publishing snapshot at index %d", rc.snapshotIndex)
defer log.Printf("finished publishing snapshot at index %d", rc.snapshotIndex)
if snapshotToSave.Metadata.Index <= rc.appliedIndex {
log.Fatalf("snapshot index [%d] should > progress.appliedIndex [%d]", snapshotToSave.Metadata.Index, rc.appliedIndex)
}
rc.commitC <- nil // trigger kvstore to load snapshot
rc.confState = snapshotToSave.Metadata.ConfState
rc.snapshotIndex = snapshotToSave.Metadata.Index
rc.appliedIndex = snapshotToSave.Metadata.Index
}
var snapshotCatchUpEntriesN uint64 = 10000
func (rc *raftNode) maybeTriggerSnapshot(applyDoneC <-chan struct{}) {
if rc.appliedIndex-rc.snapshotIndex <= rc.snapCount {
return
}
// wait until all committed entries are applied (or server is closed)
if applyDoneC != nil {
select {
case <-applyDoneC:
case <-rc.stopc:
return
}
}
log.Printf("start snapshot [applied index: %d | last snapshot index: %d]", rc.appliedIndex, rc.snapshotIndex)
data, err := rc.getSnapshot()
if err != nil {
log.Panic(err)
}
snap, err := rc.raftStorage.CreateSnapshot(rc.appliedIndex, &rc.confState, data)
if err != nil {
panic(err)
}
if err := rc.saveSnap(snap); err != nil {
panic(err)
}
compactIndex := uint64(1)
if rc.appliedIndex > snapshotCatchUpEntriesN {
compactIndex = rc.appliedIndex - snapshotCatchUpEntriesN
}
if err := rc.raftStorage.Compact(compactIndex); err != nil {
if !errors.Is(err, raft.ErrCompacted) {
panic(err)
}
} else {
log.Printf("compacted log at index %d", compactIndex)
}
rc.snapshotIndex = rc.appliedIndex
}
func (rc *raftNode) serveChannels() {
snap, err := rc.raftStorage.Snapshot()
if err != nil {
panic(err)
}
rc.confState = snap.Metadata.ConfState
rc.snapshotIndex = snap.Metadata.Index
rc.appliedIndex = snap.Metadata.Index
defer rc.wal.Close()
ticker := time.NewTicker(100 * time.Millisecond)
defer ticker.Stop()
// send proposals over raft
go func() {
confChangeCount := uint64(0)
for rc.proposeC != nil && rc.confChangeC != nil {
select {
case prop, ok := <-rc.proposeC:
if !ok {
rc.proposeC = nil
} else {
// blocks until accepted by raft state machine
rc.node.Propose(context.TODO(), []byte(prop))
}
case cc, ok := <-rc.confChangeC:
if !ok {
rc.confChangeC = nil
} else {
confChangeCount++
cc.ID = confChangeCount
rc.node.ProposeConfChange(context.TODO(), cc)
}
}
}
// client closed channel; shutdown raft if not already
close(rc.stopc)
}()
// event loop on raft state machine updates
for {
select {
case <-ticker.C:
rc.node.Tick()
// store raft entries to wal, then publish over commit channel
case rd := <-rc.node.Ready():
// Must save the snapshot file and WAL snapshot entry before saving any other entries
// or hardstate to ensure that recovery after a snapshot restore is possible.
if !raft.IsEmptySnap(rd.Snapshot) {
rc.saveSnap(rd.Snapshot)
}
rc.wal.Save(rd.HardState, rd.Entries)
if !raft.IsEmptySnap(rd.Snapshot) {
rc.raftStorage.ApplySnapshot(rd.Snapshot)
rc.publishSnapshot(rd.Snapshot)
}
rc.raftStorage.Append(rd.Entries)
rc.transport.Send(rc.processMessages(rd.Messages))
applyDoneC, ok := rc.publishEntries(rc.entriesToApply(rd.CommittedEntries))
if !ok {
rc.stop()
return
}
rc.maybeTriggerSnapshot(applyDoneC)
rc.node.Advance()
case err := <-rc.transport.ErrorC:
rc.writeError(err)
return
case <-rc.stopc:
rc.stop()
return
}
}
}
// When there is a `raftpb.EntryConfChange` after creating the snapshot,
// then the confState included in the snapshot is out of date. so We need
// to update the confState before sending a snapshot to a follower.
func (rc *raftNode) processMessages(ms []raftpb.Message) []raftpb.Message {
for i := 0; i < len(ms); i++ {
if ms[i].Type == raftpb.MsgSnap {
ms[i].Snapshot.Metadata.ConfState = rc.confState
}
}
return ms
}
func (rc *raftNode) serveRaft() {
url, err := url.Parse(rc.peers[rc.id-1])
if err != nil {
log.Fatalf("raftexample: Failed parsing URL (%v)", err)
}
ln, err := newStoppableListener(url.Host, rc.httpstopc)
if err != nil {
log.Fatalf("raftexample: Failed to listen rafthttp (%v)", err)
}
err = (&http.Server{Handler: rc.transport.Handler()}).Serve(ln)
select {
case <-rc.httpstopc:
default:
log.Fatalf("raftexample: Failed to serve rafthttp (%v)", err)
}
close(rc.httpdonec)
}
func (rc *raftNode) Process(ctx context.Context, m raftpb.Message) error {
return rc.node.Step(ctx, m)
}
func (rc *raftNode) IsIDRemoved(_ uint64) bool { return false }
func (rc *raftNode) ReportUnreachable(id uint64) { rc.node.ReportUnreachable(id) }
func (rc *raftNode) ReportSnapshot(id uint64, status raft.SnapshotStatus) {
rc.node.ReportSnapshot(id, status)
}
================================================
FILE: contrib/raftexample/raft_test.go
================================================
// Copyright 2022 The etcd Authors
//
// 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 main
import (
"reflect"
"testing"
"github.com/stretchr/testify/require"
"go.etcd.io/raft/v3/raftpb"
)
func TestProcessMessages(t *testing.T) {
cases := []struct {
name string
confState raftpb.ConfState
InputMessages []raftpb.Message
ExpectedMessages []raftpb.Message
}{
{
name: "only one snapshot message",
confState: raftpb.ConfState{
Voters: []uint64{2, 6, 8, 10},
},
InputMessages: []raftpb.Message{
{
Type: raftpb.MsgSnap,
To: 8,
Snapshot: &raftpb.Snapshot{
Metadata: raftpb.SnapshotMetadata{
Index: 100,
Term: 3,
ConfState: raftpb.ConfState{
Voters: []uint64{2, 6, 8},
AutoLeave: true,
},
},
},
},
},
ExpectedMessages: []raftpb.Message{
{
Type: raftpb.MsgSnap,
To: 8,
Snapshot: &raftpb.Snapshot{
Metadata: raftpb.SnapshotMetadata{
Index: 100,
Term: 3,
ConfState: raftpb.ConfState{
Voters: []uint64{2, 6, 8, 10},
},
},
},
},
},
},
{
name: "one snapshot message and one other message",
confState: raftpb.ConfState{
Voters: []uint64{2, 7, 8, 12},
},
InputMessages: []raftpb.Message{
{
Type: raftpb.MsgSnap,
To: 8,
Snapshot: &raftpb.Snapshot{
Metadata: raftpb.SnapshotMetadata{
Index: 100,
Term: 3,
ConfState: raftpb.ConfState{
Voters: []uint64{2, 6, 8},
AutoLeave: true,
},
},
},
},
{
Type: raftpb.MsgApp,
From: 6,
To: 8,
},
},
ExpectedMessages: []raftpb.Message{
{
Type: raftpb.MsgSnap,
To: 8,
Snapshot: &raftpb.Snapshot{
Metadata: raftpb.SnapshotMetadata{
Index: 100,
Term: 3,
ConfState: raftpb.ConfState{
Voters: []uint64{2, 7, 8, 12},
},
},
},
},
{
Type: raftpb.MsgApp,
From: 6,
To: 8,
},
},
},
}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
rn := &raftNode{
confState: tc.confState,
}
outputMessages := rn.processMessages(tc.InputMessages)
require.Truef(t, reflect.DeepEqual(outputMessages, tc.ExpectedMessages), "Unexpected messages, expected: %v, got %v", tc.ExpectedMessages, outputMessages)
})
}
}
================================================
FILE: contrib/raftexample/raftexample_test.go
================================================
// Copyright 2015 The etcd Authors
//
// 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 main
import (
"bytes"
"fmt"
"io"
"net/http"
"net/http/httptest"
"os"
"sync"
"testing"
"time"
"github.com/stretchr/testify/require"
"go.etcd.io/raft/v3/raftpb"
)
func getSnapshotFn() (func() ([]byte, error), <-chan struct{}) {
snapshotTriggeredC := make(chan struct{})
return func() ([]byte, error) {
snapshotTriggeredC <- struct{}{}
return nil, nil
}, snapshotTriggeredC
}
type cluster struct {
peers []string
commitC []<-chan *commit
errorC []<-chan error
proposeC []chan string
confChangeC []chan raftpb.ConfChange
snapshotTriggeredC []<-chan struct{}
}
// newCluster creates a cluster of n nodes
func newCluster(n int) *cluster {
peers := make([]string, n)
for i := range peers {
peers[i] = fmt.Sprintf("http://127.0.0.1:%d", 10000+i)
}
clus := &cluster{
peers: peers,
commitC: make([]<-chan *commit, len(peers)),
errorC: make([]<-chan error, len(peers)),
proposeC: make([]chan string, len(peers)),
confChangeC: make([]chan raftpb.ConfChange, len(peers)),
snapshotTriggeredC: make([]<-chan struct{}, len(peers)),
}
for i := range clus.peers {
os.RemoveAll(fmt.Sprintf("raftexample-%d", i+1))
os.RemoveAll(fmt.Sprintf("raftexample-%d-snap", i+1))
clus.proposeC[i] = make(chan string, 1)
clus.confChangeC[i] = make(chan raftpb.ConfChange, 1)
fn, snapshotTriggeredC := getSnapshotFn()
clus.snapshotTriggeredC[i] = snapshotTriggeredC
clus.commitC[i], clus.errorC[i], _ = newRaftNode(i+1, clus.peers, false, fn, clus.proposeC[i], clus.confChangeC[i])
}
return clus
}
// Close closes all cluster nodes and returns an error if any failed.
func (clus *cluster) Close() (err error) {
for i := range clus.peers {
go func(i int) {
for range clus.commitC[i] { //revive:disable-line:empty-block
// drain pending commits
}
}(i)
close(clus.proposeC[i])
// wait for channel to close
if erri := <-clus.errorC[i]; erri != nil {
err = erri
}
// clean intermediates
os.RemoveAll(fmt.Sprintf("raftexample-%d", i+1))
os.RemoveAll(fmt.Sprintf("raftexample-%d-snap", i+1))
}
return err
}
func (clus *cluster) closeNoErrors(t *testing.T) {
t.Log("closing cluster...")
err := clus.Close()
require.NoError(t, err)
t.Log("closing cluster [done]")
}
// TestProposeOnCommit starts three nodes and feeds commits back into the proposal
// channel. The intent is to ensure blocking on a proposal won't block raft progress.
func TestProposeOnCommit(t *testing.T) {
clus := newCluster(3)
defer clus.closeNoErrors(t)
donec := make(chan struct{})
for i := range clus.peers {
// feedback for "n" committed entries, then update donec
go func(pC chan<- string, cC <-chan *commit, eC <-chan error) {
for n := 0; n < 100; n++ {
c, ok := <-cC
if !ok {
pC = nil
}
select {
case pC <- c.data[0]:
continue
case err := <-eC:
t.Errorf("eC message (%v)", err)
}
}
donec <- struct{}{}
for range cC { //revive:disable-line:empty-block
// acknowledge the commits from other nodes so
// raft continues to make progress
}
}(clus.proposeC[i], clus.commitC[i], clus.errorC[i])
// one message feedback per node
go func(i int) { clus.proposeC[i] <- "foo" }(i)
}
for range clus.peers {
<-donec
}
}
// TestCloseProposerBeforeReplay tests closing the producer before raft starts.
func TestCloseProposerBeforeReplay(t *testing.T) {
clus := newCluster(1)
// close before replay so raft never starts
defer clus.closeNoErrors(t)
}
// TestCloseProposerInflight tests closing the producer while
// committed messages are being published to the client.
func TestCloseProposerInflight(t *testing.T) {
clus := newCluster(1)
defer clus.closeNoErrors(t)
var wg sync.WaitGroup
// some inflight ops
wg.Go(func() {
clus.proposeC[0] <- "foo"
clus.proposeC[0] <- "bar"
})
// wait for one message
if c, ok := <-clus.commitC[0]; !ok || c.data[0] != "foo" {
t.Fatalf("Commit failed")
}
wg.Wait()
}
func TestPutAndGetKeyValue(t *testing.T) {
clusters := []string{"http://127.0.0.1:9021"}
proposeC := make(chan string)
defer close(proposeC)
confChangeC := make(chan raftpb.ConfChange)
defer close(confChangeC)
var kvs *kvstore
getSnapshot := func() ([]byte, error) { return kvs.getSnapshot() }
commitC, errorC, snapshotterReady := newRaftNode(1, clusters, false, getSnapshot, proposeC, confChangeC)
kvs = newKVStore(<-snapshotterReady, proposeC, commitC, errorC)
srv := httptest.NewServer(&httpKVAPI{
store: kvs,
confChangeC: confChangeC,
})
defer srv.Close()
// wait server started
<-time.After(time.Second * 3)
wantKey, wantValue := "test-key", "test-value"
url := fmt.Sprintf("%s/%s", srv.URL, wantKey)
body := bytes.NewBufferString(wantValue)
cli := srv.Client()
req, err := http.NewRequest(http.MethodPut, url, body)
require.NoError(t, err)
req.Header.Set("Content-Type", "text/html; charset=utf-8")
_, err = cli.Do(req)
require.NoError(t, err)
// wait for a moment for processing message, otherwise get would be failed.
<-time.After(time.Second)
resp, err := cli.Get(url)
require.NoError(t, err)
data, err := io.ReadAll(resp.Body)
require.NoError(t, err)
defer resp.Body.Close()
gotValue := string(data)
require.Equalf(t, wantValue, gotValue, "expect %s, got %s", wantValue, gotValue)
}
// TestAddNewNode tests adding new node to the existing cluster.
func TestAddNewNode(t *testing.T) {
clus := newCluster(3)
defer clus.closeNoErrors(t)
os.RemoveAll("raftexample-4")
os.RemoveAll("raftexample-4-snap")
defer func() {
os.RemoveAll("raftexample-4")
os.RemoveAll("raftexample-4-snap")
}()
newNodeURL := "http://127.0.0.1:10004"
clus.confChangeC[0] <- raftpb.ConfChange{
Type: raftpb.ConfChangeAddNode,
NodeID: 4,
Context: []byte(newNodeURL),
}
proposeC := make(chan string)
defer close(proposeC)
confChangeC := make(chan raftpb.ConfChange)
defer close(confChangeC)
newRaftNode(4, append(clus.peers, newNodeURL), true, nil, proposeC, confChangeC)
go func() {
proposeC <- "foo"
}()
if c, ok := <-clus.commitC[0]; !ok || c.data[0] != "foo" {
t.Fatalf("Commit failed")
}
}
func TestSnapshot(t *testing.T) {
prevDefaultSnapshotCount := defaultSnapshotCount
prevSnapshotCatchUpEntriesN := snapshotCatchUpEntriesN
defaultSnapshotCount = 4
snapshotCatchUpEntriesN = 4
defer func() {
defaultSnapshotCount = prevDefaultSnapshotCount
snapshotCatchUpEntriesN = prevSnapshotCatchUpEntriesN
}()
clus := newCluster(3)
defer clus.closeNoErrors(t)
go func() {
clus.proposeC[0] <- "foo"
}()
c := <-clus.commitC[0]
select {
case <-clus.snapshotTriggeredC[0]:
t.Fatalf("snapshot triggered before applying done")
default:
}
close(c.applyDoneC)
<-clus.snapshotTriggeredC[0]
}
================================================
FILE: contrib/systemd/etcd.service
================================================
[Unit]
Description=etcd key-value store
Documentation=https://github.com/etcd-io/etcd
After=network-online.target local-fs.target remote-fs.target time-sync.target
Wants=network-online.target local-fs.target remote-fs.target time-sync.target
[Service]
User=etcd
Type=notify
Environment=ETCD_DATA_DIR=/var/lib/etcd
Environment=ETCD_NAME=%m
ExecStart=/usr/bin/etcd
Restart=always
RestartSec=10s
LimitNOFILE=40000
[Install]
WantedBy=multi-user.target
================================================
FILE: contrib/systemd/etcd3-multinode/README.md
================================================
# etcd3 multi-node cluster
Here's how to deploy etcd cluster with systemd.
## Set up data directory
etcd needs data directory on host machine. Configure the data directory accessible to systemd as:
```
sudo mkdir -p /var/lib/etcd
sudo chown -R root:$(whoami) /var/lib/etcd
sudo chmod -R a+rw /var/lib/etcd
```
## Write systemd service file
In each machine, write etcd systemd service files:
```
cat > /tmp/my-etcd-1.service < /tmp/my-etcd-2.service < /tmp/my-etcd-3.service <
etcdctl
========
`etcdctl` is a command line client for [etcd][etcd].
The v3 API is used by default on main branch. For the v2 API, make sure to set environment variable `ETCDCTL_API=2`. See also [READMEv2][READMEv2].
If using released versions earlier than v3.4, set `ETCDCTL_API=3` to use v3 API.
Global flags (e.g., `dial-timeout`, `--cacert`, `--cert`, `--key`) can be set with environment variables:
```
ETCDCTL_DIAL_TIMEOUT=3s
ETCDCTL_CACERT=/tmp/ca.pem
ETCDCTL_CERT=/tmp/cert.pem
ETCDCTL_KEY=/tmp/key.pem
```
Prefix flag strings with `ETCDCTL_`, convert all letters to upper-case, and replace dash(`-`) with underscore(`_`). Note that the environment variables with the prefix `ETCDCTL_` can only be used with the etcdctl global flags. Also, the environment variable `ETCDCTL_API` is a special case variable for etcdctl internal use only.
## Key-value commands
### PUT [options] \ \
PUT assigns the specified value with the specified key. If key already holds a value, it is overwritten.
RPC: Put
#### Options
- lease -- lease ID (in hexadecimal) to attach to the key.
- prev-kv -- return the previous key-value pair before modification.
- ignore-value -- updates the key using its current value.
- ignore-lease -- updates the key using its current lease.
#### Output
`OK`
#### Examples
```bash
./etcdctl put foo bar --lease=1234abcd
# OK
./etcdctl get foo
# foo
# bar
./etcdctl put foo --ignore-value # to detache lease
# OK
```
```bash
./etcdctl put foo bar --lease=1234abcd
# OK
./etcdctl put foo bar1 --ignore-lease # to use existing lease 1234abcd
# OK
./etcdctl get foo
# foo
# bar1
```
```bash
./etcdctl put foo bar1 --prev-kv
# OK
# foo
# bar
./etcdctl get foo
# foo
# bar1
```
#### Remarks
If \ isn't given as command line argument, this command tries to read the value from standard input.
When \ begins with '-', \ is interpreted as a flag.
Insert '--' for workaround:
```bash
./etcdctl put --
./etcdctl put --
```
Providing \ in a new line after using `carriage return` is not supported and etcdctl may hang in that case. For example, following case is not supported:
```bash
./etcdctl put \r
```
A \ can have multiple lines or spaces but it must be provided with a double-quote as demonstrated below:
```bash
./etcdctl put foo "bar1 2 3"
```
### GET [options] \ [range_end]
GET gets the key or a range of keys [key, range_end) if range_end is given.
RPC: Range
#### Options
- hex -- print out key and value as hex encode string
- limit -- maximum number of results
- prefix -- get keys by matching prefix
- order -- order of results; ASCEND or DESCEND
- sort-by -- sort target; CREATE, KEY, MODIFY, VALUE, or VERSION
- rev -- specify the kv revision
- print-value-only -- print only value when used with write-out=simple
- consistency -- Linearizable(l) or Serializable(s), defaults to Linearizable(l).
- from-key -- Get keys that are greater than or equal to the given key using byte compare
- keys-only -- Get only the keys
- max-create-revision -- restrict results to kvs with create revision lower or equal than the supplied revision
- min-create-revision -- restrict results to kvs with create revision greater or equal than the supplied revision
- max-mod-revision -- restrict results to kvs with modified revision lower or equal than the supplied revision
- min-mod-revision -- restrict results to kvs with modified revision greater or equal than the supplied revision
#### Output
Prints the data in format below,
```
\\n\\n\\n\...
```
Note serializable requests are better for lower latency requirement, but
stale data might be returned if serializable option (`--consistency=s`)
is specified.
#### Examples
First, populate etcd with some keys:
```bash
./etcdctl put foo bar
# OK
./etcdctl put foo1 bar1
# OK
./etcdctl put foo2 bar2
# OK
./etcdctl put foo3 bar3
# OK
```
Get the key named `foo`:
```bash
./etcdctl get foo
# foo
# bar
```
Get all keys:
```bash
./etcdctl get --from-key ''
# foo
# bar
# foo1
# bar1
# foo2
# foo2
# foo3
# bar3
```
Get all keys with names greater than or equal to `foo1`:
```bash
./etcdctl get --from-key foo1
# foo1
# bar1
# foo2
# bar2
# foo3
# bar3
```
Get keys with names greater than or equal to `foo1` and less than `foo3`:
```bash
./etcdctl get foo1 foo3
# foo1
# bar1
# foo2
# bar2
```
#### Remarks
If any key or value contains non-printable characters or control characters, simple formatted output can be ambiguous due to new lines. To resolve this issue, set `--hex` to hex encode all strings.
### DEL [options] \ [range_end]
Removes the specified key or range of keys [key, range_end) if range_end is given.
RPC: DeleteRange
#### Options
- prefix -- delete keys by matching prefix
- prev-kv -- return deleted key-value pairs
- from-key -- delete keys that are greater than or equal to the given key using byte compare
#### Output
Prints the number of keys that were removed in decimal if DEL succeeded.
#### Examples
```bash
./etcdctl put foo bar
# OK
./etcdctl del foo
# 1
./etcdctl get foo
```
```bash
./etcdctl put key val
# OK
./etcdctl del --prev-kv key
# 1
# key
# val
./etcdctl get key
```
```bash
./etcdctl put a 123
# OK
./etcdctl put b 456
# OK
./etcdctl put z 789
# OK
./etcdctl del --from-key a
# 3
./etcdctl get --from-key a
```
```bash
./etcdctl put zoo val
# OK
./etcdctl put zoo1 val1
# OK
./etcdctl put zoo2 val2
# OK
./etcdctl del --prefix zoo
# 3
./etcdctl get zoo2
```
### TXN [options]
TXN reads multiple etcd requests from standard input and applies them as a single atomic transaction.
A transaction consists of list of conditions, a list of requests to apply if all the conditions are true, and a list of requests to apply if any condition is false.
RPC: Txn
#### Options
- hex -- print out keys and values as hex encoded strings.
- interactive -- input transaction with interactive prompting.
#### Input Format
```ebnf
::= * "\n" "\n" "\n"
::= (||||) "\n"
::= "<" | "=" | ">"
:= ("c"|"create")"("")" ::= ("m"|"mod")"("")" ::= ("val"|"value")"("")" ::= ("ver"|"version")"("")" ::= "lease("")" ::= *
::= *
::= ((see put, get, del etcdctl command syntax)) "\n"
::= (%q formatted string)
::= (%q formatted string)
::= "\""[0-9]+"\""
::= "\""[0-9]+"\""
::= "\""[0-9]+\""
```
#### Output
`SUCCESS` if etcd processed the transaction success list, `FAILURE` if etcd processed the transaction failure list. Prints the output for each command in the executed request list, each separated by a blank line.
#### Examples
txn in interactive mode:
```bash
./etcdctl txn -i
# compares:
mod("key1") > "0"
# success requests (get, put, delete):
put key1 "overwrote-key1"
# failure requests (get, put, delete):
put key1 "created-key1"
put key2 "some extra key"
# FAILURE
# OK
# OK
```
txn in non-interactive mode:
```bash
./etcdctl txn <<<'mod("key1") > "0"
put key1 "overwrote-key1"
put key1 "created-key1"
put key2 "some extra key"
'
# FAILURE
# OK
# OK
```
#### Remarks
When using multi-line values within a TXN command, newlines must be represented as `\n`. Literal newlines will cause parsing failures. This differs from other commands (such as PUT) where the shell will convert literal newlines for us. For example:
```bash
./etcdctl txn <<<'mod("key1") > "0"
put key1 "overwrote-key1"
put key1 "created-key1"
put key2 "this is\na multi-line\nvalue"
'
# FAILURE
# OK
# OK
```
### COMPACTION [options] \
COMPACTION discards all etcd event history prior to a given revision. Since etcd uses a multiversion concurrency control
model, it preserves all key updates as event history. When the event history up to some revision is no longer needed,
all superseded keys may be compacted away to reclaim storage space in the etcd backend database.
RPC: Compact
#### Options
- physical -- 'true' to wait for compaction to physically remove all old revisions
#### Output
Prints the compacted revision.
#### Example
```bash
./etcdctl compaction 1234
# compacted revision 1234
```
### WATCH [options] [key or prefix] [range_end] [--] [exec-command arg1 arg2 ...]
Watch watches events stream on keys or prefixes, [key or prefix, range_end) if range_end is given. The watch command runs until it encounters an error or is terminated by the user. If range_end is given, it must be lexicographically greater than key or "\x00".
RPC: Watch
#### Options
- hex -- print out key and value as hex encode string
- interactive -- begins an interactive watch session
- prefix -- watch on a prefix if prefix is set.
- prev-kv -- get the previous key-value pair before the event happens.
- rev -- the revision to start watching. Specifying a revision is useful for observing past events.
#### Input format
Input is only accepted for interactive mode.
```
watch [options] \n
```
#### Output
\[\n\\n\]\n\\n\\n\\n\\n\\n...
#### Examples
##### Non-interactive
```bash
./etcdctl watch foo
# PUT
# foo
# bar
```
```bash
ETCDCTL_WATCH_KEY=foo ./etcdctl watch
# PUT
# foo
# bar
```
Receive events and execute `echo watch event received`:
```bash
./etcdctl watch foo -- echo watch event received
# PUT
# foo
# bar
# watch event received
```
Watch response is set via `ETCD_WATCH_*` environmental variables:
```bash
./etcdctl watch foo -- sh -c "env | grep ETCD_WATCH_"
# PUT
# foo
# bar
# ETCD_WATCH_REVISION=11
# ETCD_WATCH_KEY="foo"
# ETCD_WATCH_EVENT_TYPE="PUT"
# ETCD_WATCH_VALUE="bar"
```
Watch with environmental variables and execute `echo watch event received`:
```bash
export ETCDCTL_WATCH_KEY=foo
./etcdctl watch -- echo watch event received
# PUT
# foo
# bar
# watch event received
```
```bash
export ETCDCTL_WATCH_KEY=foo
export ETCDCTL_WATCH_RANGE_END=foox
./etcdctl watch -- echo watch event received
# PUT
# fob
# bar
# watch event received
```
##### Interactive
```bash
./etcdctl watch -i
watch foo
watch foo
# PUT
# foo
# bar
# PUT
# foo
# bar
```
Receive events and execute `echo watch event received`:
```bash
./etcdctl watch -i
watch foo -- echo watch event received
# PUT
# foo
# bar
# watch event received
```
Watch with environmental variables and execute `echo watch event received`:
```bash
export ETCDCTL_WATCH_KEY=foo
./etcdctl watch -i
watch -- echo watch event received
# PUT
# foo
# bar
# watch event received
```
```bash
export ETCDCTL_WATCH_KEY=foo
export ETCDCTL_WATCH_RANGE_END=foox
./etcdctl watch -i
watch -- echo watch event received
# PUT
# fob
# bar
# watch event received
```
### LEASE \
LEASE provides commands for key lease management.
### LEASE GRANT \
LEASE GRANT creates a fresh lease with a server-selected time-to-live in seconds
greater than or equal to the requested TTL value.
RPC: LeaseGrant
#### Output
Prints a message with the granted lease ID.
#### Example
```bash
./etcdctl lease grant 60
# lease 32695410dcc0ca06 granted with TTL(60s)
```
### LEASE REVOKE \
LEASE REVOKE destroys a given lease, deleting all attached keys.
RPC: LeaseRevoke
#### Output
Prints a message indicating the lease is revoked.
#### Example
```bash
./etcdctl lease revoke 32695410dcc0ca06
# lease 32695410dcc0ca06 revoked
```
### LEASE TIMETOLIVE \ [options]
LEASE TIMETOLIVE retrieves the lease information with the given lease ID.
RPC: LeaseTimeToLive
#### Options
- keys -- Get keys attached to this lease
#### Output
Prints lease information.
#### Example
```bash
./etcdctl lease grant 500
# lease 2d8257079fa1bc0c granted with TTL(500s)
./etcdctl put foo1 bar --lease=2d8257079fa1bc0c
# OK
./etcdctl put foo2 bar --lease=2d8257079fa1bc0c
# OK
./etcdctl lease timetolive 2d8257079fa1bc0c
# lease 2d8257079fa1bc0c granted with TTL(500s), remaining(481s)
./etcdctl lease timetolive 2d8257079fa1bc0c --keys
# lease 2d8257079fa1bc0c granted with TTL(500s), remaining(472s), attached keys([foo2 foo1])
./etcdctl lease timetolive 2d8257079fa1bc0c --write-out=json
# {"cluster_id":17186838941855831277,"member_id":4845372305070271874,"revision":3,"raft_term":2,"id":3279279168933706764,"ttl":465,"granted-ttl":500,"keys":null}
./etcdctl lease timetolive 2d8257079fa1bc0c --write-out=json --keys
# {"cluster_id":17186838941855831277,"member_id":4845372305070271874,"revision":3,"raft_term":2,"id":3279279168933706764,"ttl":459,"granted-ttl":500,"keys":["Zm9vMQ==","Zm9vMg=="]}
./etcdctl lease timetolive 2d8257079fa1bc0c
# lease 2d8257079fa1bc0c already expired
```
### LEASE LIST
LEASE LIST lists all active leases.
RPC: LeaseLeases
#### Output
Prints a message with a list of active leases.
#### Example
```bash
./etcdctl lease grant 60
# lease 32695410dcc0ca06 granted with TTL(60s)
./etcdctl lease list
32695410dcc0ca06
```
### LEASE KEEP-ALIVE \
LEASE KEEP-ALIVE periodically refreshes a lease so it does not expire.
RPC: LeaseKeepAlive
#### Output
Prints a message for every keep alive sent or prints a message indicating the lease is gone.
#### Example
```bash
./etcdctl lease keep-alive 32695410dcc0ca0
# lease 32695410dcc0ca0 keepalived with TTL(100)
# lease 32695410dcc0ca0 keepalived with TTL(100)
# lease 32695410dcc0ca0 keepalived with TTL(100)
...
```
## Cluster maintenance commands
### MEMBER \
MEMBER provides commands for managing etcd cluster membership.
### MEMBER ADD \ [options]
MEMBER ADD introduces a new member into the etcd cluster as a new peer.
RPC: MemberAdd
#### Options
- peer-urls -- comma separated list of URLs to associate with the new member.
#### Output
Prints the member ID of the new member and the cluster ID.
#### Example
```bash
./etcdctl member add newMember --peer-urls=https://127.0.0.1:12345
Member ced000fda4d05edf added to cluster 8c4281cc65c7b112
ETCD_NAME="newMember"
ETCD_INITIAL_CLUSTER="newMember=https://127.0.0.1:12345,default=http://10.0.0.30:2380"
ETCD_INITIAL_CLUSTER_STATE="existing"
```
### MEMBER UPDATE \ [options]
MEMBER UPDATE sets the peer URLs for an existing member in the etcd cluster.
RPC: MemberUpdate
#### Options
- peer-urls -- comma separated list of URLs to associate with the updated member.
#### Output
Prints the member ID of the updated member and the cluster ID.
#### Example
```bash
./etcdctl member update 2be1eb8f84b7f63e --peer-urls=https://127.0.0.1:11112
# Member 2be1eb8f84b7f63e updated in cluster ef37ad9dc622a7c4
```
### MEMBER REMOVE \
MEMBER REMOVE removes a member of an etcd cluster from participating in cluster consensus.
RPC: MemberRemove
#### Output
Prints the member ID of the removed member and the cluster ID.
#### Example
```bash
./etcdctl member remove 2be1eb8f84b7f63e
# Member 2be1eb8f84b7f63e removed from cluster ef37ad9dc622a7c4
```
### MEMBER LIST
MEMBER LIST prints the member details for all members associated with an etcd cluster.
RPC: MemberList
#### Options
- consistency -- Linearizable(l) or Serializable(s), defaults to Linearizable(l).
#### Output
Prints a humanized table of the member IDs, statuses, names, peer addresses, and client addresses.
Note serializable requests are better for lower latency requirement, but
stale member list might be returned if serializable option (`--consistency=s`)
is specified. In some situations users may want to use serializable requests.
For example, when adding a new member to a one-node cluster, it's reasonable
and safe to use serializable request before the new added member gets started.
#### Examples
```bash
./etcdctl member list
# 8211f1d0f64f3269, started, infra1, http://127.0.0.1:12380, http://127.0.0.1:2379
# 91bc3c398fb3c146, started, infra2, http://127.0.0.1:22380, http://127.0.0.1:22379
# fd422379fda50e48, started, infra3, http://127.0.0.1:32380, http://127.0.0.1:32379
```
```bash
./etcdctl -w json member list
# {"header":{"cluster_id":17237436991929493444,"member_id":9372538179322589801,"raft_term":2},"members":[{"ID":9372538179322589801,"name":"infra1","peerURLs":["http://127.0.0.1:12380"],"clientURLs":["http://127.0.0.1:2379"]},{"ID":10501334649042878790,"name":"infra2","peerURLs":["http://127.0.0.1:22380"],"clientURLs":["http://127.0.0.1:22379"]},{"ID":18249187646912138824,"name":"infra3","peerURLs":["http://127.0.0.1:32380"],"clientURLs":["http://127.0.0.1:32379"]}]}
```
```bash
./etcdctl -w table member list
+------------------+---------+--------+------------------------+------------------------+
| ID | STATUS | NAME | PEER ADDRS | CLIENT ADDRS |
+------------------+---------+--------+------------------------+------------------------+
| 8211f1d0f64f3269 | started | infra1 | http://127.0.0.1:12380 | http://127.0.0.1:2379 |
| 91bc3c398fb3c146 | started | infra2 | http://127.0.0.1:22380 | http://127.0.0.1:22379 |
| fd422379fda50e48 | started | infra3 | http://127.0.0.1:32380 | http://127.0.0.1:32379 |
+------------------+---------+--------+------------------------+------------------------+
```
### ENDPOINT \
ENDPOINT provides commands for querying individual endpoints.
#### Options
- cluster -- fetch and use all endpoints from the etcd cluster member list
### ENDPOINT HEALTH
ENDPOINT HEALTH checks the health of the list of endpoints with respect to cluster. An endpoint is unhealthy
when it cannot participate in consensus with the rest of the cluster.
#### Output
If an endpoint can participate in consensus, prints a message indicating the endpoint is healthy. If an endpoint fails to participate in consensus, prints a message indicating the endpoint is unhealthy.
#### Example
Check the default endpoint's health:
```bash
./etcdctl endpoint health
# 127.0.0.1:2379 is healthy: successfully committed proposal: took = 2.095242ms
```
Check all endpoints for the cluster associated with the default endpoint:
```bash
./etcdctl endpoint --cluster health
# http://127.0.0.1:2379 is healthy: successfully committed proposal: took = 1.060091ms
# http://127.0.0.1:22379 is healthy: successfully committed proposal: took = 903.138µs
# http://127.0.0.1:32379 is healthy: successfully committed proposal: took = 1.113848ms
```
### ENDPOINT STATUS
ENDPOINT STATUS queries the status of each endpoint in the given endpoint list.
#### Output
##### Simple format
Prints a humanized table of each endpoint URL, ID, version, database size, leadership status, raft term, and raft status.
##### JSON format
Prints a line of JSON encoding each endpoint URL, ID, version, database size, leadership status, raft term, and raft status.
#### Examples
Get the status for the default endpoint:
```bash
./etcdctl endpoint status
# 127.0.0.1:2379, 8211f1d0f64f3269, 3.0.0, 25 kB, false, 2, 63
```
Get the status for the default endpoint as JSON:
```bash
./etcdctl -w json endpoint status
# [{"Endpoint":"127.0.0.1:2379","Status":{"header":{"cluster_id":17237436991929493444,"member_id":9372538179322589801,"revision":2,"raft_term":2},"version":"3.0.0","dbSize":24576,"leader":18249187646912138824,"raftIndex":32623,"raftTerm":2}}]
```
Get the status for all endpoints in the cluster associated with the default endpoint:
```bash
./etcdctl -w table endpoint --cluster status
+------------------------+------------------+---------------+-----------------+---------+----------------+-----------+------------+-----------+------------+--------------------+--------+
| ENDPOINT | ID | VERSION | STORAGE VERSION | DB SIZE | DB SIZE IN USE | IS LEADER | IS LEARNER | RAFT TERM | RAFT INDEX | RAFT APPLIED INDEX | ERRORS |
+------------------------+------------------+---------------+-----------------+---------+----------------+-----------+------------+-----------+------------+--------------------+--------+
| http://127.0.0.1:2379 | 8211f1d0f64f3269 | 3.6.0-alpha.0 | 3.6.0 | 25 kB | 25 kB | false | false | 2 | 8 | 8 | |
| http://127.0.0.1:22379 | 91bc3c398fb3c146 | 3.6.0-alpha.0 | 3.6.0 | 25 kB | 25 kB | true | false | 2 | 8 | 8 | |
| http://127.0.0.1:32379 | fd422379fda50e48 | 3.6.0-alpha.0 | 3.6.0 | 25 kB | 25 kB | false | false | 2 | 8 | 8 | |
+------------------------+------------------+---------------+-----------------+---------+----------------+-----------+------------+-----------+------------+--------------------+--------+
```
### ENDPOINT HASHKV
ENDPOINT HASHKV fetches the hash of the key-value store of an endpoint.
#### Output
##### Simple format
Prints a humanized table of each endpoint URL and KV history hash.
##### JSON format
Prints a line of JSON encoding each endpoint URL and KV history hash.
#### Examples
Get the hash for the default endpoint:
```bash
./etcdctl endpoint hashkv --cluster
http://127.0.0.1:2379, 2064120424, 13
http://127.0.0.1:22379, 2064120424, 13
http://127.0.0.1:32379, 2064120424, 13
```
Get the status for the default endpoint as JSON:
```bash
./etcdctl endpoint hash --cluster -w json | jq
[
{
"Endpoint": "http://127.0.0.1:2379",
"HashKV": {
"header": {
"cluster_id": 17237436991929494000,
"member_id": 9372538179322590000,
"revision": 13,
"raft_term": 2
},
"hash": 2064120424,
"compact_revision": -1,
"hash_revision": 13
}
},
{
"Endpoint": "http://127.0.0.1:22379",
"HashKV": {
"header": {
"cluster_id": 17237436991929494000,
"member_id": 10501334649042878000,
"revision": 13,
"raft_term": 2
},
"hash": 2064120424,
"compact_revision": -1,
"hash_revision": 13
}
},
{
"Endpoint": "http://127.0.0.1:32379",
"HashKV": {
"header": {
"cluster_id": 17237436991929494000,
"member_id": 18249187646912140000,
"revision": 13,
"raft_term": 2
},
"hash": 2064120424,
"compact_revision": -1,
"hash_revision": 13
}
}
]
```
Get the status for all endpoints in the cluster associated with the default endpoint:
```bash
$ ./etcdctl endpoint hash --cluster -w table
+------------------------+-----------+---------------+
| ENDPOINT | HASH | HASH REVISION |
+------------------------+-----------+---------------+
| http://127.0.0.1:2379 | 784522900 | 16 |
| http://127.0.0.1:22379 | 784522900 | 16 |
| http://127.0.0.1:32379 | 784522900 | 16 |
+------------------------+-----------+---------------+
```
### ALARM \
Provides alarm related commands
### ALARM DISARM
`alarm disarm` Disarms all alarms
RPC: Alarm
#### Output
`alarm:` if alarm is present and disarmed.
#### Examples
```bash
./etcdctl alarm disarm
```
If NOSPACE alarm is present:
```bash
./etcdctl alarm disarm
# alarm:NOSPACE
```
### ALARM LIST
`alarm list` lists all alarms.
RPC: Alarm
#### Output
`alarm:` if alarm is present, empty string if no alarms present.
#### Examples
```bash
./etcdctl alarm list
```
If NOSPACE alarm is present:
```bash
./etcdctl alarm list
# alarm:NOSPACE
```
### DEFRAG [options]
DEFRAG defragments the backend database file for a set of given endpoints while etcd is running. When an etcd member reclaims storage space from deleted and compacted keys, the space is kept in a free list and the database file remains the same size. By defragmenting the database, the etcd member releases this free space back to the file system.
**Note: to defragment offline (`--data-dir` flag), use: `etcutl defrag` instead**
**Note that defragmentation to a live member blocks the system from reading and writing data while rebuilding its states.**
**Note that defragmentation request does not get replicated over cluster. That is, the request is only applied to the local node. Specify all members in `--endpoints` flag or `--cluster` flag to automatically find all cluster members.**
#### Output
For each endpoints, prints a message indicating whether the endpoint was successfully defragmented.
#### Example
```bash
./etcdctl --endpoints=localhost:2379,badendpoint:2379 defrag
# Finished defragmenting etcd member[localhost:2379]
# Failed to defragment etcd member[badendpoint:2379] (grpc: timed out trying to connect)
```
Run defragment operations for all endpoints in the cluster associated with the default endpoint:
```bash
./etcdctl defrag --cluster
Finished defragmenting etcd member[http://127.0.0.1:2379]
Finished defragmenting etcd member[http://127.0.0.1:22379]
Finished defragmenting etcd member[http://127.0.0.1:32379]
```
#### Remarks
DEFRAG returns a zero exit code only if it succeeded defragmenting all given endpoints.
### SNAPSHOT \
SNAPSHOT provides commands to restore a snapshot of a running etcd server into a fresh cluster.
### SNAPSHOT SAVE \
SNAPSHOT SAVE writes a point-in-time snapshot of the etcd backend database to a file.
#### Output
The backend snapshot is written to the given file path.
#### Example
Save a snapshot to "snapshot.db":
```
./etcdctl snapshot save snapshot.db
```
### SNAPSHOT RESTORE [options] \
Removed in v3.6. Use `etcdutl snapshot restore` instead.
### SNAPSHOT STATUS \
Removed in v3.6. Use `etcdutl snapshot status` instead.
### MOVE-LEADER \
MOVE-LEADER transfers leadership from the leader to another member in the cluster.
#### Example
```bash
# to choose transferee
transferee_id=$(./etcdctl \
--endpoints localhost:2379,localhost:22379,localhost:32379 \
endpoint status | grep -m 1 "false" | awk -F', ' '{print $2}')
echo ${transferee_id}
# c89feb932daef420
# endpoints should include leader node
./etcdctl --endpoints ${transferee_ep} move-leader ${transferee_id}
# Error: no leader endpoint given at [localhost:22379 localhost:32379]
# request to leader with target node ID
./etcdctl --endpoints ${leader_ep} move-leader ${transferee_id}
# Leadership transferred from 45ddc0e800e20b93 to c89feb932daef420
```
### DOWNGRADE \
NOTICE: Downgrades is an experimental feature in v3.6 and is not recommended for production clusters.
Downgrade provides commands to downgrade cluster.
Normally etcd members cannot be downgraded due to cluster version mechanism.
After initial bootstrap, cluster members agree on the cluster version. Every 5 seconds, leader checks versions of all members and picks lowers minor version.
New members will refuse joining cluster with cluster version newer than theirs, thus preventing cluster from downgrading.
Downgrade commands allow cluster administrator to force cluster version to be lowered to previous minor version, thus allowing to downgrade the cluster.
Downgrade should be executed in stages:
1. Verify that cluster is ready to be downgraded by running `etcdctl downgrade validate `
2. Start the downgrade process by running `etcdctl downgrade enable `
3. For each cluster member:
1. Ensure that member is ready for downgrade by confirming that it wrote `The server is ready to downgrade` log.
2. Replace member binary with one with older version.
3. Confirm that member has correctly started and joined the cluster.
4. Ensure that downgrade process has succeeded by checking leader log for `the cluster has been downgraded`
Downgrade can be canceled by running `etcdctl downgrade cancel` command.
In case of downgrade being canceled, cluster version will return to its normal behavior (pick the lowest member minor version).
If no members were downgraded, cluster version will return to original value.
If at least one member was downgraded, cluster version will stay at the `` until downgraded members are upgraded back.
### DOWNGRADE VALIDATE \
DOWNGRADE VALIDATE validate downgrade capability before starting downgrade.
#### Example
```bash
./etcdctl downgrade validate 3.5
Downgrade validate success, cluster version 3.6
./etcdctl downgrade validate 3.4
Error: etcdserver: invalid downgrade target version
```
### DOWNGRADE ENABLE \
DOWNGRADE ENABLE starts a downgrade action to cluster.
#### Example
```bash
./etcdctl downgrade enable 3.5
Downgrade enable success, cluster version 3.6
```
### DOWNGRADE CANCEL
DOWNGRADE CANCEL cancels the ongoing downgrade action to cluster.
#### Example
```bash
./etcdctl downgrade cancel
Downgrade cancel success, cluster version 3.5
```
### DIAGNOSIS
`etcdctl diagnosis [flags]` - Collects and analyzes troubleshooting data from a running etcd cluster.
The `diagnosis` command gathers a concise set of diagnostic details from each cluster member by performing several checks, including:
* **Membership checks**: Verifies the cluster membership information.
* **Endpoint status**: Retrieves the status of each endpoint.
* **Serializable and linearizable reads**: Performs read operations to validate data consistency.
* **Metrics snapshot**: Collects a small snapshot of key metrics.
#### Flags
- `--cluster`: use all endpoints discovered from the cluster member list.
- `--etcd-storage-quota-bytes`: expected etcd storage quota in bytes (value passed to etcd with `--quota-backend-bytes`).
- `-o, --output`: optional file path to write the JSON report; by default the report is written to stdout. Logs are written to stderr.
Global flags (like `--endpoints`, TLS, auth, and timeouts) are shared with other `etcdctl` commands. See `etcdctl options` for the full list.
#### Examples
To perform analysis of a running etcd cluster, you can use the following command. This will collect and analyze data from all specified endpoints.
```bash
etcdctl diagnosis --endpoints=https://10.0.1.10:2379,https://10.0.1.11:2379,https://10.0.1.12:2379 \
--cacert ./ca.crt --key ./etcd-diagnosis.key --cert ./etcd-diagnosis.crt
# Use cluster-discovered endpoints
etcdctl diagnosis --cluster
# Write report to a file (logs still go to stderr)
etcdctl diagnosis -o report.json
```
Example output: see [ctlv3/command/diagnosis/examples/etcd_diagnosis_report.json](ctlv3/command/diagnosis/examples/etcd_diagnosis_report.json)
## Concurrency commands
### LOCK [options] \ [command arg1 arg2 ...]
LOCK acquires a distributed mutex with a given name. Once the lock is acquired, it will be held until etcdctl is terminated.
#### Options
- ttl - time out in seconds of lock session.
#### Output
Once the lock is acquired but no command is given, the result for the GET on the unique lock holder key is displayed.
If a command is given, it will be executed with environment variables `ETCD_LOCK_KEY` and `ETCD_LOCK_REV` set to the lock's holder key and revision.
#### Example
Acquire lock with standard output display:
```bash
./etcdctl lock mylock
# mylock/1234534535445
```
Acquire lock and execute `echo lock acquired`:
```bash
./etcdctl lock mylock echo lock acquired
# lock acquired
```
Acquire lock and execute `etcdctl put` command
```bash
./etcdctl lock mylock ./etcdctl put foo bar
# OK
```
#### Remarks
LOCK returns a zero exit code only if it is terminated by a signal and releases the lock.
If LOCK is abnormally terminated or fails to contact the cluster to release the lock, the lock will remain held until the lease expires. Progress may be delayed by up to the default lease length of 60 seconds.
### ELECT [options] \ [proposal]
ELECT participates on a named election. A node announces its candidacy in the election by providing
a proposal value. If a node wishes to observe the election, ELECT listens for new leaders values.
Whenever a leader is elected, its proposal is given as output.
#### Options
- listen -- observe the election.
#### Output
- If a candidate, ELECT displays the GET on the leader key once the node is elected election.
- If observing, ELECT streams the result for a GET on the leader key for the current election and all future elections.
#### Example
```bash
./etcdctl elect myelection foo
# myelection/1456952310051373265
# foo
```
#### Remarks
ELECT returns a zero exit code only if it is terminated by a signal and can revoke its candidacy or leadership, if any.
If a candidate is abnormally terminated, election progress may be delayed by up to the default lease length of 60 seconds.
## Authentication commands
### AUTH \
`auth enable` activates authentication on an etcd cluster and `auth disable` deactivates. When authentication is enabled, etcd checks all requests for appropriate authorization.
RPC: AuthEnable/AuthDisable
#### Output
`Authentication Enabled`.
#### Examples
```bash
./etcdctl user add root
# Password of root:#type password for root
# Type password of root again for confirmation:#re-type password for root
# User root created
./etcdctl user grant-role root root
# Role root is granted to user root
./etcdctl user get root
# User: root
# Roles: root
./etcdctl role add root
# Role root created
./etcdctl role get root
# Role root
# KV Read:
# KV Write:
./etcdctl auth enable
# Authentication Enabled
```
### ROLE \
ROLE is used to specify different roles which can be assigned to etcd user(s).
### ROLE ADD \
`role add` creates a role.
RPC: RoleAdd
#### Output
`Role created`.
#### Examples
```bash
./etcdctl --user=root:123 role add myrole
# Role myrole created
```
### ROLE GET \
`role get` lists detailed role information.
RPC: RoleGet
#### Output
Detailed role information.
#### Examples
```bash
./etcdctl --user=root:123 role get myrole
# Role myrole
# KV Read:
# foo
# KV Write:
# foo
```
### ROLE DELETE \
`role delete` deletes a role.
RPC: RoleDelete
#### Output
`Role deleted`.
#### Examples
```bash
./etcdctl --user=root:123 role delete myrole
# Role myrole deleted
```
### ROLE LIST \
`role list` lists all roles in etcd.
RPC: RoleList
#### Output
A role per line.
#### Examples
```bash
./etcdctl --user=root:123 role list
# roleA
# roleB
# myrole
```
### ROLE GRANT-PERMISSION [options] \ \ \ [endkey]
`role grant-permission` grants a key to a role.
RPC: RoleGrantPermission
#### Options
- from-key -- grant a permission of keys that are greater than or equal to the given key using byte compare
- prefix -- grant a prefix permission
#### Output
`Role updated`.
#### Examples
Grant read and write permission on the key `foo` to role `myrole`:
```bash
./etcdctl --user=root:123 role grant-permission myrole readwrite foo
# Role myrole updated
```
Grant read permission on the wildcard key pattern `foo/*` to role `myrole`:
```bash
./etcdctl --user=root:123 role grant-permission --prefix myrole readwrite foo/
# Role myrole updated
```
### ROLE REVOKE-PERMISSION \ \ \ [endkey]
`role revoke-permission` revokes a key from a role.
RPC: RoleRevokePermission
#### Options
- from-key -- revoke a permission of keys that are greater than or equal to the given key using byte compare
- prefix -- revoke a prefix permission
#### Output
`Permission of key is revoked from role ` for single key. `Permission of range [, ) is revoked from role ` for a key range. Exit code is zero.
#### Examples
```bash
./etcdctl --user=root:123 role revoke-permission myrole foo
# Permission of key foo is revoked from role myrole
```
### USER \
USER provides commands for managing users of etcd.
### USER ADD \ [options]
`user add` creates a user.
RPC: UserAdd
#### Options
- interactive -- Read password from stdin instead of interactive terminal
#### Output
`User created`.
#### Examples
```bash
./etcdctl --user=root:123 user add myuser
# Password of myuser: #type password for my user
# Type password of myuser again for confirmation:#re-type password for my user
# User myuser created
```
### USER GET \ [options]
`user get` lists detailed user information.
RPC: UserGet
#### Options
- detail -- Show permissions of roles granted to the user
#### Output
Detailed user information.
#### Examples
```bash
./etcdctl --user=root:123 user get myuser
# User: myuser
# Roles:
```
### USER DELETE \
`user delete` deletes a user.
RPC: UserDelete
#### Output
`User deleted`.
#### Examples
```bash
./etcdctl --user=root:123 user delete myuser
# User myuser deleted
```
### USER LIST
`user list` lists detailed user information.
RPC: UserList
#### Output
- List of users, one per line.
#### Examples
```bash
./etcdctl --user=root:123 user list
# user1
# user2
# myuser
```
### USER PASSWD \ [options]
`user passwd` changes a user's password.
RPC: UserChangePassword
#### Options
- interactive -- if true, read password in interactive terminal
#### Output
`Password updated`.
#### Examples
```bash
./etcdctl --user=root:123 user passwd myuser
# Password of myuser: #type new password for my user
# Type password of myuser again for confirmation: #re-type the new password for my user
# Password updated
```
### USER GRANT-ROLE \ \
`user grant-role` grants a role to a user
RPC: UserGrantRole
#### Output
`Role is granted to user `.
#### Examples
```bash
./etcdctl --user=root:123 user grant-role userA roleA
# Role roleA is granted to user userA
```
### USER REVOKE-ROLE \ \
`user revoke-role` revokes a role from a user
RPC: UserRevokeRole
#### Output
`Role is revoked from user `.
#### Examples
```bash
./etcdctl --user=root:123 user revoke-role userA roleA
# Role roleA is revoked from user userA
```
## Utility commands
### MAKE-MIRROR [options] \
[make-mirror][mirror] mirrors a key prefix in an etcd cluster to a destination etcd cluster.
#### Options
- dest-cacert -- TLS certificate authority file for destination cluster
- dest-cert -- TLS certificate file for destination cluster
- dest-key -- TLS key file for destination cluster
- prefix -- The key-value prefix to mirror
- dest-prefix -- The destination prefix to mirror a prefix to a different prefix in the destination cluster
- no-dest-prefix -- Mirror key-values to the root of the destination cluster
- dest-insecure-transport -- Disable transport security for client connections
- max-txn-ops -- Maximum number of operations permitted in a transaction during syncing updates
#### Output
The approximate total number of keys transferred to the destination cluster, updated every 30 seconds.
#### Examples
```
./etcdctl make-mirror mirror.example.com:2379
# 10
# 18
```
[mirror]: ./doc/mirror_maker.md
### VERSION
Prints the version of etcdctl.
#### Output
Prints etcd version and API version.
#### Examples
```bash
./etcdctl version
# etcdctl version: 3.1.0-alpha.0+git
# API version: 3.1
```
### CHECK \
CHECK provides commands for checking properties of the etcd cluster.
### CHECK PERF [options]
CHECK PERF checks the performance of the etcd cluster for 60 seconds. Running the `check perf` often can create a large keyspace history which can be auto compacted and defragmented using the `--auto-compact` and `--auto-defrag` options as described below.
Notice that different workload models use different configurations in terms of number of clients and throughput. Here is the configuration for each load:
| Load | Number of clients | Number of put requests (requests/sec) |
|---------|------|---------|
| Small | 50 | 10000 |
| Medium | 200 | 100000 |
| Large | 500 | 1000000 |
| xLarge | 1000 | 3000000 |
The test checks for the following conditions:
- The throughput should be at least 90% of the issued request
- All the requests should be done in less than 500 ms
- The standard deviation of the requests should be less than 100 ms
Hence, a workload model may work while another one might fail.
RPC: CheckPerf
#### Options
- load -- the performance check's workload model. Accepted workloads: s(small), m(medium), l(large), xl(xLarge)
- prefix -- the prefix for writing the performance check's keys.
- auto-compact -- if true, compact storage with last revision after test is finished.
- auto-defrag -- if true, defragment storage after test is finished.
#### Output
Prints the result of performance check on different criteria like throughput. Also prints an overall status of the check as pass or fail.
#### Examples
Shows examples of both, pass and fail, status. The failure is due to the fact that a large workload was tried on a single node etcd cluster running on a laptop environment created for development and testing purpose.
```bash
./etcdctl check perf --load="s"
# 60 / 60 Booooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo! 100.00%1m0s
# PASS: Throughput is 150 writes/s
# PASS: Slowest request took 0.087509s
# PASS: Stddev is 0.011084s
# PASS
./etcdctl check perf --load="l"
# 60 / 60 Booooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo! 100.00%1m0s
# FAIL: Throughput too low: 6808 writes/s
# PASS: Slowest request took 0.228191s
# PASS: Stddev is 0.033547s
# FAIL
```
### CHECK DATASCALE [options]
CHECK DATASCALE checks the memory usage of holding data for different workloads on a given server endpoint. Running the `check datascale` often can create a large keyspace history which can be auto compacted and defragmented using the `--auto-compact` and `--auto-defrag` options as described below.
RPC: CheckDatascale
#### Options
- load -- the datascale check's workload model. Accepted workloads: s(small), m(medium), l(large), xl(xLarge)
- prefix -- the prefix for writing the datascale check's keys.
- auto-compact -- if true, compact storage with last revision after test is finished.
- auto-defrag -- if true, defragment storage after test is finished.
#### Output
Prints the system memory usage for a given workload. Also prints status of compact and defragment if related options are passed.
#### Examples
```bash
./etcdctl check datascale --load="s" --auto-compact=true --auto-defrag=true
# Start data scale check for work load [10000 key-value pairs, 1024 bytes per key-value, 50 concurrent clients].
# Compacting with revision 18346204
# Compacted with revision 18346204
# Defragmenting "127.0.0.1:2379"
# Defragmented "127.0.0.1:2379"
# PASS: Approximate system memory used : 64.30 MB.
```
## Exit codes
For all commands, a successful execution return a zero exit code. All failures will return non-zero exit codes.
## Output formats
All commands accept an output format by setting `-w` or `--write-out`. All commands default to the "simple" output format, which is meant to be human-readable. The simple format is listed in each command's `Output` description since it is customized for each command. If a command has a corresponding RPC, it will respect all output formats.
If a command fails, returning a non-zero exit code, an error string will be written to standard error regardless of output format.
### Simple
A format meant to be easy to parse and human-readable. Specific to each command.
### JSON
The JSON encoding of the command's [RPC response][etcdrpc]. Since etcd's RPCs use byte strings, the JSON output will encode keys and values in base64.
Some commands without an RPC also support JSON; see the command's `Output` description.
### Protobuf
The protobuf encoding of the command's [RPC response][etcdrpc]. If an RPC is streaming, the stream messages will be concetenated. If an RPC is not given for a command, the protobuf output is not defined.
### Fields
An output format similar to JSON but meant to parse with coreutils. For an integer field named `Field`, it writes a line in the format `"Field" : %d` where `%d` is go's integer formatting. For byte array fields, it writes `"Field" : %q` where `%q` is go's quoted string formatting (e.g., `[]byte{'a', '\n'}` is written as `"a\n"`).
## Compatibility Support
etcdctl is still in its early stage. We try out best to ensure fully compatible releases, however we might break compatibility to fix bugs or improve commands. If we intend to release a version of etcdctl with backward incompatibilities, we will provide notice prior to release and have instructions on how to upgrade.
### Input Compatibility
Input includes the command name, its flags, and its arguments. We ensure backward compatibility of the input of normal commands in non-interactive mode.
### Output Compatibility
Output includes output from etcdctl and its exit code. etcdctl provides `simple` output format by default.
We ensure compatibility for the `simple` output format of normal commands in non-interactive mode. Currently, we do not ensure
backward compatibility for `JSON` format and the format in non-interactive mode. Currently, we do not ensure backward compatibility of utility commands.
### TODO: compatibility with etcd server
[etcd]: https://github.com/coreos/etcd
[READMEv2]: READMEv2.md
[etcdrpc]: ../api/etcdserverpb/rpc.proto
================================================
FILE: etcdctl/READMEv2.md
================================================
etcdctl
========
`etcdctl` is a command line client for [etcd][etcd].
It can be used in scripts or for administrators to explore an etcd cluster.
## Getting etcdctl
The latest release is available as a binary at [Github][github-release] along with etcd.
etcdctl can also be built from source using the build script found in the parent directory.
## Configuration
### --debug
+ output cURL commands which can be used to reproduce the request
### --no-sync
+ don't synchronize cluster information before sending request
+ Use this to access non-published client endpoints
+ Without this flag, values from `--endpoint` flag will be overwritten by etcd cluster when it does internal sync.
### --output, -o
+ output response in the given format (`simple`, `extended` or `json`)
+ default: `"simple"`
### --discovery-srv, -D
+ domain name to query for SRV records describing cluster endpoints
+ default: none
+ env variable: ETCDCTL_DISCOVERY_SRV
### --peers
+ a comma-delimited list of machine addresses in the cluster
+ default: `"http://127.0.0.1:2379"`
+ env variable: ETCDCTL_PEERS
### --endpoint
+ a comma-delimited list of machine addresses in the cluster
+ default: `"http://127.0.0.1:2379"`
+ env variable: ETCDCTL_ENDPOINT
+ Without `--no-sync` flag, this will be overwritten by etcd cluster when it does internal sync.
### --cert-file
+ identify HTTPS client using this SSL certificate file
+ default: none
+ env variable: ETCDCTL_CERT_FILE
### --key-file
+ identify HTTPS client using this SSL key file
+ default: none
+ env variable: ETCDCTL_KEY_FILE
### --ca-file
+ verify certificates of HTTPS-enabled servers using this CA bundle
+ default: none
+ env variable: ETCDCTL_CA_FILE
### --username, -u
+ provide username[:password] and prompt if password is not supplied
+ default: none
+ env variable: ETCDCTL_USERNAME
### --timeout
+ connection timeout per request
+ default: `"1s"`
### --total-timeout
+ timeout for the command execution (except watch)
+ default: `"5s"`
## Usage
### Setting Key Values
Set a value on the `/foo/bar` key:
```sh
$ etcdctl set /foo/bar "Hello world"
Hello world
```
Set a value on the `/foo/bar` key with a value that expires in 60 seconds:
```sh
$ etcdctl set /foo/bar "Hello world" --ttl 60
Hello world
```
Conditionally set a value on `/foo/bar` if the previous value was "Hello world":
```sh
$ etcdctl set /foo/bar "Goodbye world" --swap-with-value "Hello world"
Goodbye world
```
Conditionally set a value on `/foo/bar` if the previous etcd index was 12:
```sh
$ etcdctl set /foo/bar "Goodbye world" --swap-with-index 12
Goodbye world
```
Create a new key `/foo/bar`, only if the key did not previously exist:
```sh
$ etcdctl mk /foo/new_bar "Hello world"
Hello world
```
Create a new in-order key under dir `/fooDir`:
```sh
$ etcdctl mk --in-order /fooDir "Hello world"
```
Create a new dir `/fooDir`, only if the key did not previously exist:
```sh
$ etcdctl mkdir /fooDir
```
Update an existing key `/foo/bar`, only if the key already existed:
```sh
$ etcdctl update /foo/bar "Hola mundo"
Hola mundo
```
Create or update a directory called `/mydir`:
```sh
$ etcdctl setdir /mydir
```
### Retrieving a key value
Get the current value for a single key in the local etcd node:
```sh
$ etcdctl get /foo/bar
Hello world
```
Get the value of a key with additional metadata in a parseable format:
```sh
$ etcdctl -o extended get /foo/bar
Key: /foo/bar
Modified-Index: 72
TTL: 0
Etcd-Index: 72
Raft-Index: 5611
Raft-Term: 1
Hello World
```
### Listing a directory
Explore the keyspace using the `ls` command
```sh
$ etcdctl ls
/akey
/adir
$ etcdctl ls /adir
/adir/key1
/adir/key2
```
Add `--recursive` to recursively list subdirectories encountered.
```sh
$ etcdctl ls --recursive
/akey
/adir
/adir/key1
/adir/key2
```
Directories can also have a trailing `/` added to output using `-p`.
```sh
$ etcdctl ls -p
/akey
/adir/
```
### Deleting a key
Delete a key:
```sh
$ etcdctl rm /foo/bar
```
Delete an empty directory or a key-value pair
```sh
$ etcdctl rmdir /path/to/dir
```
or
```sh
$ etcdctl rm /path/to/dir --dir
```
Recursively delete a key and all child keys:
```sh
$ etcdctl rm /path/to/dir --recursive
```
Conditionally delete `/foo/bar` if the previous value was "Hello world":
```sh
$ etcdctl rm /foo/bar --with-value "Hello world"
```
Conditionally delete `/foo/bar` if the previous etcd index was 12:
```sh
$ etcdctl rm /foo/bar --with-index 12
```
### Watching for changes
Watch for only the next change on a key:
```sh
$ etcdctl watch /foo/bar
Hello world
```
Continuously watch a key:
```sh
$ etcdctl watch /foo/bar --forever
Hello world
.... client hangs forever until ctrl+C printing values as key change
```
Continuously watch a key, starting with a given etcd index:
```sh
$ etcdctl watch /foo/bar --forever --index 12
Hello world
.... client hangs forever until ctrl+C printing values as key change
```
Continuously watch a key and exec a program:
```sh
$ etcdctl exec-watch /foo/bar -- sh -c "env | grep ETCD"
ETCD_WATCH_ACTION=set
ETCD_WATCH_VALUE=My configuration stuff
ETCD_WATCH_MODIFIED_INDEX=1999
ETCD_WATCH_KEY=/foo/bar
ETCD_WATCH_ACTION=set
ETCD_WATCH_VALUE=My new configuration stuff
ETCD_WATCH_MODIFIED_INDEX=2000
ETCD_WATCH_KEY=/foo/bar
```
Continuously and recursively watch a key and exec a program:
```sh
$ etcdctl exec-watch --recursive /foo -- sh -c "env | grep ETCD"
ETCD_WATCH_ACTION=set
ETCD_WATCH_VALUE=My configuration stuff
ETCD_WATCH_MODIFIED_INDEX=1999
ETCD_WATCH_KEY=/foo/bar
ETCD_WATCH_ACTION=set
ETCD_WATCH_VALUE=My new configuration stuff
ETCD_WATCH_MODIFIED_INDEX=2000
ETCD_WATCH_KEY=/foo/barbar
```
## Return Codes
The following exit codes can be returned from etcdctl:
```
0 Success
1 Malformed etcdctl arguments
2 Failed to connect to host
3 Failed to auth (client cert rejected, ca validation failure, etc)
4 400 error from etcd
5 500 error from etcd
```
## Endpoint
If the etcd cluster isn't available on `http://127.0.0.1:2379`, specify a `--endpoint` flag or `ETCDCTL_ENDPOINT` environment variable. One endpoint or a comma-separated list of endpoints can be listed. This option is ignored if the `--discovery-srv` option is provided.
```sh
ETCDCTL_ENDPOINT="http://10.0.28.1:4002" etcdctl set my-key to-a-value
ETCDCTL_ENDPOINT="http://10.0.28.1:4002,http://10.0.28.2:4002,http://10.0.28.3:4002" etcdctl set my-key to-a-value
etcdctl --endpoint http://10.0.28.1:4002 my-key to-a-value
etcdctl --endpoint http://10.0.28.1:4002,http://10.0.28.2:4002,http://10.0.28.3:4002 etcdctl set my-key to-a-value
```
## Username and Password
If the etcd cluster is protected by [authentication][authentication], specify username and password using the [`--username`][username-flag] or `ETCDCTL_USERNAME` environment variable. When `--username` flag or `ETCDCTL_USERNAME` environment variable doesn't contain password, etcdctl will prompt password in interactive mode.
```sh
ETCDCTL_USERNAME="root:password" etcdctl set my-key to-a-value
```
## DNS Discovery
To discover the etcd cluster through domain SRV records, specify a `--discovery-srv` flag or `ETCDCTL_DISCOVERY_SRV` environment variable. This option takes precedence over the `--endpoint` flag.
```sh
ETCDCTL_DISCOVERY_SRV="some-domain" etcdctl set my-key to-a-value
etcdctl --discovery-srv some-domain set my-key to-a-value
```
## Project Details
### Versioning
etcdctl uses [semantic versioning][semver].
Releases will follow lockstep with the etcd release cycle.
### License
etcdctl is under the Apache 2.0 license. See the [LICENSE][license] file for details.
[authentication]: https://github.com/etcd-io/website/blob/main/content/docs/v2/authentication.md
[etcd]: https://github.com/coreos/etcd
[github-release]: https://github.com/coreos/etcd/releases/
[license]: ../LICENSE
[semver]: http://semver.org/
[username-flag]: #--username--u
================================================
FILE: etcdctl/ctlv3/command/alarm_command.go
================================================
// Copyright 2016 The etcd Authors
//
// 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 command
import (
"fmt"
"github.com/spf13/cobra"
v3 "go.etcd.io/etcd/client/v3"
"go.etcd.io/etcd/pkg/v3/cobrautl"
)
// NewAlarmCommand returns the cobra command for "alarm".
func NewAlarmCommand() *cobra.Command {
ac := &cobra.Command{
Use: "alarm ",
Short: "Alarm related commands. Use `etcdctl alarm --help` to see subcommands",
Long: "Alarm related commands",
GroupID: groupClusterMaintenanceID,
}
ac.AddCommand(NewAlarmDisarmCommand())
ac.AddCommand(NewAlarmListCommand())
return ac
}
func NewAlarmDisarmCommand() *cobra.Command {
cmd := cobra.Command{
Use: "disarm",
Short: "Disarms all alarms",
Run: alarmDisarmCommandFunc,
}
return &cmd
}
// alarmDisarmCommandFunc executes the "alarm disarm" command.
func alarmDisarmCommandFunc(cmd *cobra.Command, args []string) {
if len(args) != 0 {
cobrautl.ExitWithError(cobrautl.ExitBadArgs, fmt.Errorf("alarm disarm command accepts no arguments"))
}
ctx, cancel := commandCtx(cmd)
resp, err := mustClientFromCmd(cmd).AlarmDisarm(ctx, &v3.AlarmMember{})
cancel()
if err != nil {
cobrautl.ExitWithError(cobrautl.ExitError, err)
}
display.Alarm(*resp)
}
func NewAlarmListCommand() *cobra.Command {
cmd := cobra.Command{
Use: "list",
Short: "Lists all alarms",
Run: alarmListCommandFunc,
}
return &cmd
}
// alarmListCommandFunc executes the "alarm list" command.
func alarmListCommandFunc(cmd *cobra.Command, args []string) {
if len(args) != 0 {
cobrautl.ExitWithError(cobrautl.ExitBadArgs, fmt.Errorf("alarm list command accepts no arguments"))
}
ctx, cancel := commandCtx(cmd)
resp, err := mustClientFromCmd(cmd).AlarmList(ctx)
cancel()
if err != nil {
cobrautl.ExitWithError(cobrautl.ExitError, err)
}
display.Alarm(*resp)
}
================================================
FILE: etcdctl/ctlv3/command/auth_command.go
================================================
// Copyright 2016 The etcd Authors
//
// 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 command
import (
"errors"
"fmt"
"github.com/spf13/cobra"
"go.etcd.io/etcd/api/v3/v3rpc/rpctypes"
"go.etcd.io/etcd/pkg/v3/cobrautl"
)
// NewAuthCommand returns the cobra command for "auth".
func NewAuthCommand() *cobra.Command {
ac := &cobra.Command{
Use: "auth ",
Short: "Enable or disable authentication. Use `etcdctl auth --help` to see subcommands",
Long: "Enable or disable authentication",
GroupID: groupAuthenticationID,
}
ac.AddCommand(newAuthEnableCommand())
ac.AddCommand(newAuthDisableCommand())
ac.AddCommand(newAuthStatusCommand())
return ac
}
func newAuthStatusCommand() *cobra.Command {
return &cobra.Command{
Use: "status",
Short: "Returns authentication status",
Run: authStatusCommandFunc,
}
}
// authStatusCommandFunc executes the "auth status" command.
func authStatusCommandFunc(cmd *cobra.Command, args []string) {
if len(args) != 0 {
cobrautl.ExitWithError(cobrautl.ExitBadArgs, fmt.Errorf("auth status command does not accept any arguments"))
}
ctx, cancel := commandCtx(cmd)
result, err := mustClientFromCmd(cmd).Auth.AuthStatus(ctx)
cancel()
if err != nil {
cobrautl.ExitWithError(cobrautl.ExitError, err)
}
display.AuthStatus(*result)
}
func newAuthEnableCommand() *cobra.Command {
return &cobra.Command{
Use: "enable",
Short: "Enables authentication",
Run: authEnableCommandFunc,
}
}
// authEnableCommandFunc executes the "auth enable" command.
func authEnableCommandFunc(cmd *cobra.Command, args []string) {
if len(args) != 0 {
cobrautl.ExitWithError(cobrautl.ExitBadArgs, fmt.Errorf("auth enable command does not accept any arguments"))
}
ctx, cancel := commandCtx(cmd)
cli := mustClientFromCmd(cmd)
var err error
for err == nil {
if _, err = cli.AuthEnable(ctx); err == nil {
break
}
if errors.Is(err, rpctypes.ErrRootRoleNotExist) {
if _, err = cli.RoleAdd(ctx, "root"); err != nil {
break
}
if _, err = cli.UserGrantRole(ctx, "root", "root"); err != nil {
break
}
}
}
cancel()
if err != nil {
cobrautl.ExitWithError(cobrautl.ExitError, err)
}
fmt.Println("Authentication Enabled")
}
func newAuthDisableCommand() *cobra.Command {
return &cobra.Command{
Use: "disable",
Short: "Disables authentication",
Run: authDisableCommandFunc,
}
}
// authDisableCommandFunc executes the "auth disable" command.
func authDisableCommandFunc(cmd *cobra.Command, args []string) {
if len(args) != 0 {
cobrautl.ExitWithError(cobrautl.ExitBadArgs, fmt.Errorf("auth disable command does not accept any arguments"))
}
ctx, cancel := commandCtx(cmd)
_, err := mustClientFromCmd(cmd).Auth.AuthDisable(ctx)
cancel()
if err != nil {
cobrautl.ExitWithError(cobrautl.ExitError, err)
}
fmt.Println("Authentication Disabled")
}
================================================
FILE: etcdctl/ctlv3/command/check.go
================================================
// Copyright 2017 The etcd Authors
//
// 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 command
import (
"context"
"encoding/binary"
"fmt"
"math"
"math/rand"
"os"
"os/signal"
"strconv"
"sync"
"time"
"github.com/cheggaaa/pb/v3"
"github.com/spf13/cobra"
"golang.org/x/time/rate"
v3 "go.etcd.io/etcd/client/v3"
"go.etcd.io/etcd/pkg/v3/cobrautl"
"go.etcd.io/etcd/pkg/v3/report"
)
var (
checkPerfLoad string
checkPerfPrefix string
checkDatascaleLoad string
checkDatascalePrefix string
autoCompact bool
autoDefrag bool
)
type checkPerfCfg struct {
limit int
clients int
duration int
}
var checkPerfCfgMap = map[string]checkPerfCfg{
// TODO: support read limit
"s": {
limit: 150,
clients: 50,
duration: 60,
},
"m": {
limit: 1000,
clients: 200,
duration: 60,
},
"l": {
limit: 8000,
clients: 500,
duration: 60,
},
"xl": {
limit: 15000,
clients: 1000,
duration: 60,
},
}
type checkDatascaleCfg struct {
limit int
kvSize int
clients int
}
var checkDatascaleCfgMap = map[string]checkDatascaleCfg{
"s": {
limit: 10000,
kvSize: 1024,
clients: 50,
},
"m": {
limit: 100000,
kvSize: 1024,
clients: 200,
},
"l": {
limit: 1000000,
kvSize: 1024,
clients: 500,
},
"xl": {
// xl tries to hit the upper bound aggressively which is 3 versions of 1M objects (3M in total)
limit: 3000000,
kvSize: 1024,
clients: 1000,
},
}
// NewCheckCommand returns the cobra command for "check".
func NewCheckCommand() *cobra.Command {
cc := &cobra.Command{
Use: "check ",
Short: "commands for checking properties of the etcd cluster. Use `etcdctl check --help` to see subcommands",
Long: "commands for checking properties of the etcd cluster",
GroupID: groupUtilityID,
}
cc.AddCommand(NewCheckPerfCommand())
cc.AddCommand(NewCheckDatascaleCommand())
return cc
}
// NewCheckPerfCommand returns the cobra command for "check perf".
func NewCheckPerfCommand() *cobra.Command {
cmd := &cobra.Command{
Use: "perf [options]",
Short: "Check the performance of the etcd cluster",
Run: newCheckPerfCommand,
}
// TODO: support customized configuration
cmd.Flags().StringVar(&checkPerfLoad, "load", "s", "The performance check's workload model. Accepted workloads: s(small), m(medium), l(large), xl(xLarge). Different workload models use different configurations in terms of number of clients and expected throughput.")
cmd.Flags().StringVar(&checkPerfPrefix, "prefix", "/etcdctl-check-perf/", "The prefix for writing the performance check's keys.")
cmd.Flags().BoolVar(&autoCompact, "auto-compact", false, "Compact storage with last revision after test is finished.")
cmd.Flags().BoolVar(&autoDefrag, "auto-defrag", false, "Defragment storage after test is finished.")
cmd.RegisterFlagCompletionFunc("load", func(_ *cobra.Command, _ []string, _ string) ([]string, cobra.ShellCompDirective) {
return []string{"small", "medium", "large", "xLarge"}, cobra.ShellCompDirectiveDefault
})
return cmd
}
// newCheckPerfCommand executes the "check perf" command.
func newCheckPerfCommand(cmd *cobra.Command, args []string) {
checkPerfAlias := map[string]string{
"s": "s", "small": "s",
"m": "m", "medium": "m",
"l": "l", "large": "l",
"xl": "xl", "xLarge": "xl",
}
model, ok := checkPerfAlias[checkPerfLoad]
if !ok {
cobrautl.ExitWithError(cobrautl.ExitBadFeature, fmt.Errorf("unknown load option %v", checkPerfLoad))
}
cfg := checkPerfCfgMap[model]
requests := make(chan v3.Op, cfg.clients)
limit := rate.NewLimiter(rate.Limit(cfg.limit), 1)
cc := clientConfigFromCmd(cmd)
clients := make([]*v3.Client, cfg.clients)
for i := 0; i < cfg.clients; i++ {
clients[i] = mustClient(cc)
}
ctx, cancel := context.WithTimeout(context.Background(), time.Duration(cfg.duration)*time.Second)
defer cancel()
ctx, icancel := interruptableContext(ctx, func() { attemptCleanup(clients[0], false) })
defer icancel()
gctx, gcancel := context.WithCancel(ctx)
resp, err := clients[0].Get(gctx, checkPerfPrefix, v3.WithPrefix(), v3.WithLimit(1))
gcancel()
if err != nil {
cobrautl.ExitWithError(cobrautl.ExitError, err)
}
if len(resp.Kvs) > 0 {
cobrautl.ExitWithError(cobrautl.ExitInvalidInput, fmt.Errorf("prefix %q has keys. Delete with 'etcdctl del --prefix %s' first", checkPerfPrefix, checkPerfPrefix))
}
ksize, vsize := 256, 1024
k, v := make([]byte, ksize), string(make([]byte, vsize))
bar := pb.New(cfg.duration)
bar.Start()
r := report.NewReport("%4.4f", "", false)
var wg sync.WaitGroup
wg.Add(len(clients))
for i := range clients {
go func(c *v3.Client) {
defer wg.Done()
for op := range requests {
st := time.Now()
_, derr := c.Do(context.Background(), op)
r.Results() <- report.Result{Err: derr, Start: st, End: time.Now()}
}
}(clients[i])
}
go func() {
cctx, ccancel := context.WithCancel(ctx)
defer ccancel()
for limit.Wait(cctx) == nil {
binary.PutVarint(k, rand.Int63n(math.MaxInt64))
requests <- v3.OpPut(checkPerfPrefix+string(k), v)
}
close(requests)
}()
go func() {
for i := 0; i < cfg.duration; i++ {
time.Sleep(time.Second)
bar.Add(1)
}
bar.Finish()
}()
sc := r.Stats()
wg.Wait()
close(r.Results())
s := <-sc
attemptCleanup(clients[0], autoCompact)
if autoDefrag {
for _, ep := range clients[0].Endpoints() {
defrag(clients[0], ep)
}
}
ok = true
if len(s.ErrorDist) != 0 {
fmt.Println("FAIL: too many errors")
for k, v := range s.ErrorDist {
fmt.Printf("FAIL: ERROR(%v) -> %d\n", k, v)
}
ok = false
}
if s.RPS/float64(cfg.limit) <= 0.9 {
fmt.Printf("FAIL: Throughput too low: %d writes/s\n", int(s.RPS)+1)
ok = false
} else {
fmt.Printf("PASS: Throughput is %d writes/s\n", int(s.RPS)+1)
}
if s.Slowest > 0.5 { // slowest request > 500ms
fmt.Printf("Slowest request took too long: %fs\n", s.Slowest)
ok = false
} else {
fmt.Printf("PASS: Slowest request took %fs\n", s.Slowest)
}
if s.Stddev > 0.1 { // stddev > 100ms
fmt.Printf("Stddev too high: %fs\n", s.Stddev)
ok = false
} else {
fmt.Printf("PASS: Stddev is %fs\n", s.Stddev)
}
if !ok {
fmt.Println("FAIL")
os.Exit(cobrautl.ExitError)
}
fmt.Println("PASS")
}
func attemptCleanup(client *v3.Client, autoCompact bool) {
dctx, dcancel := context.WithTimeout(context.Background(), 30*time.Second)
defer dcancel()
dresp, err := client.Delete(dctx, checkPerfPrefix, v3.WithPrefix())
if err != nil {
fmt.Printf("FAIL: Cleanup failed during key deletion: ERROR(%v)\n", err)
return
}
if autoCompact {
compact(client, dresp.Header.Revision)
}
}
func interruptableContext(ctx context.Context, attemptCleanup func()) (context.Context, func()) {
ctx, cancel := context.WithCancel(ctx)
signalChan := make(chan os.Signal, 1)
signal.Notify(signalChan, os.Interrupt)
go func() {
defer signal.Stop(signalChan)
select {
case <-signalChan:
cancel()
attemptCleanup()
}
}()
return ctx, cancel
}
// NewCheckDatascaleCommand returns the cobra command for "check datascale".
func NewCheckDatascaleCommand() *cobra.Command {
cmd := &cobra.Command{
Use: "datascale [options]",
Short: "Check the memory usage of holding data for different workloads on a given server endpoint.",
Long: "If no endpoint is provided, localhost will be used. If multiple endpoints are provided, first endpoint will be used.",
Run: newCheckDatascaleCommand,
}
cmd.Flags().StringVar(&checkDatascaleLoad, "load", "s", "The datascale check's workload model. Accepted workloads: s(small), m(medium), l(large), xl(xLarge)")
cmd.Flags().StringVar(&checkDatascalePrefix, "prefix", "/etcdctl-check-datascale/", "The prefix for writing the datascale check's keys.")
cmd.Flags().BoolVar(&autoCompact, "auto-compact", false, "Compact storage with last revision after test is finished.")
cmd.Flags().BoolVar(&autoDefrag, "auto-defrag", false, "Defragment storage after test is finished.")
return cmd
}
// newCheckDatascaleCommand executes the "check datascale" command.
func newCheckDatascaleCommand(cmd *cobra.Command, args []string) {
checkDatascaleAlias := map[string]string{
"s": "s", "small": "s",
"m": "m", "medium": "m",
"l": "l", "large": "l",
"xl": "xl", "xLarge": "xl",
}
model, ok := checkDatascaleAlias[checkDatascaleLoad]
if !ok {
cobrautl.ExitWithError(cobrautl.ExitBadFeature, fmt.Errorf("unknown load option %v", checkDatascaleLoad))
}
cfg := checkDatascaleCfgMap[model]
requests := make(chan v3.Op, cfg.clients)
cc := clientConfigFromCmd(cmd)
clients := make([]*v3.Client, cfg.clients)
for i := 0; i < cfg.clients; i++ {
clients[i] = mustClient(cc)
}
// get endpoints
eps, errEndpoints := endpointsFromCmd(cmd)
if errEndpoints != nil {
cobrautl.ExitWithError(cobrautl.ExitError, errEndpoints)
}
sec := secureCfgFromCmd(cmd)
ctx, cancel := context.WithCancel(context.Background())
resp, err := clients[0].Get(ctx, checkDatascalePrefix, v3.WithPrefix(), v3.WithLimit(1))
cancel()
if err != nil {
cobrautl.ExitWithError(cobrautl.ExitError, err)
}
if len(resp.Kvs) > 0 {
cobrautl.ExitWithError(cobrautl.ExitInvalidInput, fmt.Errorf("prefix %q has keys. Delete with etcdctl del --prefix %s first", checkDatascalePrefix, checkDatascalePrefix))
}
ksize, vsize := 512, 512
k, v := make([]byte, ksize), string(make([]byte, vsize))
r := report.NewReport("%4.4f", "", false)
var wg sync.WaitGroup
wg.Add(len(clients))
// get the process_resident_memory_bytes and process_virtual_memory_bytes before the put operations
bytesBefore := endpointMemoryMetrics(eps[0], sec)
if bytesBefore == 0 {
fmt.Println("FAIL: Could not read process_resident_memory_bytes before the put operations.")
os.Exit(cobrautl.ExitError)
}
fmt.Printf("Start data scale check for work load [%v key-value pairs, %v bytes per key-value, %v concurrent clients].\n", cfg.limit, cfg.kvSize, cfg.clients)
bar := pb.New(cfg.limit)
bar.Start()
for i := range clients {
go func(c *v3.Client) {
defer wg.Done()
for op := range requests {
st := time.Now()
_, derr := c.Do(context.Background(), op)
r.Results() <- report.Result{Err: derr, Start: st, End: time.Now()}
bar.Increment()
}
}(clients[i])
}
go func() {
for i := 0; i < cfg.limit; i++ {
binary.PutVarint(k, rand.Int63n(math.MaxInt64))
requests <- v3.OpPut(checkDatascalePrefix+string(k), v)
}
close(requests)
}()
sc := r.Stats()
wg.Wait()
close(r.Results())
bar.Finish()
s := <-sc
// get the process_resident_memory_bytes after the put operations
bytesAfter := endpointMemoryMetrics(eps[0], sec)
if bytesAfter == 0 {
fmt.Println("FAIL: Could not read process_resident_memory_bytes after the put operations.")
os.Exit(cobrautl.ExitError)
}
// delete the created kv pairs
ctx, cancel = context.WithCancel(context.Background())
dresp, derr := clients[0].Delete(ctx, checkDatascalePrefix, v3.WithPrefix())
defer cancel()
if derr != nil {
cobrautl.ExitWithError(cobrautl.ExitError, derr)
}
if autoCompact {
compact(clients[0], dresp.Header.Revision)
}
if autoDefrag {
for _, ep := range clients[0].Endpoints() {
defrag(clients[0], ep)
}
}
if bytesAfter == 0 {
fmt.Println("FAIL: Could not read process_resident_memory_bytes after the put operations.")
os.Exit(cobrautl.ExitError)
}
bytesUsed := bytesAfter - bytesBefore
mbUsed := bytesUsed / (1024 * 1024)
if len(s.ErrorDist) != 0 {
fmt.Println("FAIL: too many errors")
for k, v := range s.ErrorDist {
fmt.Printf("FAIL: ERROR(%v) -> %d\n", k, v)
}
os.Exit(cobrautl.ExitError)
}
fmt.Printf("PASS: Approximate system memory used : %v MB.\n", strconv.FormatFloat(mbUsed, 'f', 2, 64))
}
================================================
FILE: etcdctl/ctlv3/command/compaction_command.go
================================================
// Copyright 2015 The etcd Authors
//
// 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 command
import (
"fmt"
"strconv"
"github.com/spf13/cobra"
clientv3 "go.etcd.io/etcd/client/v3"
"go.etcd.io/etcd/pkg/v3/cobrautl"
)
var compactPhysical bool
// NewCompactionCommand returns the cobra command for "compaction".
func NewCompactionCommand() *cobra.Command {
cmd := &cobra.Command{
Use: "compaction [options] ",
Short: "Compacts the event history in etcd",
Run: compactionCommandFunc,
GroupID: groupKVID,
}
cmd.Flags().BoolVar(&compactPhysical, "physical", false, "'true' to wait for compaction to physically remove all old revisions")
return cmd
}
// compactionCommandFunc executes the "compaction" command.
func compactionCommandFunc(cmd *cobra.Command, args []string) {
if len(args) != 1 {
cobrautl.ExitWithError(cobrautl.ExitBadArgs, fmt.Errorf("compaction command needs 1 argument"))
}
rev, err := strconv.ParseInt(args[0], 10, 64)
if err != nil {
cobrautl.ExitWithError(cobrautl.ExitError, err)
}
var opts []clientv3.CompactOption
if compactPhysical {
opts = append(opts, clientv3.WithCompactPhysical())
}
c := mustClientFromCmd(cmd)
ctx, cancel := commandCtx(cmd)
_, cerr := c.Compact(ctx, rev, opts...)
cancel()
if cerr != nil {
cobrautl.ExitWithError(cobrautl.ExitError, cerr)
}
fmt.Println("compacted revision", rev)
}
================================================
FILE: etcdctl/ctlv3/command/completion_command.go
================================================
// Copyright 2021 The etcd Authors
//
// 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 command
import (
"os"
"github.com/spf13/cobra"
)
func NewCompletionCommand() *cobra.Command {
cmd := &cobra.Command{
Use: "completion [bash|zsh|fish|powershell]",
Short: "Generate completion script",
Long: `To load completions:
Bash:
$ source <(etcdctl completion bash)
# To load completions for each session, execute once:
# Linux:
$ etcdctl completion bash > /etc/bash_completion.d/etcdctl
# macOS:
$ etcdctl completion bash > /usr/local/etc/bash_completion.d/etcdctl
Zsh:
# If shell completion is not already enabled in your environment,
# you will need to enable it. You can execute the following once:
$ echo "autoload -U compinit; compinit" >> ~/.zshrc
# To load completions for each session, execute once:
$ etcdctl completion zsh > "${fpath[1]}/_etcdctl"
# You will need to start a new shell for this setup to take effect.
fish:
$ etcdctl completion fish | source
# To load completions for each session, execute once:
$ etcdctl completion fish > ~/.config/fish/completions/etcdctl.fish
PowerShell:
PS> etcdctl completion powershell | Out-String | Invoke-Expression
# To load completions for every new session, run:
PS> etcdctl completion powershell > etcdctl.ps1
# and source this file from your PowerShell profile.
`,
DisableFlagsInUseLine: true,
ValidArgs: []string{"bash", "zsh", "fish", "powershell"},
Args: cobra.MatchAll(cobra.ExactArgs(1), cobra.OnlyValidArgs),
Run: func(cmd *cobra.Command, args []string) {
switch args[0] {
case "bash":
cmd.Root().GenBashCompletion(os.Stdout)
case "zsh":
cmd.Root().GenZshCompletion(os.Stdout)
case "fish":
cmd.Root().GenFishCompletion(os.Stdout, true)
case "powershell":
cmd.Root().GenPowerShellCompletionWithDesc(os.Stdout)
}
},
GroupID: groupUtilityID,
}
return cmd
}
================================================
FILE: etcdctl/ctlv3/command/defrag_command.go
================================================
// Copyright 2016 The etcd Authors
//
// 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 command
import (
"fmt"
"os"
"time"
"github.com/spf13/cobra"
"go.etcd.io/etcd/pkg/v3/cobrautl"
)
// NewDefragCommand returns the cobra command for "Defrag".
func NewDefragCommand() *cobra.Command {
cmd := &cobra.Command{
Use: "defrag",
Short: "Defragments the storage of the etcd members with given endpoints",
Run: defragCommandFunc,
GroupID: groupClusterMaintenanceID,
}
cmd.PersistentFlags().BoolVar(&epClusterEndpoints, "cluster", false, "use all endpoints from the cluster member list")
return cmd
}
func defragCommandFunc(cmd *cobra.Command, args []string) {
failures := 0
cfg := clientConfigFromCmd(cmd)
for _, ep := range endpointsFromCluster(cmd) {
cfg.Endpoints = []string{ep}
c := mustClient(cfg)
ctx, cancel := commandCtx(cmd)
start := time.Now()
_, err := c.Defragment(ctx, ep)
d := time.Since(start)
cancel()
if err != nil {
fmt.Fprintf(os.Stderr, "Failed to defragment etcd member[%s]. took %s. (%v)\n", ep, d.String(), err)
failures++
} else {
fmt.Printf("Finished defragmenting etcd member[%s]. took %s\n", ep, d.String())
}
c.Close()
}
if failures != 0 {
os.Exit(cobrautl.ExitError)
}
}
================================================
FILE: etcdctl/ctlv3/command/del_command.go
================================================
// Copyright 2015 The etcd Authors
//
// 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 command
import (
"fmt"
"os"
"time"
"github.com/spf13/cobra"
clientv3 "go.etcd.io/etcd/client/v3"
"go.etcd.io/etcd/pkg/v3/cobrautl"
)
var (
delPrefix bool
delPrevKV bool
delFromKey bool
delRange bool
)
// NewDelCommand returns the cobra command for "del".
func NewDelCommand() *cobra.Command {
cmd := &cobra.Command{
Use: "del [options] [range_end]",
Short: "Removes the specified key or range of keys [key, range_end)",
Run: delCommandFunc,
GroupID: groupKVID,
}
cmd.Flags().BoolVar(&delPrefix, "prefix", false, "delete keys with matching prefix")
cmd.Flags().BoolVar(&delPrevKV, "prev-kv", false, "return deleted key-value pairs")
cmd.Flags().BoolVar(&delFromKey, "from-key", false, "delete keys that are greater than or equal to the given key using byte compare")
cmd.Flags().BoolVar(&delRange, "range", false, "delete range of keys")
return cmd
}
// delCommandFunc executes the "del" command.
func delCommandFunc(cmd *cobra.Command, args []string) {
key, opts := getDelOp(args)
ctx, cancel := commandCtx(cmd)
resp, err := mustClientFromCmd(cmd).Delete(ctx, key, opts...)
cancel()
if err != nil {
cobrautl.ExitWithError(cobrautl.ExitError, err)
}
display.Del(*resp)
}
func getDelOp(args []string) (string, []clientv3.OpOption) {
if len(args) == 0 || len(args) > 2 {
cobrautl.ExitWithError(cobrautl.ExitBadArgs, fmt.Errorf("del command needs one argument as key and an optional argument as range_end"))
}
if delPrefix && delFromKey {
cobrautl.ExitWithError(cobrautl.ExitBadArgs, fmt.Errorf("`--prefix` and `--from-key` cannot be set at the same time, choose one"))
}
var opts []clientv3.OpOption
key := args[0]
if len(args) > 1 {
if delPrefix || delFromKey {
cobrautl.ExitWithError(cobrautl.ExitBadArgs, fmt.Errorf("too many arguments, only accept one argument when `--prefix` or `--from-key` is set"))
}
opts = append(opts, clientv3.WithRange(args[1]))
if !delRange {
fmt.Fprintf(os.Stderr, "Warning: Keys between %q and %q will be deleted. Please interrupt the command within next 2 seconds to cancel. "+
"You can provide `--range` flag to avoid the delay.\n", args[0], args[1])
time.Sleep(2 * time.Second)
}
}
if delPrefix {
if len(key) == 0 {
key = "\x00"
opts = append(opts, clientv3.WithFromKey())
} else {
opts = append(opts, clientv3.WithPrefix())
}
}
if delPrevKV {
opts = append(opts, clientv3.WithPrevKV())
}
if delFromKey {
if len(key) == 0 {
key = "\x00"
}
opts = append(opts, clientv3.WithFromKey())
}
return key, opts
}
================================================
FILE: etcdctl/ctlv3/command/diagnosis/engine/diagnosis.go
================================================
// Copyright 2025 The etcd Authors
//
// 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 engine
import (
"encoding/json"
"go.etcd.io/etcd/etcdctl/v3/ctlv3/command/diagnosis/engine/intf"
)
type report struct {
Input any `json:"input,omitempty"`
Results []any `json:"results,omitempty"`
}
// Diagnose runs all provided plugins and returns a JSON report.
// It logs plugin progress and individual results to stderr.
func Diagnose(input any, plugins []intf.Plugin) ([]byte, error) {
rp := report{
Input: input,
}
for _, plugin := range plugins {
result := plugin.Diagnose()
rp.Results = append(rp.Results, result)
}
return json.MarshalIndent(rp, "", "\t")
}
================================================
FILE: etcdctl/ctlv3/command/diagnosis/engine/intf/plugin.go
================================================
// Copyright 2025 The etcd Authors
//
// 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 intf
type Plugin interface {
// Name returns the name of the plugin
Name() string
// Diagnose performs diagnosis and returns the result. If it fails
// to do the diagnosis for any reason, it gets the detailed reason
// included in the diagnosis result.
Diagnose() any
}
// FailedResult is the result returned by a plugin if it fails to
// perform the diagnosis for any reason.
type FailedResult struct {
Name string `json:"name"`
Reason string `json:"reason"`
}
================================================
FILE: etcdctl/ctlv3/command/diagnosis/examples/etcd_diagnosis_report.json
================================================
{
"input": {
"endpoints": [
"http://127.0.0.1:2379"
],
"useClusterEndpoints": true,
"dial-timeout": 2000000000,
"command-timeout": 5000000000,
"keep-alive-time": 2000000000,
"keep-alive-timeout": 5000000000,
"insecure": true,
"insecure-discovery": true,
"db-quota-bytes": 2147483648
},
"results": [
{
"name": "membershipChecker",
"memberList": {
"header": {
"cluster_id": 17237436991929493444,
"member_id": 9372538179322589801,
"raft_term": 2
},
"members": [
{
"ID": 9372538179322589801,
"name": "infra1",
"peerURLs": [
"http://127.0.0.1:12380"
],
"clientURLs": [
"http://127.0.0.1:2379"
]
},
{
"ID": 10501334649042878790,
"name": "infra2",
"peerURLs": [
"http://127.0.0.1:22380"
],
"clientURLs": [
"http://127.0.0.1:22379"
]
},
{
"ID": 18249187646912138824,
"name": "infra3",
"peerURLs": [
"http://127.0.0.1:32380"
],
"clientURLs": [
"http://127.0.0.1:32379"
]
}
]
}
},
{
"name": "epStatusChecker",
"summary": [
"Successful"
],
"epStatusList": [
{
"endpoint": "http://127.0.0.1:2379",
"epStatus": {
"header": {
"cluster_id": 17237436991929493444,
"member_id": 9372538179322589801,
"revision": 1,
"raft_term": 2
},
"version": "3.5.9",
"dbSize": 98304,
"leader": 18249187646912138824,
"raftIndex": 8,
"raftTerm": 2,
"raftAppliedIndex": 8,
"dbSizeInUse": 98304
}
},
{
"endpoint": "http://127.0.0.1:22379",
"epStatus": {
"header": {
"cluster_id": 17237436991929493444,
"member_id": 10501334649042878790,
"revision": 1,
"raft_term": 2
},
"version": "3.5.9",
"dbSize": 98304,
"leader": 18249187646912138824,
"raftIndex": 8,
"raftTerm": 2,
"raftAppliedIndex": 8,
"dbSizeInUse": 98304
}
},
{
"endpoint": "http://127.0.0.1:32379",
"epStatus": {
"header": {
"cluster_id": 17237436991929493444,
"member_id": 18249187646912138824,
"revision": 1,
"raft_term": 2
},
"version": "3.5.9",
"dbSize": 98304,
"leader": 18249187646912138824,
"raftIndex": 8,
"raftTerm": 2,
"raftAppliedIndex": 8,
"dbSizeInUse": 98304
}
}
]
},
{
"name": "serializableReadChecker",
"summary": "Successful",
"readResponses": [
{
"endpoint": "http://127.0.0.1:2379",
"took": "686.5µs"
},
{
"endpoint": "http://127.0.0.1:22379",
"took": "1.129291ms"
},
{
"endpoint": "http://127.0.0.1:32379",
"took": "1.034625ms"
}
]
},
{
"name": "linearizableReadChecker",
"summary": "Successful",
"readResponses": [
{
"endpoint": "http://127.0.0.1:2379",
"took": "1.286333ms"
},
{
"endpoint": "http://127.0.0.1:22379",
"took": "890.417µs"
},
{
"endpoint": "http://127.0.0.1:32379",
"took": "1.257791ms"
}
]
},
{
"name": "metricsChecker",
"summary": [
"Successful"
],
"epMetricsList": [
{
"endpoint": "http://127.0.0.1:2379",
"took": "3.752625ms",
"epMetrics": {
"etcd_disk_backend_commit_duration_seconds_bucket": [
"etcd_disk_backend_commit_duration_seconds_bucket{le=\"0.001\"} 0"
],
"etcd_disk_wal_fsync_duration_seconds_bucket": [
"etcd_disk_wal_fsync_duration_seconds_bucket{le=\"0.001\"} 0"
],
"etcd_network_peer_round_trip_time_seconds_bucket": [
"etcd_network_peer_round_trip_time_seconds_bucket{To=\"91bc3c398fb3c146\",le=\"0.0001\"} 2"
],
"process_resident_memory_bytes": null
}
}
]
}
]
}
================================================
FILE: etcdctl/ctlv3/command/diagnosis/plugins/common/checker.go
================================================
// Copyright 2025 The etcd Authors
//
// 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 common
import (
"time"
clientv3 "go.etcd.io/etcd/client/v3"
)
// Checker carries shared configuration for diagnosis plugins.
// It embeds generic options such as the etcd client configuration,
// resolved endpoints, and command timeout.
type Checker struct {
Cfg *clientv3.ConfigSpec
Endpoints []string
CommandTimeout time.Duration
DbQuotaBytes int64
Name string
}
================================================
FILE: etcdctl/ctlv3/command/diagnosis/plugins/common/client.go
================================================
// Copyright 2025 The etcd Authors
//
// 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 common
import (
"go.uber.org/zap"
"go.etcd.io/etcd/client/pkg/v3/logutil"
clientv3 "go.etcd.io/etcd/client/v3"
)
// NewClient creates an etcd client from the given configuration spec.
func NewClient(cfg *clientv3.ConfigSpec) (*clientv3.Client, error) {
lg, _ := logutil.CreateDefaultZapLogger(zap.InfoLevel)
cliCfg, err := clientv3.NewClientConfig(cfg, lg)
if err != nil {
return nil, err
}
return clientv3.New(*cliCfg)
}
// ConfigWithEndpoint returns a shallow copy of cfg with Endpoints set to the
// provided single endpoint.
func ConfigWithEndpoint(cfg *clientv3.ConfigSpec, ep string) *clientv3.ConfigSpec {
c := *cfg
c.Endpoints = []string{ep}
return &c
}
================================================
FILE: etcdctl/ctlv3/command/diagnosis/plugins/epstatus/plugin.go
================================================
// Copyright 2025 The etcd Authors
//
// 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 epstatus
import (
"context"
"fmt"
"log"
"time"
clientv3 "go.etcd.io/etcd/client/v3"
"go.etcd.io/etcd/etcdctl/v3/ctlv3/command/diagnosis/engine/intf"
"go.etcd.io/etcd/etcdctl/v3/ctlv3/command/diagnosis/plugins/common"
)
type epStatusChecker struct {
common.Checker
}
type epStatus struct {
Endpoint string `json:"endpoint,omitempty"`
EpStatus *clientv3.StatusResponse `json:"epStatus,omitempty"`
}
type checkResult struct {
Name string `json:"name,omitempty"`
Summary []string `json:"summary,omitempty"`
EpStatusList []epStatus `json:"epStatusList,omitempty"`
}
func NewPlugin(cfg *clientv3.ConfigSpec, eps []string, timeout time.Duration, dbQuota int64) intf.Plugin {
return &epStatusChecker{
Checker: common.Checker{
Cfg: cfg,
Endpoints: eps,
CommandTimeout: timeout,
DbQuotaBytes: dbQuota,
Name: "epStatusChecker",
},
}
}
func (ck *epStatusChecker) Name() string {
return ck.Checker.Name
}
func (ck *epStatusChecker) Diagnose() (result any) {
var err error
eps := ck.Endpoints
defer func() {
if err != nil {
result = &intf.FailedResult{
Name: ck.Name(),
Reason: err.Error(),
}
}
}()
var (
maxRetries = 3
retries = 0
shouldRetry = true
chkResult = initCheckResult(ck.Name(), len(eps))
)
for {
for i, ep := range eps {
chkResult.EpStatusList[i].Endpoint = ep
cfg := common.ConfigWithEndpoint(ck.Cfg, ep)
c, err := common.NewClient(cfg)
if err != nil {
appendSummary(&chkResult, "Failed to create client for %q: %v", ep, err)
continue
}
ctx, cancel := context.WithTimeout(context.Background(), ck.CommandTimeout)
chkResult.EpStatusList[i].EpStatus, err = c.Status(ctx, ep)
cancel()
c.Close()
if err != nil {
appendSummary(&chkResult, "Failed to get endpoint status from %q: %v", ep, err)
continue
}
if len(chkResult.EpStatusList[i].EpStatus.Errors) > 0 {
appendSummary(&chkResult, "Detected errors in endpoint %q: %v\n", ep, chkResult.EpStatusList[i].EpStatus.Errors)
shouldRetry = false
continue
}
if i > 0 {
if !compareHardInfo(chkResult.EpStatusList[0].EpStatus, chkResult.EpStatusList[i].EpStatus) {
appendSummary(&chkResult, "Detected inconsistent hard endpoint info between %q and %q\n", eps[0], eps[i])
shouldRetry = false
}
if !shouldRetry {
continue
}
if !compareSoftInfo(chkResult.EpStatusList[0].EpStatus, chkResult.EpStatusList[i].EpStatus) {
appendSummary(&chkResult, "Detected inconsistent soft endpoint info between %q and %q\n", eps[0], eps[i])
}
}
}
retries++
if len(chkResult.Summary) == 0 || !shouldRetry || retries >= maxRetries {
break
}
chkResult = initCheckResult(ck.Name(), len(eps))
log.Printf("Retrying checking endpoint status: %d/%d\n", retries, maxRetries)
time.Sleep(time.Second)
}
checkDBSize(&chkResult, ck.DbQuotaBytes)
if len(chkResult.Summary) == 0 {
chkResult.Summary = []string{"Successful"}
}
result = chkResult
return result
}
func initCheckResult(name string, epCount int) checkResult {
return checkResult{
Name: name,
Summary: []string{},
EpStatusList: make([]epStatus, epCount),
}
}
func appendSummary(chkResult *checkResult, format string, v ...any) {
errMsg := fmt.Sprintf(format, v...)
log.Println(errMsg)
chkResult.Summary = append(chkResult.Summary, errMsg)
}
func compareHardInfo(s1, s2 *clientv3.StatusResponse) bool {
if s1 == nil || s2 == nil {
return false
}
return s1.Header.ClusterId == s2.Header.ClusterId &&
s1.Version == s2.Version &&
s1.StorageVersion == s2.StorageVersion
}
func compareSoftInfo(s1, s2 *clientv3.StatusResponse) bool {
if s1 == nil || s2 == nil {
return false
}
return s1.Header.Revision == s2.Header.Revision &&
s1.RaftTerm == s2.RaftTerm &&
s1.RaftIndex == s2.RaftIndex &&
s1.RaftAppliedIndex == s2.RaftAppliedIndex &&
s1.Leader == s2.Leader
}
func checkDBSize(chkResult *checkResult, dbQuota int64) {
for _, sts := range chkResult.EpStatusList {
if sts.EpStatus == nil {
continue
}
freeSize := sts.EpStatus.DbSize - sts.EpStatus.DbSizeInUse
if freeSize > sts.EpStatus.DbSizeInUse && freeSize > 1_000_000_000 /* about 1GB */ || sts.EpStatus.DbSize >= dbQuota*80/100 {
appendSummary(chkResult, "Detected large amount of db [free] space for endpoint %q, dbQuota: %d, dbSize: %d, dbSizeInUse: %d, dbSizeFree: %d", sts.Endpoint, dbQuota, sts.EpStatus.DbSize, sts.EpStatus.DbSizeInUse, freeSize)
}
}
}
================================================
FILE: etcdctl/ctlv3/command/diagnosis/plugins/membership/plugin.go
================================================
// Copyright 2025 The etcd Authors
//
// 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 membership
import (
"context"
"log"
"reflect"
"time"
clientv3 "go.etcd.io/etcd/client/v3"
"go.etcd.io/etcd/etcdctl/v3/ctlv3/command/diagnosis/engine/intf"
"go.etcd.io/etcd/etcdctl/v3/ctlv3/command/diagnosis/plugins/common"
)
type membershipChecker struct {
common.Checker
}
type checkResult struct {
Name string `json:"name,omitempty"`
Summary string `json:"summary,omitempty"`
MemberList *clientv3.MemberListResponse `json:"memberList,omitempty"`
AllMemberLists []*clientv3.MemberListResponse `json:"allMemberLists,omitempty"`
}
func NewPlugin(cfg *clientv3.ConfigSpec, eps []string, timeout time.Duration) intf.Plugin {
return &membershipChecker{
Checker: common.Checker{
Cfg: cfg,
Endpoints: eps,
CommandTimeout: timeout,
Name: "membershipChecker",
},
}
}
func (ck *membershipChecker) Name() string {
return ck.Checker.Name
}
func (ck *membershipChecker) Diagnose() (result any) {
var err error
eps := ck.Endpoints
defer func() {
if err != nil {
result = &intf.FailedResult{
Name: ck.Name(),
Reason: err.Error(),
}
}
}()
memberLists := make([]*clientv3.MemberListResponse, len(eps))
detectedInconsistency := false
for i, ep := range eps {
cfg := common.ConfigWithEndpoint(ck.Cfg, ep)
c, err := common.NewClient(cfg)
if err != nil {
detectedInconsistency = true
log.Printf("Failed to create client for %q: %v\n", ep, err)
continue
}
ctx, cancel := context.WithTimeout(context.Background(), ck.CommandTimeout)
memberLists[i], err = c.MemberList(ctx, clientv3.WithSerializable())
cancel()
c.Close()
if err != nil {
detectedInconsistency = true
log.Printf("Failed to get member list from %q: %v\n", ep, err)
continue
}
if i > 0 {
if !compareMembers(memberLists[0], memberLists[i]) {
detectedInconsistency = true
log.Printf("Detected inconsistent member list between %q and %q\n", eps[0], eps[i])
}
}
}
if detectedInconsistency {
result = checkResult{
Name: ck.Name(),
Summary: "Detected inconsistent member list between different members",
AllMemberLists: memberLists,
}
} else {
result = checkResult{
Name: ck.Name(),
Summary: "Successful",
MemberList: memberLists[0],
}
}
return result
}
func compareMembers(m1, m2 *clientv3.MemberListResponse) bool {
if m1 == nil || m2 == nil {
return false
}
return m1.Header.ClusterId == m2.Header.ClusterId && reflect.DeepEqual(m1.Members, m2.Members)
}
================================================
FILE: etcdctl/ctlv3/command/diagnosis/plugins/metrics/plugin.go
================================================
// Copyright 2025 The etcd Authors
//
// 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 metrics
import (
"crypto/tls"
"crypto/x509"
"fmt"
"io"
"log"
"net/http"
"net/url"
"os"
"strings"
"time"
clientv3 "go.etcd.io/etcd/client/v3"
"go.etcd.io/etcd/etcdctl/v3/ctlv3/command/diagnosis/engine/intf"
"go.etcd.io/etcd/etcdctl/v3/ctlv3/command/diagnosis/plugins/common"
)
var metricsNames = []string{
"etcd_disk_wal_fsync_duration_seconds_bucket",
"etcd_disk_backend_commit_duration_seconds_bucket",
"etcd_network_peer_round_trip_time_seconds_bucket",
"process_resident_memory_bytes",
//"process_cpu_seconds_total",
}
type metricsChecker struct {
common.Checker
}
type epMetrics struct {
Endpoint string `json:"endpoint,omitempty"`
Took string `json:"took,omitempty"`
EpMetrics map[string][]string `json:"epMetrics,omitempty"`
}
type checkResult struct {
Name string `json:"name,omitempty"`
Summary []string `json:"summary,omitempty"`
EpMetricsList []epMetrics `json:"epMetricsList,omitempty"`
}
func NewPlugin(cfg *clientv3.ConfigSpec, eps []string, timeout time.Duration) intf.Plugin {
return &metricsChecker{
Checker: common.Checker{
Cfg: cfg,
Endpoints: eps,
CommandTimeout: timeout,
Name: "metricsChecker",
},
}
}
func (ck *metricsChecker) Name() string {
return ck.Checker.Name
}
func (ck *metricsChecker) Diagnose() (result any) {
var err error
eps := ck.Endpoints
defer func() {
if err != nil {
result = &intf.FailedResult{
Name: ck.Name(),
Reason: err.Error(),
}
}
}()
chkResult := checkResult{
Name: ck.Name(),
Summary: []string{},
EpMetricsList: make([]epMetrics, len(eps)),
}
for i, ep := range eps {
chkResult.EpMetricsList[i].Endpoint = ep
startTs := time.Now()
allMetrics, err := fetchMetrics(ck.Cfg, ep, ck.CommandTimeout)
chkResult.EpMetricsList[i].Took = time.Since(startTs).String()
if err != nil {
appendSummary(&chkResult, "Failed to get endpoint metrics from %q: %v", ep, err)
continue
}
metricsMap := map[string][]string{}
for _, prefix := range metricsNames {
ret := metrics(allMetrics, prefix)
metricsMap[prefix] = ret
}
chkResult.EpMetricsList[i].EpMetrics = metricsMap
}
if len(chkResult.Summary) == 0 {
chkResult.Summary = []string{"Successful"}
}
result = chkResult
return result
}
func metrics(lines []string, prefix string) []string {
var ret []string
for _, line := range lines {
if strings.HasPrefix(line, prefix) {
ret = append(ret, line)
}
}
return ret
}
func appendSummary(chkResult *checkResult, format string, v ...any) {
errMsg := fmt.Sprintf(format, v...)
log.Println(errMsg)
chkResult.Summary = append(chkResult.Summary, errMsg)
}
func fetchMetrics(cfg *clientv3.ConfigSpec, ep string, timeout time.Duration) ([]string, error) {
if !strings.HasPrefix(ep, "http://") && !strings.HasPrefix(ep, "https://") {
ep = "http://" + ep
}
urlPath, err := url.JoinPath(ep, "metrics")
if err != nil {
return nil, fmt.Errorf("failed to join metrics url path: %w", err)
}
client := &http.Client{Timeout: timeout}
if strings.HasPrefix(urlPath, "https://") && cfg.Secure != nil {
cert, certErr := tls.LoadX509KeyPair(cfg.Secure.Cert, cfg.Secure.Key)
if certErr != nil {
return nil, fmt.Errorf("failed to load certificate: %w", certErr)
}
caCert, caErr := os.ReadFile(cfg.Secure.Cacert)
if caErr != nil {
return nil, fmt.Errorf("failed to load CA: %w", caErr)
}
caCertPool := x509.NewCertPool()
caCertPool.AppendCertsFromPEM(caCert)
tr := &http.Transport{
TLSClientConfig: &tls.Config{
Certificates: []tls.Certificate{cert},
RootCAs: caCertPool,
InsecureSkipVerify: cfg.Secure.InsecureSkipVerify,
},
}
client.Transport = tr
}
resp, err := client.Get(urlPath)
if err != nil {
return nil, fmt.Errorf("http get failed: %w", err)
}
defer resp.Body.Close()
data, err := io.ReadAll(resp.Body)
if err != nil {
return nil, fmt.Errorf("failed to read metrics response: %w", err)
}
return strings.Split(string(data), "\n"), nil
}
================================================
FILE: etcdctl/ctlv3/command/diagnosis/plugins/read/plugin.go
================================================
// Copyright 2025 The etcd Authors
//
// 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 read
import (
"context"
"errors"
"log"
"time"
"go.etcd.io/etcd/api/v3/v3rpc/rpctypes"
clientv3 "go.etcd.io/etcd/client/v3"
"go.etcd.io/etcd/etcdctl/v3/ctlv3/command/diagnosis/engine/intf"
"go.etcd.io/etcd/etcdctl/v3/ctlv3/command/diagnosis/plugins/common"
)
type readChecker struct {
common.Checker
linearizable bool
}
type readResponse struct {
Endpoint string `json:"endpoint,omitempty"`
Took string `json:"took,omitempty"`
Error string `json:"error,omitempty"`
}
type checkResult struct {
Name string `json:"name,omitempty"`
Summary string `json:"summary,omitempty"`
ReadResponses []readResponse `json:"readResponses,omitempty"`
}
func NewPlugin(cfg *clientv3.ConfigSpec, eps []string, timeout time.Duration, linearizable bool) intf.Plugin {
return &readChecker{
Checker: common.Checker{
Cfg: cfg,
Endpoints: eps,
CommandTimeout: timeout,
Name: generateName(linearizable),
},
linearizable: linearizable,
}
}
func (ck *readChecker) Name() string {
return ck.Checker.Name
}
func generateName(linearizable bool) string {
if linearizable {
return "linearizableReadChecker"
}
return "serializableReadChecker"
}
func (ck *readChecker) Diagnose() (result any) {
var err error
eps := ck.Endpoints
defer func() {
if err != nil {
result = &intf.FailedResult{
Name: ck.Name(),
Reason: err.Error(),
}
}
}()
var (
maxRetries = 3
retries = 0
chkResult = initCheckResult(ck.Name(), len(eps))
)
for {
shouldRetry := false
for i, ep := range eps {
chkResult.ReadResponses[i].Endpoint = ep
startTs := time.Now()
cfg := common.ConfigWithEndpoint(ck.Cfg, ep)
c, err := common.NewClient(cfg)
if err != nil {
chkResult.ReadResponses[i].Error = err.Error()
shouldRetry = true
continue
}
ctx, cancel := context.WithTimeout(context.Background(), ck.CommandTimeout)
if ck.linearizable {
_, err = c.Get(ctx, "health")
} else {
_, err = c.Get(ctx, "health", clientv3.WithSerializable())
}
cancel()
c.Close()
if err != nil && !errors.Is(err, rpctypes.ErrPermissionDenied) {
chkResult.ReadResponses[i].Error = err.Error()
shouldRetry = true
}
chkResult.ReadResponses[i].Took = time.Since(startTs).String()
}
retries++
if !shouldRetry || retries >= maxRetries {
break
}
chkResult = initCheckResult(ck.Name(), len(eps))
log.Printf("Retrying checking read: %d/%d\n", retries, maxRetries)
time.Sleep(time.Second)
}
chkResult.Summary = "Successful"
for _, resp := range chkResult.ReadResponses {
if len(resp.Error) > 0 {
chkResult.Summary = "Unsuccessful"
break
}
}
result = chkResult
return result
}
func initCheckResult(name string, epCount int) checkResult {
return checkResult{
Name: name,
Summary: "",
ReadResponses: make([]readResponse, epCount),
}
}
================================================
FILE: etcdctl/ctlv3/command/diagnosis_command.go
================================================
// Copyright 2025 The etcd Authors
//
// 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 command
import (
"fmt"
"os"
"github.com/spf13/cobra"
"go.etcd.io/etcd/etcdctl/v3/ctlv3/command/diagnosis/engine"
"go.etcd.io/etcd/etcdctl/v3/ctlv3/command/diagnosis/engine/intf"
"go.etcd.io/etcd/etcdctl/v3/ctlv3/command/diagnosis/plugins/epstatus"
"go.etcd.io/etcd/etcdctl/v3/ctlv3/command/diagnosis/plugins/membership"
"go.etcd.io/etcd/etcdctl/v3/ctlv3/command/diagnosis/plugins/metrics"
readplugin "go.etcd.io/etcd/etcdctl/v3/ctlv3/command/diagnosis/plugins/read"
"go.etcd.io/etcd/pkg/v3/cobrautl"
)
var (
useCluster bool
dbQuotaBytes int64
outputFile string
)
// NewDiagnosisCommand returns the cobra command for "diagnosis".
func NewDiagnosisCommand() *cobra.Command {
cmd := &cobra.Command{
Use: "diagnosis",
Short: "One-stop etcd diagnosis tool",
Run: runDiagnosis,
GroupID: groupClusterMaintenanceID,
}
cmd.Flags().BoolVar(&useCluster, "cluster", false, "use all endpoints from the cluster member list")
cmd.Flags().Int64Var(&dbQuotaBytes, "etcd-storage-quota-bytes", 2*1024*1024*1024, "etcd storage quota in bytes (the value passed to etcd instance by flag --quota-backend-bytes)")
cmd.Flags().StringVarP(&outputFile, "output", "o", "", "write report to file instead of stdout")
return cmd
}
func runDiagnosis(cmd *cobra.Command, args []string) {
cfg := clientConfigFromCmd(cmd)
cli := mustClientFromCmd(cmd)
defer cli.Close()
eps := cfg.Endpoints
if useCluster {
ctx, cancel := commandCtx(cmd)
members, err := cli.MemberList(ctx)
cancel()
if err != nil {
fmt.Fprintf(os.Stderr, "failed to fetch member list: %v\n", err)
os.Exit(cobrautl.ExitError)
}
var clusterEps []string
for _, m := range members.Members {
clusterEps = append(clusterEps, m.ClientURLs...)
}
eps = clusterEps
cfg.Endpoints = eps
}
timeout, err := cmd.Flags().GetDuration("command-timeout")
if err != nil {
fmt.Fprintf(os.Stderr, "failed to get command-timeout: %v\n", err)
os.Exit(cobrautl.ExitError)
}
plugins := []intf.Plugin{
membership.NewPlugin(cfg, eps, timeout),
epstatus.NewPlugin(cfg, eps, timeout, dbQuotaBytes),
readplugin.NewPlugin(cfg, eps, timeout, false),
readplugin.NewPlugin(cfg, eps, timeout, true),
metrics.NewPlugin(cfg, eps, timeout),
}
report, err := engine.Diagnose(cfg, plugins)
if err != nil {
fmt.Fprintf(os.Stderr, "diagnosis failed: %v\n", err)
os.Exit(cobrautl.ExitError)
}
if outputFile != "" {
if err := os.WriteFile(outputFile, report, 0o644); err != nil {
fmt.Fprintf(os.Stderr, "failed to write report: %v\n", err)
os.Exit(cobrautl.ExitError)
}
return
}
fmt.Fprintln(os.Stdout, string(report))
}
================================================
FILE: etcdctl/ctlv3/command/doc.go
================================================
// Copyright 2015 The etcd Authors
//
// 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 command is a set of libraries for etcd v3 commands.
package command
================================================
FILE: etcdctl/ctlv3/command/downgrade_command.go
================================================
// Copyright 2016 The etcd Authors
//
// 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 command
import (
"errors"
"github.com/spf13/cobra"
clientv3 "go.etcd.io/etcd/client/v3"
"go.etcd.io/etcd/pkg/v3/cobrautl"
)
// NewDowngradeCommand returns the cobra command for "downgrade".
func NewDowngradeCommand() *cobra.Command {
dc := &cobra.Command{
Use: "downgrade ",
Short: "Downgrade related commands. Use `etcdctl downgrade --help` to see subcommands",
Long: "Downgrade related commands",
GroupID: groupClusterMaintenanceID,
}
dc.AddCommand(NewDowngradeValidateCommand())
dc.AddCommand(NewDowngradeEnableCommand())
dc.AddCommand(NewDowngradeCancelCommand())
return dc
}
// NewDowngradeValidateCommand returns the cobra command for "downgrade validate".
func NewDowngradeValidateCommand() *cobra.Command {
cc := &cobra.Command{
Use: "validate ",
Short: "Validate downgrade capability before starting downgrade",
Run: downgradeValidateCommandFunc,
}
return cc
}
// NewDowngradeEnableCommand returns the cobra command for "downgrade enable".
func NewDowngradeEnableCommand() *cobra.Command {
cc := &cobra.Command{
Use: "enable ",
Short: "Start a downgrade action to cluster",
Run: downgradeEnableCommandFunc,
}
return cc
}
// NewDowngradeCancelCommand returns the cobra command for "downgrade cancel".
func NewDowngradeCancelCommand() *cobra.Command {
cc := &cobra.Command{
Use: "cancel",
Short: "Cancel the ongoing downgrade action to cluster",
Run: downgradeCancelCommandFunc,
}
return cc
}
// downgradeValidateCommandFunc executes the "downgrade validate" command.
func downgradeValidateCommandFunc(cmd *cobra.Command, args []string) {
if len(args) < 1 {
cobrautl.ExitWithError(cobrautl.ExitBadArgs, errors.New("TARGET_VERSION not provided"))
}
if len(args) > 1 {
cobrautl.ExitWithError(cobrautl.ExitBadArgs, errors.New("too many arguments"))
}
targetVersion := args[0]
if len(targetVersion) == 0 {
cobrautl.ExitWithError(cobrautl.ExitBadArgs, errors.New("target version not provided"))
}
ctx, cancel := commandCtx(cmd)
cli := mustClientFromCmd(cmd)
resp, err := cli.Downgrade(ctx, clientv3.DowngradeValidate, targetVersion)
cancel()
if err != nil {
cobrautl.ExitWithError(cobrautl.ExitError, err)
}
display.DowngradeValidate(*resp)
}
// downgradeEnableCommandFunc executes the "downgrade enable" command.
func downgradeEnableCommandFunc(cmd *cobra.Command, args []string) {
if len(args) < 1 {
cobrautl.ExitWithError(cobrautl.ExitBadArgs, errors.New("TARGET_VERSION not provided"))
}
if len(args) > 1 {
cobrautl.ExitWithError(cobrautl.ExitBadArgs, errors.New("too many arguments"))
}
targetVersion := args[0]
if len(targetVersion) == 0 {
cobrautl.ExitWithError(cobrautl.ExitBadArgs, errors.New("target version not provided"))
}
ctx, cancel := commandCtx(cmd)
cli := mustClientFromCmd(cmd)
resp, err := cli.Downgrade(ctx, clientv3.DowngradeEnable, targetVersion)
cancel()
if err != nil {
cobrautl.ExitWithError(cobrautl.ExitError, err)
}
display.DowngradeEnable(*resp)
}
// downgradeCancelCommandFunc executes the "downgrade cancel" command.
func downgradeCancelCommandFunc(cmd *cobra.Command, args []string) {
ctx, cancel := commandCtx(cmd)
cli := mustClientFromCmd(cmd)
resp, err := cli.Downgrade(ctx, clientv3.DowngradeCancel, "")
cancel()
if err != nil {
cobrautl.ExitWithError(cobrautl.ExitError, err)
}
display.DowngradeCancel(*resp)
}
================================================
FILE: etcdctl/ctlv3/command/elect_command.go
================================================
// Copyright 2016 The etcd Authors
//
// 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 command
import (
"context"
"errors"
"os"
"os/signal"
"syscall"
"github.com/spf13/cobra"
clientv3 "go.etcd.io/etcd/client/v3"
"go.etcd.io/etcd/client/v3/concurrency"
"go.etcd.io/etcd/pkg/v3/cobrautl"
)
var electListen bool
// NewElectCommand returns the cobra command for "elect".
func NewElectCommand() *cobra.Command {
cmd := &cobra.Command{
Use: "elect [proposal]",
Short: "Observes and participates in leader election",
Run: electCommandFunc,
GroupID: groupConcurrencyID,
}
cmd.Flags().BoolVarP(&electListen, "listen", "l", false, "observation mode")
return cmd
}
func electCommandFunc(cmd *cobra.Command, args []string) {
if len(args) != 1 && len(args) != 2 {
cobrautl.ExitWithError(cobrautl.ExitBadArgs, errors.New("elect takes one election name argument and an optional proposal argument"))
}
c := mustClientFromCmd(cmd)
var err error
if len(args) == 1 {
if !electListen {
cobrautl.ExitWithError(cobrautl.ExitBadArgs, errors.New("no proposal argument but -l not set"))
}
err = observe(c, args[0])
} else {
if electListen {
cobrautl.ExitWithError(cobrautl.ExitBadArgs, errors.New("proposal given but -l is set"))
}
err = campaign(c, args[0], args[1])
}
if err != nil {
cobrautl.ExitWithError(cobrautl.ExitError, err)
}
}
func observe(c *clientv3.Client, election string) error {
s, err := concurrency.NewSession(c)
if err != nil {
return err
}
e := concurrency.NewElection(s, election)
ctx, cancel := context.WithCancel(context.TODO())
donec := make(chan struct{})
sigc := make(chan os.Signal, 1)
signal.Notify(sigc, syscall.SIGINT, syscall.SIGTERM)
go func() {
<-sigc
cancel()
}()
go func() {
for resp := range e.Observe(ctx) {
display.Get(resp)
}
close(donec)
}()
<-donec
select {
case <-ctx.Done():
default:
return errors.New("elect: observer lost")
}
return nil
}
func campaign(c *clientv3.Client, election string, prop string) error {
s, err := concurrency.NewSession(c)
if err != nil {
return err
}
e := concurrency.NewElection(s, election)
ctx, cancel := context.WithCancel(context.TODO())
donec := make(chan struct{})
sigc := make(chan os.Signal, 1)
signal.Notify(sigc, syscall.SIGINT, syscall.SIGTERM)
go func() {
<-sigc
cancel()
close(donec)
}()
if err = e.Campaign(ctx, prop); err != nil {
return err
}
// print key since elected
resp, err := c.Get(ctx, e.Key())
if err != nil {
return err
}
display.Get(*resp)
select {
case <-donec:
case <-s.Done():
return errors.New("elect: session expired")
}
return e.Resign(context.TODO())
}
================================================
FILE: etcdctl/ctlv3/command/ep_command.go
================================================
// Copyright 2015 The etcd Authors
//
// 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 command
import (
"errors"
"fmt"
"os"
"sync"
"time"
"github.com/spf13/cobra"
"go.uber.org/zap"
"go.etcd.io/etcd/api/v3/etcdserverpb"
"go.etcd.io/etcd/api/v3/v3rpc/rpctypes"
"go.etcd.io/etcd/client/pkg/v3/logutil"
clientv3 "go.etcd.io/etcd/client/v3"
"go.etcd.io/etcd/pkg/v3/cobrautl"
)
var (
epClusterEndpoints bool
epHashKVRev int64
)
// NewEndpointCommand returns the cobra command for "endpoint".
func NewEndpointCommand() *cobra.Command {
ec := &cobra.Command{
Use: "endpoint ",
Short: "Endpoint related commands. Use `etcdctl endpoint --help` to see subcommands",
Long: "Endpoint related commands",
GroupID: groupClusterMaintenanceID,
}
ec.PersistentFlags().BoolVar(&epClusterEndpoints, "cluster", false, "use all endpoints from the cluster member list")
ec.AddCommand(newEpHealthCommand())
ec.AddCommand(newEpStatusCommand())
ec.AddCommand(newEpHashKVCommand())
return ec
}
func newEpHealthCommand() *cobra.Command {
cmd := &cobra.Command{
Use: "health",
Short: "Checks the healthiness of endpoints specified in `--endpoints` flag",
Run: epHealthCommandFunc,
}
return cmd
}
func newEpStatusCommand() *cobra.Command {
return &cobra.Command{
Use: "status",
Short: "Prints out the status of endpoints specified in `--endpoints` flag",
Long: `When --write-out is set to simple, this command prints out comma-separated status lists for each endpoint.
The items in the lists are endpoint, ID, version, db size, is leader, is learner, raft term, raft index, raft applied index, errors.
`,
Run: epStatusCommandFunc,
}
}
func newEpHashKVCommand() *cobra.Command {
hc := &cobra.Command{
Use: "hashkv",
Short: "Prints the KV history hash for each endpoint in --endpoints",
Run: epHashKVCommandFunc,
}
hc.PersistentFlags().Int64Var(&epHashKVRev, "rev", 0, "maximum revision to hash (default: latest revision)")
return hc
}
type epHealth struct {
Ep string `json:"endpoint"`
Health bool `json:"health"`
Took string `json:"took"`
Error string `json:"error,omitempty"`
}
// epHealthCommandFunc executes the "endpoint-health" command.
func epHealthCommandFunc(cmd *cobra.Command, args []string) {
lg, err := logutil.CreateDefaultZapLogger(zap.InfoLevel)
if err != nil {
cobrautl.ExitWithError(cobrautl.ExitError, err)
}
cfgSpec := clientConfigFromCmd(cmd)
var cfgs []*clientv3.Config
for _, ep := range endpointsFromCluster(cmd) {
cloneCfgSpec := cfgSpec.Clone()
cloneCfgSpec.Endpoints = []string{ep}
cfg, err := clientv3.NewClientConfig(cloneCfgSpec, lg)
if err != nil {
cobrautl.ExitWithError(cobrautl.ExitBadArgs, err)
}
cfgs = append(cfgs, cfg)
}
var wg sync.WaitGroup
hch := make(chan epHealth, len(cfgs))
for _, cfg := range cfgs {
wg.Add(1)
go func(cfg *clientv3.Config) {
defer wg.Done()
ep := cfg.Endpoints[0]
cfg.Logger = lg.Named("client")
cli, err := clientv3.New(*cfg)
if err != nil {
hch <- epHealth{Ep: ep, Health: false, Error: err.Error()}
return
}
st := time.Now()
// get a random key. As long as we can get the response without an error, the
// endpoint is health.
ctx, cancel := commandCtx(cmd)
_, err = cli.Get(ctx, "health")
eh := epHealth{Ep: ep, Health: false, Took: time.Since(st).String()}
// permission denied is OK since proposal goes through consensus to get it
if err == nil || errors.Is(err, rpctypes.ErrPermissionDenied) {
eh.Health = true
} else {
eh.Error = err.Error()
}
if eh.Health {
resp, err := cli.AlarmList(ctx)
if err == nil && len(resp.Alarms) > 0 {
eh.Health = false
eh.Error = "Active Alarm(s): "
for _, v := range resp.Alarms {
switch v.Alarm {
case etcdserverpb.AlarmType_NOSPACE:
eh.Error = eh.Error + "NOSPACE "
case etcdserverpb.AlarmType_CORRUPT:
eh.Error = eh.Error + "CORRUPT "
default:
eh.Error = eh.Error + "UNKNOWN "
}
}
} else if err != nil {
eh.Health = false
eh.Error = "Unable to fetch the alarm list"
}
}
cancel()
hch <- eh
}(cfg)
}
wg.Wait()
close(hch)
errs := false
var healthList []epHealth
for h := range hch {
healthList = append(healthList, h)
if h.Error != "" {
errs = true
}
}
display.EndpointHealth(healthList)
if errs {
cobrautl.ExitWithError(cobrautl.ExitError, fmt.Errorf("unhealthy cluster"))
}
}
type epStatus struct {
Ep string `json:"Endpoint"`
Resp *clientv3.StatusResponse `json:"Status"`
}
func epStatusCommandFunc(cmd *cobra.Command, args []string) {
cfg := clientConfigFromCmd(cmd)
var statusList []epStatus
var err error
for _, ep := range endpointsFromCluster(cmd) {
cfg.Endpoints = []string{ep}
c := mustClient(cfg)
ctx, cancel := commandCtx(cmd)
resp, serr := c.Status(ctx, ep)
cancel()
c.Close()
if serr != nil {
err = serr
fmt.Fprintf(os.Stderr, "Failed to get the status of endpoint %s (%v)\n", ep, serr)
continue
}
statusList = append(statusList, epStatus{Ep: ep, Resp: resp})
}
display.EndpointStatus(statusList)
if err != nil {
os.Exit(cobrautl.ExitError)
}
}
type epHashKV struct {
Ep string `json:"Endpoint"`
Resp *clientv3.HashKVResponse `json:"HashKV"`
}
func epHashKVCommandFunc(cmd *cobra.Command, args []string) {
cfg := clientConfigFromCmd(cmd)
var hashList []epHashKV
var err error
for _, ep := range endpointsFromCluster(cmd) {
cfg.Endpoints = []string{ep}
c := mustClient(cfg)
ctx, cancel := commandCtx(cmd)
resp, serr := c.HashKV(ctx, ep, epHashKVRev)
cancel()
c.Close()
if serr != nil {
err = serr
fmt.Fprintf(os.Stderr, "Failed to get the hash of endpoint %s (%v)\n", ep, serr)
continue
}
hashList = append(hashList, epHashKV{Ep: ep, Resp: resp})
}
display.EndpointHashKV(hashList)
if err != nil {
cobrautl.ExitWithError(cobrautl.ExitError, err)
}
}
func endpointsFromCluster(cmd *cobra.Command) []string {
if !epClusterEndpoints {
endpoints, err := cmd.Flags().GetStringSlice("endpoints")
if err != nil {
cobrautl.ExitWithError(cobrautl.ExitError, err)
}
return endpoints
}
sec := secureCfgFromCmd(cmd)
dt := dialTimeoutFromCmd(cmd)
ka := keepAliveTimeFromCmd(cmd)
kat := keepAliveTimeoutFromCmd(cmd)
eps, err := endpointsFromCmd(cmd)
if err != nil {
cobrautl.ExitWithError(cobrautl.ExitError, err)
}
// exclude auth for not asking needless password (MemberList() doesn't need authentication)
lg, _ := logutil.CreateDefaultZapLogger(zap.InfoLevel)
cfg, err := clientv3.NewClientConfig(&clientv3.ConfigSpec{
Endpoints: eps,
DialTimeout: dt,
KeepAliveTime: ka,
KeepAliveTimeout: kat,
Secure: sec,
}, lg)
if err != nil {
cobrautl.ExitWithError(cobrautl.ExitError, err)
}
c, err := clientv3.New(*cfg)
if err != nil {
cobrautl.ExitWithError(cobrautl.ExitError, err)
}
ctx, cancel := commandCtx(cmd)
defer func() {
c.Close()
cancel()
}()
membs, err := c.MemberList(ctx)
if err != nil {
err = fmt.Errorf("failed to fetch endpoints from etcd cluster member list: %w", err)
cobrautl.ExitWithError(cobrautl.ExitError, err)
}
var ret []string
for _, m := range membs.Members {
ret = append(ret, m.ClientURLs...)
}
return ret
}
================================================
FILE: etcdctl/ctlv3/command/get_command.go
================================================
// Copyright 2015 The etcd Authors
//
// 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 command
import (
"fmt"
"strings"
"github.com/spf13/cobra"
clientv3 "go.etcd.io/etcd/client/v3"
"go.etcd.io/etcd/pkg/v3/cobrautl"
)
var (
getConsistency string
getLimit int64
getSortOrder string
getSortTarget string
getPrefix bool
getFromKey bool
getRev int64
getKeysOnly bool
getCountOnly bool
printValueOnly bool
getMinCreateRev int64
getMaxCreateRev int64
getMinModRev int64
getMaxModRev int64
)
// NewGetCommand returns the cobra command for "get".
func NewGetCommand() *cobra.Command {
cmd := &cobra.Command{
Use: "get [options] [range_end]",
Short: "Gets the key or a range of keys",
Run: getCommandFunc,
GroupID: groupKVID,
}
cmd.Flags().StringVar(&getConsistency, "consistency", "l", "Linearizable(l) or Serializable(s)")
cmd.Flags().StringVar(&getSortOrder, "order", "", "Order of results; ASCEND or DESCEND (ASCEND by default)")
cmd.Flags().StringVar(&getSortTarget, "sort-by", "", "Sort target; CREATE, KEY, MODIFY, VALUE, or VERSION")
cmd.Flags().Int64Var(&getLimit, "limit", 0, "Maximum number of results")
cmd.Flags().BoolVar(&getPrefix, "prefix", false, "Get keys with matching prefix")
cmd.Flags().BoolVar(&getFromKey, "from-key", false, "Get keys that are greater than or equal to the given key using byte compare")
cmd.Flags().Int64Var(&getRev, "rev", 0, "Specify the kv revision")
cmd.Flags().BoolVar(&getKeysOnly, "keys-only", false, "Get only the keys")
cmd.Flags().BoolVar(&getCountOnly, "count-only", false, "Get only the count")
cmd.Flags().BoolVar(&printValueOnly, "print-value-only", false, `Only write values when using the "simple" output format`)
cmd.Flags().Int64Var(&getMinCreateRev, "min-create-rev", 0, "Minimum create revision")
cmd.Flags().Int64Var(&getMaxCreateRev, "max-create-rev", 0, "Maximum create revision")
cmd.Flags().Int64Var(&getMinModRev, "min-mod-rev", 0, "Minimum modification revision")
cmd.Flags().Int64Var(&getMaxModRev, "max-mod-rev", 0, "Maximum modification revision")
cmd.RegisterFlagCompletionFunc("consistency", func(_ *cobra.Command, _ []string, _ string) ([]string, cobra.ShellCompDirective) {
return []string{"l", "s"}, cobra.ShellCompDirectiveDefault
})
cmd.RegisterFlagCompletionFunc("order", func(_ *cobra.Command, _ []string, _ string) ([]string, cobra.ShellCompDirective) {
return []string{"ASCEND", "DESCEND"}, cobra.ShellCompDirectiveDefault
})
cmd.RegisterFlagCompletionFunc("sort-by", func(_ *cobra.Command, _ []string, _ string) ([]string, cobra.ShellCompDirective) {
return []string{"CREATE", "KEY", "MODIFY", "VALUE", "VERSION"}, cobra.ShellCompDirectiveDefault
})
return cmd
}
// getCommandFunc executes the "get" command.
func getCommandFunc(cmd *cobra.Command, args []string) {
key, opts := getGetOp(args)
ctx, cancel := commandCtx(cmd)
resp, err := mustClientFromCmd(cmd).Get(ctx, key, opts...)
cancel()
if err != nil {
cobrautl.ExitWithError(cobrautl.ExitError, err)
}
if getCountOnly {
if _, fields := display.(*fieldsPrinter); !fields {
cobrautl.ExitWithError(cobrautl.ExitBadArgs, fmt.Errorf("--count-only is only for `--write-out=fields`"))
}
}
if printValueOnly {
dp, simple := (display).(*simplePrinter)
if !simple {
cobrautl.ExitWithError(cobrautl.ExitBadArgs, fmt.Errorf("print-value-only is only for `--write-out=simple`"))
}
dp.valueOnly = true
}
display.Get(*resp)
}
func getGetOp(args []string) (string, []clientv3.OpOption) {
if len(args) == 0 {
cobrautl.ExitWithError(cobrautl.ExitBadArgs, fmt.Errorf("get command needs one argument as key and an optional argument as range_end"))
}
if getPrefix && getFromKey {
cobrautl.ExitWithError(cobrautl.ExitBadArgs, fmt.Errorf("`--prefix` and `--from-key` cannot be set at the same time, choose one"))
}
if getKeysOnly && getCountOnly {
cobrautl.ExitWithError(cobrautl.ExitBadArgs, fmt.Errorf("`--keys-only` and `--count-only` cannot be set at the same time, choose one"))
}
var opts []clientv3.OpOption
if IsSerializable(getConsistency) {
opts = append(opts, clientv3.WithSerializable())
}
key := args[0]
if len(args) > 1 {
if getPrefix || getFromKey {
cobrautl.ExitWithError(cobrautl.ExitBadArgs, fmt.Errorf("too many arguments, only accept one argument when `--prefix` or `--from-key` is set"))
}
opts = append(opts, clientv3.WithRange(args[1]))
}
opts = append(opts, clientv3.WithLimit(getLimit))
if getRev > 0 {
opts = append(opts, clientv3.WithRev(getRev))
}
sortByOrder := clientv3.SortNone
sortOrder := strings.ToUpper(getSortOrder)
switch {
case sortOrder == "ASCEND":
sortByOrder = clientv3.SortAscend
case sortOrder == "DESCEND":
sortByOrder = clientv3.SortDescend
case sortOrder == "":
// nothing
default:
cobrautl.ExitWithError(cobrautl.ExitBadFeature, fmt.Errorf("bad sort order %v", getSortOrder))
}
sortByTarget := clientv3.SortByKey
sortTarget := strings.ToUpper(getSortTarget)
switch {
case sortTarget == "CREATE":
sortByTarget = clientv3.SortByCreateRevision
case sortTarget == "KEY":
sortByTarget = clientv3.SortByKey
case sortTarget == "MODIFY":
sortByTarget = clientv3.SortByModRevision
case sortTarget == "VALUE":
sortByTarget = clientv3.SortByValue
case sortTarget == "VERSION":
sortByTarget = clientv3.SortByVersion
case sortTarget == "":
// nothing
default:
cobrautl.ExitWithError(cobrautl.ExitBadFeature, fmt.Errorf("bad sort target %v", getSortTarget))
}
opts = append(opts, clientv3.WithSort(sortByTarget, sortByOrder))
if getPrefix {
if len(key) == 0 {
key = "\x00"
opts = append(opts, clientv3.WithFromKey())
} else {
opts = append(opts, clientv3.WithPrefix())
}
}
if getFromKey {
if len(key) == 0 {
key = "\x00"
}
opts = append(opts, clientv3.WithFromKey())
}
if getKeysOnly {
opts = append(opts, clientv3.WithKeysOnly())
}
if getCountOnly {
opts = append(opts, clientv3.WithCountOnly())
}
if getMinCreateRev > 0 {
opts = append(opts, clientv3.WithMinCreateRev(getMinCreateRev))
}
if getMaxCreateRev > 0 {
if getMinCreateRev > getMaxCreateRev {
cobrautl.ExitWithError(cobrautl.ExitBadFeature,
fmt.Errorf("getMinCreateRev(=%v) > getMaxCreateRev(=%v)", getMinCreateRev, getMaxCreateRev))
}
opts = append(opts, clientv3.WithMaxCreateRev(getMaxCreateRev))
}
if getMinModRev > 0 {
opts = append(opts, clientv3.WithMinModRev(getMinModRev))
}
if getMaxModRev > 0 {
if getMinModRev > getMaxModRev {
cobrautl.ExitWithError(cobrautl.ExitBadFeature,
fmt.Errorf("getMinModRev(=%v) > getMaxModRev(=%v)", getMinModRev, getMaxModRev))
}
opts = append(opts, clientv3.WithMaxModRev(getMaxModRev))
}
return key, opts
}
================================================
FILE: etcdctl/ctlv3/command/global.go
================================================
// Copyright 2015 The etcd Authors
//
// 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 command
import (
"errors"
"fmt"
"io"
"os"
"strings"
"time"
"github.com/bgentry/speakeasy"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
"go.uber.org/zap"
"google.golang.org/grpc/grpclog"
"go.etcd.io/etcd/client/pkg/v3/logutil"
"go.etcd.io/etcd/client/pkg/v3/srv"
"go.etcd.io/etcd/client/pkg/v3/transport"
clientv3 "go.etcd.io/etcd/client/v3"
"go.etcd.io/etcd/pkg/v3/cobrautl"
"go.etcd.io/etcd/pkg/v3/flags"
)
// GlobalFlags are flags that defined globally
// and are inherited to all sub-commands.
type GlobalFlags struct {
Insecure bool
InsecureSkipVerify bool
InsecureDiscovery bool
Endpoints []string
DialTimeout time.Duration
CommandTimeOut time.Duration
KeepAliveTime time.Duration
KeepAliveTimeout time.Duration
MaxCallSendMsgSize int
MaxCallRecvMsgSize int
DNSClusterServiceName string
TLS transport.TLSInfo
OutputFormat string
IsHex bool
User string
Password string
Token string
Debug bool
}
type discoveryCfg struct {
domain string
insecure bool
serviceName string
}
var display printer = &simplePrinter{}
func initDisplayFromCmd(cmd *cobra.Command) {
isHex, err := cmd.Flags().GetBool("hex")
if err != nil {
cobrautl.ExitWithError(cobrautl.ExitError, err)
}
outputType, err := cmd.Flags().GetString("write-out")
if err != nil {
cobrautl.ExitWithError(cobrautl.ExitError, err)
}
if display = NewPrinter(outputType, isHex); display == nil {
cobrautl.ExitWithError(cobrautl.ExitBadFeature, errors.New("unsupported output format"))
}
}
type discardValue struct{}
func (*discardValue) String() string { return "" }
func (*discardValue) Set(string) error { return nil }
func (*discardValue) Type() string { return "" }
func clientConfigFromCmd(cmd *cobra.Command) *clientv3.ConfigSpec {
lg, err := logutil.CreateDefaultZapLogger(zap.InfoLevel)
if err != nil {
cobrautl.ExitWithError(cobrautl.ExitError, err)
}
fs := cmd.InheritedFlags()
if strings.HasPrefix(cmd.Use, "watch") {
// silence "pkg/flags: unrecognized environment variable ETCDCTL_WATCH_KEY=foo" warnings
// silence "pkg/flags: unrecognized environment variable ETCDCTL_WATCH_RANGE_END=bar" warnings
fs.AddFlag(&pflag.Flag{Name: "watch-key", Value: &discardValue{}})
fs.AddFlag(&pflag.Flag{Name: "watch-range-end", Value: &discardValue{}})
}
flags.SetPflagsFromEnv(lg, "ETCDCTL", fs)
debug, err := cmd.Flags().GetBool("debug")
if err != nil {
cobrautl.ExitWithError(cobrautl.ExitError, err)
}
if debug {
grpclog.SetLoggerV2(grpclog.NewLoggerV2WithVerbosity(os.Stderr, os.Stderr, os.Stderr, 4))
fs.VisitAll(func(f *pflag.Flag) {
fmt.Fprintf(os.Stderr, "%s=%v\n", flags.FlagToEnv("ETCDCTL", f.Name), f.Value)
})
} else {
// WARNING logs contain important information like TLS misconfirugation, but spams
// too many routine connection disconnects to turn on by default.
//
// See https://github.com/etcd-io/etcd/pull/9623 for background
grpclog.SetLoggerV2(grpclog.NewLoggerV2(io.Discard, io.Discard, os.Stderr))
}
cfg := &clientv3.ConfigSpec{}
cfg.Endpoints, err = endpointsFromCmd(cmd)
if err != nil {
cobrautl.ExitWithError(cobrautl.ExitError, err)
}
cfg.DialTimeout = dialTimeoutFromCmd(cmd)
cfg.KeepAliveTime = keepAliveTimeFromCmd(cmd)
cfg.KeepAliveTimeout = keepAliveTimeoutFromCmd(cmd)
cfg.MaxCallSendMsgSize = maxCallSendMsgSizeFromCmd(cmd)
cfg.MaxCallRecvMsgSize = maxCallRecvMsgSizeFromCmd(cmd)
cfg.Secure = secureCfgFromCmd(cmd)
cfg.Auth = authCfgFromCmd(cmd)
initDisplayFromCmd(cmd)
return cfg
}
func mustClientCfgFromCmd(cmd *cobra.Command) *clientv3.Config {
cc := clientConfigFromCmd(cmd)
lg, _ := logutil.CreateDefaultZapLogger(zap.InfoLevel)
cfg, err := clientv3.NewClientConfig(cc, lg)
if err != nil {
cobrautl.ExitWithError(cobrautl.ExitBadArgs, err)
}
return cfg
}
func mustClientFromCmd(cmd *cobra.Command) *clientv3.Client {
cfg := clientConfigFromCmd(cmd)
return mustClient(cfg)
}
func mustClient(cc *clientv3.ConfigSpec) *clientv3.Client {
lg, _ := logutil.CreateDefaultZapLogger(zap.InfoLevel)
cfg, err := clientv3.NewClientConfig(cc, lg)
if err != nil {
cobrautl.ExitWithError(cobrautl.ExitBadArgs, err)
}
client, err := clientv3.New(*cfg)
if err != nil {
cobrautl.ExitWithError(cobrautl.ExitBadConnection, err)
}
return client
}
func argOrStdin(args []string, stdin io.Reader, i int) (string, error) {
if i < len(args) {
return args[i], nil
}
bytes, err := io.ReadAll(stdin)
if string(bytes) == "" || err != nil {
return "", errors.New("no available argument and stdin")
}
return string(bytes), nil
}
func dialTimeoutFromCmd(cmd *cobra.Command) time.Duration {
dialTimeout, err := cmd.Flags().GetDuration("dial-timeout")
if err != nil {
cobrautl.ExitWithError(cobrautl.ExitError, err)
}
return dialTimeout
}
func keepAliveTimeFromCmd(cmd *cobra.Command) time.Duration {
keepAliveTime, err := cmd.Flags().GetDuration("keepalive-time")
if err != nil {
cobrautl.ExitWithError(cobrautl.ExitError, err)
}
return keepAliveTime
}
func keepAliveTimeoutFromCmd(cmd *cobra.Command) time.Duration {
keepAliveTimeout, err := cmd.Flags().GetDuration("keepalive-timeout")
if err != nil {
cobrautl.ExitWithError(cobrautl.ExitError, err)
}
return keepAliveTimeout
}
func maxCallSendMsgSizeFromCmd(cmd *cobra.Command) int {
maxRequestBytes, err := cmd.Flags().GetInt("max-request-bytes")
if err != nil {
cobrautl.ExitWithError(cobrautl.ExitError, err)
}
return maxRequestBytes
}
func maxCallRecvMsgSizeFromCmd(cmd *cobra.Command) int {
maxReceiveBytes, err := cmd.Flags().GetInt("max-recv-bytes")
if err != nil {
cobrautl.ExitWithError(cobrautl.ExitError, err)
}
return maxReceiveBytes
}
func secureCfgFromCmd(cmd *cobra.Command) *clientv3.SecureConfig {
cert, key, cacert := keyAndCertFromCmd(cmd)
insecureTr := insecureTransportFromCmd(cmd)
skipVerify := insecureSkipVerifyFromCmd(cmd)
discoveryCfg := discoveryCfgFromCmd(cmd)
if discoveryCfg.insecure {
discoveryCfg.domain = ""
}
return &clientv3.SecureConfig{
Cert: cert,
Key: key,
Cacert: cacert,
ServerName: discoveryCfg.domain,
InsecureTransport: insecureTr,
InsecureSkipVerify: skipVerify,
}
}
func insecureTransportFromCmd(cmd *cobra.Command) bool {
insecureTr, err := cmd.Flags().GetBool("insecure-transport")
if err != nil {
cobrautl.ExitWithError(cobrautl.ExitError, err)
}
return insecureTr
}
func insecureSkipVerifyFromCmd(cmd *cobra.Command) bool {
skipVerify, err := cmd.Flags().GetBool("insecure-skip-tls-verify")
if err != nil {
cobrautl.ExitWithError(cobrautl.ExitError, err)
}
return skipVerify
}
func keyAndCertFromCmd(cmd *cobra.Command) (cert, key, cacert string) {
var err error
if cert, err = cmd.Flags().GetString("cert"); err != nil {
cobrautl.ExitWithError(cobrautl.ExitBadArgs, err)
} else if cert == "" && cmd.Flags().Changed("cert") {
cobrautl.ExitWithError(cobrautl.ExitBadArgs, errors.New("empty string is passed to --cert option"))
}
if key, err = cmd.Flags().GetString("key"); err != nil {
cobrautl.ExitWithError(cobrautl.ExitBadArgs, err)
} else if key == "" && cmd.Flags().Changed("key") {
cobrautl.ExitWithError(cobrautl.ExitBadArgs, errors.New("empty string is passed to --key option"))
}
if cacert, err = cmd.Flags().GetString("cacert"); err != nil {
cobrautl.ExitWithError(cobrautl.ExitBadArgs, err)
} else if cacert == "" && cmd.Flags().Changed("cacert") {
cobrautl.ExitWithError(cobrautl.ExitBadArgs, errors.New("empty string is passed to --cacert option"))
}
return cert, key, cacert
}
func authCfgFromCmd(cmd *cobra.Command) *clientv3.AuthConfig {
userFlag, err := cmd.Flags().GetString("user")
if err != nil {
cobrautl.ExitWithError(cobrautl.ExitBadArgs, err)
}
passwordFlag, err := cmd.Flags().GetString("password")
if err != nil {
cobrautl.ExitWithError(cobrautl.ExitBadArgs, err)
}
tokenFlag, err := cmd.Flags().GetString("auth-jwt-token")
if err != nil {
cobrautl.ExitWithError(cobrautl.ExitBadArgs, err)
}
if userFlag == "" && tokenFlag == "" {
return nil
}
var cfg clientv3.AuthConfig
if tokenFlag != "" {
cfg.Token = tokenFlag
return &cfg
}
if passwordFlag == "" {
splitted := strings.SplitN(userFlag, ":", 2)
if len(splitted) < 2 {
cfg.Username = userFlag
cfg.Password, err = speakeasy.Ask("Password: ")
if err != nil {
cobrautl.ExitWithError(cobrautl.ExitError, err)
}
} else {
cfg.Username = splitted[0]
cfg.Password = splitted[1]
}
} else {
cfg.Username = userFlag
cfg.Password = passwordFlag
}
return &cfg
}
func insecureDiscoveryFromCmd(cmd *cobra.Command) bool {
discovery, err := cmd.Flags().GetBool("insecure-discovery")
if err != nil {
cobrautl.ExitWithError(cobrautl.ExitError, err)
}
return discovery
}
func discoverySrvFromCmd(cmd *cobra.Command) string {
domainStr, err := cmd.Flags().GetString("discovery-srv")
if err != nil {
cobrautl.ExitWithError(cobrautl.ExitBadArgs, err)
}
return domainStr
}
func discoveryDNSClusterServiceNameFromCmd(cmd *cobra.Command) string {
serviceNameStr, err := cmd.Flags().GetString("discovery-srv-name")
if err != nil {
cobrautl.ExitWithError(cobrautl.ExitBadArgs, err)
}
return serviceNameStr
}
func discoveryCfgFromCmd(cmd *cobra.Command) *discoveryCfg {
return &discoveryCfg{
domain: discoverySrvFromCmd(cmd),
insecure: insecureDiscoveryFromCmd(cmd),
serviceName: discoveryDNSClusterServiceNameFromCmd(cmd),
}
}
func endpointsFromCmd(cmd *cobra.Command) ([]string, error) {
eps, err := endpointsFromFlagValue(cmd)
if err != nil {
return nil, err
}
// If domain discovery returns no endpoints, check endpoints flag
if len(eps) == 0 {
eps, err = cmd.Flags().GetStringSlice("endpoints")
if err == nil {
for i, ip := range eps {
eps[i] = strings.TrimSpace(ip)
}
}
}
return eps, err
}
func endpointsFromFlagValue(cmd *cobra.Command) ([]string, error) {
discoveryCfg := discoveryCfgFromCmd(cmd)
// If we still don't have domain discovery, return nothing
if discoveryCfg.domain == "" {
return []string{}, nil
}
srvs, err := srv.GetClient("etcd-client", discoveryCfg.domain, discoveryCfg.serviceName)
if err != nil {
return nil, err
}
eps := srvs.Endpoints
if discoveryCfg.insecure {
return eps, err
}
// strip insecure connections
var ret []string
for _, ep := range eps {
if strings.HasPrefix(ep, "http://") {
fmt.Fprintf(os.Stderr, "ignoring discovered insecure endpoint %q\n", ep)
continue
}
ret = append(ret, ep)
}
return ret, err
}
================================================
FILE: etcdctl/ctlv3/command/groups.go
================================================
// Copyright 2025 The etcd Authors
//
// 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 command
import "github.com/spf13/cobra"
const (
groupKVID = "kv"
groupClusterMaintenanceID = "cluster maintenance"
groupConcurrencyID = "concurrency"
groupAuthenticationID = "authentication"
groupUtilityID = "utility"
)
func NewKVGroup() *cobra.Group {
return &cobra.Group{
ID: groupKVID,
Title: "Key-value commands",
}
}
func NewClusterMaintenanceGroup() *cobra.Group {
return &cobra.Group{
ID: groupClusterMaintenanceID,
Title: "Cluster maintenance commands",
}
}
func NewConcurrencyGroup() *cobra.Group {
return &cobra.Group{
ID: groupConcurrencyID,
Title: "Concurrency commands",
}
}
func NewAuthenticationGroup() *cobra.Group {
return &cobra.Group{
ID: groupAuthenticationID,
Title: "Authentication commands",
}
}
func NewUtilityGroup() *cobra.Group {
return &cobra.Group{
ID: groupUtilityID,
Title: "Utility commands",
}
}
================================================
FILE: etcdctl/ctlv3/command/help_command.go
================================================
// Copyright 2025 The etcd Authors
//
// 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 command
import "github.com/spf13/cobra"
func SetHelpCmdGroup(rootCmd *cobra.Command) {
rootCmd.SetHelpCommandGroupID(groupUtilityID)
}
================================================
FILE: etcdctl/ctlv3/command/lease_command.go
================================================
// Copyright 2016 The etcd Authors
//
// 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 command
import (
"context"
"fmt"
"strconv"
"github.com/spf13/cobra"
v3 "go.etcd.io/etcd/client/v3"
"go.etcd.io/etcd/pkg/v3/cobrautl"
)
// NewLeaseCommand returns the cobra command for "lease".
func NewLeaseCommand() *cobra.Command {
lc := &cobra.Command{
Use: "lease ",
Short: "Lease related commands. Use `etcdctl lease --help` to see subcommands",
Long: "Lease related commands",
GroupID: groupKVID,
}
lc.AddCommand(NewLeaseGrantCommand())
lc.AddCommand(NewLeaseRevokeCommand())
lc.AddCommand(NewLeaseTimeToLiveCommand())
lc.AddCommand(NewLeaseListCommand())
lc.AddCommand(NewLeaseKeepAliveCommand())
return lc
}
// NewLeaseGrantCommand returns the cobra command for "lease grant".
func NewLeaseGrantCommand() *cobra.Command {
lc := &cobra.Command{
Use: "grant ",
Short: "Creates leases",
Run: leaseGrantCommandFunc,
}
return lc
}
// leaseGrantCommandFunc executes the "lease grant" command.
func leaseGrantCommandFunc(cmd *cobra.Command, args []string) {
if len(args) != 1 {
cobrautl.ExitWithError(cobrautl.ExitBadArgs, fmt.Errorf("lease grant command needs TTL argument"))
}
ttl, err := strconv.ParseInt(args[0], 10, 64)
if err != nil {
cobrautl.ExitWithError(cobrautl.ExitBadArgs, fmt.Errorf("bad TTL (%w)", err))
}
ctx, cancel := commandCtx(cmd)
resp, err := mustClientFromCmd(cmd).Grant(ctx, ttl)
cancel()
if err != nil {
cobrautl.ExitWithError(cobrautl.ExitError, fmt.Errorf("failed to grant lease (%w)", err))
}
display.Grant(*resp)
}
// NewLeaseRevokeCommand returns the cobra command for "lease revoke".
func NewLeaseRevokeCommand() *cobra.Command {
lc := &cobra.Command{
Use: "revoke ",
Short: "Revokes leases",
Run: leaseRevokeCommandFunc,
}
return lc
}
// leaseRevokeCommandFunc executes the "lease grant" command.
func leaseRevokeCommandFunc(cmd *cobra.Command, args []string) {
if len(args) != 1 {
cobrautl.ExitWithError(cobrautl.ExitBadArgs, fmt.Errorf("lease revoke command needs 1 argument"))
}
id := leaseFromArgs(args[0])
ctx, cancel := commandCtx(cmd)
resp, err := mustClientFromCmd(cmd).Revoke(ctx, id)
cancel()
if err != nil {
cobrautl.ExitWithError(cobrautl.ExitError, fmt.Errorf("failed to revoke lease (%w)", err))
}
display.Revoke(id, *resp)
}
var timeToLiveKeys bool
// NewLeaseTimeToLiveCommand returns the cobra command for "lease timetolive".
func NewLeaseTimeToLiveCommand() *cobra.Command {
lc := &cobra.Command{
Use: "timetolive [options]",
Short: "Get lease information",
Run: leaseTimeToLiveCommandFunc,
}
lc.Flags().BoolVar(&timeToLiveKeys, "keys", false, "Get keys attached to this lease")
return lc
}
// leaseTimeToLiveCommandFunc executes the "lease timetolive" command.
func leaseTimeToLiveCommandFunc(cmd *cobra.Command, args []string) {
if len(args) != 1 {
cobrautl.ExitWithError(cobrautl.ExitBadArgs, fmt.Errorf("lease timetolive command needs lease ID as argument"))
}
var opts []v3.LeaseOption
if timeToLiveKeys {
opts = append(opts, v3.WithAttachedKeys())
}
resp, rerr := mustClientFromCmd(cmd).TimeToLive(context.TODO(), leaseFromArgs(args[0]), opts...)
if rerr != nil {
cobrautl.ExitWithError(cobrautl.ExitBadConnection, rerr)
}
display.TimeToLive(*resp, timeToLiveKeys)
}
// NewLeaseListCommand returns the cobra command for "lease list".
func NewLeaseListCommand() *cobra.Command {
lc := &cobra.Command{
Use: "list",
Short: "List all active leases",
Run: leaseListCommandFunc,
}
return lc
}
// leaseListCommandFunc executes the "lease list" command.
func leaseListCommandFunc(cmd *cobra.Command, args []string) {
resp, rerr := mustClientFromCmd(cmd).Leases(context.TODO())
if rerr != nil {
cobrautl.ExitWithError(cobrautl.ExitBadConnection, rerr)
}
display.Leases(*resp)
}
var leaseKeepAliveOnce bool
// NewLeaseKeepAliveCommand returns the cobra command for "lease keep-alive".
func NewLeaseKeepAliveCommand() *cobra.Command {
lc := &cobra.Command{
Use: "keep-alive [options] ",
Short: "Keeps leases alive (renew)",
Run: leaseKeepAliveCommandFunc,
}
lc.Flags().BoolVar(&leaseKeepAliveOnce, "once", false, "Resets the keep-alive time to its original value and cobrautl.Exits immediately")
return lc
}
// leaseKeepAliveCommandFunc executes the "lease keep-alive" command.
func leaseKeepAliveCommandFunc(cmd *cobra.Command, args []string) {
if len(args) != 1 {
cobrautl.ExitWithError(cobrautl.ExitBadArgs, fmt.Errorf("lease keep-alive command needs lease ID as argument"))
}
id := leaseFromArgs(args[0])
if leaseKeepAliveOnce {
respc, kerr := mustClientFromCmd(cmd).KeepAliveOnce(context.TODO(), id)
if kerr != nil {
cobrautl.ExitWithError(cobrautl.ExitBadConnection, kerr)
}
display.KeepAlive(*respc)
return
}
respc, kerr := mustClientFromCmd(cmd).KeepAlive(context.TODO(), id)
if kerr != nil {
cobrautl.ExitWithError(cobrautl.ExitBadConnection, kerr)
}
for resp := range respc {
display.KeepAlive(*resp)
}
if _, ok := (display).(*simplePrinter); ok {
fmt.Printf("lease %016x expired or revoked.\n", id)
}
}
func leaseFromArgs(arg string) v3.LeaseID {
id, err := strconv.ParseInt(arg, 16, 64)
if err != nil {
cobrautl.ExitWithError(cobrautl.ExitBadArgs, fmt.Errorf("bad lease ID arg (%w), expecting ID in Hex", err))
}
return v3.LeaseID(id)
}
================================================
FILE: etcdctl/ctlv3/command/lock_command.go
================================================
// Copyright 2016 The etcd Authors
//
// 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 command
import (
"context"
"errors"
"fmt"
"os"
"os/exec"
"os/signal"
"syscall"
"github.com/spf13/cobra"
clientv3 "go.etcd.io/etcd/client/v3"
"go.etcd.io/etcd/client/v3/concurrency"
"go.etcd.io/etcd/pkg/v3/cobrautl"
)
var lockTTL = 10
// NewLockCommand returns the cobra command for "lock".
func NewLockCommand() *cobra.Command {
c := &cobra.Command{
Use: "lock [exec-command arg1 arg2 ...]",
Short: "Acquires a named lock",
Run: lockCommandFunc,
GroupID: groupConcurrencyID,
}
c.Flags().IntVarP(&lockTTL, "ttl", "", lockTTL, "timeout for session")
return c
}
func lockCommandFunc(cmd *cobra.Command, args []string) {
if len(args) == 0 {
cobrautl.ExitWithError(cobrautl.ExitBadArgs, errors.New("lock takes a lock name argument and an optional command to execute"))
}
c := mustClientFromCmd(cmd)
if err := lockUntilSignal(c, args[0], args[1:]); err != nil {
code := getExitCodeFromError(err)
cobrautl.ExitWithError(code, err)
}
}
func getExitCodeFromError(err error) int {
if err == nil {
return cobrautl.ExitSuccess
}
var exitErr *exec.ExitError
if errors.As(err, &exitErr) {
if status, ok := exitErr.Sys().(syscall.WaitStatus); ok {
return status.ExitStatus()
}
}
return cobrautl.ExitError
}
func lockUntilSignal(c *clientv3.Client, lockname string, cmdArgs []string) error {
s, err := concurrency.NewSession(c, concurrency.WithTTL(lockTTL))
if err != nil {
return err
}
m := concurrency.NewMutex(s, lockname)
ctx, cancel := context.WithCancel(context.TODO())
// unlock in case of ordinary shutdown
donec := make(chan struct{})
sigc := make(chan os.Signal, 1)
signal.Notify(sigc, syscall.SIGINT, syscall.SIGTERM)
go func() {
<-sigc
cancel()
close(donec)
}()
if err := m.Lock(ctx); err != nil {
return err
}
if len(cmdArgs) > 0 {
cmd := exec.Command(cmdArgs[0], cmdArgs[1:]...)
cmd.Env = append(environLockResponse(m), os.Environ()...)
cmd.Stdout, cmd.Stderr = os.Stdout, os.Stderr
err := cmd.Run()
unlockErr := m.Unlock(context.TODO())
if err != nil {
return err
}
return unlockErr
}
k, kerr := c.Get(ctx, m.Key())
if kerr != nil {
return kerr
}
if len(k.Kvs) == 0 {
return errors.New("lock lost on init")
}
display.Get(*k)
select {
case <-donec:
return m.Unlock(context.TODO())
case <-s.Done():
}
return errors.New("session expired")
}
func environLockResponse(m *concurrency.Mutex) []string {
return []string{
"ETCD_LOCK_KEY=" + m.Key(),
fmt.Sprintf("ETCD_LOCK_REV=%d", m.Header().Revision),
}
}
================================================
FILE: etcdctl/ctlv3/command/make_mirror_command.go
================================================
// Copyright 2016 The etcd Authors
//
// 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 command
import (
"context"
"errors"
"fmt"
"strings"
"sync/atomic"
"time"
"github.com/bgentry/speakeasy"
"github.com/spf13/cobra"
"go.etcd.io/etcd/api/v3/mvccpb"
"go.etcd.io/etcd/api/v3/v3rpc/rpctypes"
clientv3 "go.etcd.io/etcd/client/v3"
"go.etcd.io/etcd/client/v3/mirror"
"go.etcd.io/etcd/pkg/v3/cobrautl"
)
const (
defaultMaxTxnOps = uint(128)
)
var (
mminsecureTr bool
mmcert string
mmkey string
mmcacert string
mmprefix string
mmdestprefix string
mmuser string
mmpassword string
mmnodestprefix bool
mmrev int64
mmmaxTxnOps uint
)
// NewMakeMirrorCommand returns the cobra command for "makeMirror".
func NewMakeMirrorCommand() *cobra.Command {
c := &cobra.Command{
Use: "make-mirror [options] ",
Short: "Makes a mirror at the destination etcd cluster",
Run: makeMirrorCommandFunc,
GroupID: groupUtilityID,
}
c.Flags().StringVar(&mmprefix, "prefix", "", "Key-value prefix to mirror")
c.Flags().Int64Var(&mmrev, "rev", 0, "Specify the kv revision to start to mirror")
c.Flags().UintVar(&mmmaxTxnOps, "max-txn-ops", defaultMaxTxnOps, "Maximum number of operations permitted in a transaction during syncing updates.")
c.Flags().StringVar(&mmdestprefix, "dest-prefix", "", "destination prefix to mirror a prefix to a different prefix in the destination cluster")
c.Flags().BoolVar(&mmnodestprefix, "no-dest-prefix", false, "mirror key-values to the root of the destination cluster")
c.Flags().StringVar(&mmcert, "dest-cert", "", "Identify secure client using this TLS certificate file for the destination cluster")
c.Flags().StringVar(&mmkey, "dest-key", "", "Identify secure client using this TLS key file")
c.Flags().StringVar(&mmcacert, "dest-cacert", "", "Verify certificates of TLS enabled secure servers using this CA bundle")
// TODO: secure by default when etcd enables secure gRPC by default.
c.Flags().BoolVar(&mminsecureTr, "dest-insecure-transport", true, "Disable transport security for client connections")
c.Flags().StringVar(&mmuser, "dest-user", "", "Destination username[:password] for authentication (prompt if password is not supplied)")
c.Flags().StringVar(&mmpassword, "dest-password", "", "Destination password for authentication (if this option is used, --user option shouldn't include password)")
return c
}
func authDestCfg() *clientv3.AuthConfig {
if mmuser == "" {
return nil
}
var cfg clientv3.AuthConfig
if mmpassword == "" {
splitted := strings.SplitN(mmuser, ":", 2)
if len(splitted) < 2 {
var err error
cfg.Username = mmuser
cfg.Password, err = speakeasy.Ask("Destination Password: ")
if err != nil {
cobrautl.ExitWithError(cobrautl.ExitError, err)
}
} else {
cfg.Username = splitted[0]
cfg.Password = splitted[1]
}
} else {
cfg.Username = mmuser
cfg.Password = mmpassword
}
return &cfg
}
func makeMirrorCommandFunc(cmd *cobra.Command, args []string) {
if len(args) != 1 {
cobrautl.ExitWithError(cobrautl.ExitBadArgs, errors.New("make-mirror takes one destination argument"))
}
dialTimeout := dialTimeoutFromCmd(cmd)
keepAliveTime := keepAliveTimeFromCmd(cmd)
keepAliveTimeout := keepAliveTimeoutFromCmd(cmd)
maxCallSendMsgSize := maxCallSendMsgSizeFromCmd(cmd)
maxCallRecvMsgSize := maxCallRecvMsgSizeFromCmd(cmd)
sec := &clientv3.SecureConfig{
Cert: mmcert,
Key: mmkey,
Cacert: mmcacert,
InsecureTransport: mminsecureTr,
}
auth := authDestCfg()
cc := &clientv3.ConfigSpec{
Endpoints: []string{args[0]},
DialTimeout: dialTimeout,
KeepAliveTime: keepAliveTime,
KeepAliveTimeout: keepAliveTimeout,
MaxCallSendMsgSize: maxCallSendMsgSize,
MaxCallRecvMsgSize: maxCallRecvMsgSize,
Secure: sec,
Auth: auth,
}
dc := mustClient(cc)
c := mustClientFromCmd(cmd)
err := makeMirror(context.TODO(), c, dc)
cobrautl.ExitWithError(cobrautl.ExitError, err)
}
func makeMirror(ctx context.Context, c *clientv3.Client, dc *clientv3.Client) error {
total := int64(0)
// if destination prefix is specified and remove destination prefix is true return error
if mmnodestprefix && len(mmdestprefix) > 0 {
cobrautl.ExitWithError(cobrautl.ExitBadArgs, errors.New("`--dest-prefix` and `--no-dest-prefix` cannot be set at the same time, choose one"))
}
go func() {
for {
time.Sleep(30 * time.Second)
fmt.Println(atomic.LoadInt64(&total))
}
}()
startRev := mmrev - 1
if startRev < 0 {
startRev = 0
}
s := mirror.NewSyncer(c, mmprefix, startRev)
// If a rev is provided, then do not sync the whole key space.
// Instead, just start watching the key space starting from the rev
if startRev == 0 {
rc, errc := s.SyncBase(ctx)
// if remove destination prefix is false and destination prefix is empty set the value of destination prefix same as prefix
if !mmnodestprefix && len(mmdestprefix) == 0 {
mmdestprefix = mmprefix
}
for r := range rc {
for _, kv := range r.Kvs {
_, err := dc.Put(ctx, modifyPrefix(string(kv.Key)), string(kv.Value))
if err != nil {
return err
}
atomic.AddInt64(&total, 1)
}
}
err := <-errc
if err != nil {
return err
}
}
wc := s.SyncUpdates(ctx)
for wr := range wc {
if wr.CompactRevision != 0 {
return rpctypes.ErrCompacted
}
var lastRev int64
var ops []clientv3.Op
for _, ev := range wr.Events {
nextRev := ev.Kv.ModRevision
if lastRev != 0 && nextRev > lastRev {
_, err := dc.Txn(ctx).Then(ops...).Commit()
if err != nil {
return err
}
ops = []clientv3.Op{}
}
lastRev = nextRev
if len(ops) == int(mmmaxTxnOps) {
_, err := dc.Txn(ctx).Then(ops...).Commit()
if err != nil {
return err
}
ops = []clientv3.Op{}
}
switch ev.Type {
case mvccpb.Event_PUT:
ops = append(ops, clientv3.OpPut(modifyPrefix(string(ev.Kv.Key)), string(ev.Kv.Value)))
atomic.AddInt64(&total, 1)
case mvccpb.Event_DELETE:
ops = append(ops, clientv3.OpDelete(modifyPrefix(string(ev.Kv.Key))))
atomic.AddInt64(&total, 1)
default:
panic("unexpected event type")
}
}
if len(ops) != 0 {
_, err := dc.Txn(ctx).Then(ops...).Commit()
if err != nil {
return err
}
}
}
return nil
}
func modifyPrefix(key string) string {
return strings.Replace(key, mmprefix, mmdestprefix, 1)
}
================================================
FILE: etcdctl/ctlv3/command/member_command.go
================================================
// Copyright 2016 The etcd Authors
//
// 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 command
import (
"errors"
"fmt"
"strconv"
"strings"
"github.com/spf13/cobra"
clientv3 "go.etcd.io/etcd/client/v3"
"go.etcd.io/etcd/pkg/v3/cobrautl"
)
var (
memberPeerURLs string
isLearner bool
memberConsistency string
)
// NewMemberCommand returns the cobra command for "member".
func NewMemberCommand() *cobra.Command {
mc := &cobra.Command{
Use: "member ",
Short: "Membership related commands. Use `etcdctl member --help` to see subcommands",
Long: "Membership related commands",
GroupID: groupClusterMaintenanceID,
}
mc.AddCommand(NewMemberAddCommand())
mc.AddCommand(NewMemberRemoveCommand())
mc.AddCommand(NewMemberUpdateCommand())
mc.AddCommand(NewMemberListCommand())
mc.AddCommand(NewMemberPromoteCommand())
return mc
}
// NewMemberAddCommand returns the cobra command for "member add".
func NewMemberAddCommand() *cobra.Command {
cc := &cobra.Command{
Use: "add [options]",
Short: "Adds a member into the cluster",
Run: memberAddCommandFunc,
}
cc.Flags().StringVar(&memberPeerURLs, "peer-urls", "", "comma separated peer URLs for the new member.")
cc.Flags().BoolVar(&isLearner, "learner", false, "indicates if the new member is raft learner")
return cc
}
// NewMemberRemoveCommand returns the cobra command for "member remove".
func NewMemberRemoveCommand() *cobra.Command {
cc := &cobra.Command{
Use: "remove ",
Short: "Removes a member from the cluster",
Run: memberRemoveCommandFunc,
}
return cc
}
// NewMemberUpdateCommand returns the cobra command for "member update".
func NewMemberUpdateCommand() *cobra.Command {
cc := &cobra.Command{
Use: "update [options]",
Short: "Updates a member in the cluster",
Run: memberUpdateCommandFunc,
}
cc.Flags().StringVar(&memberPeerURLs, "peer-urls", "", "comma separated peer URLs for the updated member.")
return cc
}
// NewMemberListCommand returns the cobra command for "member list".
func NewMemberListCommand() *cobra.Command {
cc := &cobra.Command{
Use: "list",
Short: "Lists all members in the cluster",
Long: `When --write-out is set to simple, this command prints out comma-separated member lists for each endpoint.
The items in the lists are ID, Status, Name, Peer Addrs, Client Addrs, Is Learner.
`,
Run: memberListCommandFunc,
}
cc.Flags().StringVar(&memberConsistency, "consistency", "l", "Linearizable(l) or Serializable(s)")
return cc
}
// NewMemberPromoteCommand returns the cobra command for "member promote".
func NewMemberPromoteCommand() *cobra.Command {
cc := &cobra.Command{
Use: "promote ",
Short: "Promotes a non-voting member in the cluster",
Long: `Promotes a non-voting learner member to a voting one in the cluster.
`,
Run: memberPromoteCommandFunc,
}
return cc
}
// memberAddCommandFunc executes the "member add" command.
func memberAddCommandFunc(cmd *cobra.Command, args []string) {
if len(args) < 1 {
cobrautl.ExitWithError(cobrautl.ExitBadArgs, errors.New("member name not provided"))
}
if len(args) > 1 {
ev := "too many arguments"
for _, s := range args {
if strings.HasPrefix(strings.ToLower(s), "http") {
ev += fmt.Sprintf(`, did you mean --peer-urls=%s`, s)
}
}
cobrautl.ExitWithError(cobrautl.ExitBadArgs, errors.New(ev))
}
newMemberName := args[0]
if len(memberPeerURLs) == 0 {
cobrautl.ExitWithError(cobrautl.ExitBadArgs, errors.New("member peer urls not provided"))
}
urls := strings.Split(memberPeerURLs, ",")
ctx, cancel := commandCtx(cmd)
cli := mustClientFromCmd(cmd)
var (
resp *clientv3.MemberAddResponse
err error
)
if isLearner {
resp, err = cli.MemberAddAsLearner(ctx, urls)
} else {
resp, err = cli.MemberAdd(ctx, urls)
}
cancel()
if err != nil {
cobrautl.ExitWithError(cobrautl.ExitError, err)
}
newID := resp.Member.ID
display.MemberAdd(*resp)
if _, ok := (display).(*simplePrinter); ok {
var conf []string
for _, memb := range resp.Members {
for _, u := range memb.PeerURLs {
n := memb.Name
if memb.ID == newID {
n = newMemberName
}
conf = append(conf, fmt.Sprintf("%s=%s", n, u))
}
}
fmt.Print("\n")
fmt.Printf("ETCD_NAME=%q\n", newMemberName)
fmt.Printf("ETCD_INITIAL_CLUSTER=%q\n", strings.Join(conf, ","))
fmt.Printf("ETCD_INITIAL_ADVERTISE_PEER_URLS=%q\n", memberPeerURLs)
fmt.Print("ETCD_INITIAL_CLUSTER_STATE=\"existing\"\n")
}
}
// memberRemoveCommandFunc executes the "member remove" command.
func memberRemoveCommandFunc(cmd *cobra.Command, args []string) {
if len(args) != 1 {
cobrautl.ExitWithError(cobrautl.ExitBadArgs, fmt.Errorf("member ID is not provided"))
}
id, err := strconv.ParseUint(args[0], 16, 64)
if err != nil {
cobrautl.ExitWithError(cobrautl.ExitBadArgs, fmt.Errorf("bad member ID arg (%w), expecting ID in Hex", err))
}
ctx, cancel := commandCtx(cmd)
resp, err := mustClientFromCmd(cmd).MemberRemove(ctx, id)
cancel()
if err != nil {
cobrautl.ExitWithError(cobrautl.ExitError, err)
}
display.MemberRemove(id, *resp)
}
// memberUpdateCommandFunc executes the "member update" command.
func memberUpdateCommandFunc(cmd *cobra.Command, args []string) {
if len(args) != 1 {
cobrautl.ExitWithError(cobrautl.ExitBadArgs, fmt.Errorf("member ID is not provided"))
}
id, err := strconv.ParseUint(args[0], 16, 64)
if err != nil {
cobrautl.ExitWithError(cobrautl.ExitBadArgs, fmt.Errorf("bad member ID arg (%w), expecting ID in Hex", err))
}
if len(memberPeerURLs) == 0 {
cobrautl.ExitWithError(cobrautl.ExitBadArgs, fmt.Errorf("member peer urls not provided"))
}
urls := strings.Split(memberPeerURLs, ",")
ctx, cancel := commandCtx(cmd)
resp, err := mustClientFromCmd(cmd).MemberUpdate(ctx, id, urls)
cancel()
if err != nil {
cobrautl.ExitWithError(cobrautl.ExitError, err)
}
display.MemberUpdate(id, *resp)
}
// memberListCommandFunc executes the "member list" command.
func memberListCommandFunc(cmd *cobra.Command, args []string) {
var opts []clientv3.OpOption
if IsSerializable(memberConsistency) {
opts = append(opts, clientv3.WithSerializable())
}
ctx, cancel := commandCtx(cmd)
resp, err := mustClientFromCmd(cmd).MemberList(ctx, opts...)
cancel()
if err != nil {
cobrautl.ExitWithError(cobrautl.ExitError, err)
}
display.MemberList(*resp)
}
// memberPromoteCommandFunc executes the "member promote" command.
func memberPromoteCommandFunc(cmd *cobra.Command, args []string) {
if len(args) != 1 {
cobrautl.ExitWithError(cobrautl.ExitBadArgs, fmt.Errorf("member ID is not provided"))
}
id, err := strconv.ParseUint(args[0], 16, 64)
if err != nil {
cobrautl.ExitWithError(cobrautl.ExitBadArgs, fmt.Errorf("bad member ID arg (%w), expecting ID in Hex", err))
}
ctx, cancel := commandCtx(cmd)
resp, err := mustClientFromCmd(cmd).MemberPromote(ctx, id)
cancel()
if err != nil {
cobrautl.ExitWithError(cobrautl.ExitError, err)
}
display.MemberPromote(id, *resp)
}
================================================
FILE: etcdctl/ctlv3/command/move_leader_command.go
================================================
// Copyright 2017 The etcd Authors
//
// 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 command
import (
"fmt"
"strconv"
"github.com/spf13/cobra"
clientv3 "go.etcd.io/etcd/client/v3"
"go.etcd.io/etcd/pkg/v3/cobrautl"
)
// NewMoveLeaderCommand returns the cobra command for "move-leader".
func NewMoveLeaderCommand() *cobra.Command {
cmd := &cobra.Command{
Use: "move-leader ",
Short: "Transfers leadership to another etcd cluster member.",
Run: transferLeadershipCommandFunc,
GroupID: groupClusterMaintenanceID,
}
return cmd
}
// transferLeadershipCommandFunc executes the "compaction" command.
func transferLeadershipCommandFunc(cmd *cobra.Command, args []string) {
if len(args) != 1 {
cobrautl.ExitWithError(cobrautl.ExitBadArgs, fmt.Errorf("move-leader command needs 1 argument"))
}
target, err := strconv.ParseUint(args[0], 16, 64)
if err != nil {
cobrautl.ExitWithError(cobrautl.ExitBadArgs, err)
}
cfg := clientConfigFromCmd(cmd)
cli := mustClient(cfg)
eps := cli.Endpoints()
cli.Close()
ctx, cancel := commandCtx(cmd)
// find current leader
var leaderCli *clientv3.Client
var leaderID uint64
for _, ep := range eps {
cfg.Endpoints = []string{ep}
cli := mustClient(cfg)
resp, serr := cli.Status(ctx, ep)
if serr != nil {
cobrautl.ExitWithError(cobrautl.ExitError, serr)
}
if resp.Header.GetMemberId() == resp.Leader {
leaderCli = cli
leaderID = resp.Leader
break
}
cli.Close()
}
if leaderCli == nil {
cobrautl.ExitWithError(cobrautl.ExitBadArgs, fmt.Errorf("no leader endpoint given at %v", eps))
}
var resp *clientv3.MoveLeaderResponse
resp, err = leaderCli.MoveLeader(ctx, target)
cancel()
if err != nil {
cobrautl.ExitWithError(cobrautl.ExitError, err)
}
display.MoveLeader(leaderID, target, *resp)
}
================================================
FILE: etcdctl/ctlv3/command/options_command.go
================================================
// Copyright 2025 The etcd Authors
//
// 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 command
import (
"fmt"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
)
func NewOptionsCommand(rootCmd *cobra.Command) *cobra.Command {
cmd := &cobra.Command{
Use: "options",
Short: "Show the global command-line flags",
Run: func(cmd *cobra.Command, args []string) {
fs := unhideCopy(rootCmd.PersistentFlags())
fmt.Fprintf(cmd.OutOrStdout(), "The following options can be passed to any command:\n\n")
fmt.Fprint(cmd.OutOrStdout(), fs.FlagUsages())
},
GroupID: groupUtilityID,
}
return cmd
}
func unhideCopy(src *pflag.FlagSet) *pflag.FlagSet {
out := pflag.NewFlagSet("global", pflag.ContinueOnError)
src.VisitAll(func(f *pflag.Flag) {
nf := *f
nf.Hidden = false
out.AddFlag(&nf)
})
return out
}
================================================
FILE: etcdctl/ctlv3/command/printer.go
================================================
// Copyright 2016 The etcd Authors
//
// 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 command
import (
"errors"
"fmt"
"strconv"
"strings"
"github.com/dustin/go-humanize"
pb "go.etcd.io/etcd/api/v3/etcdserverpb"
v3 "go.etcd.io/etcd/client/v3"
"go.etcd.io/etcd/pkg/v3/cobrautl"
)
type printer interface {
Del(v3.DeleteResponse)
Get(v3.GetResponse)
Put(v3.PutResponse)
Txn(v3.TxnResponse)
Watch(v3.WatchResponse)
Grant(r v3.LeaseGrantResponse)
Revoke(id v3.LeaseID, r v3.LeaseRevokeResponse)
KeepAlive(r v3.LeaseKeepAliveResponse)
TimeToLive(r v3.LeaseTimeToLiveResponse, keys bool)
Leases(r v3.LeaseLeasesResponse)
MemberAdd(v3.MemberAddResponse)
MemberRemove(id uint64, r v3.MemberRemoveResponse)
MemberUpdate(id uint64, r v3.MemberUpdateResponse)
MemberPromote(id uint64, r v3.MemberPromoteResponse)
MemberList(v3.MemberListResponse)
EndpointHealth([]epHealth)
EndpointStatus([]epStatus)
EndpointHashKV([]epHashKV)
MoveLeader(leader, target uint64, r v3.MoveLeaderResponse)
DowngradeValidate(r v3.DowngradeResponse)
DowngradeEnable(r v3.DowngradeResponse)
DowngradeCancel(r v3.DowngradeResponse)
Alarm(v3.AlarmResponse)
RoleAdd(role string, r v3.AuthRoleAddResponse)
RoleGet(role string, r v3.AuthRoleGetResponse)
RoleDelete(role string, r v3.AuthRoleDeleteResponse)
RoleList(v3.AuthRoleListResponse)
RoleGrantPermission(role string, r v3.AuthRoleGrantPermissionResponse)
RoleRevokePermission(role string, key string, end string, r v3.AuthRoleRevokePermissionResponse)
UserAdd(user string, r v3.AuthUserAddResponse)
UserGet(user string, r v3.AuthUserGetResponse)
UserList(r v3.AuthUserListResponse)
UserChangePassword(v3.AuthUserChangePasswordResponse)
UserGrantRole(user string, role string, r v3.AuthUserGrantRoleResponse)
UserRevokeRole(user string, role string, r v3.AuthUserRevokeRoleResponse)
UserDelete(user string, r v3.AuthUserDeleteResponse)
AuthStatus(r v3.AuthStatusResponse)
}
func NewPrinter(printerType string, isHex bool) printer {
switch printerType {
case "simple":
return &simplePrinter{isHex: isHex}
case "fields":
return &fieldsPrinter{printer: newPrinterUnsupported("fields"), isHex: isHex}
case "json":
return newJSONPrinter(isHex)
case "protobuf":
return newPBPrinter()
case "table":
return &tablePrinter{newPrinterUnsupported("table")}
}
return nil
}
type printerRPC struct {
printer
p func(any)
}
func (p *printerRPC) Del(r v3.DeleteResponse) { p.p((*pb.DeleteRangeResponse)(&r)) }
func (p *printerRPC) Get(r v3.GetResponse) { p.p((*pb.RangeResponse)(&r)) }
func (p *printerRPC) Put(r v3.PutResponse) { p.p((*pb.PutResponse)(&r)) }
func (p *printerRPC) Txn(r v3.TxnResponse) { p.p((*pb.TxnResponse)(&r)) }
func (p *printerRPC) Watch(r v3.WatchResponse) { p.p(&r) }
func (p *printerRPC) Grant(r v3.LeaseGrantResponse) { p.p(r) }
func (p *printerRPC) Revoke(id v3.LeaseID, r v3.LeaseRevokeResponse) { p.p(r) }
func (p *printerRPC) KeepAlive(r v3.LeaseKeepAliveResponse) { p.p(r) }
func (p *printerRPC) TimeToLive(r v3.LeaseTimeToLiveResponse, keys bool) { p.p(&r) }
func (p *printerRPC) Leases(r v3.LeaseLeasesResponse) { p.p(&r) }
func (p *printerRPC) MemberAdd(r v3.MemberAddResponse) { p.p((*pb.MemberAddResponse)(&r)) }
func (p *printerRPC) MemberRemove(id uint64, r v3.MemberRemoveResponse) {
p.p((*pb.MemberRemoveResponse)(&r))
}
func (p *printerRPC) MemberUpdate(id uint64, r v3.MemberUpdateResponse) {
p.p((*pb.MemberUpdateResponse)(&r))
}
func (p *printerRPC) MemberPromote(id uint64, r v3.MemberPromoteResponse) {
p.p((*pb.MemberPromoteResponse)(&r))
}
func (p *printerRPC) MemberList(r v3.MemberListResponse) { p.p((*pb.MemberListResponse)(&r)) }
func (p *printerRPC) Alarm(r v3.AlarmResponse) { p.p((*pb.AlarmResponse)(&r)) }
func (p *printerRPC) MoveLeader(leader, target uint64, r v3.MoveLeaderResponse) {
p.p((*pb.MoveLeaderResponse)(&r))
}
func (p *printerRPC) DowngradeValidate(r v3.DowngradeResponse) { p.p((*pb.DowngradeResponse)(&r)) }
func (p *printerRPC) DowngradeEnable(r v3.DowngradeResponse) { p.p((*pb.DowngradeResponse)(&r)) }
func (p *printerRPC) DowngradeCancel(r v3.DowngradeResponse) { p.p((*pb.DowngradeResponse)(&r)) }
func (p *printerRPC) RoleAdd(_ string, r v3.AuthRoleAddResponse) { p.p((*pb.AuthRoleAddResponse)(&r)) }
func (p *printerRPC) RoleGet(_ string, r v3.AuthRoleGetResponse) { p.p((*pb.AuthRoleGetResponse)(&r)) }
func (p *printerRPC) RoleDelete(_ string, r v3.AuthRoleDeleteResponse) {
p.p((*pb.AuthRoleDeleteResponse)(&r))
}
func (p *printerRPC) RoleList(r v3.AuthRoleListResponse) { p.p((*pb.AuthRoleListResponse)(&r)) }
func (p *printerRPC) RoleGrantPermission(_ string, r v3.AuthRoleGrantPermissionResponse) {
p.p((*pb.AuthRoleGrantPermissionResponse)(&r))
}
func (p *printerRPC) RoleRevokePermission(_ string, _ string, _ string, r v3.AuthRoleRevokePermissionResponse) {
p.p((*pb.AuthRoleRevokePermissionResponse)(&r))
}
func (p *printerRPC) UserAdd(_ string, r v3.AuthUserAddResponse) { p.p((*pb.AuthUserAddResponse)(&r)) }
func (p *printerRPC) UserGet(_ string, r v3.AuthUserGetResponse) { p.p((*pb.AuthUserGetResponse)(&r)) }
func (p *printerRPC) UserList(r v3.AuthUserListResponse) { p.p((*pb.AuthUserListResponse)(&r)) }
func (p *printerRPC) UserChangePassword(r v3.AuthUserChangePasswordResponse) {
p.p((*pb.AuthUserChangePasswordResponse)(&r))
}
func (p *printerRPC) UserGrantRole(_ string, _ string, r v3.AuthUserGrantRoleResponse) {
p.p((*pb.AuthUserGrantRoleResponse)(&r))
}
func (p *printerRPC) UserRevokeRole(_ string, _ string, r v3.AuthUserRevokeRoleResponse) {
p.p((*pb.AuthUserRevokeRoleResponse)(&r))
}
func (p *printerRPC) UserDelete(_ string, r v3.AuthUserDeleteResponse) {
p.p((*pb.AuthUserDeleteResponse)(&r))
}
func (p *printerRPC) AuthStatus(r v3.AuthStatusResponse) {
p.p((*pb.AuthStatusResponse)(&r))
}
type printerUnsupported struct{ printerRPC }
func newPrinterUnsupported(n string) printer {
f := func(any) {
cobrautl.ExitWithError(cobrautl.ExitBadFeature, errors.New(n+" not supported as output format"))
}
return &printerUnsupported{printerRPC{nil, f}}
}
func (p *printerUnsupported) EndpointHealth([]epHealth) { p.p(nil) }
func (p *printerUnsupported) EndpointStatus([]epStatus) { p.p(nil) }
func (p *printerUnsupported) EndpointHashKV([]epHashKV) { p.p(nil) }
func (p *printerUnsupported) MoveLeader(leader, target uint64, r v3.MoveLeaderResponse) { p.p(nil) }
func (p *printerUnsupported) DowngradeValidate(r v3.DowngradeResponse) { p.p(nil) }
func (p *printerUnsupported) DowngradeEnable(r v3.DowngradeResponse) { p.p(nil) }
func (p *printerUnsupported) DowngradeCancel(r v3.DowngradeResponse) { p.p(nil) }
func makeMemberListTable(r v3.MemberListResponse) (hdr []string, rows [][]string) {
hdr = []string{"ID", "Status", "Name", "Peer Addrs", "Client Addrs", "Is Learner"}
for _, m := range r.Members {
status := "started"
if len(m.Name) == 0 {
status = "unstarted"
}
isLearner := "false"
if m.IsLearner {
isLearner = "true"
}
rows = append(rows, []string{
fmt.Sprintf("%x", m.ID),
status,
m.Name,
strings.Join(m.PeerURLs, ","),
strings.Join(m.ClientURLs, ","),
isLearner,
})
}
return hdr, rows
}
func makeEndpointHealthTable(healthList []epHealth) (hdr []string, rows [][]string) {
hdr = []string{"endpoint", "health", "took", "error"}
for _, h := range healthList {
rows = append(rows, []string{
h.Ep,
fmt.Sprintf("%v", h.Health),
h.Took,
h.Error,
})
}
return hdr, rows
}
func makeEndpointStatusTable(statusList []epStatus) (hdr []string, rows [][]string) {
hdr = []string{
"endpoint", "ID", "version", "storage version", "db size", "in use", "percentage not in use", "quota", "is leader", "is learner", "raft term",
"raft index", "raft applied index", "errors", "downgrade target version", "downgrade enabled",
}
for _, status := range statusList {
rows = append(rows, []string{
status.Ep,
fmt.Sprintf("%x", status.Resp.Header.MemberId),
status.Resp.Version,
status.Resp.StorageVersion,
humanize.Bytes(uint64(status.Resp.DbSize)),
humanize.Bytes(uint64(status.Resp.DbSizeInUse)),
fmt.Sprintf("%d%%", int(float64(100-(status.Resp.DbSizeInUse*100/status.Resp.DbSize)))),
humanize.Bytes(uint64(status.Resp.DbSizeQuota)),
fmt.Sprint(status.Resp.Leader == status.Resp.Header.MemberId),
fmt.Sprint(status.Resp.IsLearner),
fmt.Sprint(status.Resp.RaftTerm),
fmt.Sprint(status.Resp.RaftIndex),
fmt.Sprint(status.Resp.RaftAppliedIndex),
fmt.Sprint(strings.Join(status.Resp.Errors, ", ")),
status.Resp.DowngradeInfo.GetTargetVersion(),
strconv.FormatBool(status.Resp.DowngradeInfo.GetEnabled()),
})
}
return hdr, rows
}
func makeEndpointHashKVTable(hashList []epHashKV) (hdr []string, rows [][]string) {
hdr = []string{"endpoint", "hash", "hash_revision"}
for _, h := range hashList {
rows = append(rows, []string{
h.Ep,
fmt.Sprint(h.Resp.Hash),
fmt.Sprint(h.Resp.HashRevision),
})
}
return hdr, rows
}
================================================
FILE: etcdctl/ctlv3/command/printer_fields.go
================================================
// Copyright 2016 The etcd Authors
//
// 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 command
import (
"fmt"
pb "go.etcd.io/etcd/api/v3/etcdserverpb"
spb "go.etcd.io/etcd/api/v3/mvccpb"
"go.etcd.io/etcd/client/pkg/v3/types"
v3 "go.etcd.io/etcd/client/v3"
)
type fieldsPrinter struct {
printer
isHex bool
}
func (p *fieldsPrinter) kv(pfx string, kv *spb.KeyValue) {
fmt.Printf("\"%sKey\" : %q\n", pfx, string(kv.Key))
fmt.Printf("\"%sCreateRevision\" : %d\n", pfx, kv.CreateRevision)
fmt.Printf("\"%sModRevision\" : %d\n", pfx, kv.ModRevision)
fmt.Printf("\"%sVersion\" : %d\n", pfx, kv.Version)
fmt.Printf("\"%sValue\" : %q\n", pfx, string(kv.Value))
if p.isHex {
fmt.Printf("\"%sLease\" : %016x\n", pfx, kv.Lease)
} else {
fmt.Printf("\"%sLease\" : %d\n", pfx, kv.Lease)
}
}
func (p *fieldsPrinter) hdr(h *pb.ResponseHeader) {
if p.isHex {
fmt.Println(`"ClusterID" :`, types.ID(h.ClusterId))
fmt.Println(`"MemberID" :`, types.ID(h.MemberId))
} else {
fmt.Println(`"ClusterID" :`, h.ClusterId)
fmt.Println(`"MemberID" :`, h.MemberId)
}
// Revision only makes sense for k/v responses. For other kinds of
// responses, i.e. MemberList, usually the revision isn't populated
// at all; so it would be better to hide this field in these cases.
if h.Revision > 0 {
fmt.Println(`"Revision" :`, h.Revision)
}
fmt.Println(`"RaftTerm" :`, h.RaftTerm)
}
func (p *fieldsPrinter) Del(r v3.DeleteResponse) {
p.hdr(r.Header)
fmt.Println(`"Deleted" :`, r.Deleted)
for _, kv := range r.PrevKvs {
p.kv("Prev", kv)
}
}
func (p *fieldsPrinter) Get(r v3.GetResponse) {
p.hdr(r.Header)
for _, kv := range r.Kvs {
p.kv("", kv)
}
fmt.Println(`"More" :`, r.More)
fmt.Println(`"Count" :`, r.Count)
}
func (p *fieldsPrinter) Put(r v3.PutResponse) {
p.hdr(r.Header)
if r.PrevKv != nil {
p.kv("Prev", r.PrevKv)
}
}
func (p *fieldsPrinter) Txn(r v3.TxnResponse) {
p.hdr(r.Header)
fmt.Println(`"Succeeded" :`, r.Succeeded)
for _, resp := range r.Responses {
switch v := resp.Response.(type) {
case *pb.ResponseOp_ResponseDeleteRange:
p.Del((v3.DeleteResponse)(*v.ResponseDeleteRange))
case *pb.ResponseOp_ResponsePut:
p.Put((v3.PutResponse)(*v.ResponsePut))
case *pb.ResponseOp_ResponseRange:
p.Get((v3.GetResponse)(*v.ResponseRange))
default:
fmt.Printf("\"Unknown\" : %q\n", fmt.Sprintf("%+v", v))
}
}
}
func (p *fieldsPrinter) Watch(resp v3.WatchResponse) {
p.hdr(&resp.Header)
for _, e := range resp.Events {
fmt.Println(`"Type" :`, e.Type)
if e.PrevKv != nil {
p.kv("Prev", e.PrevKv)
}
p.kv("", e.Kv)
}
}
func (p *fieldsPrinter) Grant(r v3.LeaseGrantResponse) {
p.hdr(r.ResponseHeader)
if p.isHex {
fmt.Printf("\"ID\" : %016x\n", r.ID)
} else {
fmt.Println(`"ID" :`, r.ID)
}
fmt.Println(`"TTL" :`, r.TTL)
}
func (p *fieldsPrinter) Revoke(id v3.LeaseID, r v3.LeaseRevokeResponse) {
p.hdr(r.Header)
}
func (p *fieldsPrinter) KeepAlive(r v3.LeaseKeepAliveResponse) {
p.hdr(r.ResponseHeader)
if p.isHex {
fmt.Printf("\"ID\" : %016x\n", r.ID)
} else {
fmt.Println(`"ID" :`, r.ID)
}
fmt.Println(`"TTL" :`, r.TTL)
}
func (p *fieldsPrinter) TimeToLive(r v3.LeaseTimeToLiveResponse, keys bool) {
p.hdr(r.ResponseHeader)
if p.isHex {
fmt.Printf("\"ID\" : %016x\n", r.ID)
} else {
fmt.Println(`"ID" :`, r.ID)
}
fmt.Println(`"TTL" :`, r.TTL)
fmt.Println(`"GrantedTTL" :`, r.GrantedTTL)
for _, k := range r.Keys {
fmt.Printf("\"Key\" : %q\n", string(k))
}
}
func (p *fieldsPrinter) Leases(r v3.LeaseLeasesResponse) {
p.hdr(r.ResponseHeader)
for _, item := range r.Leases {
if p.isHex {
fmt.Printf("\"ID\" : %016x\n", item.ID)
} else {
fmt.Println(`"ID" :`, item.ID)
}
}
}
func (p *fieldsPrinter) MemberList(r v3.MemberListResponse) {
p.hdr(r.Header)
for _, m := range r.Members {
if p.isHex {
fmt.Println(`"ID" :`, types.ID(m.ID))
} else {
fmt.Println(`"ID" :`, m.ID)
}
fmt.Printf("\"Name\" : %q\n", m.Name)
for _, u := range m.PeerURLs {
fmt.Printf("\"PeerURL\" : %q\n", u)
}
for _, u := range m.ClientURLs {
fmt.Printf("\"ClientURL\" : %q\n", u)
}
fmt.Println(`"IsLearner" :`, m.IsLearner)
fmt.Println()
}
}
func (p *fieldsPrinter) EndpointHealth(hs []epHealth) {
for _, h := range hs {
fmt.Printf("\"Endpoint\" : %q\n", h.Ep)
fmt.Println(`"Health" :`, h.Health)
fmt.Println(`"Took" :`, h.Took)
fmt.Println(`"Error" :`, h.Error)
fmt.Println()
}
}
func (p *fieldsPrinter) EndpointStatus(eps []epStatus) {
for _, ep := range eps {
p.hdr(ep.Resp.Header)
fmt.Printf("\"Version\" : %q\n", ep.Resp.Version)
fmt.Printf("\"StorageVersion\" : %q\n", ep.Resp.StorageVersion)
fmt.Println(`"DBSize" :`, ep.Resp.DbSize)
fmt.Println(`"DBSizeInUse" :`, ep.Resp.DbSizeInUse)
fmt.Println(`"DBSizeQuota" :`, ep.Resp.DbSizeQuota)
fmt.Println(`"Leader" :`, ep.Resp.Leader)
fmt.Println(`"IsLearner" :`, ep.Resp.IsLearner)
fmt.Println(`"RaftIndex" :`, ep.Resp.RaftIndex)
fmt.Println(`"RaftTerm" :`, ep.Resp.RaftTerm)
fmt.Println(`"RaftAppliedIndex" :`, ep.Resp.RaftAppliedIndex)
fmt.Println(`"Errors" :`, ep.Resp.Errors)
fmt.Printf("\"Endpoint\" : %q\n", ep.Ep)
fmt.Printf("\"DowngradeTargetVersion\" : %q\n", ep.Resp.DowngradeInfo.GetTargetVersion())
fmt.Println(`"DowngradeEnabled" :`, ep.Resp.DowngradeInfo.GetEnabled())
fmt.Println()
}
}
func (p *fieldsPrinter) EndpointHashKV(hs []epHashKV) {
for _, h := range hs {
p.hdr(h.Resp.Header)
fmt.Printf("\"Endpoint\" : %q\n", h.Ep)
fmt.Println(`"Hash" :`, h.Resp.Hash)
fmt.Println(`"HashRevision" :`, h.Resp.HashRevision)
fmt.Println()
}
}
func (p *fieldsPrinter) Alarm(r v3.AlarmResponse) {
p.hdr(r.Header)
for _, a := range r.Alarms {
if p.isHex {
fmt.Println(`"MemberID" :`, types.ID(a.MemberID))
} else {
fmt.Println(`"MemberID" :`, a.MemberID)
}
fmt.Println(`"AlarmType" :`, a.Alarm)
fmt.Println()
}
}
func (p *fieldsPrinter) RoleAdd(role string, r v3.AuthRoleAddResponse) { p.hdr(r.Header) }
func (p *fieldsPrinter) RoleGet(role string, r v3.AuthRoleGetResponse) {
p.hdr(r.Header)
for _, p := range r.Perm {
fmt.Println(`"PermType" : `, p.PermType.String())
fmt.Printf("\"Key\" : %q\n", string(p.Key))
fmt.Printf("\"RangeEnd\" : %q\n", string(p.RangeEnd))
}
}
func (p *fieldsPrinter) RoleDelete(role string, r v3.AuthRoleDeleteResponse) { p.hdr(r.Header) }
func (p *fieldsPrinter) RoleList(r v3.AuthRoleListResponse) {
p.hdr(r.Header)
fmt.Print(`"Roles" :`)
for _, r := range r.Roles {
fmt.Printf(" %q", r)
}
fmt.Println()
}
func (p *fieldsPrinter) RoleGrantPermission(role string, r v3.AuthRoleGrantPermissionResponse) {
p.hdr(r.Header)
}
func (p *fieldsPrinter) RoleRevokePermission(role string, key string, end string, r v3.AuthRoleRevokePermissionResponse) {
p.hdr(r.Header)
}
func (p *fieldsPrinter) UserAdd(user string, r v3.AuthUserAddResponse) { p.hdr(r.Header) }
func (p *fieldsPrinter) UserChangePassword(r v3.AuthUserChangePasswordResponse) { p.hdr(r.Header) }
func (p *fieldsPrinter) UserGrantRole(user string, role string, r v3.AuthUserGrantRoleResponse) {
p.hdr(r.Header)
}
func (p *fieldsPrinter) UserRevokeRole(user string, role string, r v3.AuthUserRevokeRoleResponse) {
p.hdr(r.Header)
}
func (p *fieldsPrinter) UserDelete(user string, r v3.AuthUserDeleteResponse) { p.hdr(r.Header) }
================================================
FILE: etcdctl/ctlv3/command/printer_json.go
================================================
// Copyright 2016 The etcd Authors
//
// 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 command
import (
"encoding/json"
"fmt"
"io"
"os"
pb "go.etcd.io/etcd/api/v3/etcdserverpb"
clientv3 "go.etcd.io/etcd/client/v3"
)
type jsonPrinter struct {
writer io.Writer
isHex bool
printer
}
type (
HexResponseHeader pb.ResponseHeader
HexMember pb.Member
)
func (h *HexResponseHeader) MarshalJSON() ([]byte, error) {
type Alias pb.ResponseHeader
return json.Marshal(&struct {
ClusterID string `json:"cluster_id"`
MemberID string `json:"member_id"`
Alias
}{
ClusterID: fmt.Sprintf("%x", h.ClusterId),
MemberID: fmt.Sprintf("%x", h.MemberId),
Alias: (Alias)(*h),
})
}
func (m *HexMember) MarshalJSON() ([]byte, error) {
type Alias pb.Member
return json.Marshal(&struct {
ID string `json:"ID"`
Alias
}{
ID: fmt.Sprintf("%x", m.ID),
Alias: (Alias)(*m),
})
}
func newJSONPrinter(isHex bool) printer {
return &jsonPrinter{
writer: os.Stdout,
isHex: isHex,
printer: &printerRPC{newPrinterUnsupported("json"), printJSON},
}
}
func (p *jsonPrinter) EndpointHealth(r []epHealth) { printJSON(r) }
func (p *jsonPrinter) EndpointStatus(r []epStatus) { printJSON(r) }
func (p *jsonPrinter) EndpointHashKV(r []epHashKV) { printJSON(r) }
func (p *jsonPrinter) MemberAdd(r clientv3.MemberAddResponse) { p.printJSON(r) }
func (p *jsonPrinter) MemberRemove(_ uint64, r clientv3.MemberRemoveResponse) { p.printJSON(r) }
func (p *jsonPrinter) MemberUpdate(_ uint64, r clientv3.MemberUpdateResponse) { p.printJSON(r) }
func (p *jsonPrinter) MemberPromote(_ uint64, r clientv3.MemberPromoteResponse) { p.printJSON(r) }
func (p *jsonPrinter) MemberList(r clientv3.MemberListResponse) { p.printJSON(r) }
func printJSONTo(w io.Writer, v any) {
b, err := json.Marshal(v)
if err != nil {
fmt.Fprintf(os.Stderr, "%v\n", err)
return
}
fmt.Fprintln(w, string(b))
}
func printJSON(v any) {
printJSONTo(os.Stdout, v)
}
func (p *jsonPrinter) printJSON(v any) {
var data any
if !p.isHex {
printJSONTo(p.writer, v)
return
}
switch r := v.(type) {
case clientv3.MemberAddResponse:
type Alias clientv3.MemberAddResponse
data = &struct {
Header *HexResponseHeader `json:"header"`
Member *HexMember `json:"member"`
Members []*HexMember `json:"members"`
*Alias
}{
Header: (*HexResponseHeader)(r.Header),
Member: (*HexMember)(r.Member),
Members: toHexMembers(r.Members),
Alias: (*Alias)(&r),
}
case clientv3.MemberRemoveResponse:
type Alias clientv3.MemberRemoveResponse
data = &struct {
Header *HexResponseHeader `json:"header"`
Members []*HexMember `json:"members"`
*Alias
}{
Header: (*HexResponseHeader)(r.Header),
Members: toHexMembers(r.Members),
Alias: (*Alias)(&r),
}
case clientv3.MemberUpdateResponse:
type Alias clientv3.MemberUpdateResponse
data = &struct {
Header *HexResponseHeader `json:"header"`
Members []*HexMember `json:"members"`
*Alias
}{
Header: (*HexResponseHeader)(r.Header),
Members: toHexMembers(r.Members),
Alias: (*Alias)(&r),
}
case clientv3.MemberPromoteResponse:
type Alias clientv3.MemberPromoteResponse
data = &struct {
Header *HexResponseHeader `json:"header"`
Members []*HexMember `json:"members"`
*Alias
}{
Header: (*HexResponseHeader)(r.Header),
Members: toHexMembers(r.Members),
Alias: (*Alias)(&r),
}
case clientv3.MemberListResponse:
type Alias clientv3.MemberListResponse
data = &struct {
Header *HexResponseHeader `json:"header"`
Members []*HexMember `json:"members"`
*Alias
}{
Header: (*HexResponseHeader)(r.Header),
Members: toHexMembers(r.Members),
Alias: (*Alias)(&r),
}
default:
data = v
}
printJSONTo(p.writer, data)
}
func toHexMembers(members []*pb.Member) []*HexMember {
hexMembers := make([]*HexMember, len(members))
for i, member := range members {
hexMembers[i] = (*HexMember)(member)
}
return hexMembers
}
================================================
FILE: etcdctl/ctlv3/command/printer_json_test.go
================================================
// Copyright 2025 The etcd Authors
//
// 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 command
import (
"bytes"
"encoding/json"
"fmt"
"math"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
pb "go.etcd.io/etcd/api/v3/etcdserverpb"
clientv3 "go.etcd.io/etcd/client/v3"
)
const (
keyHeader = "header"
keyMember = "member"
keyMembers = "members"
keyClusterID = "cluster_id"
keyMemberID = "member_id"
keyRaftTerm = "raft_term"
keyRevision = "revision"
keyID = "ID"
)
func assertNumericFieldEqual(t *testing.T, obj map[string]any, key string, want int64) {
raw, ok := obj[key]
require.Truef(t, ok, "missing key %q in map %v", key, obj)
n, ok := raw.(json.Number)
require.Truef(t, ok, "field %q is not json.Number: %v", key, raw)
val, err := n.Int64()
require.NoErrorf(t, err, "failed to convert field %q to int64: %v", key, n)
assert.Equalf(t, want, val, "unexpected value for field %q", key)
}
func assertHexFieldEqual(t *testing.T, obj map[string]any, key string, want string) {
raw, ok := obj[key]
require.Truef(t, ok, "missing key %q in map %v", key, obj)
str, ok := raw.(string)
require.Truef(t, ok, "field %q is not a string: %v", key, str)
assert.Equalf(t, want, str, "unexpected value for hex field %q", key)
}
func assertHeader(t *testing.T, testGroup *testScenario, tt *testCase, got map[string]any) {
rawHeader, ok := got[keyHeader]
require.Truef(t, ok, "output does not contain %q field: %v", keyHeader, got)
header, ok := rawHeader.(map[string]any)
require.Truef(t, ok, "field %q is not map[string]any: %v", keyHeader, rawHeader)
if testGroup.isHex {
assertHexFieldEqual(t, header, keyClusterID, tt.wantHexString)
assertHexFieldEqual(t, header, keyMemberID, tt.wantHexString)
} else {
assertNumericFieldEqual(t, header, keyClusterID, tt.wantDecimalNumber)
assertNumericFieldEqual(t, header, keyMemberID, tt.wantDecimalNumber)
}
assertNumericFieldEqual(t, header, keyRaftTerm, tt.wantDecimalNumber)
assertNumericFieldEqual(t, header, keyRevision, tt.wantDecimalNumber)
}
func assertMember(t *testing.T, testGroup *testScenario, tt *testCase, rawMember any) {
member, ok := rawMember.(map[string]any)
require.Truef(t, ok, "field %q is not map[string]any: %v", keyMember, rawMember)
if testGroup.isHex {
assertHexFieldEqual(t, member, keyID, tt.wantHexString)
} else {
assertNumericFieldEqual(t, member, keyID, tt.wantDecimalNumber)
}
}
func assertMembers(t *testing.T, testGroup *testScenario, tt *testCase, got map[string]any) {
rawMembers, ok := got[keyMembers]
require.Truef(t, ok, "output does not contain %q field: %v", keyMembers, got)
members, ok := rawMembers.([]any)
require.Truef(t, ok, "field %q is not []any: %v", keyMembers, rawMembers)
for _, rawMember := range members {
assertMember(t, testGroup, tt, rawMember)
}
}
type testCase struct {
number uint64
wantHexString string
wantDecimalNumber int64
}
type testScenario struct {
name string
isHex bool
cases []testCase
}
var testCases = []testCase{
{1, "1", 1},
{100, "64", 100},
{1234567890, "499602d2", 1234567890},
{math.MaxInt64, "7fffffffffffffff", math.MaxInt64},
}
func TestMemberAdd(t *testing.T) {
tests := []testScenario{
{name: "decimal", isHex: false, cases: testCases},
{name: "hex", isHex: true, cases: testCases},
}
for _, testGroup := range tests {
t.Run(testGroup.name, func(t *testing.T) {
var buffer bytes.Buffer
p := &jsonPrinter{writer: &buffer, isHex: testGroup.isHex}
for _, tt := range testGroup.cases {
t.Run(fmt.Sprintf("number=%d", tt.number), func(t *testing.T) {
buffer.Reset()
decoder := json.NewDecoder(&buffer)
decoder.UseNumber()
response := clientv3.MemberAddResponse{
Header: &pb.ResponseHeader{
ClusterId: tt.number,
MemberId: tt.number,
Revision: int64(tt.number),
RaftTerm: tt.number,
},
Member: &pb.Member{ID: tt.number},
Members: []*pb.Member{{ID: tt.number}},
}
p.MemberAdd(response)
var got map[string]any
err := decoder.Decode(&got)
require.NoErrorf(t, err, "failed to decode JSON")
assertHeader(t, &testGroup, &tt, got)
rawMember, ok := got[keyMember]
require.Truef(t, ok, "output does not contain %q field: %v", keyMember, got)
assertMember(t, &testGroup, &tt, rawMember)
assertMembers(t, &testGroup, &tt, got)
})
}
})
}
}
func TestMemberRemove(t *testing.T) {
tests := []testScenario{
{name: "decimal", isHex: false, cases: testCases},
{name: "hex", isHex: true, cases: testCases},
}
for _, testGroup := range tests {
t.Run(testGroup.name, func(t *testing.T) {
var buffer bytes.Buffer
p := &jsonPrinter{writer: &buffer, isHex: testGroup.isHex}
for _, tt := range testGroup.cases {
t.Run(fmt.Sprintf("number=%d", tt.number), func(t *testing.T) {
buffer.Reset()
decoder := json.NewDecoder(&buffer)
decoder.UseNumber()
response := clientv3.MemberRemoveResponse{
Header: &pb.ResponseHeader{
ClusterId: tt.number,
MemberId: tt.number,
Revision: int64(tt.number),
RaftTerm: tt.number,
},
Members: []*pb.Member{{ID: tt.number}},
}
p.MemberRemove(0, response)
var got map[string]any
err := decoder.Decode(&got)
require.NoErrorf(t, err, "failed to decode JSON")
assertHeader(t, &testGroup, &tt, got)
assertMembers(t, &testGroup, &tt, got)
})
}
})
}
}
func TestMemberUpdate(t *testing.T) {
tests := []testScenario{
{name: "decimal", isHex: false, cases: testCases},
{name: "hex", isHex: true, cases: testCases},
}
for _, testGroup := range tests {
t.Run(testGroup.name, func(t *testing.T) {
var buffer bytes.Buffer
p := &jsonPrinter{writer: &buffer, isHex: testGroup.isHex}
for _, tt := range testGroup.cases {
t.Run(fmt.Sprintf("number=%d", tt.number), func(t *testing.T) {
buffer.Reset()
decoder := json.NewDecoder(&buffer)
decoder.UseNumber()
response := clientv3.MemberUpdateResponse{
Header: &pb.ResponseHeader{
ClusterId: tt.number,
MemberId: tt.number,
Revision: int64(tt.number),
RaftTerm: tt.number,
},
Members: []*pb.Member{{ID: tt.number}},
}
p.MemberUpdate(0, response)
var got map[string]any
err := decoder.Decode(&got)
require.NoErrorf(t, err, "failed to decode JSON")
assertHeader(t, &testGroup, &tt, got)
assertMembers(t, &testGroup, &tt, got)
})
}
})
}
}
func TestMemberPromote(t *testing.T) {
tests := []testScenario{
{name: "decimal", isHex: false, cases: testCases},
{name: "hex", isHex: true, cases: testCases},
}
for _, testGroup := range tests {
t.Run(testGroup.name, func(t *testing.T) {
var buffer bytes.Buffer
p := &jsonPrinter{writer: &buffer, isHex: testGroup.isHex}
for _, tt := range testGroup.cases {
t.Run(fmt.Sprintf("number=%d", tt.number), func(t *testing.T) {
buffer.Reset()
decoder := json.NewDecoder(&buffer)
decoder.UseNumber()
response := clientv3.MemberPromoteResponse{
Header: &pb.ResponseHeader{
ClusterId: tt.number,
MemberId: tt.number,
Revision: int64(tt.number),
RaftTerm: tt.number,
},
Members: []*pb.Member{{ID: tt.number}},
}
p.MemberPromote(0, response)
var got map[string]any
err := decoder.Decode(&got)
require.NoErrorf(t, err, "failed to decode JSON")
assertHeader(t, &testGroup, &tt, got)
assertMembers(t, &testGroup, &tt, got)
})
}
})
}
}
func TestMemberList(t *testing.T) {
tests := []testScenario{
{name: "decimal", isHex: false, cases: testCases},
{name: "hex", isHex: true, cases: testCases},
}
for _, testGroup := range tests {
t.Run(testGroup.name, func(t *testing.T) {
var buffer bytes.Buffer
p := &jsonPrinter{writer: &buffer, isHex: testGroup.isHex}
for _, tt := range testGroup.cases {
t.Run(fmt.Sprintf("number=%d", tt.number), func(t *testing.T) {
buffer.Reset()
decoder := json.NewDecoder(&buffer)
decoder.UseNumber()
response := clientv3.MemberListResponse{
Header: &pb.ResponseHeader{
ClusterId: tt.number,
MemberId: tt.number,
Revision: int64(tt.number),
RaftTerm: tt.number,
},
Members: []*pb.Member{{ID: tt.number}},
}
p.MemberList(response)
var got map[string]any
err := decoder.Decode(&got)
require.NoErrorf(t, err, "failed to decode JSON")
assertHeader(t, &testGroup, &tt, got)
assertMembers(t, &testGroup, &tt, got)
})
}
})
}
}
================================================
FILE: etcdctl/ctlv3/command/printer_protobuf.go
================================================
// Copyright 2016 The etcd Authors
//
// 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 command
import (
"fmt"
"os"
pb "go.etcd.io/etcd/api/v3/etcdserverpb"
mvccpb "go.etcd.io/etcd/api/v3/mvccpb"
v3 "go.etcd.io/etcd/client/v3"
"go.etcd.io/etcd/pkg/v3/cobrautl"
)
type pbPrinter struct{ printer }
type pbMarshal interface {
Marshal() ([]byte, error)
}
func newPBPrinter() printer {
return &pbPrinter{
&printerRPC{newPrinterUnsupported("protobuf"), printPB},
}
}
func (p *pbPrinter) Watch(r v3.WatchResponse) {
evs := make([]*mvccpb.Event, len(r.Events))
for i, ev := range r.Events {
evs[i] = (*mvccpb.Event)(ev)
}
wr := pb.WatchResponse{
Header: &r.Header,
Events: evs,
CompactRevision: r.CompactRevision,
Canceled: r.Canceled,
Created: r.Created,
}
printPB(&wr)
}
func printPB(v any) {
m, ok := v.(pbMarshal)
if !ok {
cobrautl.ExitWithError(cobrautl.ExitBadFeature, fmt.Errorf("marshal unsupported for type %T (%v)", v, v))
}
b, err := m.Marshal()
if err != nil {
fmt.Fprintf(os.Stderr, "%v\n", err)
return
}
fmt.Print(string(b))
}
================================================
FILE: etcdctl/ctlv3/command/printer_simple.go
================================================
// Copyright 2016 The etcd Authors
//
// 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 command
import (
"fmt"
"os"
"strings"
pb "go.etcd.io/etcd/api/v3/etcdserverpb"
"go.etcd.io/etcd/client/pkg/v3/types"
v3 "go.etcd.io/etcd/client/v3"
)
const rootRole = "root"
type simplePrinter struct {
isHex bool
valueOnly bool
}
func (s *simplePrinter) Del(resp v3.DeleteResponse) {
fmt.Println(resp.Deleted)
for _, kv := range resp.PrevKvs {
printKV(s.isHex, s.valueOnly, kv)
}
}
func (s *simplePrinter) Get(resp v3.GetResponse) {
for _, kv := range resp.Kvs {
printKV(s.isHex, s.valueOnly, kv)
}
}
func (s *simplePrinter) Put(r v3.PutResponse) {
fmt.Println("OK")
if r.PrevKv != nil {
printKV(s.isHex, s.valueOnly, r.PrevKv)
}
}
func (s *simplePrinter) Txn(resp v3.TxnResponse) {
if resp.Succeeded {
fmt.Println("SUCCESS")
} else {
fmt.Println("FAILURE")
}
for _, r := range resp.Responses {
fmt.Println("")
switch v := r.Response.(type) {
case *pb.ResponseOp_ResponseDeleteRange:
s.Del((v3.DeleteResponse)(*v.ResponseDeleteRange))
case *pb.ResponseOp_ResponsePut:
s.Put((v3.PutResponse)(*v.ResponsePut))
case *pb.ResponseOp_ResponseRange:
s.Get(((v3.GetResponse)(*v.ResponseRange)))
default:
fmt.Printf("unexpected response %+v\n", r)
}
}
}
func (s *simplePrinter) Watch(resp v3.WatchResponse) {
for _, e := range resp.Events {
fmt.Println(e.Type)
if e.PrevKv != nil {
printKV(s.isHex, s.valueOnly, e.PrevKv)
}
printKV(s.isHex, s.valueOnly, e.Kv)
}
}
func (s *simplePrinter) Grant(resp v3.LeaseGrantResponse) {
fmt.Printf("lease %016x granted with TTL(%ds)\n", resp.ID, resp.TTL)
}
func (s *simplePrinter) Revoke(id v3.LeaseID, r v3.LeaseRevokeResponse) {
fmt.Printf("lease %016x revoked\n", id)
}
func (s *simplePrinter) KeepAlive(resp v3.LeaseKeepAliveResponse) {
fmt.Printf("lease %016x keepalived with TTL(%d)\n", resp.ID, resp.TTL)
}
func (s *simplePrinter) TimeToLive(resp v3.LeaseTimeToLiveResponse, keys bool) {
if resp.GrantedTTL == 0 && resp.TTL == -1 {
fmt.Printf("lease %016x already expired\n", resp.ID)
return
}
txt := fmt.Sprintf("lease %016x granted with TTL(%ds), remaining(%ds)", resp.ID, resp.GrantedTTL, resp.TTL)
if keys {
ks := make([]string, len(resp.Keys))
for i := range resp.Keys {
ks[i] = string(resp.Keys[i])
}
txt += fmt.Sprintf(", attached keys(%v)", ks)
}
fmt.Println(txt)
}
func (s *simplePrinter) Leases(resp v3.LeaseLeasesResponse) {
fmt.Printf("found %d leases\n", len(resp.Leases))
for _, item := range resp.Leases {
fmt.Printf("%016x\n", item.ID)
}
}
func (s *simplePrinter) Alarm(resp v3.AlarmResponse) {
for _, e := range resp.Alarms {
fmt.Printf("%+v\n", e)
}
}
func (s *simplePrinter) MemberAdd(r v3.MemberAddResponse) {
asLearner := " "
if r.Member.IsLearner {
asLearner = " as learner "
}
fmt.Printf("Member %16x added%sto cluster %16x\n", r.Member.ID, asLearner, r.Header.ClusterId)
}
func (s *simplePrinter) MemberRemove(id uint64, r v3.MemberRemoveResponse) {
fmt.Printf("Member %16x removed from cluster %16x\n", id, r.Header.ClusterId)
}
func (s *simplePrinter) MemberUpdate(id uint64, r v3.MemberUpdateResponse) {
fmt.Printf("Member %16x updated in cluster %16x\n", id, r.Header.ClusterId)
}
func (s *simplePrinter) MemberPromote(id uint64, r v3.MemberPromoteResponse) {
fmt.Printf("Member %16x promoted in cluster %16x\n", id, r.Header.ClusterId)
}
func (s *simplePrinter) MemberList(resp v3.MemberListResponse) {
_, rows := makeMemberListTable(resp)
for _, row := range rows {
fmt.Println(strings.Join(row, ", "))
}
}
func (s *simplePrinter) EndpointHealth(hs []epHealth) {
for _, h := range hs {
if h.Error == "" {
fmt.Printf("%s is healthy: successfully committed proposal: took = %v\n", h.Ep, h.Took)
} else {
fmt.Fprintf(os.Stderr, "%s is unhealthy: failed to commit proposal: %v\n", h.Ep, h.Error)
}
}
}
func (s *simplePrinter) EndpointStatus(statusList []epStatus) {
_, rows := makeEndpointStatusTable(statusList)
for _, row := range rows {
fmt.Println(strings.Join(row, ", "))
}
}
func (s *simplePrinter) EndpointHashKV(hashList []epHashKV) {
_, rows := makeEndpointHashKVTable(hashList)
for _, row := range rows {
fmt.Println(strings.Join(row, ", "))
}
}
func (s *simplePrinter) MoveLeader(leader, target uint64, r v3.MoveLeaderResponse) {
fmt.Printf("Leadership transferred from %s to %s\n", types.ID(leader), types.ID(target))
}
func (s *simplePrinter) DowngradeValidate(r v3.DowngradeResponse) {
fmt.Printf("Downgrade validate success, cluster version %s\n", r.Version)
}
func (s *simplePrinter) DowngradeEnable(r v3.DowngradeResponse) {
fmt.Printf("Downgrade enable success, cluster version %s\n", r.Version)
}
func (s *simplePrinter) DowngradeCancel(r v3.DowngradeResponse) {
fmt.Printf("Downgrade cancel success, cluster version %s\n", r.Version)
}
func (s *simplePrinter) RoleAdd(role string, r v3.AuthRoleAddResponse) {
fmt.Printf("Role %s created\n", role)
}
func (s *simplePrinter) RoleGet(role string, r v3.AuthRoleGetResponse) {
fmt.Printf("Role %s\n", role)
if rootRole == role && r.Perm == nil {
fmt.Println("KV Read:")
fmt.Println("\t[, ")
fmt.Println("KV Write:")
fmt.Println("\t[, ")
return
}
fmt.Println("KV Read:")
printRange := func(perm *v3.Permission) {
sKey := string(perm.Key)
sRangeEnd := string(perm.RangeEnd)
if sRangeEnd != "\x00" {
fmt.Printf("\t[%s, %s)", sKey, sRangeEnd)
} else {
fmt.Printf("\t[%s, ", sKey)
}
if v3.GetPrefixRangeEnd(sKey) == sRangeEnd && len(sKey) > 0 {
fmt.Printf(" (prefix %s)", sKey)
}
fmt.Print("\n")
}
for _, perm := range r.Perm {
if perm.PermType == v3.PermRead || perm.PermType == v3.PermReadWrite {
if len(perm.RangeEnd) == 0 {
fmt.Printf("\t%s\n", perm.Key)
} else {
printRange((*v3.Permission)(perm))
}
}
}
fmt.Println("KV Write:")
for _, perm := range r.Perm {
if perm.PermType == v3.PermWrite || perm.PermType == v3.PermReadWrite {
if len(perm.RangeEnd) == 0 {
fmt.Printf("\t%s\n", perm.Key)
} else {
printRange((*v3.Permission)(perm))
}
}
}
}
func (s *simplePrinter) RoleList(r v3.AuthRoleListResponse) {
for _, role := range r.Roles {
fmt.Printf("%s\n", role)
}
}
func (s *simplePrinter) RoleDelete(role string, r v3.AuthRoleDeleteResponse) {
fmt.Printf("Role %s deleted\n", role)
}
func (s *simplePrinter) RoleGrantPermission(role string, r v3.AuthRoleGrantPermissionResponse) {
fmt.Printf("Role %s updated\n", role)
}
func (s *simplePrinter) RoleRevokePermission(role string, key string, end string, r v3.AuthRoleRevokePermissionResponse) {
if len(end) == 0 {
fmt.Printf("Permission of key %s is revoked from role %s\n", key, role)
return
}
if end != "\x00" {
fmt.Printf("Permission of range [%s, %s) is revoked from role %s\n", key, end, role)
} else {
fmt.Printf("Permission of range [%s, is revoked from role %s\n", key, role)
}
}
func (s *simplePrinter) UserAdd(name string, r v3.AuthUserAddResponse) {
fmt.Printf("User %s created\n", name)
}
func (s *simplePrinter) UserGet(name string, r v3.AuthUserGetResponse) {
fmt.Printf("User: %s\n", name)
fmt.Print("Roles:")
for _, role := range r.Roles {
fmt.Printf(" %s", role)
}
fmt.Print("\n")
}
func (s *simplePrinter) UserChangePassword(v3.AuthUserChangePasswordResponse) {
fmt.Println("Password updated")
}
func (s *simplePrinter) UserGrantRole(user string, role string, r v3.AuthUserGrantRoleResponse) {
fmt.Printf("Role %s is granted to user %s\n", role, user)
}
func (s *simplePrinter) UserRevokeRole(user string, role string, r v3.AuthUserRevokeRoleResponse) {
fmt.Printf("Role %s is revoked from user %s\n", role, user)
}
func (s *simplePrinter) UserDelete(user string, r v3.AuthUserDeleteResponse) {
fmt.Printf("User %s deleted\n", user)
}
func (s *simplePrinter) UserList(r v3.AuthUserListResponse) {
for _, user := range r.Users {
fmt.Printf("%s\n", user)
}
}
func (s *simplePrinter) AuthStatus(r v3.AuthStatusResponse) {
fmt.Println("Authentication Status:", r.Enabled)
fmt.Println("AuthRevision:", r.AuthRevision)
}
================================================
FILE: etcdctl/ctlv3/command/printer_table.go
================================================
// Copyright 2016 The etcd Authors
//
// 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 command
import (
"os"
"github.com/olekukonko/tablewriter"
"github.com/olekukonko/tablewriter/tw"
v3 "go.etcd.io/etcd/client/v3"
)
type tablePrinter struct{ printer }
func (tp *tablePrinter) MemberList(r v3.MemberListResponse) {
hdr, rows := makeMemberListTable(r)
cfgBuilder := tablewriter.NewConfigBuilder().WithRowAlignment(tw.AlignRight)
table := tablewriter.NewTable(os.Stdout, tablewriter.WithConfig(cfgBuilder.Build()))
table.Header(hdr)
for _, row := range rows {
table.Append(row)
}
table.Render()
}
func (tp *tablePrinter) EndpointHealth(r []epHealth) {
hdr, rows := makeEndpointHealthTable(r)
cfgBuilder := tablewriter.NewConfigBuilder().WithRowAlignment(tw.AlignRight)
table := tablewriter.NewTable(os.Stdout, tablewriter.WithConfig(cfgBuilder.Build()))
table.Header(hdr)
for _, row := range rows {
table.Append(row)
}
table.Render()
}
func (tp *tablePrinter) EndpointStatus(r []epStatus) {
hdr, rows := makeEndpointStatusTable(r)
cfgBuilder := tablewriter.NewConfigBuilder().WithRowAlignment(tw.AlignRight)
table := tablewriter.NewTable(os.Stdout, tablewriter.WithConfig(cfgBuilder.Build()))
table.Header(hdr)
for _, row := range rows {
table.Append(row)
}
table.Render()
}
func (tp *tablePrinter) EndpointHashKV(r []epHashKV) {
hdr, rows := makeEndpointHashKVTable(r)
cfgBuilder := tablewriter.NewConfigBuilder().WithRowAlignment(tw.AlignRight)
table := tablewriter.NewTable(os.Stdout, tablewriter.WithConfig(cfgBuilder.Build()))
table.Header(hdr)
for _, row := range rows {
table.Append(row)
}
table.Render()
}
================================================
FILE: etcdctl/ctlv3/command/put_command.go
================================================
// Copyright 2015 The etcd Authors
//
// 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 command
import (
"fmt"
"os"
"strconv"
"github.com/spf13/cobra"
clientv3 "go.etcd.io/etcd/client/v3"
"go.etcd.io/etcd/pkg/v3/cobrautl"
)
var (
leaseStr string
putPrevKV bool
putIgnoreVal bool
putIgnoreLease bool
)
// NewPutCommand returns the cobra command for "put".
func NewPutCommand() *cobra.Command {
cmd := &cobra.Command{
Use: "put [options] ( can also be given from stdin)",
Short: "Puts the given key into the store",
Long: `
Puts the given key into the store.
When begins with '-', is interpreted as a flag.
Insert '--' for workaround:
$ put --
$ put --
If isn't given as a command line argument and '--ignore-value' is not specified,
this command tries to read the value from standard input.
If isn't given as a command line argument and '--ignore-lease' is not specified,
this command tries to read the value from standard input.
For example,
$ cat file | put
will store the content of the file to .
`,
Run: putCommandFunc,
GroupID: groupKVID,
}
cmd.Flags().StringVar(&leaseStr, "lease", "0", "lease ID (in hexadecimal) to attach to the key")
cmd.Flags().BoolVar(&putPrevKV, "prev-kv", false, "return the previous key-value pair before modification")
cmd.Flags().BoolVar(&putIgnoreVal, "ignore-value", false, "updates the key using its current value")
cmd.Flags().BoolVar(&putIgnoreLease, "ignore-lease", false, "updates the key using its current lease")
return cmd
}
// putCommandFunc executes the "put" command.
func putCommandFunc(cmd *cobra.Command, args []string) {
key, value, opts := getPutOp(args)
ctx, cancel := commandCtx(cmd)
resp, err := mustClientFromCmd(cmd).Put(ctx, key, value, opts...)
cancel()
if err != nil {
cobrautl.ExitWithError(cobrautl.ExitError, err)
}
display.Put(*resp)
}
func getPutOp(args []string) (string, string, []clientv3.OpOption) {
if len(args) == 0 {
cobrautl.ExitWithError(cobrautl.ExitBadArgs, fmt.Errorf("put command needs 1 argument and input from stdin or 2 arguments"))
}
key := args[0]
if putIgnoreVal && len(args) > 1 {
cobrautl.ExitWithError(cobrautl.ExitBadArgs, fmt.Errorf("put command needs only 1 argument when 'ignore-value' is set"))
}
var value string
var err error
if !putIgnoreVal {
value, err = argOrStdin(args, os.Stdin, 1)
if err != nil {
cobrautl.ExitWithError(cobrautl.ExitBadArgs, fmt.Errorf("put command needs 1 argument and input from stdin or 2 arguments"))
}
}
id, err := strconv.ParseInt(leaseStr, 16, 64)
if err != nil {
cobrautl.ExitWithError(cobrautl.ExitBadArgs, fmt.Errorf("bad lease ID (%w), expecting ID in Hex", err))
}
var opts []clientv3.OpOption
if id != 0 {
opts = append(opts, clientv3.WithLease(clientv3.LeaseID(id)))
}
if putPrevKV {
opts = append(opts, clientv3.WithPrevKV())
}
if putIgnoreVal {
opts = append(opts, clientv3.WithIgnoreValue())
}
if putIgnoreLease {
opts = append(opts, clientv3.WithIgnoreLease())
}
return key, value, opts
}
================================================
FILE: etcdctl/ctlv3/command/role_command.go
================================================
// Copyright 2016 The etcd Authors
//
// 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 command
import (
"context"
"fmt"
"github.com/spf13/cobra"
clientv3 "go.etcd.io/etcd/client/v3"
"go.etcd.io/etcd/pkg/v3/cobrautl"
)
var (
rolePermPrefix bool
rolePermFromKey bool
)
// NewRoleCommand returns the cobra command for "role".
func NewRoleCommand() *cobra.Command {
ac := &cobra.Command{
Use: "role ",
Short: "Role related commands. Use `etcdctl role --help` to see subcommands",
Long: "Role related commands",
GroupID: groupAuthenticationID,
}
ac.AddCommand(newRoleAddCommand())
ac.AddCommand(newRoleDeleteCommand())
ac.AddCommand(newRoleGetCommand())
ac.AddCommand(newRoleListCommand())
ac.AddCommand(newRoleGrantPermissionCommand())
ac.AddCommand(newRoleRevokePermissionCommand())
return ac
}
func newRoleAddCommand() *cobra.Command {
return &cobra.Command{
Use: "add