github.com/smartcontractkit/chainlink-testing-framework/libs@v0.0.0-20240227141906-ec710b4eb1a3/docker/test_env/env_component.go (about) 1 package test_env 2 3 import ( 4 "context" 5 "fmt" 6 "time" 7 8 "strings" 9 10 "github.com/rs/zerolog" 11 tc "github.com/testcontainers/testcontainers-go" 12 13 "github.com/smartcontractkit/chainlink-testing-framework/libs/logstream" 14 "github.com/smartcontractkit/chainlink-testing-framework/libs/utils/osutil" 15 ) 16 17 const ( 18 BaseCMD = `docker run -i --rm -v /var/run/docker.sock:/var/run/docker.sock --network %s gaiaadm/pumba --log-level=info` 19 ) 20 21 type EnvComponent struct { 22 ContainerName string `json:"containerName"` 23 ContainerImage string `json:"containerImage"` 24 ContainerVersion string `json:"containerVersion"` 25 ContainerEnvs map[string]string `json:"containerEnvs"` 26 Networks []string `json:"networks"` 27 Container tc.Container `json:"-"` 28 LogStream *logstream.LogStream `json:"-"` 29 PostStartsHooks []tc.ContainerHook `json:"-"` 30 PostStopsHooks []tc.ContainerHook `json:"-"` 31 PreTerminatesHooks []tc.ContainerHook `json:"-"` 32 } 33 34 type EnvComponentOption = func(c *EnvComponent) 35 36 func WithContainerName(name string) EnvComponentOption { 37 return func(c *EnvComponent) { 38 if name != "" { 39 c.ContainerName = name 40 } 41 } 42 } 43 44 func WithContainerImageWithVersion(imageWithVersion string) EnvComponentOption { 45 return func(c *EnvComponent) { 46 split := strings.Split(imageWithVersion, ":") 47 if len(split) == 2 { 48 c.ContainerImage = split[0] 49 c.ContainerVersion = split[1] 50 } 51 } 52 } 53 54 func WithLogStream(ls *logstream.LogStream) EnvComponentOption { 55 return func(c *EnvComponent) { 56 c.LogStream = ls 57 } 58 } 59 60 func WithPostStartsHooks(hooks ...tc.ContainerHook) EnvComponentOption { 61 return func(c *EnvComponent) { 62 c.PostStartsHooks = hooks 63 } 64 } 65 66 func WithPostStopsHooks(hooks ...tc.ContainerHook) EnvComponentOption { 67 return func(c *EnvComponent) { 68 c.PostStopsHooks = hooks 69 } 70 } 71 72 func WithPreTerminatesHooks(hooks ...tc.ContainerHook) EnvComponentOption { 73 return func(c *EnvComponent) { 74 c.PreTerminatesHooks = hooks 75 } 76 } 77 78 func (ec *EnvComponent) SetDefaultHooks() { 79 ec.PostStartsHooks = []tc.ContainerHook{ 80 func(ctx context.Context, c tc.Container) error { 81 if ec.LogStream != nil { 82 return ec.LogStream.ConnectContainer(ctx, c, "") 83 } 84 return nil 85 }, 86 } 87 ec.PostStopsHooks = []tc.ContainerHook{ 88 func(ctx context.Context, c tc.Container) error { 89 if ec.LogStream != nil { 90 return ec.LogStream.DisconnectContainer(c) 91 } 92 return nil 93 }, 94 } 95 } 96 97 func (ec *EnvComponent) GetImageWithVersion() string { 98 return fmt.Sprintf("%s:%s", ec.ContainerImage, ec.ContainerVersion) 99 } 100 101 // ChaosPause pauses the container for the specified duration 102 func (ec EnvComponent) ChaosPause( 103 l zerolog.Logger, 104 duration time.Duration, 105 ) error { 106 withNet := fmt.Sprintf(BaseCMD, ec.Networks[0]) 107 return osutil.ExecCmd(l, fmt.Sprintf(`%s pause --duration=%s %s`, withNet, duration.String(), ec.ContainerName)) 108 } 109 110 // ChaosNetworkDelay delays the container's network traffic for the specified duration 111 func (ec EnvComponent) ChaosNetworkDelay( 112 l zerolog.Logger, 113 duration time.Duration, 114 delay time.Duration, 115 targetInterfaceName string, 116 targetIPs []string, 117 targetIngressPorts []string, 118 targetEgressPorts []string, 119 ) error { 120 var sb strings.Builder 121 withNet := fmt.Sprintf(BaseCMD, ec.Networks[0]) 122 sb.Write([]byte(fmt.Sprintf(`%s netem --tc-image=gaiadocker/iproute2 --duration=%s`, withNet, duration.String()))) 123 writeTargetNetworkParams(&sb, targetInterfaceName, targetIPs, targetIngressPorts, targetEgressPorts) 124 sb.Write([]byte(fmt.Sprintf(` delay --time=%d %s`, delay, ec.ContainerName))) 125 return osutil.ExecCmd(l, sb.String()) 126 } 127 128 // ChaosNetworkLoss causes the container to lose the specified percentage of network traffic for the specified duration 129 func (ec EnvComponent) ChaosNetworkLoss( 130 l zerolog.Logger, 131 duration time.Duration, 132 lossPercentage int, 133 targetInterfaceName string, 134 targetIPs []string, 135 targetIngressPorts []string, 136 targetEgressPorts []string, 137 ) error { 138 var sb strings.Builder 139 withNet := fmt.Sprintf(BaseCMD, ec.Networks[0]) 140 sb.Write([]byte(fmt.Sprintf(`%s netem --tc-image=gaiadocker/iproute2 --duration=%s`, withNet, duration.String()))) 141 writeTargetNetworkParams(&sb, targetInterfaceName, targetIPs, targetIngressPorts, targetEgressPorts) 142 sb.Write([]byte(fmt.Sprintf(` loss --percent %d %s`, lossPercentage, ec.ContainerName))) 143 return osutil.ExecCmd(l, sb.String()) 144 } 145 146 // writeTargetNetworkParams writes the target network parameters to the provided strings.Builder 147 func writeTargetNetworkParams(sb *strings.Builder, targetInterfaceName string, targetIPs []string, targetIngressPorts []string, targetEgressPorts []string) { 148 if targetInterfaceName == "" { 149 targetInterfaceName = "eth0" 150 } 151 for _, ip := range targetIPs { 152 sb.Write([]byte(fmt.Sprintf(` -t %s`, ip))) 153 } 154 sb.Write([]byte(fmt.Sprintf(" --interface %s", targetInterfaceName))) 155 for _, p := range targetIngressPorts { 156 sb.Write([]byte(fmt.Sprintf(` --ingress-port %s`, p))) 157 } 158 for _, p := range targetEgressPorts { 159 sb.Write([]byte(fmt.Sprintf(` --egress-port %s`, p))) 160 } 161 }