devtools/ci/remote-cache: init

This is a first pass at a Bazel remote cache. It notably does not yet do
any authentication, upload limits or garbage collection.

We won't be deploying it to prod until these are done.

Change-Id: I70a89dbe8b3ec933b2ce82e234a969e8337ba1d9
diff --git a/devtools/ci/remote-cache/main.go b/devtools/ci/remote-cache/main.go
new file mode 100644
index 0000000..dfb23a6
--- /dev/null
+++ b/devtools/ci/remote-cache/main.go
@@ -0,0 +1,77 @@
+package main
+
+import (
+	"flag"
+	"net"
+	"net/http"
+
+	"code.hackerspace.pl/hscloud/go/mirko"
+
+	"github.com/golang/glog"
+	"github.com/minio/minio-go/v7"
+	"github.com/minio/minio-go/v7/pkg/credentials"
+)
+
+var (
+	flagListenPublic    = ":8080"
+	flagObjectEndpoint  = "object.ceph-waw3.hswaw.net"
+	flagObjectAccessKey = ""
+	flagObjectSecretKey = ""
+	flagObjectBucket    = ""
+	flagObjectPrefix    = "cache/"
+)
+
+func main() {
+	flag.StringVar(&flagListenPublic, "listen_public", flagListenPublic, "Address to listen on for Bazel HTTP caching protocol clients")
+	flag.StringVar(&flagObjectEndpoint, "object_endpoint", flagObjectEndpoint, "Object Storage endpoint name")
+	flag.StringVar(&flagObjectAccessKey, "object_access_key", flagObjectEndpoint, "Object Storage AccessKey")
+	flag.StringVar(&flagObjectSecretKey, "object_secret_key", flagObjectEndpoint, "Object Storage SecretKey")
+	flag.StringVar(&flagObjectBucket, "object_bucket", flagObjectBucket, "Object Storage bucket name")
+	flag.StringVar(&flagObjectPrefix, "object_prefix", flagObjectPrefix, "Object Storage prefix for paths")
+	flag.Parse()
+
+	if flagObjectBucket == "" {
+		glog.Exitf("object_bucket must be set")
+	}
+
+	m := mirko.New()
+	if err := m.Listen(); err != nil {
+		glog.Exitf("Listen(): %v", err)
+	}
+
+	minioClient, err := minio.New(flagObjectEndpoint, &minio.Options{
+		Creds:  credentials.NewStaticV4(flagObjectAccessKey, flagObjectSecretKey, ""),
+		Secure: true,
+	})
+
+	if err != nil {
+		glog.Exitf("Failed to initialize Object Storage client: %v", err)
+	}
+
+	s := newService(minioClient, flagObjectBucket, flagObjectPrefix)
+
+	httpListen, err := net.Listen("tcp", flagListenPublic)
+	if err != nil {
+		glog.Exitf("net.Listen: %v", err)
+	}
+	httpServer := &http.Server{
+		Addr:    flagListenPublic,
+		Handler: s.publicHandler,
+	}
+
+	errs := make(chan error, 0)
+	go func() {
+		glog.Infof("Public listening on %s", flagListenPublic)
+		errs <- httpServer.Serve(httpListen)
+	}()
+
+	if err := m.Serve(); err != nil {
+		glog.Exitf("Serve(): %v", err)
+	}
+
+	select {
+	case <-m.Done():
+	case err := <-errs:
+		glog.Exitf("Serve(): %v", err)
+	}
+}