16981: Extracts test federation building code to the arvadostest package.
[arvados.git] / sdk / go / arvadostest / federation_servers.go
1 // Copyright (C) The Arvados Authors. All rights reserved.
2 //
3 // SPDX-License-Identifier: AGPL-3.0
4
5 package arvadostest
6
7 import (
8         "bytes"
9         "context"
10         "net/url"
11
12         "git.arvados.org/arvados.git/lib/boot"
13         "git.arvados.org/arvados.git/lib/config"
14         "git.arvados.org/arvados.git/lib/controller/rpc"
15         "git.arvados.org/arvados.git/lib/service"
16         "git.arvados.org/arvados.git/sdk/go/arvados"
17         "git.arvados.org/arvados.git/sdk/go/arvadosclient"
18         "git.arvados.org/arvados.git/sdk/go/auth"
19         "git.arvados.org/arvados.git/sdk/go/ctxlog"
20         "git.arvados.org/arvados.git/sdk/go/keepclient"
21         "gopkg.in/check.v1"
22 )
23
24 // TestCluster stores a working test cluster data
25 type TestCluster struct {
26         Super         boot.Supervisor
27         Config        arvados.Config
28         ControllerURL *url.URL
29         ClusterID     string
30 }
31
32 type logger struct {
33         loggerfunc func(...interface{})
34 }
35
36 func (l logger) Log(args ...interface{}) {
37         l.loggerfunc(args)
38 }
39
40 // NewTestCluster loads the provided configuration, and sets up a test cluster
41 // ready for being started.
42 func NewTestCluster(srcPath string, clusterID, yamlConf, listenHost string, logWriter func(...interface{})) (*TestCluster, error) {
43         loader := config.NewLoader(bytes.NewBufferString(yamlConf), ctxlog.TestLogger(logger{logWriter}))
44         loader.Path = "-"
45         loader.SkipLegacy = true
46         loader.SkipAPICalls = true
47         cfg, err := loader.Load()
48         if err != nil {
49                 return nil, err
50         }
51         return &TestCluster{
52                 Super: boot.Supervisor{
53                         SourcePath:           srcPath,
54                         ClusterType:          "test",
55                         ListenHost:           listenHost,
56                         ControllerAddr:       ":0",
57                         OwnTemporaryDatabase: true,
58                         Stderr: &service.LogPrefixer{
59                                 Writer: ctxlog.LogWriter(logWriter),
60                                 Prefix: []byte("[" + clusterID + "] ")},
61                 },
62                 Config:    *cfg,
63                 ClusterID: clusterID,
64         }, nil
65 }
66
67 // Start the test cluster.
68 func (tc *TestCluster) Start() {
69         tc.Super.Start(context.Background(), &tc.Config, "-")
70 }
71
72 // WaitReady waits for all components to report healthy, and finishes setting
73 // up the TestCluster struct.
74 func (tc *TestCluster) WaitReady() bool {
75         au, ok := tc.Super.WaitReady()
76         if !ok {
77                 return ok
78         }
79         u := url.URL(*au)
80         tc.ControllerURL = &u
81         return ok
82 }
83
84 // ClientsWithToken returns Context, Arvados.Client and keepclient structs
85 // initialized to connect to the cluster with the supplied Arvados token.
86 func (tc *TestCluster) ClientsWithToken(token string) (context.Context, *arvados.Client, *keepclient.KeepClient) {
87         cl := tc.Config.Clusters[tc.ClusterID]
88         ctx := auth.NewContext(context.Background(), auth.NewCredentials(token))
89         ac, err := arvados.NewClientFromConfig(&cl)
90         if err != nil {
91                 panic(err)
92         }
93         ac.AuthToken = token
94         arv, err := arvadosclient.New(ac)
95         if err != nil {
96                 panic(err)
97         }
98         kc := keepclient.New(arv)
99         return ctx, ac, kc
100 }
101
102 // UserClients logs in as a user called "example", get the user's API token,
103 // initialize clients with the API token, set up the user and
104 // optionally activate the user.  Return client structs for
105 // communicating with the cluster on behalf of the 'example' user.
106 func (tc *TestCluster) UserClients(rootctx context.Context, c *check.C, conn *rpc.Conn, authEmail string, activate bool) (context.Context, *arvados.Client, *keepclient.KeepClient, arvados.User) {
107         login, err := conn.UserSessionCreate(rootctx, rpc.UserSessionCreateOptions{
108                 ReturnTo: ",https://example.com",
109                 AuthInfo: rpc.UserSessionAuthInfo{
110                         Email:     authEmail,
111                         FirstName: "Example",
112                         LastName:  "User",
113                         Username:  "example",
114                 },
115         })
116         c.Assert(err, check.IsNil)
117         redirURL, err := url.Parse(login.RedirectLocation)
118         c.Assert(err, check.IsNil)
119         userToken := redirURL.Query().Get("api_token")
120         c.Logf("user token: %q", userToken)
121         ctx, ac, kc := tc.ClientsWithToken(userToken)
122         user, err := conn.UserGetCurrent(ctx, arvados.GetOptions{})
123         c.Assert(err, check.IsNil)
124         _, err = conn.UserSetup(rootctx, arvados.UserSetupOptions{UUID: user.UUID})
125         c.Assert(err, check.IsNil)
126         if activate {
127                 _, err = conn.UserActivate(rootctx, arvados.UserActivateOptions{UUID: user.UUID})
128                 c.Assert(err, check.IsNil)
129                 user, err = conn.UserGetCurrent(ctx, arvados.GetOptions{})
130                 c.Assert(err, check.IsNil)
131                 c.Logf("user UUID: %q", user.UUID)
132                 if !user.IsActive {
133                         c.Fatalf("failed to activate user -- %#v", user)
134                 }
135         }
136         return ctx, ac, kc, user
137 }
138
139 // RootClients returns Context, arvados.Client and keepclient structs initialized
140 // to communicate with the cluster as the system root user.
141 func (tc *TestCluster) RootClients() (context.Context, *arvados.Client, *keepclient.KeepClient) {
142         return tc.ClientsWithToken(tc.Config.Clusters[tc.ClusterID].SystemRootToken)
143 }
144
145 // AnonymousClients returns Context, arvados.Client and keepclient structs initialized
146 // to communicate with the cluster as the anonymous user.
147 func (tc *TestCluster) AnonymousClients() (context.Context, *arvados.Client, *keepclient.KeepClient) {
148         return tc.ClientsWithToken(tc.Config.Clusters[tc.ClusterID].Users.AnonymousUserToken)
149 }
150
151 // Conn gets rpc connection struct initialized to communicate with the
152 // specified cluster.
153 func (tc *TestCluster) Conn() *rpc.Conn {
154         return rpc.NewConn(tc.ClusterID, tc.ControllerURL, true, rpc.PassthroughTokenProvider)
155 }