13493: Proxy requests to remote clusters.
[arvados.git] / lib / controller / federation_test.go
1 // Copyright (C) The Arvados Authors. All rights reserved.
2 //
3 // SPDX-License-Identifier: AGPL-3.0
4
5 package controller
6
7 import (
8         "encoding/json"
9         "net/http"
10         "net/http/httptest"
11         "strings"
12
13         "git.curoverse.com/arvados.git/sdk/go/arvados"
14         "git.curoverse.com/arvados.git/sdk/go/arvadostest"
15         "git.curoverse.com/arvados.git/sdk/go/httpserver"
16         "github.com/Sirupsen/logrus"
17         check "gopkg.in/check.v1"
18 )
19
20 // Gocheck boilerplate
21 var _ = check.Suite(&FederationSuite{})
22
23 type FederationSuite struct {
24         log          *logrus.Logger
25         localServer  *httpserver.Server
26         remoteServer *httpserver.Server
27         handler      *Handler
28 }
29
30 func (s *FederationSuite) SetUpTest(c *check.C) {
31         s.log = logrus.New()
32         s.log.Formatter = &logrus.JSONFormatter{}
33         s.log.Out = &logWriter{c.Log}
34
35         s.remoteServer = newServerFromIntegrationTestEnv(c)
36         c.Assert(s.remoteServer.Start(), check.IsNil)
37
38         nodeProfile := arvados.NodeProfile{
39                 Controller: arvados.SystemServiceInstance{Listen: ":"},
40                 RailsAPI:   arvados.SystemServiceInstance{Listen: ":1"}, // local reqs will error "connection refused"
41         }
42         s.handler = &Handler{Cluster: &arvados.Cluster{
43                 ClusterID: "zhome",
44                 NodeProfiles: map[string]arvados.NodeProfile{
45                         "*": nodeProfile,
46                 },
47         }, NodeProfile: &nodeProfile}
48         s.localServer = newServerFromIntegrationTestEnv(c)
49         s.localServer.Server.Handler = httpserver.AddRequestIDs(httpserver.LogRequests(s.log, s.handler))
50         s.handler.Cluster.RemoteClusters = map[string]arvados.RemoteCluster{
51                 "zzzzz": {
52                         Host:   s.remoteServer.Addr,
53                         Proxy:  true,
54                         Scheme: "http",
55                 },
56         }
57         c.Assert(s.localServer.Start(), check.IsNil)
58 }
59
60 func (s *FederationSuite) TearDownTest(c *check.C) {
61         if s.remoteServer != nil {
62                 s.remoteServer.Close()
63         }
64         if s.localServer != nil {
65                 s.localServer.Close()
66         }
67 }
68
69 func (s *FederationSuite) TestLocalRequestError(c *check.C) {
70         req := httptest.NewRequest("GET", "/arvados/v1/workflows/"+strings.Replace(arvadostest.WorkflowWithDefinitionYAMLUUID, "zzzzz-", "zhome-", 1), nil)
71         resp := httptest.NewRecorder()
72         s.handler.ServeHTTP(resp, req)
73         c.Check(resp.Code, check.Equals, http.StatusInternalServerError)
74         s.checkJSONErrorMatches(c, resp, `.*connection refused`)
75 }
76
77 func (s *FederationSuite) TestNoAuth(c *check.C) {
78         req := httptest.NewRequest("GET", "/arvados/v1/workflows/"+arvadostest.WorkflowWithDefinitionYAMLUUID, nil)
79         resp := httptest.NewRecorder()
80         s.handler.ServeHTTP(resp, req)
81         c.Check(resp.Code, check.Equals, http.StatusUnauthorized)
82         s.checkJSONErrorMatches(c, resp, `Not logged in`)
83 }
84
85 func (s *FederationSuite) TestBadAuth(c *check.C) {
86         req := httptest.NewRequest("GET", "/arvados/v1/workflows/"+arvadostest.WorkflowWithDefinitionYAMLUUID, nil)
87         req.Header.Set("Authorization", "Bearer aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")
88         resp := httptest.NewRecorder()
89         s.handler.ServeHTTP(resp, req)
90         c.Check(resp.Code, check.Equals, http.StatusUnauthorized)
91         s.checkJSONErrorMatches(c, resp, `Not logged in`)
92 }
93
94 func (s *FederationSuite) TestNoAccess(c *check.C) {
95         req := httptest.NewRequest("GET", "/arvados/v1/workflows/"+arvadostest.WorkflowWithDefinitionYAMLUUID, nil)
96         req.Header.Set("Authorization", "Bearer "+arvadostest.SpectatorToken)
97         resp := httptest.NewRecorder()
98         s.handler.ServeHTTP(resp, req)
99         c.Check(resp.Code, check.Equals, http.StatusNotFound)
100         s.checkJSONErrorMatches(c, resp, `.*not found`)
101 }
102
103 func (s *FederationSuite) TestGetUnknownRemote(c *check.C) {
104         req := httptest.NewRequest("GET", "/arvados/v1/workflows/"+strings.Replace(arvadostest.WorkflowWithDefinitionYAMLUUID, "zzzzz-", "zz404-", 1), nil)
105         req.Header.Set("Authorization", "Bearer "+arvadostest.ActiveToken)
106         resp := httptest.NewRecorder()
107         s.handler.ServeHTTP(resp, req)
108         c.Check(resp.Code, check.Equals, http.StatusNotFound)
109         s.checkJSONErrorMatches(c, resp, `.*no proxy available for cluster zz404`)
110 }
111
112 func (s *FederationSuite) TestRemoteDown(c *check.C) {
113 }
114
115 func (s *FederationSuite) TestGetRemoteWorkflow(c *check.C) {
116         req := httptest.NewRequest("GET", "/arvados/v1/workflows/"+arvadostest.WorkflowWithDefinitionYAMLUUID, nil)
117         req.Header.Set("Authorization", "Bearer "+arvadostest.ActiveToken)
118         resp := httptest.NewRecorder()
119         s.handler.ServeHTTP(resp, req)
120         c.Check(resp.Code, check.Equals, http.StatusOK)
121         var wf arvados.Workflow
122         c.Check(json.Unmarshal(resp.Body.Bytes(), &wf), check.IsNil)
123         c.Check(wf.UUID, check.Equals, arvadostest.WorkflowWithDefinitionYAMLUUID)
124         c.Check(wf.OwnerUUID, check.Equals, arvadostest.ActiveUserUUID)
125 }
126
127 func (s *FederationSuite) checkJSONErrorMatches(c *check.C, resp *httptest.ResponseRecorder, re string) {
128         var jresp httpserver.ErrorResponse
129         err := json.Unmarshal(resp.Body.Bytes(), &jresp)
130         c.Check(err, check.IsNil)
131         c.Assert(len(jresp.Errors), check.Equals, 1)
132         c.Check(jresp.Errors[0], check.Matches, re)
133 }