vitess.io/vitess@v0.16.2/go/vt/topo/cells_aliases.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 topo 18 19 import ( 20 "fmt" 21 "path" 22 23 "google.golang.org/protobuf/proto" 24 25 "context" 26 27 topodatapb "vitess.io/vitess/go/vt/proto/topodata" 28 ) 29 30 // This file provides the utility methods to save / retrieve CellsAliases 31 // in the topology server. 32 // 33 // CellsAliases records are not meant to be changed while the system is 34 // running. In a running system, a CellsAlias can be added, and 35 // topology server implementations should be able to read them to 36 // access the cells upon demand. 37 38 func pathForCellsAlias(alias string) string { 39 return path.Join(CellsAliasesPath, alias, CellsAliasFile) 40 } 41 42 // GetCellsAliases returns the names of the existing cells. They are 43 // sorted by name. 44 func (ts *Server) GetCellsAliases(ctx context.Context, strongRead bool) (ret map[string]*topodatapb.CellsAlias, err error) { 45 conn := ts.globalCell 46 if !strongRead { 47 conn = ts.globalReadOnlyCell 48 } 49 50 entries, err := ts.globalCell.ListDir(ctx, CellsAliasesPath, false /*full*/) 51 switch { 52 case IsErrType(err, NoNode): 53 return nil, nil 54 case err == nil: 55 aliases := DirEntriesToStringArray(entries) 56 ret = make(map[string]*topodatapb.CellsAlias, len(aliases)) 57 for _, alias := range aliases { 58 aliasPath := pathForCellsAlias(alias) 59 contents, _, err := conn.Get(ctx, aliasPath) 60 if err != nil { 61 return nil, err 62 } 63 64 // Unpack the contents. 65 cellsAlias := &topodatapb.CellsAlias{} 66 if err := proto.Unmarshal(contents, cellsAlias); err != nil { 67 return nil, err 68 } 69 70 ret[alias] = cellsAlias 71 } 72 return ret, nil 73 default: 74 return nil, err 75 } 76 } 77 78 // GetCellsAlias returns the CellsAlias that matches the given name. 79 func (ts *Server) GetCellsAlias(ctx context.Context, name string, strongRead bool) (*topodatapb.CellsAlias, error) { 80 conn := ts.globalCell 81 if !strongRead { 82 conn = ts.globalReadOnlyCell 83 } 84 85 aliasPath := pathForCellsAlias(name) 86 contents, _, err := conn.Get(ctx, aliasPath) 87 if err != nil { 88 return nil, err 89 } 90 91 // Unpack the contents. 92 cellsAlias := &topodatapb.CellsAlias{} 93 if err := proto.Unmarshal(contents, cellsAlias); err != nil { 94 return nil, err 95 } 96 97 return cellsAlias, nil 98 } 99 100 // DeleteCellsAlias deletes the specified CellsAlias 101 func (ts *Server) DeleteCellsAlias(ctx context.Context, alias string) error { 102 ts.clearCellAliasesCache() 103 104 filePath := pathForCellsAlias(alias) 105 return ts.globalCell.Delete(ctx, filePath, nil) 106 } 107 108 // CreateCellsAlias creates a new CellInfo with the provided content. 109 func (ts *Server) CreateCellsAlias(ctx context.Context, alias string, cellsAlias *topodatapb.CellsAlias) error { 110 currentAliases, err := ts.GetCellsAliases(ctx, true) 111 if err != nil { 112 return err 113 } 114 115 if err := validateAlias(currentAliases, alias, cellsAlias); err != nil { 116 return fmt.Errorf("cells alias %v is not valid: %v", alias, err) 117 } 118 119 ts.clearCellAliasesCache() 120 121 // Pack the content. 122 contents, err := proto.Marshal(cellsAlias) 123 if err != nil { 124 return err 125 } 126 127 // Save it. 128 filePath := pathForCellsAlias(alias) 129 _, err = ts.globalCell.Create(ctx, filePath, contents) 130 return err 131 } 132 133 // UpdateCellsAlias updates cells for a given alias 134 func (ts *Server) UpdateCellsAlias(ctx context.Context, alias string, update func(*topodatapb.CellsAlias) error) error { 135 ts.clearCellAliasesCache() 136 137 filePath := pathForCellsAlias(alias) 138 for { 139 cellsAlias := &topodatapb.CellsAlias{} 140 141 // Read the file, unpack the contents. 142 contents, version, err := ts.globalCell.Get(ctx, filePath) 143 switch { 144 case err == nil: 145 if err := proto.Unmarshal(contents, cellsAlias); err != nil { 146 return err 147 } 148 case IsErrType(err, NoNode): 149 // Nothing to do. 150 default: 151 return err 152 } 153 154 // Call update method. 155 if err = update(cellsAlias); err != nil { 156 if IsErrType(err, NoUpdateNeeded) { 157 return nil 158 } 159 return err 160 } 161 162 currentAliases, err := ts.GetCellsAliases(ctx, true) 163 if err != nil { 164 return err 165 } 166 167 if err := validateAlias(currentAliases, alias, cellsAlias); err != nil { 168 return fmt.Errorf("cells alias %v is not valid: %v", alias, err) 169 } 170 171 // Pack and save. 172 contents, err = proto.Marshal(cellsAlias) 173 if err != nil { 174 return err 175 } 176 if _, err = ts.globalCell.Update(ctx, filePath, contents, version); !IsErrType(err, BadVersion) { 177 // This includes the 'err=nil' case. 178 return err 179 } 180 } 181 } 182 183 // validateAlias checks whether the given alias is allowed. 184 // If the alias overlaps with any existing alias other than itself, this returns 185 // a non-nil error. 186 func validateAlias(currentAliases map[string]*topodatapb.CellsAlias, newAliasName string, newAlias *topodatapb.CellsAlias) error { 187 for name, alias := range currentAliases { 188 // Skip the alias we're checking against. It's allowed to overlap with itself. 189 if name == newAliasName { 190 continue 191 } 192 193 for _, cell := range alias.Cells { 194 if InCellList(cell, newAlias.Cells) { 195 return fmt.Errorf("cell set overlaps with existing alias %v", name) 196 } 197 } 198 } 199 return nil 200 }