1 // Copyright (C) The Arvados Authors. All rights reserved.
3 // SPDX-License-Identifier: AGPL-3.0
15 "git.curoverse.com/arvados.git/sdk/go/arvados"
16 "git.curoverse.com/arvados.git/sdk/go/arvadostest"
17 "git.curoverse.com/arvados.git/sdk/go/httpserver"
18 "github.com/Sirupsen/logrus"
19 check "gopkg.in/check.v1"
22 // Gocheck boilerplate
23 var _ = check.Suite(&FederationSuite{})
25 type FederationSuite struct {
27 localServer *httpserver.Server
28 remoteServer *httpserver.Server
32 func (s *FederationSuite) SetUpTest(c *check.C) {
34 s.log.Formatter = &logrus.JSONFormatter{}
35 s.log.Out = &logWriter{c.Log}
37 s.remoteServer = newServerFromIntegrationTestEnv(c)
38 c.Assert(s.remoteServer.Start(), check.IsNil)
40 nodeProfile := arvados.NodeProfile{
41 Controller: arvados.SystemServiceInstance{Listen: ":"},
42 RailsAPI: arvados.SystemServiceInstance{Listen: ":1"}, // local reqs will error "connection refused"
44 s.handler = &Handler{Cluster: &arvados.Cluster{
46 NodeProfiles: map[string]arvados.NodeProfile{
49 }, NodeProfile: &nodeProfile}
50 s.localServer = newServerFromIntegrationTestEnv(c)
51 s.localServer.Server.Handler = httpserver.AddRequestIDs(httpserver.LogRequests(s.log, s.handler))
52 s.handler.Cluster.RemoteClusters = map[string]arvados.RemoteCluster{
54 Host: s.remoteServer.Addr,
59 c.Assert(s.localServer.Start(), check.IsNil)
62 func (s *FederationSuite) TearDownTest(c *check.C) {
63 if s.remoteServer != nil {
64 s.remoteServer.Close()
66 if s.localServer != nil {
71 func (s *FederationSuite) TestLocalRequest(c *check.C) {
72 req := httptest.NewRequest("GET", "/arvados/v1/workflows/"+strings.Replace(arvadostest.WorkflowWithDefinitionYAMLUUID, "zzzzz-", "zhome-", 1), nil)
73 resp := httptest.NewRecorder()
74 s.handler.ServeHTTP(resp, req)
75 s.checkHandledLocally(c, resp)
78 func (s *FederationSuite) checkHandledLocally(c *check.C, resp *httptest.ResponseRecorder) {
79 // Our "home" controller can't handle local requests because
80 // it doesn't have its own stub/test Rails API, so we rely on
81 // "connection refused" to indicate the controller tried to
82 // proxy the request to its local Rails API.
83 c.Check(resp.Code, check.Equals, http.StatusInternalServerError)
84 s.checkJSONErrorMatches(c, resp, `.*connection refused`)
87 func (s *FederationSuite) TestNoAuth(c *check.C) {
88 req := httptest.NewRequest("GET", "/arvados/v1/workflows/"+arvadostest.WorkflowWithDefinitionYAMLUUID, nil)
89 resp := httptest.NewRecorder()
90 s.handler.ServeHTTP(resp, req)
91 c.Check(resp.Code, check.Equals, http.StatusUnauthorized)
92 s.checkJSONErrorMatches(c, resp, `Not logged in`)
95 func (s *FederationSuite) TestBadAuth(c *check.C) {
96 req := httptest.NewRequest("GET", "/arvados/v1/workflows/"+arvadostest.WorkflowWithDefinitionYAMLUUID, nil)
97 req.Header.Set("Authorization", "Bearer aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")
98 resp := httptest.NewRecorder()
99 s.handler.ServeHTTP(resp, req)
100 c.Check(resp.Code, check.Equals, http.StatusUnauthorized)
101 s.checkJSONErrorMatches(c, resp, `Not logged in`)
104 func (s *FederationSuite) TestNoAccess(c *check.C) {
105 req := httptest.NewRequest("GET", "/arvados/v1/workflows/"+arvadostest.WorkflowWithDefinitionYAMLUUID, nil)
106 req.Header.Set("Authorization", "Bearer "+arvadostest.SpectatorToken)
107 resp := httptest.NewRecorder()
108 s.handler.ServeHTTP(resp, req)
109 c.Check(resp.Code, check.Equals, http.StatusNotFound)
110 s.checkJSONErrorMatches(c, resp, `.*not found`)
113 func (s *FederationSuite) TestGetUnknownRemote(c *check.C) {
114 req := httptest.NewRequest("GET", "/arvados/v1/workflows/"+strings.Replace(arvadostest.WorkflowWithDefinitionYAMLUUID, "zzzzz-", "zz404-", 1), nil)
115 req.Header.Set("Authorization", "Bearer "+arvadostest.ActiveToken)
116 resp := httptest.NewRecorder()
117 s.handler.ServeHTTP(resp, req)
118 c.Check(resp.Code, check.Equals, http.StatusNotFound)
119 s.checkJSONErrorMatches(c, resp, `.*no proxy available for cluster zz404`)
122 func (s *FederationSuite) TestRemoteError(c *check.C) {
123 rc := s.handler.Cluster.RemoteClusters["zzzzz"]
125 s.handler.Cluster.RemoteClusters["zzzzz"] = rc
127 req := httptest.NewRequest("GET", "/arvados/v1/workflows/"+arvadostest.WorkflowWithDefinitionYAMLUUID, nil)
128 req.Header.Set("Authorization", "Bearer "+arvadostest.ActiveToken)
129 resp := httptest.NewRecorder()
130 s.handler.ServeHTTP(resp, req)
131 c.Check(resp.Code, check.Equals, http.StatusInternalServerError)
132 s.checkJSONErrorMatches(c, resp, `.*HTTP response to HTTPS client`)
135 func (s *FederationSuite) TestGetRemoteWorkflow(c *check.C) {
136 req := httptest.NewRequest("GET", "/arvados/v1/workflows/"+arvadostest.WorkflowWithDefinitionYAMLUUID, nil)
137 req.Header.Set("Authorization", "Bearer "+arvadostest.ActiveToken)
138 resp := httptest.NewRecorder()
139 s.handler.ServeHTTP(resp, req)
140 c.Check(resp.Code, check.Equals, http.StatusOK)
141 var wf arvados.Workflow
142 c.Check(json.Unmarshal(resp.Body.Bytes(), &wf), check.IsNil)
143 c.Check(wf.UUID, check.Equals, arvadostest.WorkflowWithDefinitionYAMLUUID)
144 c.Check(wf.OwnerUUID, check.Equals, arvadostest.ActiveUserUUID)
147 func (s *FederationSuite) TestUpdateRemoteWorkflow(c *check.C) {
148 updateDescription := func(descr string) *httptest.ResponseRecorder {
149 req := httptest.NewRequest("PATCH", "/arvados/v1/workflows/"+arvadostest.WorkflowWithDefinitionYAMLUUID, strings.NewReader(url.Values{
150 "workflow": {`{"description":"` + descr + `"}`},
152 req.Header.Set("Content-type", "application/x-www-form-urlencoded")
153 req.Header.Set("Authorization", "Bearer "+arvadostest.ActiveToken)
154 resp := httptest.NewRecorder()
155 s.handler.ServeHTTP(resp, req)
156 s.checkResponseOK(c, resp)
160 // Update description twice so running this test twice in a
161 // row still causes ModifiedAt to change
162 updateDescription("updated once by TestUpdateRemoteWorkflow")
163 resp := updateDescription("updated twice by TestUpdateRemoteWorkflow")
165 var wf arvados.Workflow
166 c.Check(json.Unmarshal(resp.Body.Bytes(), &wf), check.IsNil)
167 c.Check(wf.UUID, check.Equals, arvadostest.WorkflowWithDefinitionYAMLUUID)
168 c.Assert(wf.ModifiedAt, check.NotNil)
169 c.Logf("%s", *wf.ModifiedAt)
170 c.Check(time.Since(*wf.ModifiedAt) < time.Minute, check.Equals, true)
173 func (s *FederationSuite) checkResponseOK(c *check.C, resp *httptest.ResponseRecorder) {
174 c.Check(resp.Code, check.Equals, http.StatusOK)
175 if resp.Code != http.StatusOK {
176 c.Logf("... response body = %s\n", resp.Body.String())
180 func (s *FederationSuite) checkJSONErrorMatches(c *check.C, resp *httptest.ResponseRecorder, re string) {
181 var jresp httpserver.ErrorResponse
182 err := json.Unmarshal(resp.Body.Bytes(), &jresp)
183 c.Check(err, check.IsNil)
184 c.Assert(len(jresp.Errors), check.Equals, 1)
185 c.Check(jresp.Errors[0], check.Matches, re)