Skip to content

Add CRE Storage Service docker compose component - #2869

Draft
akcll wants to merge 1 commit into
mainfrom
axel/DEVSVCS-6421-cre-storage-component
Draft

akcll wants to merge 1 commit into
mainfrom
axel/DEVSVCS-6421-cre-storage-component

Conversation

@akcll

@akcll akcll commented Oct 13, 2026 •

Copy link
Copy Markdown

Adds framework/components/dockercompose/cre_storage_service: a thin testcontainers-go wrapper over the compose file cre-storage-service owns (local-stack/compose.yaml), in the style of billing_platform_service. The compose file stays in the service's repo; this component only knows its contract: the inputs it takes and the role aliases and ports it serves.

  • Input: compose_file (local path or credential-free URL), network (default ctf), and the CapabilitiesRegistry the stack reads nodes from (registry_rpc_url, registry_chain_selector, registry_address) plus don_family. Image overrides (CRE_STORAGE_IMAGE, CRE_STORAGE_MIGRATION_IMAGE, MOCK_ACCOUNT_REGISTRY_IMAGE) and LOG_LEVEL come from the host env.
  • Output: UserService (cre-deploy-api:50051), NodeService (cre-workflow-source / cre-artifact-api:50052) and ArtifactStore (cre-artifact-store:4566), each with an internal URL (the alias on the network) and an external URL (a free host port). gRPC is served without TLS.
  • Stop() removes the stacks it started, with volumes.
  • Book page under Components.

Local CRE (chainlink core/scripts/cre/environment, [extensions]) will move to this component, and CREC system tests can import it to run against the real storage service instead of mocks.

Testing

  • Unit tests for input validation and env construction.
  • Manually: anvil on ctf with a seeded CapabilitiesRegistry; New() brings up the stack (5 services healthy, aliases on ctf, tenant loaded from the chain, host endpoints reachable). cre-storage-service's smoke test (deploy, node list, node download, unknown node rejected) passes against it. Stop() leaves no containers or volumes.
  • No CI e2e yet: the images are in CRE Dev ECR, which this repo's CI role can't pull from.

Note

framework/components/dockercompose doesn't build in-module on main: the containerd/v2 => v2.1.5 replace conflicts with buildkit v0.29, which needs containerd v2.2. Consumers are unaffected, since replaces don't propagate. Not addressed here.

After merge, this needs a framework/components/dockercompose release tag so chainlink can bump to it.

Reproduction Steps

Prerequisites

  • Docker running.
  • A cre-storage-service checkout at ~/Workspace/cre-storage-service, with its images built (see local-stack/README.md, "Build"):
    cd ~/Workspace/cre-storage-service
    GITHUB_TOKEN=$(gh auth token) docker compose --env-file local-stack/standalone.env \
      -f local-stack/compose.yaml -f local-stack/compose.build.yaml build
  • This branch checked out at ~/Workspace/chainlink-testing-framework.

1. Unit tests

framework/components/dockercompose doesn't build in-module on main (the containerd/v2 replace conflicts with buildkit v0.29), so use a temporary go.mod without that replace:

cd ~/Workspace/chainlink-testing-framework/framework/components/dockercompose
grep -v 'containerd/v2 => ' go.mod > /tmp/dc.mod && cp go.sum /tmp/dc.sum
go mod tidy -modfile=/tmp/dc.mod
go test -modfile=/tmp/dc.mod -count=1 -v ./cre_storage_service/

Expect TestEnv and TestNewRejectsIncompleteInput to pass.

2. Create a consumer module

mkdir -p /tmp/ctf-e2e/seed && cd /tmp/ctf-e2e
cat > go.mod <<EOF
module ctfe2e

go 1.26.5

replace (
      github.com/smartcontractkit/chainlink-testing-framework/framework => $HOME/Workspace/chainlink-testing-framework/framework
      github.com/smartcontractkit/chainlink-testing-framework/framework/components/dockercompose => $HOME/Workspace/chainlink-testing-framework/framework/components/dockercompose
)
EOF
cat > e2e_test.go <<'EOF'
package ctfe2e

import (
      "net"
      "net/http"
      "os"
      "testing"
      "time"

      "github.com/stretchr/testify/require"

      storage "github.com/smartcontractkit/chainlink-testing-framework/framework/components/dockercompose/cre_storage_service"
)

func input() *storage.Input {
      return &storage.Input{
              ComposeFile:           os.Getenv("STORAGE_COMPOSE"),
              RegistryRPCURL:        "http://e2e-chain:8545",
              RegistryChainSelector: 3379446385462418246,
              RegistryAddress:       "0x5FbDB2315678afecb367f032d93F642f64180aa3",
              DonFamily:             "zone-a",
      }
}

func TestStart(t *testing.T) {
      out, err := storage.New(input())
      require.NoError(t, err)
      t.Logf("user=%+v node=%+v store=%+v", *out.UserService, *out.NodeService, *out.ArtifactStore)

      for _, addr := range []string{out.UserService.ExternalURL, out.NodeService.ExternalURL} {
              c, err := net.DialTimeout("tcp", addr, 5*time.Second)
              require.NoError(t, err, addr)
              c.Close()
      }
      r, err := http.Get(out.ArtifactStore.ExternalURL + "/_localstack/health")
      require.NoError(t, err)
      r.Body.Close()
      require.Equal(t, http.StatusOK, r.StatusCode)
}

