Skip to content

Commit

Permalink
Merge pull request #116 from rst0git/json-test
Browse files Browse the repository at this point in the history
test: add e2e test for json format
  • Loading branch information
rst0git authored Jan 17, 2024
2 parents 2f15c0b + 436b6ef commit 311918e
Show file tree
Hide file tree
Showing 6 changed files with 276 additions and 25 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/coverage.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ jobs:
fetch-depth: 0
- name: Install tools
run: |
sudo apt-get install -qqy bats
sudo apt-get install -qqy bats iptables iproute2 jq
# Add PPA for CRIU
sudo add-apt-repository ppa:criu/ppa
sudo apt-get update
Expand Down
6 changes: 4 additions & 2 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,13 @@ jobs:
runs-on: ubuntu-latest
container:
image: registry.fedoraproject.org/fedora:latest
options: --privileged
options: --privileged --cap-add=NET_ADMIN --cap-add=NET_RAW -v /lib/modules:/lib/modules
steps:
- uses: actions/checkout@v3
- name: Install tools
run: sudo dnf -y install ShellCheck bats golang criu asciidoctor
run: |
sudo dnf -y install ShellCheck bats golang criu asciidoctor iptables iproute kmod jq
sudo modprobe -va ip_tables ip6table_filter nf_conntrack nf_conntrack_netlink
- name: Run make shellcheck
run: make shellcheck
- name: Run make all
Expand Down
2 changes: 1 addition & 1 deletion json.go
Original file line number Diff line number Diff line change
Expand Up @@ -269,7 +269,7 @@ func buildJSONPsNode(psTree *crit.PsTree, checkpointOutputDir string) (PsNode, e
envVarMap := make(map[string]string)
for _, envVar := range envVars {
i := strings.IndexByte(envVar, '=')
if i == -1 || i == 0 || i == len(envVar)-1 {
if i == -1 || i == 0 {
return PsNode{}, fmt.Errorf("invalid environment variable %s", envVar)
}
key := envVar[:i]
Expand Down
4 changes: 2 additions & 2 deletions test/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,9 @@ test-junit: test-imgs
bats -F junit checkpointctl.bats > junit.xml

test-imgs: piggie/piggie
$(eval PID := $(shell piggie/piggie))
$(eval PID := $(shell export TEST_ENV=BAR TEST_ENV_EMPTY=; piggie/piggie --tcp-socket))
mkdir -p $@
$(CRIU) dump -v4 -o dump.log -D $@ -t $(PID)
$(CRIU) dump --tcp-established -v4 -o dump.log -D $@ -t $(PID) || cat $@/dump.log

piggie/piggie: piggie/piggie.c
$(CC) $^ -o $@
Expand Down
61 changes: 56 additions & 5 deletions test/checkpointctl.bats
Original file line number Diff line number Diff line change
Expand Up @@ -301,7 +301,7 @@ function teardown() {
[[ ${lines[10]} == *"piggie/piggie"* ]]
}

@test "Run checkpointctl inspect with tar file and --ps-tree-cmd and missing pages-*.img {
@test "Run checkpointctl inspect with tar file and --ps-tree-cmd and missing pages-*.img" {
cp data/config.dump \
data/spec.dump "$TEST_TMP_DIR1"
mkdir "$TEST_TMP_DIR1"/checkpoint
Expand Down Expand Up @@ -332,7 +332,7 @@ function teardown() {
[[ ${lines[12]} == *"="* ]]
}

@test "Run checkpointctl inspect with tar file and --ps-tree-env and missing pages-*.img {
@test "Run checkpointctl inspect with tar file and --ps-tree-env and missing pages-*.img" {
cp data/config.dump \
data/spec.dump "$TEST_TMP_DIR1"
mkdir "$TEST_TMP_DIR1"/checkpoint
Expand All @@ -359,9 +359,9 @@ function teardown() {
( cd "$TEST_TMP_DIR1" && tar cf "$TEST_TMP_DIR2"/test.tar . )
checkpointctl inspect "$TEST_TMP_DIR2"/test.tar --files
[ "$status" -eq 0 ]
[[ ${lines[11]} == *"[REG 0]"* ]]
[[ ${lines[12]} == *"[cwd]"* ]]
[[ ${lines[13]} == *"[root]"* ]]
[[ ${lines[24]} == *"[REG 0]"* ]]
[[ ${lines[25]} == *"[cwd]"* ]]
[[ ${lines[26]} == *"[root]"* ]]
}

@test "Run checkpointctl inspect with tar file and --files and missing files.img" {
Expand Down Expand Up @@ -589,3 +589,54 @@ function teardown() {
[ "$status" -eq 1 ]
[[ ${lines[0]} == *"no process with PID 9999"* ]]
}

@test "Run checkpointctl inspect with json format" {
cp data/config.dump data/spec.dump test-imgs/stats-dump "$TEST_TMP_DIR1"
mkdir "$TEST_TMP_DIR1"/checkpoint
cp test-imgs/*.img "$TEST_TMP_DIR1"/checkpoint
( cd "$TEST_TMP_DIR1" && tar cf "$TEST_TMP_DIR2"/test.tar . )

# test function definitions for JSON output using jq
test_engine() { jq -e '.[0].engine == "Podman"'; }
export -f test_engine

test_pstree_cmd() { jq -e '.[0].process_tree.command == "piggie"'; }
export -f test_pstree_cmd

test_pstree_child1() { jq -e '.[0].process_tree.children[0].command == "tcp-server"'; }
export -f test_pstree_child1

test_pstree_child2() { jq -e '.[0].process_tree.children[1].command == "tcp-client"'; }
export -f test_pstree_child2

test_pstree_env() { jq -e '.[0].process_tree.environment_variables.TEST_ENV == "BAR"'; }
export -f test_pstree_env

test_pstree_env_empty() { jq -e '.[0].process_tree.environment_variables.TEST_ENV_EMPTY == ""'; }
export -f test_pstree_env_empty

test_socket_protocol() { jq -e '.[0].sockets[0].open_sockets[0].protocol == "TCP"'; }
export -f test_socket_protocol

test_socket_src_port() { jq -e '.[0].sockets[0].open_sockets[0].data.src_port == 5000'; }
export -f test_socket_src_port

# Run tests
run bash -c "$CHECKPOINTCTL inspect $TEST_TMP_DIR2/test.tar --format=json | test_engine"
[ "$status" -eq 0 ]

run bash -c "$CHECKPOINTCTL inspect $TEST_TMP_DIR2/test.tar --format=json --ps-tree | test_pstree_cmd"
[ "$status" -eq 0 ]

run bash -c "$CHECKPOINTCTL inspect $TEST_TMP_DIR2/test.tar --format=json --all | test_pstree_env"
[ "$status" -eq 0 ]

run bash -c "$CHECKPOINTCTL inspect $TEST_TMP_DIR2/test.tar --format=json --all | test_pstree_env_empty"
[ "$status" -eq 0 ]

run bash -c "$CHECKPOINTCTL inspect $TEST_TMP_DIR2/test.tar --format=json --sockets | test_socket_protocol"
[ "$status" -eq 0 ]

run bash -c "$CHECKPOINTCTL inspect $TEST_TMP_DIR2/test.tar --format=json --sockets | test_socket_src_port"
[ "$status" -eq 0 ]
}
226 changes: 212 additions & 14 deletions test/piggie/piggie.c
Original file line number Diff line number Diff line change
@@ -1,20 +1,150 @@
#define _GNU_SOURCE
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <stdlib.h>
#include <signal.h>
#include <sched.h>
#include <fcntl.h>
#include <stdbool.h>
#include <string.h>
#include <arpa/inet.h>
#include <sys/prctl.h>

#define STKS (4*4096)

#ifndef CLONE_NEWPID
#define CLONE_NEWPID 0x20000000
#endif

static int do_test(void *logf)
#define SERVER_IP "127.0.0.1"
#define PORT 5000
#define MAX_BUFFER_SIZE 1024

typedef struct {
char *log_file;
bool use_tcp_socket;
} opts_t;

void run_tcp_server(void)
{
int fd, i = 0;
int server_socket, client_socket, ret;
struct sockaddr_in server_address, client_address;
char buffer[MAX_BUFFER_SIZE];
const int enable = 1;

ret = fork();
if (ret < 0) {
perror("tcp-server: fork failed");
return;
}

if (ret > 0) {
return;
}

prctl(PR_SET_NAME, "tcp-server");

server_socket = socket(AF_INET, SOCK_STREAM, 0);
if (server_socket == -1) {
perror("tcp-server: Socket creation failed");
exit(EXIT_FAILURE);
}

setsockopt(server_socket, SOL_SOCKET, SO_REUSEADDR, &(int){1}, sizeof(int));
setsockopt(server_socket, SOL_SOCKET, SO_REUSEPORT, &(int){1}, sizeof(int));

server_address.sin_family = AF_INET;
server_address.sin_addr.s_addr = INADDR_ANY;
server_address.sin_port = htons(PORT);

if (bind(server_socket, (struct sockaddr*)&server_address, sizeof(server_address)) == -1) {
perror("tcp-server: Socket bind failed");
exit(EXIT_FAILURE);
}

if (listen(server_socket, 5) == -1) {
perror("tcp-server: Listen failed");
exit(EXIT_FAILURE);
}

socklen_t client_address_len = sizeof(client_address);
client_socket = accept(server_socket, (struct sockaddr*)&client_address, &client_address_len);
if (client_socket == -1) {
perror("tcp-server: Accept failed");
exit(EXIT_FAILURE);
}

while (1) {
memset(buffer, 0, sizeof(buffer));
recv(client_socket, buffer, sizeof(buffer), 0);
}

close(server_socket);
close(client_socket);
exit(0);
}

void run_tcp_client(void)
{
int client_socket, max_connection_tries = 5;
struct sockaddr_in server_address;
char buffer[MAX_BUFFER_SIZE];
bool connected = false, ret;

ret = fork();
if (ret < 0) {
perror("tcp-client: fork failed");
return;
}

if (ret > 0) {
return;
}

prctl(PR_SET_NAME, "tcp-client");

client_socket = socket(AF_INET, SOCK_STREAM, 0);
if (client_socket == -1) {
perror("Socket creation failed");
exit(EXIT_FAILURE);
}

server_address.sin_family = AF_INET;
server_address.sin_port = htons(PORT);

if (inet_pton(AF_INET, SERVER_IP, &server_address.sin_addr) <= 0) {
perror("tcp-client: Invalid address");
exit(EXIT_FAILURE);
}

while (!connected) {
if (connect(client_socket, (struct sockaddr*)&server_address, sizeof(server_address)) == -1) {
if (max_connection_tries > 0) {
max_connection_tries--;
continue;
}

perror("tcp-client: Connection failed");
exit(EXIT_FAILURE);
}
connected = true;
}

while (1) {
send(client_socket, "ping", 5, 0);
/* Send messages every second */
sleep(1);
}

close(client_socket);
exit(0);
}

static int do_test(void *opts_ptr)
{
opts_t *opts = opts_ptr;
int fd, ret, i = 0;

setsid();

Expand All @@ -28,11 +158,22 @@ static int do_test(void *logf)
close(fd);
}

fd = open(logf, O_WRONLY | O_TRUNC | O_CREAT, 0600);
dup2(fd, 1);
dup2(fd, 2);
if (fd != 1 && fd != 2)
close(fd);
if (opts->log_file) {
fd = open(opts->log_file, O_WRONLY | O_TRUNC | O_CREAT, 0600);
dup2(fd, 1);
dup2(fd, 2);
if (fd != 1 && fd != 2)
close(fd);
}

if (opts->use_tcp_socket) {
if (unshare(CLONE_NEWNET))
return 1;
if (system("ip link set up dev lo"))
return 1;
run_tcp_server();
run_tcp_client();
}

while (1) {
sleep(1);
Expand All @@ -43,19 +184,76 @@ static int do_test(void *logf)
return 0;
}

int main(int argc, char **argv)
static int parse_options(int argc, char **argv, bool *usage_error, opts_t *opts)
{
int pid;
int i = 1, exit_code = -1;

while (i < argc) {
if ((!strcmp(argv[i], "--help")) || (!strcmp(argv[i], "-h"))) {
*usage_error = false;
return 1;
}

if ((!strcmp(argv[i], "--log-file")) || (!strcmp(argv[i], "-o"))) {
opts->log_file = argv[i + 1];
i += 2;
continue;
}

if ((!strcmp(argv[i], "--tcp-socket")) || (!strcmp(argv[i], "-t"))) {
opts->use_tcp_socket = true;
i++;
continue;
}

printf("Unknown option: %s\n", argv[i]);
*usage_error = true;
goto out;
}

exit_code = 0;
out:
return exit_code;
}

int main(int argc, char **argv) {
void *stk;
int i, pid, log_fd, option;
bool usage_error = false;
opts_t opts = {NULL};
int ret;

stk = mmap(NULL, STKS, PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANON | MAP_GROWSDOWN, 0, 0);
pid = clone(do_test, stk + STKS, SIGCHLD | CLONE_NEWPID, argv[1]);
ret = parse_options(argc, argv, &usage_error, &opts);
if (ret) {
fprintf(stderr, "Usage: %s -o/--log-file <log_file> [-t/--tcp-socket]\n", argv[0]);
return (usage_error != false);
}

stk = mmap(NULL, STKS, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON | MAP_GROWSDOWN, 0, 0);
pid = clone(do_test, stk + STKS, SIGCHLD | CLONE_NEWPID, (void*)&opts);
if (pid < 0) {
fprintf(stderr, "clone() failed: %m\n");
return 1;
}

/* Wait for TCP sockets to be created if requested */
if (opts.use_tcp_socket) {
sleep(3);
}
printf("%d\n", pid);

if (opts.log_file) {
log_fd = open(opts.log_file, O_WRONLY | O_CREAT | O_TRUNC, 0666);
if (log_fd == -1) {
perror("Error opening log file");
return 1;
}

dup2(log_fd, STDOUT_FILENO);
dup2(log_fd, STDERR_FILENO);

close(log_fd);
}

return 0;
}

0 comments on commit 311918e

Please sign in to comment.