Fix Eoan.

Moved to pure Python where clib conflicts arose in using command line
tools.

Fixed erroneous assumptions about the presence and reliability of a
$HOME variable while running init.

Added tests specific to eoan, disco and xenial. They are not yet part
of the gate.

Change-Id: I2fc74fcc2ae9876442bb87a3446aef48d0428f2f
This commit is contained in:
Pete Vander Giessen 2019-11-05 03:22:42 +00:00
parent 5404a261aa
commit cb940157ab
7 changed files with 188 additions and 26 deletions

View File

@ -8,8 +8,9 @@ description: |
grade: stable grade: stable
confinement: classic confinement: classic
environment: environment:
LD_LIBRARY_PATH: $SNAP/lib:$SNAP/lib/$SNAPCRAFT_ARCH_TRIPLET:$SNAP/usr/lib:$SNAP/usr/lib/$SNAPCRAFT_ARCH_TRIPLET # Edit the following lines with tools/update_path.py
PATH: $SNAP/usr/sbin:$SNAP/usr/bin:$SNAP/sbin:$SNAP/bin:/snap/core18/current/bin:$PATH LD_LIBRARY_PATH: $SNAP/lib:$SNAP/lib/$SNAPCRAFT_ARCH_TRIPLET:$SNAP/usr/lib:$SNAP/usr/lib/$SNAPCRAFT_ARCH_TRIPLET:$SNAP/usr/lib/$SNAPCRAFT_ARCH_TRIPLET/pulseaudio
PATH: $SNAP/usr/sbin:$SNAP/usr/bin:$SNAP/sbin:$SNAP/bin:$PATH
LC_ALL: C LC_ALL: C
OS_PLACEMENT_CONFIG_DIR: $SNAP/etc/nova/ OS_PLACEMENT_CONFIG_DIR: $SNAP/etc/nova/
@ -219,8 +220,6 @@ apps:
libvirtd: libvirtd:
command: libvirtd command: libvirtd
daemon: simple daemon: simple
environment:
LD_LIBRARY_PATH: $SNAP/lib:$SNAP/lib/$SNAPCRAFT_ARCH_TRIPLET:$SNAP/usr/lib:$SNAP/usr/lib/$SNAPCRAFT_ARCH_TRIPLET:$SNAP/usr/lib/$SNAPCRAFT_ARCH_TRIPLET/pulseaudio
virtlogd: virtlogd:
command: virtlogd command: virtlogd
daemon: simple daemon: simple

View File

@ -72,10 +72,11 @@ class Framework(unittest.TestCase):
self.MACHINE = petname.generate() self.MACHINE = petname.generate()
self.PREFIX = ['multipass', 'exec', self.MACHINE, '--'] self.PREFIX = ['multipass', 'exec', self.MACHINE, '--']
distro = os.environ.get('DISTRO') or self.DISTRO
check('sudo', 'snap', 'install', '--classic', '--edge', 'multipass') check('sudo', 'snap', 'install', '--classic', '--edge', 'multipass')
check('multipass', 'launch', '--cpus', '2', '--mem', '8G', self.DISTRO, check('multipass', 'launch', '--cpus', '2', '--mem', '8G', distro,
'--name', self.MACHINE) '--name', self.MACHINE)
check('multipass', 'copy-files', self.SNAP, '{}:'.format(self.MACHINE)) check('multipass', 'copy-files', self.SNAP, '{}:'.format(self.MACHINE))

View File

@ -14,8 +14,10 @@ Web IDE.
""" """
import json
import os import os
import sys import sys
import time
import unittest import unittest
import xvfbwrapper import xvfbwrapper
from selenium import webdriver from selenium import webdriver
@ -50,7 +52,7 @@ class TestBasics(Framework):
""" """
launch = '/snap/bin/microstack.launch' launch = '/snap/bin/microstack.launch'
# openstack = '/snap/bin/microstack.openstack' openstack = '/snap/bin/microstack.openstack'
print("Testing microstack.launch ...") print("Testing microstack.launch ...")
@ -66,6 +68,55 @@ class TestBasics(Framework):
# Endpoints should not contain localhost # Endpoints should not contain localhost
self.assertFalse("localhost" in endpoints) self.assertFalse("localhost" in endpoints)
if 'multipass' in self.PREFIX:
# Verify that microstack.launch completed successfully
# Skip these tests in the gate, as they are not reliable there.
# TODO: fix these in the gate!
# Ping the instance
ip = None
servers = check_output(*self.PREFIX, openstack,
'server', 'list', '--format', 'json')
servers = json.loads(servers)
for server in servers:
if server['Name'] == 'breakfast':
ip = server['Networks'].split(",")[1].strip()
break
self.assertTrue(ip)
pings = 1
max_pings = 600 # ~10 minutes!
while not call(*self.PREFIX, 'ping', '-c1', '-w1', ip):
pings += 1
if pings > max_pings:
self.assertFalse(True, msg='Max pings reached!')
print("Testing instances' ability to connect to the Internet")
# Test Internet connectivity
attempts = 1
max_attempts = 300 # ~10 minutes!
username = check_output(*self.PREFIX, 'whoami')
while not call(
*self.PREFIX,
'ssh',
'-oStrictHostKeyChecking=no',
'-i', '/home/{}/.ssh/id_microstack'.format(username),
'cirros@{}'.format(ip),
'--', 'ping', '-c1', '91.189.94.250'):
attempts += 1
if attempts > max_attempts:
self.assertFalse(
True,
msg='Unable to access the Internet!')
time.sleep(1)
else:
# Artificial wait, to allow for stuff to settle for the GUI test.
# TODO: get rid of this, when we drop the ping tests back int.
time.sleep(10)
if 'multipass' in self.PREFIX: if 'multipass' in self.PREFIX:
print("Opening {}:80 up to the outside world".format( print("Opening {}:80 up to the outside world".format(
self.HORIZON_IP)) self.HORIZON_IP))

