vitess.io/vitess@v0.16.2/go/vt/vtctld/explorer.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 vtctld
    18  
    19  import (
    20  	"fmt"
    21  	"net/http"
    22  	"path"
    23  	"sort"
    24  	"strings"
    25  
    26  	"context"
    27  
    28  	"vitess.io/vitess/go/vt/topo"
    29  )
    30  
    31  // backendExplorer is a class that uses the Backend interface of a
    32  // topo.Server to display content. It is separated in its own class
    33  // now for easier testing.
    34  type backendExplorer struct {
    35  	ts *topo.Server
    36  }
    37  
    38  // Result is what the backendExplorer returns. It represents one directory node.
    39  // It is exported so the JSON encoder can print it.
    40  type Result struct {
    41  	Data     string
    42  	Children []string
    43  	Error    string
    44  }
    45  
    46  func newBackendExplorer(ts *topo.Server) *backendExplorer {
    47  	return &backendExplorer{
    48  		ts: ts,
    49  	}
    50  }
    51  
    52  // HandlePath is the main function for this class.
    53  func (ex *backendExplorer) HandlePath(nodePath string, r *http.Request) *Result {
    54  	ctx := context.Background()
    55  	result := &Result{}
    56  
    57  	// Handle toplevel display: global, then one line per cell.
    58  	if nodePath == "/" {
    59  		cells, err := ex.ts.GetKnownCells(ctx)
    60  		if err != nil {
    61  			result.Error = err.Error()
    62  			return result
    63  		}
    64  		sort.Strings(cells)
    65  		result.Children = append([]string{topo.GlobalCell}, cells...)
    66  		return result
    67  	}
    68  
    69  	// Now find the cell.
    70  	parts := strings.Split(nodePath, "/")
    71  	if parts[0] != "" || len(parts) < 2 {
    72  		result.Error = "Invalid path: " + nodePath
    73  		return result
    74  	}
    75  	cell := parts[1]
    76  	relativePath := nodePath[len(cell)+1:]
    77  	conn, err := ex.ts.ConnForCell(ctx, cell)
    78  	if err != nil {
    79  		result.Error = fmt.Sprintf("Invalid cell: %v", err)
    80  		return result
    81  	}
    82  
    83  	// Get the file contents, if any.
    84  	data, _, err := conn.Get(ctx, relativePath)
    85  	switch err {
    86  	case nil:
    87  		if len(data) > 0 {
    88  			// It has contents, we just use it if possible.
    89  			decoded, err := topo.DecodeContent(relativePath, data, false)
    90  			if err != nil {
    91  				result.Error = err.Error()
    92  			} else {
    93  				result.Data = decoded
    94  			}
    95  
    96  			// With contents, it can't have children, so we're done.
    97  			return result
    98  		}
    99  	default:
   100  		// Something is wrong. Might not be a file.
   101  		result.Error = err.Error()
   102  	}
   103  
   104  	// Get the children, if any.
   105  	children, err := conn.ListDir(ctx, relativePath, false /*full*/)
   106  	if err != nil {
   107  		// It failed as a directory, let's just return what it did
   108  		// as a file.
   109  		return result
   110  	}
   111  
   112  	// It worked as a directory, clear any file error.
   113  	result.Error = ""
   114  	result.Children = topo.DirEntriesToStringArray(children)
   115  	return result
   116  }
   117  
   118  // initExplorer initializes the redirects for explorer
   119  func initExplorer(ts *topo.Server) {
   120  	// Main backend explorer functions.
   121  	be := newBackendExplorer(ts)
   122  	handleCollection("topodata", func(r *http.Request) (any, error) {
   123  		return be.HandlePath(path.Clean("/"+getItemPath(r.URL.Path)), r), nil
   124  	})
   125  }