slapgrid.py 67.2 KB
Newer Older
Cédric de Saint Martin's avatar
Cédric de Saint Martin committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
##############################################################################
#
# Copyright (c) 2010 Vifib SARL and Contributors. All Rights Reserved.
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsibility of assessing all potential
# consequences resulting from its eventual inadequacies and bugs
# End users who are looking for a ready-to-use solution with commercial
# guarantees and support are strongly adviced to contract a Free Software
# Service Company
#
# This program is Free Software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 3
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
#
##############################################################################

Łukasz Nowak's avatar
Łukasz Nowak committed
28
from slapos.grid import slapgrid
Łukasz Nowak's avatar
Łukasz Nowak committed
29 30
import httplib
import logging
Łukasz Nowak's avatar
Łukasz Nowak committed
31 32
import os
import shutil
33
import signal
34
import slapos.slap.slap
35
import slapos.grid.utils
36
from slapos.grid.watchdog import Watchdog, getWatchdogID
Łukasz Nowak's avatar
Łukasz Nowak committed
37
import socket
38
import sys
39
import tempfile
40
import time
41
import unittest
Łukasz Nowak's avatar
Łukasz Nowak committed
42
import urlparse
43
import xml_marshaller
Łukasz Nowak's avatar
Łukasz Nowak committed
44

45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71

WATCHDOG_TEMPLATE = """#!%(python_path)s -S

import sys
sys.path=%(sys_path)s
import slapos.slap.slap
import slapos.grid.watchdog

def setBang():
  def getBang():
    def bang(self_partition,message):
      report = ""
      for key in self_partition.__dict__:
        report += (key + ': ' + str(self_partition.__dict__[key]) + '  ')
        if key == '_connection_helper':
          for el in self_partition.__dict__[key].__dict__:
            report += ('    ' + el +': ' +
                       str(self_partition.__dict__[key].__dict__[el]) + '  ')
      report += message
      open('%(watchdog_banged)s','w').write(report)
    return bang
  slapos.slap.ComputerPartition.bang = getBang()

setBang()
slapos.grid.watchdog.main()
"""

72 73 74 75 76 77 78 79
WRAPPER_CONTENT = """#!/bin/sh
touch worked &&
mkdir -p etc/run &&
echo "#!/bin/sh" > etc/run/wrapper &&
echo "while :; do echo "Working\\nWorking\\n" ; sleep 0.1; done" >> etc/run/wrapper &&
chmod 755 etc/run/wrapper
"""

80 81 82 83 84 85 86
DAEMON_CONTENT = """#!/bin/sh
mkdir -p etc/service &&
echo "#!/bin/sh" > etc/service/daemon &&
echo "touch launched
if [ -f ./crashed ]; then
while :; do echo "Working\\nWorking\\n" ; sleep 0.1; done
else
87
touch ./crashed; echo "Failing\\nFailing\\n"; sleep 1; exit 111;
88 89 90 91 92
fi" >> etc/service/daemon &&
chmod 755 etc/service/daemon &&
touch worked
"""

Łukasz Nowak's avatar
Łukasz Nowak committed
93
class BasicMixin:
94 95 96
  def assertSortedListEqual(self, list1, list2, msg=None):
    self.assertListEqual(sorted(list1), sorted(list2), msg)

Łukasz Nowak's avatar
Łukasz Nowak committed
97 98
  def setUp(self):
    self._tempdir = tempfile.mkdtemp()
99 100
    self.software_root = os.path.join(self._tempdir, 'software')
    self.instance_root = os.path.join(self._tempdir, 'instance')
101 102 103
    logging.basicConfig(level=logging.DEBUG)
    self.setSlapgrid()

Cédric de Saint Martin's avatar
Cédric de Saint Martin committed
104
  def setSlapgrid(self, develop=False):
105
    if getattr(self, 'master_url', None) is None:
106
      self.master_url = 'http://127.0.0.1:80/'
Łukasz Nowak's avatar
Łukasz Nowak committed
107 108 109 110 111 112 113 114
    self.computer_id = 'computer'
    self.supervisord_socket = os.path.join(self._tempdir, 'supervisord.sock')
    self.supervisord_configuration_path = os.path.join(self._tempdir,
      'supervisord')
    self.usage_report_periodicity = 1
    self.buildout = None
    self.grid = slapgrid.Slapgrid(self.software_root, self.instance_root,
      self.master_url, self.computer_id, self.supervisord_socket,
115
      self.supervisord_configuration_path,
Cédric de Saint Martin's avatar
Cédric de Saint Martin committed
116
      self.buildout, develop=develop)
117 118 119 120
    # monkey patch buildout bootstrap
    def dummy(*args, **kw):
      pass
    slapos.grid.utils.bootstrapBuildout = dummy
Cédric de Saint Martin's avatar
Cédric de Saint Martin committed
121

Cédric Le Ninivin's avatar
Cédric Le Ninivin committed
122 123 124
  def launchSlapgrid(self,develop=False):
    self.setSlapgrid(develop=develop)
    return self.grid.processComputerPartitionList()
125

126 127 128 129
  def launchSlapgridSoftware(self,develop=False):
    self.setSlapgrid(develop=develop)
    return self.grid.processSoftwareReleaseList()

Łukasz Nowak's avatar
Łukasz Nowak committed
130
  def tearDown(self):
Łukasz Nowak's avatar
Łukasz Nowak committed
131 132 133 134 135 136 137 138 139
    # XXX: Hardcoded pid, as it is not configurable in slapos
    svc = os.path.join(self.instance_root, 'var', 'run', 'supervisord.pid')
    if os.path.exists(svc):
      try:
        pid = int(open(svc).read().strip())
      except ValueError:
        pass
      else:
        os.kill(pid, signal.SIGTERM)
140
    shutil.rmtree(self._tempdir, True)
Łukasz Nowak's avatar
Łukasz Nowak committed
141

142

143
class TestBasicSlapgridCP(BasicMixin, unittest.TestCase):
Łukasz Nowak's avatar
Łukasz Nowak committed
144 145 146 147 148 149 150 151 152 153 154
  def test_no_software_root(self):
    self.assertRaises(OSError, self.grid.processComputerPartitionList)

  def test_no_instance_root(self):
    os.mkdir(self.software_root)
    self.assertRaises(OSError, self.grid.processComputerPartitionList)

  def test_no_master(self):
    os.mkdir(self.software_root)
    os.mkdir(self.instance_root)
    self.assertRaises(socket.error, self.grid.processComputerPartitionList)
155 156

class MasterMixin(BasicMixin):
157

158
  def _patchHttplib(self):
159
    """Overrides httplib"""
160 161 162 163 164 165 166
    import mock.httplib

    self.saved_httplib = dict()

    for fake in vars(mock.httplib):
      self.saved_httplib[fake] = getattr(httplib, fake, None)
      setattr(httplib, fake, getattr(mock.httplib, fake))
167

168
  def _unpatchHttplib(self):
169
    """Restores httplib overriding"""
170 171 172 173
    import httplib
    for name, original_value in self.saved_httplib.items():
      setattr(httplib, name, original_value)
    del self.saved_httplib
174

175 176 177 178 179 180 181 182 183 184 185 186 187
  def _mock_sleep(self):
    self.fake_waiting_time = None
    self.real_sleep = time.sleep

    def mocked_sleep(secs):
      if self.fake_waiting_time is not None:
        secs = self.fake_waiting_time
      self.real_sleep(secs)

    time.sleep = mocked_sleep

  def _unmock_sleep(self):
    time.sleep = self.real_sleep
188
  def setUp(self):
189
    self._patchHttplib()
190
    self._mock_sleep()
191
    BasicMixin.setUp(self)
192 193

  def tearDown(self):
194
    self._unpatchHttplib()
195
    self._unmock_sleep()
196 197
    BasicMixin.tearDown(self)

198

199
class ComputerForTest:
200 201 202 203
  """
  Class to set up environment for tests setting instance, software
  and server response
  """
204 205 206 207 208
  def __init__(self,
               software_root,
               instance_root,
               instance_amount=1,
               software_amount=1):
209 210 211 212
    """
    Will set up instances, software and sequence
    """
    self.sequence = []
213 214 215 216
    self.instance_amount = instance_amount
    self.software_amount = software_amount
    self.software_root = software_root
    self.instance_root = instance_root
Cédric Le Ninivin's avatar
Cédric Le Ninivin committed
217 218 219 220
    if not os.path.isdir(self.instance_root):
      os.mkdir(self.instance_root)
    if not os.path.isdir(self.software_root):
      os.mkdir(self.software_root)
Cédric Le Ninivin's avatar
Cédric Le Ninivin committed
221 222
    self.setSoftwares()
    self.setInstances()
Cédric Le Ninivin's avatar
Cédric Le Ninivin committed
223
    self.setServerResponse()
224

225 226 227 228 229 230 231 232 233
  def setSoftwares(self):
    """
    Will set requested amount of software
    """
    self.software_list = range(0,self.software_amount)
    for i in self.software_list:
      name = str(i)
      self.software_list[i] = SoftwareForTest(self.software_root, name=name)

234
  def setInstances(self):
235 236 237
    """
    Will set requested amount of instance giving them by default first software
    """
238
    self.instance_list = range(0, self.instance_amount)
Cédric Le Ninivin's avatar
Cédric Le Ninivin committed
239 240
    for i in self.instance_list:
      name = str(i)
Cédric Le Ninivin's avatar
Cédric Le Ninivin committed
241 242 243 244
      if len(self.software_list) is not 0:
        software = self.software_list[0]
      else:
        software = None
Cédric Le Ninivin's avatar
Cédric Le Ninivin committed
245
      self.instance_list[i] = InstanceForTest(self.instance_root, name=name,
Cédric Le Ninivin's avatar
Cédric Le Ninivin committed
246
                                 software=software)
247 248

  def getComputer (self, computer_id):
249 250 251
    """
    Will return current requested state of computer
    """
252 253
    slap_computer = slapos.slap.Computer(computer_id)
    slap_computer._software_release_list = []
Cédric Le Ninivin's avatar
Cédric Le Ninivin committed
254
    slap_computer._computer_partition_list = []
255 256 257
    for instance in self.instance_list:
      slap_computer._computer_partition_list.append(
        instance.getInstance(computer_id))
258 259 260
    for software in self.software_list:
      slap_computer._software_release_list.append(
        software.getSoftware(computer_id))
261 262
    return slap_computer

Cédric Le Ninivin's avatar
Cédric Le Ninivin committed
263 264 265
  def setServerResponse(self):
    httplib.HTTPConnection._callback = self.getServerResponse()

266
  def getServerResponse(self):
