vitess.io/vitess@v0.16.2/go/vt/topo/memorytopo/file.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 memorytopo 18 19 import ( 20 "context" 21 "fmt" 22 "path" 23 "strings" 24 25 "vitess.io/vitess/go/vt/proto/vtrpc" 26 "vitess.io/vitess/go/vt/vterrors" 27 28 "vitess.io/vitess/go/vt/topo" 29 ) 30 31 // Create is part of topo.Conn interface. 32 func (c *Conn) Create(ctx context.Context, filePath string, contents []byte) (topo.Version, error) { 33 if err := c.dial(ctx); err != nil { 34 return nil, err 35 } 36 37 if contents == nil { 38 contents = []byte{} 39 } 40 41 c.factory.mu.Lock() 42 defer c.factory.mu.Unlock() 43 44 if c.factory.err != nil { 45 return nil, c.factory.err 46 } 47 48 // Get the parent dir. 49 dir, file := path.Split(filePath) 50 p := c.factory.getOrCreatePath(c.cell, dir) 51 if p == nil { 52 return nil, vterrors.Errorf(vtrpc.Code_INVALID_ARGUMENT, "trying to create file %v in cell %v in a path that contains files", filePath, c.cell) 53 } 54 55 // Check the file doesn't already exist. 56 if _, ok := p.children[file]; ok { 57 return nil, topo.NewError(topo.NodeExists, file) 58 } 59 60 // Create the file. 61 n := c.factory.newFile(file, contents, p) 62 p.children[file] = n 63 64 n.propagateRecursiveWatch(&topo.WatchDataRecursive{ 65 Path: filePath, 66 WatchData: topo.WatchData{ 67 Contents: n.contents, 68 Version: NodeVersion(n.version), 69 }, 70 }) 71 72 return NodeVersion(n.version), nil 73 } 74 75 // Update is part of topo.Conn interface. 76 func (c *Conn) Update(ctx context.Context, filePath string, contents []byte, version topo.Version) (topo.Version, error) { 77 if err := c.dial(ctx); err != nil { 78 return nil, err 79 } 80 81 if contents == nil { 82 contents = []byte{} 83 } 84 85 c.factory.mu.Lock() 86 defer c.factory.mu.Unlock() 87 88 if c.factory.err != nil { 89 return nil, c.factory.err 90 } 91 92 // Get the parent dir, we'll need it in case of creation. 93 dir, file := path.Split(filePath) 94 p := c.factory.nodeByPath(c.cell, dir) 95 if p == nil { 96 // Parent doesn't exist, let's create it if we need to. 97 if version != nil { 98 return nil, topo.NewError(topo.NoNode, filePath) 99 } 100 p = c.factory.getOrCreatePath(c.cell, dir) 101 if p == nil { 102 return nil, vterrors.Errorf(vtrpc.Code_FAILED_PRECONDITION, "trying to create file %v in cell %v in a path that contains files", filePath, c.cell) 103 } 104 } 105 106 // Get the existing file. 107 n, ok := p.children[file] 108 if !ok { 109 // File doesn't exist, see if we need to create it. 110 if version != nil { 111 return nil, topo.NewError(topo.NoNode, filePath) 112 } 113 n = c.factory.newFile(file, contents, p) 114 p.children[file] = n 115 return NodeVersion(n.version), nil 116 } 117 118 // Check if it's a directory. 119 if n.isDirectory() { 120 return nil, vterrors.Errorf(vtrpc.Code_INVALID_ARGUMENT, "Update(%v, %v) failed: it's a directory", c.cell, filePath) 121 } 122 123 // Check the version. 124 if version != nil && n.version != uint64(version.(NodeVersion)) { 125 return nil, topo.NewError(topo.BadVersion, filePath) 126 } 127 128 // Now we can update. 129 n.version = c.factory.getNextVersion() 130 n.contents = contents 131 132 // Call the watches 133 for _, w := range n.watches { 134 if w.contents != nil { 135 w.contents <- &topo.WatchData{ 136 Contents: n.contents, 137 Version: NodeVersion(n.version), 138 } 139 } 140 } 141 142 n.propagateRecursiveWatch(&topo.WatchDataRecursive{ 143 Path: filePath, 144 WatchData: topo.WatchData{ 145 Contents: n.contents, 146 Version: NodeVersion(n.version), 147 }, 148 }) 149 150 return NodeVersion(n.version), nil 151 } 152 153 // Get is part of topo.Conn interface. 154 func (c *Conn) Get(ctx context.Context, filePath string) ([]byte, topo.Version, error) { 155 if err := c.dial(ctx); err != nil { 156 return nil, nil, err 157 } 158 159 c.factory.mu.Lock() 160 defer c.factory.mu.Unlock() 161 162 if c.factory.err != nil { 163 return nil, nil, c.factory.err 164 } 165 166 // Get the node. 167 n := c.factory.nodeByPath(c.cell, filePath) 168 if n == nil { 169 return nil, nil, topo.NewError(topo.NoNode, filePath) 170 } 171 if n.contents == nil { 172 // it's a directory 173 return nil, nil, fmt.Errorf("cannot Get() directory %v in cell %v", filePath, c.cell) 174 } 175 return n.contents, NodeVersion(n.version), nil 176 } 177 178 // List is part of the topo.Conn interface. 179 func (c *Conn) List(ctx context.Context, filePathPrefix string) ([]topo.KVInfo, error) { 180 if err := c.dial(ctx); err != nil { 181 return nil, err 182 } 183 184 c.factory.mu.Lock() 185 defer c.factory.mu.Unlock() 186 187 if c.factory.err != nil { 188 return nil, c.factory.err 189 } 190 191 dir, file := path.Split(filePathPrefix) 192 // Get the node to list. 193 n := c.factory.nodeByPath(c.cell, dir) 194 if n == nil { 195 return []topo.KVInfo{}, topo.NewError(topo.NoNode, filePathPrefix) 196 } 197 198 var result []topo.KVInfo 199 for name, child := range n.children { 200 if !strings.HasPrefix(name, file) { 201 continue 202 } 203 if child.isDirectory() { 204 result = append(result, gatherChildren(child, path.Join(dir, name))...) 205 } else { 206 result = append(result, topo.KVInfo{ 207 Key: []byte(path.Join(dir, name)), 208 Value: child.contents, 209 Version: NodeVersion(child.version), 210 }) 211 } 212 } 213 214 if len(result) == 0 { 215 return []topo.KVInfo{}, topo.NewError(topo.NoNode, filePathPrefix) 216 } 217 218 return result, nil 219 } 220 221 func gatherChildren(n *node, dirPath string) []topo.KVInfo { 222 var result []topo.KVInfo 223 for name, child := range n.children { 224 if child.isDirectory() { 225 result = append(result, gatherChildren(child, path.Join(dirPath, name))...) 226 } else { 227 result = append(result, topo.KVInfo{ 228 Key: []byte(path.Join(dirPath, name)), 229 Value: child.contents, 230 Version: NodeVersion(child.version), 231 }) 232 } 233 } 234 return result 235 } 236 237 // Delete is part of topo.Conn interface. 238 func (c *Conn) Delete(ctx context.Context, filePath string, version topo.Version) error { 239 if err := c.dial(ctx); err != nil { 240 return err 241 } 242 243 c.factory.mu.Lock() 244 defer c.factory.mu.Unlock() 245 246 if c.factory.err != nil { 247 return c.factory.err 248 } 249 250 // Get the parent dir. 251 dir, file := path.Split(filePath) 252 p := c.factory.nodeByPath(c.cell, dir) 253 if p == nil { 254 return topo.NewError(topo.NoNode, filePath) 255 } 256 257 // Get the existing file. 258 n, ok := p.children[file] 259 if !ok { 260 return topo.NewError(topo.NoNode, filePath) 261 } 262 263 // Check if it's a directory. 264 if n.isDirectory() { 265 //lint:ignore ST1005 Delete is a function name 266 return fmt.Errorf("delete(%v, %v) failed: it's a directory", c.cell, filePath) 267 } 268 269 // Check the version. 270 if version != nil && n.version != uint64(version.(NodeVersion)) { 271 return topo.NewError(topo.BadVersion, filePath) 272 } 273 274 // Now we can delete. 275 c.factory.recursiveDelete(n) 276 277 // Call the watches 278 for _, w := range n.watches { 279 if w.contents != nil { 280 w.contents <- &topo.WatchData{ 281 Err: topo.NewError(topo.NoNode, filePath), 282 } 283 close(w.contents) 284 } 285 if w.lock != nil { 286 close(w.lock) 287 } 288 } 289 290 n.propagateRecursiveWatch(&topo.WatchDataRecursive{ 291 Path: filePath, 292 WatchData: topo.WatchData{ 293 Err: topo.NewError(topo.NoNode, filePath), 294 }, 295 }) 296 297 return nil 298 }