github.com/htcondor/osdf-client/v6@v6.13.0-rc1.0.20231009141709-766e7b4d1dc8/cmd/stash_plugin/main.go (about)

     1  package main
     2  
     3  import (
     4  	"bufio"
     5  	"errors"
     6  	"fmt"
     7  	"os"
     8  	"strings"
     9  	"time"
    10  
    11  	stashcp "github.com/htcondor/osdf-client/v6"
    12  	"github.com/htcondor/osdf-client/v6/classads"
    13  	log "github.com/sirupsen/logrus"
    14  )
    15  
    16  var (
    17  	version = "dev"
    18  	commit  = "none"
    19  	date    = "unknown"
    20  	builtBy = "unknown"
    21  )
    22  
    23  type Transfer struct {
    24  	url       string
    25  	localFile string
    26  }
    27  
    28  func main() {
    29  	// Parse command line arguments
    30  
    31  	var upload bool = false
    32  	// Set the options
    33  	stashcp.Options.Recursive = false
    34  	stashcp.Options.ProgressBars = false
    35  	stashcp.Options.Version = version
    36  	if err := setLogging(log.PanicLevel); err != nil {
    37  		log.Panicln("Failed to set log level")
    38  	}
    39  	methods := []string{"cvmfs", "http"}
    40  	var infile, outfile, testCachePath string
    41  	var useOutFile bool = false
    42  	var getCaches bool = false
    43  
    44  	// Pop the executable off the args list
    45  	_, os.Args = os.Args[0], os.Args[1:]
    46  	for len(os.Args) > 0 {
    47  
    48  		if os.Args[0] == "-classad" {
    49  			// Print classad and exit
    50  			fmt.Println("MultipleFileSupport = true")
    51  			fmt.Println("PluginVersion = \"" + version + "\"")
    52  			fmt.Println("PluginType = \"FileTransfer\"")
    53  			fmt.Println("SupportedMethods = \"stash, osdf\"")
    54  			os.Exit(0)
    55  		} else if os.Args[0] == "-version" || os.Args[0] == "-v" {
    56  			fmt.Println("Version:", version)
    57  			fmt.Println("Build Date:", date)
    58  			fmt.Println("Build Commit:", commit)
    59  			fmt.Println("Built By:", builtBy)
    60  			os.Exit(0)
    61  		} else if os.Args[0] == "-upload" {
    62  			log.Debugln("Upload detected")
    63  			upload = true
    64  		} else if os.Args[0] == "-infile" {
    65  			infile = os.Args[1]
    66  			os.Args = os.Args[1:]
    67  			log.Debugln("Infile:", infile)
    68  		} else if os.Args[0] == "-outfile" {
    69  			outfile = os.Args[1]
    70  			os.Args = os.Args[1:]
    71  			useOutFile = true
    72  			log.Debugln("Outfile:", outfile)
    73  		} else if os.Args[0] == "-d" {
    74  			if err := setLogging(log.DebugLevel); err != nil {
    75  				log.Panicln("Failed to set log level to debug")
    76  			}
    77  		} else if os.Args[0] == "-get-caches" {
    78  			if len(os.Args) < 2 {
    79  				log.Errorln("-get-caches requires an argument")
    80  				os.Exit(1)
    81  			}
    82  			testCachePath = os.Args[1]
    83  			os.Args = os.Args[1:]
    84  			getCaches = true
    85  		} else if strings.HasPrefix(os.Args[0], "-") {
    86  			log.Errorln("Do not understand the option:", os.Args[0])
    87  			os.Exit(1)
    88  		} else {
    89  			// Must be the start of a source / destination
    90  			break
    91  		}
    92  		// Pop off the args
    93  		_, os.Args = os.Args[0], os.Args[1:]
    94  	}
    95  
    96  	if getCaches {
    97  		urls, err := stashcp.GetCacheHostnames(testCachePath)
    98  		if err != nil {
    99  			log.Panicln("Failed to get cache URLs:", err)
   100  		}
   101  
   102  		cachesToTry := stashcp.CachesToTry
   103  		if cachesToTry > len(urls) {
   104  			cachesToTry = len(urls)
   105  		}
   106  
   107  		for _, url := range urls[:cachesToTry] {
   108  			fmt.Println(url)
   109  		}
   110  		os.Exit(0)
   111  	}
   112  
   113  	var source []string
   114  	var dest string
   115  	var result error
   116  	//var downloaded int64 = 0
   117  	var transfers []Transfer
   118  
   119  	if len(os.Args) == 0 && (infile == "" || outfile == "") {
   120  		fmt.Fprint(os.Stderr, "No source or destination specified\n")
   121  		os.Exit(1)
   122  	}
   123  
   124  	if len(os.Args) == 0 {
   125  		// Open the input and output files
   126  		infileFile, err := os.Open(infile)
   127  		if err != nil {
   128  			log.Panicln("Failed to open infile:", err)
   129  		}
   130  		defer infileFile.Close()
   131  		// Read in classad from stdin
   132  		transfers, err = readMultiTransfers(*bufio.NewReader(infileFile))
   133  		if err != nil {
   134  			log.Errorln("Failed to read in from stdin:", err)
   135  			os.Exit(1)
   136  		}
   137  	} else {
   138  		source = os.Args[:len(os.Args)-1]
   139  		dest = os.Args[len(os.Args)-1]
   140  		for _, src := range source {
   141  			transfers = append(transfers, Transfer{url: src, localFile: dest})
   142  		}
   143  	}
   144  
   145  	var resultAds []*classads.ClassAd
   146  	retryable := false
   147  	for _, transfer := range transfers {
   148  
   149  		var tmpDownloaded int64
   150  		if upload {
   151  			source = append(source, transfer.localFile)
   152  			log.Debugln("Uploading:", transfer.localFile, "to", transfer.url)
   153  			tmpDownloaded, result = stashcp.DoStashCPSingle(transfer.localFile, transfer.url, methods, false)
   154  		} else {
   155  			source = append(source, transfer.url)
   156  			log.Debugln("Downloading:", transfer.url, "to", transfer.localFile)
   157  			tmpDownloaded, result = stashcp.DoStashCPSingle(transfer.url, transfer.localFile, methods, false)
   158  		}
   159  		startTime := time.Now().Unix()
   160  		resultAd := classads.NewClassAd()
   161  		resultAd.Set("TransferStartTime", startTime)
   162  		resultAd.Set("TransferEndTime", time.Now().Unix())
   163  		hostname, _ := os.Hostname()
   164  		resultAd.Set("TransferLocalMachineName", hostname)
   165  		resultAd.Set("TransferProtocol", "stash")
   166  		resultAd.Set("TransferUrl", transfer.url)
   167  		resultAd.Set("TransferFileName", transfer.localFile)
   168  		if upload {
   169  			resultAd.Set("TransferType", "upload")
   170  		} else {
   171  			resultAd.Set("TransferType", "download")
   172  		}
   173  		if result == nil {
   174  			resultAd.Set("TransferSuccess", true)
   175  			resultAd.Set("TransferFileBytes", tmpDownloaded)
   176  			resultAd.Set("TransferTotalBytes", tmpDownloaded)
   177  		} else {
   178  			resultAd.Set("TransferSuccess", false)
   179  			if stashcp.GetErrors() == "" {
   180  				resultAd.Set("TransferError", result.Error())
   181  			} else {
   182  				errMsg := " Failure "
   183  				if upload {
   184  					errMsg += "uploading "
   185  				} else {
   186  					errMsg += "downloading "
   187  				}
   188  				errMsg += transfer.url + ": " + stashcp.GetErrors()
   189  				resultAd.Set("TransferError", errMsg)
   190  				stashcp.ClearErrors()
   191  			}
   192  			resultAd.Set("TransferFileBytes", 0)
   193  			resultAd.Set("TransferTotalBytes", 0)
   194  			if stashcp.ErrorsRetryable() {
   195  				resultAd.Set("TransferRetryable", true)
   196  				retryable = true
   197  			} else {
   198  				resultAd.Set("TransferRetryable", false)
   199  				retryable = false
   200  
   201  			}
   202  		}
   203  		resultAds = append(resultAds, resultAd)
   204  
   205  	}
   206  
   207  	outputFile := os.Stdout
   208  	if useOutFile {
   209  		var err error
   210  		outputFile, err = os.Create(outfile)
   211  		if err != nil {
   212  			log.Panicln("Failed to open outfile:", err)
   213  		}
   214  		defer outputFile.Close()
   215  	}
   216  
   217  	success := true
   218  	for _, resultAd := range resultAds {
   219  		_, err := outputFile.WriteString(resultAd.String() + "\n")
   220  		if err != nil {
   221  			log.Panicln("Failed to write to outfile:", err)
   222  		}
   223  		transferSuccess, err := resultAd.Get("TransferSuccess")
   224  		if err != nil {
   225  			log.Errorln("Failed to get TransferSuccess:", err)
   226  			success = false
   227  		}
   228  		success = success && transferSuccess.(bool)
   229  	}
   230  
   231  	if success {
   232  		os.Exit(0)
   233  	} else if retryable {
   234  		os.Exit(11)
   235  	} else {
   236  		os.Exit(1)
   237  	}
   238  }
   239  
   240  func setLogging(logLevel log.Level) error {
   241  	textFormatter := log.TextFormatter{}
   242  	textFormatter.DisableLevelTruncation = true
   243  	textFormatter.FullTimestamp = true
   244  	log.SetFormatter(&textFormatter)
   245  	log.SetLevel(logLevel)
   246  	return nil
   247  }
   248  
   249  // readMultiTransfers reads the transfers from a Reader, such as stdin
   250  func readMultiTransfers(stdin bufio.Reader) (transfers []Transfer, err error) {
   251  	// Check stdin for a list of transfers
   252  	ads, err := classads.ReadClassAd(&stdin)
   253  	if err != nil {
   254  		return nil, err
   255  	}
   256  	if ads == nil {
   257  		return nil, errors.New("No transfers found")
   258  	}
   259  	for _, ad := range ads {
   260  		url, err := ad.Get("Url")
   261  		if err != nil {
   262  			return nil, err
   263  		}
   264  		destination, err := ad.Get("LocalFileName")
   265  		if err != nil {
   266  			return nil, err
   267  		}
   268  		transfers = append(transfers, Transfer{url: url.(string), localFile: destination.(string)})
   269  	}
   270  
   271  	return transfers, nil
   272  }