testcontainers-go icon indicating copy to clipboard operation
testcontainers-go copied to clipboard

container.Exec exit code is 1, but the cmd execute well using docker exec directly

Open lsongseven opened this issue 2 years ago • 3 comments

Start a mysql container, and then to execute some inits, the container.Exec exit code is 1, and the log shows

mysql: [Warning] Using a password on the command line interface can be insecure.
ERROR 2003 (HY000): Can't connect to MySQL server on 'localhost:3306' (99)

The cmd like ["/bin/bash", "/prepare_data.sh"] , and what in prepare_data.sh is

#!/bin/bash
mysql -hlocalhost -P3306 --protocol=tcp -uroot -pTest1234 < ddl.sql 2>> m.log

here the ddl.sql is very simple

create database xxx;

However, when I type the command in terminal directly like

# assume the containerId is b32cdb7845b3
docker exec b32cdb7845b3 /bin/bash /prepare_data.sh 2>>m.log

it works fine.

I don't know what's wrong, so can anyone help me?

  • code to reproduce
func SetupMysql(ctx context.Context) (*mysqlContainer, error) {
	req := testcontainers.ContainerRequest{
		Image:        "mysql:8.0",
		ExposedPorts: []string{"3306/tcp"},
		WaitingFor:   wait.ForLog("MySQL init process done. Ready for start up."),
		Env: map[string]string{
			"MYSQL_ROOT_PASSWORD": "Test1234",
		},
	}

	container, err := testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{
		ContainerRequest: req,
		Started:          true,
	})

	if err != nil {
		return nil, err
	}

	err = containerInit(ctx, container)

	if err != nil {
		return nil, err
	}

	mappedPort, err := container.MappedPort(ctx, "3306")
	if err != nil {
		return nil, err
	}

	hostIP, err := container.Host(ctx)
	if err != nil {
		return nil, err
	}

	serverIpPort := fmt.Sprintf("%s:%s", hostIP, mappedPort.Port())

	dsn := fmt.Sprintf("root:Test1234@tcp(%s)/life_goods_frontend?charset=utf8mb4&parseTime=True&loc=Local", serverIpPort)

	return &mysqlContainer{
		Container: container,
		DSN:       dsn,
	}, nil
}


func containerInit(ctx context.Context, container testcontainers.Container) error {
	pwd, _ := os.Getwd()

	hostFilePath := pwd + "/../data/prepare_data.sh"
	containerFilePath := "/prepare_data.sh"

	err := container.CopyFileToContainer(ctx, hostFilePath, containerFilePath, 0777)
	if err != nil {
		return err
	}

	hostFilePath = pwd + "/../data/ddl.sql"
	containerFilePath = "/ddl.sql"

	err = container.CopyFileToContainer(ctx, hostFilePath, containerFilePath, 0777)
	if err != nil {
		return err
	}

	cmds := []string{"/bin/bash", "/prepare_data.sh"}
	fmt.Println(cmds)
	exitCode, err := container.Exec(ctx, cmds)
	if err != nil {
		return err
	}
	fmt.Printf("exitCode=%+v\n", exitCode)
	return nil
}

lsongseven avatar Apr 13 '22 07:04 lsongseven

Hi @lsongseven thanks for opening this issue, although I think this kind of questions fits more in the Slack channel :)

Have you tried to mount the preparation script into the docker container before it's run? https://dev.mysql.com/doc/mysql-installation-excerpt/8.0/en/docker-mysql-more-topics.html#docker-additional-init. The official MySQL image should run your .SH/.SQL files for you first.

mdelapenya avatar Apr 13 '22 07:04 mdelapenya

Hi @lsongseven thanks for opening this issue, although I think this kind of questions fits more in the Slack channel :)

Have you tried to mount the preparation script into the docker container before it's run? https://dev.mysql.com/doc/mysql-installation-excerpt/8.0/en/docker-mysql-more-topics.html#docker-additional-init. The official MySQL image should run your .SH/.SQL files for you first.

