From 44dea2501c2dcec169e061875dc4479e52ab58e4 Mon Sep 17 00:00:00 2001 From: Tate Date: Thu, 20 Jun 2024 08:11:33 -0600 Subject: [PATCH] [TT-1275] Add retreiving of requests from killgrave (#994) Note: This requires approval of: https://github.com/friendsofgo/killgrave/pull/170 which may still need changes. --- .../workflows/update-internal-mirrors.yaml | 2 +- Makefile | 4 + docker/test_env/killgrave.go | 101 ++++++++++++++++-- docker/test_env/killgrave_test.go | 53 ++++++++- 4 files changed, 146 insertions(+), 14 deletions(-) diff --git a/.github/workflows/update-internal-mirrors.yaml b/.github/workflows/update-internal-mirrors.yaml index d3b3d6513..da170384a 100644 --- a/.github/workflows/update-internal-mirrors.yaml +++ b/.github/workflows/update-internal-mirrors.yaml @@ -24,7 +24,7 @@ jobs: - name: ethereum/client-go expression: '^(alltools-v|v)[0-9]\.[0-9]+\.[0-9]+$' - name: friendsofgo/killgrave - expression: '^[0-9]+\.[0-9]+\.[0-9]+$' + expression: '^v?[0-9]+\.[0-9]+\.[0-9]+$' - name: mockserver/mockserver expression: '^[0-9]+\.[0-9]+\.[0-9]+$' - name: testcontainers/ryuk diff --git a/Makefile b/Makefile index cc0152518..f2d7e3179 100644 --- a/Makefile +++ b/Makefile @@ -148,6 +148,10 @@ chaosmesh: ## there is currently a bug on JS side to import all CRDs from one ya gotestloghelper_build: cd ./tools/gotestloghelper && go build -o ../../gotestloghelper . && cd - +.PHONY: typos +typos: + pre-commit run detect-typos --all-files --show-diff-on-failure --color=always + .PHONY: nix_shell nix_shell: nix develop diff --git a/docker/test_env/killgrave.go b/docker/test_env/killgrave.go index 34eba1cc1..cacecbc6f 100644 --- a/docker/test_env/killgrave.go +++ b/docker/test_env/killgrave.go @@ -19,22 +19,23 @@ import ( tc "github.com/testcontainers/testcontainers-go" "github.com/testcontainers/testcontainers-go/wait" + "github.com/smartcontractkit/chainlink-testing-framework/k8s/config" "github.com/smartcontractkit/chainlink-testing-framework/logging" - "github.com/smartcontractkit/chainlink-testing-framework/mirror" "github.com/smartcontractkit/chainlink-testing-framework/utils/testcontext" ) -const defaultKillgraveImage = "friendsofgo/killgrave:0.4.1" +const defaultKillgraveImage = "friendsofgo/killgrave:v0.5.1-request-dump" type Killgrave struct { EnvComponent - ExternalEndpoint string - InternalPort string - InternalEndpoint string - impostersPath string - impostersDirBinding string - t *testing.T - l zerolog.Logger + ExternalEndpoint string + InternalPort string + InternalEndpoint string + impostersPath string + impostersDirBinding string + requestDumpDirBinding string + t *testing.T + l zerolog.Logger } // Imposter define an imposter structure @@ -106,6 +107,10 @@ func (k *Killgrave) StartContainer() error { if err != nil { return err } + err = k.setupRequestDump() + if err != nil { + return err + } if k.t != nil { k.t.Cleanup(func() { os.RemoveAll(k.impostersDirBinding) @@ -141,19 +146,33 @@ func (k *Killgrave) StartContainer() error { } func (k *Killgrave) getContainerRequest() (tc.ContainerRequest, error) { - killgraveImage := mirror.AddMirrorToImageIfSet(defaultKillgraveImage) + // TT-1290 Temporary work around using fork of killgrave, uncomment line below when fork is merged + // killgraveImage := mirror.AddMirrorToImageIfSet(defaultKillgraveImage) + // TT-1290 Temporary code to set image to the fork or the ecr mirror depending on the config + killgraveImage := "tateexon/killgrave:v0.5.1-request-dump" + ecr := os.Getenv(config.EnvVarInternalDockerRepo) + if ecr != "" { + killgraveImage = fmt.Sprintf("%s/%s", ecr, defaultKillgraveImage) + } + // end temporary code + return tc.ContainerRequest{ Name: k.ContainerName, Networks: k.Networks, Image: killgraveImage, ExposedPorts: []string{NatPortFormat(k.InternalPort)}, - Cmd: []string{"-host=0.0.0.0", "-imposters=/imposters", "-watcher"}, + Cmd: []string{"-H=0.0.0.0", "-i=/imposters", "-w", "-v", "-d=/requestDump/requestDump.log"}, HostConfigModifier: func(hostConfig *container.HostConfig) { hostConfig.Mounts = append(hostConfig.Mounts, mount.Mount{ Type: mount.TypeBind, Source: k.impostersDirBinding, Target: "/imposters", ReadOnly: false, + }, mount.Mount{ + Type: mount.TypeBind, + Source: k.requestDumpDirBinding, + Target: "/requestDump", + ReadOnly: false, }) }, WaitingFor: wait.ForLog("The fake server is on tap now"), @@ -187,6 +206,13 @@ func (k *Killgrave) setupImposters() error { return k.SetAdapterBasedIntValuePath("/five", []string{http.MethodGet, http.MethodPost}, 5) } +func (k *Killgrave) setupRequestDump() error { + // create temporary directory for request dumps + var err error + k.requestDumpDirBinding, err = os.MkdirTemp(k.requestDumpDirBinding, "requestDump*") + return err +} + // AddImposter adds an imposter to the killgrave container func (k *Killgrave) AddImposter(imposters []KillgraveImposter) error { // if the endpoint paths do not start with '/' then add it @@ -282,3 +308,56 @@ func (k *Killgrave) SetAnyValueResponse(path string, methods []string, v interfa func (k *Killgrave) SetAdapterBasedIntValuePath(path string, methods []string, v int) error { return k.SetAdapterBasedAnyValuePath(path, methods, v) } + +type RequestData struct { + Method string `json:"method"` + Host string `json:"host"` + URL string `json:"url"` + Header map[string][]string `json:"header"` + Body string `json:"body"` +} + +func (k *Killgrave) GetReceivedRequests() ([]RequestData, error) { + // killgrave uses a channel to write the request data to a file so we want to make sure + // all requests have been written before reading the file + time.Sleep(1 * time.Second) + + // Read the directory entries + files, err := os.ReadDir(k.requestDumpDirBinding) + if err != nil { + return nil, err + } + + // Iterate over the directory entries + fmt.Println("Files Start") + for _, file := range files { + fmt.Println(file.Name()) + } + fmt.Println("Files End") + + fileContent, err := os.ReadFile(filepath.Join(k.requestDumpDirBinding, "requestDump.log")) + if err != nil { + return nil, fmt.Errorf("error reading file: %w", err) + } + + fmt.Println("File Content Start") + fmt.Println(string(fileContent)) + fmt.Println("File Content End") + + // Split the contents by the newline separator + requestDumps := strings.Split(string(fileContent), "\n") + requestsData := []RequestData{} + for _, requestDump := range requestDumps { + if requestDump == "" { + continue + } + + rd := RequestData{} + err := json.Unmarshal([]byte(requestDump), &rd) + if err != nil { + return nil, fmt.Errorf("error unmarshalling JSON: %w", err) + } + requestsData = append(requestsData, rd) + } + return requestsData, nil +} diff --git a/docker/test_env/killgrave_test.go b/docker/test_env/killgrave_test.go index 532fd69a1..318f784ef 100644 --- a/docker/test_env/killgrave_test.go +++ b/docker/test_env/killgrave_test.go @@ -109,8 +109,9 @@ func runTestWithExpectations(t *testing.T, k *Killgrave, expectations []kgTest) }) var err error // Check the different kinds of responses - for _, e := range expectations { - test := e + for _, test := range expectations { + test := test + t.Run(test.Name, func(t *testing.T) { t.Parallel() m := []string{http.MethodGet} @@ -163,3 +164,51 @@ func runTestWithExpectations(t *testing.T, k *Killgrave, expectations []kgTest) }) } } + +func TestKillgraveRequestDump(t *testing.T) { + t.Parallel() + l := logging.GetTestLogger(t) + network, err := docker.CreateNetwork(l) + require.NoError(t, err) + + k := NewKillgrave([]string{network.Name}, "./killgrave_imposters"). + WithTestInstance(t) + err = k.StartContainer() + require.NoError(t, err) + + path := "/stringany" + m := []string{http.MethodGet} + headers := map[string]string{"Content-Type": "text/plain"} + err = k.SetStringValuePath("/stringany", m, headers, "{\"id\":\"\",\"data\":{\"result\":5},\"error\":null}") + require.NoError(t, err) + var url string + if strings.HasPrefix(path, "/") { + url = fmt.Sprintf("%s%s", k.ExternalEndpoint, path) + } else { + url = fmt.Sprintf("%s/%s", k.ExternalEndpoint, path) + } + bodyRequest := []byte("{\n\"a\":5,\n\"b\":6\n}") + req1, err := http.NewRequest(m[0], url, bytes.NewBuffer(bodyRequest)) + require.NoError(t, err) + req1.Header.Set("Content-Type", "application/json") + req2, err := http.NewRequest(m[0], url, bytes.NewBuffer(bodyRequest)) + require.NoError(t, err) + client := &http.Client{ + Timeout: 10 * time.Second, + } + resp1, err := client.Do(req1) + require.NoError(t, err) + defer resp1.Body.Close() + require.Equal(t, http.StatusOK, resp1.StatusCode, fmt.Sprintf("url: %s", url)) + resp2, err := client.Do(req2) + require.NoError(t, err) + defer resp2.Body.Close() + require.Equal(t, http.StatusOK, resp2.StatusCode, fmt.Sprintf("url: %s", url)) + + requests, err := k.GetReceivedRequests() + require.NoError(t, err) + fmt.Printf("Requests: %+v\n", requests) + require.Equal(t, 2, len(requests)) + require.Equal(t, string(bodyRequest), requests[0].Body) + require.Equal(t, string(bodyRequest), requests[1].Body) +}