github.com/TrueBlocks/trueblocks-core/src/apps/chifra@v0.0.0-20241022031540-b362680128f7/internal/scrape/validate.go (about)

     1  // Copyright 2021 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  package scrapePkg
     6  
     7  import (
     8  	"fmt"
     9  	"os"
    10  	"path/filepath"
    11  
    12  	"github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/base"
    13  	"github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/config"
    14  	"github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/file"
    15  	"github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/logger"
    16  	"github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/utils"
    17  	"github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/validate"
    18  )
    19  
    20  // TODO: this is a much more elegant way to do error strings:
    21  // TODO: https://github.com/storj/uplink/blob/v1.7.0/bucket.go#L19
    22  
    23  func (opts *ScrapeOptions) validateScrape() error {
    24  	chain := opts.Globals.Chain
    25  	testMode := opts.Globals.TestMode
    26  
    27  	opts.testLog()
    28  
    29  	if opts.BadFlag != nil {
    30  		return opts.BadFlag
    31  	}
    32  
    33  	if !config.IsChainConfigured(chain) {
    34  		return validate.Usage("chain {0} is not properly configured.", chain)
    35  	}
    36  
    37  	if opts.Notify {
    38  		if !NotifyConfigured() {
    39  			return validate.Usage("The {0} feature is {1}.", "--notify", "not properly configured. See the README.md")
    40  		}
    41  		if !config.IpfsRunning() {
    42  			return validate.Usage("The {0} option requires {1}.", "--notify", "a locally running IPFS daemon")
    43  		}
    44  	} else if !testMode && NotifyConfigured() && !opts.DryRun {
    45  		msg := validate.Usage("The notify feature is configured but not running. Enable it with the {0} flag.", "--notify").Error()
    46  		logger.Warn(msg)
    47  	}
    48  
    49  	err, ok := opts.Conn.IsNodeTracing()
    50  	if !ok {
    51  		return validate.Usage("{0} requires {1}, try {2} instead. Error: {3}", "chifra scrape", "tracing", "chifra init", err.Error())
    52  	}
    53  
    54  	if !opts.Conn.IsNodeArchive() {
    55  		return validate.Usage("{0} requires {1}, try {2} instead.", "chifra scrape", "an archive node", "chifra init")
    56  	}
    57  
    58  	if opts.Globals.IsApiMode() {
    59  		return validate.Usage("chifra scrape is not available in API mode")
    60  	}
    61  
    62  	if opts.Sleep < .25 {
    63  		return validate.Usage("The {0} option ({1}) must {2}.", "--sleep", fmt.Sprintf("%f", opts.Sleep), "be at least .25")
    64  	}
    65  
    66  	if opts.BlockCnt < 10 {
    67  		return validate.Usage("Specify at least {0} with {0}.", "10 blocks per round", "chifra scrape")
    68  	}
    69  
    70  	// We can't really test this code, so we just report and quit
    71  	if opts.Globals.TestMode && !opts.DryRun {
    72  		return validate.Usage("Cannot test block scraper")
    73  	}
    74  
    75  	meta, err := opts.Conn.GetMetaData(opts.Globals.TestMode)
    76  	if err != nil {
    77  		return err
    78  	}
    79  	m := base.Max(meta.Ripe, base.Max(meta.Staging, meta.Finalized)) + 1
    80  	if !opts.DryRun && m > meta.Latest {
    81  		fmt.Println(validate.Usage("The index ({0}) is ahead of the chain ({1}).", fmt.Sprintf("%d", m), fmt.Sprintf("%d", meta.Latest)))
    82  	}
    83  
    84  	if len(opts.Publisher) > 0 {
    85  		err := validate.ValidateExactlyOneAddr([]string{opts.Publisher})
    86  		if err != nil {
    87  			return err
    88  		}
    89  	}
    90  
    91  	ret := opts.Globals.Validate()
    92  
    93  	pidPath := opts.getPidFilePath()
    94  	if file.FileExists(pidPath) {
    95  		pid := base.MustParseInt64(file.AsciiFileToString(pidPath))
    96  		// fmt.Println("Pid file exists with contents:", pid)
    97  		if running, err := utils.PidExists(pid); err == nil && running {
    98  			return validate.Usage("The {0} is already running. If it is not, remove {1} and try again.", "scraper", pidPath)
    99  		} else if err != nil {
   100  			return validate.Usage("The {0} is already running. If it is not, remove {1} and try again.", "scraper", pidPath)
   101  		}
   102  		// fmt.Println("Removing pid file")
   103  		os.Remove(pidPath)
   104  	}
   105  	// If we've gotten this far, we're the only one running. As we enter the forever
   106  	// loop, we want to make sure no-one else runs. We do this by writing our pid to
   107  	// a file. Note that this probably doesn't work in the server.
   108  	if err := os.MkdirAll(filepath.Dir(pidPath), 0700); err != nil {
   109  		return fmt.Errorf("creating temp directory: %w", err)
   110  	}
   111  	if err := file.StringToAsciiFile(pidPath, fmt.Sprintf("%d", os.Getpid())); err != nil {
   112  		return fmt.Errorf("creating scrape.pid file: %w", err)
   113  	}
   114  
   115  	return ret
   116  }