Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

User Zero / Super Admin #1021

Merged
merged 18 commits into from
Dec 23, 2024
Merged
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion cron/client.py
Original file line number Diff line number Diff line change
@@ -147,7 +147,7 @@ def do_cleanup(cur_temp, cooldown_time_after_job):
if client_main['send_control_workload_status_mail'] and config_main['admin']['notification_email']:
Job.insert(
'email',
user_id=None,
user_id=0, # User 0 is the [GMT-SYSTEM] user
email=config_main['admin']['notification_email'],
name=f"{config_main['machine']['description']} is operating normally. All STDDEV fine.",
message='\n'.join(message)
12 changes: 6 additions & 6 deletions data/demo_data.sql

Large diffs are not rendered by default.

27 changes: 16 additions & 11 deletions docker/structure.sql
Original file line number Diff line number Diff line change
@@ -26,7 +26,12 @@ CREATE TRIGGER users_moddatetime
-- Default password for authentication is DEFAULT
INSERT INTO "public"."users"("name","token","capabilities","created_at","updated_at")
VALUES
(E'DEFAULT',E'89dbf71048801678ca4abfbaa3ea8f7c651aae193357a3e23d68e21512cd07f5',E'{"api":{"quotas":{},"routes":["/v2/carbondb/filters","/v2/carbondb","/v2/carbondb/add","/v2/ci/measurement/add","/v1/ci/measurement/add","/v1/software/add","/v1/hog/add","/v1/authentication/data"]},"data":{"runs":{"retention":2678400},"hog_tasks":{"retention":2678400},"measurements":{"retention":2678400},"hog_coalitions":{"retention":2678400},"ci_measurements":{"retention":2678400},"hog_measurements":{"retention":2678400}},"jobs":{"schedule_modes":["one-off","daily","weekly","commit","variance","tag","commit-variance","tag-variance"]},"machines":[1],"measurement":{"quotas":{},"settings":{"total-duration":86400,"flow-process-duration":86400}},"optimizations":["container_memory_utilization","container_cpu_utilization","message_optimization","container_build_time","container_boot_time","container_image_size"]}',E'2024-08-22 11:28:24.937262+00',NULL);
(E'DEFAULT',E'89dbf71048801678ca4abfbaa3ea8f7c651aae193357a3e23d68e21512cd07f5',E'{"user":{"is_super_user": true},"api":{"quotas":{},"routes":["/v2/carbondb/filters","/v2/carbondb","/v2/carbondb/add","/v2/ci/measurement/add","/v1/ci/measurement/add","/v1/software/add","/v1/hog/add","/v1/authentication/data"]},"data":{"runs":{"retention":2678400},"hog_tasks":{"retention":2678400},"measurements":{"retention":2678400},"hog_coalitions":{"retention":2678400},"ci_measurements":{"retention":2678400},"hog_measurements":{"retention":2678400}},"jobs":{"schedule_modes":["one-off","daily","weekly","commit","variance","tag","commit-variance","tag-variance"]},"machines":[1],"measurement":{"quotas":{},"settings":{"total-duration":86400,"flow-process-duration":86400}},"optimizations":["container_memory_utilization","container_cpu_utilization","message_optimization","container_build_time","container_boot_time","container_image_size"]}',E'2024-08-22 11:28:24.937262+00',NULL);

-- Default password for user 0 is empty
INSERT INTO "public"."users"("id", "name","token","capabilities","created_at","updated_at")
VALUES
(0, E'[GMT-SYSTEM]',E'',E'{"user":{"is_super_user": false},"api":{"quotas":{},"routes":[]},"data":{"runs":{"retention":2678400},"hog_tasks":{"retention":2678400},"measurements":{"retention":2678400},"hog_coalitions":{"retention":2678400},"ci_measurements":{"retention":2678400},"hog_measurements":{"retention":2678400}},"jobs":{"schedule_modes":[]},"machines":[],"measurement":{"quotas":{},"settings":{"total-duration":86400,"flow-process-duration":86400}},"optimizations":[]}',E'2024-11-06 11:28:24.937262+00',NULL);

CREATE TABLE machines (
id SERIAL PRIMARY KEY,
@@ -66,7 +71,7 @@ CREATE TABLE jobs (
categories int[],
machine_id int REFERENCES machines(id) ON DELETE SET NULL ON UPDATE CASCADE,
ArneTR marked this conversation as resolved.
Show resolved Hide resolved
message text,
user_id integer REFERENCES users(id) ON DELETE SET NULL ON UPDATE CASCADE,
user_id integer NOT NULL REFERENCES users(id) ON DELETE RESTRICT ON UPDATE CASCADE,
created_at timestamp with time zone DEFAULT now(),
updated_at timestamp with time zone
);
@@ -98,7 +103,7 @@ CREATE TABLE runs (
logs text,
invalid_run text,
failed boolean DEFAULT false,
user_id integer REFERENCES users(id) ON DELETE SET NULL ON UPDATE CASCADE, -- this must allowed to be null for CLI runs
user_id integer NOT NULL REFERENCES users(id) ON DELETE RESTRICT ON UPDATE CASCADE,
created_at timestamp with time zone DEFAULT now(),
updated_at timestamp with time zone
);
@@ -219,7 +224,7 @@ CREATE TABLE ci_measurements (
filter_project text NOT NULL,
filter_machine text NOT NULL,
filter_tags text[] NOT NULL,
user_id integer NOT NULL REFERENCES users(id) ON DELETE SET NULL ON UPDATE CASCADE,
user_id integer NOT NULL REFERENCES users(id) ON DELETE RESTRICT ON UPDATE CASCADE,
created_at timestamp with time zone DEFAULT now(),
updated_at timestamp with time zone
);
@@ -255,7 +260,7 @@ CREATE TABLE timeline_projects (
schedule_mode text NOT NULL,
last_scheduled timestamp with time zone,
last_marker text,
user_id integer NOT NULL REFERENCES users(id) ON DELETE SET NULL ON UPDATE CASCADE,
user_id integer NOT NULL REFERENCES users(id) ON DELETE RESTRICT ON UPDATE CASCADE,
created_at timestamp with time zone DEFAULT now(),
updated_at timestamp with time zone
);
@@ -277,7 +282,7 @@ CREATE TABLE hog_measurements (
thermal_pressure text,
settings jsonb,
data jsonb,
user_id integer NOT NULL REFERENCES users(id) ON DELETE SET NULL ON UPDATE CASCADE,
user_id integer NOT NULL REFERENCES users(id) ON DELETE RESTRICT ON UPDATE CASCADE,
created_at timestamp with time zone DEFAULT now(),
updated_at timestamp with time zone
);
@@ -364,7 +369,7 @@ CREATE INDEX optimizations_runs ON optimizations(run_id);
CREATE TABLE carbondb_types (
id SERIAL PRIMARY KEY,
type text NOT NULL,
user_id integer NOT NULL REFERENCES users(id) ON DELETE SET NULL ON UPDATE CASCADE,
user_id integer NOT NULL REFERENCES users(id) ON DELETE RESTRICT ON UPDATE CASCADE,
created_at timestamp with time zone DEFAULT now(),
updated_at timestamp with time zone
);
@@ -374,7 +379,7 @@ CREATE UNIQUE INDEX carbondb_types_unique ON carbondb_types(type text_ops,user_i
CREATE TABLE carbondb_tags (
id SERIAL PRIMARY KEY,
tag text NOT NULL,
user_id integer NOT NULL REFERENCES users(id) ON DELETE SET NULL ON UPDATE CASCADE,
user_id integer NOT NULL REFERENCES users(id) ON DELETE RESTRICT ON UPDATE CASCADE,
created_at timestamp with time zone DEFAULT now(),
updated_at timestamp with time zone
);
@@ -384,7 +389,7 @@ CREATE UNIQUE INDEX carbondb_tags_unique ON carbondb_tags(tag text_ops,user_id i
CREATE TABLE carbondb_machines (
id SERIAL PRIMARY KEY,
machine text NOT NULL,
user_id integer NOT NULL REFERENCES users(id) ON DELETE SET NULL ON UPDATE CASCADE,
user_id integer NOT NULL REFERENCES users(id) ON DELETE RESTRICT ON UPDATE CASCADE,
created_at timestamp with time zone DEFAULT now(),
updated_at timestamp with time zone
);
@@ -393,7 +398,7 @@ CREATE UNIQUE INDEX carbondb_machines_unique ON carbondb_machines(machine text_o
CREATE TABLE carbondb_projects (
id SERIAL PRIMARY KEY,
project text NOT NULL,
user_id integer NOT NULL REFERENCES users(id) ON DELETE SET NULL ON UPDATE CASCADE,
user_id integer NOT NULL REFERENCES users(id) ON DELETE RESTRICT ON UPDATE CASCADE,
created_at timestamp with time zone DEFAULT now(),
updated_at timestamp with time zone
);
@@ -402,7 +407,7 @@ CREATE UNIQUE INDEX carbondb_projects_unique ON carbondb_projects(project text_o
CREATE TABLE carbondb_sources (
id SERIAL PRIMARY KEY,
source text NOT NULL,
user_id integer NOT NULL REFERENCES users(id) ON DELETE SET NULL ON UPDATE CASCADE,
user_id integer NOT NULL REFERENCES users(id) ON DELETE RESTRICT ON UPDATE CASCADE,
created_at timestamp with time zone DEFAULT now(),
updated_at timestamp with time zone
);
3 changes: 2 additions & 1 deletion lib/error_helpers.py
Original file line number Diff line number Diff line change
@@ -41,4 +41,5 @@ def log_error(*messages, **kwargs):
print(TerminalColors.FAIL, err, TerminalColors.ENDC, file=sys.stderr)

if error_email := GlobalConfig().config['admin']['error_email']:
Job.insert('email', user_id=None, email=error_email, name='Green Metrics Tool Error', message=err)
# User 0 is the [GMT-SYSTEM] user
Job.insert('email', user_id=0, email=error_email, name='Green Metrics Tool Error', message=err)
ArneTR marked this conversation as resolved.
Show resolved Hide resolved
6 changes: 6 additions & 0 deletions lib/user.py
Original file line number Diff line number Diff line change
@@ -8,6 +8,9 @@
class User():

def __init__(self, user_id: int):
if user_id == 0:
raise RuntimeError('User 0 is system user and cannot log in')
ArneTR marked this conversation as resolved.
Show resolved Hide resolved

user = DB().fetch_one("""
SELECT id, name, capabilities
FROM users
@@ -35,6 +38,9 @@ def update(self):
WHERE id = %s
""", params=(json.dumps(self._capabilities), self._id, ))

def is_super_admin(self):
return bool(self._capabilities['user']['is_super_admin'])
ArneTR marked this conversation as resolved.
Show resolved Hide resolved

def can_use_machine(self, machine_id: int):
return machine_id in self._capabilities['machines']

1 change: 1 addition & 0 deletions lib/validate.py
Original file line number Diff line number Diff line change
@@ -87,6 +87,7 @@ def run_workload(name, uri, filename, branch):
full_docker_prune=False,
docker_prune=True,
job_id=None,
user_id=0, # User id 0 is the [GMT-SYSTEM] user
ArneTR marked this conversation as resolved.
Show resolved Hide resolved
)
# Start main code. Only URL is allowed for cron jobs
runner.run()
11 changes: 11 additions & 0 deletions migrations/2024_12_17_user_zero.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
INSERT INTO "public"."users"("id", "name","token","capabilities","created_at","updated_at")
VALUES
(0, E'[GMT-SYSTEM]',E'',E'{"user":{"is_super_user": false},"api":{"quotas":{},"routes":[]},"data":{"runs":{"retention":2678400},"hog_tasks":{"retention":2678400},"measurements":{"retention":2678400},"hog_coalitions":{"retention":2678400},"ci_measurements":{"retention":2678400},"hog_measurements":{"retention":2678400}},"jobs":{"schedule_modes":[]},"machines":[],"measurement":{"quotas":{},"settings":{"total-duration":86400,"flow-process-duration":86400}},"optimizations":[]}',E'2024-11-06 11:28:24.937262+00',NULL);
ArneTR marked this conversation as resolved.
Show resolved Hide resolved

UPDATE jobs SET user_id = 0 WHERE user_id IS NULL;

UPDATE runs SET user_id = 1 WHERE user_id IS NULL and uri != 'https://github.com/green-coding-solutions/measurement-control-workload';
UPDATE runs SET user_id = 0 WHERE user_id IS NULL and uri = 'https://github.com/green-coding-solutions/measurement-control-workload';
ArneTR marked this conversation as resolved.
Show resolved Hide resolved
ArneTR marked this conversation as resolved.
Show resolved Hide resolved

ALTER TABLE "public"."runs" ALTER COLUMN "user_id" SET NOT NULL;
ALTER TABLE "public"."jobs" ALTER COLUMN "user_id" SET NOT NULL;
ArneTR marked this conversation as resolved.
Show resolved Hide resolved
5 changes: 3 additions & 2 deletions runner.py
Original file line number Diff line number Diff line change
@@ -50,7 +50,7 @@ def __init__(self,
skip_unsafe=False, verbose_provider_boot=False, full_docker_prune=False,
dev_no_sleeps=False, dev_cache_build=False, dev_no_metrics=False,
dev_flow_timetravel=False, dev_no_optimizations=False, docker_prune=False, job_id=None,
user_id=None, measurement_flow_process_duration=None, measurement_total_duration=None, dev_no_phase_stats=False):
user_id=1, measurement_flow_process_duration=None, measurement_total_duration=None, dev_no_phase_stats=False):

if skip_unsafe is True and allow_unsafe is True:
raise RuntimeError('Cannot specify both --skip-unsafe and --allow-unsafe')
@@ -1686,6 +1686,7 @@ def run(self):
parser.add_argument('--branch', type=str, help='Optionally specify the git branch when targeting a git repository')
parser.add_argument('--name', type=str, help='A name which will be stored to the database to discern this run from others')
parser.add_argument('--filename', type=str, default='usage_scenario.yml', help='An optional alternative filename if you do not want to use "usage_scenario.yml"')
parser.add_argument('--user-id', type=int, default=1, help='A user-ID the run shall be mapped to. Defaults to 1 (the default user)')
ArneTR marked this conversation as resolved.
Show resolved Hide resolved
parser.add_argument('--config-override', type=str, help='Override the configuration file with the passed in yml file. Supply full path.')
parser.add_argument('--file-cleanup', action='store_true', help='Delete all temporary files that the runner produced')
parser.add_argument('--debug', action='store_true', help='Activate steppable debug mode')
@@ -1754,7 +1755,7 @@ def run(self):
full_docker_prune=args.full_docker_prune, dev_no_sleeps=args.dev_no_sleeps,
dev_cache_build=args.dev_cache_build, dev_no_metrics=args.dev_no_metrics,
dev_flow_timetravel=args.dev_flow_timetravel, dev_no_optimizations=args.dev_no_optimizations,
docker_prune=args.docker_prune, dev_no_phase_stats=args.dev_no_phase_stats)
docker_prune=args.docker_prune, dev_no_phase_stats=args.dev_no_phase_stats, user_id=args.user_id)

# Using a very broad exception makes sense in this case as we have excepted all the specific ones before
#pylint: disable=broad-except
4 changes: 2 additions & 2 deletions tests/api/test_api_gmt.py
Original file line number Diff line number Diff line change
@@ -26,9 +26,9 @@ def test_get_runs():
run_name = 'test_' + utils.randomword(12)
uri = os.path.abspath(os.path.join(
CURRENT_DIR, 'stress-application/'))
pid = DB().fetch_one("INSERT INTO runs (name,uri,branch,filename,email,created_at) \
pid = DB().fetch_one("INSERT INTO runs (name,uri,branch,filename,email,created_at,user_id) \
VALUES \
(%s,%s,'testing','testing', 'testing', NOW()) RETURNING id;", params=(run_name, uri))[0]
(%s,%s,'testing','testing', 'testing', NOW(), 1) RETURNING id;", params=(run_name, uri))[0]

response = requests.get(f"{API_URL}/v1/runs?repo=&filename=", timeout=15)
res_json = response.json()
12 changes: 6 additions & 6 deletions tests/frontend/test_frontend.py
Original file line number Diff line number Diff line change
@@ -205,13 +205,13 @@ def test_stats():
assert chart_label.strip() == 'CPU % via procfs'


first_metric = new_page.locator("#runtime-steps > div.ui.bottom.attached.active.tab.segment > div.ui.segment.secondary > phase-metrics > div.ui.accordion > div.content.active > table > tbody > tr:nth-child(1) > td:nth-child(1)").text_content()
first_metric = new_page.locator("#runtime-steps > div.ui.bottom.attached.active.tab.segment > div.ui.segment.secondary > phase-metrics > div.ui.accordion > div.content.active > table > tbody > tr:nth-child(14) > td:nth-child(1)").text_content()
assert first_metric.strip() == 'CPU Energy (Package)'

first_value = new_page.locator("#runtime-steps > div.ui.bottom.attached.active.tab.segment > div.ui.segment.secondary > phase-metrics > div.ui.accordion > div.content.active > table > tbody > tr:nth-child(1) > td:nth-child(6)").text_content()
first_value = new_page.locator("#runtime-steps > div.ui.bottom.attached.active.tab.segment > div.ui.segment.secondary > phase-metrics > div.ui.accordion > div.content.active > table > tbody > tr:nth-child(14) > td:nth-child(6)").text_content()
assert first_value.strip() == '45.05'

first_unit = new_page.locator("#runtime-steps > div.ui.bottom.attached.active.tab.segment > div.ui.segment.secondary > phase-metrics > div.ui.accordion > div.content.active > table > tbody > tr:nth-child(1) > td:nth-child(7)").text_content()
first_unit = new_page.locator("#runtime-steps > div.ui.bottom.attached.active.tab.segment > div.ui.segment.secondary > phase-metrics > div.ui.accordion > div.content.active > table > tbody > tr:nth-child(14) > td:nth-child(7)").text_content()
assert first_unit.strip() == 'J'


@@ -220,13 +220,13 @@ def test_stats():
new_page.locator('div[data-tab="[BASELINE]"] .ui.accordion .title > a').click()

first_metric = new_page.locator("#main > div.ui.tab.attached.segment.secondary.active > phase-metrics > div.ui.accordion > div.content.active > table > tbody > tr:nth-child(1) > td:nth-child(1)").text_content()
assert first_metric.strip() == 'CPU Energy (Package)'
assert first_metric.strip() == 'Embodied Carbon'

first_value = new_page.locator("#main > div.ui.tab.attached.segment.secondary.active > phase-metrics > div.ui.accordion > div.content.active > table > tbody > tr:nth-child(1) > td:nth-child(6)").text_content()
assert first_value.strip() == '9.69'
assert first_value.strip() == '0.01'

first_unit = new_page.locator("#main > div.ui.tab.attached.segment.secondary.active > phase-metrics > div.ui.accordion > div.content.active > table > tbody > tr:nth-child(1) > td:nth-child(7)").text_content()
assert first_unit.strip() == 'J'
assert first_unit.strip() == 'g'

new_page.close()

Loading