github.com/stffabi/git-lfs@v2.3.5-0.20180214015214-8eeaa8d88902+incompatible/test/cmd/lfstest-standalonecustomadapter.go (about) 1 // +build testtools 2 3 package main 4 5 import ( 6 "bufio" 7 "encoding/json" 8 "fmt" 9 "io/ioutil" 10 "os" 11 "path/filepath" 12 "strings" 13 "time" 14 15 "github.com/git-lfs/git-lfs/tools" 16 ) 17 18 var backupDir string 19 20 // This test custom adapter just copies the files to a folder. 21 func main() { 22 scanner := bufio.NewScanner(os.Stdin) 23 writer := bufio.NewWriter(os.Stdout) 24 errWriter := bufio.NewWriter(os.Stderr) 25 backupDir = os.Getenv("TEST_STANDALONE_BACKUP_PATH") 26 if backupDir == "" { 27 writeToStderr("TEST_STANDALONE_BACKUP_PATH backup dir not set", errWriter) 28 os.Exit(1) 29 } 30 31 for scanner.Scan() { 32 line := scanner.Text() 33 var req request 34 if err := json.Unmarshal([]byte(line), &req); err != nil { 35 writeToStderr(fmt.Sprintf("Unable to parse request: %v\n", line), errWriter) 36 continue 37 } 38 39 switch req.Event { 40 case "init": 41 writeToStderr(fmt.Sprintf("Initialised test custom adapter for %s\n", req.Operation), errWriter) 42 resp := &initResponse{} 43 sendResponse(resp, writer, errWriter) 44 case "download": 45 writeToStderr(fmt.Sprintf("Received download request for %s\n", req.Oid), errWriter) 46 performDownload(req.Oid, req.Size, writer, errWriter) 47 case "upload": 48 writeToStderr(fmt.Sprintf("Received upload request for %s\n", req.Oid), errWriter) 49 performUpload(req.Oid, req.Size, req.Path, writer, errWriter) 50 case "terminate": 51 writeToStderr("Terminating test custom adapter gracefully.\n", errWriter) 52 break 53 } 54 } 55 56 } 57 58 func writeToStderr(msg string, errWriter *bufio.Writer) { 59 if !strings.HasSuffix(msg, "\n") { 60 msg = msg + "\n" 61 } 62 errWriter.WriteString(msg) 63 errWriter.Flush() 64 } 65 66 func sendResponse(r interface{}, writer, errWriter *bufio.Writer) error { 67 b, err := json.Marshal(r) 68 if err != nil { 69 return err 70 } 71 // Line oriented JSON 72 b = append(b, '\n') 73 _, err = writer.Write(b) 74 if err != nil { 75 return err 76 } 77 writer.Flush() 78 writeToStderr(fmt.Sprintf("Sent message %v", string(b)), errWriter) 79 return nil 80 } 81 82 func sendTransferError(oid string, code int, message string, writer, errWriter *bufio.Writer) { 83 resp := &transferResponse{"complete", oid, "", &transferError{code, message}} 84 err := sendResponse(resp, writer, errWriter) 85 if err != nil { 86 writeToStderr(fmt.Sprintf("Unable to send transfer error: %v\n", err), errWriter) 87 } 88 } 89 90 func sendProgress(oid string, bytesSoFar int64, bytesSinceLast int, writer, errWriter *bufio.Writer) { 91 resp := &progressResponse{"progress", oid, bytesSoFar, bytesSinceLast} 92 err := sendResponse(resp, writer, errWriter) 93 if err != nil { 94 writeToStderr(fmt.Sprintf("Unable to send progress update: %v\n", err), errWriter) 95 } 96 } 97 98 func performCopy(oid, src, dst string, size int64, writer, errWriter *bufio.Writer) error { 99 writeToStderr(fmt.Sprintf("Copying %s to %s\n", src, dst), errWriter) 100 srcFile, err := os.OpenFile(src, os.O_RDONLY, 0644) 101 if err != nil { 102 sendTransferError(oid, 10, err.Error(), writer, errWriter) 103 return err 104 } 105 defer srcFile.Close() 106 107 dstFile, err := os.Create(dst) 108 if err != nil { 109 sendTransferError(oid, 11, err.Error(), writer, errWriter) 110 return err 111 } 112 defer dstFile.Close() 113 114 // Turn callback into progress messages 115 cb := func(totalSize int64, readSoFar int64, readSinceLast int) error { 116 sendProgress(oid, readSoFar, readSinceLast, writer, errWriter) 117 return nil 118 } 119 _, err = tools.CopyWithCallback(dstFile, srcFile, size, cb) 120 if err != nil { 121 sendTransferError(oid, 4, fmt.Sprintf("cannot write data to dst %q: %v", dst, err), writer, errWriter) 122 os.Remove(dst) 123 return err 124 } 125 if err := dstFile.Close(); err != nil { 126 sendTransferError(oid, 5, fmt.Sprintf("can't close dst %q: %v", dst, err), writer, errWriter) 127 os.Remove(dst) 128 return err 129 } 130 return nil 131 } 132 133 func performDownload(oid string, size int64, writer, errWriter *bufio.Writer) { 134 dlFile, err := ioutil.TempFile("", "lfscustomdl") 135 if err != nil { 136 sendTransferError(oid, 1, err.Error(), writer, errWriter) 137 return 138 } 139 if err = dlFile.Close(); err != nil { 140 sendTransferError(oid, 2, err.Error(), writer, errWriter) 141 return 142 } 143 dlfilename := dlFile.Name() 144 backupPath := filepath.Join(backupDir, oid) 145 if err = performCopy(oid, backupPath, dlfilename, size, writer, errWriter); err != nil { 146 return 147 } 148 149 // completed 150 complete := &transferResponse{"complete", oid, dlfilename, nil} 151 if err := sendResponse(complete, writer, errWriter); err != nil { 152 writeToStderr(fmt.Sprintf("Unable to send completion message: %v\n", err), errWriter) 153 } 154 } 155 156 func performUpload(oid string, size int64, fromPath string, writer, errWriter *bufio.Writer) { 157 backupPath := filepath.Join(backupDir, oid) 158 if err := performCopy(oid, fromPath, backupPath, size, writer, errWriter); err != nil { 159 return 160 } 161 162 // completed 163 complete := &transferResponse{"complete", oid, "", nil} 164 if err := sendResponse(complete, writer, errWriter); err != nil { 165 writeToStderr(fmt.Sprintf("Unable to send completion message: %v\n", err), errWriter) 166 } 167 } 168 169 // Structs reimplemented so closer to a real external implementation 170 type header struct { 171 Key string `json:"key"` 172 Value string `json:"value"` 173 } 174 type action struct { 175 Href string `json:"href"` 176 Header map[string]string `json:"header,omitempty"` 177 ExpiresAt time.Time `json:"expires_at,omitempty"` 178 } 179 type transferError struct { 180 Code int `json:"code"` 181 Message string `json:"message"` 182 } 183 184 // Combined request struct which can accept anything 185 type request struct { 186 Event string `json:"event"` 187 Operation string `json:"operation"` 188 Concurrent bool `json:"concurrent"` 189 ConcurrentTransfers int `json:"concurrenttransfers"` 190 Oid string `json:"oid"` 191 Size int64 `json:"size"` 192 Path string `json:"path"` 193 Action *action `json:"action"` 194 } 195 196 type initResponse struct { 197 Error *transferError `json:"error,omitempty"` 198 } 199 type transferResponse struct { 200 Event string `json:"event"` 201 Oid string `json:"oid"` 202 Path string `json:"path,omitempty"` // always blank for upload 203 Error *transferError `json:"error,omitempty"` 204 } 205 type progressResponse struct { 206 Event string `json:"event"` 207 Oid string `json:"oid"` 208 BytesSoFar int64 `json:"bytesSoFar"` 209 BytesSinceLast int `json:"bytesSinceLast"` 210 }