1#!/usr/bin/env python
2#
3# Copyright 2012 the V8 project authors. All rights reserved.
4# Redistribution and use in source and binary forms, with or without
5# modification, are permitted provided that the following conditions are
6# met:
7#
8#     * Redistributions of source code must retain the above copyright
9#       notice, this list of conditions and the following disclaimer.
10#     * Redistributions in binary form must reproduce the above
11#       copyright notice, this list of conditions and the following
12#       disclaimer in the documentation and/or other materials provided
13#       with the distribution.
14#     * Neither the name of Google Inc. nor the names of its
15#       contributors may be used to endorse or promote products derived
16#       from this software without specific prior written permission.
17#
18# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
30
31import os
32import subprocess
33import sys
34
35
36PIDFILE = "/tmp/v8-distributed-testing-server.pid"
37ROOT = os.path.abspath(os.path.dirname(sys.argv[0]))
38
39
40def _PrintUsage():
41  print("""Usage: python %s COMMAND
42
43Where COMMAND can be any of:
44  start     Starts the server. Forks to the background.
45  stop      Stops the server.
46  restart   Stops, then restarts the server.
47  setup     Creates or updates the environment for the server to run.
48  update    Alias for "setup".
49  trust <keyfile>  Adds the given public key to the list of trusted keys.
50  help      Displays this help text.
51  """ % sys.argv[0])
52
53
54def _IsDaemonRunning():
55  return os.path.exists(PIDFILE)
56
57
58def _Cmd(cmd):
59  code = subprocess.call(cmd, shell=True)
60  if code != 0:
61    print("Command '%s' returned error code %d" % (cmd, code))
62    sys.exit(code)
63
64
65def Update():
66  # Create directory for private data storage.
67  data_dir = os.path.join(ROOT, "data")
68  if not os.path.exists(data_dir):
69    os.makedirs(data_dir)
70
71  # Create directory for trusted public keys of peers (and self).
72  trusted_dir = os.path.join(ROOT, "trusted")
73  if not os.path.exists(trusted_dir):
74    os.makedirs(trusted_dir)
75
76  # Install UltraJSON. It is much faster than Python's builtin json.
77  try:
78    import ujson  #@UnusedImport
79  except ImportError:
80    # Install pip if it doesn't exist.
81    code = subprocess.call("which pip > /dev/null", shell=True)
82    if code != 0:
83      apt_get_code = subprocess.call("which apt-get > /dev/null", shell=True)
84      if apt_get_code == 0:
85        print("Installing pip...")
86        _Cmd("sudo apt-get install python-pip")
87      else:
88        print("Please install pip on your machine. You can get it at: "
89              "http://www.pip-installer.org/en/latest/installing.html "
90              "or via your distro's package manager.")
91        sys.exit(1)
92    print("Using pip to install UltraJSON...")
93    _Cmd("sudo pip install ujson")
94
95  # Make sure we have a key pair for signing binaries.
96  privkeyfile = os.path.expanduser("~/.ssh/v8_dtest")
97  if not os.path.exists(privkeyfile):
98    _Cmd("ssh-keygen -t rsa -f %s -N '' -q" % privkeyfile)
99  fingerprint = subprocess.check_output("ssh-keygen -lf %s" % privkeyfile,
100                                        shell=True)
101  fingerprint = fingerprint.split(" ")[1].replace(":", "")[:16]
102  pubkeyfile = os.path.join(trusted_dir, "%s.pem" % fingerprint)
103  if (not os.path.exists(pubkeyfile) or
104      os.path.getmtime(pubkeyfile) < os.path.getmtime(privkeyfile)):
105    _Cmd("openssl rsa -in %s -out %s -pubout" % (privkeyfile, pubkeyfile))
106    with open(pubkeyfile, "a") as f:
107      f.write(fingerprint + "\n")
108    datafile = os.path.join(data_dir, "mypubkey")
109    with open(datafile, "w") as f:
110      f.write(fingerprint + "\n")
111
112  # Check out or update the server implementation in the current directory.
113  testrunner_dir = os.path.join(ROOT, "testrunner")
114  if os.path.exists(os.path.join(testrunner_dir, "server/daemon.py")):
115    _Cmd("cd %s; svn up" % testrunner_dir)
116  else:
117    path = ("http://v8.googlecode.com/svn/branches/bleeding_edge/"
118            "tools/testrunner")
119    _Cmd("svn checkout --force %s %s" % (path, testrunner_dir))
120
121  # Update this very script.
122  path = ("http://v8.googlecode.com/svn/branches/bleeding_edge/"
123          "tools/test-server.py")
124  scriptname = os.path.abspath(sys.argv[0])
125  _Cmd("svn cat %s > %s" % (path, scriptname))
126
127  # Check out or update V8.
128  v8_dir = os.path.join(ROOT, "v8")
129  if os.path.exists(v8_dir):
130    _Cmd("cd %s; git fetch" % v8_dir)
131  else:
132    _Cmd("git clone git://github.com/v8/v8.git %s" % v8_dir)
133
134  print("Finished.")
135
136
137# Handle "setup" here, because when executing that we can't import anything
138# else yet.
139if __name__ == "__main__" and len(sys.argv) == 2:
140  if sys.argv[1] in ("setup", "update"):
141    if _IsDaemonRunning():
142      print("Please stop the server before updating. Exiting.")
143      sys.exit(1)
144    Update()
145    sys.exit(0)
146  # Other parameters are handled below.
147
148
149#==========================================================
150# At this point we can assume that the implementation is available,
151# so we can import it.
152try:
153  from testrunner.server import constants
154  from testrunner.server import local_handler
155  from testrunner.server import main
156except Exception, e:
157  print(e)
158  print("Failed to import implementation. Have you run 'setup'?")
159  sys.exit(1)
160
161
162def _StartDaemon(daemon):
163  if not os.path.isdir(os.path.join(ROOT, "v8")):
164    print("No 'v8' working directory found. Have you run 'setup'?")
165    sys.exit(1)
166  daemon.start()
167
168
169if __name__ == "__main__":
170  if len(sys.argv) == 2:
171    arg = sys.argv[1]
172    if arg == "start":
173      daemon = main.Server(PIDFILE, ROOT)
174      _StartDaemon(daemon)
175    elif arg == "stop":
176      daemon = main.Server(PIDFILE, ROOT)
177      daemon.stop()
178    elif arg == "restart":
179      daemon = main.Server(PIDFILE, ROOT)
180      daemon.stop()
181      _StartDaemon(daemon)
182    elif arg in ("help", "-h", "--help"):
183      _PrintUsage()
184    elif arg == "status":
185      if not _IsDaemonRunning():
186        print("Server not running.")
187      else:
188        print(local_handler.LocalQuery([constants.REQUEST_STATUS]))
189    else:
190      print("Unknown command")
191      _PrintUsage()
192      sys.exit(2)
193  elif len(sys.argv) == 3:
194    arg = sys.argv[1]
195    if arg == "approve":
196      filename = sys.argv[2]
197      if not os.path.exists(filename):
198        print("%s does not exist.")
199        sys.exit(1)
200      filename = os.path.abspath(filename)
201      if _IsDaemonRunning():
202        response = local_handler.LocalQuery([constants.ADD_TRUSTED, filename])
203      else:
204        daemon = main.Server(PIDFILE, ROOT)
205        response = daemon.CopyToTrusted(filename)
206      print("Added certificate %s to trusted certificates." % response)
207    else:
208      print("Unknown command")
209      _PrintUsage()
210      sys.exit(2)
211  else:
212    print("Unknown command")
213    _PrintUsage()
214    sys.exit(2)
215  sys.exit(0)
216