package main import ( "reflect" "strings" "testing" ) func TestEnvNameMatchesNodeAgent(t *testing.T) { for key, want := range map[string]string{ "RCONPort": "RCONPORT", "ServerAdminPassword": "SERVER_ADMIN_PASSWORD", "AllowFlyerCarryPvE": "ALLOW_FLYER_CARRY_PV_E", "bDisableStructurePlacementCollision": "B_DISABLE_STRUCTURE_PLACEMENT_COLLISION", "RCON_PASSWORD": "RCON_PASSWORD", "MaxPlayers": "MAX_PLAYERS", } { if got := envName(key); got != want { t.Errorf("envName(%q) = %q, want %q", key, got, want) } } } // Probe output as ARK's image produces it (trimmed) const arkProbe = probeMarker + ` passwd root:x:0:0:root:/root:/bin/bash gameserver:x:25000:25000::/home/gameserver:/bin/bash nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin ` + probeMarker + ` homes /home/gameserver|25000|1000|700 /root|0|0|700 ` + probeMarker + ` tools bash|/usr/bin/bash ` + probeMarker + ` script /usr/bin/start_server #!/bin/bash cd /home/gameserver/steamcmd echo "$ASA_START_PARAMS ${ENABLE_DEBUG} $HOME" ` func arkFacts() *imageFacts { f := &imageFacts{Env: map[string]string{}, Tools: map[string]string{}, Scripts: map[string]string{}} parseProbe(f, arkProbe) suggestContainerUser(f) return f } func TestProbeParsing(t *testing.T) { f := arkFacts() if len(f.Users) != 1 || f.Users[0].Name != "gameserver" || f.Users[0].UID != 25000 { t.Fatalf("users = %+v", f.Users) } if len(f.Homes) != 2 || f.Homes[0].Mode != "700" { t.Fatalf("homes = %+v", f.Homes) } if !strings.Contains(f.Scripts["/usr/bin/start_server"], "steamcmd") { t.Fatalf("script not captured: %q", f.Scripts) } if want := []string{"ASA_START_PARAMS", "ENABLE_DEBUG"}; !reflect.DeepEqual(f.EnvRead, want) { t.Errorf("envRead = %v, want %v (HOME is common and left out)", f.EnvRead, want) } } func TestSuggestContainerUser(t *testing.T) { // Locked home owned by another uid: use that account, with its own group if f := arkFacts(); f.SuggestedContainerUser != "25000:25000" { t.Errorf("ARK: suggested %q, want 25000:25000", f.SuggestedContainerUser) } // uid 1000 images need nothing f := &imageFacts{Homes: []homeDir{{Path: "/home/steam", UID: 1000, GID: 1000, Mode: "700"}}} suggestContainerUser(f) if f.SuggestedContainerUser != "" { t.Errorf("uid 1000 image: suggested %q", f.SuggestedContainerUser) } // A home others may enter doesn't force a user f = &imageFacts{Homes: []homeDir{{Path: "/home/app", UID: 2000, GID: 2000, Mode: "755"}}} suggestContainerUser(f) if f.SuggestedContainerUser != "" { t.Errorf("open home: suggested %q", f.SuggestedContainerUser) } // A declared USER is resolved through /etc/passwd f = &imageFacts{User: "steam", Users: []imageUser{{Name: "steam", UID: 4242, GID: 4243}}} suggestContainerUser(f) if f.SuggestedContainerUser != "4242:4243" { t.Errorf("declared USER: suggested %q", f.SuggestedContainerUser) } } func TestCheckTemplate(t *testing.T) { f := arkFacts() game := map[string]any{ "defaultConfig": map[string]any{"RCONPort": 27020, "MaxPlayers": 70}, // Mirrors ARK's old command: ASA_START_PARAMS is assigned, SERVER_MAP has a default, RCON_PORT is the // misnamed one, MAX_PLAYERS is set by a setting, MISSING has no default at all "defaultStartupCommand": `export ASA_START_PARAMS="${ASA_START_PARAMS:-${SERVER_MAP:-TheIsland}?RCONPort=${RCON_PORT:-27020} -Max=${MAX_PLAYERS:-70} ${MISSING}}"`, } problems, _ := checkTemplate(game, f) joined := strings.Join(problems, "\n") for _, want := range []string{"containerUser \"25000:25000\"", "${RCON_PORT}, but the setting arrives as RCONPORT", "${MISSING}"} { if !strings.Contains(joined, want) { t.Errorf("missing problem %q in:\n%s", want, joined) } } for _, unwanted := range []string{"ASA_START_PARAMS", "SERVER_MAP", "MAX_PLAYERS}"} { if strings.Contains(joined, unwanted) { t.Errorf("false problem about %s in:\n%s", unwanted, joined) } } if len(problems) != 3 { t.Errorf("got %d problems, want 3:\n%s", len(problems), joined) } // With the fixes, only the deliberately missing variable is left; 25000:1000 counts (same uid) game["containerUser"] = "25000:1000" game["defaultStartupCommand"] = `export ASA_START_PARAMS="${ASA_START_PARAMS:-x}?RCONPort=${RCONPORT:-27020}"` if problems, _ := checkTemplate(game, f); len(problems) != 0 { t.Errorf("fixed template still has problems: %v", problems) } } func TestContainerPort(t *testing.T) { for in, want := range map[string]string{"8211:8211/udp": "8211/udp", "7777/udp": "7777/udp", "25565": "25565/tcp"} { if got := containerPort(in); got != want { t.Errorf("containerPort(%q) = %q, want %q", in, got, want) } } }