267 268 269 270 271
    """
    Define _callback.
    Will register global sequence of message, sequence by partition
    and error and error message by partition
    """
272 273
    def server_response(self_httplib, path, method, body, header):
      parsed_url = urlparse.urlparse(path.lstrip('/'))
Cédric Le Ninivin's avatar
Cédric Le Ninivin committed
274
      self.sequence.append(parsed_url.path)
275 276 277 278 279 280
      if method == 'GET':
        parsed_qs = urlparse.parse_qs(parsed_url.query)
      else:
        parsed_qs = urlparse.parse_qs(body)
      if parsed_url.path == 'getFullComputerInformation' and \
            'computer_id' in parsed_qs:
Cédric Le Ninivin's avatar
Cédric Le Ninivin committed
281 282
        slap_computer = self.getComputer(parsed_qs['computer_id'][0])
        return (200, {}, xml_marshaller.xml_marshaller.dumps(slap_computer))
283
      if method == 'POST' and 'computer_partition_id' in parsed_qs:
Cédric Le Ninivin's avatar
Cédric Le Ninivin committed
284
        instance = self.instance_list[int(parsed_qs['computer_partition_id'][0])]
285 286 287 288 289 290 291 292 293
        instance.sequence.append(parsed_url.path)
        if parsed_url.path == 'availableComputerPartition':
          return (200, {}, '')
        if parsed_url.path == 'startedComputerPartition':
          instance.state = 'started'
          return (200, {}, '')
        if parsed_url.path == 'stoppedComputerPartition':
          instance.state = 'stopped'
          return (200, {}, '')
294 295 296
        if parsed_url.path == 'destroyedComputerPartition':
          instance.state = 'destroyed'
          return (200, {}, '')
297 298
        if parsed_url.path == 'softwareInstanceBang':
          return (200, {}, '')
299
        if parsed_url.path == 'softwareInstanceError':
300 301 302
          instance.error_log = '\n'.join([line for line \
                                   in parsed_qs['error_log'][0].splitlines()
                                 if 'dropPrivileges' not in line])
303 304
          instance.error = True
          return (200, {}, '')
305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320

      elif method == 'POST' and 'url' in parsed_qs:
        # XXX hardcoded to first sofwtare release!
        software = self.software_list[0]
        software.sequence.append(parsed_url.path)
        if parsed_url.path == 'buildingSoftwareRelease':
          return (200, {}, '')
        if parsed_url.path == 'softwareReleaseError':
          software.error_log = '\n'.join([line for line \
                                   in parsed_qs['error_log'][0].splitlines()
                                 if 'dropPrivileges' not in line])
          software.error = True
          return (200, {}, '')

      else:
        return (500, {}, '')
321 322 323 324 325 326 327
    return server_response


class InstanceForTest:
  """
  Class containing all needed paramaters and function to simulate instances
  """
Cédric Le Ninivin's avatar
Cédric Le Ninivin committed
328
  def __init__(self, instance_root, name, software):
329
    self.instance_root = instance_root
330
    self.software = software
331
    self.requested_state = 'stopped'
332
    self.state = None
Cédric Le Ninivin's avatar
Cédric Le Ninivin committed
333
    self.error = False
334
    self.error_log = None
335 336 337 338 339 340 341
    self.sequence = []
    self.name = name
    self.partition_path = os.path.join(self.instance_root, self.name)
    os.mkdir(self.partition_path, 0750)
    self.timestamp = None

  def getInstance (self, computer_id):
342 343 344
    """
    Will return current requested state of instance
    """
345
    partition = slapos.slap.ComputerPartition(computer_id, self.name)
Cédric Le Ninivin's avatar
Cédric Le Ninivin committed
346
    partition._software_release_document = self.getSoftwareRelease()
347
    partition._requested_state = self.requested_state
Cédric Le Ninivin's avatar
Cédric Le Ninivin committed
348 349 350
    if self.software is not None:
      if self.timestamp is not None :
        partition._parameter_dict = {'timestamp': self.timestamp}
351 352 353
    return partition

  def getSoftwareRelease (self):
354 355 356
    """
    Return software release for Instance
    """
Cédric Le Ninivin's avatar
Cédric Le Ninivin committed
357
    if self.software is not None:
Cédric Le Ninivin's avatar
Cédric Le Ninivin committed
358
      sr = slapos.slap.SoftwareRelease()
Cédric Le Ninivin's avatar
Cédric Le Ninivin committed
359 360 361
      sr._software_release = self.software.name
      return sr
    else: return None
362

363 364 365 366 367 368 369 370 371 372 373
  def setPromise (self, promise_name, promise_content):
    """
    This function will set promise and return its path
    """
    promise_path = os.path.join(self.partition_path, 'etc', 'promise')
    if not os.path.isdir(promise_path):
      os.makedirs(promise_path)
    promise = os.path.join(promise_path,promise_name)
    open(promise, 'w').write(promise_content)
    os.chmod(promise, 0777)

Cédric Le Ninivin's avatar
Cédric Le Ninivin committed
374

375
class SoftwareForTest:
376 377 378 379
  """
  Class to prepare and simulate software.
  each instance has a sotfware attributed
  """
380
  def __init__(self, software_root, name=''):
381 382 383
    """
    Will set file and variable for software
    """
384 385 386 387 388 389
    self.software_root = software_root
    self.name = 'http://sr%s/' % name
    self.sequence = []
    self.software_hash = \
        slapos.grid.utils.getSoftwareUrlHash(self.name)
    self.srdir = os.path.join(self.software_root, self.software_hash)
390
    self.requested_state = 'available'
391
    os.mkdir(self.srdir)
Cédric Le Ninivin's avatar
Cédric Le Ninivin committed
392
    self.setTemplateCfg()
393 394
    self.srbindir = os.path.join(self.srdir, 'bin')
    os.mkdir(self.srbindir)
Cédric Le Ninivin's avatar
Cédric Le Ninivin committed
395
    self.setBuildout()
396

397 398 399 400 401 402 403 404 405 406
  def getSoftware (self, computer_id):
    """
    Will return current requested state of software
    """
    software = slapos.slap.SoftwareRelease(self.name, computer_id)
    software._requested_state = self.requested_state
    return software


  def setTemplateCfg (self,template="""[buildout]"""):
407 408 409
    """
    Set template.cfg
    """
410 411
    open(os.path.join(self.srdir, 'template.cfg'), 'w').write(template)

412
  def setBuildout (self, buildout="""#!/bin/sh
413
touch worked"""):
414 415 416
    """
    Set a buildout exec in bin
    """
417 418 419
    open(os.path.join(self.srbindir, 'buildout'), 'w').write(buildout)
    os.chmod(os.path.join(self.srbindir, 'buildout'), 0755)

Cédric Le Ninivin's avatar
Cédric Le Ninivin committed
420
  def setPeriodicity(self,periodicity):
421 422 423
    """
    Set a periodicity file
    """
424 425 426
    open(os.path.join(self.srdir, 'periodicity'), 'w').write(
      """%s""" % (periodicity))

Cédric Le Ninivin's avatar
Cédric Le Ninivin committed
427 428


429
class TestSlapgridCPWithMaster(MasterMixin, unittest.TestCase):
430

431
  def test_nothing_to_do(self):
432

Cédric Le Ninivin's avatar
Cédric Le Ninivin committed
433
    computer = ComputerForTest(self.software_root,self.instance_root,0,0)
434

435
    self.assertEqual(self.grid.processComputerPartitionList(), slapgrid.SLAPGRID_SUCCESS)
436 437
    self.assertSortedListEqual(os.listdir(self.instance_root), ['etc', 'var'])
    self.assertSortedListEqual(os.listdir(self.software_root), [])
438 439

  def test_one_partition(self):
Cédric Le Ninivin's avatar
Cédric Le Ninivin committed
440 441
    computer = ComputerForTest(self.software_root,self.instance_root)
    instance = computer.instance_list[0]
442
    self.assertEqual(self.grid.processComputerPartitionList(), slapgrid.SLAPGRID_SUCCESS)
443 444 445 446 447 448
    self.assertSortedListEqual(os.listdir(self.instance_root), ['0', 'etc',
      'var'])
    partition = os.path.join(self.instance_root, '0')
    self.assertSortedListEqual(os.listdir(partition), ['worked',
      'buildout.cfg'])
    self.assertSortedListEqual(os.listdir(self.software_root),
Cédric Le Ninivin's avatar
Cédric Le Ninivin committed
449 450
      [instance.software.software_hash])
    self.assertEqual(computer.sequence,
451 452 453 454 455 456 457 458
                     ['getFullComputerInformation', 'availableComputerPartition',
                      'stoppedComputerPartition'])

  def test_one_partition_instance_cfg(self):
    """
    Check that slapgrid processes instance is profile is not named
    "template.cfg" but "instance.cfg".
    """
Cédric Le Ninivin's avatar
Cédric Le Ninivin committed
459 460
    computer = ComputerForTest(self.software_root,self.instance_root)
    instance = computer.instance_list[0]
461
    self.assertEqual(self.grid.processComputerPartitionList(), slapgrid.SLAPGRID_SUCCESS)
462 463
    self.assertSortedListEqual(os.listdir(self.instance_root), ['0', 'etc',
      'var'])
Łukasz Nowak's avatar
Łukasz Nowak committed
464 465 466
    partition = os.path.join(self.instance_root, '0')
    self.assertSortedListEqual(os.listdir(partition), ['worked',
      'buildout.cfg'])
467
    self.assertSortedListEqual(os.listdir(self.software_root),
Cédric Le Ninivin's avatar
Cédric Le Ninivin committed
468 469
      [instance.software.software_hash])
    self.assertEqual(computer.sequence,
470 471
                     ['getFullComputerInformation', 'availableComputerPartition',
                      'stoppedComputerPartition'])
472

473 474
  def test_one_free_partition(self):
    """
475
    Test if slapgrid cp don't process "free" partition
476
    """
Cédric Le Ninivin's avatar
Cédric Le Ninivin committed
477 478 479
    computer = ComputerForTest(self.software_root,self.instance_root,
                               software_amount = 0)
    partition = computer.instance_list[0]
480
    partition.requested_state = 'destroyed'
481
    self.assertEqual(self.grid.processComputerPartitionList(), slapgrid.SLAPGRID_SUCCESS)
Cédric Le Ninivin's avatar
Cédric Le Ninivin committed
482 483
    self.assertSortedListEqual(os.listdir(self.instance_root), ['0','etc', 'var'])
    self.assertSortedListEqual(os.listdir(partition.partition_path), [])
484
    self.assertSortedListEqual(os.listdir(self.software_root), [])
