18790: Fix .../containers/.../log routing.
authorTom Clegg <tom@curii.com>
Mon, 3 Apr 2023 15:23:03 +0000 (11:23 -0400)
committerTom Clegg <tom@curii.com>
Mon, 3 Apr 2023 16:29:21 +0000 (12:29 -0400)
Arvados-DCO-1.1-Signed-off-by: Tom Clegg <tom@curii.com>

lib/controller/handler.go
lib/controller/router/router.go
lib/controller/rpc/conn.go
sdk/go/arvados/api.go

index 4810ec3c257e18d626cbf8a12ff44c475a46ab5d..2feae7635395f235aa40d144f727550d55263926 100644 (file)
@@ -139,6 +139,7 @@ func (h *Handler) setup() {
 
        hs := http.NotFoundHandler()
        hs = prepend(hs, h.proxyRailsAPI)
+       hs = prepend(hs, h.routeContainerEndpoints(rtr))
        hs = prepend(hs, h.limitLogCreateRequests)
        hs = h.setupProxyRemoteCluster(hs)
        hs = prepend(hs, oidcAuthorizer.Middleware)
@@ -194,6 +195,24 @@ func (h *Handler) localClusterRequest(req *http.Request) (*http.Response, error)
        return h.proxy.Do(req, urlOut, client)
 }
 
