m/n/core/consensus/client: implement ThinClient()
Summary:
This implements a ThinClient function on Namespaced which returns an
clientv3.Client. These can be useful when dealing with existing code
which expects this type, but only uses non-management APIs. For example,
the clientv3 concurrency library.
Test Plan: To be used by future code, and basic enough that it IMO does not warrant unit tests?
Change-Id: Ic6e38ff654bafd8bb385cf108637f8ed058015dc
Reviewed-on: https://review.monogon.dev/c/monogon/+/121
Reviewed-by: Lorenz Brun <lorenz@nexantic.com>
diff --git a/metropolis/node/core/consensus/client/BUILD.bazel b/metropolis/node/core/consensus/client/BUILD.bazel
index 35dd097..a98eaf5 100644
--- a/metropolis/node/core/consensus/client/BUILD.bazel
+++ b/metropolis/node/core/consensus/client/BUILD.bazel
@@ -2,7 +2,10 @@
go_library(
name = "go_default_library",
- srcs = ["client.go"],
+ srcs = [
+ "client.go",
+ "unimplemented.go",
+ ],
importpath = "source.monogon.dev/metropolis/node/core/consensus/client",
visibility = ["//visibility:public"],
deps = [
diff --git a/metropolis/node/core/consensus/client/client.go b/metropolis/node/core/consensus/client/client.go
index 168f445..05c230f 100644
--- a/metropolis/node/core/consensus/client/client.go
+++ b/metropolis/node/core/consensus/client/client.go
@@ -51,6 +51,26 @@
// Namespace.Sub("a").Sub("b").Sub("c") are used to create an etcd k/v
// prefix `a:b:c/` into which K/V access is remapped.
Sub(space string) (Namespaced, error)
+
+ // ThinClient returns a clientv3.Client which has the same namespacing as
+ // the namespaced interface. It only implements the KV, Lease and Watcher
+ // interfaces - all other interfaces are unimplemented and will panic when
+ // called.
+ ThinClient() *clientv3.Client
+}
+
+// ThinClient takes a set of KV, Lease and Watcher etcd clients and turns them
+// into a full Client struct. The rest of the interfaces (Cluster, Auth,
+// Maintenance) will all panic when called.
+func ThinClient(kv clientv3.KV, lease clientv3.Lease, watcher clientv3.Watcher) *clientv3.Client {
+ return &clientv3.Client{
+ Cluster: &unimplementedCluster{},
+ KV: kv,
+ Lease: lease,
+ Watcher: watcher,
+ Auth: &unimplementedAuth{},
+ Maintenance: &unimplementedMaintenance{},
+ }
}
// local implements the Namespaced client to access a locally running etc.
@@ -95,6 +115,10 @@
return sub, nil
}
+func (l *local) ThinClient() *clientv3.Client {
+ return ThinClient(l.KV, l.Lease, l.Watcher)
+}
+
func (l *local) Close() error {
errW := l.Watcher.Close()
errL := l.Lease.Close()
diff --git a/metropolis/node/core/consensus/client/unimplemented.go b/metropolis/node/core/consensus/client/unimplemented.go
new file mode 100644
index 0000000..b0bb695
--- /dev/null
+++ b/metropolis/node/core/consensus/client/unimplemented.go
@@ -0,0 +1,148 @@
+package client
+
+import (
+ "context"
+ "errors"
+ "io"
+
+ "go.etcd.io/etcd/clientv3"
+)
+
+var (
+ // UnimplementedInNamespaced will be raised by panic() any time a method
+ // from the Cluster, Auth and Maintenance APIs gets called on a
+ // clientv3.Client returned by ThinClient or Namespaced.ThinClient.
+ UnimplementedInNamespaced = errors.New("interface not implemented in Namespaced etcd client")
+)
+
+// unimplementedCluster implements clientv3.Cluster, but panics on any call.
+type unimplementedCluster struct {
+}
+
+func (c *unimplementedCluster) MemberList(_ context.Context) (*clientv3.MemberListResponse, error) {
+ panic(UnimplementedInNamespaced)
+}
+
+func (c *unimplementedCluster) MemberAdd(_ context.Context, _ []string) (*clientv3.MemberAddResponse, error) {
+ panic(UnimplementedInNamespaced)
+}
+
+func (c *unimplementedCluster) MemberAddAsLearner(_ context.Context, _ []string) (*clientv3.MemberAddResponse, error) {
+ panic(UnimplementedInNamespaced)
+}
+
+func (c *unimplementedCluster) MemberRemove(_ context.Context, _ uint64) (*clientv3.MemberRemoveResponse, error) {
+ panic(UnimplementedInNamespaced)
+}
+
+func (c *unimplementedCluster) MemberUpdate(_ context.Context, _ uint64, _ []string) (*clientv3.MemberUpdateResponse, error) {
+ panic(UnimplementedInNamespaced)
+}
+
+func (c *unimplementedCluster) MemberPromote(_ context.Context, _ uint64) (*clientv3.MemberPromoteResponse, error) {
+ panic(UnimplementedInNamespaced)
+}
+
+// unimplementedAuth implements clientv3.Auth but panics on any call.
+type unimplementedAuth struct {
+}
+
+func (c *unimplementedAuth) Authenticate(ctx context.Context, name string, password string) (*clientv3.AuthenticateResponse, error) {
+ panic(UnimplementedInNamespaced)
+}
+
+func (c *unimplementedAuth) AuthEnable(ctx context.Context) (*clientv3.AuthEnableResponse, error) {
+ panic(UnimplementedInNamespaced)
+}
+
+func (c *unimplementedAuth) AuthDisable(ctx context.Context) (*clientv3.AuthDisableResponse, error) {
+ panic(UnimplementedInNamespaced)
+}
+
+func (c *unimplementedAuth) UserAdd(ctx context.Context, name string, password string) (*clientv3.AuthUserAddResponse, error) {
+ panic(UnimplementedInNamespaced)
+}
+
+func (c *unimplementedAuth) UserAddWithOptions(ctx context.Context, name string, password string, opt *clientv3.UserAddOptions) (*clientv3.AuthUserAddResponse, error) {
+ panic(UnimplementedInNamespaced)
+}
+
+func (c *unimplementedAuth) UserDelete(ctx context.Context, name string) (*clientv3.AuthUserDeleteResponse, error) {
+ panic(UnimplementedInNamespaced)
+}
+
+func (c *unimplementedAuth) UserChangePassword(ctx context.Context, name string, password string) (*clientv3.AuthUserChangePasswordResponse, error) {
+ panic(UnimplementedInNamespaced)
+}
+
+func (c *unimplementedAuth) UserGrantRole(ctx context.Context, user string, role string) (*clientv3.AuthUserGrantRoleResponse, error) {
+ panic(UnimplementedInNamespaced)
+}
+
+func (c *unimplementedAuth) UserGet(ctx context.Context, name string) (*clientv3.AuthUserGetResponse, error) {
+ panic(UnimplementedInNamespaced)
+}
+
+func (c *unimplementedAuth) UserList(ctx context.Context) (*clientv3.AuthUserListResponse, error) {
+ panic(UnimplementedInNamespaced)
+}
+
+func (c *unimplementedAuth) UserRevokeRole(ctx context.Context, name string, role string) (*clientv3.AuthUserRevokeRoleResponse, error) {
+ panic(UnimplementedInNamespaced)
+}
+
+func (c *unimplementedAuth) RoleAdd(ctx context.Context, name string) (*clientv3.AuthRoleAddResponse, error) {
+ panic(UnimplementedInNamespaced)
+}
+
+func (c *unimplementedAuth) RoleGrantPermission(ctx context.Context, name string, key, rangeEnd string, permType clientv3.PermissionType) (*clientv3.AuthRoleGrantPermissionResponse, error) {
+ panic(UnimplementedInNamespaced)
+}
+
+func (c *unimplementedAuth) RoleGet(ctx context.Context, role string) (*clientv3.AuthRoleGetResponse, error) {
+ panic(UnimplementedInNamespaced)
+}
+
+func (c *unimplementedAuth) RoleList(ctx context.Context) (*clientv3.AuthRoleListResponse, error) {
+ panic(UnimplementedInNamespaced)
+}
+
+func (c *unimplementedAuth) RoleRevokePermission(ctx context.Context, role string, key, rangeEnd string) (*clientv3.AuthRoleRevokePermissionResponse, error) {
+ panic(UnimplementedInNamespaced)
+}
+
+func (c *unimplementedAuth) RoleDelete(ctx context.Context, role string) (*clientv3.AuthRoleDeleteResponse, error) {
+ panic(UnimplementedInNamespaced)
+}
+
+// unimplementedMaintenance implements clientv3.Maintenance but panics on any call.
+type unimplementedMaintenance struct {
+}
+
+func (c *unimplementedMaintenance) AlarmList(ctx context.Context) (*clientv3.AlarmResponse, error) {
+ panic(UnimplementedInNamespaced)
+}
+
+func (c *unimplementedMaintenance) AlarmDisarm(ctx context.Context, m *clientv3.AlarmMember) (*clientv3.AlarmResponse, error) {
+ panic(UnimplementedInNamespaced)
+}
+
+func (c *unimplementedMaintenance) Defragment(ctx context.Context, endpoint string) (*clientv3.DefragmentResponse, error) {
+ panic(UnimplementedInNamespaced)
+}
+
+func (c *unimplementedMaintenance) Status(ctx context.Context, endpoint string) (*clientv3.StatusResponse, error) {
+ panic(UnimplementedInNamespaced)
+}
+
+func (c *unimplementedMaintenance) HashKV(ctx context.Context, endpoint string, rev int64) (*clientv3.HashKVResponse, error) {
+ panic(UnimplementedInNamespaced)
+}
+
+func (c *unimplementedMaintenance) Snapshot(ctx context.Context) (io.ReadCloser, error) {
+ panic(UnimplementedInNamespaced)
+}
+
+func (c *unimplementedMaintenance) MoveLeader(ctx context.Context, transfereeID uint64) (*clientv3.MoveLeaderResponse, error) {
+ panic(UnimplementedInNamespaced)
+}