move store out
This commit is contained in:
		@ -1,8 +0,0 @@
 | 
			
		||||
# ------------------------------------------------------------------------
 | 
			
		||||
from mcluseau/golang-builder:1.13.1 as build
 | 
			
		||||
 | 
			
		||||
# ------------------------------------------------------------------------
 | 
			
		||||
from alpine:3.10
 | 
			
		||||
volume /srv/dkl-store
 | 
			
		||||
entrypoint ["/bin/dkl-store"]
 | 
			
		||||
copy --from=build /go/bin/ /bin/
 | 
			
		||||
@ -1,68 +0,0 @@
 | 
			
		||||
package main
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"crypto/sha1"
 | 
			
		||||
	"encoding/hex"
 | 
			
		||||
	"flag"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"io"
 | 
			
		||||
	"log"
 | 
			
		||||
	"net/http"
 | 
			
		||||
	"os"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var (
 | 
			
		||||
	token = flag.String("token", "", "Upload token")
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func main() {
 | 
			
		||||
	flag.Parse()
 | 
			
		||||
 | 
			
		||||
	args := flag.Args()
 | 
			
		||||
	if len(args) != 2 {
 | 
			
		||||
		fmt.Print("source file and target URL are required")
 | 
			
		||||
		os.Exit(1)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	inPath := args[0]
 | 
			
		||||
	outURL := args[1]
 | 
			
		||||
 | 
			
		||||
	in, err := os.Open(inPath)
 | 
			
		||||
	fail(err)
 | 
			
		||||
 | 
			
		||||
	// hash the file
 | 
			
		||||
	log.Print("hashing...")
 | 
			
		||||
	h := sha1.New()
 | 
			
		||||
	_, err = io.Copy(h, in)
 | 
			
		||||
	fail(err)
 | 
			
		||||
 | 
			
		||||
	sha1Hex := hex.EncodeToString(h.Sum(nil))
 | 
			
		||||
 | 
			
		||||
	log.Print("SHA1 of source: ", sha1Hex)
 | 
			
		||||
 | 
			
		||||
	// rewind
 | 
			
		||||
	_, err = in.Seek(0, os.SEEK_SET)
 | 
			
		||||
	fail(err)
 | 
			
		||||
 | 
			
		||||
	// upload
 | 
			
		||||
	req, err := http.NewRequest("POST", outURL, in)
 | 
			
		||||
	fail(err)
 | 
			
		||||
 | 
			
		||||
	req.Header.Set("X-Content-SHA1", sha1Hex)
 | 
			
		||||
 | 
			
		||||
	log.Print("uploading...")
 | 
			
		||||
	resp, err := http.DefaultClient.Do(req)
 | 
			
		||||
	fail(err)
 | 
			
		||||
 | 
			
		||||
	if resp.StatusCode != http.StatusCreated {
 | 
			
		||||
		log.Fatalf("unexpected HTTP status: %s", resp.Status)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	log.Print("uploaded successfully")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func fail(err error) {
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		log.Fatal(err)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@ -1,181 +0,0 @@
 | 
			
		||||
package main
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"crypto/sha1"
 | 
			
		||||
	"encoding/hex"
 | 
			
		||||
	"flag"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"io"
 | 
			
		||||
	"io/ioutil"
 | 
			
		||||
	"log"
 | 
			
		||||
	"net/http"
 | 
			
		||||
	"os"
 | 
			
		||||
	"path/filepath"
 | 
			
		||||
	"time"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var (
 | 
			
		||||
	bind        = flag.String("bind", ":8080", "Bind address")
 | 
			
		||||
	uploadToken = flag.String("upload-token", "", "Upload token (no uploads allowed if empty)")
 | 
			
		||||
	storeDir    = flag.String("store-dir", "/srv/dkl-store", "Store directory")
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func main() {
 | 
			
		||||
	log.SetFlags(log.LstdFlags | log.Lshortfile)
 | 
			
		||||
	flag.Parse()
 | 
			
		||||
 | 
			
		||||
	http.HandleFunc("/", handleHTTP)
 | 
			
		||||
 | 
			
		||||
	log.Print("listening on ", *bind)
 | 
			
		||||
	log.Fatal(http.ListenAndServe(*bind, nil))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func handleHTTP(w http.ResponseWriter, req *http.Request) {
 | 
			
		||||
	filePath := filepath.Join(*storeDir, req.URL.Path)
 | 
			
		||||
 | 
			
		||||
	l := fmt.Sprintf("%s %s", req.Method, filePath)
 | 
			
		||||
	log.Print(l)
 | 
			
		||||
	defer log.Print(l, " done")
 | 
			
		||||
 | 
			
		||||
	stat, err := os.Stat(filePath)
 | 
			
		||||
	if err != nil && !os.IsNotExist(err) {
 | 
			
		||||
		writeErr(err, w)
 | 
			
		||||
		return
 | 
			
		||||
	} else if err == nil && stat.Mode().IsDir() {
 | 
			
		||||
		http.NotFound(w, req)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	switch req.Method {
 | 
			
		||||
	case "GET", "HEAD":
 | 
			
		||||
		sha1Hex, err := hashOf(filePath)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			writeErr(err, w)
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		w.Header().Set("X-Content-SHA1", sha1Hex)
 | 
			
		||||
		http.ServeFile(w, req, filePath)
 | 
			
		||||
 | 
			
		||||
	case "POST":
 | 
			
		||||
		tmpOut := filepath.Join(filepath.Dir(filePath), "."+filepath.Base(filePath))
 | 
			
		||||
 | 
			
		||||
		if err := os.MkdirAll(filepath.Dir(filePath), 0755); err != nil {
 | 
			
		||||
			writeErr(err, w)
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		out, err := os.Create(tmpOut)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			writeErr(err, w)
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		h := sha1.New()
 | 
			
		||||
		mw := io.MultiWriter(out, h)
 | 
			
		||||
 | 
			
		||||
		_, err = io.Copy(mw, req.Body)
 | 
			
		||||
		out.Close()
 | 
			
		||||
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			os.Remove(tmpOut)
 | 
			
		||||
 | 
			
		||||
			writeErr(err, w)
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		sha1Hex := hex.EncodeToString(h.Sum(nil))
 | 
			
		||||
		log.Print("upload SHA1: ", sha1Hex)
 | 
			
		||||
 | 
			
		||||
		reqSHA1 := req.Header.Get("X-Content-SHA1")
 | 
			
		||||
		if reqSHA1 != "" {
 | 
			
		||||
			if reqSHA1 != sha1Hex {
 | 
			
		||||
				err = fmt.Errorf("upload SHA1 does not match given SHA1: %s", reqSHA1)
 | 
			
		||||
				w.WriteHeader(http.StatusBadRequest)
 | 
			
		||||
				w.Write([]byte(err.Error() + "\n"))
 | 
			
		||||
				return
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			log.Print("upload SHA1 is as expected")
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		os.Rename(tmpOut, filePath)
 | 
			
		||||
 | 
			
		||||
		if err := ioutil.WriteFile(filePath+".sha1", []byte(sha1Hex), 0644); err != nil {
 | 
			
		||||
			writeErr(err, w)
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		w.WriteHeader(http.StatusCreated)
 | 
			
		||||
 | 
			
		||||
	default:
 | 
			
		||||
		http.NotFound(w, req)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func writeErr(err error, w http.ResponseWriter) {
 | 
			
		||||
	if os.IsNotExist(err) {
 | 
			
		||||
		w.WriteHeader(http.StatusNotFound)
 | 
			
		||||
		w.Write([]byte("Not found\n"))
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	log.Output(2, fmt.Sprint("internal error: ", err))
 | 
			
		||||
	w.WriteHeader(http.StatusInternalServerError)
 | 
			
		||||
	w.Write([]byte("Internal error\n"))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func hashOf(filePath string) (sha1Hex string, err error) {
 | 
			
		||||
	sha1Path := filePath + ".sha1"
 | 
			
		||||
 | 
			
		||||
	fileStat, err := os.Stat(filePath)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	sha1Stat, err := os.Stat(sha1Path)
 | 
			
		||||
 | 
			
		||||
	if err == nil {
 | 
			
		||||
		if sha1Stat.ModTime().After(fileStat.ModTime()) {
 | 
			
		||||
			// cached value is up-to-date
 | 
			
		||||
			sha1HexBytes, readErr := ioutil.ReadFile(sha1Path)
 | 
			
		||||
 | 
			
		||||
			if readErr == nil {
 | 
			
		||||
				sha1Hex = string(sha1HexBytes)
 | 
			
		||||
				return
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	} else if !os.IsNotExist(err) {
 | 
			
		||||
		// failed to stat cached value
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// no cached value could be read
 | 
			
		||||
	log.Print("hashing ", filePath)
 | 
			
		||||
	start := time.Now()
 | 
			
		||||
 | 
			
		||||
	// hash the input
 | 
			
		||||
	f, err := os.Open(filePath)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	defer f.Close()
 | 
			
		||||
 | 
			
		||||
	h := sha1.New()
 | 
			
		||||
	_, err = io.Copy(h, f)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	sha1Hex = hex.EncodeToString(h.Sum(nil))
 | 
			
		||||
 | 
			
		||||
	log.Print("hashing ", filePath, " took ", time.Since(start).Truncate(time.Millisecond))
 | 
			
		||||
 | 
			
		||||
	if writeErr := ioutil.WriteFile(sha1Path, []byte(sha1Hex), 0644); writeErr != nil {
 | 
			
		||||
		log.Printf("WARNING: failed to cache SHA1: %v", writeErr)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user