From b174d477252e01ecde7bf4876fe9b5b22b931ff4 Mon Sep 17 00:00:00 2001 From: Safeer Jiwan Date: Wed, 30 Oct 2024 13:42:20 -0700 Subject: [PATCH] chore: GHA to smoke test against last released exemplar (#2983) This adds a GitHub Action that smoke tests branches against the exemplar in the latest tagged release. It will fetch the latest tagged FTL release from dockerhub, checkout the repo at that tag, then deploy that version to a local kube cluster. Testing this tagged version against the current exemplar is disabled (chicken-and-egg since test change are needed in the tagged release). Finally, it will upgrade the local kube cluster to that in the current branch and run the exemplar smoketest to verify the upgrade path was successful. --------- Co-authored-by: Stuart Douglas Co-authored-by: github-actions[bot] --- .github/workflows/smoketest.yml | 92 ++++++++ deployment/Justfile | 3 +- internal/integration/actions.go | 2 +- internal/integration/harness.go | 2 +- smoketest/echo/echo.go | 14 ++ smoketest/echo/ftl.toml | 2 + smoketest/echo/go.mod | 18 ++ smoketest/echo/go.sum | 206 ++++++++++++++++++ smoketest/echo/types.ftl.go | 17 ++ ...n_test.go => exemplar_integration_test.go} | 16 +- smoketest/exemplar_smoke_test.go | 90 ++++++++ smoketest/jecho/ftl.toml | 2 + smoketest/jecho/pom.xml | 14 ++ .../src/main/java/com/example/EchoVerb.java | 13 ++ smoketest/origin/origin.go | 19 +- .../src/main/java/com/example/Pulse.java | 23 +- smoketest/relay/relay.go | 52 ++++- 17 files changed, 543 insertions(+), 42 deletions(-) create mode 100644 .github/workflows/smoketest.yml create mode 100644 smoketest/echo/echo.go create mode 100644 smoketest/echo/ftl.toml create mode 100644 smoketest/echo/go.mod create mode 100644 smoketest/echo/go.sum create mode 100644 smoketest/echo/types.ftl.go rename smoketest/{smoketest_integration_test.go => exemplar_integration_test.go} (75%) create mode 100644 smoketest/exemplar_smoke_test.go create mode 100644 smoketest/jecho/ftl.toml create mode 100644 smoketest/jecho/pom.xml create mode 100644 smoketest/jecho/src/main/java/com/example/EchoVerb.java diff --git a/.github/workflows/smoketest.yml b/.github/workflows/smoketest.yml new file mode 100644 index 0000000000..a7f67da2b8 --- /dev/null +++ b/.github/workflows/smoketest.yml @@ -0,0 +1,92 @@ +on: + push: + branches: + - main +name: SmokeTest +concurrency: + group: ${{ github.ref }}-smoketest + cancel-in-progress: true +jobs: + smoke-test-upgrade-path: + name: Smoke Test Upgrade Path + if: github.event_name != 'pull_request' || github.event.action == 'enqueued' || contains( github.event.pull_request.labels.*.name, 'run-all') + runs-on: ubuntu-latest + steps: + - name: Checkout the repository with full history + uses: actions/checkout@v4 + with: + ref: ${{ github.head_ref }} + fetch-depth: 0 + fetch-tags: true + - name: Get last tagged release + run: | + latest_release=$(git tag --sort=-v:refname | grep -v v1 | head -n 1) + echo "Latest release: $latest_release" + echo "LATEST_VERSION=${latest_release#v}" >> "$GITHUB_ENV" + - name: Check out the last tagged release + uses: actions/checkout@v4 + with: + ref: v${{ env.LATEST_VERSION }} + fetch-depth: 1 + fetch-tags: true + # The following grabs the current smoke test and deployment Justfile from the HEAD. This + # is to solve the chicken-and-egg. Once it's in the tagged release, skip this. + - name: Check out the current smoke test + uses: actions/checkout@v4 + with: + clean: false + repository: TBD54566975/ftl.git + path: tmpsmoketest + ref: ${{ github.head_ref }} + fetch-depth: 0 + sparse-checkout: | + smoketest + deployment + sparse-checkout-cone-mode: false + # When we remove the above step to fetch the current smoke tests and deployment Justfile + # into tmpsmoketest, remove the overwrite step as well. + - name: Replace the tagged release smoketest with the current smoketest + run: | + set -euo pipefail + echo "Replacing tagged release smoketest with current smoketest" + rm -r ./smoketest && mv ./tmpsmoketest/smoketest ./smoketest + echo "Replacing deployment Justfile with current Justfile" + cp ./tmpsmoketest/deployment/Justfile ./deployment/Justfile + echo "Removing tmpsmoketest" + rm -r ./tmpsmoketest + - name: Init Hermit + uses: cashapp/activate-hermit@v1 + with: + cache: true + - name: Build Cache + uses: ./.github/actions/build-cache + - name: Download Go Modules + run: go mod download + - name: Set up a kube cluster with the tagged release dockerhub image + run: | + set -euo pipefail + echo "Deploying the tagged release to the cluster" + cd deployment && just deploy-version ${{ env.LATEST_VERSION }} && cd .. + # We skip this since it requires an integration test change to skip the full kube deploy. + # Re-enable this step when the tagged release has the integration test change. + # - name: Smoke test the tagged release images + # run: | + # set -euo pipefail + # echo "Running smoke test on the tagged release images" + # USE_DB_CONFIG=true SKIP_KUBE_FULL_DEPLOY=true go test -v -tags smoketest -run '^Test' ./smoketest + - name: Check out HEAD of the current branch + uses: actions/checkout@v4 + with: + ref: ${{ github.head_ref }} + fetch-depth: 1 + - name: Smoke test HEAD with a full deploy to test upgrade path + run: | + set -euo pipefail + echo "Running smoke test on the HEAD images" + USE_DB_CONFIG=true go test -v -timeout 15m -tags smoketest -run '^Test' ./smoketest + - name: Archive Report + uses: actions/upload-artifact@v4 + if: always() + with: + name: kube-report-smoke-test-upgrade + path: /tmp/ftl-kube-report/ diff --git a/deployment/Justfile b/deployment/Justfile index fcfac310d9..7c8f9a0ba0 100755 --- a/deployment/Justfile +++ b/deployment/Justfile @@ -72,8 +72,9 @@ apply: kubectl delete job --ignore-not-found=true ftl-dbmig-latest helm upgrade --install ftl ../charts/ftl -f values.yaml -deploy-version version: setup-cluster +deploy-version version: setup-cluster install-istio helm repo add ftl https://tbd54566975.github.io/ftl-charts --force-update + helm upgrade --install ftl ftl/ftl --version={{version}} -f values-release.yaml || sleep 5 # wait for CRDs to be created, the initial apply will usually fail helm upgrade --install ftl ftl/ftl --version={{version}} -f values-release.yaml delete: diff --git a/internal/integration/actions.go b/internal/integration/actions.go index b2b6a0227c..909f4b8662 100644 --- a/internal/integration/actions.go +++ b/internal/integration/actions.go @@ -1,4 +1,4 @@ -//go:build integration || infrastructure +//go:build integration || infrastructure || smoketest package integration diff --git a/internal/integration/harness.go b/internal/integration/harness.go index ffc07fa688..6dd30c0176 100644 --- a/internal/integration/harness.go +++ b/internal/integration/harness.go @@ -1,4 +1,4 @@ -//go:build integration || infrastructure +//go:build integration || infrastructure || smoketest package integration diff --git a/smoketest/echo/echo.go b/smoketest/echo/echo.go new file mode 100644 index 0000000000..6ce84c47b6 --- /dev/null +++ b/smoketest/echo/echo.go @@ -0,0 +1,14 @@ +// This is the echo module. +package echo + +import ( + "context" + "fmt" +) + +// Echo returns a greeting with the current time. +// +//ftl:verb export +func Echo(ctx context.Context, req string) (string, error) { + return fmt.Sprintf("Hello, %s!!!", req), nil +} diff --git a/smoketest/echo/ftl.toml b/smoketest/echo/ftl.toml new file mode 100644 index 0000000000..72ce292aa2 --- /dev/null +++ b/smoketest/echo/ftl.toml @@ -0,0 +1,2 @@ +module = "echo" +language = "go" diff --git a/smoketest/echo/go.mod b/smoketest/echo/go.mod new file mode 100644 index 0000000000..7a9cff947f --- /dev/null +++ b/smoketest/echo/go.mod @@ -0,0 +1,18 @@ +module ftl/echo + +go 1.23.0 + +replace github.com/TBD54566975/ftl => ../.. + +require github.com/TBD54566975/ftl v0.0.0-00010101000000-000000000000 + +require ( + github.com/alecthomas/participle/v2 v2.1.1 // indirect + github.com/alecthomas/types v0.16.0 // indirect + github.com/deckarep/golang-set/v2 v2.6.0 // indirect + github.com/hashicorp/cronexpr v1.1.2 // indirect + github.com/swaggest/jsonschema-go v0.3.72 // indirect + github.com/swaggest/refl v1.3.0 // indirect + golang.org/x/exp v0.0.0-20240909161429-701f63a606c0 // indirect + google.golang.org/protobuf v1.35.1 // indirect +) diff --git a/smoketest/echo/go.sum b/smoketest/echo/go.sum new file mode 100644 index 0000000000..c74b6955ee --- /dev/null +++ b/smoketest/echo/go.sum @@ -0,0 +1,206 @@ +al.essio.dev/pkg/shellescape v1.5.1 h1:86HrALUujYS/h+GtqoB26SBEdkWfmMI6FubjXlsXyho= +al.essio.dev/pkg/shellescape v1.5.1/go.mod h1:6sIqp7X2P6mThCQ7twERpZTuigpr6KbZWtls1U8I890= +connectrpc.com/connect v1.16.2 h1:ybd6y+ls7GOlb7Bh5C8+ghA6SvCBajHwxssO2CGFjqE= +connectrpc.com/connect v1.16.2/go.mod h1:n2kgwskMHXC+lVqb18wngEpF95ldBHXjZYJussz5FRc= +connectrpc.com/grpcreflect v1.2.0 h1:Q6og1S7HinmtbEuBvARLNwYmTbhEGRpHDhqrPNlmK+U= +connectrpc.com/grpcreflect v1.2.0/go.mod h1:nwSOKmE8nU5u/CidgHtPYk1PFI3U9ignz7iDMxOYkSY= +connectrpc.com/otelconnect v0.7.1 h1:scO5pOb0i4yUE66CnNrHeK1x51yq0bE0ehPg6WvzXJY= +connectrpc.com/otelconnect v0.7.1/go.mod h1:dh3bFgHBTb2bkqGCeVVOtHJreSns7uu9wwL2Tbz17ms= +github.com/TBD54566975/scaffolder v1.2.0 h1:7NqCC+iTDgk2awkOgk2Pj95Nz3GonhdIhwHQgQxug9k= +github.com/TBD54566975/scaffolder v1.2.0/go.mod h1:oHLiKFPkkSMHP4ALVZ91T2V/xyx4MvPpRSpQttJxY7g= +github.com/alecthomas/assert/v2 v2.11.0 h1:2Q9r3ki8+JYXvGsDyBXwH3LcJ+WK5D0gc5E8vS6K3D0= +github.com/alecthomas/assert/v2 v2.11.0/go.mod h1:Bze95FyfUr7x34QZrjL+XP+0qgp/zg8yS+TtBj1WA3k= +github.com/alecthomas/concurrency v0.0.2 h1:Q3kGPtLbleMbH9lHX5OBFvJygfyFw29bXZKBg+IEVuo= +github.com/alecthomas/concurrency v0.0.2/go.mod h1:GmuQb/iHX7mbNtPlC/WDzEFxDMB0HYFer2Qda9QTs7w= +github.com/alecthomas/participle/v2 v2.1.1 h1:hrjKESvSqGHzRb4yW1ciisFJ4p3MGYih6icjJvbsmV8= +github.com/alecthomas/participle/v2 v2.1.1/go.mod h1:Y1+hAs8DHPmc3YUFzqllV+eSQ9ljPTk0ZkPMtEdAx2c= +github.com/alecthomas/repr v0.4.0 h1:GhI2A8MACjfegCPVq9f1FLvIBS+DrQ2KQBFZP1iFzXc= +github.com/alecthomas/repr v0.4.0/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4= +github.com/alecthomas/types v0.16.0 h1:o9+JSwCRB6DDaWDeR/Mg7v/zh3R+MlknM6DrnDyY7U0= +github.com/alecthomas/types v0.16.0/go.mod h1:Tswm0qQpjpVq8rn70OquRsUtFxbQKub/8TMyYYGI0+k= +github.com/benbjohnson/clock v1.3.5 h1:VvXlSJBzZpA/zum6Sj74hxwYI2DIxRWuNIoXAzHZz5o= +github.com/benbjohnson/clock v1.3.5/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= +github.com/bool64/dev v0.2.35 h1:M17TLsO/pV2J7PYI/gpe3Ua26ETkzZGb+dC06eoMqlk= +github.com/bool64/dev v0.2.35/go.mod h1:iJbh1y/HkunEPhgebWRNcs8wfGq7sjvJ6W5iabL8ACg= +github.com/bool64/shared v0.1.5 h1:fp3eUhBsrSjNCQPcSdQqZxxh9bBwrYiZ+zOKFkM0/2E= +github.com/bool64/shared v0.1.5/go.mod h1:081yz68YC9jeFB3+Bbmno2RFWvGKv1lPKkMP6MHJlPs= +github.com/danieljoos/wincred v1.2.2 h1:774zMFJrqaeYCK2W57BgAem/MLi6mtSE47MB6BOJ0i0= +github.com/danieljoos/wincred v1.2.2/go.mod h1:w7w4Utbrz8lqeMbDAK0lkNJUv5sAOkFi7nd/ogr0Uh8= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/deckarep/golang-set/v2 v2.6.0 h1:XfcQbWM1LlMB8BsJ8N9vW5ehnnPVIw0je80NsVHagjM= +github.com/deckarep/golang-set/v2 v2.6.0/go.mod h1:VAky9rY/yGXJOLEDv3OMci+7wtDpOF4IN+y82NBOac4= +github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= +github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= +github.com/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxERmMY4rD+g= +github.com/emicklei/go-restful/v3 v3.11.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= +github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E= +github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ= +github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= +github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/go-openapi/jsonpointer v0.19.6 h1:eCs3fxoIi3Wh6vtgmLTOjdhSpiqphQ+DaPn38N2ZdrE= +github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs= +github.com/go-openapi/jsonreference v0.20.2 h1:3sVjiK66+uXK/6oQ8xgcRKcFgQ5KXa2KvnJRumpMGbE= +github.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k= +github.com/go-openapi/swag v0.22.4 h1:QLMzNJnMGPRNDCbySlcj1x01tzU8/9LTTL9hZZZogBU= +github.com/go-openapi/swag v0.22.4/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= +github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= +github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= +github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= +github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I= +github.com/google/gnostic-models v0.6.8/go.mod h1:5n7qKqH0f5wFt+aWF8CW6pZLLNOfYuF5OpfBSENuI8U= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= +github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/hashicorp/cronexpr v1.1.2 h1:wG/ZYIKT+RT3QkOdgYc+xsKWVRgnxJ1OJtjjy84fJ9A= +github.com/hashicorp/cronexpr v1.1.2/go.mod h1:P4wA0KBl9C5q2hABiMO7cp6jcIg96CDh1Efb3g1PWA4= +github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k= +github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM= +github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM= +github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg= +github.com/iancoleman/orderedmap v0.3.0 h1:5cbR2grmZR/DiVt+VJopEhtVs9YGInGIxAoMJn+Ichc= +github.com/iancoleman/orderedmap v0.3.0/go.mod h1:XuLcCUkdL5owUCQeF2Ue9uuw1EptkJDkXXS7VoV7XGE= +github.com/imdario/mergo v0.3.13 h1:lFzP57bqS/wsqKssCGmtLAb8A0wKjLGrve2q3PPVcBk= +github.com/imdario/mergo v0.3.13/go.mod h1:4lJ1jqUDcsbIECGy0RUJAXNIhg+6ocWgb1ALK2O4oXg= +github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= +github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= +github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo= +github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= +github.com/jackc/pgx/v5 v5.7.1 h1:x7SYsPBYDkHDksogeSmZZ5xzThcTgRz++I5E+ePFUcs= +github.com/jackc/pgx/v5 v5.7.1/go.mod h1:e7O26IywZZ+naJtWWos6i6fvWK+29etgITqrqHLfoZA= +github.com/jackc/puddle/v2 v2.2.2 h1:PR8nw+E/1w0GLuRFSmiioY6UooMp6KJv0/61nB7icHo= +github.com/jackc/puddle/v2 v2.2.2/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4= +github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= +github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= +github.com/jpillora/backoff v1.0.0 h1:uvFg412JmmHBHw7iwprIxkPMI+sGQ4kzOWsMeHnm2EA= +github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= +github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs= +github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8= +github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= +github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/multiformats/go-base36 v0.2.0 h1:lFsAbNOGeKtuKozrtBsAkSVhv1p9D0/qedU9rQyccr0= +github.com/multiformats/go-base36 v0.2.0/go.mod h1:qvnKE++v+2MWCfePClUEjE78Z7P2a1UV0xHgWc0hkp4= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= +github.com/ncruces/go-strftime v0.1.9 h1:bY0MQC28UADQmHmaF5dgpLmImcShSi2kHU9XLdhx/f4= +github.com/ncruces/go-strftime v0.1.9/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls= +github.com/otiai10/copy v1.14.0 h1:dCI/t1iTdYGtkvCuBG2BgR6KZa83PTclw4U5n2wAllU= +github.com/otiai10/copy v1.14.0/go.mod h1:ECfuL02W+/FkTWZWgQqXPWZgW9oeKCSQ5qVfSc4qc4w= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/puzpuzpuz/xsync/v3 v3.4.0 h1:DuVBAdXuGFHv8adVXjWWZ63pJq+NRXOWVXlKDBZ+mJ4= +github.com/puzpuzpuz/xsync/v3 v3.4.0/go.mod h1:VjzYrABPabuM4KyBh1Ftq6u8nhwY5tBPKP9jpmh0nnA= +github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE= +github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= +github.com/santhosh-tekuri/jsonschema/v5 v5.3.1 h1:lZUw3E0/J3roVtGQ+SCrUrg3ON6NgVqpn3+iol9aGu4= +github.com/santhosh-tekuri/jsonschema/v5 v5.3.1/go.mod h1:uToXkOrWAZ6/Oc07xWQrPOhJotwFIyu2bBVN41fcDUY= +github.com/sergi/go-diff v1.3.1 h1:xkr+Oxo4BOQKmkn/B9eMK0g5Kg/983T9DqqPHwYqD+8= +github.com/sergi/go-diff v1.3.1/go.mod h1:aMJSSKb2lpPvRNec0+w3fl7LP9IOFzdc9Pa4NFbPK1I= +github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= +github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8= +github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/swaggest/assertjson v1.9.0 h1:dKu0BfJkIxv/xe//mkCrK5yZbs79jL7OVf9Ija7o2xQ= +github.com/swaggest/assertjson v1.9.0/go.mod h1:b+ZKX2VRiUjxfUIal0HDN85W0nHPAYUbYH5WkkSsFsU= +github.com/swaggest/jsonschema-go v0.3.72 h1:IHaGlR1bdBUBPfhe4tfacN2TGAPKENEGiNyNzvnVHv4= +github.com/swaggest/jsonschema-go v0.3.72/go.mod h1:OrGyEoVqpfSFJ4Am4V/FQcQ3mlEC1vVeleA+5ggbVW4= +github.com/swaggest/refl v1.3.0 h1:PEUWIku+ZznYfsoyheF97ypSduvMApYyGkYF3nabS0I= +github.com/swaggest/refl v1.3.0/go.mod h1:3Ujvbmh1pfSbDYjC6JGG7nMgPvpG0ehQL4iNonnLNbg= +github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= +github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= +github.com/yudai/gojsondiff v1.0.0 h1:27cbfqXLVEJ1o8I6v3y9lg8Ydm53EKqHXAOMxEGlCOA= +github.com/yudai/gojsondiff v1.0.0/go.mod h1:AY32+k2cwILAkW1fbgxQ5mUmMiZFgLIV+FBNExI05xg= +github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82 h1:BHyfKlQyqbsFN5p3IfnEUduWvb9is428/nNb5L3U01M= +github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82/go.mod h1:lgjkn3NuSvDfVJdfcVVdX+jpBxNmX4rDAzaS45IcYoM= +github.com/zalando/go-keyring v0.2.6 h1:r7Yc3+H+Ux0+M72zacZoItR3UDxeWfKTcabvkI8ua9s= +github.com/zalando/go-keyring v0.2.6/go.mod h1:2TCrxYrbUNYfNS/Kgy/LSrkSQzZ5UPVH85RwfczwvcI= +go.opentelemetry.io/otel v1.31.0 h1:NsJcKPIW0D0H3NgzPDHmo0WW6SptzPdqg/L1zsIm2hY= +go.opentelemetry.io/otel v1.31.0/go.mod h1:O0C14Yl9FgkjqcCZAsE053C13OaddMYr/hz6clDkEJE= +go.opentelemetry.io/otel/metric v1.31.0 h1:FSErL0ATQAmYHUIzSezZibnyVlft1ybhy4ozRPcF2fE= +go.opentelemetry.io/otel/metric v1.31.0/go.mod h1:C3dEloVbLuYoX41KpmAhOqNriGbA+qqH6PQ5E5mUfnY= +go.opentelemetry.io/otel/trace v1.31.0 h1:ffjsj1aRouKewfr85U2aGagJ46+MvodynlQ1HYdmJys= +go.opentelemetry.io/otel/trace v1.31.0/go.mod h1:TXZkRk7SM2ZQLtR6eoAWQFIHPvzQ06FJAsO1tJg480A= +golang.org/x/crypto v0.28.0 h1:GBDwsMXVQi34v5CCYUm2jkJvu4cbtru2U4TN2PSyQnw= +golang.org/x/crypto v0.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7U= +golang.org/x/exp v0.0.0-20240909161429-701f63a606c0 h1:e66Fs6Z+fZTbFBAxKfP3PALWBtpfqks2bwGcexMxgtk= +golang.org/x/exp v0.0.0-20240909161429-701f63a606c0/go.mod h1:2TbTHSBQa924w8M6Xs1QcRcFwyucIwBGpK1p2f1YFFY= +golang.org/x/mod v0.21.0 h1:vvrHzRwRfVKSiLrG+d4FMl/Qi4ukBCE6kZlTUkDYRT0= +golang.org/x/mod v0.21.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= +golang.org/x/net v0.30.0 h1:AcW1SDZMkb8IpzCdQUaIq2sP4sZ4zw+55h6ynffypl4= +golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU= +golang.org/x/oauth2 v0.23.0 h1:PbgcYx2W7i4LvjJWEbf0ngHV6qJYr86PkAV3bXdLEbs= +golang.org/x/oauth2 v0.23.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= +golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= +golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo= +golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/term v0.25.0 h1:WtHI/ltw4NvSUig5KARz9h521QvRC8RmF/cuYqifU24= +golang.org/x/term v0.25.0/go.mod h1:RPyXicDX+6vLxogjjRxjgD2TKtmAO6NZBsBRfrOLu7M= +golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM= +golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= +golang.org/x/time v0.6.0 h1:eTDhh4ZXt5Qf0augr54TN6suAUudPcawVZeIAPU7D4U= +golang.org/x/time v0.6.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= +google.golang.org/genproto/googleapis/api v0.0.0-20241007155032-5fefd90f89a9 h1:T6rh4haD3GVYsgEfWExoCZA2o2FmbNyKpTuAxbEFPTg= +google.golang.org/genproto/googleapis/api v0.0.0-20241007155032-5fefd90f89a9/go.mod h1:wp2WsuBYj6j8wUdo3ToZsdxxixbvQNAHqVJrTgi5E5M= +google.golang.org/protobuf v1.35.1 h1:m3LfL6/Ca+fqnjnlqQXNpFPABW1UD7mjh8KO2mKFytA= +google.golang.org/protobuf v1.35.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= +gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= +gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gotest.tools/v3 v3.5.1 h1:EENdUnS3pdur5nybKYIh2Vfgc8IUNBjxDPSjtiJcOzU= +gotest.tools/v3 v3.5.1/go.mod h1:isy3WKz7GK6uNw/sbHzfKBLvlvXwUyV06n6brMxxopU= +istio.io/api v1.23.3 h1:+CP0AHz8/+WJ7ZKJLbilHEiqBCi5KLe1Yil9bJI39ow= +istio.io/api v1.23.3/go.mod h1:QPSTGXuIQdnZFEm3myf9NZ5uBMwCdJWUvfj9ZZ+2oBM= +istio.io/client-go v1.23.3 h1:rs+mO4A+NaXVcZgDO0RRZE7KRAlDooq2PSkxl7tevig= +istio.io/client-go v1.23.3/go.mod h1:Lfa3anzx7/kCOpcAciR+JiRMj/SYuzDcbXQDjkThnLg= +k8s.io/api v0.31.2 h1:3wLBbL5Uom/8Zy98GRPXpJ254nEFpl+hwndmk9RwmL0= +k8s.io/api v0.31.2/go.mod h1:bWmGvrGPssSK1ljmLzd3pwCQ9MgoTsRCuK35u6SygUk= +k8s.io/apimachinery v0.31.2 h1:i4vUt2hPK56W6mlT7Ry+AO8eEsyxMD1U44NR22CLTYw= +k8s.io/apimachinery v0.31.2/go.mod h1:rsPdaZJfTfLsNJSQzNHQvYoTmxhoOEofxtOsF3rtsMo= +k8s.io/client-go v0.31.2 h1:Y2F4dxU5d3AQj+ybwSMqQnpZH9F30//1ObxOKlTI9yc= +k8s.io/client-go v0.31.2/go.mod h1:NPa74jSVR/+eez2dFsEIHNa+3o09vtNaWwWwb1qSxSs= +k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk= +k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= +k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 h1:BZqlfIlq5YbRMFko6/PM7FjZpUb45WallggurYhKGag= +k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340/go.mod h1:yD4MZYeKMBwQKVht279WycxKyM84kkAx2DPrTXaeb98= +k8s.io/utils v0.0.0-20240711033017-18e509b52bc8 h1:pUdcCO1Lk/tbT5ztQWOBi5HBgbBP1J8+AsQnQCKsi8A= +k8s.io/utils v0.0.0-20240711033017-18e509b52bc8/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= +modernc.org/gc/v3 v3.0.0-20240107210532-573471604cb6 h1:5D53IMaUuA5InSeMu9eJtlQXS2NxAhyWQvkKEgXZhHI= +modernc.org/gc/v3 v3.0.0-20240107210532-573471604cb6/go.mod h1:Qz0X07sNOR1jWYCrJMEnbW/X55x206Q7Vt4mz6/wHp4= +modernc.org/libc v1.55.3 h1:AzcW1mhlPNrRtjS5sS+eW2ISCgSOLLNyFzRh/V3Qj/U= +modernc.org/libc v1.55.3/go.mod h1:qFXepLhz+JjFThQ4kzwzOjA/y/artDeg+pcYnY+Q83w= +modernc.org/mathutil v1.6.0 h1:fRe9+AmYlaej+64JsEEhoWuAYBkOtQiMEU7n/XgfYi4= +modernc.org/mathutil v1.6.0/go.mod h1:Ui5Q9q1TR2gFm0AQRqQUaBWFLAhQpCwNcuhBOSedWPo= +modernc.org/memory v1.8.0 h1:IqGTL6eFMaDZZhEWwcREgeMXYwmW83LYW8cROZYkg+E= +modernc.org/memory v1.8.0/go.mod h1:XPZ936zp5OMKGWPqbD3JShgd/ZoQ7899TUuQqxY+peU= +modernc.org/sqlite v1.33.1 h1:trb6Z3YYoeM9eDL1O8do81kP+0ejv+YzgyFo+Gwy0nM= +modernc.org/sqlite v1.33.1/go.mod h1:pXV2xHxhzXZsgT/RtTFAPY6JJDEvOTcTdwADQCCWD4k= +modernc.org/strutil v1.2.0 h1:agBi9dp1I+eOnxXeiZawM8F4LawKv4NzGWSaLfyeNZA= +modernc.org/strutil v1.2.0/go.mod h1:/mdcBmfOibveCTBxUl5B5l6W+TTH1FXPLHZE6bTosX0= +modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y= +modernc.org/token v1.1.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM= +sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo= +sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= +sigs.k8s.io/structured-merge-diff/v4 v4.4.1 h1:150L+0vs/8DA78h1u02ooW1/fFq/Lwr+sGiqlzvrtq4= +sigs.k8s.io/structured-merge-diff/v4 v4.4.1/go.mod h1:N8hJocpFajUSSeSJ9bOZ77VzejKZaXsTtZo4/u7Io08= +sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E= +sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY= diff --git a/smoketest/echo/types.ftl.go b/smoketest/echo/types.ftl.go new file mode 100644 index 0000000000..c32e18f9a2 --- /dev/null +++ b/smoketest/echo/types.ftl.go @@ -0,0 +1,17 @@ +// Code generated by FTL. DO NOT EDIT. +package echo + +import ( + "context" + "github.com/TBD54566975/ftl/go-runtime/ftl/reflection" +) + +type EchoClient func(context.Context, string) (string, error) + +func init() { + reflection.Register( + reflection.ProvideResourcesForVerb( + Echo, + ), + ) +} diff --git a/smoketest/smoketest_integration_test.go b/smoketest/exemplar_integration_test.go similarity index 75% rename from smoketest/smoketest_integration_test.go rename to smoketest/exemplar_integration_test.go index a24956e730..8233acef79 100644 --- a/smoketest/smoketest_integration_test.go +++ b/smoketest/exemplar_integration_test.go @@ -15,7 +15,7 @@ import ( in "github.com/TBD54566975/ftl/internal/integration" ) -func TestSmokeTest(t *testing.T) { +func TestExemplarIntegration(t *testing.T) { tmpDir := t.TempDir() logFilePath := filepath.Join(tmpDir, "smoketest.log") @@ -33,7 +33,7 @@ func TestSmokeTest(t *testing.T) { in.CopyModule("origin"), in.CopyModule("relay"), in.CopyModule("pulse"), - in.CreateDBAction("relay", "exemplardb", false), + // in.CreateDBAction("relay", "exemplardb", false), in.ExecWithOutput("ftl", []string{"config", "set", "origin.nonce", "--inline", nonce}, func(output string) { fmt.Println(output) @@ -47,19 +47,21 @@ func TestSmokeTest(t *testing.T) { in.Deploy("relay"), in.Deploy("pulse"), - in.ExecWithOutput("curl", []string{"-s", "-X", "POST", "http://127.0.0.1:8891/http/agent", "-H", "Content-Type: application/json", "-d", fmt.Sprintf(`{"id": %v, "alias": "james", "license_to_kill": true, "hired_at": "2023-10-23T23:20:45.00Z"}`, successAgentId)}, func(output string) { + in.ExecWithOutput("curl", []string{"-s", "-X", "POST", "http://127.0.0.1:8891/ingress/agent", "-H", "Content-Type: application/json", "-d", fmt.Sprintf(`{"id": %v, "alias": "james", "license_to_kill": true, "hired_at": "2023-10-23T23:20:45.00Z"}`, successAgentId)}, func(output string) { + fmt.Printf("output: %s\n", output) err := json.Unmarshal([]byte(output), &postResult) assert.NoError(t, err) assert.Equal(t, successAgentId, postResult.ID) }), - in.ExecWithOutput("curl", []string{"-s", "-X", "POST", "http://127.0.0.1:8891/http/agent", "-H", "Content-Type: application/json", "-d", fmt.Sprintf(`{"id": %v, "alias": "bill", "license_to_kill": false, "hired_at": "2024-08-12T21:10:37.00Z"}`, failedAgentId)}, func(output string) { + in.ExecWithOutput("curl", []string{"-s", "-X", "POST", "http://127.0.0.1:8891/ingress/agent", "-H", "Content-Type: application/json", "-d", fmt.Sprintf(`{"id": %v, "alias": "bill", "license_to_kill": false, "hired_at": "2024-08-12T21:10:37.00Z"}`, failedAgentId)}, func(output string) { + fmt.Printf("output: %s\n", output) err := json.Unmarshal([]byte(output), &postResult) assert.NoError(t, err) assert.Equal(t, failedAgentId, postResult.ID) }), - in.Sleep(2*time.Second), + in.Sleep(5*time.Second), in.ExecWithOutput("ftl", []string{"call", "relay.missionResult", fmt.Sprintf(`{"agentId": %v, "successful": true}`, successAgentId)}, func(output string) { fmt.Println(output) @@ -71,6 +73,10 @@ func TestSmokeTest(t *testing.T) { in.Sleep(2*time.Second), + // in.Call("relay", "fetchLogs", in.Obj{}, func(t testing.TB, resp in.Obj) { + // fmt.Printf("fetchLogs: %v\n", resp) + // }), + in.FileContains(logFilePath, fmt.Sprintf("deployed %d", successAgentId)), in.FileContains(logFilePath, fmt.Sprintf("deployed %d", failedAgentId)), in.FileContains(logFilePath, fmt.Sprintf("succeeded %d", successAgentId)), diff --git a/smoketest/exemplar_smoke_test.go b/smoketest/exemplar_smoke_test.go new file mode 100644 index 0000000000..53466b678f --- /dev/null +++ b/smoketest/exemplar_smoke_test.go @@ -0,0 +1,90 @@ +//go:build smoketest + +package smoketest + +import ( + "encoding/json" + "fmt" + "math/rand" + "path/filepath" + "testing" + "time" + + "github.com/alecthomas/assert/v2" + + in "github.com/TBD54566975/ftl/internal/integration" +) + +func TestExemplarSmoke(t *testing.T) { + tmpDir := "/tmp" + logFilePath := filepath.Join(tmpDir, "smoketest.log") + + var postResult struct { + ID int `json:"id"` + } + var successAgentId int = 7 + var failedAgentId int = 99 + nonce := randomString(4) + + in.Run(t, + in.WithKubernetes(), + in.WithJavaBuild(), + in.WithFTLConfig("../../../ftl-project.toml"), + in.WithTestDataDir("."), + in.CopyModule("origin"), + in.CopyModule("relay"), + in.CopyModule("pulse"), + + in.ExecWithOutput("ftl", []string{"config", "set", "origin.nonce", "--db", nonce}, func(output string) { + fmt.Println(output) + }), + + in.ExecWithOutput("ftl", []string{"config", "set", "relay.log_file", "--db", logFilePath}, func(output string) { + fmt.Println(output) + }), + + in.Deploy("origin"), + in.Deploy("relay"), + in.Deploy("pulse"), + + in.ExecWithOutput("curl", []string{"-s", "-X", "POST", "http://127.0.0.1:8891/ingress/agent", "-H", "Content-Type: application/json", "-d", fmt.Sprintf(`{"id": %v, "alias": "james", "license_to_kill": true, "hired_at": "2023-10-23T23:20:45.00Z"}`, successAgentId)}, func(output string) { + fmt.Printf("output: %s\n", output) + err := json.Unmarshal([]byte(output), &postResult) + assert.NoError(t, err) + assert.Equal(t, successAgentId, postResult.ID) + }), + + in.ExecWithOutput("curl", []string{"-s", "-X", "POST", "http://127.0.0.1:8891/ingress/agent", "-H", "Content-Type: application/json", "-d", fmt.Sprintf(`{"id": %v, "alias": "bill", "license_to_kill": false, "hired_at": "2024-08-12T21:10:37.00Z"}`, failedAgentId)}, func(output string) { + fmt.Printf("output: %s\n", output) + err := json.Unmarshal([]byte(output), &postResult) + assert.NoError(t, err) + assert.Equal(t, failedAgentId, postResult.ID) + }), + + in.Sleep(5*time.Second), + + in.ExecWithOutput("ftl", []string{"call", "relay.missionResult", fmt.Sprintf(`{"agentId": %v, "successful": true}`, successAgentId)}, func(output string) { + fmt.Println(output) + }), + + in.ExecWithOutput("ftl", []string{"call", "relay.missionResult", fmt.Sprintf(`{"agentId": %v, "successful": false}`, failedAgentId)}, func(output string) { + fmt.Println(output) + }), + + in.Sleep(2*time.Second), + + in.Call("relay", "fetchLogs", in.Obj{}, func(t testing.TB, resp in.Obj) { + fmt.Printf("fetchLogs: %v\n", resp) + }), + ) +} + +const charset = "abcdefghijklmnopqrstuvwxyz" + +func randomString(length int) string { + b := make([]byte, length) + for i := range b { + b[i] = charset[rand.Intn(len(charset))] + } + return string(b) +} diff --git a/smoketest/jecho/ftl.toml b/smoketest/jecho/ftl.toml new file mode 100644 index 0000000000..795fff84b5 --- /dev/null +++ b/smoketest/jecho/ftl.toml @@ -0,0 +1,2 @@ +module = "jecho" +language = "java" diff --git a/smoketest/jecho/pom.xml b/smoketest/jecho/pom.xml new file mode 100644 index 0000000000..aaae86efe4 --- /dev/null +++ b/smoketest/jecho/pom.xml @@ -0,0 +1,14 @@ + + + 4.0.0 + ftl.com.example + jecho + 1.0-SNAPSHOT + + + xyz.block.ftl + ftl-build-parent-java + 1.0-SNAPSHOT + + + diff --git a/smoketest/jecho/src/main/java/com/example/EchoVerb.java b/smoketest/jecho/src/main/java/com/example/EchoVerb.java new file mode 100644 index 0000000000..7cd2af1422 --- /dev/null +++ b/smoketest/jecho/src/main/java/com/example/EchoVerb.java @@ -0,0 +1,13 @@ +package com.example; + +import xyz.block.ftl.Export; +import xyz.block.ftl.Verb; + +public class EchoVerb { + + @Export + @Verb + public String echo(String request) { + return "Hello, " + request + "!"; + } +} diff --git a/smoketest/origin/origin.go b/smoketest/origin/origin.go index 9aa2ed7892..97693299ea 100644 --- a/smoketest/origin/origin.go +++ b/smoketest/origin/origin.go @@ -15,11 +15,10 @@ var nonce = ftl.Config[string]("nonce") var AgentBroadcast = ftl.Topic[Agent]("agentBroadcast") type Agent struct { - ID int `json:"id"` - Alias string `json:"alias"` - LicenseToKill bool `json:"license_to_kill"` - HiredAt time.Time `json:"hired_at"` - BriefedAt ftl.Option[time.Time] `json:"briefed_at"` + ID int `json:"id"` + Alias string `json:"alias"` + LicenseToKill bool `json:"license_to_kill"` + HiredAt time.Time `json:"hired_at"` } type PostAgentResponse struct { @@ -28,7 +27,7 @@ type PostAgentResponse struct { type PostAgentErrorResponse string -//ftl:ingress POST /http/agent +//ftl:ingress POST /ingress/agent func PostAgent(ctx context.Context, req builtin.HttpRequest[Agent, ftl.Unit, ftl.Unit]) (builtin.HttpResponse[PostAgentResponse, PostAgentErrorResponse], error) { agent := Agent{ ID: req.Body.ID, @@ -36,7 +35,13 @@ func PostAgent(ctx context.Context, req builtin.HttpRequest[Agent, ftl.Unit, ftl LicenseToKill: req.Body.LicenseToKill, HiredAt: req.Body.HiredAt, } - AgentBroadcast.Publish(ctx, agent) + err := AgentBroadcast.Publish(ctx, agent) + if err != nil { + return builtin.HttpResponse[PostAgentResponse, PostAgentErrorResponse]{ + Status: 500, + Body: ftl.None[PostAgentResponse](), + }, err + } return builtin.HttpResponse[PostAgentResponse, PostAgentErrorResponse]{ Status: 201, Body: ftl.Some(PostAgentResponse{ID: agent.ID}), diff --git a/smoketest/pulse/src/main/java/com/example/Pulse.java b/smoketest/pulse/src/main/java/com/example/Pulse.java index d3880c31ab..ea2808e1b8 100644 --- a/smoketest/pulse/src/main/java/com/example/Pulse.java +++ b/smoketest/pulse/src/main/java/com/example/Pulse.java @@ -6,28 +6,19 @@ import ftl.origin.GetNonceClient; import ftl.origin.GetNonceRequest; import ftl.origin.GetNonceResponse; -import ftl.relay.GetLogFileClient; -import ftl.relay.GetLogFileRequest; -import ftl.relay.GetLogFileResponse; +import ftl.relay.AppendLogClient; +import ftl.relay.AppendLogRequest; +import ftl.relay.AppendLogResponse; import io.quarkus.logging.Log; import xyz.block.ftl.Cron; public class Pulse { @Cron("1s") - public void cron10s(GetNonceClient nonceClient, GetLogFileClient logClient) throws Exception { - GetNonceResponse nr = nonceClient.getNonce(new GetNonceRequest()); - GetLogFileResponse lfr = logClient.getLogFile(new GetLogFileRequest()); - Log.infof("Cron job triggered, nonce %s, log file: %s", nr.getNonce(), lfr.getPath()); - appendLog(lfr.getPath(), "cron %s", nr.getNonce()); - } - - public static void appendLog(String path, String msg, Object... args) { - try (FileWriter writer = new FileWriter(path, true)) { - writer.write(String.format(msg + "%n", args)); - } catch (IOException e) { - throw new RuntimeException("Error writing to log file", e); - } + public void cron10s(GetNonceClient getNonceClient, AppendLogClient appendLogClient) throws Exception { + GetNonceResponse nr = getNonceClient.getNonce(new GetNonceRequest()); + Log.infof("Cron job triggered, nonce %s", nr.getNonce()); + appendLogClient.appendLog(new AppendLogRequest(String.format("cron %s", nr.getNonce()))); } } diff --git a/smoketest/relay/relay.go b/smoketest/relay/relay.go index b32f4ea8ca..30011acad9 100644 --- a/smoketest/relay/relay.go +++ b/smoketest/relay/relay.go @@ -12,7 +12,8 @@ import ( ) var logFile = ftl.Config[string]("log_file") -var db = ftl.PostgresDatabase("exemplardb") + +// var db = ftl.PostgresDatabase("exemplardb") // PubSub @@ -41,9 +42,7 @@ type AgentTerminated struct { //ftl:verb func Briefed(ctx context.Context, agent origin.Agent, deployed DeployedClient) error { - briefedAt := time.Now() - ftl.LoggerFromContext(ctx).Infof("Briefed agent %v at %s", agent.Id, briefedAt) - agent.BriefedAt = ftl.Some(briefedAt) + ftl.LoggerFromContext(ctx).Infof("Briefed agent %v", agent.Id) d := AgentDeployment{ Agent: agent, Target: "villain", @@ -88,7 +87,7 @@ func MissionResult(ctx context.Context, req MissionResultRequest, success Succee AgentID: int(agentID), SuccessAt: time.Now(), } - err := success(ctx, event.(MissionSuccess)) + err := success(ctx, event.(MissionSuccess)) //nolint:forcetypeassert if err != nil { return MissionResultResponse{}, err } @@ -97,7 +96,7 @@ func MissionResult(ctx context.Context, req MissionResultRequest, success Succee AgentID: int(agentID), TerminatedAt: time.Now(), } - err := failure(ctx, event.(AgentTerminated)) + err := failure(ctx, event.(AgentTerminated)) //nolint:forcetypeassert if err != nil { return MissionResultResponse{}, err } @@ -106,14 +105,45 @@ func MissionResult(ctx context.Context, req MissionResultRequest, success Succee return MissionResultResponse{}, nil } -type GetLogFileRequest struct{} -type GetLogFileResponse struct { - Path string +// Logging + +type AppendLogRequest struct { + Message string `json:"message"` +} + +type FetchLogsRequest struct{} + +type FetchLogsResponse struct { + Messages []string `json:"messages"` +} + +//ftl:verb export +func AppendLog(ctx context.Context, req AppendLogRequest) error { + ftl.LoggerFromContext(ctx).Infof("Appending message: %s", req.Message) + return appendLog(ctx, req.Message) } //ftl:verb export -func GetLogFile(ctx context.Context, req GetLogFileRequest) (GetLogFileResponse, error) { - return GetLogFileResponse{Path: logFile.Get(ctx)}, nil +func FetchLogs(ctx context.Context, req FetchLogsRequest) (FetchLogsResponse, error) { + path := logFile.Get(ctx) + if path == "" { + return FetchLogsResponse{}, fmt.Errorf("log_file config not set") + } + r, err := os.Open(path) + if err != nil { + return FetchLogsResponse{}, fmt.Errorf("failed to open log file %q: %w", path, err) + } + defer r.Close() + var messages []string + for { + var msg string + _, err := fmt.Fscanln(r, &msg) + if err != nil { + break + } + messages = append(messages, msg) + } + return FetchLogsResponse{Messages: messages}, nil } // Helpers