vitess.io/vitess@v0.16.2/go/vt/topo/helpers/copy.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 helpers contains a few utility classes to handle topo.Server
    18  // objects, and transitions from one topo implementation to another.
    19  package helpers
    20  
    21  import (
    22  	"context"
    23  
    24  	"google.golang.org/protobuf/proto"
    25  
    26  	"vitess.io/vitess/go/vt/log"
    27  	"vitess.io/vitess/go/vt/topo"
    28  	"vitess.io/vitess/go/vt/topo/topoproto"
    29  
    30  	topodatapb "vitess.io/vitess/go/vt/proto/topodata"
    31  )
    32  
    33  // CopyKeyspaces will create the keyspaces in the destination topo.
    34  func CopyKeyspaces(ctx context.Context, fromTS, toTS *topo.Server) {
    35  	keyspaces, err := fromTS.GetKeyspaces(ctx)
    36  	if err != nil {
    37  		log.Fatalf("GetKeyspaces: %v", err)
    38  	}
    39  
    40  	for _, keyspace := range keyspaces {
    41  
    42  		ki, err := fromTS.GetKeyspace(ctx, keyspace)
    43  		if err != nil {
    44  			log.Fatalf("GetKeyspace(%v): %v", keyspace, err)
    45  		}
    46  
    47  		if err := toTS.CreateKeyspace(ctx, keyspace, ki.Keyspace); err != nil {
    48  			if topo.IsErrType(err, topo.NodeExists) {
    49  				log.Warningf("keyspace %v already exists", keyspace)
    50  			} else {
    51  				log.Errorf("CreateKeyspace(%v): %v", keyspace, err)
    52  			}
    53  		}
    54  
    55  		vs, err := fromTS.GetVSchema(ctx, keyspace)
    56  		switch {
    57  		case err == nil:
    58  			if err := toTS.SaveVSchema(ctx, keyspace, vs); err != nil {
    59  				log.Errorf("SaveVSchema(%v): %v", keyspace, err)
    60  			}
    61  		case topo.IsErrType(err, topo.NoNode):
    62  			// Nothing to do.
    63  		default:
    64  			log.Errorf("GetVSchema(%v): %v", keyspace, err)
    65  		}
    66  	}
    67  }
    68  
    69  // CopyShards will create the shards in the destination topo.
    70  func CopyShards(ctx context.Context, fromTS, toTS *topo.Server) {
    71  	keyspaces, err := fromTS.GetKeyspaces(ctx)
    72  	if err != nil {
    73  		log.Fatalf("fromTS.GetKeyspaces: %v", err)
    74  	}
    75  
    76  	for _, keyspace := range keyspaces {
    77  		shards, err := fromTS.GetShardNames(ctx, keyspace)
    78  		if err != nil {
    79  			log.Fatalf("GetShardNames(%v): %v", keyspace, err)
    80  			return
    81  		}
    82  
    83  		for _, shard := range shards {
    84  
    85  			si, err := fromTS.GetShard(ctx, keyspace, shard)
    86  			if err != nil {
    87  				log.Fatalf("GetShard(%v, %v): %v", keyspace, shard, err)
    88  			}
    89  
    90  			if err := toTS.CreateShard(ctx, keyspace, shard); err != nil {
    91  				if topo.IsErrType(err, topo.NodeExists) {
    92  					log.Warningf("shard %v/%v already exists", keyspace, shard)
    93  				} else {
    94  					log.Fatalf("CreateShard(%v, %v): %v", keyspace, shard, err)
    95  				}
    96  			}
    97  			if _, err := toTS.UpdateShardFields(ctx, keyspace, shard, func(toSI *topo.ShardInfo) error {
    98  				toSI.Shard = proto.Clone(si.Shard).(*topodatapb.Shard)
    99  				return nil
   100  			}); err != nil {
   101  				log.Fatalf("UpdateShardFields(%v, %v): %v", keyspace, shard, err)
   102  			}
   103  		}
   104  	}
   105  }
   106  
   107  // CopyTablets will create the tablets in the destination topo.
   108  func CopyTablets(ctx context.Context, fromTS, toTS *topo.Server) {
   109  	cells, err := fromTS.GetKnownCells(ctx)
   110  	if err != nil {
   111  		log.Fatalf("fromTS.GetKnownCells: %v", err)
   112  	}
   113  
   114  	for _, cell := range cells {
   115  		tabletAliases, err := fromTS.GetTabletAliasesByCell(ctx, cell)
   116  		if err != nil {
   117  			log.Fatalf("GetTabletsByCell(%v): %v", cell, err)
   118  		} else {
   119  			for _, tabletAlias := range tabletAliases {
   120  
   121  				// read the source tablet
   122  				ti, err := fromTS.GetTablet(ctx, tabletAlias)
   123  				if err != nil {
   124  					log.Fatalf("GetTablet(%v): %v", tabletAlias, err)
   125  				}
   126  
   127  				// try to create the destination
   128  				err = toTS.CreateTablet(ctx, ti.Tablet)
   129  				if topo.IsErrType(err, topo.NodeExists) {
   130  					// update the destination tablet
   131  					log.Warningf("tablet %v already exists, updating it", tabletAlias)
   132  					_, err = toTS.UpdateTabletFields(ctx, tabletAlias, func(t *topodatapb.Tablet) error {
   133  						proto.Merge(t, ti.Tablet)
   134  						return nil
   135  					})
   136  				}
   137  				if err != nil {
   138  					log.Fatalf("CreateTablet(%v): %v", tabletAlias, err)
   139  				}
   140  			}
   141  		}
   142  	}
   143  }
   144  
   145  // CopyShardReplications will create the ShardReplication objects in
   146  // the destination topo.
   147  func CopyShardReplications(ctx context.Context, fromTS, toTS *topo.Server) {
   148  	keyspaces, err := fromTS.GetKeyspaces(ctx)
   149  	if err != nil {
   150  		log.Fatalf("fromTS.GetKeyspaces: %v", err)
   151  	}
   152  
   153  	cells, err := fromTS.GetCellInfoNames(ctx)
   154  	if err != nil {
   155  		log.Fatalf("GetCellInfoNames(): %v", err)
   156  	}
   157  
   158  	for _, keyspace := range keyspaces {
   159  		shards, err := fromTS.GetShardNames(ctx, keyspace)
   160  		if err != nil {
   161  			log.Fatalf("GetShardNames(%v): %v", keyspace, err)
   162  		}
   163  
   164  		for _, shard := range shards {
   165  			for _, cell := range cells {
   166  				sri, err := fromTS.GetShardReplication(ctx, cell, keyspace, shard)
   167  				if err != nil {
   168  					log.Fatalf("GetShardReplication(%v, %v, %v): %v", cell, keyspace, shard, err)
   169  				}
   170  
   171  				sriNodes := map[string]struct{}{}
   172  				for _, node := range sri.Nodes {
   173  					sriNodes[topoproto.TabletAliasString(node.TabletAlias)] = struct{}{}
   174  				}
   175  
   176  				if err := toTS.UpdateShardReplicationFields(ctx, cell, keyspace, shard, func(oldSR *topodatapb.ShardReplication) error {
   177  					var nodes []*topodatapb.ShardReplication_Node
   178  					for _, oldNode := range oldSR.Nodes {
   179  						if _, ok := sriNodes[topoproto.TabletAliasString(oldNode.TabletAlias)]; ok {
   180  							continue
   181  						}
   182  
   183  						nodes = append(nodes, oldNode)
   184  					}
   185  
   186  					nodes = append(nodes, sri.ShardReplication.Nodes...)
   187  					// Even though ShardReplication currently only has the .Nodes field,
   188  					// keeping the proto.Merge call here prevents this copy from
   189  					// unintentionally breaking if we add new fields.
   190  					proto.Merge(oldSR, sri.ShardReplication)
   191  					oldSR.Nodes = nodes
   192  					return nil
   193  				}); err != nil {
   194  					log.Warningf("UpdateShardReplicationFields(%v, %v, %v): %v", cell, keyspace, shard, err)
   195  				}
   196  			}
   197  		}
   198  	}
   199  }
   200  
   201  // CopyRoutingRules will create the routing rules in the destination topo.
   202  func CopyRoutingRules(ctx context.Context, fromTS, toTS *topo.Server) {
   203  	rr, err := fromTS.GetRoutingRules(ctx)
   204  	if err != nil {
   205  		log.Fatalf("GetRoutingRules: %v", err)
   206  	}
   207  	if err := toTS.SaveRoutingRules(ctx, rr); err != nil {
   208  		log.Errorf("SaveRoutingRules(%v): %v", rr, err)
   209  	}
   210  }