-
Notifications
You must be signed in to change notification settings - Fork 11
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
This commit adds a new way to manage releases inside Argus, called Release Planner. Release Planner replaces current Release Scheduling mechanism (but not the Duty Calendar for now). Main features include: * Plans are now separate entities (replacing one global release schedule) * Each plan contains tests, groups and people it will be using for assignments * Plans can target a specific scylla version to scope the plan to a point or RC release. * Plans can be edited, deleted and copied. * On copy, plan will offer to replace missing tests/groups if it is being copied to another release and that release is missing required tests. * Each plan automatically creates and maintains a view dashboard for said plan and can be viewed from plan overview page or inside views overview * The new assignment logic is now applied to all releases not marked as "endless" (perpetual in the admin panel). Currently the only one that is marked that way is scylla-master. Fixes #377 Task: scylladb/qa-tasks#1717
- Loading branch information
Showing
35 changed files
with
2,806 additions
and
165 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,178 @@ | ||
import logging | ||
from uuid import UUID | ||
from flask import ( | ||
Blueprint, | ||
request | ||
) | ||
from argus.backend.error_handlers import handle_api_exception | ||
from argus.backend.service.planner_service import CopyPlanPayload, PlanningService, TempPlanPayload | ||
from argus.backend.service.test_lookup import TestLookup | ||
from argus.backend.service.user import api_login_required | ||
from argus.backend.util.common import get_payload | ||
|
||
bp = Blueprint('planning_api', __name__, url_prefix='/planning') | ||
LOGGER = logging.getLogger(__name__) | ||
bp.register_error_handler(Exception, handle_api_exception) | ||
|
||
|
||
@bp.route("/", methods=["GET"]) | ||
@api_login_required | ||
def version(): | ||
|
||
result = PlanningService().version() | ||
|
||
return { | ||
"status": "ok", | ||
"response": result | ||
} | ||
|
||
|
||
@bp.route("/plan/<string:plan_id>/copy/check", methods=["GET"]) | ||
@api_login_required | ||
def is_plan_eligible_for_copy(plan_id: str): | ||
release_id = request.args.get("releaseId") | ||
if not release_id: | ||
raise Exception("Missing release id.") | ||
|
||
result = PlanningService().check_plan_copy_eligibility(plan_id=UUID(plan_id), target_release_id=UUID(release_id)) | ||
|
||
return { | ||
"status": "ok", | ||
"response": result | ||
} | ||
|
||
|
||
@bp.route("/release/<string:release_id>/gridview", methods=["GET"]) | ||
@api_login_required | ||
def grid_view_for_release(release_id: str): | ||
|
||
result = PlanningService().get_gridview_for_release(release_id=UUID(release_id)) | ||
|
||
return { | ||
"status": "ok", | ||
"response": result | ||
} | ||
|
||
|
||
@bp.route("/search", methods=["GET"]) | ||
@api_login_required | ||
def search_tests(): | ||
query = request.args.get("query") | ||
release_id = request.args.get('releaseId') | ||
service = TestLookup | ||
if query: | ||
res = service.test_lookup(query, release_id=release_id) | ||
else: | ||
res = [] | ||
return { | ||
"status": "ok", | ||
"response": { | ||
"hits": res, | ||
"total": len(res) | ||
} | ||
} | ||
|
||
@bp.route("/group/<string:group_id>/explode", methods=["GET"]) | ||
@api_login_required | ||
def explode_group(group_id: str): | ||
service = TestLookup | ||
res = service.explode_group(group_id=group_id) | ||
return { | ||
"status": "ok", | ||
"response": res | ||
} | ||
|
||
|
||
@bp.route("/plan/<string:plan_id>/get", methods=["GET"]) | ||
@api_login_required | ||
def get_plan(plan_id: str): | ||
result = PlanningService().get_plan(plan_id) | ||
|
||
return { | ||
"status": "ok", | ||
"response": result | ||
} | ||
|
||
|
||
@bp.route("/release/<string:release_id>/all", methods=["GET"]) | ||
@api_login_required | ||
def get_plans_for_release(release_id: str): | ||
result = PlanningService().get_plans_for_release(release_id) | ||
|
||
return { | ||
"status": "ok", | ||
"response": result | ||
} | ||
|
||
|
||
@bp.route("/plan/create", methods=["POST"]) | ||
@api_login_required | ||
def create_plan(): | ||
payload = get_payload(request) | ||
result = PlanningService().create_plan(payload) | ||
|
||
return { | ||
"status": "ok", | ||
"response": result | ||
} | ||
|
||
|
||
@bp.route("/plan/update", methods=["POST"]) | ||
@api_login_required | ||
def update_plan(): | ||
payload = get_payload(request) | ||
result = PlanningService().update_plan(payload) | ||
|
||
return { | ||
"status": "ok", | ||
"response": result | ||
} | ||
|
||
@bp.route("/plan/copy", methods=["POST"]) | ||
@api_login_required | ||
def copy_plan(): | ||
payload = get_payload(request) | ||
payload["plan"] = TempPlanPayload(**payload["plan"]) | ||
result = PlanningService().copy_plan(CopyPlanPayload(**payload)) | ||
|
||
return { | ||
"status": "ok", | ||
"response": result | ||
} | ||
|
||
|
||
|
||
@bp.route("/plan/<string:plan_id>/delete", methods=["DELETE"]) | ||
@api_login_required | ||
def delete_plan(plan_id: str): | ||
result = PlanningService().delete_plan(plan_id) | ||
|
||
return { | ||
"status": "ok", | ||
"response": result | ||
} | ||
|
||
|
||
@bp.route("/plan/<string:plan_id>/owner/set", methods=["POST"]) | ||
@api_login_required | ||
def change_plan_owner(plan_id: str): | ||
payload = get_payload(request) | ||
result = PlanningService().change_plan_owner(plan_id=plan_id, new_owner=payload["newOwner"]) | ||
|
||
return { | ||
"status": "ok", | ||
"response": result | ||
} | ||
|
||
|
||
@bp.route("/plan/<string:plan_id>/resolve_entities", methods=["GET"]) | ||
@api_login_required | ||
def resolve_plan_entities(plan_id: str): | ||
|
||
service = PlanningService() | ||
result = service.resolve_plan(plan_id) | ||
|
||
return { | ||
"status": "ok", | ||
"response": result, | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
import datetime | ||
from cassandra.cqlengine import columns | ||
from cassandra.cqlengine.models import Model | ||
from cassandra.cqlengine.usertype import UserType | ||
from cassandra.util import uuid_from_time | ||
|
||
|
||
class ArgusReleasePlan(Model): | ||
id = columns.TimeUUID(partition_key=True, default=lambda: uuid_from_time(datetime.datetime.now(tz=datetime.UTC))) | ||
name = columns.Text(required=True) | ||
completed = columns.Boolean(default=lambda: False) | ||
description = columns.Text() | ||
owner = columns.UUID(required=True) | ||
participants = columns.List(value_type=columns.UUID) | ||
target_version = columns.Ascii() | ||
assignee_mapping = columns.Map(key_type=columns.UUID, value_type=columns.UUID) | ||
release_id = columns.UUID(index=True) | ||
tests = columns.List(value_type=columns.UUID) | ||
groups = columns.List(value_type=columns.UUID) | ||
view_id = columns.UUID(index=True) | ||
created_from = columns.UUID(index=True) | ||
creation_time = columns.DateTime(default=lambda: datetime.datetime.now(tz=datetime.UTC)) | ||
last_updated = columns.DateTime(default=lambda: datetime.datetime.now(tz=datetime.UTC)) | ||
ends_at = columns.DateTime() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.