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 }