Sorry about not-int-slack-channel @mdelapenya .

Mount preparation scripts works, but can you explain the difference between container.Exec and docker exec ? 🤔

lsongseven avatar Apr 13 '22 08:04 lsongseven

Yep same as me using docker exec and exec directly when in '-it' mode work. But testcontainers dont.

The thing that i want to run is pubsub emulator.

So the last thing that i do to satisfy my need is to used custom docker image.

mozarik avatar Jul 08 '22 18:07 mozarik

@mozarik

The thing that i want to run is pubsub emulator.

Have you checked out the pubsub example code? https://golang.testcontainers.org/examples/pubsub/

mdelapenya avatar Mar 01 '23 06:03 mdelapenya

@lsongseven sorry for the radio silence, at that time I was changing jobs and did not notice this issue to be stacked into our issues 🤦

I'm not able to reproduce your scenario, using latest version of the Exec method, which is able to receive a Multiplexed options to remove the garbage suffix prepended by Docker (see https://github.com/testcontainers/testcontainers-go/issues/624)

package mysql

import (
	"context"
	"fmt"
	"io"
	"os"
	"path/filepath"

	"github.com/testcontainers/testcontainers-go"
	tcexec "github.com/testcontainers/testcontainers-go/exec"
	"github.com/testcontainers/testcontainers-go/wait"
)

func SetupMysql(ctx context.Context) (*mysqlContainer, error) {
	req := testcontainers.ContainerRequest{
		Image:        "mysql:8.0",
		ExposedPorts: []string{"3306/tcp"},
		WaitingFor:   wait.ForLog("MySQL init process done. Ready for start up."),
		Env: map[string]string{
			"MYSQL_ROOT_PASSWORD": "Test1234",
		},
	}

	container, err := testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{
		ContainerRequest: req,
		Started:          true,
	})

	if err != nil {
		return nil, err
	}

	err = containerInit(ctx, container)

	if err != nil {
		return nil, err
	}

	mappedPort, err := container.MappedPort(ctx, "3306")
	if err != nil {
		return nil, err
	}

	hostIP, err := container.Host(ctx)
	if err != nil {
		return nil, err
	}

	serverIpPort := fmt.Sprintf("%s:%s", hostIP, mappedPort.Port())

	dsn := fmt.Sprintf("root:Test1234@tcp(%s)/life_goods_frontend?charset=utf8mb4&parseTime=True&loc=Local", serverIpPort)

	return &mysqlContainer{
		Container: container,
		DSN:       dsn,
	}, nil
}

func containerInit(ctx context.Context, container testcontainers.Container) error {
	pwd, _ := os.Getwd()

	hostFilePath := filepath.Join(pwd, "testresources", "prepare_data.sh")
	containerFilePath := "/prepare_data.sh"

	err := container.CopyFileToContainer(ctx, hostFilePath, containerFilePath, 0777)
	if err != nil {
		return err
	}

	hostFilePath = filepath.Join(pwd, "testresources", "ddl.sql")
	containerFilePath = "/ddl.sql"

	err = container.CopyFileToContainer(ctx, hostFilePath, containerFilePath, 0777)
	if err != nil {
		return err
	}

	cmds := []string{"/bin/bash", "/prepare_data.sh"}
	fmt.Println(cmds)
	exitCode, reader, err := container.Exec(ctx, cmds, tcexec.Multiplexed())
	if err != nil {
		return err
	}
	fmt.Printf("exitCode=%+v\n", exitCode)

	b, err := io.ReadAll(reader)
	str := string(b)
	fmt.Printf("str=%+v\n", str)
	return nil
}

mdelapenya avatar Mar 01 '23 07:03 mdelapenya

@lsongseven @mozarik did you have time to check the above code snippet?

mdelapenya avatar Mar 23 '23 15:03 mdelapenya

Closing due to inactivity. Please reopen if needed, thanks!

mdelapenya avatar Feb 08 '24 11:02 mdelapenya