}
proxy := &httputil.ReverseProxy{
Director: func(r *http.Request) {
- r.URL = &url.URL{
- Scheme: webdavBase.Scheme,
- Host: webdavBase.Host,
- Path: "/by_id/" + url.PathEscape(ctr.Log) + opts.Path,
- }
- // Our outgoing Host header must match
- // WebDAVDownload.ExternalURL, otherwise
- // keep-web does not accept an auth token.
- r.Host = conn.cluster.Services.WebDAVDownload.ExternalURL.Host
+ r.URL.Scheme = webdavBase.Scheme
+ r.URL.Host = webdavBase.Host
+ // Outgoing Host header specifies the
+ // collection ID.
+ r.Host = strings.Replace(ctr.Log, "+", "-", -1) + ".internal"
// We already checked permission on the
// container, so we can use a root token here
// instead of counting on the "access to log
// permission check, which can be racy when a
// request gets retried with a new container.
r.Header.Set("Authorization", "Bearer "+conn.cluster.SystemRootToken)
+ // We can't change r.URL.Path without
+ // confusing WebDAV (request body and response
+ // headers refer to the same paths) so we tell
+ // keep-web to map the log collection onto the
+ // containers/X/log/ namespace.
+ r.Header.Set("X-Webdav-Prefix", "/arvados/v1/containers/"+ctr.UUID+"/log")
},
}
if conn.cluster.TLS.Insecure {
"git.arvados.org/arvados.git/sdk/go/arvadosclient"
"git.arvados.org/arvados.git/sdk/go/arvadostest"
"git.arvados.org/arvados.git/sdk/go/ctxlog"
+ "git.arvados.org/arvados.git/sdk/go/httpserver"
"git.arvados.org/arvados.git/sdk/go/keepclient"
"golang.org/x/crypto/ssh"
check "gopkg.in/check.v1"
authKey := fmt.Sprintf("%x", h.Sum(nil))
rtr := router.New(s.localdb, router.Config{})
- s.srv = httptest.NewUnstartedServer(rtr)
+ s.srv = httptest.NewUnstartedServer(httpserver.AddRequestIDs(httpserver.LogRequests(rtr)))
s.srv.StartTLS()
// the test setup doesn't use lib/service so
// service.URLFromContext() returns nothing -- instead, this
}
}
-func (s *ContainerGatewaySuite) setupLogCollection(c *check.C, files map[string]string) {
+func (s *ContainerGatewaySuite) setupLogCollection(c *check.C) {
+ files := map[string]string{
+ "stderr.txt": "hello world\n",
+ "a/b/c/d.html": "<html></html>\n",
+ }
client := arvados.NewClientFromEnv()
ac, err := arvadosclient.New(client)
c.Assert(err, check.IsNil)
s.gw.LogCollection = cfs
}
+func (s *ContainerGatewaySuite) saveLogAndCloseGateway(c *check.C) {
+ rootctx := ctrlctx.NewWithToken(s.ctx, s.cluster, s.cluster.SystemRootToken)
+ txt, err := s.gw.LogCollection.MarshalManifest(".")
+ c.Assert(err, check.IsNil)
+ coll, err := s.localdb.CollectionCreate(rootctx, arvados.CreateOptions{
+ Attrs: map[string]interface{}{
+ "manifest_text": txt,
+ }})
+ c.Assert(err, check.IsNil)
+ _, err = s.localdb.ContainerUpdate(rootctx, arvados.UpdateOptions{
+ UUID: s.ctrUUID,
+ Attrs: map[string]interface{}{
+ "log": coll.PortableDataHash,
+ "gateway_address": "",
+ }})
+ c.Assert(err, check.IsNil)
+ // gateway_address="" above already ensures localdb
+ // can't circumvent the keep-web proxy test by getting
+ // content from the container gateway; this is just
+ // extra insurance.
+ s.gw.LogCollection = nil
+}
+
func (s *ContainerGatewaySuite) TestContainerLogViaTunnel(c *check.C) {
forceProxyForTest = true
defer func() { forceProxyForTest = false }()
s.gw = s.setupGatewayWithTunnel(c)
- s.setupLogCollection(c, map[string]string{
- "stderr.txt": "hello world\n",
- })
+ s.setupLogCollection(c)
for _, broken := range []bool{false, true} {
c.Logf("broken=%v", broken)
}
func (s *ContainerGatewaySuite) TestContainerLogViaGateway(c *check.C) {
- s.testContainerLog(c, true)
+ s.setupLogCollection(c)
+ s.testContainerLog(c)
}
func (s *ContainerGatewaySuite) TestContainerLogViaKeepWeb(c *check.C) {
- s.testContainerLog(c, false)
+ s.setupLogCollection(c)
+ s.saveLogAndCloseGateway(c)
+ s.testContainerLog(c)
}
-func (s *ContainerGatewaySuite) testContainerLog(c *check.C, viaGateway bool) {
- s.setupLogCollection(c, map[string]string{
- "stderr.txt": "hello world\n",
- "a/b/c/d.html": "<html></html>\n",
- })
- if !viaGateway {
- rootctx := ctrlctx.NewWithToken(s.ctx, s.cluster, s.cluster.SystemRootToken)
- txt, err := s.gw.LogCollection.MarshalManifest(".")
- c.Assert(err, check.IsNil)
- coll, err := s.localdb.CollectionCreate(rootctx, arvados.CreateOptions{
- Attrs: map[string]interface{}{
- "manifest_text": txt,
- }})
- c.Assert(err, check.IsNil)
- _, err = s.localdb.ContainerUpdate(rootctx, arvados.UpdateOptions{
- UUID: s.ctrUUID,
- Attrs: map[string]interface{}{
- "log": coll.PortableDataHash,
- "gateway_address": "",
- }})
- c.Assert(err, check.IsNil)
- // gateway_address="" above already ensures localdb
- // can't circumvent the keep-web proxy test by getting
- // content from the container gateway; this is just
- // extra insurance.
- s.gw.LogCollection = nil
- }
+func (s *ContainerGatewaySuite) testContainerLog(c *check.C) {
for _, trial := range []struct {
method string
path string
}
func (s *ContainerGatewaySuite) TestContainerLogViaCadaver(c *check.C) {
+ s.setupLogCollection(c)
+
out := s.runCadaver(c, arvadostest.ActiveToken, "/arvados/v1/containers/"+s.ctrUUID+"/log", "ls")
c.Check(out, check.Matches, `(?ms).*stderr\.txt\s+12\s.*`)
c.Check(out, check.Matches, `(?ms).*a\s+0\s.*`)
out = s.runCadaver(c, arvadostest.ActiveTokenV2, "/arvados/v1/containers/"+s.ctrUUID+"/log", "get stderr.txt")
c.Check(out, check.Matches, `(?ms).*Downloading .* to stderr\.txt: .* succeeded\..*`)
+
+ s.saveLogAndCloseGateway(c)
+
+ out = s.runCadaver(c, arvadostest.ActiveTokenV2, "/arvados/v1/containers/"+s.ctrUUID+"/log", "get stderr.txt")
+ c.Check(out, check.Matches, `(?ms).*Downloading .* to stderr\.txt: .* succeeded\..*`)
}
func (s *ContainerGatewaySuite) runCadaver(c *check.C, password, path, stdin string) string {
// just fail on TLS cert verification.
s.srv.Close()
rtr := router.New(s.localdb, router.Config{})
- s.srv = httptest.NewUnstartedServer(rtr)
+ s.srv = httptest.NewUnstartedServer(httpserver.AddRequestIDs(httpserver.LogRequests(rtr)))
s.srv.Start()
- s.setupLogCollection(c, map[string]string{
- "stderr.txt": "hello world\n",
- "a/b/c/d.html": "<html></html>\n",
- })
-
tempdir, err := ioutil.TempDir("", "localdb-test-")
c.Assert(err, check.IsNil)
defer os.RemoveAll(tempdir)
return
}
- pathParts := strings.Split(r.URL.Path[1:], "/")
+ webdavPrefix := ""
+ arvPath := r.URL.Path
+ if prefix := r.Header.Get("X-Webdav-Prefix"); prefix != "" {
+ // Enable a proxy (e.g., container log handler in
+ // controller) to satisfy a request for path
+ // "/foo/bar/baz.txt" using content from
+ // "//abc123-4.internal/bar/baz.txt", by adding a
+ // request header "X-Webdav-Prefix: /foo"
+ if !strings.HasPrefix(arvPath, prefix) {
+ http.Error(w, "X-Webdav-Prefix header is not a prefix of the requested path", http.StatusBadRequest)
+ return
+ }
+ arvPath = r.URL.Path[len(prefix):]
+ if arvPath == "" {
+ arvPath = "/"
+ }
+ w.Header().Set("Vary", "X-Webdav-Prefix, "+w.Header().Get("Vary"))
+ webdavPrefix = prefix
+ }
+ pathParts := strings.Split(arvPath[1:], "/")
var stripParts int
var collectionID string
if r.Method == http.MethodGet {
applyContentDispositionHdr(w, r, basename, attachment)
}
+ if webdavPrefix == "" {
+ webdavPrefix = "/" + strings.Join(pathParts[:stripParts], "/")
+ }
wh := webdav.Handler{
- Prefix: "/" + strings.Join(pathParts[:stripParts], "/"),
+ Prefix: webdavPrefix,
FileSystem: &webdavfs.FS{
FileSystem: sessionFS,
Prefix: fsprefix,