From a7ae434c48c14bfde46a871455a3aa2ac0b81376 Mon Sep 17 00:00:00 2001 From: Karen Arutyunov Date: Sun, 26 May 2024 13:46:55 +0300 Subject: Add CI cancel handler --- brep/handler/ci/ci-load.in | 20 ++++++++++++- mod/ci-common.cxx | 36 ++++++++++++++++++++++++ mod/ci-common.hxx | 30 +++++++++----------- mod/mod-build-configs.cxx | 2 -- mod/mod-build-force.cxx | 2 -- mod/mod-build-log.cxx | 2 -- mod/mod-build-result.cxx | 2 -- mod/mod-builds.cxx | 2 -- mod/mod-ci.cxx | 64 ++++++++++++++++++++++++++++++++++++++++++ mod/mod-ci.hxx | 31 +++++++++++++++++--- mod/mod-package-details.cxx | 2 -- mod/mod-repository-details.cxx | 2 -- mod/mod-repository-root.cxx | 14 +++++++++ mod/mod-repository-root.hxx | 2 ++ mod/module.cli | 26 +++++++++++++---- 15 files changed, 197 insertions(+), 40 deletions(-) diff --git a/brep/handler/ci/ci-load.in b/brep/handler/ci/ci-load.in index b3c05f0..6f65777 100644 --- a/brep/handler/ci/ci-load.in +++ b/brep/handler/ci/ci-load.in @@ -10,6 +10,11 @@ # brep tenant id to this value and include the resulting URL in the response # message. # +# --cancel-url +# CI task canceling URL base for the response. If specified, the handler will +# append the brep tenant id to this value and include the resulting URL in +# the response message. +# # # Loader program (normally brep-load(1)). # @@ -36,6 +41,7 @@ shopt -s nullglob # Expand no-match globs to nothing rather than themselves. # The handler's own options. # result_url= +cancel_url= while [[ "$#" -gt 0 ]]; do case $1 in --result-url) @@ -43,6 +49,11 @@ while [[ "$#" -gt 0 ]]; do result_url="${1%/}" shift ;; + --cancel-url) + shift + cancel_url="${1%/}" + shift + ;; *) break ;; @@ -355,4 +366,11 @@ run "$loader" "${loader_options[@]}" "$loadtab" run rm -r "$data_dir" trace "CI request for '$spec' is queued$message_suffix" -exit_with_manifest 200 "CI request is queued$message_suffix" + +msg="CI request is queued$message_suffix" + +if [[ -n "$cancel_url" ]]; then + msg="$msg"$'\n'"to cancel CI request: $cancel_url=$reference&reason=" +fi + +exit_with_manifest 200 "$msg" diff --git a/mod/ci-common.cxx b/mod/ci-common.cxx index 7c41a7b..c0ef89f 100644 --- a/mod/ci-common.cxx +++ b/mod/ci-common.cxx @@ -720,4 +720,40 @@ namespace brep return r; } + + bool ci_start:: + cancel (const basic_mark&, + const basic_mark&, + const basic_mark* trace, + const string& reason, + odb::core::database& db, + const string& tid) const + { + using namespace odb::core; + + assert (!transaction::has_current ()); + + transaction tr (db.begin ()); + + shared_ptr t (db.find (tid)); + + if (t == nullptr) + return false; + + if (!t->archived) + { + t->archived = true; + db.update (t); + } + + tr.commit (); + + if (trace != nullptr) + *trace << "CI request " << tid << " is canceled: " + << (reason.size () < 50 + ? reason + : string (reason, 0, 50) + "..."); + + return true; + } } diff --git a/mod/ci-common.hxx b/mod/ci-common.hxx index 8efeb26..848bca1 100644 --- a/mod/ci-common.hxx +++ b/mod/ci-common.hxx @@ -111,6 +111,20 @@ namespace brep const string& type, const string& id) const; + // Cancel previously created or started CI request. Return false if there + // is no tenant for the specified tenant id. Note that the reason argument + // is only used for tracing. + // + // Note: should be called out of the database transaction. + // + bool + cancel (const basic_mark& error, + const basic_mark& warn, + const basic_mark* trace, + const string& reason, + odb::core::database&, + const string& tenant_id) const; + // Helpers. // @@ -122,22 +136,6 @@ namespace brep private: shared_ptr options_; }; - - class ci_cancel - { - public: - void - init (shared_ptr, shared_ptr); - - // @@ TODO Archive the tenant. - // - void - cancel (/*...*/); - - private: - shared_ptr options_; - shared_ptr build_db_; - }; } #endif // MOD_CI_COMMON_HXX diff --git a/mod/mod-build-configs.cxx b/mod/mod-build-configs.cxx index 9282544..0ac4615 100644 --- a/mod/mod-build-configs.cxx +++ b/mod/mod-build-configs.cxx @@ -30,8 +30,6 @@ build_configs (const build_configs& r) void brep::build_configs:: init (scanner& s) { - HANDLER_DIAG; - options_ = make_shared ( s, unknown_mode::fail, unknown_mode::fail); diff --git a/mod/mod-build-force.cxx b/mod/mod-build-force.cxx index 168a835..ea921e9 100644 --- a/mod/mod-build-force.cxx +++ b/mod/mod-build-force.cxx @@ -42,8 +42,6 @@ build_force (const build_force& r, const tenant_service_map& tsm) void brep::build_force:: init (scanner& s) { - HANDLER_DIAG; - options_ = make_shared ( s, unknown_mode::fail, unknown_mode::fail); diff --git a/mod/mod-build-log.cxx b/mod/mod-build-log.cxx index c8e803b..5487f6e 100644 --- a/mod/mod-build-log.cxx +++ b/mod/mod-build-log.cxx @@ -34,8 +34,6 @@ build_log (const build_log& r) void brep::build_log:: init (scanner& s) { - HANDLER_DIAG; - options_ = make_shared ( s, unknown_mode::fail, unknown_mode::fail); diff --git a/mod/mod-build-result.cxx b/mod/mod-build-result.cxx index 64503aa..3ba18e1 100644 --- a/mod/mod-build-result.cxx +++ b/mod/mod-build-result.cxx @@ -49,8 +49,6 @@ build_result (const build_result& r, const tenant_service_map& tsm) void brep::build_result:: init (scanner& s) { - HANDLER_DIAG; - options_ = make_shared ( s, unknown_mode::fail, unknown_mode::fail); diff --git a/mod/mod-builds.cxx b/mod/mod-builds.cxx index 30562f3..81d4649 100644 --- a/mod/mod-builds.cxx +++ b/mod/mod-builds.cxx @@ -50,8 +50,6 @@ builds (const builds& r) void brep::builds:: init (scanner& s) { - HANDLER_DIAG; - options_ = make_shared ( s, unknown_mode::fail, unknown_mode::fail); diff --git a/mod/mod-ci.cxx b/mod/mod-ci.cxx index 0045002..8c47bc4 100644 --- a/mod/mod-ci.cxx +++ b/mod/mod-ci.cxx @@ -22,6 +22,8 @@ using namespace butl; using namespace web; using namespace brep::cli; +// ci +// #ifdef BREP_CI_TENANT_SERVICE brep::ci:: ci (tenant_service_map& tsm) @@ -536,3 +538,65 @@ build_unloaded (tenant_service&& ts, } #endif #endif + +// ci_cancel +// +brep::ci_cancel:: +ci_cancel (const ci_cancel& r) + : database_module (r), + options_ (r.initialized_ ? r.options_ : nullptr) +{ +} + +void brep::ci_cancel:: +init (scanner& s) +{ + options_ = make_shared ( + s, unknown_mode::fail, unknown_mode::fail); + + if (options_->build_config_specified ()) + database_module::init (*options_, options_->build_db_retry ()); +} + +bool brep::ci_cancel:: +handle (request& rq, response& rs) +{ + HANDLER_DIAG; + + if (build_db_ == nullptr) + throw invalid_request (501, "not implemented"); + + params::ci_cancel params; + + try + { + name_value_scanner s (rq.parameters (1024)); + params = params::ci_cancel (s, unknown_mode::fail, unknown_mode::fail); + } + catch (const cli::exception& e) + { + throw invalid_request (400, e.what ()); + } + + const string& reason (params.reason ()); + + if (reason.empty ()) + throw invalid_request (400, "missing CI request cancellation reason"); + + // Verify the tenant id. + // + const string tid (params.id ()); + + if (tid.empty ()) + throw invalid_request (400, "invalid CI request id"); + + if (!cancel (error, warn, verb_ ? &trace : nullptr, reason, *build_db_, tid)) + throw invalid_request (400, "unknown CI request id"); + + // We have all the data, so don't buffer the response content. + // + ostream& os (rs.content (200, "text/plain;charset=utf-8", false)); + os << "CI request " << tid << " has been canceled"; + + return true; +} diff --git a/mod/mod-ci.hxx b/mod/mod-ci.hxx index a83b9d3..bd91e99 100644 --- a/mod/mod-ci.hxx +++ b/mod/mod-ci.hxx @@ -16,6 +16,7 @@ #include #include +#include #if defined(BREP_CI_TENANT_SERVICE_UNLOADED) && !defined(BREP_CI_TENANT_SERVICE) # error BREP_CI_TENANT_SERVICE must be defined if BREP_CI_TENANT_SERVICE_UNLOADED is defined @@ -23,10 +24,6 @@ #ifdef BREP_CI_TENANT_SERVICE # include - -#ifdef BREP_CI_TENANT_SERVICE_UNLOADED -# include -#endif #endif namespace brep @@ -110,6 +107,32 @@ namespace brep tenant_service_map& tenant_service_map_; #endif }; + + class ci_cancel: public database_module, + private ci_start + { + public: + ci_cancel () = default; + + // Create a shallow copy (handling instance) if initialized and a deep + // copy (context exemplar) otherwise. + // + explicit + ci_cancel (const ci_cancel&); + + virtual bool + handle (request&, response&) override; + + virtual const cli::options& + cli_options () const override {return options::ci_cancel::description ();} + + private: + virtual void + init (cli::scanner&) override; + + private: + shared_ptr options_; + }; } #endif // MOD_MOD_CI_HXX diff --git a/mod/mod-package-details.cxx b/mod/mod-package-details.cxx index fcd50da..1fb51da 100644 --- a/mod/mod-package-details.cxx +++ b/mod/mod-package-details.cxx @@ -37,8 +37,6 @@ package_details (const package_details& r) void brep::package_details:: init (scanner& s) { - HANDLER_DIAG; - options_ = make_shared ( s, unknown_mode::fail, unknown_mode::fail); diff --git a/mod/mod-repository-details.cxx b/mod/mod-repository-details.cxx index 082903b..93b6c9e 100644 --- a/mod/mod-repository-details.cxx +++ b/mod/mod-repository-details.cxx @@ -39,8 +39,6 @@ repository_details (const repository_details& r) void brep::repository_details:: init (scanner& s) { - HANDLER_DIAG; - options_ = make_shared ( s, unknown_mode::fail, unknown_mode::fail); diff --git a/mod/mod-repository-root.cxx b/mod/mod-repository-root.cxx index 34b4007..bc861a8 100644 --- a/mod/mod-repository-root.cxx +++ b/mod/mod-repository-root.cxx @@ -133,6 +133,7 @@ namespace brep #else ci_ (make_shared ()), #endif + ci_cancel_ (make_shared ()), upload_ (make_shared ()) { } @@ -201,6 +202,10 @@ namespace brep #else : make_shared (*r.ci_)), #endif + ci_cancel_ ( + r.initialized_ + ? r.ci_cancel_ + : make_shared (*r.ci_cancel_)), upload_ ( r.initialized_ ? r.upload_ @@ -231,6 +236,7 @@ namespace brep append (r, build_configs_->options ()); append (r, submit_->options ()); append (r, ci_->options ()); + append (r, ci_cancel_->options ()); append (r, upload_->options ()); return r; } @@ -277,6 +283,7 @@ namespace brep sub_init (*build_configs_, "build_configs"); sub_init (*submit_, "submit"); sub_init (*ci_, "ci"); + sub_init (*ci_cancel_, "ci-cancel"); sub_init (*upload_, "upload"); // Parse own configuration options. @@ -473,6 +480,13 @@ namespace brep return handle ("ci", param); } + else if (func == "ci-cancel") + { + if (handler_ == nullptr) + handler_.reset (new ci_cancel (*ci_cancel_)); + + return handle ("ci-cancel", param); + } else if (func == "upload") { if (handler_ == nullptr) diff --git a/mod/mod-repository-root.hxx b/mod/mod-repository-root.hxx index aa60fda..990587e 100644 --- a/mod/mod-repository-root.hxx +++ b/mod/mod-repository-root.hxx @@ -25,6 +25,7 @@ namespace brep class build_configs; class submit; class ci; + class ci_cancel; class upload; class repository_root: public handler @@ -74,6 +75,7 @@ namespace brep shared_ptr build_configs_; shared_ptr submit_; shared_ptr ci_; + shared_ptr ci_cancel_; shared_ptr upload_; shared_ptr options_; diff --git a/mod/module.cli b/mod/module.cli index 5f63930..5133935 100644 --- a/mod/module.cli +++ b/mod/module.cli @@ -796,10 +796,6 @@ namespace brep } }; - class ci_cancel - { - }; - class ci: ci_start, build, build_db, page, repository_url, handler { // Classic CI-specific options. @@ -815,7 +811,11 @@ namespace brep } }; - class ci_github: ci_start, ci_cancel, build, build_db, handler + class ci_cancel: build, build_db, handler + { + }; + + class ci_github: ci_start, build, build_db, handler { // GitHub CI-specific options (e.g., request timeout when invoking // GitHub APIs). @@ -1099,6 +1099,22 @@ namespace brep string simulate; }; + // All parameters are non-optional. + // + class ci_cancel + { + // CI task tenant id. + // + // Note that the ci-cancel parameter is renamed to '_' by the root + // handler (see the request_proxy class for details). + // + string id | _; + + // CI task canceling reason. Must not be empty. + // + string reason; + }; + // Parameters other than challenge must be all present. // // Note also that besides these parameters there can be others. We don't -- cgit v1.1