19175: Merge branch 'main' into 19175-doc-refactor-multi-host-installation
[arvados.git] / tools / sync-groups / federation_test.go
1 // Copyright (C) The Arvados Authors. All rights reserved.
2 //
3 // SPDX-License-Identifier: AGPL-3.0
4
5 package main
6
7 import (
8         "context"
9         "net"
10         "os"
11         "time"
12
13         "git.arvados.org/arvados.git/lib/boot"
14         "git.arvados.org/arvados.git/sdk/go/arvados"
15         "git.arvados.org/arvados.git/sdk/go/ctxlog"
16         check "gopkg.in/check.v1"
17 )
18
19 var _ = check.Suite(&FederationSuite{})
20
21 var origAPIHost, origAPIToken string
22
23 type FederationSuite struct {
24         super *boot.Supervisor
25 }
26
27 func (s *FederationSuite) SetUpSuite(c *check.C) {
28         origAPIHost = os.Getenv("ARVADOS_API_HOST")
29         origAPIToken = os.Getenv("ARVADOS_API_TOKEN")
30
31         hostport := map[string]string{}
32         for _, id := range []string{"z1111", "z2222"} {
33                 hostport[id] = func() string {
34                         // TODO: Instead of expecting random ports on
35                         // 127.0.0.11, 22 to be race-safe, try
36                         // different 127.x.y.z until finding one that
37                         // isn't in use.
38                         ln, err := net.Listen("tcp", ":0")
39                         c.Assert(err, check.IsNil)
40                         ln.Close()
41                         _, port, err := net.SplitHostPort(ln.Addr().String())
42                         c.Assert(err, check.IsNil)
43                         return "127.0.0." + id[3:] + ":" + port
44                 }()
45         }
46         yaml := "Clusters:\n"
47         for id := range hostport {
48                 yaml += `
49   ` + id + `:
50     Services:
51       Controller:
52         ExternalURL: https://` + hostport[id] + `
53     TLS:
54       Insecure: true
55     SystemLogs:
56       Format: text
57     Containers:
58       CloudVMs:
59         Enable: true
60         Driver: loopback
61     RemoteClusters:
62       z1111:
63         Host: ` + hostport["z1111"] + `
64         Scheme: https
65         Insecure: true
66         Proxy: true
67         ActivateUsers: true
68 `
69                 if id != "z2222" {
70                         yaml += `      z2222:
71         Host: ` + hostport["z2222"] + `
72         Scheme: https
73         Insecure: true
74         Proxy: true
75         ActivateUsers: true
76 `
77                 }
78                 if id == "z1111" {
79                         yaml += `
80     Login:
81       LoginCluster: z1111
82       PAM:
83         Enable: true
84 `
85                 } else {
86                         yaml += `
87     Login:
88       LoginCluster: z1111
89 `
90                 }
91         }
92         s.super = &boot.Supervisor{
93                 ClusterType:          "test",
94                 ConfigYAML:           yaml,
95                 Stderr:               ctxlog.LogWriter(c.Log),
96                 NoWorkbench1:         true,
97                 NoWorkbench2:         true,
98                 OwnTemporaryDatabase: true,
99         }
100
101         // Give up if startup takes longer than 3m
102         timeout := time.AfterFunc(3*time.Minute, s.super.Stop)
103         defer timeout.Stop()
104         s.super.Start(context.Background())
105         ok := s.super.WaitReady()
106         c.Assert(ok, check.Equals, true)
107
108         // Activate user, make it admin.
109         conn1 := s.super.Conn("z1111")
110         rootctx1, _, _ := s.super.RootClients("z1111")
111         userctx1, _, _, _ := s.super.UserClients("z1111", rootctx1, c, conn1, "admin@example.com", true)
112         user1, err := conn1.UserGetCurrent(userctx1, arvados.GetOptions{})
113         c.Assert(err, check.IsNil)
114         c.Assert(user1.IsAdmin, check.Equals, false)
115         user1, err = conn1.UserUpdate(rootctx1, arvados.UpdateOptions{
116                 UUID: user1.UUID,
117                 Attrs: map[string]interface{}{
118                         "is_admin": true,
119                 },
120         })
121         c.Assert(err, check.IsNil)
122         c.Assert(user1.IsAdmin, check.Equals, true)
123 }
124
125 func (s *FederationSuite) TearDownSuite(c *check.C) {
126         s.super.Stop()
127         _ = os.Setenv("ARVADOS_API_HOST", origAPIHost)
128         _ = os.Setenv("ARVADOS_API_TOKEN", origAPIToken)
129 }
130
131 func (s *FederationSuite) TestGroupSyncingOnFederatedCluster(c *check.C) {
132         // Get admin user's V2 token
133         conn1 := s.super.Conn("z1111")
134         rootctx1, _, _ := s.super.RootClients("z1111")
135         userctx1, _, _, _ := s.super.UserClients("z1111", rootctx1, c, conn1, "admin@example.com", true)
136         user1Auth, err := conn1.APIClientAuthorizationCurrent(userctx1, arvados.GetOptions{})
137         c.Check(err, check.IsNil)
138         userV2Token := user1Auth.TokenV2()
139
140         // Get federated admin clients on z2222 to set up environment
141         conn2 := s.super.Conn("z2222")
142         userctx2, userac2, _ := s.super.ClientsWithToken("z2222", userV2Token)
143         user2, err := conn2.UserGetCurrent(userctx2, arvados.GetOptions{})
144         c.Check(err, check.IsNil)
145         c.Check(user2.IsAdmin, check.Equals, true)
146
147         // Set up environment for sync-groups using admin user credentials on z2222
148         err = os.Setenv("ARVADOS_API_HOST", userac2.APIHost)
149         c.Assert(err, check.IsNil)
150         err = os.Setenv("ARVADOS_API_TOKEN", userac2.AuthToken)
151         c.Assert(err, check.IsNil)
152
153         // Check that no parent group is created
154         gl := arvados.GroupList{}
155         params := arvados.ResourceListParams{
156                 Filters: []arvados.Filter{{
157                         Attr:     "owner_uuid",
158                         Operator: "=",
159                         Operand:  s.super.Cluster("z2222").ClusterID + "-tpzed-000000000000000",
160                 }, {
161                         Attr:     "name",
162                         Operator: "=",
163                         Operand:  "Externally synchronized groups",
164                 }},
165         }
166         err = userac2.RequestAndDecode(&gl, "GET", "/arvados/v1/groups", nil, params)
167         c.Assert(err, check.IsNil)
168         c.Assert(gl.ItemsAvailable, check.Equals, 0)
169
170         // Set up config, confirm that the parent group was created
171         os.Args = []string{"cmd", "somefile.csv"}
172         config, err := GetConfig()
173         c.Assert(err, check.IsNil)
174         userac2.RequestAndDecode(&gl, "GET", "/arvados/v1/groups", nil, params)
175         c.Assert(gl.ItemsAvailable, check.Equals, 1)
176
177         // Run the tool with custom config
178         data := [][]string{
179                 {"TestGroup1", user2.Email},
180         }
181         tmpfile, err := MakeTempCSVFile(data)
182         c.Assert(err, check.IsNil)
183         defer os.Remove(tmpfile.Name()) // clean up
184         config.Path = tmpfile.Name()
185         err = doMain(&config)
186         c.Assert(err, check.IsNil)
187         // Check the group was created correctly, and has the user as a member
188         groupUUID, err := RemoteGroupExists(&config, "TestGroup1")
189         c.Assert(err, check.IsNil)
190         c.Assert(groupUUID, check.Not(check.Equals), "")
191         c.Assert(GroupMembershipExists(config.Client, user2.UUID, groupUUID, "can_write"), check.Equals, true)
192 }