485
    self.assertEqual(partition.sequence, [])
486

487
  def test_one_partition_started(self):
Cédric Le Ninivin's avatar
Cédric Le Ninivin committed
488 489 490 491
    computer = ComputerForTest(self.software_root,self.instance_root)
    partition = computer.instance_list[0]
    partition.requested_state = 'started'
    partition.software.setBuildout(WRAPPER_CONTENT)
492
    self.assertEqual(self.grid.processComputerPartitionList(), slapgrid.SLAPGRID_SUCCESS)
493 494
    self.assertSortedListEqual(os.listdir(self.instance_root), ['0', 'etc',
      'var'])
Cédric Le Ninivin's avatar
Cédric Le Ninivin committed
495 496
    self.assertSortedListEqual(os.listdir(partition.partition_path),
                               ['.0_wrapper.log','worked', 'buildout.cfg', 'etc'])
497
    tries = 50
Cédric Le Ninivin's avatar
Cédric Le Ninivin committed
498
    wrapper_log = os.path.join(partition.partition_path, '.0_wrapper.log')
499 500 501 502
    while tries > 0:
      tries -= 1
      if os.path.getsize(wrapper_log) > 0:
        break
503
      time.sleep(0.1)
504 505
    self.assertTrue('Working' in open(wrapper_log, 'r').read())
    self.assertSortedListEqual(os.listdir(self.software_root),
Cédric Le Ninivin's avatar
Cédric Le Ninivin committed
506 507
      [partition.software.software_hash])
    self.assertEqual(computer.sequence,
508 509
                     ['getFullComputerInformation', 'availableComputerPartition',
                      'startedComputerPartition'])
Cédric Le Ninivin's avatar
Cédric Le Ninivin committed
510
    self.assertEqual(partition.state,'started')
511

512 513

  def test_one_partition_started_stopped(self):
514 515
    computer = ComputerForTest(self.software_root,self.instance_root)
    instance = computer.instance_list[0]
516

517 518
    instance.requested_state = 'started'
    instance.software.setBuildout("""#!/bin/sh
519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535
touch worked &&
mkdir -p etc/run &&
(
cat <<'HEREDOC'
#!%(python)s
import signal
def handler(signum, frame):
  print 'Signal handler called with signal', signum
  raise SystemExit
signal.signal(signal.SIGTERM, handler)

while True:
  print "Working"
HEREDOC
)> etc/run/wrapper &&
chmod 755 etc/run/wrapper
""" % dict(python = sys.executable))
536
    self.assertEqual(self.grid.processComputerPartitionList(), slapgrid.SLAPGRID_SUCCESS)
537 538
    self.assertSortedListEqual(os.listdir(self.instance_root), ['0', 'etc',
      'var'])
539
    self.assertSortedListEqual(os.listdir(instance.partition_path), ['.0_wrapper.log',
540
      'worked', 'buildout.cfg', 'etc'])
541
    wrapper_log = os.path.join(instance.partition_path, '.0_wrapper.log')
542
    tries = 50
543 544 545 546
    while tries > 0:
      tries -= 1
      if os.path.getsize(wrapper_log) > 0:
        break
547
      time.sleep(0.1)
548
    os.path.getsize(wrapper_log)
549 550
    self.assertTrue('Working' in open(wrapper_log, 'r').read())
    self.assertSortedListEqual(os.listdir(self.software_root),
551 552
      [instance.software.software_hash])
    self.assertEqual(computer.sequence,
553 554
                     ['getFullComputerInformation', 'availableComputerPartition',
                      'startedComputerPartition'])
555
    self.assertEqual(instance.state,'started')
556

557 558
    computer.sequence = []
    instance.requested_state = 'stopped'
559
    self.assertEqual(self.launchSlapgrid(), slapgrid.SLAPGRID_SUCCESS)
Cédric Le Ninivin's avatar
Cédric Le Ninivin committed
560 561 562 563 564
    self.assertSortedListEqual(os.listdir(self.instance_root),
                               ['0', 'etc', 'var'])
    self.assertSortedListEqual(
      os.listdir(instance.partition_path),
      ['.0_wrapper.log', '.0_wrapper.log.1', 'worked', 'buildout.cfg', 'etc'])
565
    tries = 50
Łukasz Nowak's avatar
Łukasz Nowak committed
566
    expected_text = 'Signal handler called with signal 15'
567 568
    while tries > 0:
      tries -= 1
Łukasz Nowak's avatar
Łukasz Nowak committed
569 570
      found = expected_text in open(wrapper_log, 'r').read()
      if found:
571
        break
572
      time.sleep(0.1)
Łukasz Nowak's avatar
Łukasz Nowak committed
573
    self.assertTrue(found)
574
    self.assertEqual(computer.sequence,
575 576
                     ['getFullComputerInformation', 'availableComputerPartition',
                      'stoppedComputerPartition'])
577
    self.assertEqual(instance.state,'stopped')
578

579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652
  def test_one_broken_partition_stopped(self):
    """
    Check that, for, an already started instance if stop is requested,
    processes will be stopped even if instance is broken (buildout fails
    to run) but status is still started.
    """
    computer = ComputerForTest(self.software_root,self.instance_root)
    instance = computer.instance_list[0]

    instance.requested_state = 'started'
    instance.software.setBuildout("""#!/bin/sh
touch worked &&
mkdir -p etc/run &&
(
cat <<'HEREDOC'
#!%(python)s
import signal
def handler(signum, frame):
  print 'Signal handler called with signal', signum
  raise SystemExit
signal.signal(signal.SIGTERM, handler)

while True:
  print "Working"
HEREDOC
)> etc/run/wrapper &&
chmod 755 etc/run/wrapper
""" % dict(python = sys.executable))
    self.assertEqual(self.grid.processComputerPartitionList(), slapgrid.SLAPGRID_SUCCESS)
    self.assertSortedListEqual(os.listdir(self.instance_root), ['0', 'etc',
      'var'])
    self.assertSortedListEqual(os.listdir(instance.partition_path), ['.0_wrapper.log',
      'worked', 'buildout.cfg', 'etc'])
    wrapper_log = os.path.join(instance.partition_path, '.0_wrapper.log')
    tries = 50
    while tries > 0:
      tries -= 1
      if os.path.getsize(wrapper_log) > 0:
        break
      time.sleep(0.1)
    os.path.getsize(wrapper_log)
    self.assertTrue('Working' in open(wrapper_log, 'r').read())
    self.assertSortedListEqual(os.listdir(self.software_root),
      [instance.software.software_hash])
    self.assertEqual(computer.sequence,
                     ['getFullComputerInformation', 'availableComputerPartition',
                      'startedComputerPartition'])
    self.assertEqual(instance.state,'started')

    computer.sequence = []
    instance.requested_state = 'stopped'
    instance.software.setBuildout("""#!/bin/sh
exit 1
""")
    self.assertEqual(self.launchSlapgrid(), slapgrid.SLAPGRID_FAIL)
    self.assertSortedListEqual(os.listdir(self.instance_root),
                               ['0', 'etc', 'var'])
    self.assertSortedListEqual(
      os.listdir(instance.partition_path),
      ['.0_wrapper.log', '.0_wrapper.log.1', 'worked', 'buildout.cfg', 'etc'])
    tries = 50
    expected_text = 'Signal handler called with signal 15'
    while tries > 0:
      tries -= 1
      found = expected_text in open(wrapper_log, 'r').read()
      if found:
        break
      time.sleep(0.1)
    self.assertTrue(found)
    self.assertEqual(computer.sequence,
                     ['getFullComputerInformation',
                      'softwareInstanceError'])
    self.assertEqual(instance.state, 'started')

653 654

  def test_one_partition_stopped_started(self):
Cédric Le Ninivin's avatar
Cédric Le Ninivin committed
655 656 657 658
    computer = ComputerForTest(self.software_root,self.instance_root)
    instance = computer.instance_list[0]
    instance.requested_state = 'stopped'
    instance.software.setBuildout(WRAPPER_CONTENT)
659
    self.assertEqual(self.grid.processComputerPartitionList(), slapgrid.SLAPGRID_SUCCESS)
Cédric Le Ninivin's avatar
Cédric Le Ninivin committed
660

661 662 663 664 665 666
    self.assertSortedListEqual(os.listdir(self.instance_root), ['0', 'etc',
      'var'])
    partition = os.path.join(self.instance_root, '0')
    self.assertSortedListEqual(os.listdir(partition), ['worked', 'etc',
      'buildout.cfg'])
    self.assertSortedListEqual(os.listdir(self.software_root),
Cédric Le Ninivin's avatar
Cédric Le Ninivin committed
667 668
      [instance.software.software_hash])
    self.assertEqual(computer.sequence,
669 670
                     ['getFullComputerInformation', 'availableComputerPartition',
                      'stoppedComputerPartition'])
Cédric Le Ninivin's avatar
Cédric Le Ninivin committed
671
    self.assertEqual('stopped',instance.state)
672

Cédric Le Ninivin's avatar
Cédric Le Ninivin committed
673 674
    instance.requested_state = 'started'
    computer.sequence = []
675
    self.assertEqual(self.launchSlapgrid(), slapgrid.SLAPGRID_SUCCESS)
676 677 678 679 680
    self.assertSortedListEqual(os.listdir(self.instance_root), ['0', 'etc',
      'var'])
    partition = os.path.join(self.instance_root, '0')
    self.assertSortedListEqual(os.listdir(partition), ['.0_wrapper.log',
      'worked', 'etc', 'buildout.cfg'])
681
    self.assertSortedListEqual(os.listdir(self.software_root),
Cédric Le Ninivin's avatar
Cédric Le Ninivin committed
682
      [instance.software.software_hash])
683
    tries = 50
Cédric Le Ninivin's avatar
Cédric Le Ninivin committed
684
    wrapper_log = os.path.join(instance.partition_path, '.0_wrapper.log')
685 686 687 688
    while tries > 0:
      tries -= 1
      if os.path.getsize(wrapper_log) > 0:
        break
689
      time.sleep(0.1)
690
    self.assertTrue('Working' in open(wrapper_log, 'r').read())
Cédric Le Ninivin's avatar
Cédric Le Ninivin committed
691
    self.assertEqual(computer.sequence,
692 693
                     ['getFullComputerInformation', 'availableComputerPartition',
                      'startedComputerPartition'])
Cédric Le Ninivin's avatar
Cédric Le Ninivin committed
694
    self.assertEqual('started',instance.state)
695

