Merge branch '18273-java-timeout' refs #18273
[arvados.git] / lib / controller / integration_test.go
index 51748126d06a7f56329693ec7ffa0b14c960b5b9..02061547bf5b826b1618ac37298a876cd205b30b 100644 (file)
@@ -20,12 +20,14 @@ import (
        "path/filepath"
        "strconv"
        "strings"
+       "sync"
 
        "git.arvados.org/arvados.git/lib/boot"
        "git.arvados.org/arvados.git/lib/config"
        "git.arvados.org/arvados.git/sdk/go/arvados"
        "git.arvados.org/arvados.git/sdk/go/arvadostest"
        "git.arvados.org/arvados.git/sdk/go/ctxlog"
+       "git.arvados.org/arvados.git/sdk/go/httpserver"
        check "gopkg.in/check.v1"
 )
 
@@ -113,7 +115,8 @@ func (s *IntegrationSuite) SetUpSuite(c *check.C) {
         ClientSecret: ` + s.oidcprovider.ValidClientSecret + `
         EmailClaim: email
         EmailVerifiedClaim: email_verified
-        AcceptAccessTokenScope: "*"
+        AcceptAccessToken: true
+        AcceptAccessTokenScope: ""
 `
                } else {
                        yaml += `
@@ -147,6 +150,16 @@ func (s *IntegrationSuite) TearDownSuite(c *check.C) {
        }
 }
 