View File

@ -291,12 +291,10 @@ class RabbitMq(Question):
(actions may have already been run, in which case we fail silently). (actions may have already been run, in which case we fail silently).
""" """
# Add Erlang HOME to env.
env = dict(**_env)
env['HOME'] = '{SNAP_COMMON}/lib/rabbitmq'.format(**_env)
# Configure RabbitMQ # Configure RabbitMQ
call('rabbitmqctl', 'add_user', 'openstack', 'rabbitmq', env=env) call('microstack.rabbitmqctl', 'add_user', 'openstack', 'rabbitmq')
shell('rabbitmqctl set_permissions openstack ".*" ".*" ".*"', env=env) shell(
'microstack.rabbitmqctl set_permissions openstack ".*" ".*" ".*"')
def yes(self, answer: str) -> None: def yes(self, answer: str) -> None:
log.info('Waiting for RabbitMQ to start ...') log.info('Waiting for RabbitMQ to start ...')
@ -692,20 +690,18 @@ class KeyPair(Question):
def yes(self, answer: str) -> None: def yes(self, answer: str) -> None:
if 'microstack' not in check_output('openstack', 'keypair', 'list'): if 'microstack' not in check_output('openstack', 'keypair', 'list'):
user = check_output('logname')
home = '/home/{}'.format(user) # TODO make more portable!
log.info('Creating microstack keypair (~/.ssh/{})'.format(answer)) log.info('Creating microstack keypair (~/.ssh/{})'.format(answer))
check('mkdir', '-p', '{HOME}/.ssh'.format(**_env)) check('mkdir', '-p', '{home}/.ssh'.format(home=home))
check('chmod', '700', '{HOME}/.ssh'.format(**_env)) check('chmod', '700', '{home}/.ssh'.format(home=home))
id_ = check_output('openstack', 'keypair', 'create', 'microstack') id_ = check_output('openstack', 'keypair', 'create', 'microstack')
id_path = '{HOME}/.ssh/{answer}'.format( id_path = '{home}/.ssh/{answer}'.format(home=home, answer=answer)
HOME=_env['HOME'],
answer=answer
)
with open(id_path, 'w') as file_: with open(id_path, 'w') as file_:
file_.write(id_) file_.write(id_)
check('chmod', '600', id_path) check('chmod', '600', id_path)
# TODO: too many assumptions in the below. Make it portable!
user = _env['HOME'].split("/")[2]
check('chown', '{}:{}'.format(user, user), id_path) check('chown', '{}:{}'.format(user, user), id_path)

View File

@ -30,6 +30,7 @@ from typing import Dict, List
import netaddr import netaddr
import netifaces import netifaces
import pymysql import pymysql
import socket
import wget import wget
from init.config import Env, log from init.config import Env, log
@ -107,16 +108,12 @@ def shell(cmd: str, env: Dict = _env) -> int:
""" """
proc = subprocess.Popen(cmd, env=env, stdout=subprocess.PIPE, proc = subprocess.Popen(cmd, env=env, stdout=subprocess.PIPE,
stderr=subprocess.STDOUT, bufsize=1, stderr=subprocess.STDOUT, bufsize=1,
universal_newlines=True, shell=True, universal_newlines=True, shell=True)
executable='/snap/core18/current/bin/bash')
for line in iter(proc.stdout.readline, ''): for line in iter(proc.stdout.readline, ''):
log.debug(line) log.debug(line)
proc.wait() proc.wait()
if proc.returncode: if proc.returncode:
raise subprocess.CalledProcessError( raise subprocess.CalledProcessError(proc.returncode, cmd)
"Command '{}' returned non-zero exit status {}".format(
cmd,
proc.returncode))
return proc.returncode return proc.returncode
@ -141,7 +138,8 @@ def sql(cmd: str) -> None:
def nc_wait(addr: str, port: str) -> None: def nc_wait(addr: str, port: str) -> None:
"""Wait for a service to be answering on a port.""" """Wait for a service to be answering on a port."""
print('Waiting for {}:{}'.format(addr, port)) print('Waiting for {}:{}'.format(addr, port))
while not call('nc', '-z', addr, port): sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
while sock.connect_ex((addr, int(port))) != 0:
sleep(1) sleep(1)