696 697 698 699 700 701 702 703 704 705 706 707 708 709 710
  def test_one_partition_destroyed(self):
    """
    Test that an existing partition with "destroyed" status will only be
    stopped by slapgrid-cp, not processed
    """
    computer = ComputerForTest(self.software_root, self.instance_root)
    instance = computer.instance_list[0]
    instance.requested_state = 'destroyed'
    dummy_file_name = 'dummy_file'
    dummy_file = open(
        os.path.join(instance.partition_path, dummy_file_name),
        'w')
    dummy_file.write('dummy')
    dummy_file.close()

711
    self.assertEqual(self.grid.processComputerPartitionList(), slapgrid.SLAPGRID_SUCCESS)
712 713 714 715 716 717 718 719 720 721 722 723

    self.assertSortedListEqual(os.listdir(self.instance_root), ['0', 'etc',
      'var'])
    partition = os.path.join(self.instance_root, '0')
    self.assertSortedListEqual(os.listdir(partition), [dummy_file_name])
    self.assertSortedListEqual(os.listdir(self.software_root),
      [instance.software.software_hash])
    self.assertEqual(computer.sequence,
                     ['getFullComputerInformation',
                      'stoppedComputerPartition'])
    self.assertEqual('stopped', instance.state)

724

725 726
class TestSlapgridCPWithMasterWatchdog(MasterMixin, unittest.TestCase):

727 728 729 730 731 732 733 734 735 736 737 738 739
  def setUp(self):
    MasterMixin.setUp(self)
    # Prepare watchdog
    self.watchdog_banged = os.path.join(self._tempdir, 'watchdog_banged')
    watchdog_path = os.path.join(self._tempdir, 'watchdog')
    open(watchdog_path, 'w').write(
      WATCHDOG_TEMPLATE % dict(python_path=sys.executable,
                               sys_path=sys.path,
                               watchdog_banged=self.watchdog_banged))
    os.chmod(watchdog_path, 0755)
    self.grid.watchdog_path = watchdog_path
    slapos.grid.slapgrid.WATCHDOG_PATH = watchdog_path

740 741 742 743 744 745 746 747 748 749 750 751
  def test_one_failing_daemon_in_service_will_bang_with_watchdog(self):
    """
    Check that a failing service watched by watchdog trigger bang
    1.Prepare computer and set a service named daemon in etc/service
       (to be watched by watchdog). This daemon will fail.
    2.Prepare file for supervisord to call watchdog
       -Set sys.path
       -Monkeypatch computer partition bang
    3.Check damemon is launched
    4.Wait for it to fail
    5.Wait for file generated by monkeypacthed bang to appear
    """
Cédric de Saint Martin's avatar
PEP8  
Cédric de Saint Martin committed
752
    computer = ComputerForTest(self.software_root, self.instance_root)
753 754 755
    partition = computer.instance_list[0]
    partition.requested_state = 'started'
    partition.software.setBuildout(DAEMON_CONTENT)
756

757
    self.assertEqual(self.grid.processComputerPartitionList(), slapgrid.SLAPGRID_SUCCESS)
758 759
    self.assertSortedListEqual(os.listdir(self.instance_root), ['0', 'etc',
      'var'])
Cédric de Saint Martin's avatar
PEP8  
Cédric de Saint Martin committed
760 761 762
    self.assertSortedListEqual(
        os.listdir(partition.partition_path),
        ['.0_daemon.log','worked', 'buildout.cfg', 'etc'])
763
    tries = 200
764 765 766 767 768
    daemon_log = os.path.join(partition.partition_path, '.0_daemon.log')
    while tries > 0:
      tries -= 1
      if os.path.getsize(daemon_log) > 0:
        break
769
      time.sleep(0.1)
770
    self.assertTrue('Failing' in open(daemon_log, 'r').read())
771
    tries = 200
772 773
    while tries > 0:
      tries -= 1
774
      if os.path.exists(self.watchdog_banged):
775
        break
776
      time.sleep(0.1)
777 778
    self.assertTrue(os.path.exists(self.watchdog_banged))
    self.assertTrue('daemon' in open(self.watchdog_banged, 'r').read())
779 780 781 782 783

  RUN_CONTENT = """#!/bin/sh
mkdir -p etc/run &&
echo "#!/bin/sh" > etc/run/daemon &&
echo "touch launched
784
touch ./crashed; echo "Failing\\nFailing\\n"; sleep 1; exit 111;
785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805
" >> etc/run/daemon &&
chmod 755 etc/run/daemon &&
touch worked
"""

  def test_one_failing_daemon_in_run_will_not_bang_with_watchdog(self):
    """
    Check that a failing service watched by watchdog trigger bang
    1.Prepare computer and set a service named daemon in etc/run
       (not watched by watchdog). This daemon will fail.
    2.Prepare file for supervisord to call watchdog
       -Set sys.path
       -Monkeypatch computer partition bang
    3.Check damemon is launched
    4.Wait for it to fail
    5.Check that file generated by monkeypacthed bang do not appear
    """
    computer = ComputerForTest(self.software_root,self.instance_root)
    partition = computer.instance_list[0]
    partition.requested_state = 'started'
    partition.software.setBuildout(self.RUN_CONTENT)
806

807
    self.assertEqual(self.grid.processComputerPartitionList(), slapgrid.SLAPGRID_SUCCESS)
808 809 810 811
    self.assertSortedListEqual(os.listdir(self.instance_root), ['0', 'etc',
      'var'])
    self.assertSortedListEqual(os.listdir(partition.partition_path),
                               ['.0_daemon.log','worked', 'buildout.cfg', 'etc'])
812
    tries = 200
813 814 815 816 817
    daemon_log = os.path.join(partition.partition_path, '.0_daemon.log')
    while tries > 0:
      tries -= 1
      if os.path.getsize(daemon_log) > 0:
        break
818
      time.sleep(0.1)
819
    time.sleep(1)
820
    self.assertTrue('Failing' in open(daemon_log, 'r').read())
821
    tries = 200
822 823
    while tries > 0:
      tries -= 1
824
      if os.path.exists(self.watchdog_banged):
825
        break
826
      time.sleep(0.1)
827
    self.assertFalse(os.path.exists(self.watchdog_banged))
828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894


  def test_watched_by_watchdog_bang(self):
    """
    Test that a process going to fatal or exited mode in supervisord
    is banged if watched by watchdog
    (ie: watchdog id in process name)
    """
    computer = ComputerForTest(self.software_root,self.instance_root)
    instance = computer.instance_list[0]

    watchdog = Watchdog(dict(master_url=self.master_url,
                             computer_id=self.computer_id,
                             key_file=None,
                             cert_file=None))
    for event in watchdog.process_state_events:
      instance.sequence = []
      headers = dict(eventname=event)
      payload = "processname:%s groupname:%s from_state:RUNNING"\
          % ('daemon'+getWatchdogID(),instance.name)
      watchdog.handle_event(headers,payload)
      self.assertEqual(instance.sequence,['softwareInstanceBang'])

  def test_unwanted_events_will_not_bang(self):
    """
    Test that a process going to a mode not watched by watchdog
    in supervisord is not banged if watched by watchdog
    """
    computer = ComputerForTest(self.software_root,self.instance_root)
    instance = computer.instance_list[0]

    watchdog = Watchdog(dict(master_url=self.master_url,
                             computer_id=self.computer_id,
                             key_file=None,
                             cert_file=None))
    for event in ['EVENT', 'PROCESS_STATE', 'PROCESS_STATE_RUNNING',
                  'PROCESS_STATE_BACKOFF', 'PROCESS_STATE_STOPPED']:
      computer.sequence = []
      headers = dict(eventname=event)
      payload = "processname:%s groupname:%s from_state:RUNNING"\
          % ('daemon'+getWatchdogID(),instance.name)
      watchdog.handle_event(headers,payload)
      self.assertEqual(instance.sequence,[])


  def test_not_watched_by_watchdog_do_not_bang(self):
    """
    Test that a process going to fatal or exited mode in supervisord
    is not banged if not watched by watchdog
    (ie: no watchdog id in process name)
    """
    computer = ComputerForTest(self.software_root,self.instance_root)
    instance = computer.instance_list[0]

    watchdog = Watchdog(dict(master_url=self.master_url,
                             computer_id=self.computer_id,
                             key_file=None,
                             cert_file=None))
    for event in watchdog.process_state_events:
      computer.sequence = []
      headers = dict(eventname=event)
      payload = "processname:%s groupname:%s from_state:RUNNING"\
          % ('daemon',instance.name)
      watchdog.handle_event(headers,payload)
      self.assertEqual(computer.sequence,[])


895
class TestSlapgridCPPartitionProcessing (MasterMixin, unittest.TestCase):
896

897
  def test_partition_timestamp(self):
Cédric Le Ninivin's avatar
Cédric Le Ninivin committed
898 899 900 901
    computer = ComputerForTest(self.software_root,self.instance_root)
    instance = computer.instance_list[0]
    timestamp = str(int(time.time()))
    instance.timestamp = timestamp
Cédric Le Ninivin's avatar
Cédric Le Ninivin committed
902

903
    self.assertEqual(self.grid.processComputerPartitionList(), slapgrid.SLAPGRID_SUCCESS)
Cédric Le Ninivin's avatar
Cédric Le Ninivin committed
904 905
    self.assertSortedListEqual(os.listdir(self.instance_root), ['0', 'etc',
      'var'])
906
    partition = os.path.join(self.instance_root, '0')
Cédric de Saint Martin's avatar
Cédric de Saint Martin committed
907 908 909
    self.assertSortedListEqual(
        os.listdir(partition), ['.timestamp', 'worked', 'buildout.cfg'])
    self.assertSortedListEqual(
Cédric Le Ninivin's avatar
Cédric Le Ninivin committed
910 911
        os.listdir(self.software_root), [instance.software.software_hash])
    timestamp_path = os.path.join(instance.partition_path, '.timestamp')
912
    self.setSlapgrid()
913
    self.assertEqual(self.grid.processComputerPartitionList(), slapgrid.SLAPGRID_SUCCESS)
Cédric Le Ninivin's avatar
Cédric Le Ninivin committed
914 915 916 917
    self.assertTrue(timestamp in open(timestamp_path,'r').read())
    self.assertEqual(instance.sequence,
                     ['availableComputerPartition',
                      'stoppedComputerPartition'])
918

Cédric Le Ninivin's avatar
Cédric Le Ninivin committed
919

920
  def test_partition_timestamp_develop(self):
Cédric Le Ninivin's avatar
Cédric Le Ninivin committed
921 922 923 924
    computer = ComputerForTest(self.software_root,self.instance_root)
    instance = computer.instance_list[0]
    timestamp = str(int(time.time()))
    instance.timestamp = timestamp
925

