blob: 78a7795ecdd731b7946b8af11a425c19694847a1 [file] [log] [blame]
Jiri Simsad7616c92015-03-24 23:44:30 -07001// Copyright 2015 The Vanadium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
Cosmos Nicolaou93dd88b2015-02-19 15:10:53 -08005// Test the device manager and related services and tools.
6//
7// By default, this script tests the device manager in a fashion amenable
8// to automatic testing: the --single_user is passed to the device
9// manager so that all device manager components run as the same user and
10// no user input (such as an agent pass phrase) is needed.
11//
Robert Kroeger02714b72015-04-14 18:02:38 -070012// This script can exercise the device manager in two different modes. It
13// can be executed like so:
Cosmos Nicolaou93dd88b2015-02-19 15:10:53 -080014//
Robert Kroeger02714b72015-04-14 18:02:38 -070015// v23 go test -v . --v23.tests
Cosmos Nicolaou93dd88b2015-02-19 15:10:53 -080016//
Robert Kroeger02714b72015-04-14 18:02:38 -070017// This will exercise the device manager's single user mode where all
18// processes run as the same invoking user.
Cosmos Nicolaou93dd88b2015-02-19 15:10:53 -080019//
Robert Kroeger02714b72015-04-14 18:02:38 -070020// Alternatively, the device manager can be executed in multiple account
21// mode by providing the --deviceuser <deviceuser> and --appuser
22// <appuser> flags. In this case, the device manager will run as user
23// <devicemgr> and the test will run applications as user <appuser>. If
24// executed in this fashion, root permissions will be required to install
25// and it may require configuring an agent passphrase. For example:
Cosmos Nicolaou93dd88b2015-02-19 15:10:53 -080026//
Robert Kroeger02714b72015-04-14 18:02:38 -070027// v23 go test -v . --v23.tests --deviceuser devicemanager --appuser vana
28//
29// NB: the accounts provided as arguments to this test must already exist.
30// Also, the --v23.tests.shell-on-fail flag is useful to enable debugging
Robert Kroegerac7f57b2015-07-28 17:56:13 -070031// output. Note that this flag does not work for some shells. Set
32// $SHELL in that case.
Robert Kroeger02714b72015-04-14 18:02:38 -070033
Todd Wangcd4b3cc2015-04-06 16:42:02 -070034package device_test
Cosmos Nicolaou93dd88b2015-02-19 15:10:53 -080035
36//go:generate v23 test generate .
37
38import (
Cosmos Nicolaou93dd88b2015-02-19 15:10:53 -080039 "errors"
40 "flag"
41 "fmt"
Bogdan Caprita3c319212015-08-19 18:12:53 -070042 "io"
Cosmos Nicolaou93dd88b2015-02-19 15:10:53 -080043 "io/ioutil"
44 "math/rand"
45 "os"
Robert Kroeger02714b72015-04-14 18:02:38 -070046 "os/user"
Cosmos Nicolaou93dd88b2015-02-19 15:10:53 -080047 "path/filepath"
Robert Kroeger02714b72015-04-14 18:02:38 -070048 "regexp"
Cosmos Nicolaou93dd88b2015-02-19 15:10:53 -080049 "strings"
Bogdan Caprita3c319212015-08-19 18:12:53 -070050 "sync"
Cosmos Nicolaou93dd88b2015-02-19 15:10:53 -080051 "time"
52
Todd Wang8123b5e2015-05-14 18:44:43 -070053 "v.io/x/ref"
Suharsh Sivakumardcc11d72015-05-11 12:19:20 -070054 _ "v.io/x/ref/runtime/factories/generic"
Cosmos Nicolaou1381f8a2015-03-13 09:40:34 -070055 "v.io/x/ref/test/v23tests"
Cosmos Nicolaou93dd88b2015-02-19 15:10:53 -080056)
57
58var (
Robert Kroeger02714b72015-04-14 18:02:38 -070059 appUserFlag string
60 deviceUserFlag string
61 hostname string
62 errTimeout = errors.New("timeout")
Cosmos Nicolaou93dd88b2015-02-19 15:10:53 -080063)
64
65func init() {
Robert Kroeger02714b72015-04-14 18:02:38 -070066 flag.StringVar(&appUserFlag, "appuser", "", "launch apps as the specified user")
67 flag.StringVar(&deviceUserFlag, "deviceuser", "", "run the device manager as the specified user")
Cosmos Nicolaou93dd88b2015-02-19 15:10:53 -080068 name, err := os.Hostname()
69 if err != nil {
70 panic(fmt.Sprintf("Hostname() failed: %v", err))
71 }
72 hostname = name
73}
74
Asim Shankar6cc759d2015-03-14 03:31:44 -070075func V23TestDeviceManager(i *v23tests.T) {
Robert Kroeger769353e2015-05-05 19:10:07 -070076 u, err := user.Current()
77 if err != nil {
78 i.Fatalf("couldn't get the current user: %v", err)
79 }
80 testCore(i, u.Username, "", false)
81}
82
83func V23TestDeviceManagerMultiUser(i *v23tests.T) {
84 u, err := user.Current()
85 if err != nil {
86 i.Fatalf("couldn't get the current user: %v", err)
87 }
88
89 if u.Username == "veyron" && runTestOnThisPlatform {
90 // We are running on the builder so run the multiuser
91 // test with default user names. These will be created as
92 // required.
93 makeTestAccounts(i)
94 testCore(i, "vana", "devicemanager", true)
95 return
96 }
97
98 if len(deviceUserFlag) > 0 && len(appUserFlag) > 0 {
99 testCore(i, appUserFlag, deviceUserFlag, true)
100 } else {
101 i.Logf("Test skipped because running in multiuser mode requires --appuser and --deviceuser flags")
102 }
103}
104
105func testCore(i *v23tests.T, appUser, deviceUser string, withSuid bool) {
Cosmos Nicolaou93dd88b2015-02-19 15:10:53 -0800106 defer fmt.Fprintf(os.Stderr, "--------------- SHUTDOWN ---------------\n")
Robert Kroeger02714b72015-04-14 18:02:38 -0700107 tempDir := ""
Robert Kroeger769353e2015-05-05 19:10:07 -0700108
109 if withSuid {
Robert Kroeger02714b72015-04-14 18:02:38 -0700110 // When running --with_suid, TMPDIR must grant the
111 // invoking user rwx permissions and world x permissions for
112 // all parent directories back to /. Otherwise, the
113 // with_suid user will not be able to use absolute paths.
114 // On Darwin, TMPDIR defaults to a directory hieararchy
115 // in /var that is 0700. This is unworkable so force
116 // TMPDIR to /tmp in this case.
117 tempDir = "/tmp"
Cosmos Nicolaou93dd88b2015-02-19 15:10:53 -0800118 }
Cosmos Nicolaou93dd88b2015-02-19 15:10:53 -0800119
Asim Shankar6cc759d2015-03-14 03:31:44 -0700120 var (
Robert Kroeger02714b72015-04-14 18:02:38 -0700121 workDir = i.NewTempDir(tempDir)
Asim Shankar6cc759d2015-03-14 03:31:44 -0700122 binStagingDir = mkSubdir(i, workDir, "bin")
123 dmInstallDir = filepath.Join(workDir, "dm")
124
Arup Mukherjeed8048282015-06-26 23:18:44 -0700125 // Most vanadium command-line utilities will be run by a
126 // principal that has "root/u/alice" as its blessing.
Asim Shankar6cc759d2015-03-14 03:31:44 -0700127 // (Where "root" comes from i.Principal().BlessingStore().Default()).
128 // Create those credentials and options to use to setup the
129 // binaries with them.
Arup Mukherjeed8048282015-06-26 23:18:44 -0700130 aliceCreds, _ = i.Shell().NewChildCredentials("u/alice")
Todd Wang95873902015-05-22 14:21:30 -0700131 aliceOpts = i.Shell().DefaultStartOpts().ExternalProgram().WithCustomCredentials(aliceCreds)
Asim Shankar6cc759d2015-03-14 03:31:44 -0700132
133 // Build all the command-line tools and set them up to run as alice.
134 // applicationd/binaryd servers will be run by alice too.
Arup Mukherjeed8048282015-06-26 23:18:44 -0700135 // TODO: applicationd/binaryd should run as a separate "service" role, as
136 // alice is just a user.
Asim Shankar6cc759d2015-03-14 03:31:44 -0700137 namespaceBin = i.BuildV23Pkg("v.io/x/ref/cmd/namespace").WithStartOpts(aliceOpts)
Todd Wangcd4b3cc2015-04-06 16:42:02 -0700138 deviceBin = i.BuildV23Pkg("v.io/x/ref/services/device/device").WithStartOpts(aliceOpts)
Todd Wang1f7a6c62015-04-03 17:05:09 -0700139 binarydBin = i.BuildV23Pkg("v.io/x/ref/services/binary/binaryd").WithStartOpts(aliceOpts)
Todd Wang159f6ee2015-04-02 18:57:46 -0700140 applicationdBin = i.BuildV23Pkg("v.io/x/ref/services/application/applicationd").WithStartOpts(aliceOpts)
Asim Shankar6cc759d2015-03-14 03:31:44 -0700141
142 // The devicex script is not provided with any credentials, it
143 // will generate its own. This means that on "devicex start"
144 // the device will have no useful credentials and until "device
145 // claim" is invoked (as alice), it will just sit around
146 // waiting to be claimed.
147 //
148 // Other binaries, like applicationd and binaryd will be run by alice.
Bogdan Capritacb055fa2015-04-08 10:21:23 -0700149 deviceScript = i.BinaryFromPath("./devicex").WithEnv("V23_DEVICE_DIR=" + dmInstallDir)
Asim Shankar6cc759d2015-03-14 03:31:44 -0700150
151 mtName = "devices/" + hostname // Name under which the device manager will publish itself.
152 )
153
Arup Mukherjeed8048282015-06-26 23:18:44 -0700154 // We also need some tools running with different sets of credentials...
155
156 // Administration tasks will be performed with a blessing that represents a corporate
157 // adminstrator (which is usually a role account)
158 adminCreds, err := i.Shell().NewChildCredentials("r/admin")
159 if err != nil {
160 i.Fatalf("generating admin creds: %v", err)
161 }
162 adminOpts := i.Shell().DefaultStartOpts().ExternalProgram().WithCustomCredentials(adminCreds)
163 adminDeviceBin := deviceBin.WithStartOpts(adminOpts)
164 debugBin := i.BuildV23Pkg("v.io/x/ref/services/debug/debug").WithStartOpts(adminOpts)
165
166 // A special set of credentials will be used to give two blessings to the device manager
167 // when claiming it -- one blessing will be from the corporate administrator role who owns
168 // the machine, and the other will be a manufacturer blessing. (This is a hack until
169 // there's a way to separately supply a manufacturer blessing. Eventually, the claim
170 // would really be done by the administator, and the adminstrator's blessing would get
171 // added to the manufacturer's blessing, which would already be present.)
172 claimCreds, err := i.Shell().AddToChildCredentials(adminCreds, "m/orange/zphone5/ime-i007")
173 if err != nil {
174 i.Fatalf("adding the mfr blessing to admin creds: %v", err)
175 }
176 claimOpts := i.Shell().DefaultStartOpts().ExternalProgram().WithCustomCredentials(claimCreds)
177 claimDeviceBin := deviceBin.WithStartOpts(claimOpts)
178
179 // Another set of credentials be used to represent the application publisher, who
180 // signs and pushes binaries
181 pubCreds, err := i.Shell().NewChildCredentials("a/rovio")
182 if err != nil {
183 i.Fatalf("generating publisher creds: %v", err)
184 }
185 pubOpts := i.Shell().DefaultStartOpts().ExternalProgram().WithCustomCredentials(pubCreds)
186 pubDeviceBin := deviceBin.WithStartOpts(pubOpts)
187 applicationBin := i.BuildV23Pkg("v.io/x/ref/services/application/application").WithStartOpts(pubOpts)
188 binaryBin := i.BuildV23Pkg("v.io/x/ref/services/binary/binary").WithStartOpts(pubOpts)
189
Robert Kroeger02714b72015-04-14 18:02:38 -0700190 if withSuid {
191 // In multiuser mode, deviceUserFlag needs execute access to
192 // tempDir.
193 if err := os.Chmod(workDir, 0711); err != nil {
194 i.Fatalf("os.Chmod() failed: %v", err)
195 }
196 }
197
Asim Shankarf32d24d2015-04-01 16:34:26 -0700198 v23tests.RunRootMT(i, "--v23.tcp.address=127.0.0.1:0")
Asim Shankar6cc759d2015-03-14 03:31:44 -0700199 buildAndCopyBinaries(
200 i,
201 binStagingDir,
Todd Wangcd4b3cc2015-04-06 16:42:02 -0700202 "v.io/x/ref/services/device/deviced",
Todd Wangb3511492015-04-07 23:32:34 -0700203 "v.io/x/ref/services/agent/agentd",
Todd Wang392a9cc2015-04-06 14:35:11 -0700204 "v.io/x/ref/services/device/suidhelper",
205 "v.io/x/ref/services/device/inithelper")
Cosmos Nicolaou93dd88b2015-02-19 15:10:53 -0800206
Arup Mukherjeed8048282015-06-26 23:18:44 -0700207 appDName := "applications"
Cosmos Nicolaou93dd88b2015-02-19 15:10:53 -0800208 devicedAppName := filepath.Join(appDName, "deviced", "test")
209
Robert Kroeger02714b72015-04-14 18:02:38 -0700210 deviceScriptArguments := []string{
Cosmos Nicolaou93dd88b2015-02-19 15:10:53 -0800211 "install",
212 binStagingDir,
Robert Kroeger02714b72015-04-14 18:02:38 -0700213 }
214
215 if withSuid {
Robert Kroeger769353e2015-05-05 19:10:07 -0700216 deviceScriptArguments = append(deviceScriptArguments, "--devuser="+deviceUser)
Robert Kroeger02714b72015-04-14 18:02:38 -0700217 } else {
Bogdan Caprita3c319212015-08-19 18:12:53 -0700218 deviceScriptArguments = append(deviceScriptArguments, "--single_user")
Robert Kroeger02714b72015-04-14 18:02:38 -0700219 }
220
221 deviceScriptArguments = append(deviceScriptArguments, []string{
222 "--origin=" + devicedAppName,
Cosmos Nicolaou93dd88b2015-02-19 15:10:53 -0800223 "--",
Asim Shankarf32d24d2015-04-01 16:34:26 -0700224 "--v23.tcp.address=127.0.0.1:0",
Robert Kroeger02714b72015-04-14 18:02:38 -0700225 "--neighborhood-name=" + fmt.Sprintf("%s-%d-%d", hostname, os.Getpid(), rand.Int()),
226 }...)
227
228 deviceScript.Start(deviceScriptArguments...).WaitOrDie(os.Stdout, os.Stderr)
Cosmos Nicolaou93dd88b2015-02-19 15:10:53 -0800229 deviceScript.Start("start").WaitOrDie(os.Stdout, os.Stderr)
Bogdan Caprita3c319212015-08-19 18:12:53 -0700230 dmLog := filepath.Join(dmInstallDir, "dmroot/device-manager/logs/deviced.INFO")
231 stopDevMgr := func() {
232 deviceScript.Run("stop")
233 if dmLogF, err := os.Open(dmLog); err != nil {
234 i.Errorf("Failed to read dm log: %v", err)
235 } else {
236 fmt.Fprintf(os.Stderr, "--------------- START DM LOG ---------------\n")
237 defer dmLogF.Close()
238 if _, err := io.Copy(os.Stderr, dmLogF); err != nil {
239 i.Errorf("Error dumping dm log: %v", err)
240 }
241 fmt.Fprintf(os.Stderr, "--------------- END DM LOG ---------------\n")
242 }
243 }
244 var stopDevMgrOnce sync.Once
245 defer stopDevMgrOnce.Do(stopDevMgr)
Bogdan Capritae8073682015-04-25 15:37:53 -0700246 // Grab the endpoint for the claimable service from the device manager's
247 // log.
Bogdan Capritae8073682015-04-25 15:37:53 -0700248 var claimableEP string
249 expiry := time.Now().Add(30 * time.Second)
250 for {
251 if time.Now().After(expiry) {
252 i.Fatalf("Timed out looking for claimable endpoint in %v", dmLog)
253 }
254 startLog, err := ioutil.ReadFile(dmLog)
255 if err != nil {
256 i.Logf("Couldn't read log %v: %v", dmLog, err)
257 time.Sleep(time.Second)
258 continue
259 }
260 re := regexp.MustCompile(`Unclaimed device manager \((.*)\)`)
261 matches := re.FindSubmatch(startLog)
262 if len(matches) == 0 {
263 i.Logf("Couldn't find match in %v [%v]", dmLog, startLog)
264 time.Sleep(time.Second)
265 continue
266 }
267 if len(matches) != 2 {
268 i.Fatalf("Wrong match in %v (%d) %v", dmLog, len(matches), string(matches[0]))
269 }
270 claimableEP = string(matches[1])
271 break
272 }
Arup Mukherjeed8048282015-06-26 23:18:44 -0700273 // Claim the device as "root/u/alice/myworkstation".
274 claimDeviceBin.Start("claim", claimableEP, "myworkstation")
Cosmos Nicolaou93dd88b2015-02-19 15:10:53 -0800275
Cosmos Nicolaouabbcb202015-02-26 11:24:52 -0800276 resolve := func(name string) string {
277 resolver := func() (interface{}, error) {
Asim Shankar6cc759d2015-03-14 03:31:44 -0700278 // Use Start, rather than Run, since it's ok for 'namespace resolve'
Cosmos Nicolaouabbcb202015-02-26 11:24:52 -0800279 // to fail with 'name doesn't exist'
280 inv := namespaceBin.Start("resolve", name)
281 // Cleanup after ourselves to avoid leaving a ton of invocations
282 // lying around which obscure logging output.
283 defer inv.Wait(nil, os.Stderr)
284 if r := strings.TrimRight(inv.Output(), "\n"); len(r) > 0 {
285 return r, nil
286 }
287 return nil, nil
Cosmos Nicolaou93dd88b2015-02-19 15:10:53 -0800288 }
Cosmos Nicolaouabbcb202015-02-26 11:24:52 -0800289 return i.WaitFor(resolver, 100*time.Millisecond, time.Minute).(string)
Cosmos Nicolaou93dd88b2015-02-19 15:10:53 -0800290 }
Bogdan Capritae8073682015-04-25 15:37:53 -0700291
292 // Wait for the device manager to publish its mount table entry.
Cosmos Nicolaouabbcb202015-02-26 11:24:52 -0800293 mtEP := resolve(mtName)
Arup Mukherjeed8048282015-06-26 23:18:44 -0700294 adminDeviceBin.Run("acl", "set", mtName+"/devmgr/device", "root/u/alice", "Read,Resolve,Write")
Cosmos Nicolaou93dd88b2015-02-19 15:10:53 -0800295
Cosmos Nicolaou93dd88b2015-02-19 15:10:53 -0800296 if withSuid {
Arup Mukherjeed8048282015-06-26 23:18:44 -0700297 adminDeviceBin.Start("associate", "add", mtName+"/devmgr/device", appUser, "root/u/alice")
Robert Kroeger02714b72015-04-14 18:02:38 -0700298
Arup Mukherjeed8048282015-06-26 23:18:44 -0700299 aai := adminDeviceBin.Start("associate", "list", mtName+"/devmgr/device")
300 if got, expected := strings.Trim(aai.Output(), "\n "), "root/u/alice "+appUser; got != expected {
Robert Kroeger02714b72015-04-14 18:02:38 -0700301 i.Fatalf("association test, got %v, expected %v", got, expected)
302 }
Cosmos Nicolaou93dd88b2015-02-19 15:10:53 -0800303 }
304
305 // Verify the device's default blessing is as expected.
Arup Mukherjeed8048282015-06-26 23:18:44 -0700306 mfrBlessing := "root/m/orange/zphone5/ime-i007/myworkstation"
307 ownerBlessing := "root/r/admin/myworkstation"
Cosmos Nicolaou93dd88b2015-02-19 15:10:53 -0800308 inv := debugBin.Start("stats", "read", mtName+"/devmgr/__debug/stats/security/principal/*/blessingstore")
Arup Mukherjeed8048282015-06-26 23:18:44 -0700309 inv.ExpectSetEventuallyRE(".*Default Blessings[ ]+" + mfrBlessing + "," + ownerBlessing)
Cosmos Nicolaou93dd88b2015-02-19 15:10:53 -0800310
311 // Get the device's profile, which should be set to non-empty string
Arup Mukherjeed8048282015-06-26 23:18:44 -0700312 inv = adminDeviceBin.Start("describe", mtName+"/devmgr/device")
Cosmos Nicolaou93dd88b2015-02-19 15:10:53 -0800313
314 parts := inv.ExpectRE(`{Profiles:map\[(.*):{}\]}`, 1)
315 expectOneMatch := func(parts [][]string) string {
316 if len(parts) != 1 || len(parts[0]) != 2 {
317 loc := v23tests.Caller(1)
318 i.Fatalf("%s: failed to match profile: %#v", loc, parts)
319 }
320 return parts[0][1]
321 }
322 deviceProfile := expectOneMatch(parts)
323 if len(deviceProfile) == 0 {
324 i.Fatalf("failed to get profile")
325 }
326
Asim Shankar6cc759d2015-03-14 03:31:44 -0700327 // Start a binaryd server that will serve the binary for the test
328 // application to be installed on the device.
Arup Mukherjeed8048282015-06-26 23:18:44 -0700329 binarydName := "binaries"
Cosmos Nicolaou93dd88b2015-02-19 15:10:53 -0800330 binarydBin.Start(
331 "--name="+binarydName,
Suharsh Sivakumar65e38502015-04-01 18:33:18 -0700332 "--root-dir="+filepath.Join(workDir, "binstore"),
Asim Shankarf32d24d2015-04-01 16:34:26 -0700333 "--v23.tcp.address=127.0.0.1:0",
Cosmos Nicolaou93dd88b2015-02-19 15:10:53 -0800334 "--http=127.0.0.1:0")
Arup Mukherjeed8048282015-06-26 23:18:44 -0700335 // Allow publishers to update binaries
336 deviceBin.Run("acl", "set", binarydName, "root/a", "Write")
337
338 // We are also going to use the binaryd binary as our test app binary. Once our test app
339 // binary is published to the binaryd server started above, this (augmented with a
340 // timestamp) is the name the test app binary will have.
341 sampleAppBinName := binarydName + "/binaryd"
Cosmos Nicolaou93dd88b2015-02-19 15:10:53 -0800342
Asim Shankar6cc759d2015-03-14 03:31:44 -0700343 // Start an applicationd server that will serve the application
344 // envelope for the test application to be installed on the device.
Cosmos Nicolaou93dd88b2015-02-19 15:10:53 -0800345 applicationdBin.Start(
346 "--name="+appDName,
Asim Shankar6cc759d2015-03-14 03:31:44 -0700347 "--store="+mkSubdir(i, workDir, "appstore"),
Asim Shankarf32d24d2015-04-01 16:34:26 -0700348 "--v23.tcp.address=127.0.0.1:0",
Cosmos Nicolaou93dd88b2015-02-19 15:10:53 -0800349 )
Arup Mukherjeed8048282015-06-26 23:18:44 -0700350 // Allow publishers to create and update envelopes
351 deviceBin.Run("acl", "set", appDName, "root/a", "Read,Write,Resolve")
352
Bogdan Capritaa49fe642015-09-15 10:43:12 -0700353 sampleAppName := appDName + "/testapp"
Cosmos Nicolaou93dd88b2015-02-19 15:10:53 -0800354 appPubName := "testbinaryd"
355 appEnvelopeFilename := filepath.Join(workDir, "app.envelope")
Suharsh Sivakumar65e38502015-04-01 18:33:18 -0700356 appEnvelope := fmt.Sprintf("{\"Title\":\"BINARYD\", \"Args\":[\"--name=%s\", \"--root-dir=./binstore\", \"--v23.tcp.address=127.0.0.1:0\", \"--http=127.0.0.1:0\"], \"Binary\":{\"File\":%q}, \"Env\":[]}", appPubName, sampleAppBinName)
Cosmos Nicolaou93dd88b2015-02-19 15:10:53 -0800357 ioutil.WriteFile(appEnvelopeFilename, []byte(appEnvelope), 0666)
358 defer os.Remove(appEnvelopeFilename)
359
Bogdan Capritaa49fe642015-09-15 10:43:12 -0700360 output := applicationBin.Run("put", sampleAppName+"/0", deviceProfile, appEnvelopeFilename)
Bogdan Caprita164ddcb2015-09-03 11:14:10 -0700361 if got, want := output, fmt.Sprintf("Application envelope added for profile %s.", deviceProfile); got != want {
Cosmos Nicolaou93dd88b2015-02-19 15:10:53 -0800362 i.Fatalf("got %q, want %q", got, want)
363 }
364
365 // Verify that the envelope we uploaded shows up with glob.
366 inv = applicationBin.Start("match", sampleAppName, deviceProfile)
367 parts = inv.ExpectSetEventuallyRE(`"Title": "(.*)",`, `"File": "(.*)",`)
368 if got, want := len(parts), 2; got != want {
369 i.Fatalf("got %d, want %d", got, want)
370 }
371 for line, want := range []string{"BINARYD", sampleAppBinName} {
372 if got := parts[line][1]; got != want {
373 i.Fatalf("got %q, want %q", got, want)
374 }
375 }
376
Arup Mukherjeed8048282015-06-26 23:18:44 -0700377 // Publish the app (This uses the binarydBin binary and the testapp envelope from above)
378 pubDeviceBin.Start("publish", "-from", filepath.Dir(binarydBin.Path()), "-readers", "root/r/admin", filepath.Base(binarydBin.Path())+":testapp").WaitOrDie(os.Stdout, os.Stderr)
379 if got := namespaceBin.Run("glob", sampleAppBinName); len(got) == 0 {
380 i.Fatalf("glob failed for %q", sampleAppBinName)
381 }
382
Cosmos Nicolaou93dd88b2015-02-19 15:10:53 -0800383 // Install the app on the device.
384 inv = deviceBin.Start("install", mtName+"/devmgr/apps", sampleAppName)
Bogdan Caprita2b050322015-04-17 09:04:03 -0700385 installationName := inv.ReadLine()
386 if installationName == "" {
387 i.Fatalf("got empty installation name from install")
388 }
Cosmos Nicolaou93dd88b2015-02-19 15:10:53 -0800389
390 // Verify that the installation shows up when globbing the device manager.
Cosmos Nicolaouabbcb202015-02-26 11:24:52 -0800391 output = namespaceBin.Run("glob", mtName+"/devmgr/apps/BINARYD/*")
Cosmos Nicolaou93dd88b2015-02-19 15:10:53 -0800392 if got, want := output, installationName; got != want {
393 i.Fatalf("got %q, want %q", got, want)
394 }
395
396 // Start an instance of the app, granting it blessing extension myapp.
Bogdan Caprita2b050322015-04-17 09:04:03 -0700397 inv = deviceBin.Start("instantiate", installationName, "myapp")
398 instanceName := inv.ReadLine()
399 if instanceName == "" {
400 i.Fatalf("got empty instance name from new")
401 }
402 deviceBin.Start("run", instanceName)
Cosmos Nicolaou93dd88b2015-02-19 15:10:53 -0800403
Cosmos Nicolaouabbcb202015-02-26 11:24:52 -0800404 resolve(mtName + "/" + appPubName)
Cosmos Nicolaou93dd88b2015-02-19 15:10:53 -0800405
406 // Verify that the instance shows up when globbing the device manager.
Cosmos Nicolaouabbcb202015-02-26 11:24:52 -0800407 output = namespaceBin.Run("glob", mtName+"/devmgr/apps/BINARYD/*/*")
Cosmos Nicolaou93dd88b2015-02-19 15:10:53 -0800408 if got, want := output, instanceName; got != want {
409 i.Fatalf("got %q, want %q", got, want)
410 }
411
Robert Kroeger02714b72015-04-14 18:02:38 -0700412 inv = debugBin.Start("stats", "read", instanceName+"/stats/system/pid")
413 pid := inv.ExpectRE("[0-9]+$", 1)[0][0]
414 uname, err := getUserForPid(i, pid)
415 if err != nil {
416 i.Errorf("getUserForPid could not determine the user running pid %v", pid)
Robin Thellendf0a422b2015-08-27 09:55:07 -0700417 } else if uname != appUser {
Robert Kroeger769353e2015-05-05 19:10:07 -0700418 i.Errorf("app expected to be running as %v but is running as %v", appUser, uname)
Robert Kroeger02714b72015-04-14 18:02:38 -0700419 }
Cosmos Nicolaou93dd88b2015-02-19 15:10:53 -0800420
Arup Mukherjeed8048282015-06-26 23:18:44 -0700421 // Verify the app's blessings. We check the default blessing, as well as the
422 // "..." blessing, which should be the default blessing plus a publisher blessing.
423 userBlessing := "root/u/alice/myapp"
424 pubBlessing := "root/a/rovio/apps/published/binaryd"
425 appBlessing := mfrBlessing + "/a/" + pubBlessing + "," + ownerBlessing + "/a/" + pubBlessing
Cosmos Nicolaou93dd88b2015-02-19 15:10:53 -0800426 inv = debugBin.Start("stats", "read", instanceName+"/stats/security/principal/*/blessingstore")
Arup Mukherjeed8048282015-06-26 23:18:44 -0700427 inv.ExpectSetEventuallyRE(".*Default Blessings[ ]+"+userBlessing+"$", "[.][.][.][ ]+"+userBlessing+","+appBlessing)
Cosmos Nicolaou93dd88b2015-02-19 15:10:53 -0800428
Bogdan Caprita2b050322015-04-17 09:04:03 -0700429 // Kill and delete the instance.
430 deviceBin.Run("kill", instanceName)
431 deviceBin.Run("delete", instanceName)
Cosmos Nicolaou93dd88b2015-02-19 15:10:53 -0800432
433 // Verify that logs, but not stats, show up when globbing the
Bogdan Caprita2b050322015-04-17 09:04:03 -0700434 // not-running instance.
Cosmos Nicolaouabbcb202015-02-26 11:24:52 -0800435 if output = namespaceBin.Run("glob", instanceName+"/stats/..."); len(output) > 0 {
Cosmos Nicolaou93dd88b2015-02-19 15:10:53 -0800436 i.Fatalf("no output expected for glob %s/stats/..., got %q", output, instanceName)
437 }
Cosmos Nicolaouabbcb202015-02-26 11:24:52 -0800438 if output = namespaceBin.Run("glob", instanceName+"/logs/..."); len(output) == 0 {
Cosmos Nicolaou93dd88b2015-02-19 15:10:53 -0800439 i.Fatalf("output expected for glob %s/logs/..., but got none", instanceName)
440 }
441
Arup Mukherjeed8048282015-06-26 23:18:44 -0700442 // TODO: The deviced binary should probably be published by someone other than rovio :-)
443 // Maybe publishing the deviced binary should eventually use "device publish" too?
444 // For now, it uses the "application" and "binary" tools directly to ensure that those work
445
Cosmos Nicolaou93dd88b2015-02-19 15:10:53 -0800446 // Upload a deviced binary
447 devicedAppBinName := binarydName + "/deviced"
Todd Wangcd4b3cc2015-04-06 16:42:02 -0700448 binaryBin.Run("upload", devicedAppBinName, i.BuildGoPkg("v.io/x/ref/services/device/deviced").Path())
Arup Mukherjeed8048282015-06-26 23:18:44 -0700449 // Allow root/r/admin and its devices to read the binary
450 deviceBin.Run("acl", "set", devicedAppBinName, "root/r/admin", "Read")
Cosmos Nicolaou93dd88b2015-02-19 15:10:53 -0800451
Asim Shankar6cc759d2015-03-14 03:31:44 -0700452 // Upload a device manager envelope.
Cosmos Nicolaou93dd88b2015-02-19 15:10:53 -0800453 devicedEnvelopeFilename := filepath.Join(workDir, "deviced.envelope")
Asim Shankar6cc759d2015-03-14 03:31:44 -0700454 devicedEnvelope := fmt.Sprintf("{\"Title\":\"device manager\", \"Binary\":{\"File\":%q}}", devicedAppBinName)
Cosmos Nicolaou93dd88b2015-02-19 15:10:53 -0800455 ioutil.WriteFile(devicedEnvelopeFilename, []byte(devicedEnvelope), 0666)
456 defer os.Remove(devicedEnvelopeFilename)
Cosmos Nicolaouabbcb202015-02-26 11:24:52 -0800457 applicationBin.Run("put", devicedAppName, deviceProfile, devicedEnvelopeFilename)
Arup Mukherjeed8048282015-06-26 23:18:44 -0700458 // Allow root/r/admin and its devices to read the envelope
459 deviceBin.Run("acl", "set", devicedAppName, "root/r/admin", "Read")
Cosmos Nicolaou93dd88b2015-02-19 15:10:53 -0800460
461 // Update the device manager.
Arup Mukherjeed8048282015-06-26 23:18:44 -0700462 adminDeviceBin.Run("update", mtName+"/devmgr/device")
Bogdan Capritae8073682015-04-25 15:37:53 -0700463 resolveChange := func(name, old string) string {
464 resolver := func() (interface{}, error) {
465 inv := namespaceBin.Start("resolve", name)
466 defer inv.Wait(nil, os.Stderr)
467 if r := strings.TrimRight(inv.Output(), "\n"); len(r) > 0 && r != old {
468 return r, nil
469 }
470 return nil, nil
471 }
472 return i.WaitFor(resolver, 100*time.Millisecond, time.Minute).(string)
473 }
Cosmos Nicolaouabbcb202015-02-26 11:24:52 -0800474 mtEP = resolveChange(mtName, mtEP)
Cosmos Nicolaou93dd88b2015-02-19 15:10:53 -0800475
476 // Verify that device manager's mounttable is still published under the
477 // expected name (hostname).
Cosmos Nicolaouabbcb202015-02-26 11:24:52 -0800478 if namespaceBin.Run("glob", mtName) == "" {
Cosmos Nicolaou93dd88b2015-02-19 15:10:53 -0800479 i.Fatalf("failed to glob %s", mtName)
480 }
481
482 // Revert the device manager
Robin Thellendf0a422b2015-08-27 09:55:07 -0700483 // The argument to "device revert" is a glob pattern. So we need to
484 // wait for devmgr to be mounted before running the command.
485 resolve(mtEP + "/devmgr")
Arup Mukherjeed8048282015-06-26 23:18:44 -0700486 adminDeviceBin.Run("revert", mtName+"/devmgr/device")
Cosmos Nicolaouabbcb202015-02-26 11:24:52 -0800487 mtEP = resolveChange(mtName, mtEP)
Cosmos Nicolaou93dd88b2015-02-19 15:10:53 -0800488
489 // Verify that device manager's mounttable is still published under the
490 // expected name (hostname).
Cosmos Nicolaouabbcb202015-02-26 11:24:52 -0800491 if namespaceBin.Run("glob", mtName) == "" {
Cosmos Nicolaou93dd88b2015-02-19 15:10:53 -0800492 i.Fatalf("failed to glob %s", mtName)
493 }
494
495 // Verify that the local mounttable exists, and that the device manager,
496 // the global namespace, and the neighborhood are mounted on it.
Robin Thellendf0a422b2015-08-27 09:55:07 -0700497 resolve(mtEP + "/devmgr")
498 resolve(mtEP + "/nh")
499 resolve(mtEP + "/global")
500
Todd Wang8123b5e2015-05-14 18:44:43 -0700501 namespaceRoot, _ := i.GetVar(ref.EnvNamespacePrefix)
Robin Thellendf0a422b2015-08-27 09:55:07 -0700502 if got, want := namespaceBin.Run("resolve", mtEP+"/global"), namespaceRoot; got != want {
Cosmos Nicolaou93dd88b2015-02-19 15:10:53 -0800503 i.Fatalf("got %q, want %q", got, want)
504 }
505
Bogdan Caprita2b050322015-04-17 09:04:03 -0700506 // Kill the device manager (which causes it to be restarted), wait for
507 // the endpoint to change.
508 deviceBin.Run("kill", mtName+"/devmgr/device")
Cosmos Nicolaouabbcb202015-02-26 11:24:52 -0800509 mtEP = resolveChange(mtName, mtEP)
Cosmos Nicolaou93dd88b2015-02-19 15:10:53 -0800510
Bogdan Caprita2b050322015-04-17 09:04:03 -0700511 // Shut down the device manager.
Bogdan Caprita3c319212015-08-19 18:12:53 -0700512 stopDevMgrOnce.Do(stopDevMgr)
Cosmos Nicolaou93dd88b2015-02-19 15:10:53 -0800513
514 // Wait for the mounttable entry to go away.
Cosmos Nicolaouabbcb202015-02-26 11:24:52 -0800515 resolveGone := func(name string) string {
516 resolver := func() (interface{}, error) {
517 inv := namespaceBin.Start("resolve", name)
518 defer inv.Wait(nil, os.Stderr)
519 if r := strings.TrimRight(inv.Output(), "\n"); len(r) == 0 {
520 return r, nil
521 }
522 return nil, nil
Cosmos Nicolaou93dd88b2015-02-19 15:10:53 -0800523 }
Cosmos Nicolaouabbcb202015-02-26 11:24:52 -0800524 return i.WaitFor(resolver, 100*time.Millisecond, time.Minute).(string)
Cosmos Nicolaou93dd88b2015-02-19 15:10:53 -0800525 }
Cosmos Nicolaouabbcb202015-02-26 11:24:52 -0800526 resolveGone(mtName)
Cosmos Nicolaou93dd88b2015-02-19 15:10:53 -0800527
Robert Kroeger02714b72015-04-14 18:02:38 -0700528 var fi []os.FileInfo
529
530 // This doesn't work in multiuser mode because dmInstallDir is
531 // owned by the device manager user and unreadable by the user
532 // running this test.
533 if !withSuid {
534 fi, err = ioutil.ReadDir(dmInstallDir)
535 if err != nil {
536 i.Fatalf("failed to readdir for %q: %v", dmInstallDir, err)
537 }
Cosmos Nicolaou93dd88b2015-02-19 15:10:53 -0800538 }
539
Cosmos Nicolaouabbcb202015-02-26 11:24:52 -0800540 deviceScript.Run("uninstall")
Cosmos Nicolaou93dd88b2015-02-19 15:10:53 -0800541
542 fi, err = ioutil.ReadDir(dmInstallDir)
543 if err == nil || len(fi) > 0 {
544 i.Fatalf("managed to read %d entries from %q", len(fi), dmInstallDir)
545 }
546 if err != nil && !strings.Contains(err.Error(), "no such file or directory") {
547 i.Fatalf("wrong error: %v", err)
548 }
549}
Asim Shankar6cc759d2015-03-14 03:31:44 -0700550
551func buildAndCopyBinaries(i *v23tests.T, destinationDir string, packages ...string) {
552 var args []string
553 for _, pkg := range packages {
554 args = append(args, i.BuildGoPkg(pkg).Path())
555 }
556 args = append(args, destinationDir)
557 i.BinaryFromPath("/bin/cp").Start(args...).WaitOrDie(os.Stdout, os.Stderr)
558}
559
560func mkSubdir(i *v23tests.T, parent, child string) string {
561 dir := filepath.Join(parent, child)
562 if err := os.Mkdir(dir, 0755); err != nil {
563 i.Fatalf("failed to create %q: %v", dir, err)
564 }
565 return dir
566}
Robert Kroeger02714b72015-04-14 18:02:38 -0700567
568var re = regexp.MustCompile("[ \t]+")
569
570// getUserForPid determines the username running process pid.
571func getUserForPid(i *v23tests.T, pid string) (string, error) {
572 pidString := i.BinaryFromPath("/bin/ps").Start(psFlags).Output()
573 for _, line := range strings.Split(pidString, "\n") {
574 fields := re.Split(line, -1)
575 if len(fields) > 1 && pid == fields[1] {
576 return fields[0], nil
577 }
578 }
579 return "", fmt.Errorf("Couldn't determine the user for pid %s", pid)
580}