diff --git a/.github/workflows/func-tests.yml b/.github/workflows/func-tests.yml index def4deb..3084c3a 100644 --- a/.github/workflows/func-tests.yml +++ b/.github/workflows/func-tests.yml @@ -14,7 +14,7 @@ env: jobs: prepare: name: Prepare - runs-on: ubuntu-24.04 + runs-on: ubuntu-latest steps: - name: Check out code into the Go module directory uses: actions/checkout@v7 @@ -61,7 +61,7 @@ jobs: test: name: Test - runs-on: ubuntu-24.04 + runs-on: ubuntu-latest needs: [ prepare ] strategy: matrix: diff --git a/.github/workflows/golangci-lint.yml b/.github/workflows/golangci-lint.yml index a45c9ea..799bdbc 100644 --- a/.github/workflows/golangci-lint.yml +++ b/.github/workflows/golangci-lint.yml @@ -15,7 +15,7 @@ permissions: jobs: golangci: name: lint - runs-on: ubuntu-24.04 + runs-on: ubuntu-latest steps: - uses: actions/setup-go@v7 with: diff --git a/.github/workflows/jepsen-cluster-tests.yml b/.github/workflows/jepsen-cluster-tests.yml index f97eff2..ff08e2c 100644 --- a/.github/workflows/jepsen-cluster-tests.yml +++ b/.github/workflows/jepsen-cluster-tests.yml @@ -10,7 +10,7 @@ env: jobs: test: name: Test - runs-on: ubuntu-24.04 + runs-on: ubuntu-latest steps: - name: Check out code into the Go module directory uses: actions/checkout@v7 diff --git a/.github/workflows/jepsen-sentinel-tests.yml b/.github/workflows/jepsen-sentinel-tests.yml index 79df0ea..a35ff49 100644 --- a/.github/workflows/jepsen-sentinel-tests.yml +++ b/.github/workflows/jepsen-sentinel-tests.yml @@ -10,7 +10,7 @@ env: jobs: test: name: Test - runs-on: ubuntu-24.04 + runs-on: ubuntu-latest steps: - name: Check out code into the Go module directory uses: actions/checkout@v7 diff --git a/.github/workflows/unit-tests.yml b/.github/workflows/unit-tests.yml index dd1f48e..bbe5214 100644 --- a/.github/workflows/unit-tests.yml +++ b/.github/workflows/unit-tests.yml @@ -13,7 +13,7 @@ env: jobs: unittest: name: all_unittests - runs-on: ubuntu-24.04 + runs-on: ubuntu-latest steps: - name: Set up Go 1.x uses: actions/setup-go@v7 diff --git a/Makefile b/Makefile index 493cba9..ffe0fca 100644 --- a/Makefile +++ b/Makefile @@ -17,7 +17,7 @@ unittests: go test ./cmd/... ./tests/testutil/matchers/ valkey/src/valkey-server: - docker run --rm -v ${CURDIR}:/app -w /app ubuntu:noble /app/valkey_patches/build.sh + docker run --rm -v ${CURDIR}:/app -w /app ubuntu:resolute /app/valkey_patches/build.sh test: base_image valkey/src/valkey-server cmd/rdsync/rdsync recreate_logs rm -rf ./tests/images/valkey/rdsync && cp cmd/rdsync/rdsync ./tests/images/valkey/rdsync diff --git a/tests/images/base/Dockerfile b/tests/images/base/Dockerfile index dd95647..f615baf 100644 --- a/tests/images/base/Dockerfile +++ b/tests/images/base/Dockerfile @@ -1,4 +1,4 @@ -FROM ubuntu:noble +FROM ubuntu:resolute ENV container=docker ENV DEBIAN_FRONTEND=noninteractive COPY . /var/lib/dist/base diff --git a/tests/images/base/generate_certs.sh b/tests/images/base/generate_certs.sh index 2bd7e0a..23af606 100644 --- a/tests/images/base/generate_certs.sh +++ b/tests/images/base/generate_certs.sh @@ -87,7 +87,7 @@ Jl8tzMujbHNHhw+OQAQOPHi6EUPs/H37euj3G7oBaVUwXJq3Tbwg95W5Jih+CgTB Sbe6eYpR/j/SYGwbS6/DbHi3IjvblN+2pSPI05JvXMhLC/lAeqcdVJAgTvw= -----END RSA PRIVATE KEY-----" > /etc/zk-ssl/ca.key -openssl genrsa -out /etc/zk-ssl/server.key -passout pass:testpassword123 4096 +openssl genrsa -out /etc/zk-ssl/server.key -passout pass:testpassword123 2048 openssl req -new -key /etc/zk-ssl/server.key -out /etc/zk-ssl/server.csr -passin pass:testpassword123 -subj "/C=RU/ST=Test/L=Test/O=Test/OU=Test/CN=${FQDN}" echo "[SAN] subjectAltName = @alt_names diff --git a/tests/images/base/setup.sh b/tests/images/base/setup.sh index 50a1463..716bdae 100755 --- a/tests/images/base/setup.sh +++ b/tests/images/base/setup.sh @@ -35,7 +35,7 @@ ln -sf /usr/sbin/iptables-legacy /usr/sbin/iptables mkdir -p /run/sshd cp /var/lib/dist/base/sshd_config /etc/ssh/sshd_config -mkdir /root/.ssh +mkdir -p /root/.ssh chmod 0700 /root/.ssh yes | ssh-keygen -t rsa -N '' -f /root/.ssh/id_rsa cp /root/.ssh/id_rsa.pub /root/.ssh/authorized_keys diff --git a/tests/images/valkey/setup_cluster.sh b/tests/images/valkey/setup_cluster.sh index e615e39..ab28afc 100755 --- a/tests/images/valkey/setup_cluster.sh +++ b/tests/images/valkey/setup_cluster.sh @@ -18,7 +18,7 @@ supervisorctl start valkey if [ "${MASTER}" != "" ] then valkey-cli -e -a functestpassword -p 6379 config set offline no - master_addr=$(host ${MASTER} | awk '{print $NF}') + master_addr=$(host ${MASTER} | awk '/has address/ {print $NF; exit}') valkey-cli -e -a functestpassword -p 6379 cluster meet ${master_addr} 6379 master_id=$(valkey-cli -e -a functestpassword -h ${master_addr} -p 6379 cluster myid) tries=0 diff --git a/tests/images/valkey/setup_sentinel.sh b/tests/images/valkey/setup_sentinel.sh index 221bfd3..2c5202c 100755 --- a/tests/images/valkey/setup_sentinel.sh +++ b/tests/images/valkey/setup_sentinel.sh @@ -7,7 +7,7 @@ MASTER=${1} if [ "${MASTER}" != "" ] then valkey-cli -e -a functestpassword -p 6379 config set offline no - master_addr=$(host ${MASTER} | awk '{print $NF}') + master_addr=$(host ${MASTER} | awk '/has address/ {print $NF; exit}') valkey-cli -e -a functestpassword -p 6379 replicaof ${master_addr} 6379 valkey-cli -e -a functestpassword -p 6379 config rewrite tries=0 diff --git a/tests/rdsync_test.go b/tests/rdsync_test.go index 7b89c7b..a5be5b2 100644 --- a/tests/rdsync_test.go +++ b/tests/rdsync_test.go @@ -87,7 +87,18 @@ func newTestContext() (*testContext, error) { } func (tctx *testContext) saveLogs(scenario string) error { + var retVal error for _, service := range tctx.composer.Services() { + logdir := filepath.Join("logs", scenario, service) + if err := os.MkdirAll(logdir, 0755); err != nil { + return err + } + if containerLogs, err := tctx.composer.GetLogs(service); err == nil { + _ = os.WriteFile(filepath.Join(logdir, "docker.log"), []byte(containerLogs), 0644) + } else { + log.Printf("failed to save docker logs of %s: %v", service, err) + retVal = err + } var logsToSave map[string]string switch { case strings.HasPrefix(service, valkeyName): @@ -97,29 +108,28 @@ func (tctx *testContext) saveLogs(scenario string) error { default: continue } - logdir := filepath.Join("logs", scenario, service) - err := os.MkdirAll(logdir, 0755) - if err != nil { - return err - } for remotePath, localPath := range logsToSave { remoteFile, err := tctx.composer.GetFile(service, remotePath) if err != nil { - return err + log.Printf("failed to fetch %s from %s: %v", remotePath, service, err) + continue } - defer func() { _ = remoteFile.Close() }() localFile, err := os.OpenFile(filepath.Join(logdir, localPath), os.O_RDWR|os.O_CREATE, 0644) if err != nil { - return err + _ = remoteFile.Close() + log.Printf("failed to open local file for %s: %v", localPath, err) + continue } - defer func() { _ = localFile.Close() }() _, err = io.Copy(localFile, remoteFile) + _ = remoteFile.Close() + _ = localFile.Close() if err != nil { - return err + log.Printf("failed to copy %s from %s: %v", remotePath, service, err) + retVal = err } } } - return nil + return retVal } func (tctx *testContext) templateStep(step *godog.Step) error { @@ -449,18 +459,27 @@ func (tctx *testContext) stepClusteredShardIsUpAndRunning() error { if err != nil { return err } - _, _, err = tctx.composer.RunCommand("valkey1", "setup_cluster.sh", 1*time.Minute) + retcode, out, err := tctx.composer.RunCommand("valkey1", "setup_cluster.sh", 5*time.Minute) if err != nil { return err } - _, _, err = tctx.composer.RunCommand("valkey2", "setup_cluster.sh valkey1", 1*time.Minute) + if retcode != 0 { + return fmt.Errorf("setup_cluster.sh failed on valkey1: %s", out) + } + retcode, out, err = tctx.composer.RunCommand("valkey2", "setup_cluster.sh valkey1", 5*time.Minute) if err != nil { return err } - _, _, err = tctx.composer.RunCommand("valkey3", "setup_cluster.sh valkey1", 1*time.Minute) + if retcode != 0 { + return fmt.Errorf("setup_cluster.sh failed on valkey2: %s", out) + } + retcode, out, err = tctx.composer.RunCommand("valkey3", "setup_cluster.sh valkey1", 5*time.Minute) if err != nil { return err } + if retcode != 0 { + return fmt.Errorf("setup_cluster.sh failed on valkey3: %s", out) + } // check valkey nodes for _, service := range tctx.composer.Services() { @@ -484,18 +503,27 @@ func (tctx *testContext) stepSentinelShardIsUpAndRunning() error { if err != nil { return err } - _, _, err = tctx.composer.RunCommand("valkey1", "setup_sentinel.sh", 1*time.Minute) + retcode, out, err := tctx.composer.RunCommand("valkey1", "setup_sentinel.sh", 5*time.Minute) if err != nil { return err } - _, _, err = tctx.composer.RunCommand("valkey2", "setup_sentinel.sh valkey1", 1*time.Minute) + if retcode != 0 { + return fmt.Errorf("setup_sentinel.sh failed on valkey1: %s", out) + } + retcode, out, err = tctx.composer.RunCommand("valkey2", "setup_sentinel.sh valkey1", 5*time.Minute) if err != nil { return err } - _, _, err = tctx.composer.RunCommand("valkey3", "setup_sentinel.sh valkey1", 1*time.Minute) + if retcode != 0 { + return fmt.Errorf("setup_sentinel.sh failed on valkey2: %s", out) + } + retcode, out, err = tctx.composer.RunCommand("valkey3", "setup_sentinel.sh valkey1", 5*time.Minute) if err != nil { return err } + if retcode != 0 { + return fmt.Errorf("setup_sentinel.sh failed on valkey3: %s", out) + } // check valkey nodes for _, service := range tctx.composer.Services() { if strings.HasPrefix(service, valkeyName) { diff --git a/tests/testutil/docker_composer.go b/tests/testutil/docker_composer.go index 8734c5c..2a765b6 100644 --- a/tests/testutil/docker_composer.go +++ b/tests/testutil/docker_composer.go @@ -63,6 +63,8 @@ type Composer interface { RunAsyncCommand(service, cmd string) error // Returns content of the file from container by path GetFile(service, path string) (io.ReadCloser, error) + // Returns container stdout/stderr (docker logs) for given service + GetLogs(service string) (string, error) } // DockerComposer is a Composer implementation based on docker and docker-compose @@ -467,6 +469,44 @@ func (dc *DockerComposer) UnBlockHostConnections(service, host string) error { return nil } +// GetLogs returns container stdout/stderr for given service +func (dc *DockerComposer) GetLogs(service string) (string, error) { + cont, ok := dc.containers[service] + if !ok { + return "", fmt.Errorf("no such service: %s", service) + } + logsResult, err := dc.api.ContainerLogs(context.Background(), cont.ID, client.ContainerLogsOptions{ + ShowStdout: true, + ShowStderr: true, + }) + if err != nil { + return "", err + } + defer logsResult.Close() + logs, err := io.ReadAll(logsResult) + if err != nil { + return "", err + } + return demuxDockerLogs(logs), nil +} + +func demuxDockerLogs(logs []byte) string { + var out strings.Builder + for len(logs) > 0 { + if len(logs) < 8 { + out.Write(logs) + break + } + payloadLen := int(logs[4])<<24 | int(logs[5])<<16 | int(logs[6])<<8 | int(logs[7]) + if 8+payloadLen > len(logs) { + payloadLen = len(logs) - 8 + } + out.Write(logs[8 : 8+payloadLen]) + logs = logs[8+payloadLen:] + } + return out.String() +} + func newUntarReaderCloser(reader io.ReadCloser) (io.ReadCloser, error) { tarReader := tar.NewReader(reader) _, err := tarReader.Next() diff --git a/valkey_patches/build.sh b/valkey_patches/build.sh index 4e03d50..cea8251 100755 --- a/valkey_patches/build.sh +++ b/valkey_patches/build.sh @@ -3,7 +3,7 @@ set -xe apt update -DEBIAN_FRONTEND=noninteractive TZ=Etc/UTC apt -y install build-essential git +DEBIAN_FRONTEND=noninteractive TZ=Etc/UTC apt -y install build-essential git libssl-dev cd /app git clone https://github.com/valkey-io/valkey.git cd valkey @@ -14,4 +14,4 @@ do git apply "${i}" done -make -j +make BUILD_TLS=yes -j