926
    self.assertEqual(self.grid.processComputerPartitionList(), slapgrid.SLAPGRID_SUCCESS)
927 928 929
    self.assertSortedListEqual(os.listdir(self.instance_root), ['0', 'etc',
      'var'])
    partition = os.path.join(self.instance_root, '0')
Cédric de Saint Martin's avatar
Cédric de Saint Martin committed
930 931 932
    self.assertSortedListEqual(
        os.listdir(partition), ['.timestamp','worked', 'buildout.cfg'])
    self.assertSortedListEqual(
Cédric Le Ninivin's avatar
Cédric Le Ninivin committed
933
        os.listdir(self.software_root), [instance.software.software_hash])
934

935 936 937
    self.assertEqual(self.launchSlapgrid(develop=True),
                     slapgrid.SLAPGRID_SUCCESS)
    self.assertEqual(self.launchSlapgrid(), slapgrid.SLAPGRID_SUCCESS)
Cédric Le Ninivin's avatar
Cédric Le Ninivin committed
938

Cédric Le Ninivin's avatar
Cédric Le Ninivin committed
939 940 941
    self.assertEqual(instance.sequence,
                     ['availableComputerPartition', 'stoppedComputerPartition',
                      'availableComputerPartition','stoppedComputerPartition'])
Cédric Le Ninivin's avatar
Cédric Le Ninivin committed
942 943

  def test_partition_old_timestamp(self):
Cédric Le Ninivin's avatar
Cédric Le Ninivin committed
944 945 946 947
    computer = ComputerForTest(self.software_root,self.instance_root)
    instance = computer.instance_list[0]
    timestamp = str(int(time.time()))
    instance.timestamp = timestamp
Cédric Le Ninivin's avatar
Cédric Le Ninivin committed
948

949
    self.assertEqual(self.grid.processComputerPartitionList(), slapgrid.SLAPGRID_SUCCESS)
Cédric Le Ninivin's avatar
Cédric Le Ninivin committed
950 951 952
    self.assertSortedListEqual(os.listdir(self.instance_root), ['0', 'etc',
      'var'])
    partition = os.path.join(self.instance_root, '0')
Cédric de Saint Martin's avatar
Cédric de Saint Martin committed
953 954
    self.assertSortedListEqual(os.listdir(partition),
                               ['.timestamp','worked', 'buildout.cfg'])
Cédric Le Ninivin's avatar
Cédric Le Ninivin committed
955
    self.assertSortedListEqual(os.listdir(self.software_root),
Cédric Le Ninivin's avatar
Cédric Le Ninivin committed
956 957
      [instance.software.software_hash])
    instance.timestamp = str(int(timestamp) - 1)
958
    self.assertEqual(self.launchSlapgrid(), slapgrid.SLAPGRID_SUCCESS)
Cédric Le Ninivin's avatar
Cédric Le Ninivin committed
959 960
    self.assertEqual(instance.sequence,
                     ['availableComputerPartition', 'stoppedComputerPartition'])
Cédric Le Ninivin's avatar
Cédric Le Ninivin committed
961 962 963


  def test_partition_timestamp_new_timestamp(self):
Cédric Le Ninivin's avatar
Cédric Le Ninivin committed
964 965 966 967
    computer = ComputerForTest(self.software_root,self.instance_root)
    instance = computer.instance_list[0]
    timestamp = str(int(time.time()))
    instance.timestamp = timestamp
Cédric Le Ninivin's avatar
Cédric Le Ninivin committed
968

969
    self.assertEqual(self.launchSlapgrid(), slapgrid.SLAPGRID_SUCCESS)
Cédric Le Ninivin's avatar
Cédric Le Ninivin committed
970 971 972
    self.assertSortedListEqual(os.listdir(self.instance_root), ['0', 'etc',
      'var'])
    partition = os.path.join(self.instance_root, '0')
Cédric de Saint Martin's avatar
Cédric de Saint Martin committed
973 974
    self.assertSortedListEqual(os.listdir(partition),
                               ['.timestamp','worked', 'buildout.cfg'])
Cédric Le Ninivin's avatar
Cédric Le Ninivin committed
975
    self.assertSortedListEqual(os.listdir(self.software_root),
Cédric Le Ninivin's avatar
Cédric Le Ninivin committed
976 977
      [instance.software.software_hash])
    instance.timestamp = str(int(timestamp)+1)
978 979
    self.assertEqual(self.launchSlapgrid(), slapgrid.SLAPGRID_SUCCESS)
    self.assertEqual(self.launchSlapgrid(), slapgrid.SLAPGRID_SUCCESS)
Cédric Le Ninivin's avatar
Cédric Le Ninivin committed
980
    self.assertEqual(computer.sequence,
Cédric Le Ninivin's avatar
Cédric Le Ninivin committed
981 982 983 984 985 986
                     ['getFullComputerInformation', 'availableComputerPartition',
                      'stoppedComputerPartition', 'getFullComputerInformation',
                      'availableComputerPartition','stoppedComputerPartition',
                      'getFullComputerInformation'])

  def test_partition_timestamp_no_timestamp(self):
Cédric Le Ninivin's avatar
Cédric Le Ninivin committed
987 988 989 990
    computer = ComputerForTest(self.software_root,self.instance_root)
    instance = computer.instance_list[0]
    timestamp = str(int(time.time()))
    instance.timestamp = timestamp
Cédric Le Ninivin's avatar
Cédric Le Ninivin committed
991

Cédric Le Ninivin's avatar
Cédric Le Ninivin committed
992
    self.launchSlapgrid()
Cédric Le Ninivin's avatar
Cédric Le Ninivin committed
993 994 995
    self.assertSortedListEqual(os.listdir(self.instance_root), ['0', 'etc',
      'var'])
    partition = os.path.join(self.instance_root, '0')
Cédric de Saint Martin's avatar
Cédric de Saint Martin committed
996 997
    self.assertSortedListEqual(os.listdir(partition),
                               ['.timestamp','worked', 'buildout.cfg'])
Cédric Le Ninivin's avatar
Cédric Le Ninivin committed
998
    self.assertSortedListEqual(os.listdir(self.software_root),
Cédric Le Ninivin's avatar
Cédric Le Ninivin committed
999 1000 1001 1002
      [instance.software.software_hash])
    instance.timestamp = None
    self.launchSlapgrid()
    self.assertEqual(computer.sequence,
1003 1004 1005 1006 1007
                     ['getFullComputerInformation', 'availableComputerPartition',
                      'stoppedComputerPartition', 'getFullComputerInformation',
                      'availableComputerPartition','stoppedComputerPartition',])


1008 1009 1010 1011 1012 1013 1014 1015 1016 1017
  def test_partition_periodicity_is_not_overloaded_if_forced(self):
    """
    If periodicity file in software directory but periodicity is forced
    periodicity will be the one given by parameter
    1. We set force_periodicity parameter to True
    2. We put a periodicity file in the software release directory
        with an unwanted periodicity
    3. We process partition list and wait more than unwanted periodicity
    4. We relaunch, partition should not be processed
    """
Cédric Le Ninivin's avatar
Cédric Le Ninivin committed
1018 1019 1020
    computer = ComputerForTest(self.software_root,self.instance_root)
    instance = computer.instance_list[0]
    timestamp = str(int(time.time()))
1021

Cédric Le Ninivin's avatar
Cédric Le Ninivin committed
1022
    instance.timestamp = timestamp
1023
    unwanted_periodicity = 2
Cédric Le Ninivin's avatar
Cédric Le Ninivin committed
1024
    instance.software.setPeriodicity(unwanted_periodicity)
1025 1026
    self.grid.force_periodicity = True

Cédric Le Ninivin's avatar
Cédric Le Ninivin committed
1027
    self.launchSlapgrid()
1028 1029 1030 1031
    time.sleep(unwanted_periodicity + 1)

    self.setSlapgrid()
    self.grid.force_periodicity = True
1032
    self.assertEqual(self.grid.processComputerPartitionList(), slapgrid.SLAPGRID_SUCCESS)
1033
    self.assertNotEqual(unwanted_periodicity,self.grid.maximum_periodicity)
Cédric Le Ninivin's avatar
Cédric Le Ninivin committed
1034
    self.assertEqual(computer.sequence,
1035 1036 1037 1038 1039 1040 1041 1042 1043
                     ['getFullComputerInformation', 'availableComputerPartition',
                      'stoppedComputerPartition', 'getFullComputerInformation'])


  def test_one_partition_periodicity_from_file_does_not_disturb_others(self):
    """
    If time between last processing of instance and now is superior
    to periodicity then instance should be proceed
    1. We set a wanted maximum_periodicity in periodicity file in
1044
        in one software release directory and not the other one
1045 1046 1047
    2. We process computer partition and check if wanted_periodicity was
        used as maximum_periodicty
    3. We wait for a time superior to wanted_periodicty
1048 1049
    4. We launch processComputerPartition and check that partition using
        software with periodicity was runned and not the other
1050 1051
    5. We check that modification time of .timestamp was modified
    """
1052 1053
    computer = ComputerForTest(self.software_root,self.instance_root,20,20)
    instance0 = computer.instance_list[0]
Cédric Le Ninivin's avatar
Cédric Le Ninivin committed
1054
    timestamp = str(int(time.time()-5))
1055 1056 1057 1058 1059
    instance0.timestamp = timestamp
    for instance in computer.instance_list[1:]:
      instance.software = \
          computer.software_list[computer.instance_list.index(instance)]
      instance.timestamp = timestamp
Cédric Le Ninivin's avatar
Cédric Le Ninivin committed
1060

1061
    wanted_periodicity = 3
1062
    instance0.software.setPeriodicity(wanted_periodicity)
Cédric Le Ninivin's avatar
Cédric Le Ninivin committed
1063 1064

    self.launchSlapgrid()
1065
    self.assertNotEqual(wanted_periodicity,self.grid.maximum_periodicity)
Cédric Le Ninivin's avatar
Cédric Le Ninivin committed
1066
    last_runtime = os.path.getmtime(
1067
      os.path.join(instance0.partition_path, '.timestamp'))
1068
    time.sleep(wanted_periodicity + 1)
1069 1070 1071
    for instance in computer.instance_list[1:]:
      self.assertEqual(instance.sequence,
                       ['availableComputerPartition', 'stoppedComputerPartition'])
Cédric Le Ninivin's avatar
Cédric Le Ninivin committed
1072
    self.launchSlapgrid()
1073 1074
    self.assertEqual(instance0.sequence,
                     ['availableComputerPartition', 'stoppedComputerPartition',
Cédric Le Ninivin's avatar
Cédric Le Ninivin committed
1075
                      'availableComputerPartition', 'stoppedComputerPartition',
1076
                      ])
