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  }