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  }