vitess.io/vitess@v0.16.2/go/vt/srvtopo/keyspace_filtering_server.go (about)

     1  /*
     2  Copyright 2019 The Vitess Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package srvtopo
    18  
    19  import (
    20  	"context"
    21  	"fmt"
    22  
    23  	topodatapb "vitess.io/vitess/go/vt/proto/topodata"
    24  	vschemapb "vitess.io/vitess/go/vt/proto/vschema"
    25  	"vitess.io/vitess/go/vt/topo"
    26  )
    27  
    28  var (
    29  	// ErrNilUnderlyingServer is returned when attempting to create a new keyspace
    30  	// filtering server if a nil underlying server implementation is provided.
    31  	ErrNilUnderlyingServer = fmt.Errorf("unable to construct filtering server without an underlying server")
    32  )
    33  
    34  // NewKeyspaceFilteringServer constructs a new server based on the provided
    35  // implementation that prevents the specified keyspaces from being exposed
    36  // to consumers of the new Server.
    37  //
    38  // A filtering server will only allow read-only access to the topo.Server to prevent
    39  // updates that may corrupt the global VSchema keyspace.
    40  func NewKeyspaceFilteringServer(underlying Server, selectedKeyspaces []string) (Server, error) {
    41  	if underlying == nil {
    42  		return nil, ErrNilUnderlyingServer
    43  	}
    44  
    45  	keyspaces := map[string]bool{}
    46  	for _, ks := range selectedKeyspaces {
    47  		keyspaces[ks] = true
    48  	}
    49  
    50  	readOnlyServer, err := NewReadOnlyServer(underlying)
    51  	if err != nil {
    52  		return nil, err
    53  	}
    54  
    55  	return keyspaceFilteringServer{
    56  		server:          readOnlyServer,
    57  		selectKeyspaces: keyspaces,
    58  	}, nil
    59  }
    60  
    61  type keyspaceFilteringServer struct {
    62  	server          Server
    63  	selectKeyspaces map[string]bool
    64  }
    65  
    66  // GetTopoServer returns a read-only topo server
    67  func (ksf keyspaceFilteringServer) GetTopoServer() (*topo.Server, error) {
    68  	return ksf.server.GetTopoServer()
    69  }
    70  
    71  func (ksf keyspaceFilteringServer) GetSrvKeyspaceNames(
    72  	ctx context.Context,
    73  	cell string,
    74  	staleOK bool,
    75  ) ([]string, error) {
    76  	keyspaces, err := ksf.server.GetSrvKeyspaceNames(ctx, cell, staleOK)
    77  	ret := make([]string, 0, len(keyspaces))
    78  	for _, ks := range keyspaces {
    79  		if ksf.selectKeyspaces[ks] {
    80  			ret = append(ret, ks)
    81  		}
    82  	}
    83  	return ret, err
    84  }
    85  
    86  func (ksf keyspaceFilteringServer) GetSrvKeyspace(
    87  	ctx context.Context,
    88  	cell,
    89  	keyspace string,
    90  ) (*topodatapb.SrvKeyspace, error) {
    91  	if !ksf.selectKeyspaces[keyspace] {
    92  		return nil, topo.NewError(topo.NoNode, keyspace)
    93  	}
    94  
    95  	return ksf.server.GetSrvKeyspace(ctx, cell, keyspace)
    96  }
    97  
    98  func (ksf keyspaceFilteringServer) WatchSrvKeyspace(
    99  	ctx context.Context,
   100  	cell, keyspace string,
   101  	callback func(*topodatapb.SrvKeyspace, error) bool,
   102  ) {
   103  	filteringCallback := func(ks *topodatapb.SrvKeyspace, err error) bool {
   104  		if ks != nil {
   105  			if !ksf.selectKeyspaces[keyspace] {
   106  				return callback(nil, topo.NewError(topo.NoNode, keyspace))
   107  			}
   108  		}
   109  		return callback(ks, err)
   110  	}
   111  
   112  	ksf.server.WatchSrvKeyspace(ctx, cell, keyspace, filteringCallback)
   113  }
   114  
   115  func (ksf keyspaceFilteringServer) WatchSrvVSchema(
   116  	ctx context.Context,
   117  	cell string,
   118  	callback func(*vschemapb.SrvVSchema, error) bool,
   119  ) {
   120  	filteringCallback := func(schema *vschemapb.SrvVSchema, err error) bool {
   121  		if schema != nil {
   122  			for ks := range schema.Keyspaces {
   123  				if !ksf.selectKeyspaces[ks] {
   124  					delete(schema.Keyspaces, ks)
   125  				}
   126  			}
   127  		}
   128  
   129  		return callback(schema, err)
   130  	}
   131  
   132  	ksf.server.WatchSrvVSchema(ctx, cell, filteringCallback)
   133  }