Skip to content

Commit a848629

Browse files
authored
mockgcp: create gcloud test for sql instance switchover (#6826)
2 parents 435dfad + fcd9c49 commit a848629

File tree

3 files changed

+1937
-1
lines changed

3 files changed

+1937
-1
lines changed

mockgcp/mocksql/sqlinstance.go

Lines changed: 211 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -252,6 +252,10 @@ func (s *sqlInstancesService) Insert(ctx context.Context, req *pb.SqlInstancesIn
252252
return nil, fmt.Errorf("creating postgres user: %w", err)
253253
}
254254
}
255+
256+
if err := s.ensureMasterReflectsReplica(ctx, *name, nil, obj); err != nil {
257+
return nil, fmt.Errorf("ensuring master reflects replica: %w", err)
258+
}
255259
}
256260

257261
op := &pb.Operation{
@@ -267,6 +271,115 @@ func (s *sqlInstancesService) Insert(ctx context.Context, req *pb.SqlInstancesIn
267271
})
268272
}
269273

274+
func (s *sqlInstancesService) ensureMasterReflectsReplica(ctx context.Context, name InstanceName, oldInstance *pb.DatabaseInstance, newInstance *pb.DatabaseInstance) error {
275+
// If this is a replica instance, we also need to update the master instance to add the replica name.
276+
277+
newMasterInstanceName := ""
278+
if newInstance != nil {
279+
newMasterInstanceName = newInstance.MasterInstanceName
280+
}
281+
282+
oldMasterInstanceName := ""
283+
if oldInstance != nil {
284+
oldMasterInstanceName = oldInstance.MasterInstanceName
285+
}
286+
287+
if newMasterInstanceName != "" && newMasterInstanceName != oldMasterInstanceName {
288+
masterName := name
289+
290+
tokens := strings.Split(newMasterInstanceName, ":")
291+
if len(tokens) >= 2 {
292+
masterName.Project = &projects.ProjectData{ID: tokens[0]}
293+
masterName.InstanceName = tokens[1]
294+
} else {
295+
masterName.InstanceName = tokens[0]
296+
}
297+
298+
masterFQN := masterName.String()
299+
300+
master := &pb.DatabaseInstance{}
301+
if err := s.storage.Get(ctx, masterFQN, master); err != nil {
302+
return fmt.Errorf("getting old master instance: %w", err)
303+
}
304+
305+
shouldUpdate := false
306+
307+
// Add to replicaNames
308+
{
309+
found := false
310+
replicaName := name.InstanceName
311+
if name.Project.ID != masterName.Project.ID {
312+
replicaName = name.Project.ID + ":" + name.InstanceName
313+
}
314+
315+
for _, s := range master.ReplicaNames {
316+
if s == replicaName {
317+
found = true
318+
}
319+
}
320+
if !found {
321+
master.ReplicaNames = append(master.ReplicaNames, replicaName)
322+
shouldUpdate = true
323+
}
324+
}
325+
326+
if shouldUpdate {
327+
if err := s.storage.Update(ctx, masterFQN, master); err != nil {
328+
return fmt.Errorf("updating old master instance: %w", err)
329+
}
330+
}
331+
}
332+
333+
if oldMasterInstanceName != "" && newMasterInstanceName != oldMasterInstanceName {
334+
masterName := name
335+
336+
tokens := strings.Split(oldMasterInstanceName, ":")
337+
if len(tokens) >= 2 {
338+
masterName.Project = &projects.ProjectData{ID: tokens[0]}
339+
masterName.InstanceName = tokens[1]
340+
} else {
341+
masterName.InstanceName = tokens[0]
342+
}
343+
344+
masterFQN := masterName.String()
345+
346+
master := &pb.DatabaseInstance{}
347+
if err := s.storage.Get(ctx, masterFQN, master); err != nil {
348+
return fmt.Errorf("getting old master instance: %w", err)
349+
}
350+
351+
shouldUpdate := false
352+
353+
// Remove from replicaNames
354+
{
355+
var keep []string
356+
replicaName := name.InstanceName
357+
if name.Project.ID != masterName.Project.ID {
358+
replicaName = name.Project.ID + ":" + name.InstanceName
359+
}
360+
361+
for _, s := range master.ReplicaNames {
362+
if s == replicaName {
363+
continue
364+
}
365+
keep = append(keep, s)
366+
}
367+
if len(keep) != len(master.ReplicaNames) {
368+
master.ReplicaNames = keep
369+
shouldUpdate = true
370+
}
371+
}
372+
373+
if shouldUpdate {
374+
if err := s.storage.Update(ctx, masterFQN, master); err != nil {
375+
return fmt.Errorf("updating old master instance: %w", err)
376+
}
377+
}
378+
}
379+
380+
return nil
381+
}
382+
270383
func setDefaultInt64(pp **wrapperspb.Int64Value, defaultValue int64) {
271384
if *pp == nil {
272385
*pp = &wrapperspb.Int64Value{
@@ -541,7 +654,11 @@ func populateDefaults(obj *pb.DatabaseInstance) {
541654
}
542655

543656
if obj.InstanceType == pb.SqlInstanceType_SQL_INSTANCE_TYPE_UNSPECIFIED {
544-
obj.InstanceType = pb.SqlInstanceType_CLOUD_SQL_INSTANCE
657+
if obj.MasterInstanceName != "" {
658+
obj.InstanceType = pb.SqlInstanceType_READ_REPLICA_INSTANCE
659+
} else {
660+
obj.InstanceType = pb.SqlInstanceType_CLOUD_SQL_INSTANCE
661+
}
545662
}
546663

547664
if obj.GeminiConfig == nil {
@@ -940,6 +1057,95 @@ func (s *sqlInstancesService) Update(ctx context.Context, req *pb.SqlInstancesUp
9401057
})
9411058
}
9421059

1060+
func (s *sqlInstancesService) Switchover(ctx context.Context, req *pb.SqlInstancesSwitchoverRequest) (*pb.Operation, error) {
1061+
name, err := s.buildInstanceName(req.GetProject(), req.GetInstance())
1062+
if err != nil {
1063+
return nil, err
1064+
}
1065+
1066+
fqn := name.String()
1067+
1068+
obj := &pb.DatabaseInstance{}
1069+
if err := s.storage.Get(ctx, fqn, obj); err != nil {
1070+
return nil, err
1071+
}
1072+
1073+
oldMasterInstanceName := obj.MasterInstanceName
1074+
if oldMasterInstanceName == "" {
1075+
return nil, status.Errorf(codes.FailedPrecondition, "Invalid request: instance is not a replica instance.")
1076+
}
1077+
1078+
// A switchover makes the old master a replica of the new master, so we need to update the old master instance accordingly.
1079+
{
1080+
oldMasterName := *name
1081+
1082+
tokens := strings.Split(oldMasterInstanceName, ":")
1083+
if len(tokens) >= 2 {
1084+
oldMasterName.Project = &projects.ProjectData{ID: tokens[0]}
1085+
oldMasterName.InstanceName = tokens[1]
1086+
} else {
1087+
oldMasterName.InstanceName = oldMasterInstanceName
1088+
}
1089+
1090+
oldMasterFQN := oldMasterName.String()
1091+
1092+
oldMaster := &pb.DatabaseInstance{}
1093+
if err := s.storage.Get(ctx, oldMasterFQN, oldMaster); err != nil {
1094+
return nil, fmt.Errorf("getting old master instance: %w", err)
1095+
}
1096+
1097+
// Swap FailoverReplica
1098+
obj.FailoverReplica = oldMaster.FailoverReplica
1099+
oldMaster.FailoverReplica = nil
1100+
1101+
// Swap InstanceType
1102+
obj.InstanceType = pb.SqlInstanceType_CLOUD_SQL_INSTANCE
1103+
oldMaster.InstanceType = pb.SqlInstanceType_READ_REPLICA_INSTANCE
1104+
1105+
// Swap MasterInstanceName
1106+
obj.MasterInstanceName = ""
1107+
oldMaster.MasterInstanceName = name.Project.ID + ":" + name.InstanceName
1108+
1109+
// Swap ReplicationCluster
1110+
obj.ReplicationCluster = oldMaster.ReplicationCluster
1111+
oldMaster.ReplicationCluster = nil
1112+
1113+
// Set replica names
1114+
replicaName := oldMasterName.InstanceName
1115+
if oldMasterName.Project.ID != name.Project.ID {
1116+
replicaName = oldMasterName.Project.ID + ":" + oldMasterName.InstanceName
1117+
}
1118+
obj.ReplicaNames = []string{replicaName}
1119+
oldMaster.ReplicaNames = nil
1120+
1121+
oldMaster.Etag = fields.ComputeWeakEtag(oldMaster)
1122+
1123+
// Update the old master
1124+
if err := s.storage.Update(ctx, oldMasterFQN, oldMaster); err != nil {
1125+
return nil, fmt.Errorf("updating old master instance: %w", err)
1126+
}
1127+
}
1128+
1129+
obj.Etag = fields.ComputeWeakEtag(obj)
1130+
1131+
if err := validateDatabaseInstance(obj); err != nil {
1132+
return nil, err
1133+
}
1134+
1135+
if err := s.storage.Update(ctx, fqn, obj); err != nil {
1136+
return nil, err
1137+
}
1138+
1139+
op := &pb.Operation{
1140+
TargetProject: name.Project.ID,
1141+
OperationType: pb.Operation_SWITCHOVER,
1142+
}
1143+
1144+
return s.operations.startLRO(ctx, op, obj, func() (proto.Message, error) {
1145+
return obj, nil
1146+
})
1147+
}
1148+
9431149
func (s *sqlInstancesService) Delete(ctx context.Context, req *pb.SqlInstancesDeleteRequest) (*pb.Operation, error) {
9441150
name, err := s.buildInstanceName(req.GetProject(), req.GetInstance())
9451151
if err != nil {
@@ -953,6 +1159,10 @@ func (s *sqlInstancesService) Delete(ctx context.Context, req *pb.SqlInstancesDe
9531159
return nil, err
9541160
}
9551161

1162+
if err := s.ensureMasterReflectsReplica(ctx, *name, deleted, nil); err != nil {
1163+
return nil, fmt.Errorf("ensuring master reflects replica: %w", err)
1164+
}
1165+
9561166
op := &pb.Operation{
9571167
TargetProject: name.Project.ID,
9581168
OperationType: pb.Operation_DELETE,

0 commit comments

Comments
 (0)