kythe.io@v0.0.68-0.20240422202219-7225dbc01741/kythe/go/storage/gsutil/gsutil.go (about) 1 /* 2 * Copyright 2014 The Kythe Authors. All rights reserved. 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 gsutil is collection of helper functions for storage tools. 18 package gsutil // import "kythe.io/kythe/go/storage/gsutil" 19 20 import ( 21 "context" 22 "flag" 23 "fmt" 24 "os" 25 "os/signal" 26 "strings" 27 "syscall" 28 29 "kythe.io/kythe/go/services/graphstore" 30 "kythe.io/kythe/go/storage/inmemory" 31 "kythe.io/kythe/go/util/log" 32 ) 33 34 // Handler returns a graphstore.Service based on the given specification. 35 // See also: Register(string, Handler). 36 type Handler func(spec string) (graphstore.Service, error) 37 38 var ( 39 handlers = map[string]Handler{ 40 "in-memory": func(_ string) (graphstore.Service, error) { 41 return new(inmemory.GraphStore), nil 42 }, 43 } 44 defaultHandlerKind string 45 ) 46 47 // Register exposes the given Handler to ParseGraphStore. Each string starting 48 // with kind+":" will be passed to the given Handler. A kind can only be 49 // registered once. 50 func Register(kind string, h Handler) { 51 if _, exists := handlers[kind]; exists { 52 log.Fatalf("gsutil Handler for kind %q already exists", kind) 53 } 54 handlers[kind] = h 55 } 56 57 // RegisterDefault gives ParseGraphStore a fallback kind if not given any 58 // "____:" prefix. A default can only be set once. 59 func RegisterDefault(kind string) { 60 if defaultHandlerKind != "" { 61 log.Fatalf("default gsutil Handler kind already registered as %q", defaultHandlerKind) 62 } 63 defaultHandlerKind = kind 64 } 65 66 type gsFlag struct { 67 gs *graphstore.Service 68 } 69 70 // String implements part of the flag.Value interface. 71 func (f *gsFlag) String() string { 72 if f.gs == nil { 73 return "<graphstore>" 74 } 75 return fmt.Sprintf("%T", *f.gs) 76 } 77 78 // Get implements part of the flag.Getter interface. 79 func (f *gsFlag) Get() any { 80 return *f.gs 81 } 82 83 // Set implements part of the flag.Value interface. 84 func (f *gsFlag) Set(str string) (err error) { 85 *f.gs, err = ParseGraphStore(str) 86 return 87 } 88 89 // Flag defines a GraphStore flag with the specified name and usage string. 90 func Flag(gs *graphstore.Service, name, usage string) { 91 if gs == nil { 92 log.Fatal("GraphStoreFlag given nil GraphStore pointer") 93 } 94 f := gsFlag{gs: gs} 95 flag.Var(&f, name, usage) 96 } 97 98 // ParseGraphStore returns a GraphStore for the given specification. 99 func ParseGraphStore(str string) (graphstore.Service, error) { 100 str = strings.TrimSpace(str) 101 split := strings.SplitN(str, ":", 2) 102 var kind, spec string 103 if len(split) == 2 { 104 spec = split[1] 105 kind = split[0] 106 } else { 107 spec = str 108 switch { 109 case spec == "" || spec == "in-memory": 110 kind = "in-memory" 111 case defaultHandlerKind != "": 112 kind = defaultHandlerKind 113 default: 114 return nil, fmt.Errorf("unknown GraphStore: %q", str) 115 } 116 } 117 118 h, ok := handlers[kind] 119 if !ok { 120 return nil, fmt.Errorf("no gsutil Handler registered for kind %q", kind) 121 } 122 return h(spec) 123 } 124 125 // EnsureGracefulExit will try to close each gs when notified of an Interrupt, 126 // SIGTERM, or Kill signal and immediately exit the program unsuccessfully. Any 127 // errors will be logged. This function should only be called once and closing 128 // the GraphStores manually is still needed when the program does not receive a 129 // signal to quit. 130 func EnsureGracefulExit(gs ...graphstore.Service) { 131 c := make(chan os.Signal, 1) 132 signal.Notify(c, os.Interrupt) 133 signal.Notify(c, syscall.SIGTERM) 134 go func() { 135 sig := <-c 136 log.Infof("graphstore: signal %v", sig) 137 for _, g := range gs { 138 LogClose(context.Background(), g) 139 } 140 os.Exit(1) 141 }() 142 } 143 144 // LogClose closes gs and logs any resulting error. 145 func LogClose(ctx context.Context, gs graphstore.Service) { 146 if err := gs.Close(ctx); err != nil { 147 log.InfoContextf(ctx, "GraphStore failed to close: %v", err) 148 } 149 }