# Copyright 2013-2022 Lawrence Livermore National Security, LLC and other
# Spack Project Developers. See the top-level COPYRIGHT file for details.
#
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
import os
import sys
import pytest
import llnl.util.tty as tty
import spack.config
import spack.monitor
import spack.spec
from spack.main import SpackCommand
from spack.monitor import SpackMonitorClient
install = SpackCommand('install')
[docs]def get_client(host, prefix="ms1", allow_fail=False, tags=None, save_local=False):
"""
We replicate this function to not generate a global client.
"""
cli = SpackMonitorClient(host=host, prefix=prefix, allow_fail=allow_fail,
tags=tags, save_local=save_local)
# We will exit early if the monitoring service is not running, but
# only if we aren't doing a local save
if not save_local:
info = cli.service_info()
# If we allow failure, the response will be done
if info:
tty.debug("%s v.%s has status %s" % (
info['id'],
info['version'],
info['status'])
)
return cli
[docs]@pytest.fixture
def mock_monitor_request(monkeypatch):
"""
Monitor requests that are shared across tests go here
"""
def mock_do_request(self, endpoint, *args, **kwargs):
# monitor was originally keyed by full_hash, but now dag_hash is the full hash.
# the name of the field in monitor is still spec_full_hash, for now.
build = {"build_id": 1,
"spec_full_hash": "bpfvysmqndtmods4rmy6d6cfquwblngp",
"spec_name": "dttop"}
# Service Info
if endpoint == "":
organization = {"name": "spack", "url": "https://github.com/spack"}
return {"id": "spackmon", "status": "running",
"name": "Spack Monitor (Spackmon)",
"description": "The best spack monitor",
"organization": organization,
"contactUrl": "https://github.com/spack/spack-monitor/issues",
"documentationUrl": "https://spack-monitor.readthedocs.io",
"createdAt": "2021-04-09T21:54:51Z",
"updatedAt": "2021-05-24T15:06:46Z",
"environment": "test",
"version": "0.0.1",
"auth_instructions_url": "url"}
# New Build
elif endpoint == "builds/new/":
return {"message": "Build get or create was successful.",
"data": {
"build_created": True,
"build_environment_created": True,
"build": build
},
"code": 201}
# Update Build
elif endpoint == "builds/update/":
return {"message": "Status updated",
"data": {"build": build},
"code": 200}
# Send Analyze Metadata
elif endpoint == "analyze/builds/":
return {"message": "Metadata updated",
"data": {"build": build},
"code": 200}
# Update Build Phase
elif endpoint == "builds/phases/update/":
return {"message": "Phase autoconf was successfully updated.",
"code": 200,
"data": {
"build_phase": {
"id": 1,
"status": "SUCCESS",
"name": "autoconf"
}
}}
# Update Phase Status
elif endpoint == "phases/update/":
return {"message": "Status updated",
"data": {"build": build},
"code": 200}
# New Spec
elif endpoint == "specs/new/":
return {"message": "success",
"data": {
"full_hash": "bpfvysmqndtmods4rmy6d6cfquwblngp",
"name": "dttop",
"version": "1.0",
"spack_version": "0.16.0-1379-7a5351d495",
"specs": {
"dtbuild1": "btcmljubs4njhdjqt2ebd6nrtn6vsrks",
"dtlink1": "x4z6zv6lqi7cf6l4twz4bg7hj3rkqfmk",
"dtrun1": "i6inyro74p5yqigllqk5ivvwfjfsw6qz"
}
}}
else:
pytest.fail("bad endpoint: %s" % endpoint)
monkeypatch.setattr(spack.monitor.SpackMonitorClient, "do_request", mock_do_request)
[docs]def test_spack_monitor_auth(mock_monitor_request):
os.environ["SPACKMON_TOKEN"] = "xxxxxxxxxxxxxxxxx"
os.environ["SPACKMON_USER"] = "spackuser"
get_client(host="http://127.0.0.1")
[docs]def test_spack_monitor_without_auth(mock_monitor_request):
get_client(host="hostname")
[docs]@pytest.mark.skipif(sys.platform == 'win32',
reason="Not supported on Windows (yet)")
def test_spack_monitor_build_env(mock_monitor_request, install_mockery_mutable_config):
monitor = get_client(host="hostname")
assert hasattr(monitor, "build_environment")
for key in ["host_os", "platform", "host_target", "hostname", "spack_version",
"kernel_version"]:
assert key in monitor.build_environment
spec = spack.spec.Spec("dttop")
spec.concretize()
# Loads the build environment from the spec install folder
monitor.load_build_environment(spec)
[docs]def test_spack_monitor_basic_auth(mock_monitor_request):
monitor = get_client(host="hostname")
# Headers should be empty
assert not monitor.headers
monitor.set_basic_auth("spackuser", "password")
assert "Authorization" in monitor.headers
assert monitor.headers['Authorization'].startswith("Basic")
[docs]def test_spack_monitor_new_configuration(mock_monitor_request, install_mockery):
monitor = get_client(host="hostname")
spec = spack.spec.Spec("dttop")
spec.concretize()
response = monitor.new_configuration([spec])
# The response is a lookup of specs
assert "dttop" in response
[docs]def test_spack_monitor_new_build(mock_monitor_request, install_mockery_mutable_config,
install_mockery):
monitor = get_client(host="hostname")
spec = spack.spec.Spec("dttop")
spec.concretize()
response = monitor.new_build(spec)
assert "message" in response and "data" in response and "code" in response
assert response['code'] == 201
# We should be able to get a build id
monitor.get_build_id(spec)
[docs]def test_spack_monitor_update_build(mock_monitor_request, install_mockery,
install_mockery_mutable_config):
monitor = get_client(host="hostname")
spec = spack.spec.Spec("dttop")
spec.concretize()
response = monitor.update_build(spec, status="SUCCESS")
assert "message" in response and "data" in response and "code" in response
assert response['code'] == 200
[docs]def test_spack_monitor_fail_task(mock_monitor_request, install_mockery,
install_mockery_mutable_config):
monitor = get_client(host="hostname")
spec = spack.spec.Spec("dttop")
spec.concretize()
response = monitor.fail_task(spec)
assert "message" in response and "data" in response and "code" in response
assert response['code'] == 200
[docs]def test_spack_monitor_send_phase(mock_monitor_request, install_mockery,
install_mockery_mutable_config):
monitor = get_client(host="hostname")
def get_build_id(*args, **kwargs):
return 1
spec = spack.spec.Spec("dttop")
spec.concretize()
response = monitor.send_phase(spec.package, "autoconf",
spec.package.install_log_path,
"SUCCESS")
assert "message" in response and "data" in response and "code" in response
assert response['code'] == 200
[docs]def test_spack_monitor_info(mock_monitor_request):
os.environ["SPACKMON_TOKEN"] = "xxxxxxxxxxxxxxxxx"
os.environ["SPACKMON_USER"] = "spackuser"
monitor = get_client(host="http://127.0.0.1")
info = monitor.service_info()
for key in ['id', 'status', 'name', 'description', 'organization',
'contactUrl', 'documentationUrl', 'createdAt', 'updatedAt',
'environment', 'version', 'auth_instructions_url']:
assert key in info
[docs]@pytest.fixture(scope='session')
def test_install_monitor_save_local(install_mockery_mutable_config,
mock_fetch, tmpdir_factory):
"""
Mock installing and saving monitor results to file.
"""
reports_dir = tmpdir_factory.mktemp('reports')
spack.config.set('config:monitor_dir', str(reports_dir))
out = install('--monitor', '--monitor-save-local', 'dttop')
assert "Successfully installed dttop" in out
# The reports directory should not be empty (timestamped folders)
assert os.listdir(str(reports_dir))
# Get the spec name
spec = spack.spec.Spec("dttop")
spec.concretize()
# Ensure we have monitor results saved
for dirname in os.listdir(str(reports_dir)):
dated_dir = os.path.join(str(reports_dir), dirname)
build_metadata = "build-metadata-%s.json" % spec.dag_hash()
assert build_metadata in os.listdir(dated_dir)
spec_file = "spec-dttop-%s-config.json" % spec.version
assert spec_file in os.listdir(dated_dir)
spack.config.set('config:monitor_dir', "~/.spack/reports/monitor")