Skip to content

Commit 7069401

Browse files
Merge pull request #3077 from SwiftPackageIndex/add-mon-001-check-to-alerting
Add MON-001 check to alerting
2 parents d16ad20 + 3e845b7 commit 7069401

File tree

2 files changed

+72
-2
lines changed

2 files changed

+72
-2
lines changed

Sources/App/Commands/Alerting.swift

Lines changed: 58 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,9 @@ enum Alerting {
4848
Current.logger().info("Validation time interval: \(timePeriod.hours)h, limit: \(limit)")
4949

5050
let builds = try await Alerting.fetchBuilds(on: context.application.db, timePeriod: timePeriod, limit: limit)
51-
try await Alerting.runChecks(for: builds)
51+
try await Alerting.runBuildChecks(for: builds)
52+
try await Alerting.runMonitoring001Check(on: context.application.db, timePeriod: timePeriod)
53+
.log(check: "CHECK_MON_001")
5254
}
5355
}
5456
}
@@ -84,7 +86,7 @@ extension Alerting {
8486
}
8587
}
8688

87-
static func runChecks(for builds: [BuildInfo]) async throws {
89+
static func runBuildChecks(for builds: [BuildInfo]) async throws {
8890
// to do
8991
// - [ ] doc gen is configured but it failed
9092

@@ -128,6 +130,49 @@ extension Alerting {
128130
.map(BuildInfo.init)
129131
return builds
130132
}
133+
134+
struct Mon001Row: Decodable, CustomStringConvertible {
135+
var owner: String
136+
var repository: String
137+
var status: Package.Status?
138+
var processingStage: Package.ProcessingStage?
139+
var updatedAt: Date
140+
141+
enum CodingKeys: String, CodingKey {
142+
case owner
143+
case repository
144+
case status
145+
case processingStage = "processing_stage"
146+
case updatedAt = "updated_at"
147+
}
148+
149+
var description: String {
150+
"\(owner)/\(repository) \(status.map { $0.rawValue } ?? "-") \(processingStage.map { $0.rawValue } ?? "-") \(updatedAt)"
151+
}
152+
}
153+
154+
static func runMonitoring001Check(on database: Database, timePeriod: TimeAmount) async throws -> Alerting.Validation {
155+
guard let db = database as? SQLDatabase else {
156+
fatalError("Database must be an SQLDatabase ('as? SQLDatabase' must succeed)")
157+
}
158+
let rows = try await db.raw("""
159+
SELECT
160+
r.owner,
161+
r.name AS "repository",
162+
p.status,
163+
p.processing_stage,
164+
r.updated_at
165+
FROM
166+
repositories r
167+
JOIN packages p ON r.package_id = p.id
168+
WHERE
169+
r.updated_at < now() - INTERVAL \(literal: "\(timePeriod.hours) hours")
170+
ORDER BY
171+
updated_at
172+
""")
173+
.all(decoding: Mon001Row.self)
174+
return rows.isValid()
175+
}
131176
}
132177

133178

@@ -222,6 +267,17 @@ extension [Alerting.BuildInfo] {
222267
}
223268

224269

270+
extension [Alerting.Mon001Row] {
271+
func isValid() -> Alerting.Validation {
272+
if isEmpty {
273+
return .ok
274+
} else {
275+
return .failed(reasons: sorted(by: { $0.updatedAt < $1.updatedAt }).map { "Outdated package: \($0)" })
276+
}
277+
}
278+
}
279+
280+
225281
private extension TimeAmount {
226282
var timeInterval: TimeInterval {
227283
Double(nanoseconds) * 1e-9

Tests/AppTests/AlertingTests.swift

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,20 @@ class AlertingTests: XCTestCase {
135135
.failed(reasons: ["Global success rate of 40.1% out of bounds"]))
136136
}
137137
}
138+
139+
func test_Mon001Row_isValid() throws {
140+
XCTAssertEqual([Alerting.Mon001Row]().isValid(), .ok)
141+
XCTAssertEqual(
142+
[
143+
Alerting.Mon001Row(owner: "bar", repository: "2", status: .ok, processingStage: .analysis, updatedAt: .t1),
144+
Alerting.Mon001Row(owner: "foo", repository: "1", status: nil, processingStage: nil, updatedAt: .t0)
145+
].isValid(),
146+
.failed(reasons: [
147+
"Outdated package: foo/1 - - 1970-01-01 00:00:00 +0000",
148+
"Outdated package: bar/2 ok analysis 1970-01-01 00:00:01 +0000"
149+
])
150+
)
151+
}
138152
}
139153

140154

0 commit comments

Comments
 (0)