github.com/TrueBlocks/trueblocks-core/src/apps/chifra@v0.0.0-20241022031540-b362680128f7/internal/scrape/options.go (about) 1 // Copyright 2016, 2024 The TrueBlocks Authors. All rights reserved. 2 // Use of this source code is governed by a license that can 3 // be found in the LICENSE file. 4 /* 5 * Parts of this file were auto generated. Edit only those parts of 6 * the code inside of 'EXISTING_CODE' tags. 7 */ 8 9 package scrapePkg 10 11 import ( 12 // EXISTING_CODE 13 "encoding/json" 14 "io" 15 "net/http" 16 "net/url" 17 "os" 18 "os/user" 19 "path/filepath" 20 "runtime" 21 "strings" 22 23 "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/internal/globals" 24 "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/base" 25 "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/caps" 26 "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/config" 27 "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/configtypes" 28 "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/file" 29 "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/logger" 30 "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/rpc" 31 "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/validate" 32 "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/walk" 33 // EXISTING_CODE 34 ) 35 36 // ScrapeOptions provides all command options for the chifra scrape command. 37 type ScrapeOptions struct { 38 BlockCnt uint64 `json:"blockCnt,omitempty"` // Maximum number of blocks to process per pass 39 Sleep float64 `json:"sleep,omitempty"` // Seconds to sleep between scraper passes 40 Publisher string `json:"publisher,omitempty"` // For some query options, the publisher of the index 41 Touch base.Blknum `json:"touch,omitempty"` // First block to visit when scraping (snapped back to most recent snap_to_grid mark) 42 RunCount uint64 `json:"runCount,omitempty"` // Run the scraper this many times, then quit 43 DryRun bool `json:"dryRun,omitempty"` // Show the configuration that would be applied if run,no changes are made 44 Notify bool `json:"notify,omitempty"` // Enable the notify feature 45 Settings configtypes.ScrapeSettings `json:"settings,omitempty"` // Configuration items for the scrape 46 Globals globals.GlobalOptions `json:"globals,omitempty"` // The global options 47 Conn *rpc.Connection `json:"conn,omitempty"` // The connection to the RPC server 48 BadFlag error `json:"badFlag,omitempty"` // An error flag if needed 49 // EXISTING_CODE 50 PublisherAddr base.Address `json:"-"` 51 // EXISTING_CODE 52 } 53 54 var defaultScrapeOptions = ScrapeOptions{ 55 BlockCnt: 2000, 56 Sleep: 14, 57 } 58 59 // testLog is used only during testing to export the options for this test case. 60 func (opts *ScrapeOptions) testLog() { 61 logger.TestLog(opts.BlockCnt != 2000, "BlockCnt: ", opts.BlockCnt) 62 logger.TestLog(opts.Sleep != float64(14), "Sleep: ", opts.Sleep) 63 logger.TestLog(len(opts.Publisher) > 0, "Publisher: ", opts.Publisher) 64 logger.TestLog(opts.Touch != 0, "Touch: ", opts.Touch) 65 logger.TestLog(opts.RunCount != 0, "RunCount: ", opts.RunCount) 66 logger.TestLog(opts.DryRun, "DryRun: ", opts.DryRun) 67 logger.TestLog(opts.Notify, "Notify: ", opts.Notify) 68 opts.Settings.TestLog(opts.Globals.Chain, opts.Globals.TestMode) 69 opts.Conn.TestLog(opts.getCaches()) 70 opts.Globals.TestLog() 71 } 72 73 // String implements the Stringer interface 74 func (opts *ScrapeOptions) String() string { 75 b, _ := json.MarshalIndent(opts, "", " ") 76 return string(b) 77 } 78 79 // scrapeFinishParseApi finishes the parsing for server invocations. Returns a new ScrapeOptions. 80 func scrapeFinishParseApi(w http.ResponseWriter, r *http.Request) *ScrapeOptions { 81 values := r.URL.Query() 82 if r.Header.Get("User-Agent") == "testRunner" { 83 values.Set("testRunner", "true") 84 } 85 return ScrapeFinishParseInternal(w, values) 86 } 87 88 func ScrapeFinishParseInternal(w io.Writer, values url.Values) *ScrapeOptions { 89 copy := defaultScrapeOptions 90 copy.Globals.Caps = getCaps() 91 opts := © 92 opts.BlockCnt = 2000 93 opts.Sleep = 14 94 opts.Settings.AppsPerChunk = 2000000 95 opts.Settings.SnapToGrid = 250000 96 opts.Settings.FirstSnap = 2000000 97 opts.Settings.UnripeDist = 28 98 opts.Settings.ChannelCount = 20 99 configs := make(map[string]string, 10) 100 for key, value := range values { 101 switch key { 102 case "blockCnt": 103 opts.BlockCnt = base.MustParseUint64(value[0]) 104 case "sleep": 105 opts.Sleep = base.MustParseFloat64(value[0]) 106 case "publisher": 107 opts.Publisher = value[0] 108 case "touch": 109 opts.Touch = base.MustParseBlknum(value[0]) 110 case "runCount": 111 opts.RunCount = base.MustParseUint64(value[0]) 112 case "dryRun": 113 opts.DryRun = true 114 case "notify": 115 opts.Notify = true 116 case "appsPerChunk": 117 configs[key] = value[0] 118 case "snapToGrid": 119 configs[key] = value[0] 120 case "firstSnap": 121 configs[key] = value[0] 122 case "unripeDist": 123 configs[key] = value[0] 124 case "channelCount": 125 configs[key] = value[0] 126 case "allowMissing": 127 configs[key] = value[0] 128 default: 129 if !copy.Globals.Caps.HasKey(key) { 130 err := validate.Usage("Invalid key ({0}) in {1} route.", key, "scrape") 131 if opts.BadFlag == nil || opts.BadFlag.Error() > err.Error() { 132 opts.BadFlag = err 133 } 134 } 135 } 136 } 137 opts.Conn = opts.Globals.FinishParseApi(w, values, opts.getCaches()) 138 opts.Publisher, _ = opts.Conn.GetEnsAddress(config.GetPublisher(opts.Publisher)) 139 opts.PublisherAddr = base.HexToAddress(opts.Publisher) 140 141 // EXISTING_CODE 142 config.SetScrapeArgs(opts.Globals.Chain, configs) 143 // EXISTING_CODE 144 145 return opts 146 } 147 148 // scrapeFinishParse finishes the parsing for command line invocations. Returns a new ScrapeOptions. 149 func scrapeFinishParse(args []string) *ScrapeOptions { 150 // remove duplicates from args if any (not needed in api mode because the server does it). 151 dedup := map[string]int{} 152 if len(args) > 0 { 153 tmp := []string{} 154 for _, arg := range args { 155 if value := dedup[arg]; value == 0 { 156 tmp = append(tmp, arg) 157 } 158 dedup[arg]++ 159 } 160 args = tmp 161 } 162 163 defFmt := "txt" 164 opts := GetOptions() 165 opts.Conn = opts.Globals.FinishParse(args, opts.getCaches()) 166 opts.Publisher, _ = opts.Conn.GetEnsAddress(config.GetPublisher(opts.Publisher)) 167 opts.PublisherAddr = base.HexToAddress(opts.Publisher) 168 169 // EXISTING_CODE 170 configs := getConfigCmdsFromArgs() 171 config.SetScrapeArgs(opts.Globals.Chain, configs) 172 if len(args) == 1 && (args[0] == "run" || args[0] == "indexer") { 173 // these options have been deprecated, so do nothing 174 } else if len(args) > 1 { 175 opts.BadFlag = validate.Usage("Invalid argument {0}", args[0]) 176 } 177 // EXISTING_CODE 178 if len(opts.Globals.Format) == 0 || opts.Globals.Format == "none" { 179 opts.Globals.Format = defFmt 180 } 181 182 return opts 183 } 184 185 func GetOptions() *ScrapeOptions { 186 // EXISTING_CODE 187 // EXISTING_CODE 188 return &defaultScrapeOptions 189 } 190 191 func getCaps() caps.Capability { 192 var capabilities caps.Capability // capabilities for chifra scrape 193 capabilities = capabilities.Add(caps.Verbose) 194 capabilities = capabilities.Add(caps.Version) 195 capabilities = capabilities.Add(caps.Noop) 196 capabilities = capabilities.Add(caps.NoColor) 197 capabilities = capabilities.Add(caps.Chain) 198 // EXISTING_CODE 199 // EXISTING_CODE 200 return capabilities 201 } 202 203 func ResetOptions(testMode bool) { 204 // We want to keep writer between command file calls 205 w := GetOptions().Globals.Writer 206 opts := ScrapeOptions{} 207 globals.SetDefaults(&opts.Globals) 208 opts.Globals.TestMode = testMode 209 opts.Globals.Writer = w 210 opts.Globals.Caps = getCaps() 211 opts.BlockCnt = 2000 212 opts.Sleep = 14 213 opts.Settings.AppsPerChunk = 2000000 214 opts.Settings.SnapToGrid = 250000 215 opts.Settings.FirstSnap = 2000000 216 opts.Settings.UnripeDist = 28 217 opts.Settings.ChannelCount = 20 218 defaultScrapeOptions = opts 219 } 220 221 func (opts *ScrapeOptions) getCaches() (caches map[walk.CacheType]bool) { 222 // EXISTING_CODE 223 // EXISTING_CODE 224 return 225 } 226 227 // EXISTING_CODE 228 // getPidFilePath finds the best path for a pid file. It first tries to use 229 // "variable directory" (e.g. /run/{user}/ on Linux), if that fails it falls back 230 // to os.TempDir() 231 func (opts *ScrapeOptions) getPidFilePath() string { 232 var pidfileDir string 233 if runtime.GOOS == "darwin" { 234 // MacOS 235 pidfileDir = filepath.Join("/usr/local/var", "run") 236 } else { 237 // Linux 238 // On Linux only root can write to the main directory /run, but every logged-in 239 // user has its own writable subdirectory with the same name as user's UID 240 user, err := user.Current() 241 if err == nil { 242 pidfileDir = filepath.Join("/run/user/", user.Uid) 243 } 244 } 245 // Fallback to temp dir 246 if pidfileDir == "" || !file.FolderExists(pidfileDir) { 247 pidfileDir = os.TempDir() 248 } 249 return filepath.Join(pidfileDir, "chifra", "scrape", strings.ToLower(opts.Globals.Chain)+".pid") 250 } 251 252 func getConfigCmdsFromArgs() map[string]string { 253 configs := make(map[string]string, 10) 254 for i := 0; i < len(os.Args); i++ { 255 arg := os.Args[i] 256 next := "" 257 if i < len(os.Args)-1 { 258 next = os.Args[i+1] 259 } 260 switch arg { 261 case "--apps_per_chunk": 262 configs["appsPerChunk"] = next 263 case "--snap_to_grid": 264 configs["snapToGrid"] = next 265 case "--first_snap": 266 configs["firstSnap"] = next 267 case "--unripe_dist": 268 configs["unripeDist"] = next 269 case "--channel_count": 270 configs["channelCount"] = next 271 case "--allow_missing": 272 configs["allowMissing"] = "true" 273 } 274 } 275 return configs 276 } 277 278 // EXISTING_CODE