Merge branch '20433-crunch-log-zero-bytes-job'
[arvados.git] / lib / crunchrun / container_gateway.go
index 4c835088a8d6cd6f841b77245076167f7edef3d4..30f8957a2de7ccc33218302a1d72cbea09a3ecb7 100644 (file)
@@ -5,6 +5,7 @@
 package crunchrun
 
 import (
+       "context"
        "crypto/hmac"
        "crypto/rand"
        "crypto/rsa"
@@ -33,7 +34,6 @@ import (
        "github.com/google/shlex"
        "github.com/hashicorp/yamux"
        "golang.org/x/crypto/ssh"
-       "golang.org/x/net/context"
        "golang.org/x/net/webdav"
 )
 
@@ -81,13 +81,13 @@ type Gateway struct {
        // controller process at the other end of the tunnel.
        UpdateTunnelURL func(url string)
 
-       // Source for serving WebDAV requests at /arvados/v1/{uuid}/log/
+       // Source for serving WebDAV requests with
+       // X-Webdav-Source: /log
        LogCollection arvados.CollectionFileSystem
 
        sshConfig   ssh.ServerConfig
        requestAuth string
        respondAuth string
-       logPath     string
 }
 
 // Start starts an http server that allows authenticated clients to open an
@@ -162,8 +162,6 @@ func (gw *Gateway) Start() error {
        h.Write([]byte(gw.requestAuth))
        gw.respondAuth = fmt.Sprintf("%x", h.Sum(nil))
 
-       gw.logPath = "/arvados/v1/containers/" + gw.ContainerUUID + "/log"
-
        srv := &httpserver.Server{
                Server: http.Server{
                        Handler: gw,
@@ -276,6 +274,7 @@ var webdavMethod = map[string]bool{
 }
 
 func (gw *Gateway) ServeHTTP(w http.ResponseWriter, req *http.Request) {
+       w.Header().Set("Vary", "X-Arvados-Authorization, X-Arvados-Container-Gateway-Uuid, X-Webdav-Prefix, X-Webdav-Source")
        reqUUID := req.Header.Get("X-Arvados-Container-Gateway-Uuid")
        if reqUUID == "" {
                // older controller versions only send UUID as query param
@@ -294,7 +293,7 @@ func (gw *Gateway) ServeHTTP(w http.ResponseWriter, req *http.Request) {
        switch {
        case req.Method == "POST" && req.Header.Get("Upgrade") == "ssh":
                gw.handleSSH(w, req)
-       case req.URL.Path == gw.logPath || strings.HasPrefix(req.URL.Path, gw.logPath):
+       case req.Header.Get("X-Webdav-Source") == "/log":
                if !webdavMethod[req.Method] {
                        http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
                        return
@@ -306,12 +305,17 @@ func (gw *Gateway) ServeHTTP(w http.ResponseWriter, req *http.Request) {
 }
 
 func (gw *Gateway) handleLogsWebDAV(w http.ResponseWriter, r *http.Request) {
+       prefix := r.Header.Get("X-Webdav-Prefix")
+       if !strings.HasPrefix(r.URL.Path, prefix) {
+               http.Error(w, "X-Webdav-Prefix header is not a prefix of the requested path", http.StatusBadRequest)
+               return
+       }
        if gw.LogCollection == nil {
                http.Error(w, "Not found", http.StatusNotFound)
                return
        }
        wh := webdav.Handler{
-               Prefix: gw.logPath,
+               Prefix: prefix,
                FileSystem: &webdavfs.FS{
                        FileSystem:    gw.LogCollection,
                        Prefix:        "",
@@ -325,8 +329,10 @@ func (gw *Gateway) handleLogsWebDAV(w http.ResponseWriter, r *http.Request) {
 }
 
 func (gw *Gateway) webdavLogger(r *http.Request, err error) {
-       if err != nil {
-               ctxlog.FromContext(r.Context()).WithError(err).Error("error reported by webdav handler")
+       if err != nil && !os.IsNotExist(err) {
+               ctxlog.FromContext(r.Context()).WithError(err).Info("error reported by webdav handler")
+       } else {
+               ctxlog.FromContext(r.Context()).WithError(err).Debug("webdav request log")
        }
 }