85
tools/update_path.py Executable file
View File

@ -0,0 +1,85 @@
#!/usr/bin/python3
"""Update LD_LIBRARY_PATH and PATH snapcraft.yaml in the current
working directory.
Editing the lines in question directly in snapcraft.yaml is pretty
terrible, as the lines are long, and we cannot break them up into a
normal yaml string w/ a | and still get snapcraft's variable
expansion. (Or, if we can, I don't know what magic invocation will do
so.)
This script will not check in the new snapcraft.yaml. You should
inspect the updates and check in the file yourself!
"""
import os
import shutil
import sys
LD_LIBRARY_PATH = (
'$SNAP/lib',
'$SNAP/lib/$SNAPCRAFT_ARCH_TRIPLET',
'$SNAP/usr/lib',
'$SNAP/usr/lib/$SNAPCRAFT_ARCH_TRIPLET',
'$SNAP/usr/lib/$SNAPCRAFT_ARCH_TRIPLET/pulseaudio',
# '/snap/core18/current/lib',
# '/snap/core18/current/lib/$SNAPCRAFT_ARCH_TRIPLET',
# '/snap/core18/current/lib/systemd',
# '/snap/core18/current/usr/lib',
# '/snap/core18/current/var/lib',
# '/snap/core18/current/usr/lib/$SNAPCRAFT_ARCH_TRIPLET',
)
PATH = (
'$SNAP/usr/sbin',
'$SNAP/usr/bin',
'$SNAP/sbin',
'$SNAP/bin',
# '/snap/core18/current/bin',
# '/snap/core18/current/usr/sbin',
# '/snap/core18/current/usr/bin',
# '/snap/core18/current/sbin',
'$PATH'
)
def main():
"""Replace PATH and LD_LIBRARY_PATH with lists above.
This is dead simple code that relies on there being one setting
for LD_LIBRARY_PATH and PATH. It needs to be updated to be made
smarter if more instances are added.
Note that it would be nice if we could just read and write the
yaml, but we'd chomp comments if we did so. And we like our
comments!
"""
if not os.path.isfile('./snapcraft.yaml'):
print('Cannot file snapcraft.yaml in the current working dir!')
print('Exiting.')
sys.exit(1)
print('snapcraft.yaml found in the current working dir. '
'Updating LD_LIBRARY_PATH and PATH ...')
libs = ':'.join(LD_LIBRARY_PATH)
path_ = ':'.join(PATH)
with open('./snapcraft.yaml', 'r') as source:
with open('./snapcraft.yaml.updated', 'w') as dest:
lines = source.readlines()
for line in lines:
if line.startswith(' LD_LIBRARY_PATH: '):
line = ' LD_LIBRARY_PATH: {}\n'.format(libs)
if line.startswith(' PATH: '):
line = ' PATH: {}\n'.format(path_)
dest.write(line)
shutil.move('./snapcraft.yaml.updated', './snapcraft.yaml')
print('File updated! Please manually inspect the changes '
'and commit them via git.')
if __name__ == '__main__':
main()

32
tox.ini
View File

@ -53,6 +53,38 @@ commands =
flake8 {toxinidir}/tests/ flake8 {toxinidir}/tests/
{toxinidir}/tests/test_basic.py {toxinidir}/tests/test_basic.py
[testenv:eoan]
# Just run basic_test.sh, with multipass support, on eoan.
# TODO: refactor so that there isn't so much copypasta here and below.
deps = -r{toxinidir}/test-requirements.txt
setenv =
MULTIPASS=true
DISTRO=eoan
commands =
{toxinidir}/tools/basic_setup.sh
flake8 {toxinidir}/tests/
{toxinidir}/tests/test_basic.py
[testenv:disco]
# Just run basic_test.sh, with multipass support, on disco.
deps = -r{toxinidir}/test-requirements.txt
setenv =
MULTIPASS=true
DISTRO=disco
[testenv:xenial]
# Just run basic_test.sh, with multipass support, on xenial.
deps = -r{toxinidir}/test-requirements.txt
setenv =
MULTIPASS=true
DISTRO=xenial
commands =
{toxinidir}/tools/basic_setup.sh
flake8 {toxinidir}/tests/
{toxinidir}/tests/test_basic.py
[testenv:cluster] [testenv:cluster]
# Test out clustering! # Test out clustering!
# Requires multipass. # Requires multipass.