+func (s *IntegrationSuite) TestDefaultStorageClassesOnCollections(c *check.C) {
+       conn := s.testClusters["z1111"].Conn()
+       rootctx, _, _ := s.testClusters["z1111"].RootClients()
+       userctx, _, kc, _ := s.testClusters["z1111"].UserClients(rootctx, c, conn, s.oidcprovider.AuthEmail, true)
+       c.Assert(len(kc.DefaultStorageClasses) > 0, check.Equals, true)
+       coll, err := conn.CollectionCreate(userctx, arvados.CreateOptions{})
+       c.Assert(err, check.IsNil)
+       c.Assert(coll.StorageClassesDesired, check.DeepEquals, kc.DefaultStorageClasses)
+}
+
 func (s *IntegrationSuite) TestGetCollectionByPDH(c *check.C) {
        conn1 := s.testClusters["z1111"].Conn()
        rootctx1, _, _ := s.testClusters["z1111"].RootClients()
@@ -185,6 +198,49 @@ func (s *IntegrationSuite) TestGetCollectionByPDH(c *check.C) {
        c.Check(coll.PortableDataHash, check.Equals, pdh)
 }
 
+// Tests bug #18004
+func (s *IntegrationSuite) TestRemoteUserAndTokenCacheRace(c *check.C) {
+       conn1 := s.testClusters["z1111"].Conn()
+       rootctx1, _, _ := s.testClusters["z1111"].RootClients()
+       rootctx2, _, _ := s.testClusters["z2222"].RootClients()
+       conn2 := s.testClusters["z2222"].Conn()
+       userctx1, _, _, _ := s.testClusters["z1111"].UserClients(rootctx1, c, conn1, "user2@example.com", true)
+
+       var wg1, wg2 sync.WaitGroup
+       creqs := 100
+
+       // Make concurrent requests to z2222 with a local token to make sure more
+       // than one worker is listening.
+       wg1.Add(1)
+       for i := 0; i < creqs; i++ {
+               wg2.Add(1)
+               go func() {
+                       defer wg2.Done()
+                       wg1.Wait()
+                       _, err := conn2.UserGetCurrent(rootctx2, arvados.GetOptions{})
+                       c.Check(err, check.IsNil, check.Commentf("warm up phase failed"))
+               }()
+       }
+       wg1.Done()
+       wg2.Wait()
+
+       // Real test pass -- use a new remote token than the one used in the warm-up
+       // phase.
+       wg1.Add(1)
+       for i := 0; i < creqs; i++ {
+               wg2.Add(1)
+               go func() {
+                       defer wg2.Done()
+                       wg1.Wait()
+                       // Retrieve the remote collection from cluster z2222.
+                       _, err := conn2.UserGetCurrent(userctx1, arvados.GetOptions{})
+                       c.Check(err, check.IsNil, check.Commentf("testing phase failed"))
+               }()
+       }
+       wg1.Done()
+       wg2.Wait()
+}
+
 func (s *IntegrationSuite) TestS3WithFederatedToken(c *check.C) {
        if _, err := exec.LookPath("s3cmd"); err != nil {
                c.Skip("s3cmd not in PATH")
@@ -431,8 +487,76 @@ func (s *IntegrationSuite) TestCreateContainerRequestWithBadToken(c *check.C) {
        }
 }
 
+func (s *IntegrationSuite) TestRequestIDHeader(c *check.C) {
+       conn1 := s.testClusters["z1111"].Conn()
+       rootctx1, _, _ := s.testClusters["z1111"].RootClients()
+       userctx1, ac1, _, _ := s.testClusters["z1111"].UserClients(rootctx1, c, conn1, "user@example.com", true)
+
+       coll, err := conn1.CollectionCreate(userctx1, arvados.CreateOptions{})
+       c.Check(err, check.IsNil)
+       specimen, err := conn1.SpecimenCreate(userctx1, arvados.CreateOptions{})
+       c.Check(err, check.IsNil)
+
+       tests := []struct {
+               path            string
+               reqIdProvided   bool
+               notFoundRequest bool
+       }{
+               {"/arvados/v1/collections", false, false},
+               {"/arvados/v1/collections", true, false},
+               {"/arvados/v1/nonexistant", false, true},
+               {"/arvados/v1/nonexistant", true, true},
+               {"/arvados/v1/collections/" + coll.UUID, false, false},
+               {"/arvados/v1/collections/" + coll.UUID, true, false},
+               {"/arvados/v1/specimens/" + specimen.UUID, false, false},
+               {"/arvados/v1/specimens/" + specimen.UUID, true, false},
+               {"/arvados/v1/collections/z1111-4zz18-0123456789abcde", false, true},
+               {"/arvados/v1/collections/z1111-4zz18-0123456789abcde", true, true},
+               {"/arvados/v1/specimens/z1111-j58dm-0123456789abcde", false, true},
+               {"/arvados/v1/specimens/z1111-j58dm-0123456789abcde", true, true},
+       }
+
+       for _, tt := range tests {
+               c.Log(c.TestName() + " " + tt.path)
+               req, err := http.NewRequest("GET", "https://"+ac1.APIHost+tt.path, nil)
+               c.Assert(err, check.IsNil)
+               customReqId := "abcdeG"
+               if !tt.reqIdProvided {
+                       c.Assert(req.Header.Get("X-Request-Id"), check.Equals, "")
+               } else {
+                       req.Header.Set("X-Request-Id", customReqId)
+               }
+               resp, err := ac1.Do(req)
+               c.Assert(err, check.IsNil)
+               if tt.notFoundRequest {
+                       c.Check(resp.StatusCode, check.Equals, http.StatusNotFound)
+               } else {
+                       c.Check(resp.StatusCode, check.Equals, http.StatusOK)
+               }
+               if !tt.reqIdProvided {
+                       c.Check(resp.Header.Get("X-Request-Id"), check.Matches, "^req-[0-9a-zA-Z]{20}$")
+                       if tt.notFoundRequest {
+                               var jresp httpserver.ErrorResponse
+                               err := json.NewDecoder(resp.Body).Decode(&jresp)
+                               c.Check(err, check.IsNil)
+                               c.Assert(jresp.Errors, check.HasLen, 1)
+                               c.Check(jresp.Errors[0], check.Matches, "^.*(req-[0-9a-zA-Z]{20}).*$")
+                       }
+               } else {
+                       c.Check(resp.Header.Get("X-Request-Id"), check.Equals, customReqId)
+                       if tt.notFoundRequest {
+                               var jresp httpserver.ErrorResponse
+                               err := json.NewDecoder(resp.Body).Decode(&jresp)
+                               c.Check(err, check.IsNil)
+                               c.Assert(jresp.Errors, check.HasLen, 1)
+                               c.Check(jresp.Errors[0], check.Matches, "^.*("+customReqId+").*$")
+                       }
+               }
+       }
+}
+
 // We test the direct access to the database
-// normally an integration test would not have a database access, but  in this case we need
+// normally an integration test would not have a database access, but in this case we need
 // to test tokens that are secret, so there is no API response that will give them back
 func (s *IntegrationSuite) dbConn(c *check.C, clusterID string) (*sql.DB, *sql.Conn) {
        ctx := context.Background()
@@ -538,6 +662,111 @@ func (s *IntegrationSuite) TestIntermediateCluster(c *check.C) {
        }
 }
 
+// Test for bug #18076
+func (s *IntegrationSuite) TestStaleCachedUserRecord(c *check.C) {
+       rootctx1, _, _ := s.testClusters["z1111"].RootClients()
+       _, rootclnt3, _ := s.testClusters["z3333"].RootClients()
+       conn1 := s.testClusters["z1111"].Conn()
+       conn3 := s.testClusters["z3333"].Conn()
+
+       // Make sure LoginCluster is properly configured
+       for cls := range s.testClusters {
+               if cls == "z1111" || cls == "z3333" {
+                       c.Check(
+                               s.testClusters[cls].Config.Clusters[cls].Login.LoginCluster,
+                               check.Equals, "z1111",
+                               check.Commentf("incorrect LoginCluster config on cluster %q", cls))
+               }
+       }
+
+       for testCaseNr, testCase := range []struct {
+               name           string
+               withRepository bool
+       }{
+               {"User without local repository", false},
+               {"User with local repository", true},
+       } {
+               c.Log(c.TestName() + " " + testCase.name)
+               // Create some users, request them on the federated cluster so they're cached.
+               var users []arvados.User
+               for userNr := 0; userNr < 2; userNr++ {
+                       _, _, _, user := s.testClusters["z1111"].UserClients(
+                               rootctx1,
+                               c,
+                               conn1,
+                               fmt.Sprintf("user%d%d@example.com", testCaseNr, userNr),
+                               true)
+                       c.Assert(user.Username, check.Not(check.Equals), "")
+                       users = append(users, user)
+
+                       lst, err := conn3.UserList(rootctx1, arvados.ListOptions{Limit: -1})
+                       c.Assert(err, check.Equals, nil)
+                       userFound := false
+                       for _, fedUser := range lst.Items {
+                               if fedUser.UUID == user.UUID {
+                                       c.Assert(fedUser.Username, check.Equals, user.Username)
+                                       userFound = true
+                                       break
+                               }
+                       }
+                       c.Assert(userFound, check.Equals, true)
+
+                       if testCase.withRepository {
+                               var repo interface{}
+                               err = rootclnt3.RequestAndDecode(
+                                       &repo, "POST", "arvados/v1/repositories", nil,
+                                       map[string]interface{}{
+                                               "repository": map[string]string{
+                                                       "name":       fmt.Sprintf("%s/test", user.Username),
+                                                       "owner_uuid": user.UUID,
+                                               },
+                                       },
+                               )
+                               c.Assert(err, check.IsNil)
+                       }
+               }
+
+               // Swap the usernames
+               _, err := conn1.UserUpdate(rootctx1, arvados.UpdateOptions{
+                       UUID: users[0].UUID,
+                       Attrs: map[string]interface{}{
+                               "username": "",
+                       },
+               })
+               c.Assert(err, check.Equals, nil)
+               _, err = conn1.UserUpdate(rootctx1, arvados.UpdateOptions{
+                       UUID: users[1].UUID,
+                       Attrs: map[string]interface{}{
+                               "username": users[0].Username,
+                       },
+               })
+               c.Assert(err, check.Equals, nil)
+               _, err = conn1.UserUpdate(rootctx1, arvados.UpdateOptions{
+                       UUID: users[0].UUID,
+                       Attrs: map[string]interface{}{
+                               "username": users[1].Username,
+                       },
+               })
+               c.Assert(err, check.Equals, nil)
+
+               // Re-request the list on the federated cluster & check for updates
+               lst, err := conn3.UserList(rootctx1, arvados.ListOptions{Limit: -1})
+               c.Assert(err, check.Equals, nil)
+               var user0Found, user1Found bool
+               for _, user := range lst.Items {
+                       if user.UUID == users[0].UUID {
+                               user0Found = true
+                               c.Assert(user.Username, check.Equals, users[1].Username)
+                       } else if user.UUID == users[1].UUID {
+                               user1Found = true
+                               c.Assert(user.Username, check.Equals, users[0].Username)
+                       }
+               }
+               c.Assert(user0Found, check.Equals, true)
+               c.Assert(user1Found, check.Equals, true)
+       }
+}
+
 // Test for bug #16263
 func (s *IntegrationSuite) TestListUsers(c *check.C) {
        rootctx1, _, _ := s.testClusters["z1111"].RootClients()
@@ -562,6 +791,7 @@ func (s *IntegrationSuite) TestListUsers(c *check.C) {
        for _, user := range lst.Items {
                if user.Username == "" {
                        nullUsername = true
+                       break
                }
        }
        c.Assert(nullUsername, check.Equals, true)
@@ -601,11 +831,9 @@ func (s *IntegrationSuite) TestListUsers(c *check.C) {
        }
        c.Check(found, check.Equals, true)
 
-       // Deactivated user can see is_active==false via "get current
-       // user" API
+       // Deactivated user no longer has working token
        user1, err = conn3.UserGetCurrent(userctx1, arvados.GetOptions{})
-       c.Assert(err, check.IsNil)
-       c.Check(user1.IsActive, check.Equals, false)
+       c.Assert(err, check.ErrorMatches, `.*401 Unauthorized.*`)
 }
 
 func (s *IntegrationSuite) TestSetupUserWithVM(c *check.C) {