1077 1078 1079
    for instance in computer.instance_list[1:]:
      self.assertEqual(instance.sequence,
                       ['availableComputerPartition', 'stoppedComputerPartition'])
1080
    self.assertGreater(
1081
      os.path.getmtime(os.path.join(instance0.partition_path,'.timestamp')),
1082 1083 1084 1085
      last_runtime)
    self.assertNotEqual(wanted_periodicity,self.grid.maximum_periodicity)


1086 1087 1088 1089 1090 1091 1092 1093 1094 1095
  def test_one_partition_buildout_fail_does_not_disturb_others(self):
    """
    1. We set up two instance one using a corrupted buildout
    2. One will fail but the other one will be processed correctly
    """
    computer = ComputerForTest(self.software_root,self.instance_root,2,2)
    instance0 = computer.instance_list[0]
    instance1 = computer.instance_list[1]
    instance1.software = computer.software_list[1]
    instance0.software.setBuildout("""#!/bin/sh
1096
exit 42""")
1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136
    self.launchSlapgrid()
    self.assertEqual(instance0.sequence,
                     ['softwareInstanceError'])
    self.assertEqual(instance1.sequence,
                     ['availableComputerPartition', 'stoppedComputerPartition'])

  def test_one_partition_lacking_software_path_does_not_disturb_others(self):
    """
    1. We set up two instance but remove software path of one
    2. One will fail but the other one will be processed correctly
    """
    computer = ComputerForTest(self.software_root,self.instance_root,2,2)
    instance0 = computer.instance_list[0]
    instance1 = computer.instance_list[1]
    instance1.software = computer.software_list[1]
    shutil.rmtree(instance0.software.srdir)
    self.launchSlapgrid()
    self.assertEqual(instance0.sequence,
                     ['softwareInstanceError'])
    self.assertEqual(instance1.sequence,
                     ['availableComputerPartition', 'stoppedComputerPartition'])

  def test_one_partition_lacking_software_bin_path_does_not_disturb_others(self):
    """
    1. We set up two instance but remove software bin path of one
    2. One will fail but the other one will be processed correctly
    """
    computer = ComputerForTest(self.software_root,self.instance_root,2,2)
    instance0 = computer.instance_list[0]
    instance1 = computer.instance_list[1]
    instance1.software = computer.software_list[1]
    shutil.rmtree(instance0.software.srbindir)
    self.launchSlapgrid()
    self.assertEqual(instance0.sequence,
                     ['softwareInstanceError'])
    self.assertEqual(instance1.sequence,
                     ['availableComputerPartition', 'stoppedComputerPartition'])

  def test_one_partition_lacking_path_does_not_disturb_others(self):
    """
1137
    1. We set up two instances but remove path of one
1138 1139
    2. One will fail but the other one will be processed correctly
    """
1140
    computer = ComputerForTest(self.software_root,self.instance_root, 2, 2)
1141 1142 1143 1144 1145 1146 1147 1148 1149 1150
    instance0 = computer.instance_list[0]
    instance1 = computer.instance_list[1]
    instance1.software = computer.software_list[1]
    shutil.rmtree(instance0.partition_path)
    self.launchSlapgrid()
    self.assertEqual(instance0.sequence,
                     ['softwareInstanceError'])
    self.assertEqual(instance1.sequence,
                     ['availableComputerPartition', 'stoppedComputerPartition'])

1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168
  def test_one_partition_buildout_fail_is_correctly_logged(self):
    """
    1. We set up an instance using a corrupted buildout
    2. It will fail, make sure that whole log is sent to master
    """
    computer = ComputerForTest(self.software_root, self.instance_root, 1, 1)
    instance = computer.instance_list[0]

    line1 = "Nerdy kitten: Can I has a process crash?"
    line2 = "Cedric: Sure, here it is."
    instance.software.setBuildout("""#!/bin/sh
echo %s; echo %s; exit 42""" % (line1, line2))
    self.launchSlapgrid()
    self.assertEqual(instance.sequence, ['softwareInstanceError'])
    # We don't care of actual formatting, we just want to have full log
    self.assertTrue(line1 in instance.error_log)
    self.assertTrue(line2 in instance.error_log)
    self.assertTrue("Failed to run buildout" in instance.error_log)
1169

1170 1171 1172 1173 1174 1175 1176 1177 1178
class TestSlapgridUsageReport(MasterMixin, unittest.TestCase):
  """
  Test suite about slapgrid-ur
  """

  def test_slapgrid_destroys_instance_to_be_destroyed(self):
    """
    Test than an instance in "destroyed" state is correctly destroyed
    """
1179 1180 1181 1182
    computer = ComputerForTest(self.software_root,self.instance_root)
    instance = computer.instance_list[0]
    instance.requested_state = 'started'
    instance.software.setBuildout(WRAPPER_CONTENT)
1183
    self.assertEqual(self.grid.processComputerPartitionList(), slapgrid.SLAPGRID_SUCCESS)
1184 1185
    self.assertSortedListEqual(os.listdir(self.instance_root), ['0', 'etc',
      'var'])
1186
    self.assertSortedListEqual(os.listdir(instance.partition_path), ['.0_wrapper.log',
1187
      'worked', 'buildout.cfg', 'etc'])
1188
    tries = 50
1189
    wrapper_log = os.path.join(instance.partition_path, '.0_wrapper.log')
1190 1191 1192 1193
    while tries > 0:
      tries -= 1
      if os.path.getsize(wrapper_log) > 0:
        break
1194
      time.sleep(0.1)
1195 1196
    self.assertTrue('Working' in open(wrapper_log, 'r').read())
    self.assertSortedListEqual(os.listdir(self.software_root),
1197 1198
      [instance.software.software_hash])
    self.assertEqual(computer.sequence,
1199 1200 1201
                     ['getFullComputerInformation',
                      'availableComputerPartition',
                      'startedComputerPartition'])
1202
    self.assertEqual(instance.state,'started')
1203 1204

    # Then destroy the instance
1205 1206
    computer.sequence = []
    instance.requested_state = 'destroyed'
1207
    self.assertEqual(self.grid.agregateAndSendUsage(), slapgrid.SLAPGRID_SUCCESS)
1208 1209 1210
    # Assert partition directory is empty
    self.assertSortedListEqual(os.listdir(self.instance_root),
                               ['0', 'etc', 'var'])
1211
    self.assertSortedListEqual(os.listdir(instance.partition_path), [])
1212
    self.assertSortedListEqual(os.listdir(self.software_root),
1213
                               [instance.software.software_hash])
1214
    # Assert supervisor stopped process
1215
    tries = 50
1216
    wrapper_log = os.path.join(instance.partition_path, '.0_wrapper.log')
1217 1218 1219 1220 1221 1222
    exists = False
    while tries > 0:
      tries -= 1
      if os.path.exists(wrapper_log):
        exists = True
        break
1223
      time.sleep(0.1)
1224 1225
    self.assertFalse(exists)

1226
    self.assertEqual(computer.sequence,
1227 1228 1229
                     ['getFullComputerInformation',
                      'stoppedComputerPartition',
                      'destroyedComputerPartition'])
1230
    self.assertEqual(instance.state,'destroyed')
1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243

  def test_slapgrid_destroys_instance_to_be_destroyed_without_sr_uri(self):
    """
    Test than an instance in "destroyed" state but without SR informations
    is correctly destroyed
    """
    computer = ComputerForTest(self.software_root,self.instance_root)
    instance = computer.instance_list[0]

    instance.software.name = None

    computer.sequence = []
    instance.requested_state = 'destroyed'
1244
    self.assertEqual(self.grid.agregateAndSendUsage(), slapgrid.SLAPGRID_SUCCESS)
1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263
    # Assert partition directory is empty
    self.assertSortedListEqual(os.listdir(self.instance_root),
                               ['0', 'etc', 'var'])
    self.assertSortedListEqual(os.listdir(instance.partition_path), [])
    self.assertSortedListEqual(os.listdir(self.software_root),
                               [instance.software.software_hash])
    # Assert supervisor stopped process
    tries = 50
    wrapper_log = os.path.join(instance.partition_path, '.0_wrapper.log')
    exists = False
    while tries > 0:
      tries -= 1
      if os.path.exists(wrapper_log):
        exists = True
        break
      time.sleep(0.1)
    self.assertFalse(exists)

    self.assertEqual(computer.sequence,
1264
                     ['getFullComputerInformation'])
1265

1266
  def test_slapgrid_ignore_destroyed_instance_without_sr(self):
1267 1268
    """
    Test than an instance in "destroyed" state but without SR at all
1269
    is correctly ignored
1270 1271 1272 1273 1274 1275 1276 1277
    """
    computer = ComputerForTest(self.software_root,self.instance_root)
    instance = computer.instance_list[0]

    instance.software = None

    computer.sequence = []
    instance.requested_state = 'destroyed'
1278
    self.assertEqual(self.grid.agregateAndSendUsage(), slapgrid.SLAPGRID_SUCCESS)
1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295
    # Assert partition directory is empty
    self.assertSortedListEqual(os.listdir(self.instance_root),
                               ['0', 'etc', 'var'])
    self.assertSortedListEqual(os.listdir(instance.partition_path), [])
    # Assert supervisor stopped process
    tries = 50
    wrapper_log = os.path.join(instance.partition_path, '.0_wrapper.log')
    exists = False
    while tries > 0:
      tries -= 1
      if os.path.exists(wrapper_log):
        exists = True
        break
      time.sleep(0.1)
    self.assertFalse(exists)

    self.assertEqual(computer.sequence,
1296
                     ['getFullComputerInformation'])
1297 1298 1299 1300 1301

  def test_slapgrid_not_destroy_bad_instance(self):
    """
    Checks that slapgrid-ur don't destroy instance not to be destroyed.
    """
1302 1303 1304 1305
    computer = ComputerForTest(self.software_root,self.instance_root)
    instance = computer.instance_list[0]
    instance.requested_state = 'started'
    instance.software.setBuildout(WRAPPER_CONTENT)
1306
    self.assertEqual(self.grid.processComputerPartitionList(), slapgrid.SLAPGRID_SUCCESS)
1307 1308 1309 1310
    self.assertSortedListEqual(os.listdir(self.instance_root),
                               ['0', 'etc', 'var'])
    self.assertSortedListEqual(os.listdir(instance.partition_path),
                               ['.0_wrapper.log', 'worked', 'buildout.cfg', 'etc'])
1311
    tries = 50
1312
    wrapper_log = os.path.join(instance.partition_path, '.0_wrapper.log')