+// Route /arvados/v1/containers/{uuid}/log*, .../ssh, and
+// .../gateway_tunnel to rtr, pass everything else to next.
+//
+// (http.ServeMux doesn't let us route these without also routing
+// everything under /containers/, which we don't want yet.)
+func (h *Handler) routeContainerEndpoints(rtr http.Handler) middlewareFunc {
+       return func(w http.ResponseWriter, req *http.Request, next http.Handler) {
+               trim := strings.TrimPrefix(req.URL.Path, "/arvados/v1/containers/")
+               if trim != req.URL.Path && (strings.Index(trim, "/log") == 27 ||
+                       strings.Index(trim, "/ssh") == 27 ||
+                       strings.Index(trim, "/gateway_tunnel") == 27) {
+                       rtr.ServeHTTP(w, req)
+               } else {
+                       next.ServeHTTP(w, req)
+               }
+       }
+}
+
 func (h *Handler) limitLogCreateRequests(w http.ResponseWriter, req *http.Request, next http.Handler) {
        if cap(h.limitLogCreate) > 0 && req.Method == http.MethodPost && strings.HasPrefix(req.URL.Path, "/arvados/v1/logs") {
                select {
index 703c45701bd8c19caeec5f984c044ff2cb9d5b39..2cbd9b88dc9ae0dcb09861ad8d78dbb4c5c34c5c 100644 (file)
@@ -223,6 +223,13 @@ func (rtr *router) addRoutes() {
                                return rtr.backend.ContainerSSH(ctx, *opts.(*arvados.ContainerSSHOptions))
                        },
                },
+               {
+                       arvados.EndpointContainerSSHCompat,
+                       func() interface{} { return &arvados.ContainerSSHOptions{} },
+                       func(ctx context.Context, opts interface{}) (interface{}, error) {
+                               return rtr.backend.ContainerSSH(ctx, *opts.(*arvados.ContainerSSHOptions))
+                       },
+               },
                {
                        // arvados-client built before commit
                        // bdc29d3129f6d75aa9ce0a24ffb849a272b06f08
@@ -241,6 +248,13 @@ func (rtr *router) addRoutes() {
                                return rtr.backend.ContainerGatewayTunnel(ctx, *opts.(*arvados.ContainerGatewayTunnelOptions))
                        },
                },
+               {
+                       arvados.EndpointContainerGatewayTunnelCompat,
+                       func() interface{} { return &arvados.ContainerGatewayTunnelOptions{} },
+                       func(ctx context.Context, opts interface{}) (interface{}, error) {
+                               return rtr.backend.ContainerGatewayTunnel(ctx, *opts.(*arvados.ContainerGatewayTunnelOptions))
+                       },
+               },
                {
                        arvados.EndpointContainerRequestCreate,
                        func() interface{} { return &arvados.CreateOptions{} },
index 5176df59f4814ff27ecd29d6272126768255015e..70a936a6f69e5dc592a43caf766b3e5d5a7a8ec9 100644 (file)
@@ -356,7 +356,7 @@ func (conn *Conn) ContainerLog(ctx context.Context, options arvados.ContainerLog
 // a running container. If the returned error is nil, the caller is
 // responsible for closing sshconn.Conn.
 func (conn *Conn) ContainerSSH(ctx context.Context, options arvados.ContainerSSHOptions) (sshconn arvados.ConnectionResponse, err error) {
-       u, err := conn.baseURL.Parse("/" + strings.Replace(arvados.EndpointContainerSSH.Path, "{uuid}", options.UUID, -1))
+       u, err := conn.baseURL.Parse("/" + strings.Replace(arvados.EndpointContainerSSHCompat.Path, "{uuid}", options.UUID, -1))
        if err != nil {
                err = fmt.Errorf("url.Parse: %w", err)
                return
@@ -372,7 +372,7 @@ func (conn *Conn) ContainerSSH(ctx context.Context, options arvados.ContainerSSH
 // the controller. The caller should connect the returned resp.Conn to
 // a client-side yamux session.
 func (conn *Conn) ContainerGatewayTunnel(ctx context.Context, options arvados.ContainerGatewayTunnelOptions) (tunnelconn arvados.ConnectionResponse, err error) {
-       u, err := conn.baseURL.Parse("/" + strings.Replace(arvados.EndpointContainerGatewayTunnel.Path, "{uuid}", options.UUID, -1))
+       u, err := conn.baseURL.Parse("/" + strings.Replace(arvados.EndpointContainerGatewayTunnelCompat.Path, "{uuid}", options.UUID, -1))
        if err != nil {
                err = fmt.Errorf("url.Parse: %w", err)
                return
index 4d67b5ad4dcc4e77583d19e655c83a5de9537a22..861b8e6ceb75d68946b5f7952e8ad97f88873071 100644 (file)
@@ -50,8 +50,10 @@ var (
        EndpointContainerLock                 = APIEndpoint{"POST", "arvados/v1/containers/{uuid}/lock", ""}
        EndpointContainerUnlock               = APIEndpoint{"POST", "arvados/v1/containers/{uuid}/unlock", ""}
        EndpointContainerLog                  = APIEndpoint{"GET", "arvados/v1/containers/{uuid}/log{path:|/.*}", ""}
-       EndpointContainerSSH                  = APIEndpoint{"POST", "arvados/v1/connect/{uuid}/ssh", ""}            // move to /containers after #17014 fixes routing
-       EndpointContainerGatewayTunnel        = APIEndpoint{"POST", "arvados/v1/connect/{uuid}/gateway_tunnel", ""} // move to /containers after #17014 fixes routing
+       EndpointContainerSSH                  = APIEndpoint{"POST", "arvados/v1/containers/{uuid}/ssh", ""}
+       EndpointContainerSSHCompat            = APIEndpoint{"POST", "arvados/v1/connect/{uuid}/ssh", ""} // for compatibility with arvados <2.7
+       EndpointContainerGatewayTunnel        = APIEndpoint{"POST", "arvados/v1/containers/{uuid}/gateway_tunnel", ""}
+       EndpointContainerGatewayTunnelCompat  = APIEndpoint{"POST", "arvados/v1/connect/{uuid}/gateway_tunnel", ""} // for compatibility with arvados <2.7
        EndpointContainerRequestCreate        = APIEndpoint{"POST", "arvados/v1/container_requests", "container_request"}
        EndpointContainerRequestUpdate        = APIEndpoint{"PATCH", "arvados/v1/container_requests/{uuid}", "container_request"}
        EndpointContainerRequestGet           = APIEndpoint{"GET", "arvados/v1/container_requests/{uuid}", ""}