@@ -25,6 +25,7 @@ import (
2525 "io"
2626 "net/http"
2727 "net/http/httptest"
28+ "strings"
2829 "testing"
2930 "time"
3031
@@ -62,10 +63,11 @@ const (
6263 nspath = "/some/path"
6364 nspathHost = "host"
6465 // Fault injection tooling errors output
65- iptablesChainNotFoundError = "iptables: Bad rule (does a matching rule exist in that chain?)."
66- tcLatencyFaultExistsCommandOutput = `[{"kind":"netem","handle":"10:","parent":"1:1","options":{"limit":1000,"delay":{"delay":123456789,"jitter":4567,"correlation":0},"ecn":false,"gap":0}}]`
67- tcLossFaultExistsCommandOutput = `[{"kind":"netem","handle":"10:","dev":"eth0","parent":"1:1","options":{"limit":1000,"loss-random":{"loss":0.06,"correlation":0},"ecn":false,"gap":0}}]`
68- tcCommandEmptyOutput = `[]`
66+ iptablesChainNotFoundError = "iptables: Bad rule (does a matching rule exist in that chain?)."
67+ tcLatencyFaultExistsCommandOutput = `[{"kind":"netem","handle":"10:","parent":"1:1","options":{"limit":1000,"delay":{"delay":123456789,"jitter":4567,"correlation":0},"ecn":false,"gap":0}}]`
68+ tcLatencyFaultWithZeroDelayExistsCommandOutput = `[{"kind":"netem","handle":"10:","parent":"1:1","options":{"limit":1000,"ecn":false,"gap":0}}]`
69+ tcLossFaultExistsCommandOutput = `[{"kind":"netem","handle":"10:","dev":"eth0","parent":"1:1","options":{"limit":1000,"loss-random":{"loss":0.06,"correlation":0},"ecn":false,"gap":0}}]`
70+ tcCommandEmptyOutput = `[]`
6971 // Common Fault injection JSON responses
7072 happyFaultRunningResponse = `{"Status":"running"}`
7173 happyFaultStoppedResponse = `{"Status":"stopped"}`
@@ -234,6 +236,13 @@ var (
234236 "SourcesToFilter" : ipSourcesToFilter ,
235237 }
236238
239+ happyNetworkLatencyReqBodyWithZeroDelay = map [string ]interface {}{
240+ "DelayMilliseconds" : 0 ,
241+ "JitterMilliseconds" : jitterMilliseconds ,
242+ "Sources" : ipSources ,
243+ "SourcesToFilter" : ipSourcesToFilter ,
244+ }
245+
237246 happyNetworkPacketLossReqBody = map [string ]interface {}{
238247 "LossPercent" : lossPercent ,
239248 "Sources" : ipSources ,
@@ -2175,6 +2184,39 @@ func generateStartNetworkLatencyTestCases() []networkFaultInjectionTestCase {
21752184 },
21762185 expectedResponseJSON : happyFaultRunningResponse ,
21772186 },
2187+ {
2188+ name : "zero-delay" ,
2189+ expectedStatusCode : 200 ,
2190+ requestBody : happyNetworkLatencyReqBodyWithZeroDelay ,
2191+ expectedResponseBody : types .NewNetworkFaultInjectionSuccessResponse ("running" ),
2192+ setAgentStateExpectations : func (agentState * mock_state.MockAgentState , netConfigClient * netconfig.NetworkConfigClient ) {
2193+ agentState .EXPECT ().GetTaskMetadataWithTaskNetworkConfig (endpointId , netConfigClient ).Return (happyTaskResponse , nil )
2194+ },
2195+ setExecExpectations : func (exec * mock_execwrapper.MockExec , ctrl * gomock.Controller ) {
2196+ ctx , cancel := context .WithTimeout (context .Background (), ctxTimeoutDuration )
2197+ mockCMD := mock_execwrapper .NewMockCmd (ctrl )
2198+
2199+ commandList := strings .Split (
2200+ fmt .Sprintf ("nsenter --net=/some/path " + tcAddQdiscLatencyCommandString , "eth0" , 0 , jitterMilliseconds ),
2201+ " " )
2202+ gomock .InOrder (
2203+ exec .EXPECT ().NewExecContextWithTimeout (gomock .Any (), gomock .Any ()).Times (1 ).Return (ctx , cancel ),
2204+ // Check existing fault
2205+ exec .EXPECT ().CommandContext (gomock .Any (), gomock .Any (), gomock .Any ()).Times (1 ).Return (mockCMD ),
2206+ mockCMD .EXPECT ().CombinedOutput ().Times (1 ).Return ([]byte (tcCommandEmptyOutput ), nil ),
2207+ )
2208+ // Add root handler
2209+ exec .EXPECT ().CommandContext (gomock .Any (), gomock .Any (), gomock .Any ()).Times (1 ).Return (mockCMD )
2210+ mockCMD .EXPECT ().CombinedOutput ().Times (1 ).Return ([]byte (tcCommandEmptyOutput ), nil )
2211+ // Add lantecy handler
2212+ exec .EXPECT ().CommandContext (gomock .Any (), commandList [0 ], commandList [1 :]).Times (1 ).Return (mockCMD )
2213+ mockCMD .EXPECT ().CombinedOutput ().Times (1 ).Return ([]byte (tcCommandEmptyOutput ), nil )
2214+ // Add targets to the handler
2215+ exec .EXPECT ().CommandContext (gomock .Any (), gomock .Any (), gomock .Any ()).Times (5 ).Return (mockCMD )
2216+ mockCMD .EXPECT ().CombinedOutput ().Times (5 ).Return ([]byte (tcCommandEmptyOutput ), nil )
2217+ },
2218+ expectedResponseJSON : happyFaultRunningResponse ,
2219+ },
21782220 {
21792221 name : "no-existing-fault - two interfaces" ,
21802222 expectedStatusCode : 200 ,
@@ -2215,6 +2257,23 @@ func generateStartNetworkLatencyTestCases() []networkFaultInjectionTestCase {
22152257 },
22162258 expectedResponseJSON : fmt .Sprintf (errorResponse , latencyFaultAlreadyRunningError ),
22172259 },
2260+ {
2261+ name : "existing-network-latency-fault-with-0-delay" ,
2262+ expectedStatusCode : 409 ,
2263+ requestBody : happyNetworkLatencyReqBody ,
2264+ expectedResponseBody : types .NewNetworkFaultInjectionErrorResponse (latencyFaultAlreadyRunningError ),
2265+ setAgentStateExpectations : func (agentState * mock_state.MockAgentState , netConfigClient * netconfig.NetworkConfigClient ) {
2266+ agentState .EXPECT ().GetTaskMetadataWithTaskNetworkConfig (endpointId , netConfigClient ).Return (happyTaskResponse , nil )
2267+ },
2268+ setExecExpectations : func (exec * mock_execwrapper.MockExec , ctrl * gomock.Controller ) {
2269+ ctx , cancel := context .WithTimeout (context .Background (), ctxTimeoutDuration )
2270+ mockCMD := mock_execwrapper .NewMockCmd (ctrl )
2271+ exec .EXPECT ().NewExecContextWithTimeout (gomock .Any (), gomock .Any ()).Times (1 ).Return (ctx , cancel )
2272+ exec .EXPECT ().CommandContext (gomock .Any (), gomock .Any (), gomock .Any ()).Times (1 ).Return (mockCMD )
2273+ mockCMD .EXPECT ().CombinedOutput ().Times (1 ).Return ([]byte (tcLatencyFaultWithZeroDelayExistsCommandOutput ), nil )
2274+ },
2275+ expectedResponseJSON : fmt .Sprintf (errorResponse , latencyFaultAlreadyRunningError ),
2276+ },
22182277 {
22192278 name : "existing-network-packet-loss-fault" ,
22202279 expectedStatusCode : 409 ,
@@ -2357,6 +2416,21 @@ func generateStartNetworkLatencyTestCases() []networkFaultInjectionTestCase {
23572416 },
23582417 expectedResponseJSON : fmt .Sprintf (errorResponse , fmt .Sprintf (types .MissingRequiredFieldError , "JitterMilliseconds" )),
23592418 },
2419+ {
2420+ name : fmt .Sprintf ("%s invalid delay and jitter" , startNetworkLatencyTestPrefix ),
2421+ expectedStatusCode : 400 ,
2422+ requestBody : map [string ]interface {}{
2423+ "DelayMilliseconds" : 0 ,
2424+ "JitterMilliseconds" : 0 ,
2425+ "Sources" : ipSources ,
2426+ "SourcesToFilter" : ipSourcesToFilter ,
2427+ },
2428+ expectedResponseBody : types .NewNetworkFaultInjectionErrorResponse (types .ZeroDelayAndJitterError ),
2429+ setAgentStateExpectations : func (agentState * mock_state.MockAgentState , netConfigClient * netconfig.NetworkConfigClient ) {
2430+ agentState .EXPECT ().GetTaskMetadataWithTaskNetworkConfig (endpointId , netConfigClient ).Return (state.TaskResponse {}, nil ).Times (0 )
2431+ },
2432+ expectedResponseJSON : fmt .Sprintf (errorResponse , types .ZeroDelayAndJitterError ),
2433+ },
23602434 {
23612435 name : fmt .Sprintf ("%s invalid DelayMilliseconds in the request body 1" , startNetworkLatencyTestPrefix ),
23622436 expectedStatusCode : 400 ,
@@ -2493,6 +2567,27 @@ func generateStopNetworkLatencyTestCases() []networkFaultInjectionTestCase {
24932567 },
24942568 expectedResponseJSON : happyFaultStoppedResponse ,
24952569 },
2570+ {
2571+ name : "existing-network-latency-fault-with-0-delay-happy-request-payload" ,
2572+ expectedStatusCode : 200 ,
2573+ requestBody : happyNetworkLatencyReqBody ,
2574+ expectedResponseBody : types .NewNetworkFaultInjectionSuccessResponse ("stopped" ),
2575+ setAgentStateExpectations : func (agentState * mock_state.MockAgentState , netConfigClient * netconfig.NetworkConfigClient ) {
2576+ agentState .EXPECT ().GetTaskMetadataWithTaskNetworkConfig (endpointId , netConfigClient ).Return (happyTaskResponse , nil )
2577+ },
2578+ setExecExpectations : func (exec * mock_execwrapper.MockExec , ctrl * gomock.Controller ) {
2579+ ctx , cancel := context .WithTimeout (context .Background (), ctxTimeoutDuration )
2580+ mockCMD := mock_execwrapper .NewMockCmd (ctrl )
2581+ gomock .InOrder (
2582+ exec .EXPECT ().NewExecContextWithTimeout (gomock .Any (), gomock .Any ()).Times (1 ).Return (ctx , cancel ),
2583+ exec .EXPECT ().CommandContext (gomock .Any (), gomock .Any (), gomock .Any ()).Times (1 ).Return (mockCMD ),
2584+ mockCMD .EXPECT ().CombinedOutput ().Times (1 ).Return ([]byte (tcLatencyFaultWithZeroDelayExistsCommandOutput ), nil ),
2585+ )
2586+ exec .EXPECT ().CommandContext (gomock .Any (), gomock .Any (), gomock .Any ()).Times (2 ).Return (mockCMD )
2587+ mockCMD .EXPECT ().CombinedOutput ().Times (2 ).Return ([]byte ("" ), nil )
2588+ },
2589+ expectedResponseJSON : happyFaultStoppedResponse ,
2590+ },
24962591 {
24972592 name : "existing-network-packet-loss-fault-empty-request-payload" ,
24982593 expectedStatusCode : 200 ,
@@ -2610,6 +2705,25 @@ func generateCheckNetworkLatencyTestCases() []networkFaultInjectionTestCase {
26102705 },
26112706 expectedResponseJSON : happyFaultRunningResponse ,
26122707 },
2708+ {
2709+ name : "existing-network-latency-fault-with-0-delay-happy-request-payload" ,
2710+ expectedStatusCode : 200 ,
2711+ requestBody : happyNetworkLatencyReqBody ,
2712+ expectedResponseBody : types .NewNetworkFaultInjectionSuccessResponse ("running" ),
2713+ setAgentStateExpectations : func (agentState * mock_state.MockAgentState , netConfigClient * netconfig.NetworkConfigClient ) {
2714+ agentState .EXPECT ().GetTaskMetadataWithTaskNetworkConfig (endpointId , netConfigClient ).Return (happyTaskResponse , nil )
2715+ },
2716+ setExecExpectations : func (exec * mock_execwrapper.MockExec , ctrl * gomock.Controller ) {
2717+ ctx , cancel := context .WithTimeout (context .Background (), ctxTimeoutDuration )
2718+ mockCMD := mock_execwrapper .NewMockCmd (ctrl )
2719+ gomock .InOrder (
2720+ exec .EXPECT ().NewExecContextWithTimeout (gomock .Any (), gomock .Any ()).Times (1 ).Return (ctx , cancel ),
2721+ exec .EXPECT ().CommandContext (gomock .Any (), gomock .Any (), gomock .Any ()).Times (1 ).Return (mockCMD ),
2722+ mockCMD .EXPECT ().CombinedOutput ().Times (1 ).Return ([]byte (tcLatencyFaultWithZeroDelayExistsCommandOutput ), nil ),
2723+ )
2724+ },
2725+ expectedResponseJSON : happyFaultRunningResponse ,
2726+ },
26132727 {
26142728 name : "existing-network-packet-loss-fault-empty-request-payload" ,
26152729 expectedStatusCode : 200 ,
@@ -2883,6 +2997,23 @@ func generateStartNetworkPacketLossTestCases() []networkFaultInjectionTestCase {
28832997 },
28842998 expectedResponseJSON : fmt .Sprintf (errorResponse , latencyFaultAlreadyRunningError ),
28852999 },
3000+ {
3001+ name : "existing-network-latency-fault-with-0-delay" ,
3002+ expectedStatusCode : 409 ,
3003+ requestBody : happyNetworkPacketLossReqBody ,
3004+ expectedResponseBody : types .NewNetworkFaultInjectionErrorResponse (latencyFaultAlreadyRunningError ),
3005+ setAgentStateExpectations : func (agentState * mock_state.MockAgentState , netConfigClient * netconfig.NetworkConfigClient ) {
3006+ agentState .EXPECT ().GetTaskMetadataWithTaskNetworkConfig (endpointId , netConfigClient ).Return (happyTaskResponse , nil )
3007+ },
3008+ setExecExpectations : func (exec * mock_execwrapper.MockExec , ctrl * gomock.Controller ) {
3009+ ctx , cancel := context .WithTimeout (context .Background (), ctxTimeoutDuration )
3010+ mockCMD := mock_execwrapper .NewMockCmd (ctrl )
3011+ exec .EXPECT ().NewExecContextWithTimeout (gomock .Any (), gomock .Any ()).Times (1 ).Return (ctx , cancel )
3012+ exec .EXPECT ().CommandContext (gomock .Any (), gomock .Any (), gomock .Any ()).Times (1 ).Return (mockCMD )
3013+ mockCMD .EXPECT ().CombinedOutput ().Times (1 ).Return ([]byte (tcLatencyFaultWithZeroDelayExistsCommandOutput ), nil )
3014+ },
3015+ expectedResponseJSON : fmt .Sprintf (errorResponse , latencyFaultAlreadyRunningError ),
3016+ },
28863017 {
28873018 name : "existing-network-packet-loss-fault" ,
28883019 expectedStatusCode : 409 ,
@@ -3280,6 +3411,25 @@ func generateCheckNetworkPacketLossTestCases() []networkFaultInjectionTestCase {
32803411 },
32813412 expectedResponseJSON : happyFaultNotRunningResponse ,
32823413 },
3414+ {
3415+ name : "existing-network-latency-fault-with-0-delay-happy-request-payload" ,
3416+ expectedStatusCode : 200 ,
3417+ requestBody : happyNetworkPacketLossReqBody ,
3418+ expectedResponseBody : types .NewNetworkFaultInjectionSuccessResponse ("not-running" ),
3419+ setAgentStateExpectations : func (agentState * mock_state.MockAgentState , netConfigClient * netconfig.NetworkConfigClient ) {
3420+ agentState .EXPECT ().GetTaskMetadataWithTaskNetworkConfig (endpointId , netConfigClient ).Return (happyTaskResponse , nil )
3421+ },
3422+ setExecExpectations : func (exec * mock_execwrapper.MockExec , ctrl * gomock.Controller ) {
3423+ ctx , cancel := context .WithTimeout (context .Background (), ctxTimeoutDuration )
3424+ mockCMD := mock_execwrapper .NewMockCmd (ctrl )
3425+ gomock .InOrder (
3426+ exec .EXPECT ().NewExecContextWithTimeout (gomock .Any (), gomock .Any ()).Times (1 ).Return (ctx , cancel ),
3427+ exec .EXPECT ().CommandContext (gomock .Any (), gomock .Any (), gomock .Any ()).Times (1 ).Return (mockCMD ),
3428+ mockCMD .EXPECT ().CombinedOutput ().Times (1 ).Return ([]byte (tcLatencyFaultWithZeroDelayExistsCommandOutput ), nil ),
3429+ )
3430+ },
3431+ expectedResponseJSON : happyFaultNotRunningResponse ,
3432+ },
32833433 {
32843434 name : "existing-network-packet-loss-fault-empty-request-payload" ,
32853435 expectedStatusCode : 200 ,
@@ -3451,6 +3601,17 @@ func TestCheckTCFault(t *testing.T) {
34513601 cmd .EXPECT ().CombinedOutput ().Return ([]byte (tcLatencyFaultExistsCommandOutput ), nil )
34523602 },
34533603 },
3604+ {
3605+ name : "Latency fault with 0 delay exists" ,
3606+ deviceNames : []string {"eth0" },
3607+ expectedLatency : true ,
3608+ expectedPacketLoss : false ,
3609+ expectedError : nil ,
3610+ commandExpectations : func (exec * mock_execwrapper.MockExec , cmd * mock_execwrapper.MockCmd ) {
3611+ exec .EXPECT ().CommandContext (gomock .Any (), "tc" , []string {"-j" , "q" , "show" , "dev" , "eth0" , "parent" , "1:1" }).Return (cmd )
3612+ cmd .EXPECT ().CombinedOutput ().Return ([]byte (tcLatencyFaultWithZeroDelayExistsCommandOutput ), nil )
3613+ },
3614+ },
34543615 {
34553616 name : "Packet loss fault exists" ,
34563617 deviceNames : []string {"eth0" },
0 commit comments