func TestStop(t *testing.T) {
      require.NoError(t, storage.Stop())
}

func TestStartFailure(t *testing.T) {
      t.Setenv("MOCK_ACCOUNT_REGISTRY_IMAGE", "busybox:uclibc") // exits at once, so the stack never gets healthy
      _, err := storage.New(input())
      require.ErrorContains(t, err, "dependency failed to start")
}
EOF
GOWORK=off go mod tidy

3. Start a chain with a CapabilitiesRegistry

An anvil chain on ctf, seeded by cre-storage-service's seed tool, stands in for Local CRE:

docker network create ctf 2>/dev/null || true
docker run -d --rm --name e2e-chain --network ctf -p 18545:8545 --entrypoint anvil \
  ghcr.io/foundry-rs/foundry:stable --host 0.0.0.0 --chain-id 1337
cd ~/Workspace/cre-storage-service
go run ./local-stack/seed -rpc http://localhost:18545 -out /tmp/ctf-e2e/seed

Expect CapabilitiesRegistry 0x5FbDB2315678afecb367f032d93F642f64180aa3, node CSA key ....

4. Build the storage smoke test

It deploys a workflow, then lists and downloads it as the seeded node:

(cd registryclient/tests && GOOS=linux CGO_ENABLED=0 go build -o /tmp/ctf-e2e/seed/localstacksmoke ./localstacksmoke)

5. Start the stack through the component

cd /tmp/ctf-e2e
export GOWORK=off STORAGE_COMPOSE=$HOME/Workspace/cre-storage-service/local-stack/compose.yaml
export TESTCONTAINERS_RYUK_DISABLED=true # keep the stack after the test process exits, for steps 6-7
go test -count=1 -run '^TestStart$' -v .

Expect a pass, logging internal URLs cre-deploy-api:50051, cre-workflow-source:50052 and http://cre-artifact-store:4566, plus localhost:<port> external URLs.

6. Inspect the stack

docker ps --format '{{.Names}}\t{{.Status}}' | grep cre-storage
for c in $(docker ps -q --filter name=cre-storage); do
  docker inspect -f '{{.Name}} {{with index .NetworkSettings.Networks "ctf"}}{{.DNSNames}}{{end}}' $c
done
docker logs $(docker ps -q --filter name=storage-node-service) 2>&1 | grep -E 'CapabilityRegistryCache created|listening'

Expect:

  • 5 healthy containers.
  • Role aliases on ctf: cre-deploy-api (user-service), cre-workflow-source and cre-artifact-api (node-service), cre-artifact-store (S3).
  • node-service logs tenant_count 1 and listens on 50052.

7. Smoke test from the ctf network

docker run --rm --network ctf -v /tmp/ctf-e2e/seed:/seed:ro busybox:uclibc /seed/localstacksmoke

Expect steps 1-4 and PASS: local stack works end to end.

8. Stop

go test -count=1 -run '^TestStop$' .
docker ps -a --format '{{.Names}}' | grep -c cre-storage # 0

9. Failed start leaves nothing behind

go test -count=1 -run '^TestStartFailure$' .
docker ps -a --format '{{.Names}}' | grep -c cre-storage # 0

10. Optional: Ryuk cleanup (the default for tests)

unset TESTCONTAINERS_RYUK_DISABLED
go test -count=1 -run '^TestStart$' .
sleep 15; docker ps -a --format '{{.Names}}' | grep -c cre-storage # 0, removed once the test process exited

11. Clean up

docker rm -f e2e-chain
rm -rf /tmp/ctf-e2e /tmp/dc.mod /tmp/dc.sum

🤖 Generated with Claude Code

@github-actions

github-actions Bot commented Oct 13, 2026 •

Copy link
Copy Markdown

✅ API Diff Results - github.com/smartcontractkit/chainlink-testing-framework/framework/components/dockercompose

✅ Compatible Changes (1)

package github (1)
  • com/smartcontractkit/chainlink-testing-framework/framework/components/dockercompose/cre_storage_service — ➕ Added

📄 View full apidiff report

Thin testcontainers-go wrapper over cre-storage-service's local-stack/compose.yaml,
in the style of billing_platform_service. Returns internal (role alias) and external
(host port) endpoints for the user service, node service and artifact store.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
@akcll
akcll force-pushed the axel/DEVSVCS-6421-cre-storage-component branch from c3f09c3 to 08054c3 Compare October 13, 2026 16:30
@iljapavlovs

Copy link
Copy Markdown
Contributor

Took a look and this looks good to me. One thing we'll need to sort out together: the image story between repo CIs. Our CI will need a way to get these images, either ECR access or a build step we can run. Not blocking this PR, just want it on the radar before we try to run CREC system tests against the stack in CI.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants