Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 8 additions & 8 deletions comp/syntheticstestscheduler/common/data.go
Original file line number Diff line number Diff line change
Expand Up @@ -91,12 +91,12 @@ const (
OperatorIsNot Operator = "isNot"
// OperatorMoreThan checks if greater than target.
OperatorMoreThan Operator = "moreThan"
// OperatorMoreThanOrEquals checks if greater than or equal to target.
OperatorMoreThanOrEquals Operator = "moreThanOrEquals"
// OperatorMoreThanOrEqual checks if greater than or equal to target.
OperatorMoreThanOrEqual Operator = "moreThanOrEqual"
// OperatorLessThan checks if less than target.
OperatorLessThan Operator = "lessThan"
// OperatorLessThanOrEquals checks if less than or equal to target.
OperatorLessThanOrEquals Operator = "lessThanOrEquals"
// OperatorLessThanOrEqual checks if less than or equal to target.
OperatorLessThanOrEqual Operator = "lessThanOrEqual"
)

// AssertionType represents the type of metric being asserted in a network test.
Expand Down Expand Up @@ -127,10 +127,10 @@ const (

// Assertion represents a single condition to be checked in a network test.
type Assertion struct {
Operator Operator `json:"operator"`
Property AssertionSubType `json:"property"`
Target string `json:"target"`
Type AssertionType `json:"type"`
Operator Operator `json:"operator"`
Property *AssertionSubType `json:"property,omitempty"`
Target string `json:"target"`
Type AssertionType `json:"type"`
}

// UnmarshalJSON is a Custom unmarshal for SyntheticsTestConfig
Expand Down
2 changes: 1 addition & 1 deletion comp/syntheticstestscheduler/common/data_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,7 @@ func TestSyntheticsTestConfig_UnmarshalJSON_AllFields(t *testing.T) {
Assertions: []Assertion{
{
Operator: OperatorLessThan,
Property: AssertionSubTypeAverage,
Property: &[]AssertionSubType{AssertionSubTypeAverage}[0],
Target: "100",
Type: AssertionTypeLatency,
},
Expand Down
37 changes: 28 additions & 9 deletions comp/syntheticstestscheduler/common/result.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,12 @@ import (

// AssertionResult represents a validation check comparing expected and actual values.
type AssertionResult struct {
Operator Operator `json:"operator"`
Type AssertionType `json:"type"`
Property AssertionSubType `json:"property"`
Expected interface{} `json:"expected"`
Actual interface{} `json:"actual"`
Valid bool `json:"valid"`
Operator Operator `json:"operator"`
Type AssertionType `json:"type"`
Property *AssertionSubType `json:"property,omitempty"`
Expected interface{} `json:"expected"`
Actual interface{} `json:"actual"`
Valid bool `json:"valid"`
}

// Compare evaluates the assertion result by comparing the actual and expected values.
Expand Down Expand Up @@ -58,11 +58,11 @@ func (a *AssertionResult) Compare() error {
switch a.Operator {
case OperatorLessThan:
a.Valid = act < exp
case OperatorLessThanOrEquals:
case OperatorLessThanOrEqual:
a.Valid = act <= exp
case OperatorMoreThan:
a.Valid = act > exp
case OperatorMoreThanOrEquals:
case OperatorMoreThanOrEqual:
a.Valid = act >= exp
default:
return fmt.Errorf("unsupported operator %v", a.Operator)
Expand Down Expand Up @@ -116,13 +116,32 @@ type Result struct {
Assertions []AssertionResult `json:"assertions"`
Failure ErrorOrFailure `json:"failure"`
Duration int64 `json:"duration"`
Request Request `json:"request"`
Config Config `json:"config"`
Netstats NetStats `json:"netstats"`
Netpath payload.NetworkPath `json:"netpath"`
Status string `json:"status"`
RunType string `json:"runType"`
}

// ResultRequest represents the request configuration in the test result.
type ResultRequest struct {
DestinationService *string `json:"destinationService,omitempty"`
Port *int `json:"port,omitempty"`
MaxTTL *int `json:"maxTtl,omitempty"`
Host string `json:"host"`
TracerouteQueries *int `json:"tracerouteQueries,omitempty"`
E2eQueries *int `json:"e2eQueries,omitempty"`
SourceService *string `json:"sourceService,omitempty"`
Timeout *int `json:"timeout,omitempty"`
TCPMethod payload.TCPMethod `json:"tcpMethod,omitempty"`
}

// Config represents the test configuration in the result.
type Config struct {
Assertions []Assertion `json:"assertions"`
Request ResultRequest `json:"request"`
}

// Test represents the definition of a test including metadata and version.
type Test struct {
ID string `json:"id"`
Expand Down
4 changes: 2 additions & 2 deletions comp/syntheticstestscheduler/common/result_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ func TestAssertionResult_Compare(t *testing.T) {
{"actual equal", 10, 10, false, false},
{"actual more", 10, 20, false, false},
},
OperatorLessThanOrEquals: {
OperatorLessThanOrEqual: {
{"less is true", 10, 5, true, false},
{"equal is true", 10, 10, true, false},
{"more is false", 10, 20, false, false},
Expand All @@ -32,7 +32,7 @@ func TestAssertionResult_Compare(t *testing.T) {
{"equal is false", 10, 10, false, false},
{"less is false", 10, 5, false, false},
},
OperatorMoreThanOrEquals: {
OperatorMoreThanOrEqual: {
{"more is true", 10, 20, true, false},
{"equal is true", 10, 10, true, false},
{"less is false", 10, 5, false, false},
Expand Down
4 changes: 2 additions & 2 deletions comp/syntheticstestscheduler/impl/assertions.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ func runAssertion(assertion common.Assertion, stats common.NetStats) common.Asse
case common.AssertionTypePacketJitter:
actual = stats.Jitter
case common.AssertionTypeLatency:
switch assertion.Property {
switch *assertion.Property {
case common.AssertionSubTypeAverage:
actual = stats.Latency.Avg
case common.AssertionSubTypeMin:
Expand All @@ -48,7 +48,7 @@ func runAssertion(assertion common.Assertion, stats common.NetStats) common.Asse
}
}
case common.AssertionTypeNetworkHops:
switch assertion.Property {
switch *assertion.Property {
case common.AssertionSubTypeAverage:
actual = stats.Hops.Avg
case common.AssertionSubTypeMin:
Expand Down
6 changes: 3 additions & 3 deletions comp/syntheticstestscheduler/impl/assertions_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ func TestRunAssertion(t *testing.T) {
name: "Latency avg assertion",
assertion: common.Assertion{
Type: common.AssertionTypeLatency,
Property: common.AssertionSubTypeAverage,
Property: &[]common.AssertionSubType{common.AssertionSubTypeAverage}[0],
Operator: common.OperatorLessThan,
Target: "100",
},
Expand All @@ -58,7 +58,7 @@ func TestRunAssertion(t *testing.T) {
name: "Latency unsupported property",
assertion: common.Assertion{
Type: common.AssertionTypeLatency,
Property: "median",
Property: &[]common.AssertionSubType{"median"}[0],
Operator: common.OperatorLessThan,
Target: "100",
},
Expand All @@ -69,7 +69,7 @@ func TestRunAssertion(t *testing.T) {
name: "Hops max assertion",
assertion: common.Assertion{
Type: common.AssertionTypeNetworkHops,
Property: common.AssertionSubTypeMax,
Property: &[]common.AssertionSubType{common.AssertionSubTypeMax}[0],
Operator: common.OperatorLessThan,
Target: "15",
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -388,7 +388,7 @@ func Test_SyntheticsTestScheduler_Processing(t *testing.T) {
"config":{"assertions":[],"request":{"host":"example.com","port":443,"tcp_method":"SYN","probe_count":3,"traceroute_count":1,"max_ttl":30,"timeout":5,"source_service":"frontend","destination_service":"backend"}},
"org_id":12345,"main_dc":"us1.staging.dog","public_id":"puf-9fm-c89","run_type":"scheduled"
}`},
expectedEventJSON: `{"location":{ "id":"agent:test-hostname"},"_dd":{},"result":{"id":"4907739274636687553","initialId":"4907739274636687553","testFinishedAt":1756901488592,"testStartedAt":1756901488591,"testTriggeredAt":1756901488590,"assertions":[],"failure":null,"duration":1,"request":{"host":"example.com","port":443,"maxTtl":30,"timeout":150},"netstats":{"packetsSent":0,"packetsReceived":0,"packetLossPercentage":0,"jitter":0,"latency":{"avg":0,"min":0,"max":0},"hops":{"avg":0,"min":0,"max":0}},"netpath":{"timestamp":1756901488592,"agent_version":"","namespace":"","test_config_id":"puf-9fm-c89","test_result_id":"4907739274636687553","pathtrace_id":"pathtrace-id-111-example.com","origin":"synthetics","protocol":"TCP","source":{"name":"test-hostname","display_name":"test-hostname","hostname":"test-hostname"},"destination":{"hostname":"example.com","port":443},"traceroute":{"runs":[{"run_id":"1","source":{"ip_address":"","port":0},"destination":{"ip_address":"","port":0},"hops":[{"ttl":0,"ip_address":"1.1.1.1","reachable":false},{"ttl":0,"ip_address":"1.1.1.2","reachable":false}]}],"hop_count":{"avg":0,"min":0,"max":0}},"e2e_probe":{"rtts":null,"packets_sent":0,"packets_received":0,"packet_loss_percentage":0,"jitter":0,"rtt":{"avg":0,"min":0,"max":0}}},"status":"passed","runType":"scheduled"},"test":{"id":"puf-9fm-c89","subType":"TCP","type":"network","version":1},"v":1}`,
expectedEventJSON: `{"location":{"id":"agent:test-hostname"},"_dd":{},"result":{"id":"4907739274636687553","initialId":"4907739274636687553","testFinishedAt":1756901488592,"testStartedAt":1756901488591,"testTriggeredAt":1756901488590,"assertions":[],"failure":null,"duration":1,"config":{"assertions":[],"request":{"destinationService":"backend","port":443,"maxTtl":30,"host":"example.com","tracerouteQueries":1,"e2eQueries":3,"sourceService":"frontend","timeout":5,"tcpMethod":"SYN"}},"netstats":{"packetsSent":0,"packetsReceived":0,"packetLossPercentage":0,"jitter":0,"latency":{"avg":0,"min":0,"max":0},"hops":{"avg":0,"min":0,"max":0}},"netpath":{"timestamp":1756901488592,"agent_version":"","namespace":"","test_config_id":"puf-9fm-c89","test_result_id":"4907739274636687553","pathtrace_id":"pathtrace-id-111-example.com","origin":"synthetics","protocol":"TCP","source":{"name":"test-hostname","display_name":"test-hostname","hostname":"test-hostname"},"destination":{"hostname":"example.com","port":443},"traceroute":{"runs":[{"run_id":"1","source":{"ip_address":"","port":0},"destination":{"ip_address":"","port":0},"hops":[{"ttl":0,"ip_address":"1.1.1.1","reachable":false},{"ttl":0,"ip_address":"1.1.1.2","reachable":false}]}],"hop_count":{"avg":0,"min":0,"max":0}},"e2e_probe":{"rtts":null,"packets_sent":0,"packets_received":0,"packet_loss_percentage":0,"jitter":0,"rtt":{"avg":0,"min":0,"max":0}}},"status":"passed","runType":"scheduled"},"test":{"id":"puf-9fm-c89","subType":"tcp","type":"network","version":1},"v":1}`,
expectedRunTraceroute: func(_ context.Context, cfg config.Config, _ telemetry.Component) (payload.NetworkPath, error) {
return payload.NetworkPath{
PathtraceID: "pathtrace-id-111-" + cfg.DestHostname,
Expand Down
62 changes: 55 additions & 7 deletions comp/syntheticstestscheduler/impl/worker.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
"fmt"
"io"
"math/big"
"strings"
"sync"
"time"

Expand Down Expand Up @@ -135,6 +136,7 @@ func (s *syntheticsTestScheduler) runWorker(ctx context.Context, workerID int) {
Latency: result.E2eProbe.RTT,
Hops: result.Traceroute.HopCount,
})

status, err := s.sendResult(wResult)
if err != nil {
s.log.Debugf("[worker%d] error sending result: %s", workerID, err)
Expand Down Expand Up @@ -267,11 +269,59 @@ func runTraceroute(ctx context.Context, cfg config.Config, telemetry telemetry.C
return path, nil
}

// configRequestToResultRequest converts a ConfigRequest interface to a Result Request struct.
func configRequestToResultRequest(req common.ConfigRequest) common.ResultRequest {
result := common.ResultRequest{}

switch r := req.(type) {
case common.UDPConfigRequest:
result.Host = r.Host
result.Port = r.Port
result.DestinationService = r.DestinationService
result.SourceService = r.SourceService
result.MaxTTL = r.MaxTTL
result.Timeout = r.Timeout
result.TracerouteQueries = r.TracerouteCount
result.E2eQueries = r.ProbeCount
return result
case common.TCPConfigRequest:
result.Host = r.Host
result.Port = r.Port
result.DestinationService = r.DestinationService
result.SourceService = r.SourceService
result.MaxTTL = r.MaxTTL
result.Timeout = r.Timeout
result.TracerouteQueries = r.TracerouteCount
result.E2eQueries = r.ProbeCount
result.TCPMethod = r.TCPMethod
return result
case common.ICMPConfigRequest:
result.Host = r.Host
result.DestinationService = r.DestinationService
result.SourceService = r.SourceService
result.MaxTTL = r.MaxTTL
result.Timeout = r.Timeout
result.TracerouteQueries = r.TracerouteCount
result.E2eQueries = r.ProbeCount
return result
default:
result.Host = ""
result.DestinationService = nil
result.SourceService = nil
result.MaxTTL = nil
result.Timeout = nil
result.TracerouteQueries = nil
result.E2eQueries = nil
}

return result
}

// networkPathToTestResult converts a workerResult into the public TestResult structure.
func (s *syntheticsTestScheduler) networkPathToTestResult(w *workerResult) (*common.TestResult, error) {
t := common.Test{
ID: w.testCfg.cfg.PublicID,
SubType: string(w.testCfg.cfg.Config.Request.GetSubType()),
SubType: strings.ToLower(string(w.testCfg.cfg.Config.Request.GetSubType())),
Type: w.testCfg.cfg.Type,
Version: w.testCfg.cfg.Version,
}
Expand All @@ -297,11 +347,9 @@ func (s *syntheticsTestScheduler) networkPathToTestResult(w *workerResult) (*com
TestTriggeredAt: w.triggeredAt.UnixMilli(),
Duration: w.duration.Milliseconds(),
Assertions: w.assertionResult,
Request: common.Request{
Host: w.tracerouteCfg.DestHostname,
Port: int(w.tracerouteCfg.DestPort),
MaxTTL: int(w.tracerouteCfg.MaxTTL),
Timeout: int(w.tracerouteCfg.Timeout.Milliseconds()),
Config: common.Config{
Assertions: w.testCfg.cfg.Config.Assertions,
Request: configRequestToResultRequest(w.testCfg.cfg.Config.Request),
},
Netstats: common.NetStats{
PacketsSent: w.tracerouteResult.E2eProbe.PacketsSent,
Expand Down Expand Up @@ -367,7 +415,7 @@ func (s *syntheticsTestScheduler) setResultStatus(w *workerResult, result *commo

func hasAssertionOn100PacketLoss(assertionResults []common.AssertionResult) bool {
for _, assertion := range assertionResults {
if assertion.Type == common.AssertionTypePacketLoss && assertion.Operator == common.OperatorIs && assertion.Expected == "100" {
if assertion.Type == common.AssertionTypePacketLoss && assertion.Operator == common.OperatorIs && assertion.Expected == "1" {
return true
}
}
Expand Down
7 changes: 4 additions & 3 deletions comp/syntheticstestscheduler/impl/worker_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -334,7 +334,7 @@ func TestNetworkPathToTestResult(t *testing.T) {
assertionResult: []common.AssertionResult{{
Operator: common.OperatorIs,
Type: common.AssertionTypePacketLoss,
Expected: "100",
Expected: "1",
Valid: true,
}},
triggeredAt: now.Add(-3 * time.Second),
Expand Down Expand Up @@ -402,8 +402,9 @@ func TestNetworkPathToTestResult(t *testing.T) {
require.NotNil(t, got)
require.Equal(t, tt.worker.testCfg.cfg.PublicID, got.Test.ID)
require.Equal(t, "test-result-id-123", got.Result.ID)
require.Equal(t, tt.worker.tracerouteCfg.DestHostname, got.Result.Request.Host)
require.Equal(t, int(tt.worker.tracerouteCfg.DestPort), got.Result.Request.Port)
require.Equal(t, tt.worker.testCfg.cfg.Config.Request.(common.ICMPConfigRequest).Host, got.Result.Config.Request.Host)
require.Nil(t, got.Result.Config.Request.Port)
require.NotNil(t, got.Result.Netpath.Destination.Port)

if tt.expectFail {
require.Equal(t, "failed", got.Result.Status)
Expand Down
Loading