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  }