1313 1314 1315 1316
    while tries > 0:
      tries -= 1
      if os.path.getsize(wrapper_log) > 0:
        break
1317
      time.sleep(0.1)
1318 1319
    self.assertTrue('Working' in open(wrapper_log, 'r').read())
    self.assertSortedListEqual(os.listdir(self.software_root),
1320 1321
      [instance.software.software_hash])
    self.assertEqual(computer.sequence,
1322 1323 1324
                     ['getFullComputerInformation',
                      'availableComputerPartition',
                      'startedComputerPartition'])
1325
    self.assertEqual('started',instance.state)
1326 1327

    # Then run usage report and see if it is still working
1328
    computer.sequence = []
1329
    self.assertEqual(self.grid.agregateAndSendUsage(), slapgrid.SLAPGRID_SUCCESS)
1330 1331
    self.assertSortedListEqual(os.listdir(self.instance_root), ['0', 'etc',
      'var'])
1332 1333 1334
    self.assertSortedListEqual(
      os.listdir(instance.partition_path),
      ['.0_wrapper.log', 'worked', 'buildout.cfg', 'etc'])
1335
    tries = 50
1336
    wrapper_log = os.path.join(instance.partition_path, '.0_wrapper.log')
1337 1338 1339 1340
    while tries > 0:
      tries -= 1
      if os.path.getsize(wrapper_log) > 0:
        break
1341
      time.sleep(0.1)
1342 1343 1344
    self.assertTrue('Working' in open(wrapper_log, 'r').read())
    self.assertSortedListEqual(os.listdir(self.instance_root), ['0', 'etc',
      'var'])
1345 1346
    self.assertSortedListEqual(os.listdir(instance.partition_path),
                               ['.0_wrapper.log', 'worked', 'buildout.cfg', 'etc'])
1347
    tries = 50
1348
    wrapper_log = os.path.join(instance.partition_path, '.0_wrapper.log')
1349 1350 1351 1352
    while tries > 0:
      tries -= 1
      if os.path.getsize(wrapper_log) > 0:
        break
1353
      time.sleep(0.1)
1354
    self.assertEqual(computer.sequence,
1355
                     ['getFullComputerInformation'])
1356
    self.assertEqual('started',instance.state)
1357

1358 1359 1360 1361 1362 1363 1364 1365 1366
  def test_slapgrid_ignore_free_instance(self):
    """
    Test than a free instance (so in "destroyed" state, but empty) is ignored.
    """
    computer = ComputerForTest(self.software_root, self.instance_root)
    instance = computer.instance_list[0]

    computer.sequence = []
    instance.requested_state = 'destroyed'
1367
    self.assertEqual(self.grid.processComputerPartitionList(), slapgrid.SLAPGRID_SUCCESS)
1368 1369 1370 1371 1372 1373 1374
    # Assert partition directory is empty
    self.assertSortedListEqual(os.listdir(self.instance_root),
                               ['0', 'etc', 'var'])
    self.assertSortedListEqual(os.listdir(instance.partition_path), [])
    self.assertSortedListEqual(os.listdir(self.software_root),
                               [instance.software.software_hash])
    self.assertEqual(computer.sequence, ['getFullComputerInformation'])
1375

1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399



class TestSlapgridSoftwareRelease(MasterMixin, unittest.TestCase):
  def test_one_software_buildout_fail_is_correctly_logged(self):
    """
    1. We set up a software using a corrupted buildout
    2. It will fail, make sure that whole log is sent to master
    """
    computer = ComputerForTest(self.software_root, self.instance_root, 1, 1)
    software = computer.software_list[0]

    line1 = "Nerdy kitten: Can I has a process crash?"
    line2 = "Cedric: Sure, here it is."
    software.setBuildout("""#!/bin/sh
echo %s; echo %s; exit 42""" % (line1, line2))
    self.launchSlapgridSoftware()
    self.assertEqual(software.sequence,
                     ['buildingSoftwareRelease', 'softwareReleaseError'])
    # We don't care of actual formatting, we just want to have full log
    self.assertTrue(line1 in software.error_log)
    self.assertTrue(line2 in software.error_log)
    self.assertTrue("Failed to run buildout" in software.error_log)

1400
class SlapgridInitialization(unittest.TestCase):
1401
  """
1402 1403
  "Abstract" class setting setup and teardown for TestSlapgridArgumentTuple
  and TestSlapgridConfigurationFile.
1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426
  """

  def setUp(self):
    """
      Create the minimun default argument and configuration.
    """
    self.certificate_repository_path = tempfile.mkdtemp()
    self.fake_file_descriptor = tempfile.NamedTemporaryFile()
    self.slapos_config_descriptor = tempfile.NamedTemporaryFile()
    self.slapos_config_descriptor.write("""
[slapos]
software_root = /opt/slapgrid
instance_root = /srv/slapgrid
master_url = https://slap.vifib.com/
computer_id = your computer id
buildout = /path/to/buildout/binary
""" % dict(fake_file=self.fake_file_descriptor.name))
    self.slapos_config_descriptor.seek(0)
    self.default_arg_tuple = (
        '--cert_file', self.fake_file_descriptor.name,
        '--key_file', self.fake_file_descriptor.name,
        '--master_ca_file', self.fake_file_descriptor.name,
        '--certificate_repository_path', self.certificate_repository_path,
1427
        '-c', self.slapos_config_descriptor.name, '--now')
1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440

    self.signature_key_file_descriptor = tempfile.NamedTemporaryFile()
    self.signature_key_file_descriptor.seek(0)

  def tearDown(self):
    """
      Removing the temp file.
    """
    self.fake_file_descriptor.close()
    self.slapos_config_descriptor.close()
    self.signature_key_file_descriptor.close()
    shutil.rmtree(self.certificate_repository_path, True)

1441 1442 1443 1444 1445
class TestSlapgridArgumentTuple(SlapgridInitialization):
  """
  Test suite about arguments given to slapgrid command.
  """

1446 1447 1448 1449 1450
  def test_empty_argument_tuple(self):
    """
      Raises if the argument list if empty and without configuration file.
    """
    parser = slapgrid.parseArgumentTupleAndReturnSlapgridObject
1451 1452
    # XXX: SystemExit is too generic exception, it is only known that
    #      something is wrong
Łukasz Nowak's avatar
Łukasz Nowak committed
1453
    self.assertRaises(SystemExit, parser, *())
1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470

  def test_default_argument_tuple(self):
    """
      Check if we can have the slapgrid object returned with the minimum
      arguments.
    """
    parser = slapgrid.parseArgumentTupleAndReturnSlapgridObject
    return_list = parser(*self.default_arg_tuple)
    self.assertEquals(2, len(return_list))

  def test_signature_private_key_file_non_exists(self):
    """
      Raises if the  signature_private_key_file does not exists.
    """
    parser = slapgrid.parseArgumentTupleAndReturnSlapgridObject
    argument_tuple = ("--signature_private_key_file", "/non/exists/path") + \
                      self.default_arg_tuple
1471 1472 1473
    # XXX: SystemExit is too generic exception, it is only known that
    #      something is wrong
    self.assertRaises(SystemExit, parser, *argument_tuple)
1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486

  def test_signature_private_key_file(self):
    """
      Check if the signature private key argument value is available on
      slapgrid object.
    """
    parser = slapgrid.parseArgumentTupleAndReturnSlapgridObject
    argument_tuple = ("--signature_private_key_file",
                      self.signature_key_file_descriptor.name) + \
                      self.default_arg_tuple
    slapgrid_object = parser(*argument_tuple)[0]
    self.assertEquals(self.signature_key_file_descriptor.name,
                          slapgrid_object.signature_private_key_file)
1487

1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506
  def test_backward_compatibility_all(self):
    """
      Check if giving --all triggers "develop" option.
    """
    parser = slapgrid.parseArgumentTupleAndReturnSlapgridObject
    argument_tuple = ("--all",) + self.default_arg_tuple
    slapgrid_object = parser(*argument_tuple)[0]
    self.assertTrue(slapgrid_object.develop)

  def test_backward_compatibility_not_all(self):
    """
      Check if not giving --all neither --develop triggers "develop"
      option to be False.
    """
    parser = slapgrid.parseArgumentTupleAndReturnSlapgridObject
    argument_tuple = self.default_arg_tuple
    slapgrid_object = parser(*argument_tuple)[0]
    self.assertFalse(slapgrid_object.develop)

1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524
  def test_force_periodicity_if_periodicity_not_given(self):
    """
      Check if not giving --maximum-periodicity triggers "force_periodicity"
      option to be false.
    """
    parser = slapgrid.parseArgumentTupleAndReturnSlapgridObject
    argument_tuple = self.default_arg_tuple
    slapgrid_object = parser(*argument_tuple)[0]
    self.assertFalse(slapgrid_object.force_periodicity)

  def test_force_periodicity_if_periodicity_given(self):
    """
      Check if giving --maximum-periodicity triggers "force_periodicity" option.
    """
    parser = slapgrid.parseArgumentTupleAndReturnSlapgridObject
    argument_tuple = ("--maximum-periodicity","40") + self.default_arg_tuple
    slapgrid_object = parser(*argument_tuple)[0]
    self.assertTrue(slapgrid_object.force_periodicity)
1525

