diff --git a/framework/docker/chain_node_test.go b/framework/docker/chain_node_test.go index 04a0a74..1431a76 100644 --- a/framework/docker/chain_node_test.go +++ b/framework/docker/chain_node_test.go @@ -1,14 +1,14 @@ package docker import ( + tastoraclient "github.com/celestiaorg/tastora/framework/docker/client" "github.com/stretchr/testify/require" "testing" "github.com/celestiaorg/tastora/framework/docker/container" "github.com/celestiaorg/tastora/framework/docker/cosmos" - "go.uber.org/zap/zaptest" "github.com/cosmos/cosmos-sdk/types/module/testutil" - dockerclient "github.com/moby/moby/client" + "go.uber.org/zap/zaptest" ) func TestChainNodeHostName(t *testing.T) { @@ -20,43 +20,43 @@ func TestChainNodeHostName(t *testing.T) { // Create nodes with different indices chainParams1 := cosmos.ChainNodeParams{ - Validator: true, - ChainID: chainID, - BinaryName: "test-binary", - CoinType: "118", - GasPrices: "0.025utia", - GasAdjustment: 1.0, - Env: []string{}, + Validator: true, + ChainID: chainID, + BinaryName: "test-binary", + CoinType: "118", + GasPrices: "0.025utia", + GasAdjustment: 1.0, + Env: []string{}, AdditionalStartArgs: []string{}, - EncodingConfig: &testutil.TestEncodingConfig{}, + EncodingConfig: &testutil.TestEncodingConfig{}, } - node1 := cosmos.NewChainNode(logger, &dockerclient.Client{}, "test-network", testName, container.Image{}, "/test/home", 0, chainParams1) + node1 := cosmos.NewChainNode(logger, &tastoraclient.Client{}, "test-network", testName, container.Image{}, "/test/home", 0, chainParams1) chainParams2 := cosmos.ChainNodeParams{ - Validator: true, - ChainID: chainID, - BinaryName: "test-binary", - CoinType: "118", - GasPrices: "0.025utia", - GasAdjustment: 1.0, - Env: []string{}, + Validator: true, + ChainID: chainID, + BinaryName: "test-binary", + CoinType: "118", + GasPrices: "0.025utia", + GasAdjustment: 1.0, + Env: []string{}, AdditionalStartArgs: []string{}, - EncodingConfig: &testutil.TestEncodingConfig{}, + EncodingConfig: &testutil.TestEncodingConfig{}, } - node2 := cosmos.NewChainNode(logger, &dockerclient.Client{}, "test-network", testName, container.Image{}, "/test/home", 1, chainParams2) + node2 := cosmos.NewChainNode(logger, &tastoraclient.Client{}, "test-network", testName, container.Image{}, "/test/home", 1, chainParams2) chainParams3 := cosmos.ChainNodeParams{ - Validator: false, - ChainID: chainID, - BinaryName: "test-binary", - CoinType: "118", - GasPrices: "0.025utia", - GasAdjustment: 1.0, - Env: []string{}, + Validator: false, + ChainID: chainID, + BinaryName: "test-binary", + CoinType: "118", + GasPrices: "0.025utia", + GasAdjustment: 1.0, + Env: []string{}, AdditionalStartArgs: []string{}, - EncodingConfig: &testutil.TestEncodingConfig{}, + EncodingConfig: &testutil.TestEncodingConfig{}, } - node3 := cosmos.NewChainNode(logger, &dockerclient.Client{}, "test-network", testName, container.Image{}, "/test/home", 2, chainParams3) + node3 := cosmos.NewChainNode(logger, &tastoraclient.Client{}, "test-network", testName, container.Image{}, "/test/home", 2, chainParams3) // get hostnames hostname1 := node1.HostName() diff --git a/framework/docker/client/client.go b/framework/docker/client/client.go new file mode 100644 index 0000000..9eff539 --- /dev/null +++ b/framework/docker/client/client.go @@ -0,0 +1,32 @@ +package client + +import ( + "github.com/celestiaorg/tastora/framework/types" + "github.com/moby/moby/client" +) + +// Client wraps a Docker client with an associated cleanup label. +// the cleanup label is used to tag all resources (containers, volumes, networks) +// created by this client, enabling cleanup to find and remove exactly the resources +// associated with a specific test run. +// +// Client implements types.TastoraDockerClient. +type Client struct { + *client.Client + cleanupLabel string +} + +var _ types.TastoraDockerClient = (*Client)(nil) + +// NewClient creates a new Client with the given Docker client and cleanup label. +func NewClient(c *client.Client, cleanupLabel string) *Client { + return &Client{ + Client: c, + cleanupLabel: cleanupLabel, + } +} + +// CleanupLabel returns the cleanup label associated with this client. +func (c *Client) CleanupLabel() string { + return c.cleanupLabel +} diff --git a/framework/docker/container/image.go b/framework/docker/container/image.go index 753a442..8e77f40 100644 --- a/framework/docker/container/image.go +++ b/framework/docker/container/image.go @@ -3,8 +3,8 @@ package container import ( "context" "fmt" + "github.com/celestiaorg/tastora/framework/types" dockerimagetypes "github.com/docker/docker/api/types/image" - "github.com/moby/moby/client" "io" ) @@ -31,7 +31,7 @@ func (i Image) Ref() string { return i.Repository + ":" + i.Version } -func (i Image) PullImage(ctx context.Context, client *client.Client) error { +func (i Image) PullImage(ctx context.Context, client types.TastoraDockerClient) error { ref := i.Ref() _, _, err := client.ImageInspectWithRaw(ctx, ref) if err != nil { @@ -43,4 +43,4 @@ func (i Image) PullImage(ctx context.Context, client *client.Client) error { _ = rc.Close() } return nil -} \ No newline at end of file +} diff --git a/framework/docker/container/job.go b/framework/docker/container/job.go index 7d6dcfe..e1ef098 100644 --- a/framework/docker/container/job.go +++ b/framework/docker/container/job.go @@ -8,6 +8,7 @@ import ( "github.com/celestiaorg/tastora/framework/docker/consts" "github.com/celestiaorg/tastora/framework/docker/internal" "github.com/celestiaorg/tastora/framework/testutil/random" + "github.com/celestiaorg/tastora/framework/types" "io" "strconv" "strings" @@ -18,7 +19,6 @@ import ( dockerimagetypes "github.com/docker/docker/api/types/image" "github.com/docker/docker/api/types/mount" "github.com/docker/docker/api/types/network" - "github.com/moby/moby/client" "github.com/moby/moby/errdefs" "github.com/moby/moby/pkg/stdcopy" "go.uber.org/zap" @@ -27,7 +27,7 @@ import ( // Job is a docker job runner. type Job struct { log *zap.Logger - client *client.Client + client types.TastoraDockerClient repository, tag string networkID string @@ -42,7 +42,7 @@ type Job struct { // Most arguments (except tag) must be non-zero values or this function panics. // If tag is absent, defaults to "latest". // Currently, only public docker images are supported. -func NewJob(logger *zap.Logger, cli *client.Client, networkID string, testName string, repository, tag string) *Job { +func NewJob(logger *zap.Logger, cli types.TastoraDockerClient, networkID string, testName string, repository, tag string) *Job { if logger == nil { panic(errors.New("nil Logger")) } @@ -177,7 +177,7 @@ func (job *Job) CreateContainer(ctx context.Context, containerName, hostName str Hostname: hostName, User: opts.User, - Labels: map[string]string{consts.CleanupLabel: job.testName}, + Labels: map[string]string{consts.CleanupLabel: job.client.CleanupLabel()}, }, &container.HostConfig{ Binds: opts.Binds, diff --git a/framework/docker/container/lifecycle.go b/framework/docker/container/lifecycle.go index 4a2f9c8..a805823 100644 --- a/framework/docker/container/lifecycle.go +++ b/framework/docker/container/lifecycle.go @@ -19,7 +19,6 @@ import ( "github.com/docker/docker/api/types/network" "github.com/docker/docker/api/types/volume" "github.com/docker/go-connections/nat" - dockerclient "github.com/moby/moby/client" "github.com/moby/moby/errdefs" "go.uber.org/zap" ) @@ -29,16 +28,16 @@ var panicRe = regexp.MustCompile(`panic:.*\n`) type Lifecycle struct { log *zap.Logger - client *dockerclient.Client + client types.TastoraDockerClient containerName string id string preStartListeners port.Listeners } -func NewLifecycle(log *zap.Logger, client *dockerclient.Client, containerName string) *Lifecycle { +func NewLifecycle(log *zap.Logger, c types.TastoraDockerClient, containerName string) *Lifecycle { return &Lifecycle{ log: log, - client: client, + client: c, containerName: containerName, } } @@ -101,7 +100,7 @@ func (c *Lifecycle) CreateContainer( Cmd: cmd, Env: env, Hostname: hostName, - Labels: map[string]string{consts.CleanupLabel: testName}, + Labels: map[string]string{consts.CleanupLabel: c.client.CleanupLabel()}, ExposedPorts: pS, }, &container.HostConfig{ @@ -237,8 +236,8 @@ func (c *Lifecycle) RemoveContainer(ctx context.Context, opts ...types.RemoveOpt return nil } -func (c *Lifecycle) RemoveVolumes(ctx context.Context, cleanupLabel string) error { - filterArgs := filters.NewArgs(filters.Arg("label", fmt.Sprintf("%s=%s", consts.CleanupLabel, cleanupLabel))) +func (c *Lifecycle) RemoveVolumes(ctx context.Context) error { + filterArgs := filters.NewArgs(filters.Arg("label", fmt.Sprintf("%s=%s", consts.CleanupLabel, c.client.CleanupLabel()))) volumeList, err := c.client.VolumeList(ctx, volume.ListOptions{Filters: filterArgs}) if err != nil { return fmt.Errorf("failed to list volumes: %w", err) diff --git a/framework/docker/container/lifecycle.go.tmp b/framework/docker/container/lifecycle.go.tmp new file mode 100644 index 0000000..e69de29 diff --git a/framework/docker/container/node.go b/framework/docker/container/node.go index c083a55..58ff83b 100644 --- a/framework/docker/container/node.go +++ b/framework/docker/container/node.go @@ -12,7 +12,6 @@ import ( "github.com/docker/docker/api/types/mount" volumetypes "github.com/docker/docker/api/types/volume" "github.com/docker/go-connections/nat" - dockerclient "github.com/moby/moby/client" "go.uber.org/zap" ) @@ -20,7 +19,7 @@ import ( type Node struct { VolumeName string NetworkID string - DockerClient *dockerclient.Client + DockerClient types.TastoraDockerClient TestName string Image Image ContainerLifecycle *Lifecycle @@ -33,7 +32,7 @@ type Node struct { // NewNode creates a new Node instance with the required parameters. func NewNode( networkID string, - dockerClient *dockerclient.Client, + dockerClient types.TastoraDockerClient, testName string, image Image, homeDir string, @@ -97,7 +96,7 @@ func (n *Node) RemoveContainer(ctx context.Context, opts ...types.RemoveOption) if !removeOpts.RemoveVolumes { return nil } - return n.ContainerLifecycle.RemoveVolumes(ctx, n.TestName) + return n.ContainerLifecycle.RemoveVolumes(ctx) } // StopContainer gracefully stops the container associated with the Node using the provided context. @@ -198,7 +197,7 @@ func (n *Node) ensureVolume(ctx context.Context, nodeName, volName string) error v, createErr := n.DockerClient.VolumeCreate(ctx, volumetypes.CreateOptions{ Name: volName, Labels: map[string]string{ - consts.CleanupLabel: n.TestName, + consts.CleanupLabel: n.DockerClient.CleanupLabel(), consts.NodeOwnerLabel: nodeName, }, }) diff --git a/framework/docker/container/node.go.tmp b/framework/docker/container/node.go.tmp new file mode 100644 index 0000000..e69de29 diff --git a/framework/docker/cosmos/chain_builder.go b/framework/docker/cosmos/chain_builder.go index 7c85b8d..ccfac70 100644 --- a/framework/docker/cosmos/chain_builder.go +++ b/framework/docker/cosmos/chain_builder.go @@ -14,7 +14,6 @@ import ( cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec" "github.com/cosmos/cosmos-sdk/crypto/keyring" "github.com/cosmos/cosmos-sdk/types/module/testutil" - "github.com/moby/moby/client" "go.uber.org/zap" "go.uber.org/zap/zaptest" ) @@ -115,7 +114,7 @@ type ChainBuilder struct { // nodes is the array of node configurations that define the chain topology and individual node settings nodes []ChainNodeConfig // dockerClient is the Docker client instance used for all container operations (create, start, stop, etc.) - dockerClient *client.Client + dockerClient types.TastoraDockerClient // dockerNetworkID is the ID of the Docker network where all chain nodes are deployed dockerNetworkID string // genesisBz contains raw bytes that should be written as the config/genesis.json file for the chain (optional) @@ -247,8 +246,8 @@ func (b *ChainBuilder) WithNodes(nodeConfigs ...ChainNodeConfig) *ChainBuilder { } // WithDockerClient sets the Docker client -func (b *ChainBuilder) WithDockerClient(client *client.Client) *ChainBuilder { - b.dockerClient = client +func (b *ChainBuilder) WithDockerClient(c types.TastoraDockerClient) *ChainBuilder { + b.dockerClient = c return b } diff --git a/framework/docker/cosmos/chain_config.go b/framework/docker/cosmos/chain_config.go index 290a6ca..a973f2a 100644 --- a/framework/docker/cosmos/chain_config.go +++ b/framework/docker/cosmos/chain_config.go @@ -3,18 +3,18 @@ package cosmos import ( "context" "github.com/celestiaorg/tastora/framework/docker/container" + "github.com/celestiaorg/tastora/framework/types" "github.com/cosmos/cosmos-sdk/types/module/testutil" - "github.com/moby/moby/client" "go.uber.org/zap" ) type ChainConfig struct { Logger *zap.Logger // DockerClient is a Docker client instance used for the tests. - DockerClient *client.Client + DockerClient types.TastoraDockerClient // DockerNetworkID is the ID of the docker network the nodes are deployed to. DockerNetworkID string - + // Chain configuration fields (previously in ChainConfig) // Chain name, e.g. celestia. Name string diff --git a/framework/docker/cosmos/node.go b/framework/docker/cosmos/node.go index b759198..c9d2483 100644 --- a/framework/docker/cosmos/node.go +++ b/framework/docker/cosmos/node.go @@ -32,7 +32,6 @@ import ( govv1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1" stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" "github.com/docker/go-connections/nat" - dockerclient "github.com/moby/moby/client" "go.uber.org/zap" "google.golang.org/grpc" "google.golang.org/grpc/credentials/insecure" @@ -116,7 +115,7 @@ type ChainNodeParams struct { // NewChainNode creates a new ChainNode with injected dependencies func NewChainNode( logger *zap.Logger, - dockerClient *dockerclient.Client, + dockerClient types.TastoraDockerClient, dockerNetworkID string, testName string, image container.Image, diff --git a/framework/docker/dataavailability/config.go b/framework/docker/dataavailability/config.go index e65158d..d464b3f 100644 --- a/framework/docker/dataavailability/config.go +++ b/framework/docker/dataavailability/config.go @@ -2,7 +2,7 @@ package dataavailability import ( "github.com/celestiaorg/tastora/framework/docker/container" - "github.com/moby/moby/client" + "github.com/celestiaorg/tastora/framework/types" "go.uber.org/zap" ) @@ -11,7 +11,7 @@ type Config struct { // Logger is the logger instance used for all operations Logger *zap.Logger // DockerClient is the docker client instance - DockerClient *client.Client + DockerClient types.TastoraDockerClient // DockerNetworkID is the ID of the docker network to use DockerNetworkID string // ChainID, e.g. test-chain diff --git a/framework/docker/dataavailability/network_builder.go b/framework/docker/dataavailability/network_builder.go index 890522c..4695dc3 100644 --- a/framework/docker/dataavailability/network_builder.go +++ b/framework/docker/dataavailability/network_builder.go @@ -3,10 +3,10 @@ package dataavailability import ( "context" "fmt" + "github.com/celestiaorg/tastora/framework/types" "testing" "github.com/celestiaorg/tastora/framework/docker/container" - "github.com/moby/moby/client" "go.uber.org/zap" "go.uber.org/zap/zaptest" ) @@ -20,7 +20,7 @@ type NetworkBuilder struct { // nodes is the array of node configurations that define the network topology and individual node settings nodes []NodeConfig // dockerClient is the Docker client instance used for all container operations - dockerClient *client.Client + dockerClient types.TastoraDockerClient // dockerNetworkID is the ID of the Docker network where all Nodes are deployed dockerNetworkID string // logger is the structured logger for network operations and debugging. Defaults to test logger. @@ -97,8 +97,8 @@ func (b *NetworkBuilder) WithBinaryName(binaryName string) *NetworkBuilder { } // WithDockerClient sets the Docker client -func (b *NetworkBuilder) WithDockerClient(client *client.Client) *NetworkBuilder { - b.dockerClient = client +func (b *NetworkBuilder) WithDockerClient(c types.TastoraDockerClient) *NetworkBuilder { + b.dockerClient = c return b } diff --git a/framework/docker/docker_test.go b/framework/docker/docker_test.go index d2ad34c..9000337 100644 --- a/framework/docker/docker_test.go +++ b/framework/docker/docker_test.go @@ -11,7 +11,6 @@ import ( "github.com/celestiaorg/tastora/framework/testutil/random" "github.com/celestiaorg/tastora/framework/types" govmodule "github.com/cosmos/cosmos-sdk/x/gov" - "github.com/moby/moby/client" "sync" "testing" @@ -40,7 +39,7 @@ func configureBech32PrefixOnce() { // TestSetupConfig contains all the components needed for a Docker test type TestSetupConfig struct { - DockerClient *client.Client + DockerClient types.TastoraDockerClient NetworkID string TestName string Logger *zap.Logger @@ -64,10 +63,7 @@ func setupDockerTest(t *testing.T) *TestSetupConfig { uniqueTestName := fmt.Sprintf("%s-%s", t.Name(), random.LowerCaseLetterString(8)) ctx := context.Background() - dockerClient, networkID := DockerSetup(t) - - // Override the default cleanup to use our unique test name - t.Cleanup(DockerCleanupWithTestName(t, dockerClient, uniqueTestName)) + dockerClient, networkID := Setup(t) logger := zaptest.NewLogger(t) encConfig := testutil.MakeTestEncodingConfig(auth.AppModuleBasic{}, bank.AppModuleBasic{}, transfer.AppModuleBasic{}, govmodule.AppModuleBasic{}) diff --git a/framework/docker/evstack/chain_builder.go b/framework/docker/evstack/chain_builder.go index f24190d..b231367 100644 --- a/framework/docker/evstack/chain_builder.go +++ b/framework/docker/evstack/chain_builder.go @@ -3,10 +3,10 @@ package evstack import ( "context" "fmt" + "github.com/celestiaorg/tastora/framework/types" "testing" "github.com/celestiaorg/tastora/framework/docker/container" - "github.com/moby/moby/client" "go.uber.org/zap" "go.uber.org/zap/zaptest" ) @@ -20,7 +20,7 @@ type ChainBuilder struct { // nodes is the array of node configurations that define the chain topology and individual node settings nodes []NodeConfig // dockerClient is the Docker client instance used for all container operations - dockerClient *client.Client + dockerClient types.TastoraDockerClient // dockerNetworkID is the ID of the Docker network where all Nodes are deployed dockerNetworkID string // logger is the structured logger for chain operations and debugging. Defaults to test logger. @@ -90,7 +90,7 @@ func (b *ChainBuilder) WithAggregatorPassphrase(passphrase string) *ChainBuilder } // WithDockerClient sets the Docker client -func (b *ChainBuilder) WithDockerClient(client *client.Client) *ChainBuilder { +func (b *ChainBuilder) WithDockerClient(client types.TastoraDockerClient) *ChainBuilder { b.dockerClient = client return b } diff --git a/framework/docker/evstack/config.go b/framework/docker/evstack/config.go index 62ca285..495285b 100644 --- a/framework/docker/evstack/config.go +++ b/framework/docker/evstack/config.go @@ -2,7 +2,7 @@ package evstack import ( "github.com/celestiaorg/tastora/framework/docker/container" - "github.com/moby/moby/client" + "github.com/celestiaorg/tastora/framework/types" "go.uber.org/zap" ) @@ -10,8 +10,8 @@ import ( type Config struct { // Logger is the logger instance used for all operations Logger *zap.Logger - // DockerClient is the docker client instance - DockerClient *client.Client + // DockerClient is the docker client instance + DockerClient types.TastoraDockerClient // DockerNetworkID is the ID of the docker network to use DockerNetworkID string // ChainID, e.g. test-evstack @@ -24,4 +24,4 @@ type Config struct { AggregatorPassphrase string // Image specifies the Docker image used for the evstack nodes. Image container.Image -} \ No newline at end of file +} diff --git a/framework/docker/evstack/evmsingle/builder.go b/framework/docker/evstack/evmsingle/builder.go index 1f62ad5..aad2700 100644 --- a/framework/docker/evstack/evmsingle/builder.go +++ b/framework/docker/evstack/evmsingle/builder.go @@ -2,10 +2,10 @@ package evmsingle import ( "context" + "github.com/celestiaorg/tastora/framework/types" "testing" "github.com/celestiaorg/tastora/framework/docker/container" - dockerclient "github.com/moby/moby/client" "go.uber.org/zap" "go.uber.org/zap/zaptest" ) @@ -15,7 +15,7 @@ type ChainBuilder struct { t *testing.T testName string logger *zap.Logger - dockerClient *dockerclient.Client + dockerClient types.TastoraDockerClient networkID string image container.Image env []string @@ -51,7 +51,7 @@ func (b *ChainBuilder) WithLogger(l *zap.Logger) *ChainBuilder { return b } -func (b *ChainBuilder) WithDockerClient(c *dockerclient.Client) *ChainBuilder { +func (b *ChainBuilder) WithDockerClient(c types.TastoraDockerClient) *ChainBuilder { b.dockerClient = c return b } diff --git a/framework/docker/evstack/evmsingle/config.go b/framework/docker/evstack/evmsingle/config.go index 2a34654..815ba3d 100644 --- a/framework/docker/evstack/evmsingle/config.go +++ b/framework/docker/evstack/evmsingle/config.go @@ -2,14 +2,14 @@ package evmsingle import ( "github.com/celestiaorg/tastora/framework/docker/container" - dockerclient "github.com/moby/moby/client" + "github.com/celestiaorg/tastora/framework/types" "go.uber.org/zap" ) // Config holds chain-level configuration for ev-node-evm-single nodes type Config struct { Logger *zap.Logger - DockerClient *dockerclient.Client + DockerClient types.TastoraDockerClient DockerNetworkID string // Image is the default image for all nodes Image container.Image diff --git a/framework/docker/evstack/reth/builder.go b/framework/docker/evstack/reth/builder.go index baadcff..20afa32 100644 --- a/framework/docker/evstack/reth/builder.go +++ b/framework/docker/evstack/reth/builder.go @@ -2,10 +2,10 @@ package reth import ( "context" + "github.com/celestiaorg/tastora/framework/types" "testing" "github.com/celestiaorg/tastora/framework/docker/container" - dockerclient "github.com/moby/moby/client" "go.uber.org/zap" "go.uber.org/zap/zaptest" ) @@ -15,7 +15,7 @@ type NodeBuilder struct { t *testing.T testName string logger *zap.Logger - dockerClient *dockerclient.Client + dockerClient types.TastoraDockerClient networkID string image container.Image env []string @@ -51,7 +51,7 @@ func (b *NodeBuilder) WithLogger(l *zap.Logger) *NodeBuilder { b.logger = l return b } -func (b *NodeBuilder) WithDockerClient(c *dockerclient.Client) *NodeBuilder { +func (b *NodeBuilder) WithDockerClient(c types.TastoraDockerClient) *NodeBuilder { b.dockerClient = c return b } diff --git a/framework/docker/evstack/reth/config.go b/framework/docker/evstack/reth/config.go index d6559b7..1702e24 100644 --- a/framework/docker/evstack/reth/config.go +++ b/framework/docker/evstack/reth/config.go @@ -2,14 +2,14 @@ package reth import ( "github.com/celestiaorg/tastora/framework/docker/container" - dockerclient "github.com/moby/moby/client" + "github.com/celestiaorg/tastora/framework/types" "go.uber.org/zap" ) // Config holds node-level configuration for Reth type Config struct { Logger *zap.Logger - DockerClient *dockerclient.Client + DockerClient types.TastoraDockerClient DockerNetworkID string // Image is the default image for all nodes diff --git a/framework/docker/file/file_retriever.go b/framework/docker/file/file_retriever.go index f48a8dc..9545cfe 100644 --- a/framework/docker/file/file_retriever.go +++ b/framework/docker/file/file_retriever.go @@ -4,6 +4,7 @@ import ( "archive/tar" "context" "fmt" + "github.com/celestiaorg/tastora/framework/types" "io" "path" "time" @@ -13,19 +14,18 @@ import ( "github.com/celestiaorg/tastora/framework/testutil/random" "github.com/docker/docker/api/types/container" - "github.com/moby/moby/client" "go.uber.org/zap" ) // Retriever allows retrieving a single file from a Docker volume. type Retriever struct { log *zap.Logger - cli *client.Client + cli types.TastoraDockerClient testName string } // NewRetriever returns a new Retriever. -func NewRetriever(log *zap.Logger, cli *client.Client, testName string) *Retriever { +func NewRetriever(log *zap.Logger, cli types.TastoraDockerClient, testName string) *Retriever { return &Retriever{log: log, cli: cli, testName: testName} } @@ -46,7 +46,7 @@ func (r *Retriever) SingleFileContent(ctx context.Context, volumeName, relPath s Image: internaldocker.BusyboxRef, // Use root user to avoid permission issues when reading files from the volume. User: consts.UserRootString, - Labels: map[string]string{consts.CleanupLabel: r.testName}, + Labels: map[string]string{consts.CleanupLabel: r.cli.CleanupLabel()}, }, &container.HostConfig{ Binds: []string{volumeName + ":" + mountPath}, diff --git a/framework/docker/file/file_retriever_test.go b/framework/docker/file/file_retriever_test.go index e7ee31a..863e31c 100644 --- a/framework/docker/file/file_retriever_test.go +++ b/framework/docker/file/file_retriever_test.go @@ -3,8 +3,8 @@ package file_test import ( "context" "github.com/celestiaorg/tastora/framework/docker" - "github.com/celestiaorg/tastora/framework/docker/container" "github.com/celestiaorg/tastora/framework/docker/consts" + "github.com/celestiaorg/tastora/framework/docker/container" "github.com/celestiaorg/tastora/framework/docker/file" volumetypes "github.com/docker/docker/api/types/volume" "github.com/stretchr/testify/require" @@ -19,7 +19,7 @@ func TestFileRetriever(t *testing.T) { t.Parallel() - cli, network := docker.DockerSetup(t) + cli, network := docker.Setup(t) ctx := context.Background() v, err := cli.VolumeCreate(ctx, volumetypes.CreateOptions{ diff --git a/framework/docker/file/file_writer.go b/framework/docker/file/file_writer.go index d4a7871..4392f7b 100644 --- a/framework/docker/file/file_writer.go +++ b/framework/docker/file/file_writer.go @@ -10,9 +10,9 @@ import ( "github.com/celestiaorg/tastora/framework/docker/consts" internaldocker "github.com/celestiaorg/tastora/framework/docker/internal" "github.com/celestiaorg/tastora/framework/testutil/random" + "github.com/celestiaorg/tastora/framework/types" "github.com/docker/docker/api/types/container" - "github.com/moby/moby/client" "go.uber.org/zap" ) @@ -20,12 +20,12 @@ import ( // In the future it may allow retrieving an entire directory. type Writer struct { log *zap.Logger - cli *client.Client + cli types.TastoraDockerClient testName string } // NewWriter returns a new Writer. -func NewWriter(log *zap.Logger, cli *client.Client, testName string) *Writer { +func NewWriter(log *zap.Logger, cli types.TastoraDockerClient, testName string) *Writer { return &Writer{log: log, cli: cli, testName: testName} } @@ -54,7 +54,7 @@ func (w *Writer) WriteFile(ctx context.Context, volumeName, relPath string, cont }, // Use root user to avoid permission issues when reading files from the volume. User: consts.UserRootString, - Labels: map[string]string{consts.CleanupLabel: w.testName}, + Labels: map[string]string{consts.CleanupLabel: w.cli.CleanupLabel()}, }, &container.HostConfig{ Binds: []string{volumeName + ":" + mountPath}, diff --git a/framework/docker/file/file_writer_test.go b/framework/docker/file/file_writer_test.go index e87a16d..041f3fe 100644 --- a/framework/docker/file/file_writer_test.go +++ b/framework/docker/file/file_writer_test.go @@ -3,8 +3,8 @@ package file_test import ( "context" "github.com/celestiaorg/tastora/framework/docker" - "github.com/celestiaorg/tastora/framework/docker/container" "github.com/celestiaorg/tastora/framework/docker/consts" + "github.com/celestiaorg/tastora/framework/docker/container" "github.com/celestiaorg/tastora/framework/docker/file" volumetypes "github.com/docker/docker/api/types/volume" "github.com/stretchr/testify/require" @@ -19,7 +19,7 @@ func TestFileWriter(t *testing.T) { t.Parallel() - cli, network := docker.DockerSetup(t) + cli, network := docker.Setup(t) ctx := context.Background() v, err := cli.VolumeCreate(ctx, volumetypes.CreateOptions{ diff --git a/framework/docker/ibc/relayer/hermes.go b/framework/docker/ibc/relayer/hermes.go index 6f7002d..2467941 100644 --- a/framework/docker/ibc/relayer/hermes.go +++ b/framework/docker/ibc/relayer/hermes.go @@ -18,7 +18,6 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" "github.com/cosmos/go-bip39" - dockerclient "github.com/moby/moby/client" "go.uber.org/zap" ) @@ -37,7 +36,7 @@ type Hermes struct { } // NewHermes creates a new Hermes relayer instance. -func NewHermes(ctx context.Context, dockerClient *dockerclient.Client, testName, networkID string, index int, logger *zap.Logger) (*Hermes, error) { +func NewHermes(ctx context.Context, dockerClient types.TastoraDockerClient, testName, networkID string, index int, logger *zap.Logger) (*Hermes, error) { image := container.Image{ Repository: hermesDefaultImage, Version: hermesDefaultVersion, diff --git a/framework/docker/ibc_test.go b/framework/docker/ibc_test.go index 5d15bc5..0a9d691 100644 --- a/framework/docker/ibc_test.go +++ b/framework/docker/ibc_test.go @@ -10,13 +10,12 @@ import ( "github.com/celestiaorg/tastora/framework/types" servercfg "github.com/cosmos/cosmos-sdk/server/config" "github.com/cosmos/cosmos-sdk/types/module/testutil" - dockerclient "github.com/moby/moby/client" "github.com/stretchr/testify/require" "testing" ) // createCelestiaChain creates a celestia-app chain for IBC testing -func createCelestiaChain(t *testing.T, ctx context.Context, client *dockerclient.Client, networkID string, encConfig testutil.TestEncodingConfig, testName string) (types.Chain, error) { +func createCelestiaChain(t *testing.T, ctx context.Context, client types.TastoraDockerClient, networkID string, encConfig testutil.TestEncodingConfig, testName string) (types.Chain, error) { builder := cosmos.NewChainBuilderWithTestName(t, testName). WithDockerClient(client). WithDockerNetworkID(networkID). @@ -48,7 +47,7 @@ func createCelestiaChain(t *testing.T, ctx context.Context, client *dockerclient } // createSimappChain creates an IBC-Go simapp chain for IBC testing -func createSimappChain(t *testing.T, ctx context.Context, client *dockerclient.Client, networkID string, encConfig testutil.TestEncodingConfig, testName string) (types.Chain, error) { +func createSimappChain(t *testing.T, ctx context.Context, client types.TastoraDockerClient, networkID string, encConfig testutil.TestEncodingConfig, testName string) (types.Chain, error) { builder := cosmos.NewChainBuilderWithTestName(t, testName). WithDockerClient(client). WithDockerNetworkID(networkID). diff --git a/framework/docker/ibc_transfer_test.go b/framework/docker/ibc_transfer_test.go index 28713f5..9e48e9b 100644 --- a/framework/docker/ibc_transfer_test.go +++ b/framework/docker/ibc_transfer_test.go @@ -48,10 +48,7 @@ func setupIBCDockerTest(t *testing.T) *IBCTestSetupConfig { // Generate unique test name for parallel execution uniqueTestName := fmt.Sprintf("%s-%s", t.Name(), random.LowerCaseLetterString(8)) - dockerClient, networkID := DockerSetup(t) - - // Override the default cleanup to use our unique test name - t.Cleanup(DockerCleanupWithTestName(t, dockerClient, uniqueTestName)) + dockerClient, networkID := Setup(t) // Create celestia-app chain (chain A) chainA, err := createCelestiaChain(t, ctx, dockerClient, networkID, encConfig, uniqueTestName) diff --git a/framework/docker/internal/busybox.go b/framework/docker/internal/busybox.go index 72857f1..dfd8579 100644 --- a/framework/docker/internal/busybox.go +++ b/framework/docker/internal/busybox.go @@ -3,6 +3,7 @@ package internal import ( "context" "fmt" + "github.com/celestiaorg/tastora/framework/types" "io" "sync" "time" @@ -10,7 +11,6 @@ import ( "github.com/celestiaorg/tastora/framework/testutil/wait" "github.com/docker/docker/api/types/filters" dockerimagetypes "github.com/docker/docker/api/types/image" - "github.com/moby/moby/client" ) // Allow multiple goroutines to check for busybox @@ -25,7 +25,7 @@ var ( const BusyboxRef = "busybox:stable" -func EnsureBusybox(ctx context.Context, cli *client.Client) error { +func EnsureBusybox(ctx context.Context, cli types.TastoraDockerClient) error { ensureBusyboxMu.Lock() defer ensureBusyboxMu.Unlock() diff --git a/framework/docker/internal/docker_keyring.go b/framework/docker/internal/docker_keyring.go index 180bd2e..74c0e15 100644 --- a/framework/docker/internal/docker_keyring.go +++ b/framework/docker/internal/docker_keyring.go @@ -5,13 +5,13 @@ import ( "bytes" "context" "fmt" + tastoratypes "github.com/celestiaorg/tastora/framework/types" "github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/crypto/keyring" "github.com/cosmos/cosmos-sdk/crypto/types" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/tx/signing" "github.com/docker/docker/api/types/container" - "github.com/moby/moby/client" "os" "path/filepath" "sync" @@ -23,7 +23,7 @@ var _ keyring.Keyring = &dockerKeyring{} // Any writes to the keyring are also propagated to the container filesystem. type dockerKeyring struct { mu sync.RWMutex - dockerClient *client.Client + dockerClient tastoratypes.TastoraDockerClient containerID string containerKeyringDir string cdc codec.Codec @@ -34,7 +34,7 @@ type dockerKeyring struct { } // NewDockerKeyring creates a new dockerKeyring instance. -func NewDockerKeyring(dockerClient *client.Client, containerID, containerKeyringDir string, cdc codec.Codec) keyring.Keyring { +func NewDockerKeyring(dockerClient tastoratypes.TastoraDockerClient, containerID, containerKeyringDir string, cdc codec.Codec) keyring.Keyring { return &dockerKeyring{ dockerClient: dockerClient, containerID: containerID, diff --git a/framework/docker/internal/docker_keyring_test.go b/framework/docker/internal/docker_keyring_test.go index a29fe9f..6368eae 100644 --- a/framework/docker/internal/docker_keyring_test.go +++ b/framework/docker/internal/docker_keyring_test.go @@ -2,6 +2,7 @@ package internal import ( "context" + tastoraclient "github.com/celestiaorg/tastora/framework/docker/client" "io" "testing" "time" @@ -20,7 +21,7 @@ import ( type DockerKeyringTestSuite struct { suite.Suite - dockerClient *client.Client + dockerClient *tastoraclient.Client containerID string keyringDir string cdc codec.Codec @@ -35,7 +36,7 @@ func (s *DockerKeyringTestSuite) SetupSuite() { dockerClient, err := client.NewClientWithOpts(client.FromEnv, client.WithAPIVersionNegotiation()) s.Require().NoError(err) - s.dockerClient = dockerClient + s.dockerClient = tastoraclient.NewClient(dockerClient, "docker-keyring-test-suite") registry := codectypes.NewInterfaceRegistry() cryptocodec.RegisterInterfaces(registry) diff --git a/framework/docker/internal/keyring.go b/framework/docker/internal/keyring.go index a3fa11a..aafa748 100644 --- a/framework/docker/internal/keyring.go +++ b/framework/docker/internal/keyring.go @@ -4,13 +4,12 @@ import ( "archive/tar" "bytes" "context" + "github.com/celestiaorg/tastora/framework/types" "io" "os" "path" "path/filepath" - "github.com/moby/moby/client" - "github.com/cosmos/cosmos-sdk/codec" codectypes "github.com/cosmos/cosmos-sdk/codec/types" cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec" @@ -19,7 +18,7 @@ import ( // NewLocalKeyringFromDockerContainer copies the contents of the given container directory into a specified local directory. // This allows test hosts to sign transactions on behalf of test users. -func NewLocalKeyringFromDockerContainer(ctx context.Context, dc *client.Client, localDirectory, containerKeyringDir, containerID string) (keyring.Keyring, error) { +func NewLocalKeyringFromDockerContainer(ctx context.Context, dc types.TastoraDockerClient, localDirectory, containerKeyringDir, containerID string) (keyring.Keyring, error) { reader, _, err := dc.CopyFromContainer(ctx, containerID, containerKeyringDir) if err != nil { return nil, err diff --git a/framework/docker/internal/network.go b/framework/docker/internal/network.go index ce470aa..bb17956 100644 --- a/framework/docker/internal/network.go +++ b/framework/docker/internal/network.go @@ -3,13 +3,13 @@ package internal import ( "context" "fmt" - dockerclient "github.com/moby/moby/client" + "github.com/celestiaorg/tastora/framework/types" "net" ) // GetContainerInternalIP returns the internal IP address of a container within the docker network. // Returns empty string if container is not yet networked (no error). -func GetContainerInternalIP(ctx context.Context, client dockerclient.APIClient, containerID string) (string, error) { +func GetContainerInternalIP(ctx context.Context, client types.TastoraDockerClient, containerID string) (string, error) { inspect, err := client.ContainerInspect(ctx, containerID) if err != nil { return "", fmt.Errorf("inspecting container: %w", err) diff --git a/framework/docker/internal/startcontainer.go b/framework/docker/internal/startcontainer.go index bed6959..6bfa328 100644 --- a/framework/docker/internal/startcontainer.go +++ b/framework/docker/internal/startcontainer.go @@ -2,11 +2,11 @@ package internal import ( "context" + "github.com/celestiaorg/tastora/framework/types" "sync" "time" "github.com/docker/docker/api/types/container" - "github.com/moby/moby/client" ) // portAssignmentMu prevents race conditions during the critical window between @@ -15,7 +15,7 @@ import ( var portAssignmentMu sync.Mutex // StartContainer attempts to start the container with the given ID. -func StartContainer(ctx context.Context, cli *client.Client, id string) error { +func StartContainer(ctx context.Context, cli types.TastoraDockerClient, id string) error { // add a deadline for the request if the calling context does not provide one if _, hasDeadline := ctx.Deadline(); !hasDeadline { var cancel func() diff --git a/framework/docker/setup.go b/framework/docker/setup.go index b6744a0..8476399 100644 --- a/framework/docker/setup.go +++ b/framework/docker/setup.go @@ -4,10 +4,12 @@ import ( "bytes" "context" "fmt" + "github.com/celestiaorg/tastora/framework/types" "os" "strings" "time" + tastoraclient "github.com/celestiaorg/tastora/framework/docker/client" "github.com/celestiaorg/tastora/framework/docker/consts" "github.com/celestiaorg/tastora/framework/testutil/random" @@ -15,12 +17,13 @@ import ( "github.com/docker/docker/api/types/container" "github.com/docker/docker/api/types/filters" "github.com/docker/docker/api/types/network" + "github.com/docker/docker/api/types/volume" "github.com/moby/moby/client" "github.com/moby/moby/errdefs" ) -// DockerSetupTestingT is a subset of testing.T required for DockerSetup. -type DockerSetupTestingT interface { +// SetupTestingT is a subset of testing.T required for Setup. +type SetupTestingT interface { Helper() Name() string @@ -32,19 +35,19 @@ type DockerSetupTestingT interface { } // KeepVolumesOnFailure determines whether volumes associated with a test -// using DockerSetup are retained or deleted following a test failure. +// using Setup are retained or deleted following a test failure. // // The value is false by default, but can be initialized to true by setting the -// environment variable ICTEST_SKIP_FAILURE_CLEANUP to a non-empty value. +// environment variable TASTORA_SKIP_FAILURE_CLEANUP to a non-empty value. // Alternatively, importers of the dockerutil package may set the variable to true. // Because dockerutil is an internal package, the public API for setting this value // is interchaintest.KeepDockerVolumesOnFailure(bool). -var KeepVolumesOnFailure = os.Getenv("ICTEST_SKIP_FAILURE_CLEANUP") != "" +var KeepVolumesOnFailure = os.Getenv("TASTORA_SKIP_FAILURE_CLEANUP") != "" -// DockerSetup returns a new Docker Client and the ID of a configured network, associated with t. +// Setup returns a new Docker Client and the ID of a configured network, associated with t. // -// If any part of the setup fails, DockerSetup panics because the test cannot continue. -func DockerSetup(t DockerSetupTestingT) (*client.Client, string) { +// If any part of the setup fails, Setup panics because the test cannot continue. +func Setup(t SetupTestingT) (types.TastoraDockerClient, string) { t.Helper() cli, err := client.NewClientWithOpts(client.FromEnv, client.WithAPIVersionNegotiation()) @@ -52,26 +55,31 @@ func DockerSetup(t DockerSetupTestingT) (*client.Client, string) { panic(fmt.Errorf("failed to create docker client: %v", err)) } + cleanupLabel := fmt.Sprintf("%s-%s", t.Name(), random.LowerCaseLetterString(8)) + dockerClient := tastoraclient.NewClient(cli, cleanupLabel) + name := fmt.Sprintf("%s-%s", consts.CelestiaDockerPrefix, random.LowerCaseLetterString(8)) - network, err := cli.NetworkCreate(context.TODO(), name, network.CreateOptions{ + network, err := dockerClient.NetworkCreate(context.TODO(), name, network.CreateOptions{ Driver: "bridge", IPAM: &network.IPAM{}, - Labels: map[string]string{consts.CleanupLabel: t.Name()}, + Labels: map[string]string{consts.CleanupLabel: cleanupLabel}, }) if err != nil { panic(fmt.Errorf("failed to create docker network: %v", err)) } - return cli, network.ID + t.Cleanup(Cleanup(t, dockerClient)) + + return dockerClient, network.ID } -// DockerCleanup will clean up Docker containers, networks, and the other various config files generated in testing. -func DockerCleanup(t DockerSetupTestingT, cli *client.Client) func() { - return DockerCleanupWithTestName(t, cli, t.Name()) +// Cleanup will clean up Docker containers, networks, and the other various config files generated in testing. +func Cleanup(t SetupTestingT, cli types.TastoraDockerClient) func() { + return CleanupWithLabel(t, cli, cli.CleanupLabel()) } -// DockerCleanupWithTestName will clean up Docker containers, networks, and other config files using a custom test name. -func DockerCleanupWithTestName(t DockerSetupTestingT, cli *client.Client, testName string) func() { +// CleanupWithLabel will clean up Docker containers, networks, and other config files using a custom test name. +func CleanupWithLabel(t SetupTestingT, cli types.TastoraDockerClient, cleanupLabel string) func() { return func() { showContainerLogs := os.Getenv("SHOW_CONTAINER_LOGS") containerLogTail := os.Getenv("CONTAINER_LOG_TAIL") @@ -82,7 +90,7 @@ func DockerCleanupWithTestName(t DockerSetupTestingT, cli *client.Client, testNa cs, err := cli.ContainerList(ctx, container.ListOptions{ All: true, Filters: filters.NewArgs( - filters.Arg("label", consts.CleanupLabel+"="+testName), + filters.Arg("label", consts.CleanupLabel+"="+cleanupLabel), ), }) if err != nil { @@ -130,39 +138,53 @@ func DockerCleanupWithTestName(t DockerSetupTestingT, cli *client.Client, testNa } if !keepContainers { - PruneVolumesWithRetryAndTestName(ctx, t, cli, testName) - PruneNetworksWithRetryAndTestName(ctx, t, cli, testName) + PruneVolumesWithRetryAndCleanupLabel(ctx, t, cli, cleanupLabel) + PruneNetworksWithRetryAndCleanupLabel(ctx, t, cli, cleanupLabel) } else { t.Logf("Keeping containers - Docker cleanup skipped") } } } -func PruneVolumesWithRetry(ctx context.Context, t DockerSetupTestingT, cli *client.Client) { - PruneVolumesWithRetryAndTestName(ctx, t, cli, t.Name()) +func PruneVolumesWithRetry(ctx context.Context, t SetupTestingT, cli types.TastoraDockerClient) { + PruneVolumesWithRetryAndCleanupLabel(ctx, t, cli, cli.CleanupLabel()) } -func PruneVolumesWithRetryAndTestName(ctx context.Context, t DockerSetupTestingT, cli *client.Client, testName string) { +func PruneVolumesWithRetryAndCleanupLabel(ctx context.Context, t SetupTestingT, cli types.TastoraDockerClient, cleanupLabel string) { if KeepVolumesOnFailure && t.Failed() { return } - var msg string + var deletedCount int + var totalSpaceReclaimed uint64 err := retry.Do( func() error { - res, err := cli.VolumesPrune(ctx, filters.NewArgs(filters.Arg("label", consts.CleanupLabel+"="+testName))) + // List volumes with the cleanup label + filterArgs := filters.NewArgs(filters.Arg("label", consts.CleanupLabel+"="+cleanupLabel)) + volumeList, err := cli.VolumeList(ctx, volume.ListOptions{Filters: filterArgs}) if err != nil { - if errdefs.IsConflict(err) { - // Prune is already in progress; try again. - return err - } - - // Give up on any other error. - return retry.Unrecoverable(err) + return retry.Unrecoverable(fmt.Errorf("listing volumes: %w", err)) } - if len(res.VolumesDeleted) > 0 { - msg = fmt.Sprintf("Pruned %d volumes, reclaiming approximately %.1f MB", len(res.VolumesDeleted), float64(res.SpaceReclaimed)/(1024*1024)) + // explicitly remove each volume. + for _, vol := range volumeList.Volumes { + var spaceReclaimed uint64 + // try to get volume size before removal + if vol.UsageData != nil { + spaceReclaimed = uint64(vol.UsageData.Size) + } + + err := cli.VolumeRemove(ctx, vol.Name, true) + if err != nil { + if errdefs.IsConflict(err) { + return err + } + // Log but continue with other volumes + t.Logf("Failed to remove volume %s: %v", vol.Name, err) + } else { + totalSpaceReclaimed += spaceReclaimed + deletedCount++ + } } return nil @@ -171,26 +193,25 @@ func PruneVolumesWithRetryAndTestName(ctx context.Context, t DockerSetupTestingT retry.DelayType(retry.FixedDelay), ) if err != nil { - t.Logf("Failed to prune volumes during docker cleanup: %v", err) + t.Logf("Failed to remove volumes during docker cleanup: %v", err) return } - if msg != "" { - // Odd to Logf %s, but this is a defensive way to keep the DockerSetupTestingT interface - // with only Logf and not need to add Log. + if deletedCount > 0 { + msg := fmt.Sprintf("Removed %d volumes, reclaiming %.1f MB", deletedCount, float64(totalSpaceReclaimed)/(1024*1024)) t.Logf("%s", msg) } } -func PruneNetworksWithRetry(ctx context.Context, t DockerSetupTestingT, cli *client.Client) { - PruneNetworksWithRetryAndTestName(ctx, t, cli, t.Name()) +func PruneNetworksWithRetry(ctx context.Context, t SetupTestingT, cli types.TastoraDockerClient) { + PruneNetworksWithRetryAndCleanupLabel(ctx, t, cli, cli.CleanupLabel()) } -func PruneNetworksWithRetryAndTestName(ctx context.Context, t DockerSetupTestingT, cli *client.Client, testName string) { +func PruneNetworksWithRetryAndCleanupLabel(ctx context.Context, t SetupTestingT, cli types.TastoraDockerClient, cleanupLabel string) { var deleted []string err := retry.Do( func() error { - res, err := cli.NetworksPrune(ctx, filters.NewArgs(filters.Arg("label", consts.CleanupLabel+"="+testName))) + res, err := cli.NetworksPrune(ctx, filters.NewArgs(filters.Arg("label", consts.CleanupLabel+"="+cleanupLabel))) if err != nil { if errdefs.IsConflict(err) { // Prune is already in progress; try again. @@ -252,8 +273,8 @@ func configureLogOptions(testFailed bool, containerLogTail string) container.Log // displayContainerLogs fetches and displays container logs func displayContainerLogs( ctx context.Context, - t DockerSetupTestingT, - cli *client.Client, + t SetupTestingT, + cli types.TastoraDockerClient, containerID string, containerNames []string, logOptions container.LogsOptions, diff --git a/framework/docker/setup_test.go b/framework/docker/setup_test.go index 835a0da..41ea5f6 100644 --- a/framework/docker/setup_test.go +++ b/framework/docker/setup_test.go @@ -10,7 +10,7 @@ import ( "github.com/stretchr/testify/require" ) -// mockTestingT implements DockerSetupTestingT for testing purposes +// mockTestingT implements SetupTestingT for testing purposes type mockTestingT struct { name string failed bool @@ -130,7 +130,7 @@ func TestLogBehaviorLogic(t *testing.T) { showContainerLogs := os.Getenv("SHOW_CONTAINER_LOGS") containerLogTail := os.Getenv("CONTAINER_LOG_TAIL") - // This replicates the logic from DockerCleanup + // This replicates the logic from Cleanup shouldShowLogs := (mockT.Failed() && showContainerLogs == "") || showContainerLogs == "always" require.Equal(t, tc.expectedShowLogs, shouldShowLogs, "Log display decision should match expected") @@ -213,7 +213,7 @@ func TestLogHeaderGeneration(t *testing.T) { for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { - // Simulate the header logic from DockerCleanup + // Simulate the header logic from Cleanup var tailValue string if tc.tailSet { @@ -348,7 +348,7 @@ func TestDockerCleanupBehaviorSimulation(t *testing.T) { showContainerLogs := "" // Default - shows logs on failure containerLogTail := "50" // This should be ignored for failed tests - // Logic from DockerCleanup: should show logs because test failed + // Logic from Cleanup: should show logs because test failed shouldShowLogs := (mockT.Failed() && showContainerLogs == "") || showContainerLogs == "always" require.True(t, shouldShowLogs, "Failed test should trigger log display") diff --git a/framework/docker/toml.go b/framework/docker/toml.go index 7569c94..25c8fe1 100644 --- a/framework/docker/toml.go +++ b/framework/docker/toml.go @@ -7,7 +7,7 @@ import ( "github.com/BurntSushi/toml" "github.com/celestiaorg/tastora/framework/docker/file" tomlutil "github.com/celestiaorg/tastora/framework/testutil/toml" - "github.com/moby/moby/client" + "github.com/celestiaorg/tastora/framework/types" "go.uber.org/zap" ) @@ -15,7 +15,7 @@ import ( func ModifyConfigFile( ctx context.Context, logger *zap.Logger, - dockerClient *client.Client, + dockerClient types.TastoraDockerClient, testName string, volumeName string, filePath string, diff --git a/framework/docker/volume/owner.go b/framework/docker/volume/owner.go index 29b5e39..0b7394c 100644 --- a/framework/docker/volume/owner.go +++ b/framework/docker/volume/owner.go @@ -8,16 +8,16 @@ import ( "github.com/celestiaorg/tastora/framework/docker/consts" dockerinternal "github.com/celestiaorg/tastora/framework/docker/internal" "github.com/celestiaorg/tastora/framework/testutil/random" + "github.com/celestiaorg/tastora/framework/types" "github.com/docker/docker/api/types/container" - "github.com/moby/moby/client" "go.uber.org/zap" ) // OwnerOptions contain the configuration for the SetOwner function. type OwnerOptions struct { Log *zap.Logger - Client *client.Client + Client types.TastoraDockerClient VolumeName string ImageRef string TestName string @@ -40,6 +40,7 @@ func SetOwner(ctx context.Context, opts OwnerOptions) error { } const mountPath = "/mnt/dockervolume" + cc, err := opts.Client.ContainerCreate( ctx, &container.Config{ @@ -53,7 +54,7 @@ func SetOwner(ctx context.Context, opts OwnerOptions) error { }, // Root user so we have permissions to set ownership and mode. User: consts.UserRootString, - Labels: map[string]string{consts.CleanupLabel: opts.TestName}, + Labels: map[string]string{consts.CleanupLabel: opts.Client.CleanupLabel()}, }, &container.HostConfig{ Binds: []string{opts.VolumeName + ":" + mountPath}, diff --git a/framework/testutil/docker/toml.go b/framework/testutil/docker/toml.go index 452d8e3..c0f2f0f 100644 --- a/framework/testutil/docker/toml.go +++ b/framework/testutil/docker/toml.go @@ -4,11 +4,11 @@ import ( "bytes" "context" "fmt" + "github.com/celestiaorg/tastora/framework/types" "github.com/BurntSushi/toml" "github.com/celestiaorg/tastora/framework/docker/file" tomlutil "github.com/celestiaorg/tastora/framework/testutil/toml" - "github.com/moby/moby/client" "go.uber.org/zap" ) @@ -16,7 +16,7 @@ import ( func ModifyConfigFile( ctx context.Context, logger *zap.Logger, - dockerClient *client.Client, + dockerClient types.TastoraDockerClient, testName string, volumeName string, filePath string, @@ -48,4 +48,4 @@ func ModifyConfigFile( } return nil -} \ No newline at end of file +} diff --git a/framework/types/docker_client.go b/framework/types/docker_client.go new file mode 100644 index 0000000..80c9c86 --- /dev/null +++ b/framework/types/docker_client.go @@ -0,0 +1,12 @@ +package types + +import ( + "github.com/moby/moby/client" +) + +// TastoraDockerClient extends the Docker client.CommonAPIClient interface +// with a CleanupLabel method for resource tagging and cleanup. +type TastoraDockerClient interface { + client.CommonAPIClient + CleanupLabel() string +}