diff --git a/shard.lock b/shard.lock index f1827742..37d9330e 100644 --- a/shard.lock +++ b/shard.lock @@ -87,7 +87,7 @@ shards: git-repository: git: https://github.com/place-labs/git-repository.git - version: 1.5.0 + version: 1.6.0 google: git: https://github.com/placeos/google.git @@ -167,7 +167,7 @@ shards: place_calendar: git: https://github.com/placeos/calendar.git - version: 4.26.0 + version: 4.28.0 placeos-core-client: git: https://github.com/placeos/core-client.git @@ -183,7 +183,7 @@ shards: placeos-models: git: https://github.com/placeos/models.git - version: 9.72.3 + version: 9.74.0 placeos-resource: git: https://github.com/place-labs/resource.git diff --git a/spec/api/build_monitor_spec.cr b/spec/api/build_monitor_spec.cr new file mode 100644 index 00000000..935e0d5e --- /dev/null +++ b/spec/api/build_monitor_spec.cr @@ -0,0 +1,18 @@ +require "../helper" + +module PlaceOS::Core::Api + describe BuildMonitor, tags: "api" do + client = AC::SpecHelper.client + + describe "GET /api/core/v1/build" do + it "monitor job status" do + response = client.get("/api/core/v1/build/monitor") + response.status_code.should eq 200 + end + it "monitor job status" do + response = client.get("/api/core/v1/build/cancel/asdfasfasfasd") + response.status_code.should eq 404 + end + end + end +end diff --git a/src/api/build_monitor.cr b/src/api/build_monitor.cr new file mode 100644 index 00000000..ac980fcb --- /dev/null +++ b/src/api/build_monitor.cr @@ -0,0 +1,31 @@ +require "./application" + +module PlaceOS::Core::Api + class BuildMonitor < Application + base "/api/core/v1/build/" + + protected getter store = DriverStore.new + + @[AC::Route::GET("/monitor")] + def monitor( + @[AC::Param::Info(name: "state", description: "state of job to return. One of [pending,running,cancelled error,done]. Defaults to 'pending'", example: "pending")] + state : DriverStore::State = DriverStore::State::Pending, + ) : Array(DriverStore::TaskStatus) | String + result = store.monitor_jobs(state) + if result[:success] + render json: result[:output] + else + render status: result[:code], text: result[:output] + end + end + + @[AC::Route::DELETE("/cancel/:job")] + def cancel( + @[AC::Param::Info(name: "job", description: "ID of previously submitted compilation job")] + job : String, + ) : DriverStore::CancelStatus + result = store.cancel_job(job) + render status: result[:code], json: result[:output] + end + end +end diff --git a/src/placeos-core/driver_manager/build_api.cr b/src/placeos-core/driver_manager/build_api.cr index 340a3d11..159210c4 100644 --- a/src/placeos-core/driver_manager/build_api.cr +++ b/src/placeos-core/driver_manager/build_api.cr @@ -88,5 +88,27 @@ module PlaceOS::Core end end end + + def self.monitor(state : String) + host = URI.parse(Core.build_host) + ConnectProxy::HTTPClient.new(host) do |client| + path = "#{BUILD_API_BASE}/monitor" + params = URI::Params.encode({"state" => state}) + uri = "#{path}?#{params}" + rep = client.get(uri) + Log.debug { {message: "Getting build service monitor. Server respose: #{rep.status_code}", state: state} } + rep + end + end + + def self.cancel_job(job : String) + host = URI.parse(Core.build_host) + ConnectProxy::HTTPClient.new(host) do |client| + path = "#{BUILD_API_BASE}/cancel/#{URI.encode_www_form(job)}" + rep = client.delete(path) + Log.debug { {message: "Cancelling build job. Server respose: #{rep.status_code}", job: job} } + rep + end + end end end diff --git a/src/placeos-core/driver_manager/driver_store.cr b/src/placeos-core/driver_manager/driver_store.cr index bf0f5d54..37f867cf 100644 --- a/src/placeos-core/driver_manager/driver_store.cr +++ b/src/placeos-core/driver_manager/driver_store.cr @@ -143,5 +143,47 @@ module PlaceOS::Core @[JSON::Field(converter: Time::EpochConverter)] getter link_expiry : Time end + + enum State + Pending + Running + Cancelled + Error + Done + + def to_s(io : IO) : Nil + io << (member_name || value.to_s).downcase + end + + def to_s : String + String.build { |io| to_s(io) } + end + end + + record TaskStatus, state : State, id : String, message : String, + driver : String, repo : String, branch : String, commit : String, timestamp : Time do + include JSON::Serializable + end + + record CancelStatus, status : String, message : String do + include JSON::Serializable + end + + def monitor_jobs(state : State = State::Pending) + resp = BuildApi.monitor(state.to_s) + return {success: true, output: Array(TaskStatus).from_json(resp.body), code: 200} if resp.success? + {success: false, output: "Build service returned #{resp.status_code} with reponse #{resp.body}", code: resp.status_code} + rescue ex + {success: false, output: "Call to Build service endpoint failed with error #{ex.message}", code: 500} + end + + def cancel_job(job : String) + resp = BuildApi.cancel_job(job) + + return {success: true, output: CancelStatus.from_json(resp.body), code: resp.status_code} if resp.success? || resp.status_code == 409 + {success: false, output: CancelStatus.new("error", "Build service returned #{resp.status_code} with reponse #{resp.body}"), code: resp.status_code} + rescue ex + {success: false, output: CancelStatus.new("error", "Call to Build service endpoint failed with error #{ex.message}"), code: 500} + end end end