1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 1576 1577 1578 1579 1580 1581 1582 1583 1584 1585 1586 1587 1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645
class TestSlapgridConfigurationFile(SlapgridInitialization):
  
  def test_upload_binary_cache_blacklist(self):
    """
      Check if giving --upload-to-binary-cache-url-blacklist triggers option.
    """
    self.slapos_config_descriptor.write("""
[slapos]
software_root = /opt/slapgrid
instance_root = /srv/slapgrid
master_url = https://slap.vifib.com/
computer_id = your computer id
buildout = /path/to/buildout/binary
[networkcache]
upload-to-binary-cache-url-blacklist =
  http://1
  http://2/bla
""" % dict(fake_file=self.fake_file_descriptor.name))
    self.slapos_config_descriptor.seek(0)
    slapgrid_object = slapgrid.parseArgumentTupleAndReturnSlapgridObject(
        *self.default_arg_tuple)[0]
    self.assertEqual(
        slapgrid_object.upload_to_binary_cache_url_blacklist,
        ['http://1', 'http://2/bla']
    )
    self.assertEqual(
        slapgrid_object.download_from_binary_cache_url_blacklist,
        []
    )

  def test_download_binary_cache_blacklist(self):
    """
      Check if giving --download-from-binary-cache-url-blacklist triggers option.
    """
    self.slapos_config_descriptor.write("""
[slapos]
software_root = /opt/slapgrid
instance_root = /srv/slapgrid
master_url = https://slap.vifib.com/
computer_id = your computer id
buildout = /path/to/buildout/binary
[networkcache]
download-from-binary-cache-url-blacklist =
  http://1
  http://2/bla
""" % dict(fake_file=self.fake_file_descriptor.name))
    self.slapos_config_descriptor.seek(0)
    slapgrid_object = slapgrid.parseArgumentTupleAndReturnSlapgridObject(
        *self.default_arg_tuple)[0]
    self.assertEqual(
        slapgrid_object.upload_to_binary_cache_url_blacklist,
        []
    )
    self.assertEqual(
        slapgrid_object.download_from_binary_cache_url_blacklist,
        ['http://1', 'http://2/bla']
    )

  def test_upload_download_binary_cache_blacklist(self):
    """
      Check if giving both --download-from-binary-cache-url-blacklist
      and --upload-to-binary-cache-url-blacklist triggers options.
    """
    self.slapos_config_descriptor.write("""
[slapos]
software_root = /opt/slapgrid
instance_root = /srv/slapgrid
master_url = https://slap.vifib.com/
computer_id = your computer id
buildout = /path/to/buildout/binary
[networkcache]
upload-to-binary-cache-url-blacklist =
  http://1
  http://2/bla
download-from-binary-cache-url-blacklist =
  http://3
  http://4/bla
""" % dict(fake_file=self.fake_file_descriptor.name))
    self.slapos_config_descriptor.seek(0)
    slapgrid_object = slapgrid.parseArgumentTupleAndReturnSlapgridObject(
        *self.default_arg_tuple)[0]
    self.assertEqual(
        slapgrid_object.upload_to_binary_cache_url_blacklist,
        ['http://1', 'http://2/bla']
    )
    self.assertEqual(
        slapgrid_object.download_from_binary_cache_url_blacklist,
        ['http://3', 'http://4/bla']
    )

  def test_backward_compatibility_download_binary_cache_blacklist(self):
    """
      Check if giving both --binary-cache-url-blacklist
      and --upload-to-binary-cache-blacklist triggers options.
    """
    self.slapos_config_descriptor.write("""
[slapos]
software_root = /opt/slapgrid
instance_root = /srv/slapgrid
master_url = https://slap.vifib.com/
computer_id = your computer id
buildout = /path/to/buildout/binary
[networkcache]
binary-cache-url-blacklist =
  http://1
  http://2/bla
""" % dict(fake_file=self.fake_file_descriptor.name))
    self.slapos_config_descriptor.seek(0)
    slapgrid_object = slapgrid.parseArgumentTupleAndReturnSlapgridObject(
        *self.default_arg_tuple)[0]
    self.assertEqual(
        slapgrid_object.upload_to_binary_cache_url_blacklist,
        []
    )
    self.assertEqual(
        slapgrid_object.download_from_binary_cache_url_blacklist,
        ['http://1', 'http://2/bla']
    )


1646
class TestSlapgridCPWithMasterPromise(MasterMixin, unittest.TestCase):
1647
  def test_one_failing_promise(self):
1648 1649 1650 1651 1652
    computer = ComputerForTest(self.software_root,self.instance_root)
    instance = computer.instance_list[0]
    instance.requested_state = 'started'
    worked_file = os.path.join(instance.partition_path, 'fail_worked')
    fail = ("""#!/usr/bin/env sh
1653 1654
touch "%(worked_file)s"
exit 127""" % {'worked_file': worked_file})
1655 1656 1657
    instance.setPromise('fail', fail)
    self.assertEqual(self.grid.processComputerPartitionList(),
                     slapos.grid.slapgrid.SLAPGRID_PROMISE_FAIL)
1658
    self.assertTrue(os.path.isfile(worked_file))
1659
    self.assertTrue(instance.error)
1660
    self.assertNotEqual('started', instance.state)
1661 1662

  def test_one_succeeding_promise(self):
1663 1664 1665
    computer = ComputerForTest(self.software_root,self.instance_root)
    instance = computer.instance_list[0]
    instance.requested_state = 'started'
1666
    self.fake_waiting_time = 0.1
1667 1668
    worked_file = os.path.join(instance.partition_path, 'succeed_worked')
    succeed = ("""#!/usr/bin/env sh
1669 1670
touch "%(worked_file)s"
exit 0""" % {'worked_file': worked_file})
1671 1672
    instance.setPromise('succeed', succeed)
    self.assertEqual(self.grid.processComputerPartitionList(), slapgrid.SLAPGRID_SUCCESS)
1673
    self.assertTrue(os.path.isfile(worked_file))
1674

1675
    self.assertFalse(instance.error)
1676
    self.assertEqual(instance.state, 'started')
1677 1678

  def test_stderr_has_been_sent(self):
1679 1680 1681
    computer = ComputerForTest(self.software_root,self.instance_root)
    instance = computer.instance_list[0]
    httplib.HTTPConnection._callback = computer.getServerResponse()
1682

1683
    instance.requested_state = 'started'
1684 1685
    self.fake_waiting_time = 0.5

1686
    promise_path = os.path.join(instance.partition_path, 'etc', 'promise')
1687
    os.makedirs(promise_path)
1688
    succeed = os.path.join(promise_path, 'stderr_writer')
1689
    worked_file = os.path.join(instance.partition_path, 'stderr_worked')
1690 1691
    with open(succeed, 'w') as f:
      f.write("""#!/usr/bin/env sh
1692
touch "%(worked_file)s"
1693
echo Error 1>&2
1694
exit 127""" % {'worked_file': worked_file})
1695
    os.chmod(succeed, 0777)
1696 1697
    self.assertEqual(self.grid.processComputerPartitionList(),
                     slapos.grid.slapgrid.SLAPGRID_PROMISE_FAIL)
1698
    self.assertTrue(os.path.isfile(worked_file))
1699

1700 1701 1702
    self.assertEqual(instance.error_log, 'Error')
    self.assertTrue(instance.error)
    self.assertIsNone(instance.state)
1703

1704 1705

  def test_timeout_works(self):
1706 1707
    computer = ComputerForTest(self.software_root,self.instance_root)
    instance = computer.instance_list[0]
1708

1709
    instance.requested_state = 'started'
1710
    self.fake_waiting_time = 0.1
1711

1712
    promise_path = os.path.join(instance.partition_path, 'etc', 'promise')
1713 1714
    os.makedirs(promise_path)
    succeed = os.path.join(promise_path, 'timed_out_promise')
1715
    worked_file = os.path.join(instance.partition_path, 'timed_out_worked')
1716 1717
    with open(succeed, 'w') as f:
      f.write("""#!/usr/bin/env sh
1718
touch "%(worked_file)s"
1719
sleep 5
1720
exit 0""" % {'worked_file': worked_file})
1721
    os.chmod(succeed, 0777)
1722 1723
    self.assertEqual(self.grid.processComputerPartitionList(),
                     slapos.grid.slapgrid.SLAPGRID_PROMISE_FAIL)
1724
    self.assertTrue(os.path.isfile(worked_file))
1725

1726 1727
    self.assertTrue(instance.error)
    self.assertIsNone(instance.state)
1728 1729

  def test_two_succeeding_promises(self):
1730 1731 1732
    computer = ComputerForTest(self.software_root,self.instance_root)
    instance = computer.instance_list[0]
    instance.requested_state = 'started'
1733

1734
    self.fake_waiting_time = 0.1
1735

1736 1737 1738
    for i in range (0,2):
      worked_file = os.path.join(instance.partition_path, 'succeed_%s_worked' % i)
      succeed = ("""#!/usr/bin/env sh
1739 1740
touch "%(worked_file)s"
exit 0""" % {'worked_file': worked_file})
1741
      instance.setPromise('succeed_%s' % i, succeed)
1742

1743
    self.assertEqual(self.grid.processComputerPartitionList(), slapgrid.SLAPGRID_SUCCESS)
1744 1745 1746 1747 1748
    for i in range(0,2):
      worked_file = os.path.join(instance.partition_path, 'succeed_%s_worked' % i)
      self.assertTrue(os.path.isfile(worked_file))
    self.assertFalse(instance.error)
    self.assertEqual(instance.state, 'started')
1749

1750
  def test_one_succeeding_one_failing_promises(self):
1751 1752 1753
    computer = ComputerForTest(self.software_root,self.instance_root)
    instance = computer.instance_list[0]
    instance.requested_state = 'started'
1754
    self.fake_waiting_time = 0.1
1755

1756
    for i in range(2):
1757 1758 1759
      worked_file = os.path.join(instance.partition_path, 'promise_worked_%d' % i)
      lockfile = os.path.join(instance.partition_path, 'lock')
      promise=("""#!/usr/bin/env sh
1760
touch "%(worked_file)s"
Cédric de Saint Martin's avatar
Cédric de Saint Martin committed
1761
if [ ! -f %(lockfile)s ]
1762
then
Cédric de Saint Martin's avatar
Cédric de Saint Martin committed
1763 1764 1765
  touch "%(lockfile)s"
  exit 0
else
1766 1767
  exit 127
fi""" % {'worked_file': worked_file, 'lockfile': lockfile})
1768
      instance.setPromise('promise_%s'%i,promise)
1769 1770
    self.assertEqual(self.grid.processComputerPartitionList(),
                     slapos.grid.slapgrid.SLAPGRID_PROMISE_FAIL)
1771 1772
    self.assertEquals(instance.error, 1)
    self.assertNotEqual('started',instance.state)
1773 1774

  def test_one_succeeding_one_timing_out_promises(self):
1775 1776 1777
    computer = ComputerForTest(self.software_root,self.instance_root)
    instance = computer.instance_list[0]
    instance.requested_state = 'started'
1778
    self.fake_waiting_time = 0.1
1779
    for i in range(2):
1780 1781 1782
      worked_file = os.path.join(instance.partition_path, 'promise_worked_%d' % i)
      lockfile = os.path.join(instance.partition_path, 'lock')
      promise = ("""#!/usr/bin/env sh
1783
touch "%(worked_file)s"
1784
if [ ! -f %(lockfile)s ]
1785
then
1786 1787
  touch "%(lockfile)s"
else
1788 1789 1790
  sleep 5
fi
exit 0"""  % {'worked_file': worked_file, 'lockfile': lockfile})
1791
      instance.setPromise('promise_%d' % i, promise)
1792

1793 1794
    self.assertEqual(self.grid.processComputerPartitionList(),
                     slapos.grid.slapgrid.SLAPGRID_PROMISE_FAIL)
1795

1796 1797
    self.assertEquals(instance.error, 1)
    self.assertNotEqual(instance.state,'started')