github.com/hasnat/dolt/go@v0.0.0-20210628190320-9eb5d843fbb7/store/cmd/noms/noms_root.go (about)

     1  // Copyright 2019 Dolthub, Inc.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  //
    15  // This file incorporates work covered by the following copyright and
    16  // permission notice:
    17  //
    18  // Copyright 2016 Attic Labs, Inc. All rights reserved.
    19  // Licensed under the Apache License, version 2.0:
    20  // http://www.apache.org/licenses/LICENSE-2.0
    21  
    22  package main
    23  
    24  import (
    25  	"context"
    26  	"fmt"
    27  	"os"
    28  	"strings"
    29  
    30  	flag "github.com/juju/gnuflag"
    31  
    32  	"github.com/dolthub/dolt/go/store/cmd/noms/util"
    33  	"github.com/dolthub/dolt/go/store/config"
    34  	"github.com/dolthub/dolt/go/store/d"
    35  	"github.com/dolthub/dolt/go/store/datas"
    36  	"github.com/dolthub/dolt/go/store/hash"
    37  	"github.com/dolthub/dolt/go/store/types"
    38  )
    39  
    40  var nomsRoot = &util.Command{
    41  	Run:       runRoot,
    42  	UsageLine: "root <db-spec>",
    43  	Short:     "Get or set the current root hash of the entire database",
    44  	Long:      "See Spelling Objects at https://github.com/attic-labs/noms/blob/master/doc/spelling.md for details on the database argument.",
    45  	Flags:     setupRootFlags,
    46  	Nargs:     1,
    47  }
    48  
    49  var updateRoot = ""
    50  
    51  func setupRootFlags() *flag.FlagSet {
    52  	flagSet := flag.NewFlagSet("root", flag.ExitOnError)
    53  	flagSet.StringVar(&updateRoot, "update", "", "Replaces the entire database with the one with the given hash")
    54  	return flagSet
    55  }
    56  
    57  func runRoot(ctx context.Context, args []string) int {
    58  	if len(args) < 1 {
    59  		fmt.Fprintln(os.Stderr, "Not enough arguments")
    60  		return 0
    61  	}
    62  
    63  	cfg := config.NewResolver()
    64  	cs, err := cfg.GetChunkStore(ctx, args[0])
    65  	util.CheckErrorNoUsage(err)
    66  
    67  	currRoot, err := cs.Root(ctx)
    68  
    69  	if err != nil {
    70  		fmt.Fprintln(os.Stderr, "error getting root.", err)
    71  		return 1
    72  	}
    73  
    74  	if updateRoot == "" {
    75  		fmt.Println(currRoot)
    76  		return 0
    77  	}
    78  
    79  	if updateRoot[0] == '#' {
    80  		updateRoot = updateRoot[1:]
    81  	}
    82  	h, ok := hash.MaybeParse(updateRoot)
    83  	if !ok {
    84  		fmt.Fprintf(os.Stderr, "Invalid hash: %s\n", h.String())
    85  		return 1
    86  	}
    87  
    88  	// If BUG 3407 is correct, we might be able to just take cs and make a Database directly from that.
    89  	db, err := cfg.GetDatabase(ctx, args[0])
    90  	util.CheckErrorNoUsage(err)
    91  	defer db.Close()
    92  	v, err := db.ReadValue(ctx, h)
    93  	util.CheckErrorNoUsage(err)
    94  	if !validate(ctx, db.Format(), v) {
    95  		return 1
    96  	}
    97  
    98  	fmt.Println(`WARNING
    99  
   100  This operation replaces the entire database with the instance having the given
   101  hash. The old database becomes eligible for GC.
   102  
   103  ANYTHING NOT SAVED WILL BE LOST
   104  
   105  Continue?`)
   106  
   107  	var input string
   108  	n, err := fmt.Scanln(&input)
   109  	util.CheckErrorNoUsage(err)
   110  	if n != 1 || strings.ToLower(input) != "y" {
   111  		return 0
   112  	}
   113  
   114  	ok, err = cs.Commit(ctx, h, currRoot)
   115  
   116  	if err != nil {
   117  		fmt.Fprintf(os.Stderr, "commit error: %s", err.Error())
   118  		return 1
   119  	}
   120  
   121  	if !ok {
   122  		fmt.Fprintln(os.Stderr, "Optimistic concurrency failure")
   123  		return 1
   124  	}
   125  
   126  	fmt.Printf("Success. Previous root was: %s\n", currRoot)
   127  	return 0
   128  }
   129  
   130  func mustType(t *types.Type, err error) *types.Type {
   131  	d.PanicIfError(err)
   132  	return t
   133  }
   134  
   135  func mustString(str string, err error) string {
   136  	d.PanicIfError(err)
   137  	return str
   138  }
   139  
   140  func mustValue(v types.Value, err error) types.Value {
   141  	d.PanicIfError(err)
   142  	return v
   143  }
   144  
   145  func mustGetValue(v types.Value, ok bool, err error) types.Value {
   146  	d.PanicIfError(err)
   147  	d.PanicIfFalse(ok)
   148  	return v
   149  }
   150  
   151  func mustSet(s types.Set, err error) types.Set {
   152  	d.PanicIfError(err)
   153  	return s
   154  }
   155  
   156  func mustList(l types.List, err error) types.List {
   157  	d.PanicIfError(err)
   158  	return l
   159  }
   160  
   161  func validate(ctx context.Context, nbf *types.NomsBinFormat, r types.Value) bool {
   162  	rootType := mustType(types.MakeMapType(types.PrimitiveTypeMap[types.StringKind], mustType(types.MakeRefType(types.PrimitiveTypeMap[types.ValueKind]))))
   163  	if isSub, err := types.IsValueSubtypeOf(nbf, r, rootType); err != nil {
   164  		panic(err)
   165  	} else if !isSub {
   166  		fmt.Fprintf(os.Stderr, "Root of database must be %s, but you specified: %s\n", mustString(rootType.Describe(ctx)), mustString(mustType(types.TypeOf(r)).Describe(ctx)))
   167  		return false
   168  	}
   169  
   170  	yep, err := r.(types.Map).Any(ctx, func(k, v types.Value) bool {
   171  		if !datas.IsRefOfCommitType(nbf, mustType(types.TypeOf(v))) {
   172  			fmt.Fprintf(os.Stderr, "Invalid root map. Value for key '%s' is not a ref of commit.", string(k.(types.String)))
   173  			return false
   174  		}
   175  		return true
   176  	})
   177  
   178  	d.PanicIfError(err)
   179  
   180  	return yep
   181  }