Publish ITWorx Pulse source
Public source validation / validate (push) Failing after 3m8s

This commit is contained in:
ITWorx Pulse release export
2026-09-03 02:09:19 +02:00
commit bd774932d5
614 changed files with 77116 additions and 0 deletions
+182
View File
@@ -0,0 +1,182 @@
package service
import (
"context"
"encoding/json"
"errors"
"fmt"
"time"
"github.com/itworx/pulse/internal/probe"
"github.com/jackc/pgx/v5/pgxpool"
)
type PostgresProvider struct {
Pool *pgxpool.Pool
Policy StatusPolicy
Now func() time.Time
}
func NewPostgresProvider(pool *pgxpool.Pool, policy StatusPolicy) (*PostgresProvider, error) {
if pool == nil {
return nil, errors.New("service status provider requires a database pool")
}
policy = policy.withDefaults()
if err := policy.Validate(); err != nil {
return nil, err
}
return &PostgresProvider{Pool: pool, Policy: policy}, nil
}
func (p *PostgresProvider) Snapshot(ctx context.Context) (Snapshot, error) {
if p == nil || p.Pool == nil {
return Snapshot{}, errors.New("service status provider is unavailable")
}
if ctx == nil {
return Snapshot{}, errors.New("service status context is nil")
}
if err := ctx.Err(); err != nil {
return Snapshot{}, err
}
policy := p.Policy.withDefaults()
if err := policy.Validate(); err != nil {
return Snapshot{}, err
}
now := time.Now().UTC()
if p.Now != nil {
now = p.Now().UTC()
}
rows, err := p.Pool.Query(ctx, `
WITH bounded_services AS (
SELECT id, entity_id, source_id, name, description, revision, archived_at, created_at, updated_at
FROM services
WHERE archived_at IS NULL
ORDER BY id ASC
LIMIT $1
), bounded_results AS (
SELECT s.id::text AS service_id, s.entity_id::text AS entity_id, s.source_id::text AS source_id,
s.name, s.description, s.revision, s.archived_at, s.created_at, s.updated_at,
COALESCE((SELECT jsonb_agg(jsonb_build_object('id', pc.id::text, 'name', pc.name, 'type', pc.probe_type, 'intervalSeconds', pc.interval_seconds, 'timeoutSeconds', pc.timeout_seconds, 'enabled', pc.enabled, 'followRedirects', pc.follow_redirects, 'verifyTls', pc.verify_tls, 'revision', pc.revision) ORDER BY pc.id) FROM probes pc WHERE pc.service_id = s.id AND pc.archived_at IS NULL), '[]'::jsonb) AS probe_configs,
p.id::text AS probe_id, pr.id::text AS result_id, pr.observed_at, pr.completed_at,
pr.state, pr.response_time_ms, pr.status_code, pr.error_class, pr.error_message, pr.attributes,
certificate.id::text AS certificate_id, certificate.service_id::text AS certificate_service_id,
certificate.endpoint_id::text AS certificate_endpoint_id, certificate.observed_at AS certificate_observed_at,
certificate.expires_at AS certificate_expires_at, certificate.issuer AS certificate_issuer,
certificate.subject AS certificate_subject, certificate.hostname_valid AS certificate_hostname_valid,
certificate.verification_state AS certificate_verification_state,
row_number() OVER (PARTITION BY s.id ORDER BY pr.observed_at DESC NULLS LAST, p.id ASC, pr.id ASC) AS result_rank
FROM bounded_services s
LEFT JOIN probes p ON p.service_id = s.id AND p.archived_at IS NULL
LEFT JOIN probe_results pr ON pr.probe_id = p.id
LEFT JOIN LATERAL (
SELECT sc.id, sc.service_id, sc.endpoint_id, sc.observed_at, sc.expires_at,
sc.issuer, sc.subject, sc.hostname_valid, sc.verification_state
FROM service_certificates sc
WHERE sc.service_id = s.id
ORDER BY sc.observed_at DESC, sc.id ASC
LIMIT 1
) certificate ON true
)
SELECT service_id, COALESCE(entity_id, ''), COALESCE(source_id, ''), name, description, revision, archived_at, created_at, updated_at, probe_configs,
probe_id, result_id, observed_at, completed_at, state, response_time_ms, status_code, error_class, error_message, attributes,
certificate_id, certificate_service_id, certificate_endpoint_id, certificate_observed_at,
certificate_expires_at, certificate_issuer, certificate_subject, certificate_hostname_valid,
certificate_verification_state
FROM bounded_results
WHERE result_rank <= $2 OR result_rank IS NULL
ORDER BY service_id ASC, observed_at DESC NULLS LAST, probe_id ASC NULLS LAST, result_id ASC NULLS LAST`, policy.MaxServices, policy.MaxHistory)
if err != nil {
return Snapshot{}, fmt.Errorf("query service status: %w", err)
}
defer rows.Close()
inputs := make([]ServiceInput, 0, policy.MaxServices)
byID := make(map[string]int, policy.MaxServices)
for rows.Next() {
var serviceID, entityID, sourceID, name, description string
var revision int64
var archivedAt, createdAt, updatedAt *time.Time
var probeID, resultID *string
var observedAt, completedAt *time.Time
var state, errorClass, errorMessage *string
var responseTimeMS, statusCode *int
var certificateID, certificateServiceID, certificateEndpointID *string
var certificateObservedAt, certificateExpiresAt *time.Time
var certificateIssuer, certificateSubject, certificateVerificationState *string
var certificateHostnameValid *bool
var attributes []byte
var probeConfigs []byte
if err := rows.Scan(&serviceID, &entityID, &sourceID, &name, &description, &revision, &archivedAt, &createdAt, &updatedAt, &probeConfigs, &probeID, &resultID, &observedAt, &completedAt, &state, &responseTimeMS, &statusCode, &errorClass, &errorMessage, &attributes, &certificateID, &certificateServiceID, &certificateEndpointID, &certificateObservedAt, &certificateExpiresAt, &certificateIssuer, &certificateSubject, &certificateHostnameValid, &certificateVerificationState); err != nil {
return Snapshot{}, fmt.Errorf("scan service status: %w", err)
}
index, exists := byID[serviceID]
if !exists {
item := ServiceInput{Service: Service{ID: serviceID, EntityID: entityID, SourceID: sourceID, Name: name, Description: description, State: StateUnknown, Revision: revision, ArchivedAt: archivedAt, CreatedAt: timeValue(createdAt), UpdatedAt: timeValue(updatedAt)}}
inputs = append(inputs, item)
index = len(inputs) - 1
byID[serviceID] = index
if len(probeConfigs) > 0 {
if err := json.Unmarshal(probeConfigs, &inputs[index].ProbeConfigs); err != nil {
return Snapshot{}, fmt.Errorf("decode service probe configuration: %w", err)
}
}
}
input := &inputs[index]
if input.LatestCertificate == nil && certificateID != nil && certificateServiceID != nil && certificateObservedAt != nil && certificateVerificationState != nil {
input.LatestCertificate = &probe.Certificate{
ID: *certificateID,
ServiceID: *certificateServiceID,
EndpointID: stringValue(certificateEndpointID),
ObservedAt: certificateObservedAt.UTC(),
ExpiresAt: certificateExpiresAt,
Issuer: stringValue(certificateIssuer),
Subject: stringValue(certificateSubject),
HostnameValid: certificateHostnameValid,
VerificationState: *certificateVerificationState,
}
}
if probeID == nil || resultID == nil || observedAt == nil || state == nil {
continue
}
result := probe.Result{ID: *resultID, ProbeID: *probeID, ObservedAt: observedAt.UTC(), CompletedAt: timeValue(completedAt).UTC(), State: *state, ResponseTimeMS: responseTimeMS, StatusCode: statusCode}
if errorClass != nil {
result.ErrorClass = *errorClass
}
if errorMessage != nil {
result.ErrorMessage = *errorMessage
}
if len(attributes) > 0 {
if err := json.Unmarshal(attributes, &result.Attributes); err != nil {
return Snapshot{}, fmt.Errorf("decode service result attributes: %w", err)
}
}
appendProbeResult(input, *probeID, result)
}
if err := rows.Err(); err != nil {
return Snapshot{}, fmt.Errorf("read service status rows: %w", err)
}
return BuildSnapshot(now, inputs, policy)
}
func stringValue(value *string) string {
if value == nil {
return ""
}
return *value
}
func appendProbeResult(input *ServiceInput, probeID string, result probe.Result) {
for index := range input.Probes {
if input.Probes[index].ProbeID == probeID {
input.Probes[index].Results = append(input.Probes[index].Results, result)
return
}
}
input.Probes = append(input.Probes, ProbeHistory{ProbeID: probeID, Results: []probe.Result{result}})
}
func timeValue(value *time.Time) time.Time {
if value == nil {
return time.Time{}
}
return value.UTC()
}